pingdotgg / pingdotgg/t3code

Chat backend (SDK createQuery) never surfaces tools from slow-connecting plugin MCP servers, despite /mcp reporting them connected

Open
#5,605 0 comments 0 reactions 0 assignees View on GitHub

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-sdk pinned at 0.3.170 in pnpm-lock.yaml (current upstream is 0.3.224)
  • Comparison terminal: native claude CLI v2.1.224, run interactively, same machine, same ~/.claude.json config

Steps to reproduce

  1. 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).
  2. Open the native claude CLI 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).
  3. 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.
  4. 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 — ToolSearch with the exact tool name, exact composite server name (plugin:<name>:<name>), and broad keyword queries all return "No matching deferred tools found."
  5. Meanwhile the GUI's own /mcp panel (apparently rendered by a separate native CLI/terminal path, not the chat backend) correctly shows the server as connected · 95 tools.
  6. The smaller plugin servers' tools are discoverable via ToolSearch in 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" in init until ready. Set MCP_CONNECTION_NONBLOCKING=0 to restore the old behavior of waiting up to 5s before the first query, or mark a server alwaysLoad: true to 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:

  1. Set MCP_CONNECTION_NONBLOCKING=0 in the claudeEnvironment passed to createQuery() so the SDK waits (up to 5s) for MCP servers to connect before turn 1, and/or mark heavier plugin servers alwaysLoad: true if that's exposed per-server.
  2. 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.
  3. Consider bumping the pinned @anthropic-ai/claude-agent-sdk dependency from 0.3.170 to 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.