from __future__ import annotations import json from pathlib import Path from typing import Any, Literal from mcp.server.fastmcp import FastMCP class MCPServer: """ MCP server for serving a pre-built MCP documentation bundle. """ def __init__(self, mcp_root: Path, name: str) -> None: self.mcp_root = mcp_root self.app = FastMCP(name) self._register_resources() self._register_tools() # ------------------------------------------------------------------ # Internal helpers # ------------------------------------------------------------------ def _read_json(self, path: Path) -> Any: if not path.exists(): return { "error": "not_found", "path": str(path), } return json.loads(path.read_text(encoding="utf-8")) # ------------------------------------------------------------------ # MCP resources # ------------------------------------------------------------------ def _register_resources(self) -> None: @self.app.resource("docs://index") def index(): return self._read_json(self.mcp_root / "index.json") @self.app.resource("docs://nav") def nav(): return self._read_json(self.mcp_root / "nav.json") @self.app.resource("docs://module/{module}") def module(module: str): return self._read_json( self.mcp_root / "modules" / f"{module}.json" ) # ------------------------------------------------------------------ # MCP tools (optional / diagnostic) # ------------------------------------------------------------------ def _register_tools(self) -> None: @self.app.tool() def ping() -> str: return "pong" # ------------------------------------------------------------------ # Server lifecycle # ------------------------------------------------------------------ def run(self, transport: Literal["stdio", "sse", "streamable-http"] = "streamable-http") -> None: """ Start the MCP server. Args: transport: MCP transport (default: streamable-http) """ self.app.run(transport=transport)