feat(mcp): FastMCP server exposing read-only KB tools (stdio)
Implements F-14: thin FastMCP wrapper over existing codex domain modules.
Seven read-only tools: search, ask, wiki_read, wiki_list, discover_leads,
provenance_verify, synthesis_browse. All optional-feature tools degrade
gracefully to {"error": "feature not available"} instead of crashing.
Adds mcp[cli]>=1.0 dependency and codex-mcp console_script entry-point.
28 new tests across test_server, test_search, test_readonly, test_graceful,
test_http_auth — all green; 0 regressions in existing 172 tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
49
tests/mcp/test_server.py
Normal file
49
tests/mcp/test_server.py
Normal file
@@ -0,0 +1,49 @@
|
||||
"""test_server.py — verify the MCP server instantiates and all tools are registered."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Expected tools (must match EXACTLY what mcp_server.py registers)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
EXPECTED_TOOLS = {
|
||||
"search",
|
||||
"ask",
|
||||
"wiki_read",
|
||||
"wiki_list",
|
||||
"discover_leads",
|
||||
"provenance_verify",
|
||||
"synthesis_browse",
|
||||
}
|
||||
|
||||
|
||||
def test_server_instance_exists() -> None:
|
||||
"""The module-level ``mcp`` FastMCP instance must exist."""
|
||||
from codex.mcp_server import mcp
|
||||
|
||||
assert mcp is not None
|
||||
assert mcp.name == "codex"
|
||||
|
||||
|
||||
def test_all_tools_registered() -> None:
|
||||
"""Every expected tool name must be registered on the server."""
|
||||
from codex.mcp_server import mcp
|
||||
|
||||
tools = asyncio.run(mcp.list_tools())
|
||||
registered = {t.name for t in tools}
|
||||
|
||||
missing = EXPECTED_TOOLS - registered
|
||||
assert not missing, f"Tools not registered: {missing}"
|
||||
|
||||
|
||||
def test_no_extra_unexpected_write_tools() -> None:
|
||||
"""No tool outside the expected set should be registered (belt-and-suspenders)."""
|
||||
from codex.mcp_server import mcp
|
||||
|
||||
tools = asyncio.run(mcp.list_tools())
|
||||
registered = {t.name for t in tools}
|
||||
|
||||
unexpected = registered - EXPECTED_TOOLS
|
||||
assert not unexpected, f"Unexpected tools registered: {unexpected}"
|
||||
Reference in New Issue
Block a user