Coverage for gco_mcp / tools / stacks.py: 100.00%
221 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-09-14 22:07 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-09-14 22:07 +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: # noqa: BLE001 — best-effort
55 return None
56 try:
57 cdk_regions = _load_cdk_json()
58 except Exception: # noqa: BLE001 — best-effort
59 return None
60 if not isinstance(cdk_regions, dict):
61 return None
62 if "regional" not in cdk_regions:
63 return None
64 regional = cdk_regions["regional"]
65 if not isinstance(regional, list):
66 return None
67 # Three fixed stacks (global / api-gateway / monitoring) plus one
68 # per regional region. Analytics is opt-in and omitted from the
69 # baseline count — when enabled it adds one more stack but
70 # under-reporting is preferable to over-reporting (the progress
71 # bar rolls over rather than stopping at 95 %).
72 return 3 + len(regional)
75@mcp.tool(tags={"safe", "stacks"})
76@audit_logged
77def list_stacks() -> str:
78 """List all GCO CDK stacks."""
79 return cli_runner._run_cli("stacks", "list")
82@mcp.tool(tags={"safe", "stacks"})
83@audit_logged
84def stack_status(stack_name: str, region: str) -> str:
85 """Get detailed status of a CloudFormation stack.
87 Args:
88 stack_name: Stack name (e.g. gco-us-east-1).
89 region: AWS region.
90 """
91 return cli_runner._run_cli("stacks", "status", stack_name, "-r", region)
94@mcp.tool(tags={"low-risk", "stacks"})
95@audit_logged
96def setup_cluster_access(cluster: str | None = None, region: str | None = None) -> str:
97 """Configure kubectl access to a GCO EKS cluster.
99 Updates kubeconfig, creates an EKS access entry for your IAM principal,
100 and associates the cluster admin policy. Handles assumed roles automatically.
102 Args:
103 cluster: Cluster name (default: <project_name>-{region}).
104 region: AWS region (default: first deployment region from cdk.json).
105 """
106 args = ["stacks", "access"]
107 if cluster:
108 args.extend(["-c", cluster])
109 if region:
110 args.extend(["-r", region])
111 return cli_runner._run_cli(*args)
114@mcp.tool(tags={"safe", "stacks"})
115@audit_logged
116def fsx_status() -> str:
117 """Check FSx for Lustre configuration status."""
118 return cli_runner._run_cli("stacks", "fsx", "status")
121# =============================================================================
122# Read-only inspection tools (async)
123# =============================================================================
126@mcp.tool(tags={"safe", "stacks"})
127@audit_logged
128async def stack_diff(stack_name: str | None = None) -> str:
129 """`gco stacks diff` — show CloudFormation diff for a stack.
131 Args:
132 stack_name: Stack to diff. If omitted, diffs all stacks.
133 """
134 args = ["stacks", "diff"]
135 if stack_name:
136 args.append(stack_name)
137 return await asyncio.to_thread(cli_runner._run_cli, *args)
140@mcp.tool(tags={"safe", "stacks"})
141@audit_logged
142async def stack_outputs(stack_name: str, region: str) -> str:
143 """`gco stacks outputs` — fetch CloudFormation outputs for a stack.
145 Args:
146 stack_name: Stack name (e.g. gco-us-east-1).
147 region: AWS region.
148 """
149 return await asyncio.to_thread(
150 cli_runner._run_cli, "stacks", "outputs", stack_name, "-r", region
151 )
154@mcp.tool(tags={"safe", "stacks"})
155@audit_logged
156async def stack_synth(stack_name: str | None = None, quiet: bool = True) -> str:
157 """`gco stacks synth` — synthesize CloudFormation templates from CDK.
159 Args:
160 stack_name: Stack to synthesize. If omitted, synthesizes all stacks.
161 quiet: When True, pass ``--quiet`` to suppress verbose CDK output.
162 """
163 args = ["stacks", "synth"]
164 if stack_name:
165 args.append(stack_name)
166 if quiet:
167 args.append("--quiet")
168 return await asyncio.to_thread(cli_runner._run_cli, *args)
171@mcp.tool(tags={"safe", "stacks"})
172@audit_logged
173async def addons_status(region: str | None = None, all_regions: bool = False) -> str:
174 """`gco stacks addons status` — show per-chart Helm add-on status from SSM.
176 Args:
177 region: Region to inspect. Omit for the first deployment region.
178 all_regions: Inspect every configured deployment region.
179 """
180 args = ["stacks", "addons", "status"]
181 if all_regions:
182 args.append("--all-regions")
183 elif region:
184 args += ["-r", region]
185 return await asyncio.to_thread(cli_runner._run_cli, *args)
188@mcp.tool(tags={"safe", "stacks"})
189@audit_logged
190async def valkey_status() -> str:
191 """`gco stacks valkey status` — show Valkey cache stack status."""
192 return await asyncio.to_thread(cli_runner._run_cli, "stacks", "valkey", "status")
195@mcp.tool(tags={"safe", "stacks"})
196@audit_logged
197async def aurora_status() -> str:
198 """`gco stacks aurora status` — show Aurora database stack status."""
199 return await asyncio.to_thread(cli_runner._run_cli, "stacks", "aurora", "status")
202# =============================================================================
203# Mutating cdk.json toggles (low-risk)
204# =============================================================================
207@mcp.tool(tags={"low-risk", "stacks"})
208@audit_logged
209async def enable_fsx() -> str:
210 """`gco stacks fsx enable` — flip FSx Lustre on in cdk.json.
212 Note: this only edits the cdk.json toggle. The change does not take effect
213 until ``gco stacks deploy-all`` runs to provision the FSx file system.
214 """
215 return await asyncio.to_thread(cli_runner._run_cli, "stacks", "fsx", "enable", "-y")
218@mcp.tool(tags={"low-risk", "stacks"})
219@audit_logged
220async def disable_fsx() -> str:
221 """`gco stacks fsx disable` — flip FSx Lustre off in cdk.json.
223 Note: this only edits the cdk.json toggle. The change does not take effect
224 until ``gco stacks deploy-all`` runs to remove the FSx file system.
225 """
226 return await asyncio.to_thread(cli_runner._run_cli, "stacks", "fsx", "disable", "-y")
229@mcp.tool(tags={"low-risk", "stacks"})
230@audit_logged
231async def enable_valkey() -> str:
232 """`gco stacks valkey enable` — flip Valkey Serverless on in cdk.json.
234 Note: this only edits the cdk.json toggle. The change does not take effect
235 until ``gco stacks deploy-all`` runs to provision the Valkey cache.
236 """
237 return await asyncio.to_thread(cli_runner._run_cli, "stacks", "valkey", "enable", "-y")
240@mcp.tool(tags={"low-risk", "stacks"})
241@audit_logged
242async def disable_valkey() -> str:
243 """`gco stacks valkey disable` — flip Valkey Serverless off in cdk.json.
245 Note: this only edits the cdk.json toggle. The change does not take effect
246 until ``gco stacks deploy-all`` runs to remove the Valkey cache.
247 """
248 return await asyncio.to_thread(cli_runner._run_cli, "stacks", "valkey", "disable", "-y")
251@mcp.tool(tags={"low-risk", "stacks"})
252@audit_logged
253async def enable_aurora() -> str:
254 """`gco stacks aurora enable` — flip Aurora pgvector on in cdk.json.
256 Note: this only edits the cdk.json toggle. The change does not take effect
257 until ``gco stacks deploy-all`` runs to provision the Aurora cluster.
258 """
259 return await asyncio.to_thread(cli_runner._run_cli, "stacks", "aurora", "enable", "-y")
262@mcp.tool(tags={"low-risk", "stacks"})
263@audit_logged
264async def disable_aurora() -> str:
265 """`gco stacks aurora disable` — flip Aurora pgvector off in cdk.json.
267 Note: this only edits the cdk.json toggle. The change does not take effect
268 until ``gco stacks deploy-all`` runs to remove the Aurora cluster.
269 """
270 return await asyncio.to_thread(cli_runner._run_cli, "stacks", "aurora", "disable", "-y")
273# =============================================================================
274# Long-running stack lifecycle tools — gated by GCO_ENABLE_INFRASTRUCTURE_DEPLOY
275# =============================================================================
276#
277# deploy_stack / deploy_all / bootstrap_cdk drive CDK lifecycle operations
278# that exceed the short-running ``cli_runner._run_cli`` 120-second timeout.
279# They run via ``_run_long_task`` so progress streams back through the
280# FastMCP Progress dependency and clients can poll task status through
281# the standard MCP task protocol.
283if is_enabled(FLAG_INFRASTRUCTURE_DEPLOY):
285 @mcp.tool(tags={"infrastructure", "stacks"})
286 @audit_logged
287 async def addons_install(region: str | None = None, all_regions: bool = False) -> str:
288 """[gated by GCO_ENABLE_INFRASTRUCTURE_DEPLOY] infrastructure mutation.
290 `gco stacks addons install` — start an idempotent Helm add-on
291 re-convergence from the deployment input persisted in SSM. The command
292 starts each region's installer state machine and returns immediately;
293 inspect progress with ``addons_status``.
295 Args:
296 region: Region to re-converge. Omit for the first deployment region.
297 all_regions: Re-converge every configured deployment region.
298 """
299 args = ["stacks", "addons", "install"]
300 if all_regions:
301 args.append("--all-regions")
302 elif region:
303 args += ["-r", region]
304 return await asyncio.to_thread(cli_runner._run_cli, *args)
306 @mcp.tool(tags={"infrastructure", "stacks"}, task=_TASK_CONFIG_OPTIONAL)
307 @audit_logged
308 async def deploy_stack(
309 stack_name: str,
310 yes: bool = True,
311 outputs_file: str | None = None,
312 tags: list[str] | None = None,
313 *,
314 ctx: Any = CurrentContext(),
315 progress: Any = Progress(),
316 ) -> str:
317 """[gated by GCO_ENABLE_INFRASTRUCTURE_DEPLOY] long-running.
319 `gco stacks deploy` — deploy a single CDK stack to AWS.
321 Typical wall-clock: 15-30 minutes per regional stack. Clients that
322 speak FastMCP's task protocol can receive a task ID immediately
323 and poll `tasks://gco/{task_id}` for progress; clients that don't
324 run the tool inline with progress streamed through the FastMCP
325 Progress dependency. Cancellation sends SIGTERM to the running
326 CDK process and partial CloudFormation state may remain — inspect
327 via stack_status or the AWS console.
329 Args:
330 stack_name: Stack to deploy (e.g. ``gco-us-east-1``).
331 yes: Skip approval prompts (passes ``-y``). Defaults to True.
332 outputs_file: Optional path to write stack outputs JSON.
333 tags: Optional list of ``key=value`` tag strings applied to the stack.
334 """
335 argv = [
336 "gco",
337 "stacks",
338 "deploy",
339 stack_name,
340 ]
341 if yes:
342 argv.append("-y")
343 if outputs_file:
344 argv += ["--outputs-file", outputs_file]
345 for tag in tags or []:
346 argv += ["--tag", tag]
347 return await _run_long_task(
348 argv,
349 ctx=ctx,
350 progress=progress,
351 is_stack_op=True,
352 total_units=1,
353 )
355 @mcp.tool(tags={"infrastructure", "stacks"}, task=_TASK_CONFIG_OPTIONAL)
356 @audit_logged
357 async def deploy_all(
358 yes: bool = True,
359 outputs_file: str | None = None,
360 tags: list[str] | None = None,
361 parallel: bool = False,
362 max_workers: int | None = None,
363 *,
364 ctx: Any = CurrentContext(),
365 progress: Any = Progress(),
366 ) -> str:
367 """[gated by GCO_ENABLE_INFRASTRUCTURE_DEPLOY] long-running.
369 `gco stacks deploy-all` — deploy every CDK stack in dependency order.
371 Typical wall-clock: 30-60 minutes for a fresh multi-region deploy.
372 Clients that speak FastMCP's task protocol can receive a task ID
373 immediately and poll `tasks://gco/{task_id}` for progress; clients
374 that don't run the tool inline with progress streamed through the
375 FastMCP Progress dependency. Cancellation sends SIGTERM to the
376 running CDK process and partial CloudFormation state may remain —
377 inspect via stack_status or the AWS console.
379 Args:
380 yes: Skip approval prompts (passes ``-y``). Defaults to True.
381 outputs_file: Optional path to write stack outputs JSON.
382 tags: Optional list of ``key=value`` tag strings applied to every stack.
383 parallel: Deploy regional stacks concurrently when True.
384 max_workers: Cap on parallel deployments when ``parallel=True``.
385 """
386 argv = [
387 "gco",
388 "stacks",
389 "deploy-all",
390 ]
391 if yes:
392 argv.append("-y")
393 if outputs_file:
394 argv += ["--outputs-file", outputs_file]
395 for tag in tags or []:
396 argv += ["--tag", tag]
397 if parallel:
398 argv.append("--parallel")
399 if max_workers is not None:
400 argv += ["--max-workers", str(max_workers)]
401 return await _run_long_task(
402 argv,
403 ctx=ctx,
404 progress=progress,
405 is_stack_op=True,
406 total_units=_expected_stack_count_for_all(),
407 )
409 @mcp.tool(tags={"infrastructure", "stacks"}, task=_TASK_CONFIG_OPTIONAL)
410 @audit_logged
411 async def bootstrap_cdk(
412 region: str,
413 account: str | None = None,
414 *,
415 ctx: Any = CurrentContext(),
416 progress: Any = Progress(),
417 ) -> str:
418 """[gated by GCO_ENABLE_INFRASTRUCTURE_DEPLOY] long-running.
420 `gco stacks bootstrap` — bootstrap CDK in an AWS account/region.
422 Typical wall-clock: 2-5 minutes. Required before any stack can be
423 deployed to a new account/region. Clients that speak FastMCP's
424 task protocol can receive a task ID immediately and poll
425 `tasks://gco/{task_id}` for progress; clients that don't run the
426 tool inline with progress streamed through the FastMCP Progress
427 dependency. Cancellation sends SIGTERM to the running CDK process
428 and partial CloudFormation state may remain — inspect via
429 stack_status or the AWS console.
431 Args:
432 region: Target AWS region.
433 account: Optional AWS account ID. Defaults to the caller's account.
434 """
435 argv = ["gco", "stacks", "bootstrap", "--region", region]
436 if account:
437 argv += ["--account", account]
438 return await _run_long_task(
439 argv,
440 ctx=ctx,
441 progress=progress,
442 is_stack_op=True,
443 total_units=1,
444 )
447# =============================================================================
448# Long-running stack lifecycle tools — gated by GCO_ENABLE_INFRASTRUCTURE_DESTROY
449# =============================================================================
451if is_enabled(FLAG_INFRASTRUCTURE_DESTROY):
453 @mcp.tool(tags={"infrastructure", "stacks"}, task=_TASK_CONFIG_OPTIONAL)
454 @audit_logged
455 async def destroy_stack(
456 stack_name: str,
457 yes: bool = True,
458 retain_volumes: bool = False,
459 *,
460 ctx: Any = CurrentContext(),
461 progress: Any = Progress(),
462 ) -> str:
463 """[gated by GCO_ENABLE_INFRASTRUCTURE_DESTROY] long-running.
465 `gco stacks destroy` — destroy a single CDK stack.
467 Typical wall-clock: 5-20 minutes per stack. Clients that speak
468 FastMCP's task protocol can receive a task ID immediately and
469 poll `tasks://gco/{task_id}` for progress; clients that don't
470 run the tool inline with progress streamed through the FastMCP
471 Progress dependency. Cancellation sends SIGTERM to the running
472 CDK process and partial CloudFormation state may remain —
473 inspect via stack_status or the AWS console before retrying.
475 Args:
476 stack_name: Stack to destroy (e.g. ``gco-us-east-1``).
477 yes: Skip the confirmation prompt (passes ``-y``). Defaults to True.
478 retain_volumes: Report the cluster's orphaned EBS volumes instead
479 of deleting them (passes ``--retain-volumes``). Defaults to
480 False, matching the CLI: deleting an EKS cluster does not
481 delete the volumes its CSI driver provisioned, so they bill
482 indefinitely with nothing able to reattach them.
483 """
484 argv = ["gco", "stacks", "destroy", stack_name]
485 if yes:
486 argv.append("-y")
487 if retain_volumes:
488 argv.append("--retain-volumes")
489 return await _run_long_task(
490 argv,
491 ctx=ctx,
492 progress=progress,
493 is_stack_op=True,
494 total_units=1,
495 )
497 @mcp.tool(tags={"infrastructure", "stacks"}, task=_TASK_CONFIG_OPTIONAL)
498 @audit_logged
499 async def destroy_all(
500 yes: bool = True,
501 parallel: bool = False,
502 max_workers: int | None = None,
503 retain_volumes: bool = False,
504 *,
505 ctx: Any = CurrentContext(),
506 progress: Any = Progress(),
507 ) -> str:
508 """[gated by GCO_ENABLE_INFRASTRUCTURE_DESTROY] long-running.
510 `gco stacks destroy-all` — destroy every CDK stack in reverse dependency order.
512 Typical wall-clock: 20-40 minutes for a multi-region teardown.
513 Clients that speak FastMCP's task protocol can receive a task
514 ID immediately and poll `tasks://gco/{task_id}` for progress;
515 clients that don't run the tool inline with progress streamed
516 through the FastMCP Progress dependency. Cancellation sends
517 SIGTERM to the running CDK process and partial CloudFormation
518 state may remain — inspect via stack_status or the AWS console
519 before retrying.
521 Args:
522 yes: Skip the confirmation prompt (passes ``-y``). Defaults to True.
523 parallel: Destroy regional stacks concurrently when True.
524 max_workers: Cap on parallel destructions when ``parallel=True``.
525 retain_volumes: Report each cluster's orphaned EBS volumes instead
526 of deleting them (passes ``--retain-volumes``). Defaults to
527 False, matching the CLI.
528 """
529 argv = ["gco", "stacks", "destroy-all"]
530 if yes:
531 argv.append("-y")
532 if parallel:
533 argv.append("--parallel")
534 if max_workers is not None:
535 argv += ["--max-workers", str(max_workers)]
536 if retain_volumes:
537 argv.append("--retain-volumes")
538 return await _run_long_task(
539 argv,
540 ctx=ctx,
541 progress=progress,
542 is_stack_op=True,
543 total_units=_expected_stack_count_for_all(),
544 )
547# =============================================================================
548# Managed deployment configuration — disabled by default.
549# Set GCO_ENABLE_CONFIG_MANAGEMENT=true to enable.
550# =============================================================================
551# These tools edit cdk.json on the MCP host through the managed-config
552# engine (cli/managed_config.py): validated against the same rules CDK
553# synth enforces, atomic, idempotent, and audited. They never deploy —
554# an explicit deploy_stack / deploy_all_stacks call (separately gated by
555# GCO_ENABLE_INFRASTRUCTURE_DEPLOY) is still required for a topology
556# change to reach AWS. Installed (uvx/pip) servers resolve a read-only
557# packaged cdk.json; the engine refuses those with guidance rather than
558# half-working, so these tools are useful from a GCO checkout.
560if is_enabled(FLAG_CONFIG_MANAGEMENT):
562 @mcp.tool(tags={"safe", "stacks"})
563 @audit_logged
564 def list_deployment_regions() -> str:
565 """[gated by GCO_ENABLE_CONFIG_MANAGEMENT]
567 Show the deployment-region topology configured in cdk.json.
569 Reports the global/api_gateway/monitoring Regions, the workload
570 (regional) Region list, the resolved AWS partition, and the cdk.json
571 path backing the answer. Works on a broken configuration too — the
572 partition_error field explains what CDK synth would reject.
573 """
574 return cli_runner._run_cli("stacks", "regions", "list")
576 @mcp.tool(tags={"low-risk", "stacks"})
577 @audit_logged
578 def add_deployment_region(region: str) -> str:
579 """[gated by GCO_ENABLE_CONFIG_MANAGEMENT]
581 Add a workload Region to cdk.json deployment_regions.regional.
583 Config-only and idempotent: the Region must be SDK-known and share
584 the AWS partition of the already-configured Regions; re-adding a
585 present Region is a reported no-op. No stack is deployed — follow
586 up with deploy_stack / deploy_all_stacks to apply the topology.
588 Args:
589 region: AWS Region name to add (e.g. us-west-2).
590 """
591 return cli_runner._run_cli("stacks", "regions", "add", region, "-y")
593 @mcp.tool(tags={"low-risk", "stacks"})
594 @audit_logged
595 def remove_deployment_region(region: str) -> str:
596 """[gated by GCO_ENABLE_CONFIG_MANAGEMENT]
598 Remove a workload Region from cdk.json deployment_regions.regional.
600 Config-only and idempotent: the resulting list must stay valid (at
601 least one Region); removing an absent Region is a reported no-op.
602 A deployed stack for the removed Region is NOT destroyed — that
603 requires an explicit destroy_stack call (separately gated).
605 Args:
606 region: AWS Region name to remove (e.g. us-west-2).
607 """
608 return cli_runner._run_cli("stacks", "regions", "remove", region, "-y")
610 @mcp.tool(tags={"low-risk", "stacks"})
611 @audit_logged
612 def set_deployment_region(role: str, region: str) -> str:
613 """[gated by GCO_ENABLE_CONFIG_MANAGEMENT]
615 Set a control-plane Region scalar in cdk.json deployment_regions.
617 Config-only and idempotent: the Region must be SDK-known and keep
618 the whole topology (all three scalars plus the workload list) in one
619 AWS partition. Already-deployed stacks are not moved or destroyed —
620 the next deploy creates the stack in the new Region.
622 Args:
623 role: Which scalar to set: "global", "api_gateway", or "monitoring".
624 region: AWS Region name (e.g. us-east-2).
625 """
626 return cli_runner._run_cli("stacks", "regions", "set", role, region, "-y")
628 @mcp.tool(tags={"low-risk", "stacks"})
629 @audit_logged
630 def set_eks_endpoint_access(mode: str, cidrs: list[str] | None = None) -> str:
631 """[gated by GCO_ENABLE_CONFIG_MANAGEMENT]
633 Set cdk.json eks_cluster.endpoint_access (the EKS API endpoint mode).
635 Config-only and synth-time: no stack is deployed, and `gco stacks
636 status` reports the configured-vs-live endpoint as drift until a
637 deploy converges it. PUBLIC_AND_PRIVATE requires an explicit CIDR
638 allowlist — the CLI refuses to widen control-plane access without
639 one, and an internet-open endpoint must be spelled out as 0.0.0.0/0.
640 PRIVATE needs no CIDRs (use cluster_tunnel_command / `gco cluster
641 tunnel` for laptop access, plus an access entry via `gco stacks
642 access`).
644 Args:
645 mode: "PRIVATE" or "PUBLIC_AND_PRIVATE".
646 cidrs: CIDR allowlist entries for the public endpoint
647 (e.g. ["203.0.113.7/32"]). Required for PUBLIC_AND_PRIVATE.
648 """
649 args = ["stacks", "eks", "endpoint", "set", mode]
650 for cidr in cidrs or []:
651 args.extend(["--cidr", cidr])
652 args.append("-y")
653 return cli_runner._run_cli(*args)
655 @mcp.tool(tags={"low-risk", "stacks"})
656 @audit_logged
657 def set_mission_default_model(model_id: str) -> str:
658 """[gated by GCO_ENABLE_CONFIG_MANAGEMENT]
660 Set cdk.json bedrock.mission_default_model_id (Mission sampling).
662 Config-only and idempotent. The capacity advisor and gco autopilot
663 have their own keys (set_capacity_advisor_default_model and
664 set_claude_code_default_model). Model and inference-profile IDs are
665 free-form (custom profiles, marketplace models); validation mirrors
666 the runtime reader (non-empty, no surrounding whitespace). Sibling
667 settings (bedrock.generation_reasoning, the other model keys) are preserved;
668 explicit --bedrock-model-id / env overrides still take precedence
669 at run time.
671 Args:
672 model_id: Bedrock model or inference-profile ID
673 (e.g. us.amazon.nova-2-lite-v1:0).
674 """
675 return cli_runner._run_cli("stacks", "bedrock", "set-mission-model", model_id, "-y")
677 @mcp.tool(tags={"low-risk", "stacks"})
678 @audit_logged
679 def set_capacity_advisor_default_model(model_id: str) -> str:
680 """[gated by GCO_ENABLE_CONFIG_MANAGEMENT]
682 Set cdk.json bedrock.capacity_advisor_default_model_id.
684 The default model for gco capacity advise and its historical
685 variant. Config-only and idempotent; Mission sampling and gco
686 autopilot have their own keys (set_mission_default_model and
687 set_claude_code_default_model). Validation mirrors the runtime
688 reader (non-empty, no surrounding whitespace). Sibling settings
689 (bedrock.generation_reasoning, the other model keys) are preserved; explicit
690 --model overrides still take precedence at run time.
692 Args:
693 model_id: Bedrock model or inference-profile ID
694 (e.g. us.amazon.nova-2-lite-v1:0).
695 """
696 return cli_runner._run_cli(
697 "stacks", "bedrock", "set-capacity-advisor-model", model_id, "-y"
698 )
700 @mcp.tool(tags={"low-risk", "stacks"})
701 @audit_logged
702 def set_claude_code_default_model(model_id: str) -> str:
703 """[gated by GCO_ENABLE_CONFIG_MANAGEMENT]
705 Set cdk.json bedrock.claude_code_default_model_id (autopilot model).
707 The session model gco autopilot hands to Claude Code, independent of
708 the mission_default_model_id and capacity_advisor_default_model_id
709 knobs consumed by Mission sampling and the capacity advisor.
710 Config-only and idempotent; validation mirrors the runtime reader
711 (non-empty, no surrounding whitespace). Sibling settings are
712 preserved; explicit --model / GCO_AUTOPILOT_MODEL overrides still
713 take precedence at launch time.
715 Args:
716 model_id: Bedrock model or inference-profile ID
717 (e.g. us.anthropic.claude-sonnet-4-6).
718 """
719 return cli_runner._run_cli("stacks", "bedrock", "set-claude-code-model", model_id, "-y")
721 @mcp.tool(tags={"low-risk", "stacks"})
722 @audit_logged
723 def set_codex_default_model(model_id: str) -> str:
724 """[gated by GCO_ENABLE_CONFIG_MANAGEMENT]
726 Set cdk.json bedrock.codex_default_model_id.
728 Config-only and idempotent. The reviewed
729 bedrock.codex.reasoning_effort sibling is preserved; review the pair
730 together when changing model families. Explicit --model,
731 GCO_AUTOPILOT_CODEX_MODEL, and GCO_AUTOPILOT_MODEL overrides still
732 take precedence at launch.
734 Args:
735 model_id: Bedrock model or inference-profile ID
736 (e.g. global.openai.<model-id>).
737 """
738 return cli_runner._run_cli("stacks", "bedrock", "set-codex-model", model_id, "-y")
740 @mcp.tool(tags={"low-risk", "stacks"})
741 @audit_logged
742 def set_codex_reasoning_effort(reasoning_effort: str) -> str:
743 """[gated by GCO_ENABLE_CONFIG_MANAGEMENT]
745 Set cdk.json bedrock.codex.reasoning_effort.
747 Allowed values are minimal, low, medium, high, and xhigh. The setting
748 applies only to the canonical Codex model; explicit model overrides
749 intentionally omit canonical reasoning.
751 Args:
752 reasoning_effort: Reviewed canonical Codex reasoning effort.
753 """
754 return cli_runner._run_cli(
755 "stacks",
756 "bedrock",
757 "set-codex-reasoning-effort",
758 reasoning_effort,
759 "-y",
760 )