Coverage for mcp/resources/source.py: 87%
53 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-15 15:07 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-15 15:07 +0000
1"""Source code resources (source:// scheme) for the GCO MCP server."""
3from pathlib import Path
5from server import mcp
7PROJECT_ROOT = Path(__file__).parent.parent.parent
9_SOURCE_DIRS = {
10 "gco": PROJECT_ROOT / "gco",
11 "cli": PROJECT_ROOT / "cli",
12 "lambda": PROJECT_ROOT / "lambda",
13 "mcp": PROJECT_ROOT / "mcp",
14 "scripts": PROJECT_ROOT / "scripts",
15 "demo": PROJECT_ROOT / "demo",
16 "dockerfiles": PROJECT_ROOT / "dockerfiles",
17}
18_SKIP_DIRS = {
19 "__pycache__",
20 ".git",
21 "cdk.out",
22 "node_modules",
23 "kubectl-applier-simple-build",
24 "helm-installer-build",
25}
26_SOURCE_EXTENSIONS = {".py", ".yaml", ".yml", ".json", ".txt", ".toml", ".cfg", ".sh", ".md"}
28_CONFIG_FILES = {
29 "pyproject.toml",
30 "cdk.json",
31 "app.py",
32 "Dockerfile.dev",
33 ".gitlab-ci.yml",
34 ".pre-commit-config.yaml",
35 ".yamllint.yml",
36 ".checkov.yaml",
37 ".kics.yaml",
38 ".gitleaks.toml",
39 ".semgrepignore",
40 ".dockerignore",
41 ".gitignore",
42}
45def _list_source_files(base: Path) -> list[Path]:
46 """Walk a directory and return all source files, skipping noise."""
47 files = []
48 for p in sorted(base.rglob("*")):
49 if any(skip in p.parts for skip in _SKIP_DIRS):
50 continue
51 if p.is_file() and p.suffix in _SOURCE_EXTENSIONS:
52 files.append(p)
53 return files
56@mcp.resource("source://gco/index")
57def source_index() -> str:
58 """List all source code files available for reading, grouped by package."""
59 sections = ["# GCO Source Code Index\n"]
60 sections.append("## Project Config")
61 for name in sorted(_CONFIG_FILES):
62 if (PROJECT_ROOT / name).is_file(): 62 ↛ 61line 62 didn't jump to line 61 because the condition on line 62 was always true
63 sections.append(f"- `source://gco/config/{name}`")
64 for pkg, base in _SOURCE_DIRS.items():
65 if not base.is_dir(): 65 ↛ 66line 65 didn't jump to line 66 because the condition on line 65 was never true
66 continue
67 files = _list_source_files(base)
68 if not files: 68 ↛ 69line 68 didn't jump to line 69 because the condition on line 68 was never true
69 continue
70 sections.append(f"\n## {pkg}/ ({len(files)} files)")
71 for f in files:
72 rel = f.relative_to(PROJECT_ROOT)
73 sections.append(f"- `source://gco/file/{rel}`")
74 return "\n".join(sections)
77@mcp.resource("source://gco/config/{filename}")
78def config_file_resource(filename: str) -> str:
79 """Read a top-level project config file (pyproject.toml, cdk.json, etc.)."""
80 if filename not in _CONFIG_FILES:
81 return f"Not available. Allowed: {', '.join(sorted(_CONFIG_FILES))}"
82 path = PROJECT_ROOT / filename
83 if not path.is_file(): 83 ↛ 84line 83 didn't jump to line 84 because the condition on line 83 was never true
84 return f"File '{filename}' not found."
85 return path.read_text()
88@mcp.resource("source://gco/file/{filepath*}")
89def source_file_resource(filepath: str) -> str:
90 """Read any source file by its path relative to the project root."""
91 path = (PROJECT_ROOT / filepath).resolve()
92 if not str(path).startswith(str(PROJECT_ROOT.resolve())): 92 ↛ 93line 92 didn't jump to line 93 because the condition on line 92 was never true
93 return "Access denied: path is outside the project."
94 if any(skip in path.parts for skip in _SKIP_DIRS):
95 return "Access denied: path is in a skipped directory."
96 if not path.is_file():
97 return f"File '{filepath}' not found."
98 if path.suffix not in _SOURCE_EXTENSIONS: 98 ↛ 99line 98 didn't jump to line 99 because the condition on line 98 was never true
99 return f"File type '{path.suffix}' not served. Allowed: {', '.join(_SOURCE_EXTENSIONS)}"
100 return path.read_text()