Repository navigation
feat(extensions): give extensions the MCP servers a client passes, a way to tell the user, and turn barriers - #35
Merged
Conversation
…way to tell the user, and turn barriers The agent still has no MCP client and does not read mcp.json. The user chose an optional extension package (pi-mcp-py) that connects the servers a client passes in session/new, session/load and session/resume. Core, harness and CLI only get the data and the hooks that such an extension needs. Data and hooks: - ClientMcpServer (pi_agent_core.extensions): one server as plain data. The fields that can hold a token (command, args, env, url, headers) are repr=False. - ExtensionAPI.client_mcp_servers and claim_client_mcp_servers(names). AgentHarness.set_client_mcp_servers() takes the list before the extensions load; unclaimed_client_mcp_servers says which servers nobody took over. - ExtensionAPI.notify(text) shows one line to the user (NoticeEvent, an agent message in ACP). The model does not see it and the session does not save it. - ExtensionAPI.register_cleanup(callback): the public name of what the bridge already had. - ExtensionAPI.add_turn_barrier(wait): the harness awaits it before it builds the tool list of a turn. A wait that is cancelled leaves the phase idle. - HarnessBridge has four more members; test doubles must add them. CLI: - client_mcp.py turns the ACP models into records and hands them to the harness on session/new, load, resume and the reopen of a blank session. - The notice about ignored MCP servers names only the servers nobody claimed, and its text is correct for one server and for many. With the pi-mcp entry point installed it does not tell the user to install anything. - initialize offers http and sse servers only when the pi-mcp entry point is installed (package metadata only; nothing is imported). The wire shape is the same as before when it is not. - A line from notify() waits until the deferred setup has advertised the commands (the client drops earlier updates, zed#60199), then goes out in order. - prompt() waits for the turn barriers and lets session/cancel, closing the session and opening it again end the wait. Fix: a session that the client opened again (a second session/load, Zed's Retry) built a new harness and dropped the old one without close(), so the cleanup callbacks of its extensions never ran. The new harness now replaces the old one first, and the old one is closed. Design: docs/specs/2026-10-23-mcp-extension-design.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stacked on #34 (base:
fix/load-blank-session). The agent still has no MCP client. This PR gives an extension what it needs to connect the servers a client passes: the servers as data, a way to tell the user something, and a way to hold a turn back. The extension itself (pi-mcp-py) is the next PR.Also fixed here: a client that opens a session again (a second
session/load, Zed's Retry) got a new harness, and the old one was dropped withoutclose(). The cleanup of its extensions never ran, so an extension that starts a child process left it running. The new harness now takes the place first, then the old one is closed.Evidence
Before (the notice Zed showed, one server):
This agent does not connect to MCP servers, so the 1 MCP server the client passed (mcp-server-context7) were ignored and their tools are not available in this session. After:The client passed 1 MCP server (mcp-server-context7). This agent does not connect to MCP servers by itself, so its tools are not available in this session. To use it, install the pi-mcp-py package.With thepi-mcpentry point installed and nobody claiming the server:... No extension took it over, so its tools are not available in this session.Wire:
initializestill sends the samemcpCapabilitiesobject (allfalse) when thepi-mcpentry point is not installed (a test pins{"http": False, "sse": False, "acp": False, "_meta": None}). With it installed,httpandssearetrue. Only package metadata is read; nothing is imported.Tests: 103 new (
test_client_mcp_hooks.py18,test_client_mcp_harness.py32,test_client_mcp.py53) andtest_mcp_notice.pyrewritten. Four one-line mutations are each caught:pytest: Windows 2710 passed / 42 skipped; Linux (WSL2) 2732 passed / 20 skipped.
ruff check .andruff format --check .: clean (also with the CI-pinned ruff 0.16.0).Not done: no MCP client, no real server was connected (PR-B). The Zed behaviour of
session/newbefore the first worktree loads (zed#64611: emptymcpServers) is not handled; the agent cannot tell that case from "no servers".Merge Danger
Door: two-way, except the new public methods of
ExtensionAPI(client_mcp_servers,claim_client_mcp_servers,notify,register_cleanup,add_turn_barrier): when an extension package uses them, removing them breaks that package.Blast Radius: every ACP client that passes
mcp_servers; every author of aHarnessBridgedouble.HarnessBridgehas four more members. A test double that lacks them is no longer an instance of the Protocol (four test doubles in this repo needed the new members).pi-mcp-py, and that package is in the next PR and not on PyPI yet. Merge the two together, or accept a short time in which the hint points to a package that does not exist.