Coverage for gco_mcp / tools / stacks.py: 100.00%
234 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-10-28 16:19 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-10-28 16:19 +0000
1"""Infrastructure stack management MCP tools."""
3from __future__ import annotations
5import asyncio
6from typing import Any
8import cli_runner
9from audit import audit_logged
11# FastMCP's Progress / Context dependencies are optional from this
12# module's perspective — when ``fastmcp[tasks]`` is reachable they
13# inject real instances per call; otherwise the gated long-running
14# tools still register but rely on caller-provided fakes (the test path).
15from fastmcp.server.dependencies import CurrentContext, Progress
17# TaskConfig opts the gated stack-lifecycle tools into the MCP tasks
18# extension (SEP-2663, registered in gco_mcp/server.py) with
19# ``mode="optional"`` — clients that support the task protocol receive a
20# task ID immediately and poll for progress, while clients without
21# task-protocol support fall back to inline execution with progress
22# streamed through FastMCP's Progress dependency. Required-mode would lock
23# out clients that don't speak the task protocol (e.g. the GCO MCP
24# orchestrator's ``call_tool`` proxy), and these tools are useful enough
25# that the inline fallback is worth keeping.
26from fastmcp.utilities.tasks import TaskConfig
27from feature_flags import (
28 FLAG_CONFIG_MANAGEMENT,
29 FLAG_INFRASTRUCTURE_DEPLOY,
30 FLAG_INFRASTRUCTURE_DESTROY,
31 is_enabled,
32)
33from server import mcp
35from tools._long_task import _run_long_task
37_TASK_CONFIG_OPTIONAL = TaskConfig(mode="optional")
40def _expected_stack_count_for_all() -> int | None:
41 """Return the number of stacks ``deploy-all`` / ``destroy-all`` will touch.
43 Reads ``cdk.json``'s ``context.deployment_regions`` and counts the
44 fixed-position stacks (gco-global, gco-api-gateway, gco-monitoring)
45 plus one per regional region. Returns ``None`` when the config is
46 unreadable or empty so the caller falls back to indeterminate
47 progress instead of an inaccurate total.
49 The count drives ``progress.set_total(...)`` so MCP clients render
50 a real percentage during a multi-stack deploy or destroy.
51 """
52 try:
53 from cli.config import _load_cdk_json
54 except Exception: # best-effort
55 return None
56 try:
57 cdk_regions = _load_cdk_json()
58 except Exception: # best-effort
59 return None
60 # _load_cdk_json only ever returns a dict (it falls back to ``{}`` when
61 # the file is missing or the block is not a mapping), so a shape check
62 # here would be dead code; the keys are what need checking.
63 if "regional" not in cdk_regions:
64 return None
65 regional = cdk_regions["regional"]
66 if not isinstance(regional, list):
67 return None
68 # Three fixed stacks (global / api-gateway / monitoring) plus one
69 # per regional region. Analytics is opt-in and omitted from the
70 # baseline count — when enabled it adds one more stack but
71 # under-reporting is preferable to over-reporting (the progress
72 # bar rolls over rather than stopping at 95 %).
73 return 3 + len(regional)
76@mcp.tool(tags={"safe", "stacks"})
77@audit_logged
78def list_stacks() -> str:
79 """List all GCO CDK stacks."""
80 return cli_runner._run_cli("stacks", "list")
83@mcp.tool(tags={"safe", "stacks"})
84@audit_logged
85def stack_status(stack_name: str, region: str) -> str:
86 """Get detailed status of a CloudFormation stack.
88 Args:
89 stack_name: Stack name (e.g. gco-us-east-1).
90 region: AWS region.
91 """
92 return cli_runner._run_cli("stacks", "status", stack_name, "-r", region)
95@mcp.tool(tags={"low-risk", "stacks"})
96@audit_logged
97def setup_cluster_access(cluster: str | None = None, region: str | None = None) -> str:
98 """Configure kubectl access to a GCO EKS cluster.
100 Updates kubeconfig, creates an EKS access entry for your IAM principal,
101 and associates the cluster admin policy. Handles assumed roles automatically.
103 Args:
104 cluster: Cluster name (default: <project_name>-{region}).
105 region: AWS region (default: first deployment region from cdk.json).
106 """
107 args = ["stacks", "access"]
108 if cluster:
109 args.extend(["-c", cluster])
110 if region:
111 args.extend(["-r", region])
112 return cli_runner._run_cli(*args)
115@mcp.tool(tags={"safe", "stacks"})
116@audit_logged
117def fsx_status() -> str:
118 """Check FSx for Lustre configuration status."""
119 return cli_runner._run_cli("stacks", "fsx", "status")
122# =============================================================================
123# Read-only inspection tools (async)
124# =============================================================================
127@mcp.tool(tags={"safe", "stacks"})
128@audit_logged
129async def stack_diff(stack_name: str | None = None) -> str:
130 """`gco stacks diff` — show CloudFormation diff for a stack.
132 Args:
133 stack_name: Stack to diff. If omitted, diffs all stacks.
134 """
135 args = ["stacks", "diff"]
136 if stack_name:
137 args.append(stack_name)
138 return await asyncio.to_thread(cli_runner._run_cli, *args)
141@mcp.tool(tags={"safe", "stacks"})
142@audit_logged
143async def stack_outputs(stack_name: str, region: str) -> str:
144 """`gco stacks outputs` — fetch CloudFormation outputs for a stack.
146 Args:
147 stack_name: Stack name (e.g. gco-us-east-1).
148 region: AWS region.
149 """
150 return await asyncio.to_thread(
151 cli_runner._run_cli, "stacks", "outputs", stack_name, "-r", region
152 )
155@mcp.tool(tags={"safe", "stacks"})
156@audit_logged
157async def stack_synth(stack_name: str | None = None, quiet: bool = True) -> str:
158 """`gco stacks synth` — synthesize CloudFormation templates from CDK.
160 Args:
161 stack_name: Stack to synthesize. If omitted, synthesizes all stacks.
162 quiet: When True, pass ``--quiet`` to suppress verbose CDK output.
163 """
164 args = ["stacks", "synth"]
165 if stack_name:
166 args.append(stack_name)
167 if quiet:
168 args.append("--quiet")
169 return await asyncio.to_thread(cli_runner._run_cli, *args)
172@mcp.tool(tags={"safe", "stacks"})
173@audit_logged
174async def addons_status(region: str | None = None, all_regions: bool = False) -> str:
175 """`gco stacks addons status` — show per-chart Helm add-on status from SSM.
177 Args:
178 region: Region to inspect. Omit for the first deployment region.
179 all_regions: Inspect every configured deployment region.
180 """
181 args = ["stacks", "addons", "status"]
182 if all_regions:
183 args.append("--all-regions")
184 elif region:
185 args += ["-r", region]
186 return await asyncio.to_thread(cli_runner._run_cli, *args)
189@mcp.tool(tags={"safe", "stacks"})
190@audit_logged
191async def valkey_status() -> str:
192 """`gco stacks valkey status` — show Valkey cache stack status."""
193 return await asyncio.to_thread(cli_runner._run_cli, "stacks", "valkey", "status")
196@mcp.tool(tags={"safe", "stacks"})
197@audit_logged
198async def aurora_status() -> str:
199 """`gco stacks aurora status` — show Aurora database stack status."""
200 return await asyncio.to_thread(cli_runner._run_cli, "stacks", "aurora", "status")
203@mcp.tool(tags={"safe", "stacks"})
204@audit_logged
205async def eks_capabilities_status(region: str | None = None, all_regions: bool = False) -> str:
206 """`gco stacks capabilities status` — configured vs. live EKS Capabilities.
208 Reports, per AWS-managed capability type (ACK, kro), whether cdk.json
209 ``eks_capabilities`` enables it for the region, whether it is attached to
210 the cluster, its status/version, and any drift between configuration and
211 the cluster.
213 Args:
214 region: Region to inspect. Omit for the first deployment region.
215 all_regions: Inspect every configured deployment region.
216 """
217 args = ["stacks", "capabilities", "status"]
218 if all_regions:
219 args.append("--all-regions")
220 elif region:
221 args += ["-r", region]
222 return await asyncio.to_thread(cli_runner._run_cli, *args)
225# =============================================================================
226# Mutating cdk.json toggles (low-risk)
227# =============================================================================
230@mcp.tool(tags={"low-risk", "stacks"})
231@audit_logged
232async def enable_fsx() -> str:
233 """`gco stacks fsx enable` — flip FSx Lustre on in cdk.json.
235 Note: this only edits the cdk.json toggle. The change does not take effect
236 until ``gco stacks deploy-all`` runs to provision the FSx file system.
237 """
238 return await asyncio.to_thread(cli_runner._run_cli, "stacks", "fsx", "enable", "-y")
241@mcp.tool(tags={"low-risk", "stacks"})
242@audit_logged
243async def disable_fsx() -> str:
244 """`gco stacks fsx disable` — flip FSx Lustre off in cdk.json.
246 Note: this only edits the cdk.json toggle. The change does not take effect
247 until ``gco stacks deploy-all`` runs to remove the FSx file system.
248 """
249 return await asyncio.to_thread(cli_runner._run_cli, "stacks", "fsx", "disable", "-y")
252@mcp.tool(tags={"low-risk", "stacks"})
253@audit_logged
254async def enable_valkey() -> str:
255 """`gco stacks valkey enable` — flip Valkey Serverless on in cdk.json.
257 Note: this only edits the cdk.json toggle. The change does not take effect
258 until ``gco stacks deploy-all`` runs to provision the Valkey cache.
259 """
260 return await asyncio.to_thread(cli_runner._run_cli, "stacks", "valkey", "enable", "-y")
263@mcp.tool(tags={"low-risk", "stacks"})
264@audit_logged
265async def disable_valkey() -> str:
266 """`gco stacks valkey disable` — flip Valkey Serverless off in cdk.json.
268 Note: this only edits the cdk.json toggle. The change does not take effect
269 until ``gco stacks deploy-all`` runs to remove the Valkey cache.
270 """
271 return await asyncio.to_thread(cli_runner._run_cli, "stacks", "valkey", "disable", "-y")
274@mcp.tool(tags={"low-risk", "stacks"})
275@audit_logged
276async def enable_aurora() -> str:
277 """`gco stacks aurora enable` — flip Aurora pgvector on in cdk.json.
279 Note: this only edits the cdk.json toggle. The change does not take effect
280 until ``gco stacks deploy-all`` runs to provision the Aurora cluster.
281 """
282 return await asyncio.to_thread(cli_runner._run_cli, "stacks", "aurora", "enable", "-y")
285@mcp.tool(tags={"low-risk", "stacks"})
286@audit_logged
287async def disable_aurora() -> str:
288 """`gco stacks aurora disable` — flip Aurora pgvector off in cdk.json.
290 Note: this only edits the cdk.json toggle. The change does not take effect
291 until ``gco stacks deploy-all`` runs to remove the Aurora cluster.
292 """
293 return await asyncio.to_thread(cli_runner._run_cli, "stacks", "aurora", "disable", "-y")
296# =============================================================================
297# Long-running stack lifecycle tools — gated by GCO_ENABLE_INFRASTRUCTURE_DEPLOY
298# =============================================================================
299#
300# deploy_stack / deploy_all / bootstrap_cdk drive CDK lifecycle operations
301# that exceed the short-running ``cli_runner._run_cli`` 120-second timeout.
302# They run via ``_run_long_task`` so progress streams back through the
303# FastMCP Progress dependency and clients can poll task status through
304# the standard MCP task protocol.
306if is_enabled(FLAG_INFRASTRUCTURE_DEPLOY):
308 @mcp.tool(tags={"infrastructure", "stacks"})
309 @audit_logged
310 async def addons_install(region: str | None = None, all_regions: bool = False) -> str:
311 """[gated by GCO_ENABLE_INFRASTRUCTURE_DEPLOY] infrastructure mutation.
313 `gco stacks addons install` — start an idempotent Helm add-on
314 re-convergence from the deployment input persisted in SSM. The command
315 starts each region's installer state machine and returns immediately;
316 inspect progress with ``addons_status``.
318 Args:
319 region: Region to re-converge. Omit for the first deployment region.
320 all_regions: Re-converge every configured deployment region.
321 """
322 args = ["stacks", "addons", "install"]
323 if all_regions:
324 args.append("--all-regions")
325 elif region:
326 args += ["-r", region]
327 return await asyncio.to_thread(cli_runner._run_cli, *args)
329 @mcp.tool(tags={"infrastructure", "stacks"}, task=_TASK_CONFIG_OPTIONAL)
330 @audit_logged
331 async def deploy_stack(
332 stack_name: str,
333 yes: bool = True,
334 outputs_file: str | None = None,
335 tags: list[str] | None = None,
336 *,
337 ctx: Any = CurrentContext(),
338 progress: Any = Progress(),
339 ) -> str:
340 """[gated by GCO_ENABLE_INFRASTRUCTURE_DEPLOY] long-running.
342 `gco stacks deploy` — deploy a single CDK stack to AWS.
344 Typical wall-clock: 15-30 minutes per regional stack. Clients that
345 speak FastMCP's task protocol can receive a task ID immediately
346 and poll `tasks://gco/{task_id}` for progress; clients that don't
347 run the tool inline with progress streamed through the FastMCP
348 Progress dependency. Cancellation sends SIGTERM to the running
349 CDK process and partial CloudFormation state may remain — inspect
350 via stack_status or the AWS console.
352 Args:
353 stack_name: Stack to deploy (e.g. ``gco-us-east-1``).
354 yes: Skip approval prompts (passes ``-y``). Defaults to True.
355 outputs_file: Optional path to write stack outputs JSON.
356 tags: Optional list of ``key=value`` tag strings applied to the stack.
357 """
358 argv = [
359 "gco",
360 "stacks",
361 "deploy",
362 stack_name,
363 ]
364 if yes:
365 argv.append("-y")
366 if outputs_file:
367 argv += ["--outputs-file", outputs_file]
368 for tag in tags or []:
369 argv += ["--tag", tag]
370 return await _run_long_task(
371 argv,
372 ctx=ctx,
373 progress=progress,
374 is_stack_op=True,
375 total_units=1,
376 )
378 @mcp.tool(tags={"infrastructure", "stacks"}, task=_TASK_CONFIG_OPTIONAL)
379 @audit_logged
380 async def deploy_all(
381 yes: bool = True,
382 outputs_file: str | None = None,
383 tags: list[str] | None = None,
384 parallel: bool = False,
385 max_workers: int | None = None,
386 *,
387 ctx: Any = CurrentContext(),
388 progress: Any = Progress(),
389 ) -> str:
390 """[gated by GCO_ENABLE_INFRASTRUCTURE_DEPLOY] long-running.
392 `gco stacks deploy-all` — deploy every CDK stack in dependency order.
394 Typical wall-clock: 30-60 minutes for a fresh multi-region deploy.
395 Clients that speak FastMCP's task protocol can receive a task ID
396 immediately and poll `tasks://gco/{task_id}` for progress; clients
397 that don't run the tool inline with progress streamed through the
398 FastMCP Progress dependency. Cancellation sends SIGTERM to the
399 running CDK process and partial CloudFormation state may remain —
400 inspect via stack_status or the AWS console.
402 Args:
403 yes: Skip approval prompts (passes ``-y``). Defaults to True.
404 outputs_file: Optional path to write stack outputs JSON.
405 tags: Optional list of ``key=value`` tag strings applied to every stack.
406 parallel: Deploy regional stacks concurrently when True.
407 max_workers: Cap on parallel deployments when ``parallel=True``.
408 """
409 argv = [
410 "gco",
411 "stacks",
412 "deploy-all",
413 ]
414 if yes:
415 argv.append("-y")
416 if outputs_file:
417 argv += ["--outputs-file", outputs_file]
418 for tag in tags or []:
419 argv += ["--tag", tag]
420 if parallel:
421 argv.append("--parallel")
422 if max_workers is not None:
423 argv += ["--max-workers", str(max_workers)]
424 return await _run_long_task(
425 argv,
426 ctx=ctx,
427 progress=progress,
428 is_stack_op=True,
429 total_units=_expected_stack_count_for_all(),
430 )
432 @mcp.tool(tags={"infrastructure", "stacks"}, task=_TASK_CONFIG_OPTIONAL)
433 @audit_logged
434 async def bootstrap_cdk(
435 region: str,
436 account: str | None = None,
437 *,
438 ctx: Any = CurrentContext(),
439 progress: Any = Progress(),
440 ) -> str:
441 """[gated by GCO_ENABLE_INFRASTRUCTURE_DEPLOY] long-running.
443 `gco stacks bootstrap` — bootstrap CDK in an AWS account/region.
445 Typical wall-clock: 2-5 minutes. Required before any stack can be
446 deployed to a new account/region. Clients that speak FastMCP's
447 task protocol can receive a task ID immediately and poll
448 `tasks://gco/{task_id}` for progress; clients that don't run the
449 tool inline with progress streamed through the FastMCP Progress
450 dependency. Cancellation sends SIGTERM to the running CDK process
451 and partial CloudFormation state may remain — inspect via
452 stack_status or the AWS console.
454 Args:
455 region: Target AWS region.
456 account: Optional AWS account ID. Defaults to the caller's account.
457 """
458 argv = ["gco", "stacks", "bootstrap", "--region", region]
459 if account:
460 argv += ["--account", account]
461 return await _run_long_task(
462 argv,
463 ctx=ctx,
464 progress=progress,
465 is_stack_op=True,
466 total_units=1,
467 )
470# =============================================================================
471# Long-running stack lifecycle tools — gated by GCO_ENABLE_INFRASTRUCTURE_DESTROY
472# =============================================================================
474if is_enabled(FLAG_INFRASTRUCTURE_DESTROY):
476 @mcp.tool(tags={"infrastructure", "stacks"}, task=_TASK_CONFIG_OPTIONAL)
477 @audit_logged
478 async def destroy_stack(
479 stack_name: str,
480 yes: bool = True,
481 retain_volumes: bool = False,
482 *,
483 ctx: Any = CurrentContext(),
484 progress: Any = Progress(),
485 ) -> str:
486 """[gated by GCO_ENABLE_INFRASTRUCTURE_DESTROY] long-running.
488 `gco stacks destroy` — destroy a single CDK stack.
490 Typical wall-clock: 5-20 minutes per stack. Clients that speak
491 FastMCP's task protocol can receive a task ID immediately and
492 poll `tasks://gco/{task_id}` for progress; clients that don't
493 run the tool inline with progress streamed through the FastMCP
494 Progress dependency. Cancellation sends SIGTERM to the running
495 CDK process and partial CloudFormation state may remain —
496 inspect via stack_status or the AWS console before retrying.
498 Args:
499 stack_name: Stack to destroy (e.g. ``gco-us-east-1``).
500 yes: Skip the confirmation prompt (passes ``-y``). Defaults to True.
501 retain_volumes: Report the cluster's orphaned EBS volumes instead
502 of deleting them (passes ``--retain-volumes``). Defaults to
503 False, matching the CLI: deleting an EKS cluster does not
504 delete the volumes its CSI driver provisioned, so they bill
505 indefinitely with nothing able to reattach them.
506 """
507 argv = ["gco", "stacks", "destroy", stack_name]
508 if yes:
509 argv.append("-y")
510 if retain_volumes:
511 argv.append("--retain-volumes")
512 return await _run_long_task(
513 argv,
514 ctx=ctx,
515 progress=progress,
516 is_stack_op=True,
517 total_units=1,
518 )
520 @mcp.tool(tags={"infrastructure", "stacks"}, task=_TASK_CONFIG_OPTIONAL)
521 @audit_logged
522 async def destroy_all(
523 yes: bool = True,
524 parallel: bool = False,
525 max_workers: int | None = None,
526 retain_volumes: bool = False,
527 keep_control_plane: bool = False,
528 *,
529 ctx: Any = CurrentContext(),
530 progress: Any = Progress(),
531 ) -> str:
532 """[gated by GCO_ENABLE_INFRASTRUCTURE_DESTROY] long-running.
534 `gco stacks destroy-all` — destroy every CDK stack in reverse dependency order,
535 or with keep_control_plane=True only the workload tier (scale to zero Regions).
537 Typical wall-clock: 20-40 minutes for a multi-region teardown.
538 Clients that speak FastMCP's task protocol can receive a task
539 ID immediately and poll `tasks://gco/{task_id}` for progress;
540 clients that don't run the tool inline with progress streamed
541 through the FastMCP Progress dependency. Cancellation sends
542 SIGTERM to the running CDK process and partial CloudFormation
543 state may remain — inspect via stack_status or the AWS console
544 before retrying.
546 Args:
547 yes: Skip the confirmation prompt (passes ``-y``). Defaults to True.
548 parallel: Destroy regional stacks concurrently when True.
549 max_workers: Cap on parallel destructions when ``parallel=True``.
550 retain_volumes: Report each cluster's orphaned EBS volumes instead
551 of deleting them (passes ``--retain-volumes``). Defaults to
552 False, matching the CLI.
553 keep_control_plane: Destroy only the regional API bridges and
554 regional stacks and leave the global, API Gateway and
555 monitoring stacks standing (passes ``--keep-control-plane``).
556 Regional data (EFS/FSx, Valkey, Aurora, in-cluster volumes) is
557 still deleted. Defaults to False, matching the CLI.
558 """
559 argv = ["gco", "stacks", "destroy-all"]
560 if yes:
561 argv.append("-y")
562 if parallel:
563 argv.append("--parallel")
564 if max_workers is not None:
565 argv += ["--max-workers", str(max_workers)]
566 if retain_volumes:
567 argv.append("--retain-volumes")
568 if keep_control_plane:
569 argv.append("--keep-control-plane")
570 return await _run_long_task(
571 argv,
572 ctx=ctx,
573 progress=progress,
574 is_stack_op=True,
575 total_units=_expected_stack_count_for_all(),
576 )
579# =============================================================================
580# Managed deployment configuration — disabled by default.
581# Set GCO_ENABLE_CONFIG_MANAGEMENT=true to enable.
582# =============================================================================
583# These tools edit cdk.json on the MCP host through the managed-config
584# engine (cli/managed_config.py): validated against the same rules CDK
585# synth enforces, atomic, idempotent, and audited. They never deploy —
586# an explicit deploy_stack / deploy_all call (separately gated by
587# GCO_ENABLE_INFRASTRUCTURE_DEPLOY) is still required for a topology
588# change to reach AWS. Installed (uvx/pip) servers resolve a read-only
589# packaged cdk.json; the engine refuses those with guidance rather than
590# half-working, so these tools are useful from a GCO checkout.
592if is_enabled(FLAG_CONFIG_MANAGEMENT):
594 @mcp.tool(tags={"safe", "stacks"})
595 @audit_logged
596 def list_deployment_regions() -> str:
597 """[gated by GCO_ENABLE_CONFIG_MANAGEMENT]
599 Show the deployment-region topology configured in cdk.json.
601 Reports the global/api_gateway/monitoring Regions, the workload
602 (regional) Region list, the resolved AWS partition, and the cdk.json
603 path backing the answer. Works on a broken configuration too — the
604 partition_error field explains what CDK synth would reject.
605 """
606 return cli_runner._run_cli("stacks", "regions", "list")
608 @mcp.tool(tags={"low-risk", "stacks"})
609 @audit_logged
610 def add_deployment_region(region: str) -> str:
611 """[gated by GCO_ENABLE_CONFIG_MANAGEMENT]
613 Add a workload Region to cdk.json deployment_regions.regional.
615 Config-only and idempotent: the Region must be SDK-known and share
616 the AWS partition of the already-configured Regions; re-adding a
617 present Region is a reported no-op. No stack is deployed — follow
618 up with deploy_all to apply the topology (a deploy_stack of
619 only the new regional stack skips its API bridge and the monitoring
620 update). Existing inference endpoints keep their Region lists; add
621 the new Region to one with add_inference_region.
623 Args:
624 region: AWS Region name to add (e.g. us-west-2).
625 """
626 return cli_runner._run_cli("stacks", "regions", "add", region, "-y")
628 @mcp.tool(tags={"low-risk", "stacks"})
629 @audit_logged
630 def remove_deployment_region(region: str) -> str:
631 """[gated by GCO_ENABLE_CONFIG_MANAGEMENT]
633 Remove a workload Region from cdk.json deployment_regions.regional.
635 Config-only and idempotent: the resulting list must stay valid
636 (SDK-known, unique Regions in one AWS partition). Removing the last
637 Region is allowed and leaves a control-plane-only topology (global,
638 API Gateway, and monitoring stacks with no workload cluster);
639 removing an absent Region is a reported no-op.
640 A deployed stack for the removed Region is NOT destroyed, and once
641 the Region leaves cdk.json it is outside the CDK app, where
642 destroy_stack cannot reach it. Retire a deployed Region first with the
643 procedure in docs/CUSTOMIZATION.md (Removing a Region).
645 Args:
646 region: AWS Region name to remove (e.g. us-west-2).
647 """
648 return cli_runner._run_cli("stacks", "regions", "remove", region, "-y")
650 @mcp.tool(tags={"low-risk", "stacks"})
651 @audit_logged
652 def set_deployment_region(role: str, region: str) -> str:
653 """[gated by GCO_ENABLE_CONFIG_MANAGEMENT]
655 Set a control-plane Region scalar in cdk.json deployment_regions.
657 Config-only and idempotent: the Region must be SDK-known and keep
658 the whole topology (all three scalars plus the workload list) in one
659 AWS partition. Already-deployed stacks are not moved or destroyed —
660 the next deploy creates the stack in the new Region.
662 Args:
663 role: Which scalar to set: "global", "api_gateway", or "monitoring".
664 region: AWS Region name (e.g. us-east-2).
665 """
666 return cli_runner._run_cli("stacks", "regions", "set", role, region, "-y")
668 @mcp.tool(tags={"low-risk", "stacks"})
669 @audit_logged
670 def set_eks_endpoint_access(mode: str, cidrs: list[str] | None = None) -> str:
671 """[gated by GCO_ENABLE_CONFIG_MANAGEMENT]
673 Set cdk.json eks_cluster.endpoint_access (the EKS API endpoint mode).
675 Config-only and synth-time: no stack is deployed, and `gco stacks
676 status` reports the configured-vs-live endpoint as drift until a
677 deploy converges it. PUBLIC_AND_PRIVATE requires an explicit CIDR
678 allowlist — the CLI refuses to widen control-plane access without
679 one, and an internet-open endpoint must be spelled out as 0.0.0.0/0.
680 PRIVATE needs no CIDRs (use cluster_tunnel_command / `gco cluster
681 tunnel` for laptop access, plus an access entry via `gco stacks
682 access`).
684 Args:
685 mode: "PRIVATE" or "PUBLIC_AND_PRIVATE".
686 cidrs: CIDR allowlist entries for the public endpoint
687 (e.g. ["203.0.113.7/32"]). Required for PUBLIC_AND_PRIVATE.
688 """
689 args = ["stacks", "eks", "endpoint", "set", mode]
690 for cidr in cidrs or []:
691 args.extend(["--cidr", cidr])
692 args.append("-y")
693 return cli_runner._run_cli(*args)
695 @mcp.tool(tags={"low-risk", "stacks"})
696 @audit_logged
697 def set_mission_default_model(model_id: str) -> str:
698 """[gated by GCO_ENABLE_CONFIG_MANAGEMENT]
700 Set cdk.json bedrock.mission_default_model_id (Mission sampling).
702 Config-only and idempotent. The capacity advisor and gco autopilot
703 have their own keys (set_capacity_advisor_default_model and
704 set_claude_code_default_model). Model and inference-profile IDs are
705 free-form (custom profiles, marketplace models); validation mirrors
706 the runtime reader (non-empty, no surrounding whitespace). Sibling
707 settings (bedrock.generation_reasoning, the other model keys) are preserved;
708 explicit --bedrock-model-id / env overrides still take precedence
709 at run time.
711 Args:
712 model_id: Bedrock model or inference-profile ID
713 (e.g. us.amazon.nova-2-lite-v1:0).
714 """
715 return cli_runner._run_cli("stacks", "bedrock", "set-mission-model", model_id, "-y")
717 @mcp.tool(tags={"low-risk", "stacks"})
718 @audit_logged
719 def set_capacity_advisor_default_model(model_id: str) -> str:
720 """[gated by GCO_ENABLE_CONFIG_MANAGEMENT]
722 Set cdk.json bedrock.capacity_advisor_default_model_id.
724 The default model for gco capacity ai-recommend and gco capacity
725 predict. Config-only and idempotent; Mission sampling and gco
726 autopilot have their own keys (set_mission_default_model and
727 set_claude_code_default_model). Validation mirrors the runtime
728 reader (non-empty, no surrounding whitespace). Sibling settings
729 (bedrock.generation_reasoning, the other model keys) are preserved; explicit
730 --model overrides still take precedence at run time.
732 Args:
733 model_id: Bedrock model or inference-profile ID
734 (e.g. us.amazon.nova-2-lite-v1:0).
735 """
736 return cli_runner._run_cli(
737 "stacks", "bedrock", "set-capacity-advisor-model", model_id, "-y"
738 )
740 @mcp.tool(tags={"low-risk", "stacks"})
741 @audit_logged
742 def set_claude_code_default_model(model_id: str) -> str:
743 """[gated by GCO_ENABLE_CONFIG_MANAGEMENT]
745 Set cdk.json bedrock.claude_code_default_model_id (autopilot model).
747 The session model gco autopilot hands to Claude Code, independent of
748 the mission_default_model_id and capacity_advisor_default_model_id
749 knobs consumed by Mission sampling and the capacity advisor.
750 Config-only and idempotent; validation mirrors the runtime reader
751 (non-empty, no surrounding whitespace). Sibling settings are
752 preserved; explicit --model / GCO_AUTOPILOT_MODEL overrides still
753 take precedence at launch time.
755 Args:
756 model_id: Bedrock model or inference-profile ID
757 (e.g. us.anthropic.claude-sonnet-4-6).
758 """
759 return cli_runner._run_cli("stacks", "bedrock", "set-claude-code-model", model_id, "-y")
761 @mcp.tool(tags={"low-risk", "stacks"})
762 @audit_logged
763 def set_codex_default_model(model_id: str) -> str:
764 """[gated by GCO_ENABLE_CONFIG_MANAGEMENT]
766 Set cdk.json bedrock.codex_default_model_id.
768 Config-only and idempotent. The reviewed
769 bedrock.codex.reasoning_effort sibling is preserved; review the pair
770 together when changing model families. Explicit --model,
771 GCO_AUTOPILOT_CODEX_MODEL, and GCO_AUTOPILOT_MODEL overrides still
772 take precedence at launch.
774 Args:
775 model_id: Bedrock model or inference-profile ID
776 (e.g. global.openai.<model-id>).
777 """
778 return cli_runner._run_cli("stacks", "bedrock", "set-codex-model", model_id, "-y")
780 @mcp.tool(tags={"low-risk", "stacks"})
781 @audit_logged
782 def set_codex_reasoning_effort(reasoning_effort: str) -> str:
783 """[gated by GCO_ENABLE_CONFIG_MANAGEMENT]
785 Set cdk.json bedrock.codex.reasoning_effort.
787 Allowed values are minimal, low, medium, high, and xhigh. The setting
788 applies only to the canonical Codex model; explicit model overrides
789 intentionally omit canonical reasoning.
791 Args:
792 reasoning_effort: Reviewed canonical Codex reasoning effort.
793 """
794 return cli_runner._run_cli(
795 "stacks",
796 "bedrock",
797 "set-codex-reasoning-effort",
798 reasoning_effort,
799 "-y",
800 )
802 @mcp.tool(tags={"low-risk", "stacks"})
803 @audit_logged
804 def set_opencode_default_model(model_id: str) -> str:
805 """[gated by GCO_ENABLE_CONFIG_MANAGEMENT]
807 Set cdk.json bedrock.opencode_default_model_id.
809 Config-only and idempotent. OpenCode has no engine-owned reasoning
810 sibling, so this single key is the whole default. Explicit --model,
811 GCO_AUTOPILOT_OPENCODE_MODEL, and GCO_AUTOPILOT_MODEL overrides still
812 take precedence at launch.
814 Args:
815 model_id: Bedrock model or inference-profile ID
816 (e.g. global.moonshotai.<model-id>).
817 """
818 return cli_runner._run_cli("stacks", "bedrock", "set-opencode-model", model_id, "-y")