This commit is contained in:
@@ -6,7 +6,20 @@ from collections.abc import Callable, Mapping
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
RepoPath = Path(__file__).resolve().parents[3]
|
||||
|
||||
def _find_repo_root() -> Path:
|
||||
# Prefer cwd so local dev runs from the repo root without packaging.
|
||||
candidates = (Path.cwd(), Path(__file__).resolve())
|
||||
for base in candidates:
|
||||
for parent in (base, *base.parents):
|
||||
if (parent / "docs" / "constitution.md").exists():
|
||||
return parent
|
||||
if (parent / ".git").exists():
|
||||
return parent
|
||||
return Path(__file__).resolve().parents[3]
|
||||
|
||||
|
||||
RepoPath = _find_repo_root()
|
||||
DocsPath = RepoPath / "docs"
|
||||
|
||||
|
||||
@@ -50,7 +63,7 @@ class Tool:
|
||||
description: str
|
||||
inputs: tuple[InputParam, ...]
|
||||
docs_anchor: DocsAnchor
|
||||
handler: Callable[[Mapping[str, str]], tuple[str, str, list[str]]]
|
||||
handler: Callable[[Mapping[str, str]], tuple[str, object, list[str]]]
|
||||
|
||||
def as_dict(self) -> dict[str, object]:
|
||||
"""Serialize tool metadata for transport."""
|
||||
@@ -92,33 +105,51 @@ def _search_docs(term: str) -> str:
|
||||
return "\n".join(matches[:20])
|
||||
|
||||
|
||||
def _tool_handlers() -> dict[str, Callable[[Mapping[str, str]], tuple[str, str, list[str]]]]:
|
||||
def show_constitution(_: Mapping[str, str]) -> tuple[str, str, list[str]]:
|
||||
text = _read_text(DocsPath / "constitution.md")
|
||||
return ("ok", text or "Constitution not found.", [])
|
||||
def show_constitution(_: Mapping[str, str]) -> tuple[str, str, list[str]]:
|
||||
"""Return the AI constitution contents."""
|
||||
text = _read_text(DocsPath / "constitution.md")
|
||||
return ("ok", text or "Constitution not found.", [])
|
||||
|
||||
def list_playbooks(_: Mapping[str, str]) -> tuple[str, str, list[str]]:
|
||||
return ("ok", _list_playbooks(), [])
|
||||
|
||||
def show_reference(_: Mapping[str, str]) -> tuple[str, str, list[str]]:
|
||||
return ("ok", _list_reference_topics(), [])
|
||||
def list_playbooks(_: Mapping[str, str]) -> tuple[str, str, list[str]]:
|
||||
"""Return the list of available playbooks."""
|
||||
return ("ok", _list_playbooks(), [])
|
||||
|
||||
def search_docs(params: Mapping[str, str]) -> tuple[str, str, list[str]]:
|
||||
term = params.get("query", "")
|
||||
if not term:
|
||||
return ("invalid_input", "Missing query", [])
|
||||
return ("ok", _search_docs(term), [])
|
||||
|
||||
def list_tasks(_: Mapping[str, str]) -> tuple[str, str, list[str]]:
|
||||
tasks_file = RepoPath / "specs" / "001-mcp-server" / "tasks.md"
|
||||
return ("ok", _read_text(tasks_file) or "Tasks not found.", [])
|
||||
def show_reference(_: Mapping[str, str]) -> tuple[str, str, list[str]]:
|
||||
"""Return the reference index contents."""
|
||||
return ("ok", _list_reference_topics(), [])
|
||||
|
||||
|
||||
def search_docs(params: Mapping[str, str]) -> tuple[str, str, list[str]]:
|
||||
"""Search docs for a query string and return matches."""
|
||||
term = params.get("query", "")
|
||||
if not term:
|
||||
return ("invalid_input", "Missing query", [])
|
||||
return ("ok", _search_docs(term), [])
|
||||
|
||||
|
||||
def list_tasks(_: Mapping[str, str]) -> tuple[str, str, list[str]]:
|
||||
"""Return MCP task list contents."""
|
||||
tasks_file = RepoPath / "specs" / "001-mcp-server" / "tasks.md"
|
||||
return ("ok", _read_text(tasks_file) or "Tasks not found.", [])
|
||||
|
||||
|
||||
def sync_docs(_: Mapping[str, str]) -> tuple[str, object, list[str]]:
|
||||
"""Return catalog vs docs drift report."""
|
||||
from mcp_server.docs_sync import check_catalog_parity
|
||||
|
||||
return ("ok", check_catalog_parity(), [])
|
||||
|
||||
|
||||
def _tool_handlers() -> dict[str, Callable[[Mapping[str, str]], tuple[str, object, list[str]]]]:
|
||||
return {
|
||||
"show-constitution": show_constitution,
|
||||
"list-playbooks": list_playbooks,
|
||||
"show-reference": show_reference,
|
||||
"search-docs": search_docs,
|
||||
"list-mcp-tasks": list_tasks,
|
||||
"sync-docs": sync_docs,
|
||||
}
|
||||
|
||||
|
||||
@@ -150,6 +181,11 @@ def tool_catalog() -> tuple[Tool, ...]:
|
||||
anchor="tasks",
|
||||
summary="Implementation tasks for MCP feature",
|
||||
)
|
||||
anchor_sync = DocsAnchor(
|
||||
path=DocsPath / "reference" / "mcp-server.md",
|
||||
anchor="sync-docs",
|
||||
summary="Compare tool catalog against documented anchors",
|
||||
)
|
||||
return (
|
||||
Tool(
|
||||
name="show-constitution",
|
||||
@@ -186,11 +222,18 @@ def tool_catalog() -> tuple[Tool, ...]:
|
||||
docs_anchor=anchor_tasks,
|
||||
handler=handlers["list-mcp-tasks"],
|
||||
),
|
||||
Tool(
|
||||
name="sync-docs",
|
||||
description="Compare tool catalog against docs reference anchors.",
|
||||
inputs=(),
|
||||
docs_anchor=anchor_sync,
|
||||
handler=handlers["sync-docs"],
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def list_tools_payload() -> dict[str, object]:
|
||||
"""Render tool catalog payload for listTools."""
|
||||
"""Render tool catalog payload for tool discovery."""
|
||||
return {"tools": [tool.as_dict() for tool in tool_catalog()]}
|
||||
|
||||
|
||||
@@ -202,7 +245,7 @@ def invoke_tool(name: str, args: Mapping[str, str]) -> dict[str, object]:
|
||||
return {
|
||||
"status": "unsupported",
|
||||
"output": f"Tool '{name}' is not available.",
|
||||
"actions": ["call listTools to see supported tools"],
|
||||
"actions": ["call tools/list to see supported tools"],
|
||||
"docsAnchor": {},
|
||||
}
|
||||
status, output, actions = tool.handler(args)
|
||||
|
||||
Reference in New Issue
Block a user