Skip to main content
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
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().
ParameterTypeDescription
clientMCPClient | MultiSessionClientThe MCP client to source tools from.
optionsAIAdapterOptionsOptional configuration (see below).

adapter.getTools()

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.

AIAdapter.getTools(client, options?) (static)

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

FieldTypeDefaultDescription
prefixstringServer ID (8 chars)Namespace prefix added to every tool name.
toolRouterToolRouterUse a ToolRouter for intelligent tool filtering. When set, only meta-tools (search_tools, get_tool_schema) are exposed to reduce context usage.

Tool naming

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.