Skip to content

Chapter 9 · Multi-Step Tool Loops and MCP ​

Chapter goals:

  • Understand the role of Model Context Protocol (MCP): discover and use tools, resources, and prompts from external services via a standardized interface
  • Master the three transports of createMCPClient (HTTP / SSE / stdio) and their applicable scenarios
  • Learn to use mcpClient.tools() for Schema Discovery or explicit Schema Definition
  • Understand Resources, Completions, and Elicitation mechanisms
  • Master detection methods for tool definition drift (rug pull) attacks

9.1 What Is Model Context Protocol ​

AI SDK supports connecting to Model Context Protocol (MCP) servers to access their tools, resources, and prompts. This lets your AI application discover and use capabilities from various services through a standardized interface — one integration, usable everywhere.

The MCP client supports both the older initialization-based protocol version and the new stateless MCP 2026-07-28 version. The built-in stdio transport probes with server/discover first and auto-falls back to the initialize handshake for older servers.

9.2 Initializing an MCP Client ​

For production deployment, the HTTP transport is recommended (e.g., StreamableHTTPClientTransport). The stdio transport can only connect to local servers and cannot be deployed to production.

ts
import { createMCPClient } from '@ai-sdk/mcp';

const mcpClient = await createMCPClient({
  transport: {
    type: 'http',
    url: 'https://your-server.com/mcp',

    // Optional: configure HTTP headers
    headers: { Authorization: 'Bearer my-api-key' },

    // Optional: provide an OAuth client provider for automatic authorization
    authProvider: myOAuthClientProvider,

    // Optional: allow redirect responses (default 'error' to prevent SSRF)
    redirect: 'follow',
  },
});

You can also use the MCP official TypeScript SDK's StreamableHTTPClientTransport:

ts
import { createMCPClient } from '@ai-sdk/mcp';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';

const url = new URL('https://your-server.com/mcp');
const mcpClient = await createMCPClient({
  transport: new StreamableHTTPClientTransport(url, {
    sessionId: 'session_123',
  }),
});

SSE Transport ​

SSE is another HTTP-based transport option, also supporting headers and authProvider:

ts
const mcpClient = await createMCPClient({
  transport: {
    type: 'sse',
    url: 'https://my-server.com/sse',
    headers: { Authorization: 'Bearer my-api-key' },
  },
});

Stdio Transport (Local Only) ​

ts
import { createMCPClient } from '@ai-sdk/mcp';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
// Or use the stdio transport bundled with AI SDK:
// import { Experimental_StdioMCPTransport as StdioClientTransport } from '@ai-sdk/mcp/mcp-stdio';

const mcpClient = await createMCPClient({
  transport: new StdioClientTransport({
    command: 'node',
    args: ['server.js'],
  }),
});

9.3 Discovering and Using Tools ​

Once connected, discover available tools and use them in your AI calls:

ts
// List all available tools
const tools = await mcpClient.tools();

// Use in generateText
const result = await generateText({
  model,
  tools: tools,
  prompt: '...',
});

You can also explicitly define the schema instead of discovering at runtime:

ts
const result = await generateText({
  model,
  tools: mcpClient.tools({
    name: 'search_files',
    // Explicit schema definition
  }),
  prompt: '...',
});

9.4 Error Handling and Retries ​

ts
const mcpClient = await createMCPClient({
  transport: {
    type: 'http',
    url: 'https://your-server.com/mcp',
  },
  maxRetries: 2,
});

Retries are disabled by default. Built-in retries only target transient HTTP and network errors; JSON-RPC application-layer errors (e.g., invalid tool parameters) throw immediately without retrying; success responses with isError: true are also returned to the model without retrying.

⚠️ Only enable retries for retry-safe MCP tools. Retrying non-idempotent tools (e.g., sending emails, creating records) may cause duplicate side effects.

9.5 Resources, Completions, and Prompts ​

Resources are application-driven data sources — your application decides when to fetch them and passes them as context to the model (unlike tools which are model-controlled). The MCP client provides three methods:

ts
// List all available resources
const resources = await mcpClient.listResources();

// Read specific resource content by URI
const resourceData = await mcpClient.readResource({
  uri: 'file:///example/document.txt',
});

// List available resource templates
const templates = await mcpClient.listResourceTemplates();

Completions: When the server declares completions capability, you can request auto-completion suggestions from the server based on current partial parameter values:

ts
const completion = await mcpClient.complete({
  ref: {
    type: 'ref/resource',
    uri: 'file:///{path}',
  },
  argument: {
    name: 'path',
    value: 'doc',
  },
});

console.log(completion.completion.values);

For resource templates or prompts with multiple parameters, pass resolved values via context.arguments. If the connected server does not support the completions capability, the client throws MCPClientError.

Prompts are user-controlled templates (experimental feature):

ts
// List prompts
const prompts = await mcpClient.experimental_listPrompts();

// Get prompt messages, passing server-defined arguments
const prompt = await mcpClient.experimental_getPrompt({
  name: 'code_review',
  arguments: { code: 'function add(a, b) { return a + b; }' },
});

9.6 Handling Elicitation Requests ​

Elicitation is a mechanism where the MCP server requests additional information from the client during tool execution. For example, the server may need user input to complete a registration form, or confirmation for a sensitive operation. The MCP client simply forwards these requests from the server to your application code — how you handle them is up to you.

Declare capabilities when creating the client to enable:

ts
const mcpClient = await createMCPClient({
  transport: {
    type: 'sse',
    url: 'https://your-server.com/sse',
  },
  capabilities: {
    elicitation: {},
  },
});

Register a handler:

ts
mcpClient.onelicitation = async (request) => {
  // Handle the elicitation request
  return { accepted: true, value: 'user input' };
};

9.7 Tool Definition Drift Detection (Rug Pull) ​

A "rug pull" attack occurs when a connected MCP server changes its tool definitions mid-session — for example, replacing a benign tool with a malicious one. AI SDK provides detection mechanisms:

ts
const mcpClient = await createMCPClient({
  transport: { type: 'http', url: 'https://your-server.com/mcp' },
  // Enable drift detection
  detectToolDefinitionDrift: true,
});

When drift is detected, the client throws an error, preventing the model from calling unexpectedly changed tools.

Chapter Summary ​

  • MCP lets AI apps discover and use external service capabilities via a standardized interface;
  • Three transports: HTTP (recommended for production), SSE, and stdio (local only);
  • Resources are application-driven data sources; Completions provide auto-suggestions; Prompts are user-controlled templates;
  • Elicitation lets servers request additional info from clients during tool execution;
  • Tool definition drift detection prevents rug pull attacks;
  • Retries are disabled by default; only enable for idempotent tools.

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. Which MCP transport is recommended for production deployment?

2. What is the difference between Resources and Tools in MCP?

3. What does a 'rug pull' attack refer to in the MCP context?

4. When are built-in retries triggered for MCP clients?

🛠️ Hands-On Exercises ​

  1. Create an MCP client connecting to a local server via stdio; list its tools and use one in a generateText call.
  2. Connect to a remote MCP server via HTTP; configure auth headers and test tool discovery.
  3. Simulate a tool definition drift: start with one set of tools, then change the server's tool definitions; verify the drift detection mechanism catches the change.

Next: Chapter 10 · Reasoning Models