feat(soul): MCP tool-bridge — suspend agentic loop for client-executed tools #5
Reference in New Issue
Block a user
Delete Branch "feat/mcp-tool-bridge"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
"Option B" MCP tool-bridge on the soul side. The agentic chat loop
(
handle_chat_agentic) used to return the literal string"unknown tool: <name>"for any tool it could not run in-process. It now suspends and asks the client
(the Kotlin desktop app) to execute the tool — this is what lets Neuron run MCP
connectors/plugins, matching Claude's Connectors.
Built-in tools (
read_file,write_file,web_get,search_memory,run_command)and Anthropic's native server-side
web_searchare unchanged. There was nopre-existing tool-approval /
tool_pendinground-trip in the soul, so this PRdefines the contract below as a new, minimal
tool_resultendpoint.Client contract (for the Kotlin MCP-client lane)
1. Soul → client: pending signal
When the model calls a tool the soul cannot run in-process, the agentic loop
suspends and
/api/chat(agentic) returns, with HTTP 200:The client detects
tool_pending == true, executes the named MCP tool withtool_input, and captures the output as a string.2. Client → soul: result, to resume
Body:
session_idcomes from the URL path (thesession_idfield of the envelope).call_idshould echo the envelope'scall_id. If omitted/mismatched, the soulfalls back to the
tool_use_idit suspended on, so a partial client still resumes.contentis the tool's textual result (truncated to 6000 chars upstream, likelocal tool results).
3. Soul resumes
The soul appends the result as a
tool_resultblock bound to the originaltool_use_id, re-enters the agentic loop, and returns the same envelope shape:tool_pendingenvelope if the continuation hits a further MCP tool.The bridge is fully chainable across multiple MCP calls in one turn.
The saved continuation is one-shot: it is cleared on resume, so a
session_idcannot be replayed.
Implementation notes
chat.el:is_builtin_tool,next_bridge_id, refactor of the agentic loop into aresumable
agentic_loop(session_id, model, safe_sys, tools_json, messages, h, tools_log),plus
bridge_save,agentic_resume,handle_tool_result. The suspended turn's fullmessage history (including the assistant turn that requested the tool) is persisted in
soul state under
mcp_bridge:<session_id>.routes.el: routesPOST /api/sessions/{id}/tool_resulttohandle_tool_result.handle_chat_agentic) used by thedesktop app. The DHARMA-room agentic loop (
handle_dharma_room_turn_agentic) is leftunchanged for now; it still runs only built-ins.
Verification
~/el-sdk/elc --target=c <file>exits 0 with no stderr forchat.el,routes.el, and the fullsoul.elimport graph.only and needs Will's build to ship and test end-to-end.
🤖 Generated with Claude Code
When handle_chat_agentic hits a tool the soul cannot run in-process (an MCP connector/plugin surfaced by the Kotlin desktop app), instead of returning "unknown tool" it now suspends the agentic loop and returns a tool_pending envelope so the CLIENT executes the tool and posts the result back. Built-in tools (read_file/write_file/web_get/search_memory/run_command) and Anthropic's native web_search are unchanged. Client contract: - Soul returns (HTTP 200) on an unknown tool: { "tool_pending": true, "session_id": "br-...", "call_id": "<tool_use_id>", "tool_name": "...", "tool_input": { ... }, "model": "...", "agentic": true, "tools_used": [...] } - Client runs the MCP tool, then POSTs to /api/sessions/{session_id}/tool_result with body: { "call_id": "<the call_id from the envelope>", "content": "<MCP tool output as a string>" } - Soul resumes the loop and returns the same envelope shape: either a final { "reply": ..., "tools_used": [...] } or another tool_pending if the continuation needs a further MCP tool (fully chainable). Saved continuation is one-shot (cleared on resume). elc-verified (--target=c, exit 0, no stderr) on chat.el, routes.el, and the full soul.el import graph. Needs Will's build to ship. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>testto feat(soul): MCP tool-bridge — suspend agentic loop for client-executed toolsHandoff (auto) — prereq #16.
⚠ OVERLAPS #9 by ~80%: identical changes to awareness.el (+402 -9), mcp-wrapper/src/main.el (+831), neuron-api.el (+173), mcp-proxy, and the same docs. #5 and #9 are the same underlying work in two giant diffs — they will conflict. RECONCILE with #9 before merging either (see #9; likely #9 is canonical).
WHAT real: awareness.el +402, chat.el +184, mcp-wrapper +831, neuron-api.el +173, soul.el +94 -25, routes.el +25.
REAL SOURCE: ~2,181 lines (not 70k — the rest was generated dist/*.c).
RISK: higher — touches soul.el + awareness.el (cognitive core).
ORDER: last, as part of the #5/#9 reconciliation.
Pull request closed