from __future__ import annotations from dataclasses import dataclass, field from pathlib import Path import yaml @dataclass(frozen=True) class MCPBundle: root: Path name: str port: int kind: str = "docs" # "docs" -> base MCPServer; "registry" -> StandardsMCPServer @dataclass(frozen=True) class RepoDoc: name: str description: str | None lib: str | None api: str | None wiki: str | None mcp_bundle: str | None # source-relative path in the repo (collect use) mcp_server: str | None # MCP server / namespace name mcp_port: int | None @dataclass(frozen=True) class ServiceConfig: name: str html_port: int health_port: int repos: tuple[RepoDoc, ...] mcp_static: dict[str, MCPBundle] = field(default_factory=dict) @property def mcp_servers(self) -> dict[str, MCPBundle]: """Return every MCP server: one per repo that declares a bundle, plus the hand-authored static servers (for example the standards registry).""" servers: dict[str, MCPBundle] = { r.name: MCPBundle(root=Path(r.name) / "mcp", name=r.mcp_server, port=r.mcp_port) for r in self.repos if r.mcp_port is not None } servers.update(self.mcp_static) return servers def load_config(path: Path | str = "config.yml") -> ServiceConfig: p = Path(path) data = yaml.safe_load(p.read_text(encoding="utf-8")) service = data.get("service") or {} repos: list[RepoDoc] = [] for entry in data.get("repos", []): docs = entry.get("docs") or {} mcp = docs.get("mcp") repos.append(RepoDoc( name=entry["name"], description=entry.get("description"), lib=docs.get("lib"), api=docs.get("api"), wiki=docs.get("wiki"), mcp_bundle=mcp["bundle"] if mcp else None, mcp_server=mcp["server"] if mcp else None, mcp_port=mcp["port"] if mcp else None, )) mcp_static: dict[str, MCPBundle] = {} for entry in data.get("mcp_static", []): mcp_static[entry["name"]] = MCPBundle( root=Path(entry["bundle"]), name=entry.get("server") or entry["name"], port=entry["port"], kind=entry.get("kind", "docs"), ) return ServiceConfig( name=service.get("name", "aetos-docs"), html_port=service.get("html_port", 9000), health_port=service.get("health_port", 8000), repos=tuple(repos), mcp_static=mcp_static, )