How to Use MCP with Vercel AI SDK: A Developer's Guide
In the rapidly evolving landscape of AI application development, two powerful technologies are converging to create unprecedented capabilities for developers: Anthropic's Model Context Protocol (MCP) and Vercel's AI SDK. MCP represents Anthropic's approach to extending Claude's capabilities beyond basic chat interactions, allowing it to interact with external tools and services. When combined with Vercel AI SDK—a framework specifically designed for building AI-powered applications with streaming capabilities—developers gain the ability to create sophisticated, context-aware applications that leverage Claude's reasoning abilities while maintaining responsive, streaming interfaces. This comprehensive guide walks through the practical implementation of integrating MCP capabilities with Vercel AI SDK, enabling you to build applications that maintain context, execute external operations, and deliver results through optimized UI patterns.
GitHub Vercel AI SDK MCP Integration Repository (opens in a new tab)
Prerequisites
Before diving into the integration process, ensure you have the following prerequisites in place:
- Node.js 18.x or later
- npm 8.x or later
- A Vercel account
- An Anthropic API key with Claude 3 access
- Basic familiarity with Next.js and React
- Understanding of async/await patterns
- Experience with streaming API responses
Understanding the Architecture
To properly implement MCP with Vercel AI SDK, it's crucial to understand the underlying architecture:
MCP Components
MCP operates on a client-server model:
- Client (Claude): Makes requests to MCP servers
- MCP Servers: Provide specialized capabilities
- Protocol: JSON-based communication over HTTP
MCP allows Claude to:
- Execute code
- Retrieve information from external services
- Manipulate files and data
- Maintain context across interactions
Vercel AI SDK Structure
Vercel AI SDK provides:
- Streaming response handling
- UI components for chat interfaces
- Efficient token management
- Model-agnostic API abstractions
The integration architecture combines these technologies:
┌──────────────┐ ┌───────────────┐ ┌──────────────┐
│ Vercel App │ <---> │ AI SDK Layer │ <---> │ MCP Handlers │
└──────────────┘ └───────────────┘ └──────────────┘
↑
↓
┌──────────────┐
│ MCP Servers │
└──────────────┘
Setting Up the Environment
1. Create a New Next.js Project with Vercel AI SDK
Start by creating a new Next.js project with the Vercel AI SDK:
npx create-next-app@latest mcp-vercel-ai-project
cd mcp-vercel-ai-project
npm install ai @anthropic-ai/sdk @vercel/ai
2. Configure Environment Variables
Create a .env.local
file in the root directory:
ANTHROPIC_API_KEY=your_api_key_here
MCP_ENABLED=true
MCP_SERVER_URL=http://localhost:3000
3. Set Up MCP Server
For development purposes, we'll create a simple MCP server:
mkdir mcp-server
cd mcp-server
npm init -y
npm install express cors body-parser
Create a basic server.js
file:
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const app = express();
app.use(cors());
app.use(bodyParser.json());
// MCP endpoints will be added here
app.post('/capabilities', (req, res) => {
res.json({
capabilities: [
{
name: "web_search",
description: "Search the web for information"
},
{
name: "code_execution",
description: "Execute code snippets in a secure environment"
}
]
});
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`MCP server running on port ${PORT}`);
});
Integrating MCP with Vercel AI SDK
1. Create an MCP Client Wrapper
First, we need to create a wrapper for MCP functionality. Create a file at lib/mcp-client.js
:
export class MCPClient {
constructor(serverUrl) {
this.serverUrl = serverUrl;
}
async getCapabilities() {
const response = await fetch(`${this.serverUrl}/capabilities`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({})
});
return await response.json();
}
async executeAction(action, params) {
const response = await fetch(`${this.serverUrl}/${action}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(params)
});
return await response.json();
}
}
export const mcpClient = new MCPClient(process.env.MCP_SERVER_URL || 'http://localhost:3000');
2. Create the API Route for Claude with MCP
Create a file at app/api/chat/route.js
:
import { AnthropicStream, StreamingTextResponse } from 'ai';
import Anthropic from '@anthropic-ai/sdk';
import { mcpClient } from '@/lib/mcp-client';
const anthropic = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
});
export async function POST(req) {
const { messages } = await req.json();
// Get MCP capabilities
const mcpCapabilities = process.env.MCP_ENABLED === 'true'
? await mcpClient.getCapabilities()
: { capabilities: [] };
// Prepare system message with MCP capabilities
const systemMessage = `You are Claude, an AI assistant by Anthropic. ${
mcpCapabilities.capabilities.length > 0
? `You have access to the following tools: ${
mcpCapabilities.capabilities.map(cap =>
`${cap.name}: ${cap.description}`).join(', ')
}`
: 'You do not have access to any external tools.'
}`;
try {
const response = await anthropic.messages.create({
model: 'claude-3-opus-20240229',
system: systemMessage,
messages,
stream: true,
max_tokens: 4000,
});
// Create a stream from the Anthropic response
const stream = AnthropicStream(response);
// Return the stream with the appropriate headers
return new StreamingTextResponse(stream);
} catch (error) {
console.error('Error:', error);
return new Response(JSON.stringify({ error: error.message }), {
status: 500,
headers: { 'Content-Type': 'application/json' },
});
}
}
3. Implement the Chat UI with MCP Support
Create a file at app/page.js
:
'use client';
import { useState, useRef, useEffect } from 'react';
import { useChat } from 'ai/react';
export default function Home() {
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat();
const [mcpEnabled, setMcpEnabled] = useState(false);
const messagesEndRef = useRef(null);
useEffect(() => {
// Check if MCP is enabled in environment
fetch('/api/mcp-status')
.then(res => res.json())
.then(data => setMcpEnabled(data.enabled))
.catch(() => setMcpEnabled(false));
// Scroll to bottom of messages
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
return (
<div className="flex flex-col h-screen max-w-4xl mx-auto p-4">
<h1 className="text-2xl font-bold mb-4">Claude with MCP via Vercel AI SDK</h1>
{mcpEnabled && (
<div className="bg-green-100 border border-green-400 text-green-700 px-4 py-2 rounded mb-4">
MCP is enabled! Claude can access external tools.
</div>
)}
<div className="flex-1 overflow-y-auto mb-4 border rounded p-4">
{messages.map((message, i) => (
<div key={i} className={`mb-4 ${message.role === 'user' ? 'text-blue-600' : 'text-green-600'}`}>
<div className="font-bold">{message.role === 'user' ? 'You' : 'Claude'}</div>
<div className="whitespace-pre-wrap">{message.content}</div>
</div>
))}
<div ref={messagesEndRef} />
</div>
<form onSubmit={handleSubmit} className="flex items-center">
<input
className="flex-1 border rounded p-2 mr-2"
value={input}
onChange={handleInputChange}
placeholder="Ask Claude something..."
/>
<button
type="submit"
className="bg-blue-500 text-white px-4 py-2 rounded"
disabled={isLoading}
>
{isLoading ? 'Thinking...' : 'Send'}
</button>
</form>
</div>
);
}
4. Implementing Advanced MCP Features - Parallel Streams
One of the key advantages of combining MCP with Vercel AI SDK is the ability to process multiple streams in parallel. Create a file at lib/parallel-mcp.js
:
import { mcpClient } from './mcp-client';
export async function executeParallelMCP(actions, updateStream) {
// Start multiple MCP actions in parallel
const actionPromises = actions.map(async (action) => {
try {
const result = await mcpClient.executeAction(action.name, action.params);
// Send incremental updates to the stream
updateStream(`[${action.name}]: Started processing`);
// Process the result in chunks to maintain streaming behavior
const chunks = chunkResult(result);
for (const chunk of chunks) {
updateStream(`[${action.name}]: ${chunk}`);
// Add a small delay to simulate streaming
await new Promise(resolve => setTimeout(resolve, 50));
}
return { action: action.name, success: true, result };
} catch (error) {
updateStream(`[${action.name}]: Error - ${error.message}`);
return { action: action.name, success: false, error: error.message };
}
});
// Wait for all promises to resolve
return await Promise.all(actionPromises);
}
function chunkResult(result) {
// Helper function to break large results into smaller chunks
if (typeof result === 'string') {
const chunkSize = 100;
const chunks = [];
for (let i = 0; i < result.length; i += chunkSize) {
chunks.push(result.substring(i, i + chunkSize));
}
return chunks;
} else {
return [JSON.stringify(result)];
}
}
Implementing Specific MCP Capabilities
1. Web Search Capability
Extend the MCP server with a web search function:
const axios = require('axios');
// In server.js
app.post('/web_search', async (req, res) => {
try {
const { query } = req.body;
// Implement a mock search for demonstration
const response = await axios.get(`https://api.bing.microsoft.com/v7.0/search?q=${encodeURIComponent(query)}`, {
headers: {
'Ocp-Apim-Subscription-Key': process.env.BING_API_KEY
}
});
res.json({
results: response.data.webPages.value.slice(0, 5).map(page => ({
title: page.name,
url: page.url,
snippet: page.snippet
}))
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
2. Code Execution Capability
const { VM } = require('vm2');
app.post('/code_execution', async (req, res) => {
try {
const { code, language } = req.body;
if (language !== 'javascript') {
return res.status(400).json({ error: 'Only JavaScript execution is supported' });
}
// Create a secure VM
const vm = new VM({
timeout: 1000,
sandbox: {}
});
// Execute the code
const result = vm.run(code);
res.json({
result: String(result),
output_type: typeof result
});
} catch (error) {
res.json({
error: error.message,
output_type: 'error'
});
}
});
Optimizing for Production
1. MCP Request Caching
To improve performance, implement caching for MCP requests:
import { LRUCache } from 'lru-cache';
const mcpCache = new LRUCache({
max: 100, // Maximum items in cache
ttl: 1000 * 60 * 5, // Cache TTL: 5 minutes
});
export async function cachedMCPRequest(action, params) {
const cacheKey = `${action}:${JSON.stringify(params)}`;
if (mcpCache.has(cacheKey)) {
return mcpCache.get(cacheKey);
}
const result = await mcpClient.executeAction(action, params);
mcpCache.set(cacheKey, result);
return result;
}
2. Error Handling and Fallbacks
Implement robust error handling to manage MCP failures gracefully:
export async function safeMCPExecution(action, params, fallback) {
try {
return await mcpClient.executeAction(action, params);
} catch (error) {
console.error(`MCP execution failed for ${action}:`, error);
// Return fallback if provided
if (fallback) {
return typeof fallback === 'function' ? await fallback() : fallback;
}
// Otherwise return error information
return {
error: true,
message: error.message,
action
};
}
}
Troubleshooting Common Issues
Connection Problems
If you experience connection issues between your app and MCP server:
- Verify both are running on the expected ports
- Check for CORS issues by examining browser console
- Ensure environment variables are correctly loaded
- Validate your Anthropic API key has necessary permissions
Stream Handling Issues
If streams aren't processing correctly:
- Ensure you're using the latest version of Vercel AI SDK
- Check that all Promise rejections are properly caught
- Verify the client is correctly processing streamed chunks
- Add debug logging to track the flow of data
Conclusion
Integrating MCP with Vercel AI SDK opens up powerful new possibilities for AI application development. By following this guide, you've learned how to:
- Set up the necessary infrastructure for MCP with Vercel AI SDK
- Implement basic MCP capabilities like web search and code execution
- Handle parallel processing of MCP actions with streaming responses
- Optimize your implementation for production with caching and error handling
This integration allows you to build applications that combine the reasoning power of Claude with the interactivity and responsiveness of the Vercel AI SDK streaming architecture. As both technologies continue to evolve, the possibilities for creating sophisticated, context-aware AI applications will only expand.
Remember that working with AI and external tool integrations requires careful attention to security considerations, especially when implementing features like code execution. Always validate inputs, limit permissions appropriately, and consider the privacy implications of any data being processed through your MCP servers.