docs-and-mcps #1
@@ -25,9 +25,8 @@ Optional flags:
|
|||||||
--package-root NAME Root Python package name (default: mail_intake)
|
--package-root NAME Root Python package name (default: mail_intake)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
|
||||||
import json
|
|
||||||
import re
|
import re
|
||||||
|
import argparse
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
|
||||||
@@ -139,64 +138,6 @@ def extract_modules(md_file: Path) -> list[str]:
|
|||||||
return MKDOCSTRINGS_DIRECTIVE.findall(content)
|
return MKDOCSTRINGS_DIRECTIVE.findall(content)
|
||||||
|
|
||||||
|
|
||||||
def cmd_build_mcp(args: argparse.Namespace) -> None:
|
|
||||||
docs_root = args.docs_dir
|
|
||||||
mcp_root = args.mcp_dir
|
|
||||||
|
|
||||||
modules_dir = mcp_root / "modules"
|
|
||||||
modules_dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
nav = []
|
|
||||||
modules = []
|
|
||||||
|
|
||||||
for md in iter_markdown_files(docs_root):
|
|
||||||
rel = md.relative_to(docs_root)
|
|
||||||
|
|
||||||
module_refs = extract_modules(md)
|
|
||||||
if not module_refs:
|
|
||||||
continue
|
|
||||||
|
|
||||||
nav.append({
|
|
||||||
"page": str(rel),
|
|
||||||
"modules": module_refs,
|
|
||||||
})
|
|
||||||
|
|
||||||
for module in module_refs:
|
|
||||||
module_entry = {
|
|
||||||
"module": module,
|
|
||||||
"doc_page": str(rel),
|
|
||||||
}
|
|
||||||
modules.append(module_entry)
|
|
||||||
|
|
||||||
out = modules_dir / f"{module}.json"
|
|
||||||
out.parent.mkdir(parents=True, exist_ok=True)
|
|
||||||
out.write_text(
|
|
||||||
json.dumps(module_entry, indent=2),
|
|
||||||
encoding="utf-8",
|
|
||||||
)
|
|
||||||
|
|
||||||
mcp_root.mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
(mcp_root / "nav.json").write_text(
|
|
||||||
json.dumps(nav, indent=2),
|
|
||||||
encoding="utf-8",
|
|
||||||
)
|
|
||||||
|
|
||||||
(mcp_root / "index.json").write_text(
|
|
||||||
json.dumps(
|
|
||||||
{
|
|
||||||
"project": "Aetoskia Mail Intake",
|
|
||||||
"type": "docs-only",
|
|
||||||
"modules_count": len(modules),
|
|
||||||
},
|
|
||||||
indent=2,
|
|
||||||
),
|
|
||||||
encoding="utf-8",
|
|
||||||
)
|
|
||||||
|
|
||||||
print(f"MCP artifacts written to: {mcp_root}")
|
|
||||||
|
|
||||||
|
|
||||||
# -------------------------
|
# -------------------------
|
||||||
# CLI
|
# CLI
|
||||||
# -------------------------
|
# -------------------------
|
||||||
@@ -220,13 +161,6 @@ def main() -> None:
|
|||||||
help="Root Python package name",
|
help="Root Python package name",
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"--mcp-dir",
|
|
||||||
type=Path,
|
|
||||||
default=DEFAULT_MCP_DIR,
|
|
||||||
help="Output directory for MCP artifacts",
|
|
||||||
)
|
|
||||||
|
|
||||||
subparsers = parser.add_subparsers(dest="command", required=True)
|
subparsers = parser.add_subparsers(dest="command", required=True)
|
||||||
|
|
||||||
subparsers.add_parser(
|
subparsers.add_parser(
|
||||||
@@ -244,13 +178,6 @@ def main() -> None:
|
|||||||
help="Serve the MkDocs site locally",
|
help="Serve the MkDocs site locally",
|
||||||
).set_defaults(func=cmd_serve)
|
).set_defaults(func=cmd_serve)
|
||||||
|
|
||||||
subparsers.add_parser(
|
|
||||||
"build_mcp",
|
|
||||||
help="Generate MCP artifacts (docs-only)",
|
|
||||||
).set_defaults(
|
|
||||||
func=cmd_build_mcp
|
|
||||||
)
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
args.func(args)
|
args.func(args)
|
||||||
|
|
||||||
|
|||||||
@@ -2,28 +2,31 @@ from pathlib import Path
|
|||||||
import json
|
import json
|
||||||
from mcp.server.fastmcp import FastMCP
|
from mcp.server.fastmcp import FastMCP
|
||||||
|
|
||||||
MCP_ROOT = Path(__file__).resolve().parent / "mcp"
|
MCP_ROOT = Path("mcp")
|
||||||
|
|
||||||
mcp = FastMCP("aetoskia-mail-intake-docs")
|
mcp = FastMCP("aetoskia-mail-intake-docs")
|
||||||
|
|
||||||
|
def read_json(path: Path):
|
||||||
|
if not path.exists():
|
||||||
|
return {"error": "not_found", "path": str(path)}
|
||||||
|
return json.loads(path.read_text())
|
||||||
|
|
||||||
@mcp.resource("docs://index")
|
@mcp.resource("docs://index")
|
||||||
def index():
|
def index():
|
||||||
return json.loads((MCP_ROOT / "index.json").read_text())
|
return read_json(MCP_ROOT / "index.json")
|
||||||
|
|
||||||
|
|
||||||
@mcp.resource("docs://nav")
|
@mcp.resource("docs://nav")
|
||||||
def nav():
|
def nav():
|
||||||
return json.loads((MCP_ROOT / "nav.json").read_text())
|
return read_json(MCP_ROOT / "nav.json")
|
||||||
|
|
||||||
|
@mcp.resource("docs://{module}")
|
||||||
@mcp.resource("docs://module/{module}")
|
|
||||||
def module(module: str):
|
def module(module: str):
|
||||||
path = MCP_ROOT / "modules" / f"{module}.json"
|
return read_json(MCP_ROOT / "modules" / f"{module}.json")
|
||||||
if not path.exists():
|
|
||||||
raise FileNotFoundError(module)
|
|
||||||
return json.loads(path.read_text())
|
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def ping() -> str:
|
||||||
|
return "Pong! (fake tool executed)"
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
mcp.run()
|
# FastMCP owns the HTTP server
|
||||||
|
mcp.run(transport="streamable-http")
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ google-api-python-client==2.187.0
|
|||||||
google-auth-oauthlib==1.2.3
|
google-auth-oauthlib==1.2.3
|
||||||
types-beautifulsoup4
|
types-beautifulsoup4
|
||||||
mcp==1.25.0
|
mcp==1.25.0
|
||||||
|
mcp-inspector==0.1.0
|
||||||
|
fastapi
|
||||||
|
uvicorn
|
||||||
|
fastmcp
|
||||||
|
|
||||||
# Test Packages
|
# Test Packages
|
||||||
pytest==7.4.0
|
pytest==7.4.0
|
||||||
@@ -14,5 +18,5 @@ mkdocs==1.6.1
|
|||||||
mkdocs-material==9.6.23
|
mkdocs-material==9.6.23
|
||||||
neoteroi-mkdocs==1.1.3
|
neoteroi-mkdocs==1.1.3
|
||||||
pymdown-extensions==10.16.1
|
pymdown-extensions==10.16.1
|
||||||
mkdocstrings==1.0.0
|
mkdocstrings[python]==1.0.1
|
||||||
mkdocstrings-python==2.0.1
|
mkdocstrings-python==2.0.1
|
||||||
|
|||||||
Reference in New Issue
Block a user