From e029c3ed0add2ba55cc9c68b2bc3920e56d32888 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 30 Sep 2026 17:20:47 +0200 Subject: [PATCH] feat(coding-agent): stop waiting for MCP servers on the first prompt The first prompt waits only for servers with direct tools. Other servers connect in the background; codemode scripts, tool_search, and the resource tools wait for the servers they need. codemode and tool_search are activated from the config. Tool declarations no longer depend on connected servers: the codemode description leaves out deferred tools, and the tool_search description no longer lists sources. Servers are listed in an mcp_servers system prompt section instead, set in before_agent_start from the config and connected state, so changes are appended to the conversation. describeNamespace() and searchTools() accept mcp__dev-radius, mcp__dev_radius, dev-radius, and dev_radius. closes #10212 --- packages/coding-agent/CHANGELOG.md | 7 +- packages/coding-agent/docs/cli.md | 2 +- packages/coding-agent/docs/extensions.md | 2 +- packages/coding-agent/docs/mcp.md | 8 +- packages/coding-agent/src/core/mcp-servers.ts | 4 +- .../src/extensions/codemode/execute.ts | 17 +- .../src/extensions/codemode/tool.ts | 47 ++-- .../coding-agent/src/extensions/mcp/cli.ts | 2 +- .../coding-agent/src/extensions/mcp/index.ts | 254 ++++++++++++++--- .../src/extensions/tool-search/tool.ts | 31 +-- .../coding-agent/test/mcp-extension.test.ts | 44 +++ .../test/suite/agent-session-mcp.test.ts | 261 +++++++++++++++--- .../coding-agent/test/tool-search.test.ts | 43 +-- 13 files changed, 550 insertions(+), 172 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 68721de9e..11bb59036 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -4,13 +4,14 @@ ### Added -- Added a `description` field for MCP servers (`pi mcp add --description`), shown next to the server in the `codemode` and `tool_search` descriptions, and a `describeNamespace(name)` codemode helper that returns a namespace's instructions and tool names. +- Added a `description` field for MCP servers (`pi mcp add --description`), shown with the server in the system prompt and used to rank its tools in tool search, and a `describeNamespace(name)` codemode helper that returns a namespace's instructions and tool names. `describeNamespace()` and `searchTools()` accept a namespace as `mcp__dev-radius`, `mcp__dev_radius`, `dev-radius`, or `dev_radius`. - Added an `oauth.clientName` setting for MCP servers (`pi mcp add --oauth-client-name`) to change the client name sent during OAuth client registration, for servers such as Figma that only accept known clients ([#10226](https://github.com/earendil-works/pi/issues/10226)). ### Changed -- MCP servers with the default `codemode` exposure no longer list their tools in the `codemode` description; scripts find them with `searchTools()`. `codemode-deferred` is now an alias for `codemode`. Use `direct` exposure for tools the model should see without searching ([#10212](https://github.com/earendil-works/pi/issues/10212)). -- The `codemode` description no longer includes tool counts or MCP server instructions, so it no longer changes when a server's tool list changes. Scripts read server instructions with `describeNamespace()` ([#10212](https://github.com/earendil-works/pi/issues/10212)). +- MCP servers with the default `codemode` exposure no longer appear in the `codemode` description; scripts find them with `searchTools()`. `codemode-deferred` is now an alias for `codemode`. Use `direct` exposure for tools the model should see without searching ([#10212](https://github.com/earendil-works/pi/issues/10212)). +- The `codemode` description no longer includes deferred tools, tool counts, or MCP server instructions, so it no longer changes when MCP servers connect or change their tools. The `tool_search` description no longer lists the servers whose tools it can load, for the same reason. Servers are listed instead in an `mcp_servers` system prompt section with a one-line summary, updated at the start of each prompt; a changed section is appended to the conversation. Scripts read server instructions with `describeNamespace()` ([#10212](https://github.com/earendil-works/pi/issues/10212)). +- The first prompt no longer waits for MCP servers without `direct` tools. They connect in the background and are waited for when a codemode script names them, a script searches tools, or `tool_search` runs ([#10212](https://github.com/earendil-works/pi/issues/10212)). ### Fixed diff --git a/packages/coding-agent/docs/cli.md b/packages/coding-agent/docs/cli.md index f9636193a..8e1300390 100644 --- a/packages/coding-agent/docs/cli.md +++ b/packages/coding-agent/docs/cli.md @@ -171,7 +171,7 @@ A script may start with an options line such as `// @options: {"max_output_token While `codemode` is active, `codemode.mode` in [settings](settings.md#tools) decides how the other tools are presented. With `on` (default) declared tools keep being declared and their descriptions show how to call them from scripts. With `only` they are hidden from the model and listed in the `codemode` description instead, so the model calls them through scripts. -The `codemode` description lists the callable tools with their TypeScript declarations, grouped by namespace (for example one MCP server). Tools with `deferred` exposure, which includes MCP tools with the default `codemode` exposure, are not listed; their namespace is listed with its description. Declarations share a budget of 3000 estimated tokens (`codemode.inlineBudget` in [settings](settings.md#tools)), and the description says whether the list is complete. Scripts find the rest with `await searchTools(query, { limit, namespace })`, which ranks tools with BM25, and `await describeTool(name)`, or by filtering `ALL_TOOLS`. `await describeNamespace(name)` returns a namespace's description, its instructions (for MCP servers, the server instructions), and the names of its tools. +The `codemode` description lists the callable tools with their TypeScript declarations, grouped by namespace (for example one MCP server). Tools with `deferred` exposure, which includes MCP tools with the default `codemode` exposure, are not listed and do not affect the description, so it stays the same while MCP servers connect. Declarations share a budget of 3000 estimated tokens (`codemode.inlineBudget` in [settings](settings.md#tools)). Scripts find the rest with `await searchTools(query, { limit, namespace })`, which ranks tools with BM25, and `await describeTool(name)`, or by filtering `ALL_TOOLS`. `await describeNamespace(name)` returns a namespace's description, its instructions (for MCP servers, the server instructions), and the names of its tools. Tools with an output schema resolve to structured values: `bash` to `{ output, truncated, full_output_path?, exit_code, wall_time_seconds }`, also for non-zero exit codes, and MCP tools to their `CallToolResult`. Other tools resolve to their text output. The `output` of `bash` is not limited to the 2000 lines or 50KB the model sees: it holds up to 1 MiB, and longer output keeps its first and last 512 KiB around an omission marker, with `truncated` set and the full output in `full_output_path`. diff --git a/packages/coding-agent/docs/extensions.md b/packages/coding-agent/docs/extensions.md index b5ee0d690..9ecc4d995 100644 --- a/packages/coding-agent/docs/extensions.md +++ b/packages/coding-agent/docs/extensions.md @@ -177,7 +177,7 @@ pi.on("tool_call", async (event, ctx) => { }); ``` -A tool that orchestrates other tools can adjust what the model sees while it is active with `prepareLoadout(loadout)`. It runs whenever the active tools change and receives the declared tools, the callable tools, and every registered tool with its exposure and namespace. It returns replacement `descriptions` for declared tools (including its own) and `hiddenDeclarations`: active tools whose declarations requests leave out while they stay active and callable. `codemode` and `tool_search` use only this hook, `exposure`, and `ctx.executeTool()`, so another tool can implement the same behavior under a different name. +A tool that orchestrates other tools can adjust what the model sees while it is active with `prepareLoadout(loadout)`. It runs whenever the active tools change and receives the declared tools, the callable tools, and every registered tool with its exposure and namespace. It returns replacement `descriptions` for declared tools (including its own) and `hiddenDeclarations`: active tools whose declarations requests leave out while they stay active and callable. `codemode` uses only this hook, `exposure`, and `ctx.executeTool()`, so another tool can implement the same behavior under a different name. ### Activate tools dynamically diff --git a/packages/coding-agent/docs/mcp.md b/packages/coding-agent/docs/mcp.md index 9435f1a1f..073481d55 100644 --- a/packages/coding-agent/docs/mcp.md +++ b/packages/coding-agent/docs/mcp.md @@ -58,7 +58,7 @@ Both server types support: - `timeout`: per-request timeout in seconds (default 60). Progress notifications reset it. - `enabled: false`: keep the entry without connecting to it. - `exposure` and `toolExposure`: control how tools reach the model (see [Control tool exposure](#control-tool-exposure)). -- `description`: what the server offers, in a sentence. The `codemode` and `tool_search` descriptions show it next to the server, so the model knows what to search for. +- `description`: what the server offers, in a sentence. It lists the server in the system prompt (see [Control tool exposure](#control-tool-exposure)), tool search ranks the server's tools by it, and codemode's `describeNamespace()` returns it. Without it, the first line of the server instructions is used once the server connects. Keep personal servers and servers with credentials in the user-level file. Use the project file only for servers the project requires, and only in trusted projects. @@ -87,7 +87,7 @@ Run `pi mcp list` to connect to every enabled server and print its state, tools, Pi reports configuration errors, failed connections, and required sign-ins once after startup. Server logging notifications are appended to `~/.pi/agent/mcp.log` as `