The AIAdapter transforms MCP tools into the ToolSet format expected by the Vercel AI SDK. Once you have a tool set, pass it to any AI SDK function that accepts a tools parameter — streamText, generateText, or the useChat hook.
Installation
Install @mcp-ts/sdk alongside the AI SDK and your model provider of choice.
npm install @mcp-ts/sdk ai @ai-sdk/openai
pnpm add @mcp-ts/sdk ai @ai-sdk/openai
yarn add @mcp-ts/sdk ai @ai-sdk/openai
Replace @ai-sdk/openai with any other AI SDK provider (@ai-sdk/anthropic, @ai-sdk/google, etc.).
Quick start
import { MultiSessionClient } from '@mcp-ts/sdk/server';
import { AIAdapter } from '@mcp-ts/sdk/adapters/ai';
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
const client = new MultiSessionClient('user_123');
await client.connect();
const tools = await AIAdapter.getTools(client);
const result = await streamText({
model: openai('gpt-4o'),
tools,
prompt: 'Search for the latest TypeScript release notes',
});
API reference
new AIAdapter(client, options?)
Creates an adapter instance. The constructor is non-blocking — tool fetching happens in getTools().
| Parameter | Type | Description |
|---|
client | MCPClient | MultiSessionClient | The MCP client to source tools from. |
options | AIAdapterOptions | Optional configuration (see below). |
Fetches the tool list from all connected MCP servers and converts each tool into an AI SDK Tool object with an inputSchema and an execute function. Returns a Promise<ToolSet>.
When passed a MultiSessionClient, the adapter queries every connected server in parallel and merges the results. Servers that fail to respond are skipped with a console error — the rest of the tool set is still returned.
A convenience wrapper that creates an AIAdapter and immediately calls getTools(). Use this when you need a tool set in a single expression.
const tools = await AIAdapter.getTools(client, { prefix: 'search' });
AIAdapterOptions
| Field | Type | Default | Description |
|---|
prefix | string | Server ID (8 chars) | Namespace prefix added to every tool name. |
toolRouter | ToolRouter | — | Use a ToolRouter for intelligent tool filtering. When set, only meta-tools (search_tools, get_tool_schema) are exposed to reduce context usage. |
Every tool name follows the pattern tool_<prefix>_<toolName>. The prefix defaults to the first eight characters of the server ID with hyphens removed.
tool_myapp_web_search
tool_myapp_read_file
tool_abcd1234_get_weather
Set an explicit prefix when you need predictable names or to avoid collisions between multiple adapter instances.
Next.js chat route example
The following example shows a complete Next.js App Router API route that streams a response with MCP tools.
// app/api/chat/route.ts
import { NextRequest } from 'next/server';
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { MultiSessionClient } from '@mcp-ts/sdk/server';
import { AIAdapter } from '@mcp-ts/sdk/adapters/ai';
export const POST = async (req: NextRequest) => {
const { messages, userId } = await req.json();
const client = new MultiSessionClient(userId);
await client.connect();
const tools = await AIAdapter.getTools(client);
const result = await streamText({
model: openai('gpt-4o'),
messages,
tools,
maxSteps: 5,
});
return result.toDataStreamResponse();
};
Using a prefix to avoid collisions
When you run two adapter instances side by side — for example, one for a search server and one for a file system server — use distinct prefixes so their tool names never clash.
import { MCPClient } from '@mcp-ts/sdk/server';
import { AIAdapter } from '@mcp-ts/sdk/adapters/ai';
const searchClient = new MCPClient({ /* … */ });
const fsClient = new MCPClient({ /* … */ });
await Promise.all([searchClient.connect(), fsClient.connect()]);
const searchTools = await AIAdapter.getTools(searchClient, { prefix: 'search' });
const fsTools = await AIAdapter.getTools(fsClient, { prefix: 'fs' });
const tools = { ...searchTools, ...fsTools };
// tool_search_web_search, tool_fs_read_file, …
Reducing context with ToolRouter
When you have many tools, passing all their schemas to the model wastes context tokens. Supply a ToolRouter to expose only meta-tools (search_tools and get_tool_schema) — the model searches for tools at runtime instead of receiving every schema upfront.
import { MultiSessionClient } from '@mcp-ts/sdk/server';
import { AIAdapter } from '@mcp-ts/sdk/adapters/ai';
import { ToolRouter } from '@mcp-ts/sdk/shared';
const client = new MultiSessionClient('user_123');
await client.connect();
const router = new ToolRouter(client, { strategy: 'search' });
const tools = await AIAdapter.getTools(client, { toolRouter: router });
// Only search_tools and get_tool_schema are sent to the model
Using a ToolRouter with the search strategy can reduce context window usage by 80–95% when you have a large number of MCP tools.
Error handling
The execute function on each tool catches errors from the MCP server and re-throws them as standard Error objects with the message "Tool execution failed: <original message>". The AI SDK surface will receive the error and can decide how to handle it (retry, inform the user, etc.).
// The adapter's execute function wraps errors automatically:
execute: async (args) => {
try {
return await client.callTool(tool.name, args);
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
throw new Error(`Tool execution failed: ${errorMessage}`);
}
}
If the underlying MCPClient is not connected when getTools() is called, the adapter returns an empty ToolSet ({}) rather than throwing. The AI SDK will simply have no tools available for that session.