feat: expose Extra as an MCP server via stdio - #126
Conversation
|
Thanks for the contribution — I really like the direction of exposing Extra as an MCP server over stdio, and keeping the MCP layer thin around the existing There are a few blockers I think we should address before merging:
The current service expects a caller = Principal.external(effective_user_id)
await service.create(caller, session_id=effective_session_id)
result = await service.send(effective_session_id, message, caller)The MCP implementation currently passes Please reuse the same identity flow already used by
LangGraphEngine(
base_dir,
session_approval_repository=repositories.session_approvals,
tool_usage_repository=repositories.tool_usage,
run_repository=repositories.runs,
)and also passes The MCP server currently only wires the session approval repository, which means run/tool-usage state can silently fall back to process-local in-memory implementations. I would strongly prefer extracting/reusing the existing runtime composition rather than maintaining a second, slightly different setup path for MCP.
Since this example is the entry point for the new feature, it should pass the normal validation and be directly runnable.
{
"session_id": "...",
"answer": "...",
"visited": [],
"used_tools": []
}So a run that pauses for approval may look like an empty successful response to the MCP client. Either MCP mode should expose approval state and a way to resume it, or the initial version should explicitly support only auto-execution and enforce/document that limitation. Finally, the current CI is failing in the quality gate, starting with formatting, so that needs to be green before approval. The feature itself is valuable and the overall architecture is promising — I’d just like the MCP entry point to reuse the same identity, persistence, and runtime composition guarantees as the existing CLI rather than becoming a parallel runtime path. |
This adds a new Agentctl mcp serve command that starts an MCP server over stdio, exposing a single extra_chat tool backed by the existing ConversationService and engine.
The MCP layer is intentionally thin — it builds the engine and DB connection once at startup, reuses them for every request, and cleans them up on shutdown (including SIGINT/SIGTERM and stdio close). Session handling mirrors the existing �gentctl run behavior: omit session_id to generate a fresh conversation, or pass one to continue an existing one. Input validation happens before any engine work, and errors are returned as proper MCP tool errors.
Tests added:
An example under examples/mcp_server/ shows how to start the server and connect to it with a Python MCP client.