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.
HTTP Transport (Recommended)
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:
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:
const mcpClient = await createMCPClient({
transport: {
type: 'sse',
url: 'https://my-server.com/sse',
headers: { Authorization: 'Bearer my-api-key' },
},
});Stdio Transport (Local Only)
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:
// 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:
const result = await generateText({
model,
tools: mcpClient.tools({
name: 'search_files',
// Explicit schema definition
}),
prompt: '...',
});9.4 Error Handling and Retries
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:
// 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:
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):
// 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:
const mcpClient = await createMCPClient({
transport: {
type: 'sse',
url: 'https://your-server.com/sse',
},
capabilities: {
elicitation: {},
},
});Register a handler:
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:
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
- Create an MCP client connecting to a local server via stdio; list its tools and use one in a
generateTextcall. - Connect to a remote MCP server via HTTP; configure auth headers and test tool discovery.
- 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.