Chat backend (SDK createQuery) never surfaces tools from slow-connecting plugin MCP servers, despite /mcp reporting them connected
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 23k
- Forks
- 5.9k
- Avg merge
- 11h 14m
- Merged PRs (30d)
- 357
Description
Summary
A plugin MCP server with a large tool count (95 tools) shows as connected in the native /mcp status view (run via an interactive claude CLI terminal), but its tools are never callable from a chat session driven by t3code's GUI backend. ToolSearch/the deferred-tool index never contains any of that server's tools for the life of the session, even after the server is confirmed connected. Smaller plugin servers on the same account (2, 13, 32, 47 tools) work fine from the same GUI session.
Environment
- t3code desktop, chat session backed by
apps/server/src/provider/Layers/ClaudeAdapter.ts @anthropic-ai/claude-agent-sdkpinned at0.3.170inpnpm-lock.yaml(current upstream is0.3.224)- Comparison terminal: native
claudeCLI v2.1.224, run interactively, same machine, same~/.claude.jsonconfig
Steps to reproduce
- Configure a plugin MCP server with a large tool surface (in my case ~95 tools) alongside several smaller plugin servers (2/13/32/47 tools).
- Open the native
claudeCLI in a terminal and run/mcp. All servers, including the large one, show✓ connected · N tools. Calling the large server's tools works (e.g. a skill built on top of it resolves and executes correctly). - Open a chat session through the t3code GUI (same config, same working directory) and ask the agent to use a tool from the large server.
- The agent can invoke the skill associated with that server (skill instructions load fine), but cannot find or call any of the server's underlying MCP tools —
ToolSearchwith the exact tool name, exact composite server name (plugin:<name>:<name>), and broad keyword queries all return "No matching deferred tools found." - Meanwhile the GUI's own
/mcppanel (apparently rendered by a separate native CLI/terminal path, not the chat backend) correctly shows the server asconnected · 95 tools. - The smaller plugin servers' tools are discoverable via
ToolSearchin the same chat session throughout.
Root cause (from reading apps/server/src/provider/Layers/ClaudeAdapter.ts)
The chat backend calls the SDK's createQuery() once per session (around the queryOptions construction near line 4093). Per the SDK changelog:
Breaking: MCP servers now connect in the background by default; sessions start immediately and slow servers report
status: "pending"ininituntil ready. SetMCP_CONNECTION_NONBLOCKING=0to restore the old behavior of waiting up to 5s before the first query, or mark a serveralwaysLoad: trueto require it in turn 1.
ClaudeAdapter.ts does not set MCP_CONNECTION_NONBLOCKING anywhere in the environment it builds (claudeEnvironment), and does not mark any server alwaysLoad: true. So the SDK's default non-blocking-connect behavior applies: the session's initial tool manifest is built from whatever's ready at that instant, and a slower-to-connect server (plausibly correlated with a much larger tool count / heavier handshake) can miss the window and get excluded.
Compounding this: apps/server/src never calls the SDK's reloadPlugins(), toggleMcpServer(), or mcpServerStatus() methods anywhere (confirmed via repo-wide grep). So even after the slow server finishes connecting, nothing re-syncs the session's tool manifest — the omission is permanent for the life of the session, not just a startup race that resolves itself.
This would explain the exact symptom: the server is genuinely connected (hence /mcp reporting it correctly, and the interactive CLI terminal working fine since it doesn't have this turn-1 manifest freeze), but the chat backend's one-shot createQuery() session never learns about tools that weren't ready in time.
Suggested fix
In apps/server/src/provider/Layers/ClaudeAdapter.ts:
- Set
MCP_CONNECTION_NONBLOCKING=0in theclaudeEnvironmentpassed tocreateQuery()so the SDK waits (up to 5s) for MCP servers to connect before turn 1, and/or mark heavier plugin serversalwaysLoad: trueif that's exposed per-server. - After session start, call
reloadPlugins()(or equivalent) once outstanding servers finish connecting, so long-running sessions pick up tools from servers that missed the initial window instead of never seeing them. - Consider bumping the pinned
@anthropic-ai/claude-agent-sdkdependency from0.3.170to latest (0.3.224) — several MCP-related fixes have landed since (e.g. server-added-at-runtime tool injection, background-plugin-install disconnect handling) that may be relevant even if not a direct fix for this issue.
Impact
Any user with a plugin MCP server large/slow enough to miss the initial connect window loses access to that server's tools for the entire chat session in the GUI, with no visible error — the server just silently never gets referenced in tool discovery, while the status UI reports it as healthy. This is confusing to debug because every visible signal (/mcp, skill loading) says the server is fine.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in apps/server/src/provider/Layers/ClaudeAdapter.ts around the queryOptions and createQuery construction, then compare it with the SDK changelog's MCP connection behavior. Trace how the session receives later MCP connection changes and whether the mentioned SDK methods are available. Done means a slow, large plugin server's tools become discoverable in the GUI chat session, not only in the status view.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 54/100