Advanced Tool Use & Agent Engineering

Inside the MCP Connector Request: mcp_servers, mcp_toolset, and Block Types

Every MCP connector call has two halves that must match exactly — the server definition and its toolset. Get the pairing wrong and the API rejects the request; get it right and the response speaks a slightly different dialect of tool use.

Claude 3P 101 · Updated July 2026 · Unofficial guide

The MCP connector lets the Messages API talk to remote Model Context Protocol (MCP) servers without you writing an MCP client. It is enabled with the beta header mcp-client-2025-11-20 and is available on the Claude API, Claude Platform on AWS, and Microsoft Foundry Hosted-on-Anthropic deployments — see the platform split if you are on Bedrock or Vertex AI, where it is not offered. This article is about what the request and response actually look like.

Half one: the mcp_servers array

The mcp_servers parameter tells the API how to reach each server. Every entry has four possible fields:

FieldRequiredRules
typeYesOnly "url" is supported
urlYesMust start with https://
nameYesUnique; must be referenced by exactly one toolset
authorization_tokenNoOAuth token you obtained yourself

Two constraints follow directly from the type and url rules: the server must be publicly exposed over HTTP (both Streamable HTTP and SSE transports work), and local STDIO servers — the kind you launch as a subprocess on your own machine — cannot be connected. For those, use the client-side MCP helpers instead. Also note the connector supports only the tool-calling part of the MCP specification: MCP prompts and MCP resources do not come through.

Half two: the matching mcp_toolset

Defining a server does not grant Claude any tools. For each server you must add an mcp_toolset entry to the ordinary tools array, referencing the server by name. A minimal two-server request body looks like this:

{
  "mcp_servers": [
    {"type": "url", "url": "https://mcp.example.com", "name": "crm"},
    {"type": "url", "url": "https://mcp.other.com", "name": "docs"}
  ],
  "tools": [
    {"type": "mcp_toolset", "mcp_server_name": "crm"},
    {"type": "mcp_toolset", "mcp_server_name": "docs"}
  ]
}

The validation is strict and symmetric: every mcp_server_name must match a defined server, and every defined server must be referenced by exactly one toolset. One dangling half in either direction is an error. The one deliberate exception: naming an unknown tool inside a toolset's per-tool configs only logs a backend warning, because MCP servers can add and remove tools dynamically. Toolsets also accept default_config and per-tool configs for allowlist and denylist filtering, plus cache_control for prompt-caching breakpoints.

If you used the deprecated mcp-client-2025-04-04 version: its tool_configuration object (with enabled and allowed_tools on the server definition) is exactly what the toolset replaces.

The response: two dedicated block types

With ordinary client tools, Claude returns a tool_use block, stops with stop_reason: "tool_use", and waits for your application to run the tool and send back a tool_result. MCP connector calls execute server-side, so the conversation instead contains two dedicated block types recording what already happened:

The practical difference for your code: there is no execute-and-reply loop to implement for these blocks. Your application's job is to log them, display them, or audit them — not to fulfil them. If you also pass regular client tools in the same request, you handle those exactly as before; the two systems coexist in one tools array.

Odds and ends worth knowing

Claude calls MCP tools when a request maps to a tool's described capability, not for general knowledge questions about the connected service — you can steer eagerness via the system prompt. mcp_servers also works inside the Message Batches API at regular Messages pricing. And the connector is not eligible for Zero Data Retention: tool definitions and results follow Anthropic's standard retention policy.

Where to go next

Continue with tool filtering patterns, running the OAuth flow yourself, and controlling context cost with defer_loading.

Sources