Coverage for gco_mcp/resources/docs.py: 93.61%

371 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-07-30 21:22 +0000

1"""Documentation resources (docs:// scheme) for the GCO MCP server.""" 

2 

3import re 

4from pathlib import Path 

5 

6from cli_runner import PROJECT_ROOT # runtime-resolved checkout root (uvx-safe) 

7from server import mcp 

8 

9DOCS_DIR = PROJECT_ROOT / "docs" 

10EXAMPLES_DIR = PROJECT_ROOT / "examples" 

11ADR_DIR = DOCS_DIR / "adr" 

12 

13# --------------------------------------------------------------------------- 

14# Example metadata — used by both the index and the per-example resource to 

15# give the LLM rich context about what each manifest does and how to adapt it. 

16# --------------------------------------------------------------------------- 

17 

18EXAMPLE_METADATA: dict[str, dict[str, str | list[str]]] = { 

19 "simple-job": { 

20 "category": "Jobs & Training", 

21 "summary": "Basic Kubernetes Job that runs a command and completes. Start here to verify your cluster.", 

22 "gpu": "no", 

23 "opt_in": "", 

24 "submission": "gco jobs submit-sqs examples/simple-job.yaml --region us-east-1", 

25 "keywords": ["simple", "hello", "starter", "basic", "smoke test"], 

26 "instance_types": [], 

27 "use_cases": [ 

28 "verify cluster setup", 

29 "smoke test a new region", 

30 "minimal job example", 

31 ], 

32 "related": ["gpu-job", "sqs-job-submission"], 

33 }, 

34 "gpu-job": { 

35 "category": "Jobs & Training", 

36 "summary": "Requests GPU resources and runs on GPU-enabled nodes.", 

37 "gpu": "NVIDIA", 

38 "opt_in": "", 

39 "submission": "gco jobs submit-sqs examples/gpu-job.yaml --region us-east-1", 

40 "keywords": [ 

41 "gpu", 

42 "nvidia", 

43 "cuda", 

44 "single gpu", 

45 "nvidia.com/gpu", 

46 "g5", 

47 "g6", 

48 "g4dn", 

49 "tolerations", 

50 ], 

51 "instance_types": ["g5.xlarge", "g6.xlarge", "g4dn.xlarge"], 

52 "use_cases": [ 

53 "run a single GPU workload", 

54 "test GPU node provisioning", 

55 "smoke test CUDA", 

56 ], 

57 "related": ["multi-gpu-training", "gpu-timeslicing-job", "simple-job"], 

58 }, 

59 "gpu-timeslicing-job": { 

60 "category": "Jobs & Training", 

61 "summary": "Fractional GPU via NVIDIA time-slicing — multiple pods share one physical GPU.", 

62 "gpu": "NVIDIA (time-sliced)", 

63 "opt_in": "NVIDIA device plugin time-slicing ConfigMap", 

64 "submission": "kubectl apply -f examples/gpu-timeslicing-job.yaml", 

65 "keywords": ["gpu", "timeslicing", "fractional", "shared gpu", "nvidia"], 

66 "instance_types": ["g5.xlarge", "g6.xlarge"], 

67 "use_cases": [ 

68 "share one GPU between multiple pods", 

69 "lower cost for small inference workloads", 

70 ], 

71 "related": ["gpu-job", "multi-gpu-training"], 

72 }, 

73 "multi-gpu-training": { 

74 "category": "Jobs & Training", 

75 "summary": "PyTorch DistributedDataParallel (DDP) across multiple GPUs with indexed pods and headless service.", 

76 "gpu": "NVIDIA", 

77 "opt_in": "", 

78 "submission": "kubectl apply -f examples/multi-gpu-training.yaml", 

79 "keywords": [ 

80 "ddp", 

81 "distributed", 

82 "pytorch", 

83 "multi gpu", 

84 "training", 

85 "torchrun", 

86 "nccl", 

87 "indexed pods", 

88 "headless service", 

89 ], 

90 "instance_types": ["g5.12xlarge", "g6.12xlarge", "p4d.24xlarge"], 

91 "use_cases": [ 

92 "distributed PyTorch DDP training", 

93 "scale a training job across multiple GPUs", 

94 ], 

95 "related": ["gpu-job", "efa-distributed-training", "megatrain-sft-job"], 

96 }, 

97 "efa-distributed-training": { 

98 "category": "Jobs & Training", 

99 "summary": "Elastic Fabric Adapter (EFA) for high-bandwidth inter-node communication (up to 3.2 Tbps on P5, 28.8 Tbps on P6e). For p4d/p5/p5e/p5en/p6-b200/p6-b300/p6e-gb200/trn instances.", 

100 "gpu": "NVIDIA + EFA", 

101 "opt_in": "", 

102 "submission": "gco jobs submit-direct examples/efa-distributed-training.yaml -r us-east-1", 

103 "keywords": ["efa", "elastic fabric adapter", "distributed", "nccl", "high bandwidth"], 

104 "instance_types": [ 

105 "p4d.24xlarge", 

106 "p5.48xlarge", 

107 "trn1.32xlarge", 

108 "trn2.48xlarge", 

109 ], 

110 "use_cases": [ 

111 "multi-node distributed training over EFA", 

112 "high-bandwidth NCCL all-reduce", 

113 "large-scale model pretraining", 

114 ], 

115 "related": ["multi-gpu-training", "trainium-job", "megatrain-sft-job"], 

116 }, 

117 "megatrain-sft-job": { 

118 "category": "Jobs & Training", 

119 "summary": "SFT fine-tuning of Qwen2.5-1.5B on a single GPU using MegaTrain. Downloads weights to EFS.", 

120 "gpu": "NVIDIA", 

121 "opt_in": "", 

122 "submission": "gco jobs submit-direct examples/megatrain-sft-job.yaml -r us-east-1", 

123 "keywords": ["sft", "fine-tuning", "qwen", "megatrain", "llm training"], 

124 "instance_types": ["g5.xlarge", "g5.12xlarge", "g6.xlarge"], 

125 "use_cases": [ 

126 "supervised fine-tuning of an LLM", 

127 "single-GPU SFT on Qwen", 

128 "fine-tune a small open-source model", 

129 ], 

130 "related": ["multi-gpu-training", "model-download-job", "efa-distributed-training"], 

131 }, 

132 "model-download-job": { 

133 "category": "Jobs & Training", 

134 "summary": "Pre-downloads HuggingFace model weights to shared EFS for inference endpoints.", 

135 "gpu": "no", 

136 "opt_in": "", 

137 "submission": "kubectl apply -f examples/model-download-job.yaml", 

138 "keywords": ["huggingface", "download", "weights", "model cache", "efs"], 

139 "instance_types": [], 

140 "use_cases": [ 

141 "stage HuggingFace weights on EFS", 

142 "warm a model cache before serving", 

143 ], 

144 "related": ["megatrain-sft-job", "inference-vllm", "efs-output-job"], 

145 }, 

146 "sqs-job-submission": { 

147 "category": "Jobs & Training", 

148 "summary": "Demonstrates SQS-based submission (recommended). Contains CPU and GPU job examples.", 

149 "gpu": "optional", 

150 "opt_in": "", 

151 "submission": "gco jobs submit-sqs examples/sqs-job-submission.yaml --region us-east-1", 

152 "keywords": ["sqs", "submission", "queue", "broker"], 

153 "instance_types": [], 

154 "use_cases": [ 

155 "submit jobs through the SQS queue", 

156 "queue-based job submission pattern", 

157 ], 

158 "related": ["simple-job", "gpu-job", "keda-scaled-job"], 

159 }, 

160 "trainium-job": { 

161 "category": "Accelerator Jobs", 

162 "summary": "AWS Trainium instance with Neuron SDK. Lower cost than GPU for training.", 

163 "gpu": "Trainium", 

164 "opt_in": "", 

165 "submission": "gco jobs submit examples/trainium-job.yaml --region us-east-1", 

166 "keywords": ["trainium", "neuron", "trn1", "trn2", "training accelerator"], 

167 "instance_types": ["trn1.2xlarge", "trn1.32xlarge", "trn2.48xlarge"], 

168 "use_cases": [ 

169 "lower-cost training on AWS silicon", 

170 "train with the Neuron SDK", 

171 ], 

172 "related": ["inferentia-job", "efa-distributed-training"], 

173 }, 

174 "inferentia-job": { 

175 "category": "Accelerator Jobs", 

176 "summary": "AWS Inferentia2 with Neuron SDK. Optimized for low-cost, high-throughput inference.", 

177 "gpu": "Inferentia", 

178 "opt_in": "", 

179 "submission": "gco jobs submit examples/inferentia-job.yaml --region us-east-1", 

180 "keywords": ["inferentia", "neuron", "inf2", "inference accelerator"], 

181 "instance_types": ["inf2.xlarge", "inf2.8xlarge", "inf2.24xlarge", "inf2.48xlarge"], 

182 "use_cases": [ 

183 "low-cost inference on AWS silicon", 

184 "high-throughput batch inference", 

185 ], 

186 "related": ["trainium-job", "inference-vllm"], 

187 }, 

188 "inference-vllm": { 

189 "category": "Inference Serving", 

190 "summary": "vLLM OpenAI-compatible LLM serving with PagedAttention.", 

191 "gpu": "NVIDIA", 

192 "opt_in": "", 

193 "submission": "gco inference deploy my-llm -i vllm/vllm-openai:v0.24.0 --gpu-count 1", 

194 "keywords": [ 

195 "vllm", 

196 "openai", 

197 "openai-compatible", 

198 "llm serving", 

199 "pagedattention", 

200 "inference", 

201 "completions", 

202 "chat completions", 

203 "v1/chat/completions", 

204 "model server", 

205 "llama", 

206 "qwen", 

207 "mistral", 

208 ], 

209 "instance_types": ["g5.xlarge", "g5.12xlarge", "g6.xlarge"], 

210 "use_cases": [ 

211 "serve an LLM with an OpenAI-compatible API", 

212 "high-throughput LLM inference", 

213 "deploy a chat completions endpoint", 

214 ], 

215 "related": ["inference-tgi", "inference-sglang", "inference-triton", "model-download-job"], 

216 }, 

217 "inference-tgi": { 

218 "category": "Inference Serving", 

219 "summary": "HuggingFace Text Generation Inference — optimized transformer serving.", 

220 "gpu": "NVIDIA", 

221 "opt_in": "", 

222 "submission": "gco jobs submit-direct examples/inference-tgi.yaml -r us-east-1", 

223 "keywords": ["tgi", "huggingface", "text generation", "llm serving"], 

224 "instance_types": ["g5.xlarge", "g5.12xlarge", "g6.xlarge"], 

225 "use_cases": [ 

226 "serve HuggingFace LLMs with TGI", 

227 "transformer text-generation endpoint", 

228 ], 

229 "related": ["inference-vllm", "inference-sglang", "inference-torchserve"], 

230 }, 

231 "inference-triton": { 

232 "category": "Inference Serving", 

233 "summary": "NVIDIA Triton Inference Server — multi-framework (PyTorch, TensorFlow, ONNX).", 

234 "gpu": "NVIDIA", 

235 "opt_in": "", 

236 "submission": "gco jobs submit-direct examples/inference-triton.yaml -r us-east-1", 

237 "keywords": ["triton", "nvidia", "multi-framework", "onnx", "tensorflow", "inference"], 

238 "instance_types": ["g5.xlarge", "g6.xlarge"], 

239 "use_cases": [ 

240 "multi-framework inference serving", 

241 "serve ONNX or TensorFlow models", 

242 ], 

243 "related": ["inference-vllm", "inference-torchserve"], 

244 }, 

245 "inference-torchserve": { 

246 "category": "Inference Serving", 

247 "summary": "PyTorch TorchServe model serving.", 

248 "gpu": "NVIDIA", 

249 "opt_in": "", 

250 "submission": "gco jobs submit-direct examples/inference-torchserve.yaml -r us-east-1", 

251 "keywords": ["torchserve", "pytorch", "model serving"], 

252 "instance_types": ["g5.xlarge", "g6.xlarge"], 

253 "use_cases": [ 

254 "serve a PyTorch model with TorchServe", 

255 ], 

256 "related": ["inference-triton", "inference-vllm"], 

257 }, 

258 "inference-sglang": { 

259 "category": "Inference Serving", 

260 "summary": "SGLang high-throughput serving with RadixAttention for prefix caching.", 

261 "gpu": "NVIDIA", 

262 "opt_in": "", 

263 "submission": "gco jobs submit-direct examples/inference-sglang.yaml -r us-east-1", 

264 "keywords": ["sglang", "radixattention", "prefix caching", "llm serving"], 

265 "instance_types": ["g5.xlarge", "g5.12xlarge"], 

266 "use_cases": [ 

267 "high-throughput LLM serving with prefix caching", 

268 "serve LLMs with structured output", 

269 ], 

270 "related": ["inference-vllm", "inference-tgi"], 

271 }, 

272 "efs-output-job": { 

273 "category": "Storage & Persistence", 

274 "summary": "Writes output to shared EFS storage. Results persist after pod termination.", 

275 "gpu": "no", 

276 "opt_in": "", 

277 "submission": "gco jobs submit-direct examples/efs-output-job.yaml --region us-east-1 -n gco-jobs", 

278 "keywords": ["efs", "shared storage", "persistent", "output"], 

279 "instance_types": [], 

280 "use_cases": [ 

281 "persist job output to EFS", 

282 "share data between pods via EFS", 

283 ], 

284 "related": ["fsx-lustre-job", "model-download-job", "cluster-shared-bucket-upload-job"], 

285 }, 

286 "fsx-lustre-job": { 

287 "category": "Storage & Persistence", 

288 "summary": "FSx for Lustre high-performance parallel storage (1000+ GB/s throughput).", 

289 "gpu": "no", 

290 "opt_in": "FSx (gco stacks fsx enable -y)", 

291 "submission": "gco jobs submit-direct examples/fsx-lustre-job.yaml --region us-east-1 -n gco-jobs", 

292 "keywords": ["fsx", "lustre", "parallel storage", "high throughput", "hpc"], 

293 "instance_types": [], 

294 "use_cases": [ 

295 "high-throughput parallel storage for training", 

296 "stream large datasets to GPU nodes", 

297 ], 

298 "related": ["efs-output-job", "multi-gpu-training", "efa-distributed-training"], 

299 }, 

300 "valkey-cache-job": { 

301 "category": "Caching & Databases", 

302 "summary": "Valkey Serverless cache for K/V caching, prompt caching, session state, feature stores.", 

303 "gpu": "no", 

304 "opt_in": 'Valkey ("valkey": {"enabled": true} in cdk.json)', 

305 "submission": "gco jobs submit-direct examples/valkey-cache-job.yaml -r us-east-1", 

306 "keywords": ["valkey", "redis", "cache", "kv store", "session state"], 

307 "instance_types": [], 

308 "use_cases": [ 

309 "cache prompts or session state", 

310 "use Valkey from a job", 

311 "feature store backed by Valkey", 

312 ], 

313 "related": ["aurora-pgvector-job"], 

314 }, 

315 "aurora-pgvector-job": { 

316 "category": "Caching & Databases", 

317 "summary": "Aurora Serverless v2 PostgreSQL with pgvector for RAG and semantic search.", 

318 "gpu": "no", 

319 "opt_in": 'Aurora ("aurora_pgvector": {"enabled": true} in cdk.json)', 

320 "submission": "gco jobs submit-direct examples/aurora-pgvector-job.yaml -r us-east-1", 

321 "keywords": ["aurora", "pgvector", "postgres", "rag", "vector database", "embeddings"], 

322 "instance_types": [], 

323 "use_cases": [ 

324 "RAG with pgvector", 

325 "semantic search backed by Postgres", 

326 "store embeddings in Aurora", 

327 ], 

328 "related": ["valkey-cache-job", "analytics-database-export-job"], 

329 }, 

330 "cluster-shared-bucket-upload-job": { 

331 "category": "Storage & Persistence", 

332 "summary": "Uploads a file to the always-on Cluster_Shared_Bucket using the gco-cluster-shared-bucket ConfigMap via envFrom. Works with analytics disabled.", 

333 "gpu": "no", 

334 "opt_in": "", 

335 "submission": "gco jobs submit-direct examples/cluster-shared-bucket-upload-job.yaml -r us-east-1", 

336 "keywords": ["s3", "shared bucket", "upload", "configmap"], 

337 "instance_types": [], 

338 "use_cases": [ 

339 "upload artifacts to the shared S3 bucket", 

340 "share files across regions via S3", 

341 ], 

342 "related": ["efs-output-job", "analytics-s3-upload-job"], 

343 }, 

344 "analytics-s3-upload-job": { 

345 "category": "Analytics", 

346 "summary": "Publishes a dataset snapshot plus schema manifest to Cluster_Shared_Bucket under analytics-data/ so a SageMaker Studio notebook can read it.", 

347 "gpu": "no", 

348 "opt_in": 'Analytics ("analytics_environment": {"enabled": true} in cdk.json)', 

349 "submission": "gco jobs submit-direct examples/analytics-s3-upload-job.yaml -r us-east-1", 

350 "keywords": ["analytics", "s3", "sagemaker", "dataset", "schema"], 

351 "instance_types": [], 

352 "use_cases": [ 

353 "publish a dataset for a SageMaker Studio notebook", 

354 "share an analytics snapshot via S3", 

355 ], 

356 "related": ["analytics-database-export-job", "cluster-shared-bucket-upload-job"], 

357 }, 

358 "analytics-database-export-job": { 

359 "category": "Analytics", 

360 "summary": "Exports rows from the regional Aurora pgvector cluster to Cluster_Shared_Bucket as CSV for a SageMaker Studio notebook to analyse.", 

361 "gpu": "no", 

362 "opt_in": 'Aurora + Analytics ("aurora_pgvector.enabled" and "analytics_environment.enabled" in cdk.json)', 

363 "submission": "gco jobs submit-direct examples/analytics-database-export-job.yaml -r us-east-1", 

364 "keywords": ["analytics", "aurora", "csv", "export", "sagemaker"], 

365 "instance_types": [], 

366 "use_cases": [ 

367 "export Aurora rows to S3 as CSV", 

368 "feed a SageMaker Studio notebook from Postgres", 

369 ], 

370 "related": ["aurora-pgvector-job", "analytics-s3-upload-job"], 

371 }, 

372 "volcano-gang-job": { 

373 "category": "Schedulers", 

374 "summary": "Volcano gang scheduling — all pods scheduled together or none. Master + workers topology.", 

375 "gpu": "no", 

376 "opt_in": "", 

377 "submission": "kubectl apply -f examples/volcano-gang-job.yaml", 

378 "keywords": ["volcano", "gang scheduling", "batch", "scheduler"], 

379 "instance_types": [], 

380 "use_cases": [ 

381 "schedule all pods at once or none", 

382 "MPI-style master + workers topology", 

383 ], 

384 "related": ["kueue-job", "yunikorn-job", "slurm-cluster-job"], 

385 }, 

386 "kueue-job": { 

387 "category": "Schedulers", 

388 "summary": "Kueue job queueing with ClusterQueue, LocalQueue, ResourceFlavors, and fair-sharing.", 

389 "gpu": "optional", 

390 "opt_in": "", 

391 "submission": "kubectl apply -f examples/kueue-job.yaml", 

392 "keywords": ["kueue", "queueing", "fair sharing", "scheduler", "clusterqueue"], 

393 "instance_types": [], 

394 "use_cases": [ 

395 "queue jobs with quotas and fair sharing", 

396 "multi-tenant batch scheduling", 

397 ], 

398 "related": ["volcano-gang-job", "yunikorn-job"], 

399 }, 

400 "yunikorn-job": { 

401 "category": "Schedulers", 

402 "summary": "Apache YuniKorn app-aware scheduling with hierarchical queues and gang scheduling.", 

403 "gpu": "no", 

404 "opt_in": 'YuniKorn ("helm": {"yunikorn": {"enabled": true}} in cdk.json)', 

405 "submission": "kubectl apply -f examples/yunikorn-job.yaml", 

406 "keywords": ["yunikorn", "scheduler", "hierarchical queues", "gang scheduling"], 

407 "instance_types": [], 

408 "use_cases": [ 

409 "app-aware scheduling with hierarchical queues", 

410 "YuniKorn-style gang scheduling", 

411 ], 

412 "related": ["kueue-job", "volcano-gang-job"], 

413 }, 

414 "keda-scaled-job": { 

415 "category": "Schedulers", 

416 "summary": "KEDA ScaledJob — custom SQS-triggered autoscaling. Template for custom consumers.", 

417 "gpu": "no", 

418 "opt_in": "", 

419 "submission": "kubectl apply -f examples/keda-scaled-job.yaml", 

420 "keywords": ["keda", "scaledjob", "autoscaling", "sqs", "event driven"], 

421 "instance_types": [], 

422 "use_cases": [ 

423 "scale jobs from SQS queue depth", 

424 "event-driven job autoscaling", 

425 ], 

426 "related": ["sqs-job-submission"], 

427 }, 

428 "slurm-cluster-job": { 

429 "category": "Schedulers", 

430 "summary": "Slinky Slurm Operator — sbatch submission on Kubernetes for HPC workloads.", 

431 "gpu": "no", 

432 "opt_in": 'Slurm ("helm": {"slurm": {"enabled": true}} in cdk.json)', 

433 "submission": "kubectl apply -f examples/slurm-cluster-job.yaml", 

434 "keywords": ["slurm", "hpc", "sbatch", "slinky"], 

435 "instance_types": [], 

436 "use_cases": [ 

437 "submit sbatch jobs on Kubernetes", 

438 "run HPC workloads with Slurm", 

439 ], 

440 "related": ["volcano-gang-job"], 

441 }, 

442 "ray-cluster": { 

443 "category": "Distributed Computing", 

444 "summary": "KubeRay RayCluster for distributed training, tuning, and serving. Auto-scaling workers.", 

445 "gpu": "no", 

446 "opt_in": "", 

447 "submission": "kubectl apply -f examples/ray-cluster.yaml", 

448 "keywords": ["ray", "kuberay", "distributed", "tune", "serve"], 

449 "instance_types": [], 

450 "use_cases": [ 

451 "stand up a Ray cluster on EKS", 

452 "distributed training and tuning with Ray", 

453 ], 

454 "related": ["multi-gpu-training", "pipeline-dag"], 

455 }, 

456 "pipeline-dag": { 

457 "category": "DAG Pipelines", 

458 "summary": "Multi-step pipeline with dependency ordering. Preprocess → Train via shared EFS.", 

459 "gpu": "no", 

460 "opt_in": "", 

461 "submission": "gco dag run examples/pipeline-dag.yaml -r us-east-1", 

462 "keywords": ["dag", "pipeline", "workflow", "dependencies"], 

463 "instance_types": [], 

464 "use_cases": [ 

465 "run a multi-step ML pipeline", 

466 "chain preprocess and train jobs", 

467 ], 

468 "related": ["dag-step-preprocess", "dag-step-train", "ray-cluster"], 

469 }, 

470 "dag-step-preprocess": { 

471 "category": "DAG Pipelines", 

472 "summary": "DAG step 1: generates training data on shared EFS.", 

473 "gpu": "no", 

474 "opt_in": "", 

475 "submission": "(used by pipeline-dag.yaml)", 

476 "keywords": ["dag", "preprocess", "step", "pipeline"], 

477 "instance_types": [], 

478 "use_cases": [ 

479 "preprocessing step of a pipeline", 

480 "generate training data for a downstream step", 

481 ], 

482 "related": ["pipeline-dag", "dag-step-train"], 

483 }, 

484 "dag-step-train": { 

485 "category": "DAG Pipelines", 

486 "summary": "DAG step 2: reads preprocess output, trains model, writes artifacts to EFS.", 

487 "gpu": "no", 

488 "opt_in": "", 

489 "submission": "(used by pipeline-dag.yaml)", 

490 "keywords": ["dag", "train", "step", "pipeline"], 

491 "instance_types": [], 

492 "use_cases": [ 

493 "training step of a pipeline", 

494 "consume preprocess output and train", 

495 ], 

496 "related": ["pipeline-dag", "dag-step-preprocess"], 

497 }, 

498} 

499 

500 

501# --------------------------------------------------------------------------- 

502# Doc metadata — used by ``find_docs`` and the docs:// discovery resources to 

503# describe every markdown file under ``docs/``. Indexed by basename without 

504# extension (e.g. ``ARCHITECTURE``). The vocabulary in ``topics`` is kept 

505# small and consistent so topic-based search across docs stays predictable. 

506# --------------------------------------------------------------------------- 

507 

508DOC_METADATA: dict[str, dict[str, str | list[str]]] = { 

509 "ANALYTICS": { 

510 "summary": "Optional SageMaker Studio + EMR Serverless analytics environment, enabled via a single cdk.json toggle.", 

511 "topics": ["analytics", "storage", "customization", "gpu"], 

512 "keywords": [ 

513 "sagemaker studio", 

514 "emr serverless", 

515 "cognito", 

516 "data science", 

517 "notebook", 

518 "presigned url", 

519 "studio domain", 

520 "analytics environment", 

521 "user pool", 

522 ], 

523 "related": ["CLUSTER_SHARED_BUCKET", "CUSTOMIZATION"], 

524 }, 

525 "API": { 

526 "summary": ( 

527 "Reference for every GCO HTTP surface: the control plane (manifests, " 

528 "jobs, queue, templates, webhooks, cost), cross-region aggregation, " 

529 "inference, health and observability, plus which paths each API " 

530 "Gateway exposes and the cluster-internal surfaces." 

531 ), 

532 "topics": ["api", "cli", "jobs", "inference", "webhooks", "templates"], 

533 "keywords": [ 

534 "rest", 

535 "manifest processor", 

536 "inference proxy", 

537 "health monitor", 

538 "endpoints", 

539 "auth", 

540 "hmac request envelope", 

541 "api gateway", 

542 "sigv4", 

543 "openapi", 

544 "submit job", 

545 "api surface", 

546 ], 

547 "related": ["CLI", "ARCHITECTURE"], 

548 }, 

549 "ARCHITECTURE": { 

550 "summary": "Deep dive into the multi-region infrastructure, security layers, data flow, and scale characteristics.", 

551 "topics": [ 

552 "architecture", 

553 "concepts", 

554 "security", 

555 "multi-region", 

556 "eks", 

557 "capacity", 

558 "inference", 

559 "gpu", 

560 "monitoring", 

561 "deployment", 

562 "nodepools", 

563 "storage", 

564 "images", 

565 "cost", 

566 "networking", 

567 ], 

568 "keywords": [ 

569 "multi-region", 

570 "eks", 

571 "vpc", 

572 "global accelerator", 

573 "data flow", 

574 "control plane", 

575 "data plane", 

576 "regional stack", 

577 "global stack", 

578 "iam", 

579 "kms", 

580 "high level design", 

581 "blast radius", 

582 ], 

583 "related": ["CONCEPTS", "CUSTOMIZATION", "API"], 

584 }, 

585 "CLI": { 

586 "summary": "Complete command-line interface reference for the gco CLI across jobs, queues, stacks, capacity, inference, and more.", 

587 "topics": [ 

588 "cli", 

589 "api", 

590 "jobs", 

591 "capacity", 

592 "inference", 

593 "cost", 

594 "gpu", 

595 "multi-region", 

596 "images", 

597 "nodepools", 

598 "deployment", 

599 ], 

600 "keywords": [ 

601 "gco", 

602 "command-line", 

603 "subcommand", 

604 "submit job", 

605 "stacks deploy", 

606 "stacks destroy", 

607 "capacity status", 

608 "ai_recommend", 

609 "reserve_capacity", 

610 "images build", 

611 "models upload", 

612 ], 

613 "related": ["API", "RUNBOOKS"], 

614 }, 

615 "CLUSTER_SHARED_BUCKET": { 

616 "summary": "Reference for the always-on Cluster_Shared_Bucket — the S3 bucket every regional cluster can read and write by default.", 

617 "topics": ["storage", "concepts", "multi-region", "security"], 

618 "keywords": [ 

619 "s3", 

620 "shared bucket", 

621 "cross-region", 

622 "configmap", 

623 "envFrom", 

624 "kms", 

625 "iam grant", 

626 "bucket policy", 

627 "always-on", 

628 ], 

629 "related": ["ANALYTICS", "ARCHITECTURE"], 

630 }, 

631 "CONCEPTS": { 

632 "summary": "Fundamental concepts behind GCO — what it is, the problems it solves, and how the key components fit together.", 

633 "topics": [ 

634 "concepts", 

635 "architecture", 

636 "multi-region", 

637 "capacity", 

638 "gpu", 

639 "eks", 

640 "jobs", 

641 "inference", 

642 ], 

643 "keywords": [ 

644 "what is gco", 

645 "fundamentals", 

646 "components", 

647 "global queue", 

648 "capacity orchestration", 

649 "ai/ml workloads", 

650 "gpu allocation", 

651 "regional clusters", 

652 ], 

653 "related": ["ARCHITECTURE", "README"], 

654 }, 

655 "COST_MONITORING": { 

656 "summary": "Cost monitoring and cost-aware scheduling — per-region OpenCost with a Grafana cost dashboard, scheduled Parquet cost reports to S3, cross-region Athena analytics, the /api/v1/cost API, and spot price-gated central-queue dispatch.", 

657 "topics": [ 

658 "cost", 

659 "monitoring", 

660 "observability", 

661 "queue", 

662 "customization", 

663 "api", 

664 "cli", 

665 ], 

666 "keywords": [ 

667 "opencost", 

668 "cost allocation", 

669 "athena", 

670 "glue", 

671 "parquet", 

672 "cost reports", 

673 "cost dashboard", 

674 "spot price", 

675 "max spot price", 

676 "cost-aware scheduling", 

677 "namespace cost", 

678 "finops", 

679 ], 

680 "related": ["MONITORING", "CUSTOMIZATION", "API"], 

681 }, 

682 "CUSTOMIZATION": { 

683 "summary": "How to customize GCO — deployment regions, EKS configuration, GPU nodepools, and more.", 

684 "topics": [ 

685 "customization", 

686 "architecture", 

687 "gpu", 

688 "eks", 

689 "nodepools", 

690 "storage", 

691 "multi-region", 

692 "deployment", 

693 ], 

694 "keywords": [ 

695 "cdk.json", 

696 "regions", 

697 "addons", 

698 "instance types", 

699 "fsx", 

700 "valkey", 

701 "aurora", 

702 "feature toggles", 

703 "queue processor", 

704 "helm charts", 

705 "image registry config", 

706 ], 

707 "related": ["ARCHITECTURE", "ANALYTICS"], 

708 }, 

709 "FORKING": { 

710 "summary": ( 

711 "Take GCO into your own repository: repoint badges, clone URLs, the " 

712 "GitHub Pages site, and the OIDC trust-policy subject with " 

713 "scripts/migrate_fork.py, plus the follow-ups it cannot decide." 

714 ), 

715 "topics": ["forking", "migration", "repository", "ci", "oidc"], 

716 "keywords": [ 

717 "fork", 

718 "migrate", 

719 "own repository", 

720 "rename repo", 

721 "badges", 

722 "oidc trust policy", 

723 "github pages", 

724 "codeowners", 

725 "solution id", 

726 "upstream remote", 

727 ], 

728 "related": ["CUSTOMIZATION", "MAINTENANCE"], 

729 }, 

730 "IMAGE_MIRROR": { 

731 "summary": "Mirror third-party container images (chiefly Volcano's docker.io images) into the project's gco/* ECR so the cluster pulls from same-account ECR instead of a rate-limited upstream.", 

732 "topics": ["images", "customization", "deployment", "eks", "schedulers"], 

733 "keywords": [ 

734 "ecr", 

735 "mirror", 

736 "docker hub", 

737 "docker.io", 

738 "volcano", 

739 "pull-through cache", 

740 "multi-arch", 

741 "image_registry", 

742 "rate limit", 

743 "skopeo", 

744 "buildx", 

745 ], 

746 "related": ["VOLCANO", "CUSTOMIZATION"], 

747 }, 

748 "INFERENCE": { 

749 "summary": "Deploy and manage multi-region GPU inference endpoints, including model weight management and supported frameworks.", 

750 "topics": [ 

751 "inference", 

752 "architecture", 

753 "gpu", 

754 "multi-region", 

755 "cost", 

756 "images", 

757 "monitoring", 

758 ], 

759 "keywords": [ 

760 "vllm", 

761 "tgi", 

762 "triton", 

763 "torchserve", 

764 "sglang", 

765 "endpoints", 

766 "canary", 

767 "rolling update", 

768 "model weights", 

769 "global accelerator", 

770 "openai-compatible", 

771 "inference monitor", 

772 ], 

773 "related": ["ARCHITECTURE", "RUNBOOKS"], 

774 }, 

775 "KEDA": { 

776 "summary": "KEDA event-driven autoscaling integration — scales workloads from external sources like SQS, Kafka, and Prometheus.", 

777 "topics": ["schedulers", "jobs", "autoscaling"], 

778 "keywords": [ 

779 "keda", 

780 "scaledjob", 

781 "scaledobject", 

782 "sqs trigger", 

783 "event-driven", 

784 "kafka", 

785 "prometheus", 

786 "queue depth", 

787 ], 

788 "related": ["SCHEDULERS", "VOLCANO"], 

789 }, 

790 "KUBERAY": { 

791 "summary": "KubeRay operator integration — runs Ray distributed computing workloads on Kubernetes for training, tuning, and serving.", 

792 "topics": ["schedulers", "jobs", "gpu", "training", "distributed"], 

793 "keywords": [ 

794 "kuberay", 

795 "ray", 

796 "raycluster", 

797 "rayjob", 

798 "rayservice", 

799 "ray tune", 

800 "ray train", 

801 "ray serve", 

802 "distributed", 

803 ], 

804 "related": ["SCHEDULERS", "VOLCANO"], 

805 }, 

806 "KUEUE": { 

807 "summary": "Kueue integration for Kubernetes-native job queueing with resource quotas, fair sharing, and priority scheduling.", 

808 "topics": ["schedulers", "jobs"], 

809 "keywords": [ 

810 "kueue", 

811 "clusterqueue", 

812 "localqueue", 

813 "resourceflavor", 

814 "quota", 

815 "fair sharing", 

816 "priority", 

817 "preemption", 

818 ], 

819 "related": ["SCHEDULERS", "VOLCANO", "YUNIKORN"], 

820 }, 

821 "LEARNING_PATH": { 

822 "summary": "Staged, hands-on onboarding path for users new to GCO or Kubernetes — a Kubernetes primer, cost-boundary milestones, and role-based tracks that sequence the other guides.", 

823 "topics": [ 

824 "concepts", 

825 "quickstart", 

826 "cli", 

827 "jobs", 

828 "inference", 

829 "schedulers", 

830 ], 

831 "keywords": [ 

832 "learning path", 

833 "onboarding", 

834 "getting started", 

835 "tutorial", 

836 "curriculum", 

837 "new user", 

838 "beginner", 

839 "kubernetes", 

840 "first job", 

841 "role based", 

842 ], 

843 "related": ["CONCEPTS", "CLI", "SCHEDULERS"], 

844 }, 

845 "LIVE_RELEASE_VALIDATION": { 

846 "summary": "Local operator deploy-test-destroy harness for exact-commit live validation, guaranteed cleanup, and manually attached pull request reports.", 

847 "topics": [ 

848 "deployment", 

849 "runbooks", 

850 "security", 

851 "automation", 

852 "multi-region", 

853 ], 

854 "keywords": [ 

855 "live release validation", 

856 "local script", 

857 "dedicated validation account", 

858 "exact commit", 

859 "deploy test destroy", 

860 "checkpoint", 

861 "resume", 

862 "kms pending deletion", 

863 "manual pull request upload", 

864 "pull request comment", 

865 ], 

866 "related": ["RUNBOOKS", "MAINTENANCE", "CLI"], 

867 }, 

868 "MAINTENANCE": { 

869 "summary": "Routine upkeep — adding instance types to the nodepool lists, EKS Kubernetes version upgrades, base-image security-epoch refreshes, CVE-suppression renewals, and acting on the monthly dependency scan.", 

870 "topics": [ 

871 "customization", 

872 "deployment", 

873 "eks", 

874 "nodepools", 

875 "images", 

876 ], 

877 "keywords": [ 

878 "maintenance", 

879 "upkeep", 

880 "upgrade", 

881 "eks version", 

882 "kubernetes version", 

883 "instance types", 

884 "instance family", 

885 "dependency scan", 

886 "deps-scan", 

887 "security epoch", 

888 "cve suppression", 

889 "kubectl", 

890 "helm", 

891 "addon versions", 

892 "requirements-lock", 

893 ], 

894 "related": ["CUSTOMIZATION", "RUNBOOKS", "ARCHITECTURE"], 

895 }, 

896 "MISSION": { 

897 "summary": "Goal-directed iteration loop — declare a directive, criteria, tool allowlist, and budget; runs deterministic five-phase iterations until a verdict.", 

898 "topics": [ 

899 "concepts", 

900 "cli", 

901 "api", 

902 "automation", 

903 "feature-flags", 

904 ], 

905 "keywords": [ 

906 "mission", 

907 "directive", 

908 "criteria", 

909 "verdict", 

910 "iteration", 

911 "goal directed", 

912 "autonomous loop", 

913 "sampling", 

914 "sandbox", 

915 "predicate", 

916 "checkpoint", 

917 "budget", 

918 "final report", 

919 ], 

920 "related": ["CLI", "ARCHITECTURE", "RUNBOOKS"], 

921 }, 

922 "MONITORING": { 

923 "summary": "Self-hosted per-cluster observability (kube-prometheus-stack: Prometheus + Alertmanager + Grafana), on by default, with private port-forward access and the gco monitoring CLI.", 

924 "topics": [ 

925 "monitoring", 

926 "observability", 

927 "gpu", 

928 "schedulers", 

929 "cli", 

930 "concepts", 

931 "cost", 

932 ], 

933 "keywords": [ 

934 "prometheus", 

935 "grafana", 

936 "alertmanager", 

937 "kube-prometheus-stack", 

938 "dcgm", 

939 "servicemonitor", 

940 "podmonitor", 

941 "dashboards", 

942 "port-forward", 

943 "ssm tunnel", 

944 "gco monitoring", 

945 "credential rotation", 

946 "cluster observability", 

947 ], 

948 "related": ["ARCHITECTURE", "RUNBOOKS", "CLI", "INFERENCE"], 

949 }, 

950 "README": { 

951 "summary": "Documentation index — the top-level guide map for the rest of the docs/ tree.", 

952 "topics": ["concepts", "multi-region", "gpu", "capacity", "inference", "quickstart"], 

953 "keywords": [ 

954 "index", 

955 "overview", 

956 "guide map", 

957 "documentation", 

958 "table of contents", 

959 "getting started", 

960 ], 

961 "related": ["CONCEPTS", "ARCHITECTURE"], 

962 }, 

963 "RUNBOOKS": { 

964 "summary": "Operational runbooks — step-by-step procedures for common operational scenarios with symptoms, diagnosis, and resolution.", 

965 "topics": [ 

966 "runbooks", 

967 "troubleshooting", 

968 "jobs", 

969 "inference", 

970 "capacity", 

971 "monitoring", 

972 "deployment", 

973 ], 

974 "keywords": [ 

975 "incident response", 

976 "operational procedures", 

977 "stuck job", 

978 "endpoint down", 

979 "capacity exhausted", 

980 "stack rollback", 

981 "playbook", 

982 "diagnose", 

983 "remediation", 

984 ], 

985 "related": ["TROUBLESHOOTING", "CLI"], 

986 }, 

987 "SCHEDULERS": { 

988 "summary": "Comparison and overview of the six supported scheduling and orchestration tools — Volcano, Kueue, KubeRay, KEDA, Slurm, YuniKorn.", 

989 "topics": ["schedulers", "concepts", "jobs", "gpu"], 

990 "keywords": [ 

991 "volcano", 

992 "kueue", 

993 "kuberay", 

994 "keda", 

995 "slurm", 

996 "yunikorn", 

997 "gang scheduling", 

998 "batch scheduler", 

999 "scheduler comparison", 

1000 "queueing", 

1001 ], 

1002 "related": ["VOLCANO", "KUEUE", "KUBERAY"], 

1003 }, 

1004 "SLURM_OPERATOR": { 

1005 "summary": "Slinky Slurm Operator integration — runs sbatch, srun, and salloc inside an EKS cluster for HPC workflows.", 

1006 "topics": ["schedulers", "jobs", "hpc"], 

1007 "keywords": [ 

1008 "slurm", 

1009 "slinky", 

1010 "sbatch", 

1011 "srun", 

1012 "salloc", 

1013 "hpc", 

1014 "scientific computing", 

1015 "mpi", 

1016 ], 

1017 "related": ["SCHEDULERS", "VOLCANO"], 

1018 }, 

1019 "TROUBLESHOOTING": { 

1020 "summary": "Troubleshooting guide — common installation, deployment, kubectl, and pod issues with their resolutions.", 

1021 "topics": [ 

1022 "troubleshooting", 

1023 "runbooks", 

1024 "deployment", 

1025 "eks", 

1026 "jobs", 

1027 "inference", 

1028 "capacity", 

1029 ], 

1030 "keywords": [ 

1031 "kubectl", 

1032 "pod crashloop", 

1033 "imagepullbackoff", 

1034 "stack rollback", 

1035 "deployment failed", 

1036 "credentials", 

1037 "vpc", 

1038 "nodepool not scaling", 

1039 "common errors", 

1040 "fix", 

1041 ], 

1042 "related": ["RUNBOOKS", "CLI"], 

1043 }, 

1044 "VOLCANO": { 

1045 "summary": "Volcano batch scheduler integration — gang scheduling, fair-share queuing, and job lifecycle management for AI/ML and HPC.", 

1046 "topics": ["schedulers", "jobs", "gpu", "hpc"], 

1047 "keywords": [ 

1048 "volcano", 

1049 "gang scheduling", 

1050 "vcjob", 

1051 "podgroup", 

1052 "queue", 

1053 "fair share", 

1054 "job lifecycle", 

1055 "batch", 

1056 ], 

1057 "related": ["SCHEDULERS", "KUEUE", "YUNIKORN"], 

1058 }, 

1059 "YUNIKORN": { 

1060 "summary": "Apache YuniKorn integration — multi-tenant scheduler with hierarchical queues and gang scheduling.", 

1061 "topics": ["schedulers", "jobs"], 

1062 "keywords": [ 

1063 "yunikorn", 

1064 "hierarchical queues", 

1065 "multi-tenant", 

1066 "gang scheduling", 

1067 "app-aware scheduling", 

1068 "fair share", 

1069 ], 

1070 "related": ["SCHEDULERS", "KUEUE", "VOLCANO"], 

1071 }, 

1072} 

1073 

1074 

1075# --------------------------------------------------------------------------- 

1076# Root-doc metadata — searchable project-level guidance that deliberately lives 

1077# at the repository root rather than under ``docs/``. Keep this separate from 

1078# ``DOC_METADATA`` so the strict 1:1 metadata-to-``docs/*.md`` invariant remains 

1079# meaningful. Root docs use static ``docs://gco/{name}`` resources. 

1080# --------------------------------------------------------------------------- 

1081 

1082ROOT_DOC_METADATA: dict[str, dict[str, str | list[str]]] = { 

1083 "TENETS": { 

1084 "path": "TENETS.md", 

1085 "summary": ( 

1086 "Prioritized project tenets and north-star guidance for safety, truth, " 

1087 "security, global capacity orchestration, automation, operations, cost, " 

1088 "and maintainability." 

1089 ), 

1090 "topics": [ 

1091 "concepts", 

1092 "architecture", 

1093 "security", 

1094 "automation", 

1095 "multi-region", 

1096 "deployment", 

1097 "cost", 

1098 ], 

1099 "keywords": [ 

1100 "tenets", 

1101 "north star", 

1102 "principles", 

1103 "decision framework", 

1104 "project ethos", 

1105 "safety", 

1106 "truth", 

1107 "reversibility", 

1108 "least privilege", 

1109 "capacity policy", 

1110 "deterministic automation", 

1111 "definition of done", 

1112 ], 

1113 "related": ["ARCHITECTURE", "MAINTENANCE", "LIVE_RELEASE_VALIDATION"], 

1114 } 

1115} 

1116 

1117 

1118# --------------------------------------------------------------------------- 

1119# Package-doc metadata — used by ``find_docs`` and the 

1120# ``docs://gco/packages/...`` resources to describe the package-level READMEs 

1121# that live next to the code (under ``gco_mcp/``) rather than in ``docs/``. These 

1122# are developer-facing internals guides (how a package is structured and how to 

1123# customize it), kept in a catalog separate from ``DOC_METADATA`` so the strict 

1124# 1:1 ``docs/*.md`` invariant stays intact. Each entry is keyed by a stable 

1125# slug and carries a ``path`` relative to the project root. A ``related`` entry 

1126# may reference either another package slug or a ``DOC_METADATA`` key. 

1127# --------------------------------------------------------------------------- 

1128 

1129PACKAGE_DOC_METADATA: dict[str, dict[str, str | list[str]]] = { 

1130 "mcp-server": { 

1131 "path": "gco_mcp/README.md", 

1132 "summary": "GCO MCP server guide — setup across MCP clients, feature-flag gating, and the full tool and resource catalog.", 

1133 "topics": ["mcp", "concepts", "feature-flags", "customization"], 

1134 "keywords": [ 

1135 "mcp server", 

1136 "fastmcp", 

1137 "stdio", 

1138 "feature flags", 

1139 "gco_enable", 

1140 "kiro", 

1141 "claude desktop", 

1142 "cursor", 

1143 "tool search", 

1144 "available tools", 

1145 "resources", 

1146 ], 

1147 "related": ["mcp-tools", "mcp-resources", "CLI", "MISSION"], 

1148 }, 

1149 "mcp-tools": { 

1150 "path": "gco_mcp/tools/README.md", 

1151 "summary": "How MCP tools are defined — one module per domain, the @mcp.tool + audit_logged pattern, and how to add a new tool.", 

1152 "topics": ["mcp", "customization"], 

1153 "keywords": [ 

1154 "tool", 

1155 "mcp.tool", 

1156 "audit_logged", 

1157 "cli_runner", 

1158 "adding a tool", 

1159 "tool module", 

1160 "domain", 

1161 ], 

1162 "related": ["mcp-server", "mcp-resources"], 

1163 }, 

1164 "mcp-resources": { 

1165 "path": "gco_mcp/resources/README.md", 

1166 "summary": "MCP resource modules by URI scheme (docs://, source://, k8s://, …) and how to add a new resource group.", 

1167 "topics": ["mcp", "customization"], 

1168 "keywords": [ 

1169 "resource", 

1170 "mcp.resource", 

1171 "uri scheme", 

1172 "docs scheme", 

1173 "source scheme", 

1174 "resource index", 

1175 "adding a resource", 

1176 ], 

1177 "related": ["mcp-server", "mcp-tools"], 

1178 }, 

1179 "mcp-mission": { 

1180 "path": "gco_mcp/mission/README.md", 

1181 "summary": "Mission package internals — the five-phase goal-directed loop, deterministic verdict cascade, sandboxes, and how to extend each piece.", 

1182 "topics": ["mcp", "automation", "customization", "concepts"], 

1183 "keywords": [ 

1184 "mission", 

1185 "engine", 

1186 "verdict", 

1187 "five-phase loop", 

1188 "sandbox", 

1189 "predicate", 

1190 "sampling", 

1191 "criteria", 

1192 "customize mission", 

1193 "module map", 

1194 ], 

1195 "related": ["MISSION", "mcp-metric-readers", "mcp-mission-judge", "mcp-server"], 

1196 }, 

1197 "mcp-metric-readers": { 

1198 "path": "gco_mcp/metric_readers/README.md", 

1199 "summary": "Pure helpers behind the read-only metric-reader tools — and how to add an aggregation mode, file format, error code, or whole new reader source.", 

1200 "topics": ["mcp", "metrics", "customization", "automation"], 

1201 "keywords": [ 

1202 "metric reader", 

1203 "cloudwatch", 

1204 "aggregation mode", 

1205 "file format", 

1206 "parquet", 

1207 "jsonl", 

1208 "error code", 

1209 "metrics_result", 

1210 "customize metrics", 

1211 "local root", 

1212 ], 

1213 "related": ["mcp-mission-judge", "mcp-mission", "MISSION", "mcp-server"], 

1214 }, 

1215 "mcp-mission-judge": { 

1216 "path": "gco_mcp/mission_judge/README.md", 

1217 "summary": "LLM-as-judge progress scoring internals — the versioned rubric, deterministic prompt, score parsing, and how to customize the scoring for your use case.", 

1218 "topics": ["mcp", "metrics", "customization", "automation"], 

1219 "keywords": [ 

1220 "semantic progress", 

1221 "judge", 

1222 "rubric", 

1223 "prompt", 

1224 "score parsing", 

1225 "progress_score", 

1226 "gco_enable_semantic_progress", 

1227 "llm as judge", 

1228 "customize rubric", 

1229 ], 

1230 "related": ["mcp-metric-readers", "mcp-mission", "MISSION", "mcp-server"], 

1231 }, 

1232} 

1233 

1234 

1235@mcp.resource("docs://gco/index") 

1236def docs_index() -> str: 

1237 """List all available GCO documentation, examples, and configuration resources.""" 

1238 sections = ["# GCO Resource Index\n"] 

1239 sections.append("## Project Overview") 

1240 sections.append("- `docs://gco/README` — Project README and overview") 

1241 sections.append("- `docs://gco/QUICKSTART` — Quick start guide (deploy in under 60 minutes)") 

1242 sections.append("- `docs://gco/TENETS` — Prioritized project tenets and north-star guidance") 

1243 sections.append("- `docs://gco/CONTRIBUTING` — Contributing guide\n") 

1244 

1245 sections.append("## Documentation") 

1246 sections.append( 

1247 "- `find_docs(query=..., topic=..., limit=...)` tool — search the docs catalog by topic and free-text query" 

1248 ) 

1249 sections.append( 

1250 "- `docs://gco/docs/by-topic/{topic}` — list every doc tagged with a given topic phrase" 

1251 ) 

1252 sections.append( 

1253 "- `docs://gco/docs/by-related/{doc_name}` — list every doc related to the given doc" 

1254 ) 

1255 for f in sorted(DOCS_DIR.glob("*.md")): 

1256 sections.append(f"- `docs://gco/docs/{f.stem}` — {f.stem}") 

1257 

1258 sections.append("\n## Architecture Decision Records") 

1259 sections.append( 

1260 "Append-only log of significant architectural decisions — the context, " 

1261 "the decision, and its consequences." 

1262 ) 

1263 sections.append("- `docs://gco/adr/index` — every ADR with its status (directory-driven)") 

1264 sections.append("- `docs://gco/adr/README` — when to write an ADR and the authoring process") 

1265 sections.append("- `docs://gco/adr/template` — the blank ADR template") 

1266 sections.append("- `docs://gco/adr/{id}` — read one ADR by four-digit id (e.g. `0001`)") 

1267 

1268 sections.append("\n## Package Internals") 

1269 sections.append( 

1270 "Developer-facing guides to the code packages under `gco_mcp/` — structure and " 

1271 "how to customize each. Also searchable via `find_docs`." 

1272 ) 

1273 for name, meta in PACKAGE_DOC_METADATA.items(): 

1274 sections.append(f"- `docs://gco/packages/{name}` — {meta.get('summary', '')}") 

1275 

1276 sections.append("\n## Example Manifests") 

1277 sections.append("- `docs://gco/examples/README` — Examples overview and usage guide") 

1278 sections.append( 

1279 "- `docs://gco/examples/guide` — How to create new job manifests (patterns & metadata)" 

1280 ) 

1281 

1282 sections.append("\n### Discovery") 

1283 sections.append( 

1284 "- `find_examples(query=..., category=..., gpu=..., opt_in=..., limit=...)` tool — " 

1285 "search the catalog by keyword and filters" 

1286 ) 

1287 sections.append( 

1288 "- `docs://gco/examples/by-category/{category}` — list every example in a given category" 

1289 ) 

1290 sections.append( 

1291 "- `docs://gco/examples/by-use-case/{use_case}` — list every example matching a use-case phrase" 

1292 ) 

1293 sections.append( 

1294 "- `docs://gco/examples/{name}` — full manifest plus metadata header for a single example\n" 

1295 ) 

1296 

1297 # Categorize examples 

1298 categories: dict[str, list[str]] = {} 

1299 for f in sorted(EXAMPLES_DIR.glob("*.yaml")): 

1300 name = f.stem 

1301 meta = EXAMPLE_METADATA.get(name, {}) 

1302 cat_value = meta.get("category", "Other") 

1303 cat = cat_value if isinstance(cat_value, str) else "Other" 

1304 summary_value = meta.get("summary", name) 

1305 summary = summary_value if isinstance(summary_value, str) else name 

1306 entry = f"- `docs://gco/examples/{name}` — {summary}" 

1307 categories.setdefault(cat, []).append(entry) 

1308 

1309 for cat, entries in categories.items(): 

1310 sections.append(f"### {cat}") 

1311 sections.extend(entries) 

1312 sections.append("") 

1313 

1314 sections.append("## Live State") 

1315 sections.append( 

1316 "- `gco://jobs/{region}/{job_name}` — live YAML for a Kubernetes Job in the " 

1317 "explicitly selected regional EKS cluster" 

1318 ) 

1319 sections.append( 

1320 "- `gco://inference/{endpoint_name}` — desired-state record for an inference endpoint " 

1321 "from the DynamoDB store" 

1322 ) 

1323 sections.append( 

1324 "- `gco://k8s/{region}/{namespace}/{kind}/{name}` — live YAML for any Kubernetes " 

1325 "resource from the explicitly selected regional EKS cluster" 

1326 ) 

1327 sections.append( 

1328 "- `gco://cluster/{region}/topology` — Karpenter NodePools plus Pending pods snapshot for one region" 

1329 ) 

1330 sections.append( 

1331 "- `costs://gco/summary/{days_window}` — cost summary for the given day window (positive integer)" 

1332 ) 

1333 sections.append("- `tasks://gco/{task_id}` — current status of a FastMCP background task by ID") 

1334 sections.append( 

1335 "- `mission://sessions/{session_id}` — Mission session state, report, or audit replay " 

1336 "when `GCO_ENABLE_MISSION=true`" 

1337 ) 

1338 sections.append("") 

1339 

1340 sections.append("## Other Resource Groups") 

1341 sections.append("- `k8s://gco/manifests/index` — Kubernetes manifests deployed to EKS") 

1342 sections.append("- `iam://gco/policies/index` — IAM policy templates") 

1343 sections.append("- `infra://gco/index` — Dockerfiles, Helm charts, CI/CD config") 

1344 sections.append("- `ci://gco/index` — GitHub Actions workflows, composite actions, templates") 

1345 sections.append( 

1346 "- `images://gco/index` — ECR repositories, tags, images, and replication status" 

1347 ) 

1348 sections.append("- `source://gco/index` — Source code browser") 

1349 sections.append("- `demos://gco/index` — Demo walkthroughs and presentation materials") 

1350 sections.append("- `clients://gco/index` — API client examples (Python, curl, AWS CLI)") 

1351 sections.append("- `scripts://gco/index` — Utility scripts") 

1352 sections.append("- `tests://gco/index` — Test suite documentation and patterns") 

1353 sections.append( 

1354 "- `config://gco/index` — authoritative CDK configuration, MCP feature flags, and environment variables" 

1355 ) 

1356 sections.append("- `mcp://gco/tools/index` — live registered-tool catalog") 

1357 sections.append("- `mcp://gco/resources/index` — live static-resource and template catalog") 

1358 sections.append("- `mcp://gco/feature-flags` — authoritative feature-gate mapping") 

1359 return "\n".join(sections) 

1360 

1361 

1362@mcp.resource("docs://gco/README") 

1363def readme_resource() -> str: 

1364 """The main project README with overview and quickstart information.""" 

1365 return (PROJECT_ROOT / "README.md").read_text() 

1366 

1367 

1368@mcp.resource("docs://gco/QUICKSTART") 

1369def quickstart_resource() -> str: 

1370 """Quick start guide — get running in under 60 minutes.""" 

1371 path = PROJECT_ROOT / "QUICKSTART.md" 

1372 if not path.is_file(): 1372 ↛ 1373line 1372 didn't jump to line 1373 because the condition on line 1372 was never true

1373 return "QUICKSTART.md not found." 

1374 return path.read_text() 

1375 

1376 

1377@mcp.resource("docs://gco/TENETS") 

1378def tenets_resource() -> str: 

1379 """Prioritized project tenets and north-star decision guidance.""" 

1380 meta = ROOT_DOC_METADATA["TENETS"] 

1381 rel_path = str(meta["path"]) 

1382 path = PROJECT_ROOT / rel_path 

1383 if not path.is_file(): 1383 ↛ 1384line 1383 didn't jump to line 1384 because the condition on line 1383 was never true

1384 return f"{rel_path} not found." 

1385 content = path.read_text() 

1386 topics = meta.get("topics", []) 

1387 related = meta.get("related", []) 

1388 header_lines = [] 

1389 if isinstance(topics, list) and topics: 1389 ↛ 1391line 1389 didn't jump to line 1391 because the condition on line 1389 was always true

1390 header_lines.append(f"<!-- Topics: {', '.join(str(t) for t in topics)} -->") 

1391 if isinstance(related, list) and related: 1391 ↛ 1393line 1391 didn't jump to line 1393 because the condition on line 1391 was always true

1392 header_lines.append(f"<!-- Related: {', '.join(str(r) for r in related)} -->") 

1393 return "\n".join(header_lines) + "\n\n" + content 

1394 

1395 

1396@mcp.resource("docs://gco/CONTRIBUTING") 

1397def contributing_resource() -> str: 

1398 """Contributing guide — how to contribute to the project.""" 

1399 path = PROJECT_ROOT / "CONTRIBUTING.md" 

1400 if not path.is_file(): 1400 ↛ 1401line 1400 didn't jump to line 1401 because the condition on line 1400 was never true

1401 return "CONTRIBUTING.md not found." 

1402 return path.read_text() 

1403 

1404 

1405@mcp.resource("docs://gco/docs/{doc_name}") 

1406def doc_resource(doc_name: str) -> str: 

1407 """Read a documentation file by name (e.g. ARCHITECTURE, CLI, INFERENCE). 

1408 

1409 Prepends an HTML-comment header with ``Topics:`` and ``Related:`` lines 

1410 pulled from ``DOC_METADATA`` so an LLM consuming the rendered markdown 

1411 sees the doc's classification without it bleeding into the rendered 

1412 output. HTML comments are used rather than ``#`` because docs are 

1413 markdown — Python-style comments would render as text. 

1414 """ 

1415 path = DOCS_DIR / f"{doc_name}.md" 

1416 if not path.is_file(): 

1417 available = [f.stem for f in DOCS_DIR.glob("*.md")] 

1418 return f"Document '{doc_name}' not found. Available: {', '.join(available)}" 

1419 content = path.read_text() 

1420 meta = DOC_METADATA.get(doc_name, {}) 

1421 header_lines = [] 

1422 topics = meta.get("topics", []) 

1423 if isinstance(topics, list) and topics: 1423 ↛ 1425line 1423 didn't jump to line 1425 because the condition on line 1423 was always true

1424 header_lines.append(f"<!-- Topics: {', '.join(str(t) for t in topics)} -->") 

1425 related = meta.get("related", []) 

1426 if isinstance(related, list) and related: 1426 ↛ 1428line 1426 didn't jump to line 1428 because the condition on line 1426 was always true

1427 header_lines.append(f"<!-- Related: {', '.join(str(r) for r in related)} -->") 

1428 if header_lines: 1428 ↛ 1430line 1428 didn't jump to line 1430 because the condition on line 1428 was always true

1429 return "\n".join(header_lines) + "\n\n" + content 

1430 return content 

1431 

1432 

1433@mcp.resource("docs://gco/packages/{package_name}") 

1434def package_doc_resource(package_name: str) -> str: 

1435 """Read a package-level README by slug (e.g. mcp-mission, mcp-metric-readers). 

1436 

1437 Serves the developer-facing internals guides catalogued in 

1438 ``PACKAGE_DOC_METADATA`` — the README files that live next to the code 

1439 under ``gco_mcp/`` rather than in ``docs/``. Prepends an HTML-comment header 

1440 with ``Topics:`` and ``Related:`` lines (mirroring :func:`doc_resource`) 

1441 so a consuming LLM sees the classification without it bleeding into the 

1442 rendered markdown. An unknown slug returns the literal ``Package doc 'X' 

1443 not found. Available: ...`` string so callers can recover. 

1444 """ 

1445 meta = PACKAGE_DOC_METADATA.get(package_name) 

1446 if meta is None: 

1447 available = ", ".join(sorted(PACKAGE_DOC_METADATA.keys())) 

1448 return f"Package doc '{package_name}' not found. Available: {available}" 

1449 rel_path = str(meta.get("path", "")) 

1450 path = PROJECT_ROOT / rel_path 

1451 if not path.is_file(): 1451 ↛ 1452line 1451 didn't jump to line 1452 because the condition on line 1451 was never true

1452 return f"Package doc '{package_name}' file not found at '{rel_path}'." 

1453 content = path.read_text() 

1454 header_lines = [] 

1455 topics = meta.get("topics", []) 

1456 if isinstance(topics, list) and topics: 1456 ↛ 1458line 1456 didn't jump to line 1458 because the condition on line 1456 was always true

1457 header_lines.append(f"<!-- Topics: {', '.join(str(t) for t in topics)} -->") 

1458 related = meta.get("related", []) 

1459 if isinstance(related, list) and related: 1459 ↛ 1461line 1459 didn't jump to line 1461 because the condition on line 1459 was always true

1460 header_lines.append(f"<!-- Related: {', '.join(str(r) for r in related)} -->") 

1461 if header_lines: 1461 ↛ 1463line 1461 didn't jump to line 1463 because the condition on line 1461 was always true

1462 return "\n".join(header_lines) + "\n\n" + content 

1463 return content 

1464 

1465 

1466@mcp.resource("docs://gco/examples/README") 

1467def examples_readme_resource() -> str: 

1468 """Examples README — overview of all example manifests with usage instructions.""" 

1469 path = EXAMPLES_DIR / "README.md" 

1470 if not path.is_file(): 1470 ↛ 1471line 1470 didn't jump to line 1471 because the condition on line 1470 was never true

1471 return "Examples README.md not found." 

1472 return path.read_text() 

1473 

1474 

1475@mcp.resource("docs://gco/examples/guide") 

1476def examples_guide_resource() -> str: 

1477 """How to create new job manifests — patterns, metadata, and best practices. 

1478 

1479 Use this resource when you need to write a new Kubernetes manifest for GCO. 

1480 It provides the metadata for every existing example so you can pick the 

1481 closest one as a starting point and adapt it. 

1482 """ 

1483 lines = ["# GCO Example Manifest Guide\n"] 

1484 lines.append("Use this guide to create new Kubernetes manifests for GCO. Pick the closest") 

1485 lines.append("existing example as a starting point, then adapt it.\n") 

1486 lines.append("## All Examples with Metadata\n") 

1487 lines.append("| Example | Category | Keywords | GPU | Opt-in | How to Submit |") 

1488 lines.append("|---------|----------|----------|-----|--------|---------------|") 

1489 for name, meta in EXAMPLE_METADATA.items(): 

1490 gpu = meta.get("gpu", "no") 

1491 opt_in = meta.get("opt_in", "—") or "—" 

1492 submission = meta.get("submission", "") 

1493 keywords = meta.get("keywords", []) 

1494 keywords_cell = ", ".join(keywords) if isinstance(keywords, list) and keywords else "—" 

1495 lines.append( 

1496 f"| `{name}` | {meta['category']} | {keywords_cell} | {gpu} | {opt_in} | " 

1497 f"`{submission}` |" 

1498 ) 

1499 

1500 lines.append("\n## Common Patterns\n") 

1501 lines.append("### Namespace") 

1502 lines.append( 

1503 "All GCO jobs use `namespace: gco-jobs`. Inference uses `namespace: gco-inference`.\n" 

1504 ) 

1505 lines.append("### Security Context (required)") 

1506 lines.append("```yaml") 

1507 lines.append("securityContext:") 

1508 lines.append(" runAsNonRoot: true") 

1509 lines.append(" runAsUser: 1000") 

1510 lines.append(" runAsGroup: 1000") 

1511 lines.append("containers:") 

1512 lines.append("- securityContext:") 

1513 lines.append(" allowPrivilegeEscalation: false") 

1514 lines.append(" capabilities:") 

1515 lines.append(' drop: ["ALL"]') 

1516 lines.append("```\n") 

1517 lines.append("### GPU Resources") 

1518 lines.append("```yaml") 

1519 lines.append("resources:") 

1520 lines.append(" requests:") 

1521 lines.append(' nvidia.com/gpu: "1"') 

1522 lines.append(" limits:") 

1523 lines.append(' nvidia.com/gpu: "1"') 

1524 lines.append("tolerations:") 

1525 lines.append("- key: nvidia.com/gpu") 

1526 lines.append(" operator: Equal") 

1527 lines.append(' value: "true"') 

1528 lines.append(" effect: NoSchedule") 

1529 lines.append("```\n") 

1530 lines.append("### EFS Shared Storage") 

1531 lines.append("```yaml") 

1532 lines.append("volumeMounts:") 

1533 lines.append("- name: shared-storage") 

1534 lines.append(" mountPath: /mnt/gco") 

1535 lines.append("volumes:") 

1536 lines.append("- name: shared-storage") 

1537 lines.append(" persistentVolumeClaim:") 

1538 lines.append(" claimName: gco-shared-storage") 

1539 lines.append("```\n") 

1540 lines.append("### Prevent Node Consolidation (long-running jobs)") 

1541 lines.append("```yaml") 

1542 lines.append("metadata:") 

1543 lines.append(" annotations:") 

1544 lines.append(' karpenter.sh/do-not-disrupt: "true"') 

1545 lines.append("```\n") 

1546 lines.append("### Submission Methods") 

1547 lines.append("1. **SQS (recommended):** `gco jobs submit-sqs <manifest> --region <region>`") 

1548 lines.append("2. **API Gateway:** `gco jobs submit <manifest>`") 

1549 lines.append("3. **Direct kubectl:** `gco jobs submit-direct <manifest> -r <region>`") 

1550 lines.append("4. **kubectl apply:** `kubectl apply -f <manifest>`") 

1551 return "\n".join(lines) 

1552 

1553 

1554@mcp.resource("docs://gco/examples/{example_name}") 

1555def example_resource(example_name: str) -> str: 

1556 """Read an example manifest by name, with metadata context for creating similar jobs. 

1557 

1558 Returns the raw YAML manifest preceded by a metadata header that describes 

1559 what the example does, its requirements, and how to submit it. 

1560 """ 

1561 path = EXAMPLES_DIR / f"{example_name}.yaml" 

1562 if not path.is_file(): 

1563 available = [f.stem for f in EXAMPLES_DIR.glob("*.yaml")] 

1564 return f"Example '{example_name}' not found. Available: {', '.join(available)}" 

1565 

1566 meta = EXAMPLE_METADATA.get(example_name, {}) 

1567 header_lines = [] 

1568 if meta: 1568 ↛ 1594line 1568 didn't jump to line 1594 because the condition on line 1568 was always true

1569 header_lines.append(f"# Example: {example_name}") 

1570 header_lines.append(f"# Category: {meta.get('category', 'Unknown')}") 

1571 header_lines.append(f"# Summary: {meta.get('summary', '')}") 

1572 if meta.get("gpu", "no") != "no": 

1573 header_lines.append(f"# GPU/Accelerator: {meta['gpu']}") 

1574 if meta.get("opt_in"): 

1575 header_lines.append(f"# Opt-in required: {meta['opt_in']}") 

1576 header_lines.append( 

1577 f"# Submit with: {meta.get('submission', 'kubectl apply -f examples/' + example_name + '.yaml')}" 

1578 ) 

1579 keywords = meta.get("keywords", []) 

1580 if isinstance(keywords, list) and keywords: 1580 ↛ 1582line 1580 didn't jump to line 1582 because the condition on line 1580 was always true

1581 header_lines.append(f"# Keywords: {', '.join(keywords)}") 

1582 instance_types = meta.get("instance_types", []) 

1583 if isinstance(instance_types, list) and instance_types: 

1584 header_lines.append(f"# Instance Types: {', '.join(instance_types)}") 

1585 use_cases = meta.get("use_cases", []) 

1586 if isinstance(use_cases, list) and use_cases: 1586 ↛ 1588line 1586 didn't jump to line 1588 because the condition on line 1586 was always true

1587 header_lines.append(f"# Use Cases: {', '.join(use_cases)}") 

1588 related = meta.get("related", []) 

1589 if isinstance(related, list) and related: 1589 ↛ 1591line 1589 didn't jump to line 1591 because the condition on line 1589 was always true

1590 header_lines.append(f"# Related: {', '.join(related)}") 

1591 header_lines.append("#") 

1592 header_lines.append("# --- Manifest begins below ---\n") 

1593 

1594 manifest = path.read_text() 

1595 if header_lines: 1595 ↛ 1597line 1595 didn't jump to line 1597 because the condition on line 1595 was always true

1596 return "\n".join(header_lines) + manifest 

1597 return manifest 

1598 

1599 

1600@mcp.resource("docs://gco/examples/by-category/{category}") 

1601def examples_by_category_resource(category: str) -> str: 

1602 """List examples grouped by category. 

1603 

1604 Returns a markdown listing of every example in the given category. Match 

1605 is case-insensitive against the entry's ``category`` field. When the 

1606 category is not recognised, returns the literal "Category 'X' not found. 

1607 Available: ..." string so callers can recover. 

1608 """ 

1609 matches = [ 

1610 (name, meta) 

1611 for name, meta in EXAMPLE_METADATA.items() 

1612 if str(meta.get("category", "")).lower() == category.lower() 

1613 ] 

1614 if not matches: 

1615 available = sorted({str(m.get("category", "")) for m in EXAMPLE_METADATA.values()}) 

1616 return f"Category '{category}' not found. Available: {', '.join(available)}" 

1617 lines = [f"# Examples in category: {category}\n"] 

1618 for name, meta in sorted(matches): 

1619 lines.append(f"- `docs://gco/examples/{name}` — {meta.get('summary', '')}") 

1620 return "\n".join(lines) 

1621 

1622 

1623@mcp.resource("docs://gco/examples/by-use-case/{use_case}") 

1624def examples_by_use_case_resource(use_case: str) -> str: 

1625 """List examples whose use_cases include the given phrase (case-insensitive). 

1626 

1627 Substring match against every entry in each example's ``use_cases`` list. 

1628 When nothing matches, returns the literal "No examples match use case 

1629 'X'." string with a pointer to ``find_examples`` for broader search. 

1630 """ 

1631 needle = use_case.lower() 

1632 matches: list[tuple[str, dict[str, str | list[str]]]] = [] 

1633 for name, meta in EXAMPLE_METADATA.items(): 

1634 ucs = meta.get("use_cases", []) 

1635 if isinstance(ucs, list) and any(needle in str(uc).lower() for uc in ucs): 

1636 matches.append((name, meta)) 

1637 if not matches: 

1638 return ( 

1639 f"No examples match use case '{use_case}'. " 

1640 "Try `find_examples(query=...)` for broader search." 

1641 ) 

1642 lines = [f"# Examples matching use case: {use_case}\n"] 

1643 for name, meta in sorted(matches): 

1644 lines.append(f"- `docs://gco/examples/{name}` — {meta.get('summary', '')}") 

1645 return "\n".join(lines) 

1646 

1647 

1648@mcp.resource("docs://gco/docs/by-topic/{topic}") 

1649def docs_by_topic_resource(topic: str) -> str: 

1650 """List docs whose topics include the given phrase (case-insensitive). 

1651 

1652 Substring match against every entry in each doc's ``topics`` list. When 

1653 nothing matches, returns the literal ``Topic 'X' not found. Available: 

1654 ...`` string with the union of every known topic so callers can recover. 

1655 """ 

1656 needle = topic.lower() 

1657 matches: list[tuple[str, dict[str, str | list[str]]]] = [] 

1658 for name, meta in DOC_METADATA.items(): 

1659 topics = meta.get("topics", []) 

1660 if isinstance(topics, list) and any(needle in str(t).lower() for t in topics): 

1661 matches.append((name, meta)) 

1662 if not matches: 

1663 available = sorted( 

1664 { 

1665 str(t) 

1666 for meta in DOC_METADATA.values() 

1667 for t in (meta.get("topics", []) if isinstance(meta.get("topics"), list) else []) 

1668 } 

1669 ) 

1670 return f"Topic '{topic}' not found. Available: {', '.join(available)}" 

1671 lines = [f"# Docs matching topic: {topic}\n"] 

1672 for name, meta in sorted(matches): 

1673 lines.append(f"- `docs://gco/docs/{name}` — {meta.get('summary', '')}") 

1674 return "\n".join(lines) 

1675 

1676 

1677@mcp.resource("docs://gco/docs/by-related/{doc_name}") 

1678def docs_by_related_resource(doc_name: str) -> str: 

1679 """List docs related to ``doc_name``. 

1680 

1681 Combines two views of the bidirectional relation: every doc that lists 

1682 ``doc_name`` in its own ``related`` field (referenced by) and every doc 

1683 ``doc_name`` itself lists (references). Unknown names return the literal 

1684 ``Doc 'X' not found. Available: ...`` string. 

1685 """ 

1686 if doc_name not in DOC_METADATA: 

1687 available = sorted(DOC_METADATA.keys()) 

1688 return f"Doc '{doc_name}' not found. Available: {', '.join(available)}" 

1689 

1690 referenced_by: list[str] = [] 

1691 for name, meta in DOC_METADATA.items(): 

1692 related = meta.get("related", []) 

1693 if isinstance(related, list) and doc_name in related: 

1694 referenced_by.append(name) 

1695 

1696 references: list[str] = [] 

1697 referenced_self = DOC_METADATA[doc_name].get("related", []) 

1698 if isinstance(referenced_self, list): 1698 ↛ 1701line 1698 didn't jump to line 1701 because the condition on line 1698 was always true

1699 references = [str(r) for r in referenced_self] 

1700 

1701 lines = [f"# Docs related to {doc_name}\n"] 

1702 if references: 1702 ↛ 1708line 1702 didn't jump to line 1708 because the condition on line 1702 was always true

1703 lines.append("## Referenced by this doc") 

1704 for ref in sorted(set(references)): 

1705 meta = DOC_METADATA.get(ref, {}) 

1706 lines.append(f"- `docs://gco/docs/{ref}` — {meta.get('summary', '')}") 

1707 lines.append("") 

1708 if referenced_by: 1708 ↛ 1713line 1708 didn't jump to line 1713 because the condition on line 1708 was always true

1709 lines.append("## Docs that reference this one") 

1710 for ref in sorted(set(referenced_by)): 

1711 meta = DOC_METADATA.get(ref, {}) 

1712 lines.append(f"- `docs://gco/docs/{ref}` — {meta.get('summary', '')}") 

1713 return "\n".join(lines) 

1714 

1715 

1716# --------------------------------------------------------------------------- 

1717# Architecture Decision Records (ADRs) — docs://gco/adr/* 

1718# 

1719# The ADR catalog under ``docs/adr/`` is directory-driven: both the index and 

1720# the per-record resource derive everything from the files on disk, so 

1721# recording a new decision needs no change here. ``NNNN-title.md`` files are the 

1722# records; ``README.md`` (process guide) and ``template.md`` (blank form) are 

1723# guides, not records, and are excluded from the record listing. 

1724# --------------------------------------------------------------------------- 

1725 

1726_ADR_ID_RE = re.compile(r"^\d{4}-") 

1727_ADR_TITLE_PREFIX_RE = re.compile(r"^\d+\.\s*") 

1728 

1729 

1730def _adr_record_files() -> list[Path]: 

1731 """Return the numbered ADR record files (``NNNN-*.md``), sorted by id.""" 

1732 if not ADR_DIR.is_dir(): 1732 ↛ 1733line 1732 didn't jump to line 1733 because the condition on line 1732 was never true

1733 return [] 

1734 return sorted(p for p in ADR_DIR.glob("*.md") if _ADR_ID_RE.match(p.name)) 

1735 

1736 

1737def _parse_adr(path: Path) -> dict[str, str]: 

1738 """Extract ``id``, ``title``, and ``status`` from an ADR markdown file. 

1739 

1740 Title is the first level-1 heading with any ``NNNN.`` numeric prefix 

1741 stripped; status is read from the ``- **Status:** ...`` metadata line. Both 

1742 fall back to sensible defaults so a malformed file still lists rather than 

1743 breaking the index. 

1744 """ 

1745 title = "" 

1746 status = "" 

1747 for raw in path.read_text(encoding="utf-8").splitlines(): 

1748 line = raw.strip() 

1749 if not title and line.startswith("# "): 

1750 title = _ADR_TITLE_PREFIX_RE.sub("", line[2:].strip()) 

1751 continue 

1752 if not status: 

1753 # Normalize "- **Status:** Accepted" to "status: accepted" so the 

1754 # label is matched regardless of list marker or emphasis. 

1755 plain = line.lstrip("-* ").replace("*", "") 

1756 if plain.lower().startswith("status"): 

1757 status = plain[len("status") :].lstrip(": ").strip().rstrip(".") 

1758 return { 

1759 "id": path.stem, 

1760 "title": title or path.stem, 

1761 "status": status or "Unknown", 

1762 } 

1763 

1764 

1765def _resolve_adr(adr_id: str) -> Path | None: 

1766 """Resolve an ADR request to a file under ``docs/adr/``, or ``None``. 

1767 

1768 Accepts a full filename stem (``0001-record-architecture-decisions``, 

1769 ``README``, ``template``) or a numeric id in any zero-padding (``1`` -> 

1770 ``0001``). Rejects anything containing a path separator or ``..`` so a 

1771 crafted id cannot escape the directory; because the sanitized candidate has 

1772 no separators, the joined path is always a direct child of ``docs/adr/``. 

1773 """ 

1774 candidate = adr_id.strip() 

1775 if not candidate or "/" in candidate or "\\" in candidate or ".." in candidate: 

1776 return None 

1777 if candidate.isdigit(): 

1778 want = candidate.zfill(4) 

1779 return next((p for p in _adr_record_files() if p.name[:4] == want), None) 

1780 path = ADR_DIR / f"{candidate}.md" 

1781 return path if path.is_file() else None 

1782 

1783 

1784@mcp.resource("docs://gco/adr/index") 

1785def adr_index_resource() -> str: 

1786 """List every Architecture Decision Record with its id, title, and status. 

1787 

1788 Directory-driven: scans ``docs/adr/`` for numbered ``NNNN-title.md`` records 

1789 at read time, so a newly added ADR appears here with no code change. The 

1790 process guide and the blank template are available at 

1791 ``docs://gco/adr/README`` and ``docs://gco/adr/template``. 

1792 """ 

1793 lines = ["# Architecture Decision Records\n"] 

1794 lines.append( 

1795 "Append-only log of significant architectural decisions (context, " 

1796 "decision, consequences). Read the process at `docs://gco/adr/README` " 

1797 "and start a new record from `docs://gco/adr/template`.\n" 

1798 ) 

1799 files = _adr_record_files() 

1800 if not files: 

1801 lines.append("_No ADRs have been recorded yet._") 

1802 return "\n".join(lines) 

1803 for path in files: 

1804 meta = _parse_adr(path) 

1805 lines.append(f"- `docs://gco/adr/{path.name[:4]}` — {meta['title']} ({meta['status']})") 

1806 return "\n".join(lines) 

1807 

1808 

1809@mcp.resource("docs://gco/adr/{adr_id}") 

1810def adr_resource(adr_id: str) -> str: 

1811 """Read a single ADR by id, filename stem, or guide name. 

1812 

1813 Accepts a four-digit id (``0001``), a full stem 

1814 (``0001-record-architecture-decisions``), or the ``README`` / ``template`` 

1815 guides. An unknown id returns the literal ``ADR 'X' not found. Available: 

1816 ...`` string listing the numeric ids so callers can recover. 

1817 """ 

1818 path = _resolve_adr(adr_id) 

1819 if path is None: 

1820 available = ", ".join(p.name[:4] for p in _adr_record_files()) or "none" 

1821 return f"ADR '{adr_id}' not found. Available: {available}" 

1822 return path.read_text(encoding="utf-8")