Skip to content

addMcpServer({ autoRegister: false }) — let consumers suppress MCP tool auto-merge to enable compact / search+execute patterns #1336

Description

@ishpr

Summary

@cloudflare/think's beforeTurn + TurnConfig.activeTools (#1278) and MCPClientManager.getAITools({ serverId }) (#1271) together almost enable the consolidated MCP pattern popularized by Cloudflare's own Code Mode: give agents an entire API in 1,000 tokens — replace a full tool catalog with a search + execute pair and save 99%+ of tool-definition tokens.

One gap remains: activeTools limits what the model can call, but the auto-merge of MCP tools into streamText's tool list happens before beforeTurn runs, so the raw schemas still ship in every turn's prompt. We pay the full context cost of N MCP tool definitions and then tell the model it's only allowed to call two of them. No net token savings.

Use case

We build a multi-tenant platform where customers attach MCP servers to their agents. A single enterprise MCP (Atlassian, GitHub Enterprise, Salesforce) brings 30–80 tools; attaching two or three pushes the callable surface past what most models reason over cleanly. We want to:

  1. Register our own mcp_search(query) + mcp_execute(server, tool, args) builtins backed by this.mcp.callTool.
  2. Hide the raw per-tool schemas from the prompt.
  3. Let the model discover MCP tools by keyword only when needed.

Steps 1 and 3 work with the current SDK. Step 2 needs SDK help.

Minimal repro

class MyAgent extends Think<Env> {
  getTools() {
    return {
      mcp_search: tool({ /* this.mcp.listTools() keyword ranked */ }),
      mcp_execute: tool({ /* this.mcp.callTool() */ }),
    };
  }
  beforeTurn(): TurnConfig {
    // activeTools gates calls, but SDK-auto-merged MCP schemas
    // still appear in the streamText tool_definitions payload.
    return { activeTools: ["mcp_search", "mcp_execute"] };
  }
}

Expected: prompt carries 2 tool definitions. Actual: 2 + N (where N = sum of attached MCP tool counts). For our workload, N ≈ 80–200, roughly 20k–50k tokens of schema per turn.

Three shapes that would close this (preference order)

  1. addMcpServer(name, url, { autoRegister: false }) — opt-in suppression of the auto-merge. Zero default-behavior change. Consumers who want raw tools call this.mcp.getAITools() explicitly and merge themselves. Smallest API surface.
  2. Let beforeTurn replace tools instead of only appending — e.g. TurnConfig { tools, replaceTools?: boolean } or a separate setTools field. More flexible, slightly larger API.
  3. Default getAITools() to { state: "ready" } filter AND respect a per-connection register flag — matches @threepointone's comment on Add optional server filter to getAITools() #1073 ("I wonder if getAITools should only return tools for active servers by default"). Most automatic, hardest to reason about from outside.

Why existing APIs don't solve this

Offer

Happy to send a PR against option (1). Smallest change: optional third arg on addMcpServer, store the flag on the connection, check it where the Think harness pulls mcp.getAITools() into the streamText call.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestmcpIssues related to MCP functionality

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions