workspaces-euc-mcp-server 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- workspaces_euc_mcp_server/__init__.py +6 -0
- workspaces_euc_mcp_server/clients.py +101 -0
- workspaces_euc_mcp_server/consts.py +154 -0
- workspaces_euc_mcp_server/models.py +333 -0
- workspaces_euc_mcp_server/server.py +129 -0
- workspaces_euc_mcp_server/tools/__init__.py +4 -0
- workspaces_euc_mcp_server/tools/_common.py +87 -0
- workspaces_euc_mcp_server/tools/cost.py +314 -0
- workspaces_euc_mcp_server/tools/destructive.py +307 -0
- workspaces_euc_mcp_server/tools/diagnostics.py +799 -0
- workspaces_euc_mcp_server/tools/inventory.py +158 -0
- workspaces_euc_mcp_server/tools/lifecycle.py +564 -0
- workspaces_euc_mcp_server/tools/performance.py +620 -0
- workspaces_euc_mcp_server/tools/pricing.py +152 -0
- workspaces_euc_mcp_server/tools/reporting.py +529 -0
- workspaces_euc_mcp_server/tools/secure_browser.py +190 -0
- workspaces_euc_mcp_server-0.1.1.dist-info/METADATA +270 -0
- workspaces_euc_mcp_server-0.1.1.dist-info/RECORD +21 -0
- workspaces_euc_mcp_server-0.1.1.dist-info/WHEEL +4 -0
- workspaces_euc_mcp_server-0.1.1.dist-info/entry_points.txt +2 -0
- workspaces_euc_mcp_server-0.1.1.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
# Copyright bengroeneveldsg. Licensed under the Apache License, Version 2.0 (the "License").
|
|
2
|
+
# You may not use this file except in compliance with the License.
|
|
3
|
+
# A copy of the License is located at http://www.apache.org/licenses/LICENSE-2.0
|
|
4
|
+
"""Guarded lifecycle (write) tools for WorkSpaces Personal, Pools, and Applications — Phase 2,
|
|
5
|
+
IAM Tier 2.
|
|
6
|
+
|
|
7
|
+
These tools are only registered when the server is launched with ``--enable-writes``. Every action
|
|
8
|
+
is **safe by default**:
|
|
9
|
+
|
|
10
|
+
1. **Dry-run by default** — without ``confirm=True`` the tool changes nothing and returns the plan.
|
|
11
|
+
2. **Blast-radius cap** — a confirmed bulk action is refused if it targets more than
|
|
12
|
+
``--max-bulk-targets`` resources.
|
|
13
|
+
3. **Least privilege** — only the specific power/running-mode actions, gated by IAM Tier 2.
|
|
14
|
+
|
|
15
|
+
Destructive operations (terminate/rebuild/restore) are intentionally NOT here; they belong to a
|
|
16
|
+
separate, separately-gated module (``--enable-destructive``).
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from collections.abc import Callable
|
|
22
|
+
from typing import Any, Literal
|
|
23
|
+
|
|
24
|
+
from .. import consts
|
|
25
|
+
from ..clients import ClientFactory
|
|
26
|
+
from ..models import ServiceError, TargetResult, WriteOutcome
|
|
27
|
+
from ._common import try_call, writes
|
|
28
|
+
|
|
29
|
+
_VALID_RUNNING_MODES = {"AUTO_STOP", "ALWAYS_ON"}
|
|
30
|
+
|
|
31
|
+
# action -> (boto3 method, per-request key) for the batch power operations.
|
|
32
|
+
_BATCH_POWER_ACTIONS = {
|
|
33
|
+
"start": ("start_workspaces", "StartWorkspaceRequests"),
|
|
34
|
+
"stop": ("stop_workspaces", "StopWorkspaceRequests"),
|
|
35
|
+
"reboot": ("reboot_workspaces", "RebootWorkspaceRequests"),
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _refused_for_blast_radius(
|
|
40
|
+
action: str, workspace_ids: list[str], max_bulk_targets: int
|
|
41
|
+
) -> WriteOutcome:
|
|
42
|
+
return WriteOutcome(
|
|
43
|
+
action=action,
|
|
44
|
+
dry_run=False,
|
|
45
|
+
confirmed=True,
|
|
46
|
+
requested_targets=workspace_ids,
|
|
47
|
+
max_bulk_targets=max_bulk_targets,
|
|
48
|
+
blast_radius_ok=False,
|
|
49
|
+
plan=(
|
|
50
|
+
f"Refused: {len(workspace_ids)} targets exceed the blast-radius cap of "
|
|
51
|
+
f"{max_bulk_targets}. Re-run with fewer targets or raise --max-bulk-targets."
|
|
52
|
+
),
|
|
53
|
+
notes=["No changes were made."],
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _dry_run_outcome(
|
|
58
|
+
action: str, workspace_ids: list[str], max_bulk_targets: int, detail: str
|
|
59
|
+
) -> WriteOutcome:
|
|
60
|
+
return WriteOutcome(
|
|
61
|
+
action=action,
|
|
62
|
+
dry_run=True,
|
|
63
|
+
confirmed=False,
|
|
64
|
+
requested_targets=workspace_ids,
|
|
65
|
+
max_bulk_targets=max_bulk_targets,
|
|
66
|
+
blast_radius_ok=len(workspace_ids) <= max_bulk_targets,
|
|
67
|
+
plan=detail,
|
|
68
|
+
results=[
|
|
69
|
+
TargetResult(target_id=wid, status="skipped", message="dry run")
|
|
70
|
+
for wid in workspace_ids
|
|
71
|
+
],
|
|
72
|
+
notes=["Dry run — nothing was changed. Re-run with confirm=true to execute."],
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def batch_power_action_core(
|
|
77
|
+
factory: ClientFactory,
|
|
78
|
+
region: str | None,
|
|
79
|
+
action: str,
|
|
80
|
+
workspace_ids: list[str],
|
|
81
|
+
confirm: bool,
|
|
82
|
+
max_bulk_targets: int,
|
|
83
|
+
) -> WriteOutcome:
|
|
84
|
+
method_name, request_key = _BATCH_POWER_ACTIONS[action]
|
|
85
|
+
detail = f"{action.capitalize()} {len(workspace_ids)} WorkSpace(s): {', '.join(workspace_ids)}"
|
|
86
|
+
|
|
87
|
+
if not workspace_ids:
|
|
88
|
+
return WriteOutcome(
|
|
89
|
+
action=action,
|
|
90
|
+
dry_run=not confirm,
|
|
91
|
+
confirmed=confirm,
|
|
92
|
+
requested_targets=[],
|
|
93
|
+
max_bulk_targets=max_bulk_targets,
|
|
94
|
+
blast_radius_ok=True,
|
|
95
|
+
plan="No target WorkSpaces were provided.",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
if not confirm:
|
|
99
|
+
return _dry_run_outcome(action, workspace_ids, max_bulk_targets, f"Would {detail.lower()}")
|
|
100
|
+
|
|
101
|
+
if len(workspace_ids) > max_bulk_targets:
|
|
102
|
+
return _refused_for_blast_radius(action, workspace_ids, max_bulk_targets)
|
|
103
|
+
|
|
104
|
+
errors: list[ServiceError] = []
|
|
105
|
+
client = factory.client(consts.WORKSPACES_API, region=region)
|
|
106
|
+
requests = [{"WorkspaceId": wid} for wid in workspace_ids]
|
|
107
|
+
response = try_call(
|
|
108
|
+
errors,
|
|
109
|
+
consts.PRODUCT_WORKSPACES_PERSONAL,
|
|
110
|
+
method_name,
|
|
111
|
+
lambda: getattr(client, method_name)(**{request_key: requests}),
|
|
112
|
+
default={},
|
|
113
|
+
)
|
|
114
|
+
failed = {fr.get("WorkspaceId"): fr for fr in (response or {}).get("FailedRequests", [])}
|
|
115
|
+
results = [
|
|
116
|
+
TargetResult(
|
|
117
|
+
target_id=wid,
|
|
118
|
+
status="error" if wid in failed else "ok",
|
|
119
|
+
message=failed.get(wid, {}).get("ErrorMessage") if wid in failed else None,
|
|
120
|
+
)
|
|
121
|
+
for wid in workspace_ids
|
|
122
|
+
]
|
|
123
|
+
return WriteOutcome(
|
|
124
|
+
action=action,
|
|
125
|
+
dry_run=False,
|
|
126
|
+
confirmed=True,
|
|
127
|
+
requested_targets=workspace_ids,
|
|
128
|
+
max_bulk_targets=max_bulk_targets,
|
|
129
|
+
blast_radius_ok=True,
|
|
130
|
+
plan=f"{detail} (executed).",
|
|
131
|
+
results=results,
|
|
132
|
+
errors=errors,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def modify_running_mode_core(
|
|
137
|
+
factory: ClientFactory,
|
|
138
|
+
region: str | None,
|
|
139
|
+
workspace_id: str,
|
|
140
|
+
running_mode: str,
|
|
141
|
+
confirm: bool,
|
|
142
|
+
max_bulk_targets: int,
|
|
143
|
+
) -> WriteOutcome:
|
|
144
|
+
action = "modify_running_mode"
|
|
145
|
+
running_mode = running_mode.upper()
|
|
146
|
+
if running_mode not in _VALID_RUNNING_MODES:
|
|
147
|
+
return WriteOutcome(
|
|
148
|
+
action=action,
|
|
149
|
+
dry_run=True,
|
|
150
|
+
confirmed=False,
|
|
151
|
+
requested_targets=[workspace_id],
|
|
152
|
+
max_bulk_targets=max_bulk_targets,
|
|
153
|
+
blast_radius_ok=True,
|
|
154
|
+
plan=f"Invalid running mode '{running_mode}'; use {sorted(_VALID_RUNNING_MODES)}.",
|
|
155
|
+
notes=["No changes were made."],
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
detail = f"Set running mode of {workspace_id} to {running_mode}"
|
|
159
|
+
if not confirm:
|
|
160
|
+
return _dry_run_outcome(action, [workspace_id], max_bulk_targets, f"Would {detail.lower()}")
|
|
161
|
+
|
|
162
|
+
errors: list[ServiceError] = []
|
|
163
|
+
client = factory.client(consts.WORKSPACES_API, region=region)
|
|
164
|
+
try_call(
|
|
165
|
+
errors,
|
|
166
|
+
consts.PRODUCT_WORKSPACES_PERSONAL,
|
|
167
|
+
"ModifyWorkspaceProperties",
|
|
168
|
+
lambda: client.modify_workspace_properties(
|
|
169
|
+
WorkspaceId=workspace_id,
|
|
170
|
+
WorkspaceProperties={"RunningMode": running_mode},
|
|
171
|
+
),
|
|
172
|
+
default={},
|
|
173
|
+
)
|
|
174
|
+
status = "error" if errors else "ok"
|
|
175
|
+
return WriteOutcome(
|
|
176
|
+
action=action,
|
|
177
|
+
dry_run=False,
|
|
178
|
+
confirmed=True,
|
|
179
|
+
requested_targets=[workspace_id],
|
|
180
|
+
max_bulk_targets=max_bulk_targets,
|
|
181
|
+
blast_radius_ok=True,
|
|
182
|
+
plan=f"{detail} (executed)." if not errors else f"{detail} (failed).",
|
|
183
|
+
results=[
|
|
184
|
+
TargetResult(
|
|
185
|
+
target_id=workspace_id,
|
|
186
|
+
status=status,
|
|
187
|
+
message=errors[0].message if errors else None,
|
|
188
|
+
)
|
|
189
|
+
],
|
|
190
|
+
errors=errors,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _single_target_outcome(
|
|
195
|
+
action: str,
|
|
196
|
+
target_id: str,
|
|
197
|
+
confirm: bool,
|
|
198
|
+
max_bulk_targets: int,
|
|
199
|
+
detail: str,
|
|
200
|
+
service: str,
|
|
201
|
+
operation: str,
|
|
202
|
+
execute: Callable[[], Any],
|
|
203
|
+
) -> WriteOutcome:
|
|
204
|
+
"""Run a guarded single-target write (start/stop/update of one pool or fleet)."""
|
|
205
|
+
if not confirm:
|
|
206
|
+
return _dry_run_outcome(action, [target_id], max_bulk_targets, f"Would {detail.lower()}")
|
|
207
|
+
|
|
208
|
+
errors: list[ServiceError] = []
|
|
209
|
+
try_call(errors, service, operation, execute, default={})
|
|
210
|
+
status = "error" if errors else "ok"
|
|
211
|
+
return WriteOutcome(
|
|
212
|
+
action=action,
|
|
213
|
+
dry_run=False,
|
|
214
|
+
confirmed=True,
|
|
215
|
+
requested_targets=[target_id],
|
|
216
|
+
max_bulk_targets=max_bulk_targets,
|
|
217
|
+
blast_radius_ok=True,
|
|
218
|
+
plan=f"{detail} (executed)." if not errors else f"{detail} (failed).",
|
|
219
|
+
results=[
|
|
220
|
+
TargetResult(
|
|
221
|
+
target_id=target_id,
|
|
222
|
+
status=status,
|
|
223
|
+
message=errors[0].message if errors else None,
|
|
224
|
+
)
|
|
225
|
+
],
|
|
226
|
+
errors=errors,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def pool_power_action_core(
|
|
231
|
+
factory: ClientFactory,
|
|
232
|
+
region: str | None,
|
|
233
|
+
action: str,
|
|
234
|
+
pool_id: str,
|
|
235
|
+
confirm: bool,
|
|
236
|
+
max_bulk_targets: int,
|
|
237
|
+
) -> WriteOutcome:
|
|
238
|
+
method_name = "start_workspaces_pool" if action == "start_pool" else "stop_workspaces_pool"
|
|
239
|
+
operation = "StartWorkspacesPool" if action == "start_pool" else "StopWorkspacesPool"
|
|
240
|
+
detail = f"{'Start' if action == 'start_pool' else 'Stop'} WorkSpaces Pool {pool_id}"
|
|
241
|
+
client = factory.client(consts.WORKSPACES_API, region=region)
|
|
242
|
+
return _single_target_outcome(
|
|
243
|
+
action,
|
|
244
|
+
pool_id,
|
|
245
|
+
confirm,
|
|
246
|
+
max_bulk_targets,
|
|
247
|
+
detail,
|
|
248
|
+
consts.PRODUCT_WORKSPACES_POOLS,
|
|
249
|
+
operation,
|
|
250
|
+
lambda: getattr(client, method_name)(PoolId=pool_id),
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def update_pool_capacity_core(
|
|
255
|
+
factory: ClientFactory,
|
|
256
|
+
region: str | None,
|
|
257
|
+
pool_id: str,
|
|
258
|
+
desired_user_sessions: int,
|
|
259
|
+
confirm: bool,
|
|
260
|
+
max_bulk_targets: int,
|
|
261
|
+
) -> WriteOutcome:
|
|
262
|
+
action = "update_pool_capacity"
|
|
263
|
+
if desired_user_sessions < 0:
|
|
264
|
+
return WriteOutcome(
|
|
265
|
+
action=action,
|
|
266
|
+
dry_run=True,
|
|
267
|
+
confirmed=False,
|
|
268
|
+
requested_targets=[pool_id],
|
|
269
|
+
max_bulk_targets=max_bulk_targets,
|
|
270
|
+
blast_radius_ok=True,
|
|
271
|
+
plan="Invalid capacity: desired_user_sessions must be >= 0.",
|
|
272
|
+
notes=["No changes were made."],
|
|
273
|
+
)
|
|
274
|
+
detail = f"Set WorkSpaces Pool {pool_id} desired user sessions to {desired_user_sessions}"
|
|
275
|
+
client = factory.client(consts.WORKSPACES_API, region=region)
|
|
276
|
+
return _single_target_outcome(
|
|
277
|
+
action,
|
|
278
|
+
pool_id,
|
|
279
|
+
confirm,
|
|
280
|
+
max_bulk_targets,
|
|
281
|
+
detail,
|
|
282
|
+
consts.PRODUCT_WORKSPACES_POOLS,
|
|
283
|
+
"UpdateWorkspacesPool",
|
|
284
|
+
lambda: client.update_workspaces_pool(
|
|
285
|
+
PoolId=pool_id, Capacity={"DesiredUserSessions": desired_user_sessions}
|
|
286
|
+
),
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def fleet_power_action_core(
|
|
291
|
+
factory: ClientFactory,
|
|
292
|
+
region: str | None,
|
|
293
|
+
action: str,
|
|
294
|
+
fleet_name: str,
|
|
295
|
+
confirm: bool,
|
|
296
|
+
max_bulk_targets: int,
|
|
297
|
+
) -> WriteOutcome:
|
|
298
|
+
method_name = "start_fleet" if action == "start_fleet" else "stop_fleet"
|
|
299
|
+
operation = "StartFleet" if action == "start_fleet" else "StopFleet"
|
|
300
|
+
detail = f"{'Start' if action == 'start_fleet' else 'Stop'} fleet {fleet_name}"
|
|
301
|
+
client = factory.client(consts.APPSTREAM_API, region=region)
|
|
302
|
+
return _single_target_outcome(
|
|
303
|
+
action,
|
|
304
|
+
fleet_name,
|
|
305
|
+
confirm,
|
|
306
|
+
max_bulk_targets,
|
|
307
|
+
detail,
|
|
308
|
+
consts.PRODUCT_WORKSPACES_APPLICATIONS,
|
|
309
|
+
operation,
|
|
310
|
+
lambda: getattr(client, method_name)(Name=fleet_name),
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def update_fleet_capacity_core(
|
|
315
|
+
factory: ClientFactory,
|
|
316
|
+
region: str | None,
|
|
317
|
+
fleet_name: str,
|
|
318
|
+
desired_instances: int,
|
|
319
|
+
confirm: bool,
|
|
320
|
+
max_bulk_targets: int,
|
|
321
|
+
) -> WriteOutcome:
|
|
322
|
+
action = "update_fleet_capacity"
|
|
323
|
+
if desired_instances < 0:
|
|
324
|
+
return WriteOutcome(
|
|
325
|
+
action=action,
|
|
326
|
+
dry_run=True,
|
|
327
|
+
confirmed=False,
|
|
328
|
+
requested_targets=[fleet_name],
|
|
329
|
+
max_bulk_targets=max_bulk_targets,
|
|
330
|
+
blast_radius_ok=True,
|
|
331
|
+
plan="Invalid capacity: desired_instances must be >= 0.",
|
|
332
|
+
notes=["No changes were made."],
|
|
333
|
+
)
|
|
334
|
+
detail = f"Set fleet {fleet_name} desired instances to {desired_instances}"
|
|
335
|
+
client = factory.client(consts.APPSTREAM_API, region=region)
|
|
336
|
+
return _single_target_outcome(
|
|
337
|
+
action,
|
|
338
|
+
fleet_name,
|
|
339
|
+
confirm,
|
|
340
|
+
max_bulk_targets,
|
|
341
|
+
detail,
|
|
342
|
+
consts.PRODUCT_WORKSPACES_APPLICATIONS,
|
|
343
|
+
"UpdateFleet",
|
|
344
|
+
lambda: client.update_fleet(
|
|
345
|
+
Name=fleet_name, ComputeCapacity={"DesiredInstances": desired_instances}
|
|
346
|
+
),
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def register(
|
|
351
|
+
mcp: Any,
|
|
352
|
+
factory: ClientFactory,
|
|
353
|
+
*,
|
|
354
|
+
max_bulk_targets: int,
|
|
355
|
+
enable_destructive: bool = False,
|
|
356
|
+
) -> None:
|
|
357
|
+
"""Register guarded lifecycle tools. Only call this when writes are enabled."""
|
|
358
|
+
|
|
359
|
+
async def start_workspaces(
|
|
360
|
+
workspace_ids: list[str], confirm: bool = False, region: str | None = None
|
|
361
|
+
) -> dict[str, Any]:
|
|
362
|
+
"""Start (power on) one or more WorkSpaces Personal desktops.
|
|
363
|
+
|
|
364
|
+
Dry-run by default: returns the plan and changes nothing unless confirm=true. Confirmed
|
|
365
|
+
bulk actions are refused above the configured blast-radius cap.
|
|
366
|
+
|
|
367
|
+
Args:
|
|
368
|
+
workspace_ids: WorkSpace IDs to start.
|
|
369
|
+
confirm: Set true to actually start them; otherwise a dry-run plan is returned.
|
|
370
|
+
region: AWS region. Defaults to the server's configured region.
|
|
371
|
+
"""
|
|
372
|
+
outcome = batch_power_action_core(
|
|
373
|
+
factory, region or factory.region, "start", workspace_ids, confirm, max_bulk_targets
|
|
374
|
+
)
|
|
375
|
+
return outcome.model_dump()
|
|
376
|
+
|
|
377
|
+
async def stop_workspaces(
|
|
378
|
+
workspace_ids: list[str], confirm: bool = False, region: str | None = None
|
|
379
|
+
) -> dict[str, Any]:
|
|
380
|
+
"""Stop (power off) one or more AutoStop WorkSpaces Personal desktops.
|
|
381
|
+
|
|
382
|
+
Dry-run by default; confirm=true to execute. Confirmed bulk actions are refused above the
|
|
383
|
+
blast-radius cap.
|
|
384
|
+
|
|
385
|
+
Args:
|
|
386
|
+
workspace_ids: WorkSpace IDs to stop.
|
|
387
|
+
confirm: Set true to actually stop them; otherwise a dry-run plan is returned.
|
|
388
|
+
region: AWS region. Defaults to the server's configured region.
|
|
389
|
+
"""
|
|
390
|
+
outcome = batch_power_action_core(
|
|
391
|
+
factory, region or factory.region, "stop", workspace_ids, confirm, max_bulk_targets
|
|
392
|
+
)
|
|
393
|
+
return outcome.model_dump()
|
|
394
|
+
|
|
395
|
+
async def reboot_workspaces(
|
|
396
|
+
workspace_ids: list[str], confirm: bool = False, region: str | None = None
|
|
397
|
+
) -> dict[str, Any]:
|
|
398
|
+
"""Reboot one or more WorkSpaces Personal desktops.
|
|
399
|
+
|
|
400
|
+
Dry-run by default; confirm=true to execute. Confirmed bulk actions are refused above the
|
|
401
|
+
blast-radius cap. Rebooting disconnects the user.
|
|
402
|
+
|
|
403
|
+
Args:
|
|
404
|
+
workspace_ids: WorkSpace IDs to reboot.
|
|
405
|
+
confirm: Set true to actually reboot them; otherwise a dry-run plan is returned.
|
|
406
|
+
region: AWS region. Defaults to the server's configured region.
|
|
407
|
+
"""
|
|
408
|
+
outcome = batch_power_action_core(
|
|
409
|
+
factory, region or factory.region, "reboot", workspace_ids, confirm, max_bulk_targets
|
|
410
|
+
)
|
|
411
|
+
return outcome.model_dump()
|
|
412
|
+
|
|
413
|
+
async def modify_workspace_running_mode(
|
|
414
|
+
workspace_id: str,
|
|
415
|
+
running_mode: Literal["AUTO_STOP", "ALWAYS_ON"],
|
|
416
|
+
confirm: bool = False,
|
|
417
|
+
region: str | None = None,
|
|
418
|
+
) -> dict[str, Any]:
|
|
419
|
+
"""Change a WorkSpace's running mode (AUTO_STOP or ALWAYS_ON).
|
|
420
|
+
|
|
421
|
+
Dry-run by default; confirm=true to execute. Use after recommend_running_mode.
|
|
422
|
+
|
|
423
|
+
Args:
|
|
424
|
+
workspace_id: The WorkSpace ID to modify.
|
|
425
|
+
running_mode: AUTO_STOP or ALWAYS_ON.
|
|
426
|
+
confirm: Set true to actually apply the change; otherwise a dry-run plan is returned.
|
|
427
|
+
region: AWS region. Defaults to the server's configured region.
|
|
428
|
+
"""
|
|
429
|
+
outcome = modify_running_mode_core(
|
|
430
|
+
factory, region or factory.region, workspace_id, running_mode, confirm, max_bulk_targets
|
|
431
|
+
)
|
|
432
|
+
return outcome.model_dump()
|
|
433
|
+
|
|
434
|
+
async def start_workspaces_pool(
|
|
435
|
+
pool_id: str, confirm: bool = False, region: str | None = None
|
|
436
|
+
) -> dict[str, Any]:
|
|
437
|
+
"""Start a WorkSpaces Pool. Dry-run by default; confirm=true to execute.
|
|
438
|
+
|
|
439
|
+
Args:
|
|
440
|
+
pool_id: The WorkSpaces Pool ID.
|
|
441
|
+
confirm: Set true to actually start it; otherwise a dry-run plan is returned.
|
|
442
|
+
region: AWS region. Defaults to the server's configured region.
|
|
443
|
+
"""
|
|
444
|
+
outcome = pool_power_action_core(
|
|
445
|
+
factory, region or factory.region, "start_pool", pool_id, confirm, max_bulk_targets
|
|
446
|
+
)
|
|
447
|
+
return outcome.model_dump()
|
|
448
|
+
|
|
449
|
+
async def stop_workspaces_pool(
|
|
450
|
+
pool_id: str, confirm: bool = False, region: str | None = None
|
|
451
|
+
) -> dict[str, Any]:
|
|
452
|
+
"""Stop a WorkSpaces Pool. Dry-run by default; confirm=true to execute.
|
|
453
|
+
|
|
454
|
+
Args:
|
|
455
|
+
pool_id: The WorkSpaces Pool ID.
|
|
456
|
+
confirm: Set true to actually stop it; otherwise a dry-run plan is returned.
|
|
457
|
+
region: AWS region. Defaults to the server's configured region.
|
|
458
|
+
"""
|
|
459
|
+
outcome = pool_power_action_core(
|
|
460
|
+
factory, region or factory.region, "stop_pool", pool_id, confirm, max_bulk_targets
|
|
461
|
+
)
|
|
462
|
+
return outcome.model_dump()
|
|
463
|
+
|
|
464
|
+
async def update_workspaces_pool_capacity(
|
|
465
|
+
pool_id: str, desired_user_sessions: int, confirm: bool = False, region: str | None = None
|
|
466
|
+
) -> dict[str, Any]:
|
|
467
|
+
"""Set a WorkSpaces Pool's desired user-session capacity.
|
|
468
|
+
|
|
469
|
+
Dry-run by default; confirm=true to execute. Changing capacity affects cost.
|
|
470
|
+
|
|
471
|
+
Args:
|
|
472
|
+
pool_id: The WorkSpaces Pool ID.
|
|
473
|
+
desired_user_sessions: Target number of concurrent user sessions (>= 0).
|
|
474
|
+
confirm: Set true to actually apply the change; otherwise a dry-run plan is returned.
|
|
475
|
+
region: AWS region. Defaults to the server's configured region.
|
|
476
|
+
"""
|
|
477
|
+
outcome = update_pool_capacity_core(
|
|
478
|
+
factory,
|
|
479
|
+
region or factory.region,
|
|
480
|
+
pool_id,
|
|
481
|
+
desired_user_sessions,
|
|
482
|
+
confirm,
|
|
483
|
+
max_bulk_targets,
|
|
484
|
+
)
|
|
485
|
+
return outcome.model_dump()
|
|
486
|
+
|
|
487
|
+
async def start_application_fleet(
|
|
488
|
+
fleet_name: str, confirm: bool = False, region: str | None = None
|
|
489
|
+
) -> dict[str, Any]:
|
|
490
|
+
"""Start a WorkSpaces Applications (formerly AppStream 2.0) fleet. Dry-run by default.
|
|
491
|
+
|
|
492
|
+
Args:
|
|
493
|
+
fleet_name: The fleet name.
|
|
494
|
+
confirm: Set true to actually start it; otherwise a dry-run plan is returned.
|
|
495
|
+
region: AWS region. Defaults to the server's configured region.
|
|
496
|
+
"""
|
|
497
|
+
outcome = fleet_power_action_core(
|
|
498
|
+
factory, region or factory.region, "start_fleet", fleet_name, confirm, max_bulk_targets
|
|
499
|
+
)
|
|
500
|
+
return outcome.model_dump()
|
|
501
|
+
|
|
502
|
+
async def stop_application_fleet(
|
|
503
|
+
fleet_name: str, confirm: bool = False, region: str | None = None
|
|
504
|
+
) -> dict[str, Any]:
|
|
505
|
+
"""Stop a WorkSpaces Applications (formerly AppStream 2.0) fleet. Dry-run by default.
|
|
506
|
+
|
|
507
|
+
Args:
|
|
508
|
+
fleet_name: The fleet name.
|
|
509
|
+
confirm: Set true to actually stop it; otherwise a dry-run plan is returned.
|
|
510
|
+
region: AWS region. Defaults to the server's configured region.
|
|
511
|
+
"""
|
|
512
|
+
outcome = fleet_power_action_core(
|
|
513
|
+
factory, region or factory.region, "stop_fleet", fleet_name, confirm, max_bulk_targets
|
|
514
|
+
)
|
|
515
|
+
return outcome.model_dump()
|
|
516
|
+
|
|
517
|
+
async def update_application_fleet_capacity(
|
|
518
|
+
fleet_name: str, desired_instances: int, confirm: bool = False, region: str | None = None
|
|
519
|
+
) -> dict[str, Any]:
|
|
520
|
+
"""Set a WorkSpaces Applications (formerly AppStream 2.0) fleet's desired instance capacity.
|
|
521
|
+
|
|
522
|
+
Dry-run by default; confirm=true to execute. Changing capacity affects cost.
|
|
523
|
+
|
|
524
|
+
Args:
|
|
525
|
+
fleet_name: The fleet name.
|
|
526
|
+
desired_instances: Target number of fleet instances (>= 0).
|
|
527
|
+
confirm: Set true to actually apply the change; otherwise a dry-run plan is returned.
|
|
528
|
+
region: AWS region. Defaults to the server's configured region.
|
|
529
|
+
"""
|
|
530
|
+
outcome = update_fleet_capacity_core(
|
|
531
|
+
factory,
|
|
532
|
+
region or factory.region,
|
|
533
|
+
fleet_name,
|
|
534
|
+
desired_instances,
|
|
535
|
+
confirm,
|
|
536
|
+
max_bulk_targets,
|
|
537
|
+
)
|
|
538
|
+
return outcome.model_dump()
|
|
539
|
+
|
|
540
|
+
mcp.add_tool(start_workspaces, annotations=writes("Start WorkSpaces", idempotent=True))
|
|
541
|
+
mcp.add_tool(stop_workspaces, annotations=writes("Stop WorkSpaces", idempotent=True))
|
|
542
|
+
mcp.add_tool(reboot_workspaces, annotations=writes("Reboot WorkSpaces"))
|
|
543
|
+
mcp.add_tool(
|
|
544
|
+
modify_workspace_running_mode,
|
|
545
|
+
annotations=writes("Modify WorkSpace running mode", idempotent=True),
|
|
546
|
+
)
|
|
547
|
+
mcp.add_tool(
|
|
548
|
+
start_workspaces_pool, annotations=writes("Start WorkSpaces Pool", idempotent=True)
|
|
549
|
+
)
|
|
550
|
+
mcp.add_tool(stop_workspaces_pool, annotations=writes("Stop WorkSpaces Pool", idempotent=True))
|
|
551
|
+
mcp.add_tool(
|
|
552
|
+
update_workspaces_pool_capacity,
|
|
553
|
+
annotations=writes("Update Pool capacity", idempotent=True),
|
|
554
|
+
)
|
|
555
|
+
mcp.add_tool(
|
|
556
|
+
start_application_fleet, annotations=writes("Start Applications fleet", idempotent=True)
|
|
557
|
+
)
|
|
558
|
+
mcp.add_tool(
|
|
559
|
+
stop_application_fleet, annotations=writes("Stop Applications fleet", idempotent=True)
|
|
560
|
+
)
|
|
561
|
+
mcp.add_tool(
|
|
562
|
+
update_application_fleet_capacity,
|
|
563
|
+
annotations=writes("Update fleet capacity", idempotent=True),
|
|
564
|
+
)
|