9th cascade bug, follow-up to #82/#83/#85/#86 + #79.
Observed
Worker (post-autowire) reports asyncio.run(srv.list_tools()) returns only canvas/conversation/messages/channels tools — missing all 6 G2/G3/G4 tools: silo_query, confer_run, file_issue, lane_h_list, lane_h_fetch, grafted_context_fetch.
Investigation
Direct invocation in dev workspace:
from mcp_serve import create_mcp_server
mcp = create_mcp_server()
import asyncio
[t.name for t in asyncio.run(mcp.list_tools())] # → 32 tools including all 6
The G2/G3/G4 registrars (_register_devagentic_mutation_tools, _register_github_tools, _register_lane_h_tools) exist in mcp_serve.py:1447/1569/1659 AND are called from create_mcp_server at lines 877-879. Each uses @mcp.tool() decorators unconditionally — they don't gate on plugin availability (only the tool bodies check via _resolve_*_client).
So in dev, registration works. The deployment-side discrepancy is one of:
- Deployed wheel has
mcp_serve.py from a commit predating the registrar additions (b05444c / 7059267 / c0f017b)
- A silent exception in one of the registrars during create_mcp_server (currently no try/except — would crash the server entirely, inconsistent with "some tools registered")
- FastMCP version mismatch / list_tools visibility behavior change
Fix (defensive hardening — addresses all 3 possibilities)
- Wrap each registrar call in create_mcp_server with try/except + WARN log. If one registrar fails (e.g., import error), the others still register + the failure is visible in stderr, instead of silently killing the server.
- Boot-time log line:
MCP server: registered N tools: [silo_query, confer_run, ...]. Visible in MCP subprocess stderr → worker logs. Tells us EXACTLY what's registered on each deploy.
- Contract test: assert
create_mcp_server() registers all 6 required G2/G3/G4 tools by name. Catches future regressions where a registrar is removed or its decorator path breaks.
Out of scope
A bigger refactor to make plugin register(ctx) own its MCP tool surface (delegating to plugin-side register_mcp_tools(mcp) functions called by an mcp_serve loader) is appealing but a larger change. File separately if desired.
Acceptance
- Deployed worker logs
MCP server: registered N tools: [...] on subprocess spawn, exposing exactly which tools are present in that wheel.
- Contract test catches any future drop of a G2/G3/G4 registrar.
- One failing registrar no longer takes down peer registrars.
Severity
Final integration gap for poly-explorer end-to-end tool surface.
9th cascade bug, follow-up to #82/#83/#85/#86 + #79.
Observed
Worker (post-autowire) reports
asyncio.run(srv.list_tools())returns only canvas/conversation/messages/channels tools — missing all 6 G2/G3/G4 tools:silo_query,confer_run,file_issue,lane_h_list,lane_h_fetch,grafted_context_fetch.Investigation
Direct invocation in dev workspace:
The G2/G3/G4 registrars (
_register_devagentic_mutation_tools,_register_github_tools,_register_lane_h_tools) exist inmcp_serve.py:1447/1569/1659AND are called fromcreate_mcp_serverat lines 877-879. Each uses@mcp.tool()decorators unconditionally — they don't gate on plugin availability (only the tool bodies check via_resolve_*_client).So in dev, registration works. The deployment-side discrepancy is one of:
mcp_serve.pyfrom a commit predating the registrar additions (b05444c / 7059267 / c0f017b)Fix (defensive hardening — addresses all 3 possibilities)
MCP server: registered N tools: [silo_query, confer_run, ...]. Visible in MCP subprocess stderr → worker logs. Tells us EXACTLY what's registered on each deploy.create_mcp_server()registers all 6 required G2/G3/G4 tools by name. Catches future regressions where a registrar is removed or its decorator path breaks.Out of scope
A bigger refactor to make plugin
register(ctx)own its MCP tool surface (delegating to plugin-sideregister_mcp_tools(mcp)functions called by an mcp_serve loader) is appealing but a larger change. File separately if desired.Acceptance
MCP server: registered N tools: [...]on subprocess spawn, exposing exactly which tools are present in that wheel.Severity
Final integration gap for poly-explorer end-to-end tool surface.