MCP Integration Guide: Connecting AI Models to External Data
Step-by-step tutorial on implementing Model Context Protocol (MCP) integrations. Learn how to enhance AI capabilities with real-time data access.
MCP Integration Guide: Connecting AI Models to External Data
The Model Context Protocol (MCP) is revolutionizing how AI models interact with external data sources. This comprehensive guide will walk you through everything you need to know to implement MCP integrations, from basic concepts to production-ready implementations.
What is MCP?
Model Context Protocol (MCP) is an open protocol that enables AI models to securely connect to external data sources, APIs, and tools. Think of it as a standardized way for AI to access real-time information beyond its training data.
Key Benefits
- Real-time data access - Connect to live databases, APIs, and services
- Enhanced context - Provide models with up-to-date information
- Tool integration - Enable AI to use external tools and services
- Security - Controlled, audited access to sensitive systems
- Standardization - Works across different AI models and platforms
MCP Architecture Overview
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ AI Model │ ←──────→ │ MCP Server │ ←──────→ │ Data │
│ (Claude, │ MCP │ (Your API) │ API │ Source │
│ GPT, etc) │ Protocol │ │ Calls │ (DB, APIs) │
└─────────────┘ └──────────────┘ └─────────────┘
Getting Started
Prerequisites
- Basic understanding of REST APIs
- Node.js 18+ or Python 3.9+
- API access to an AI model (Claude, ChatGPT, etc.)
- A data source to connect (database, API, or file system)
Installation
Node.js:
npm install @anthropic-ai/sdk @modelcontextprotocol/sdk
# or
yarn add @anthropic-ai/sdk @modelcontextprotocol/sdk
Python:
pip install anthropic mcp-sdk
Your First MCP Integration
Let's build a simple MCP server that provides AI models with access to a product database.
Step 1: Define Your Data Source
// database.ts
interface Product {
id: string;
name: string;
price: number;
stock: number;
category: string;
}
// Simulated database
const products: Product[] = [
{ id: "1", name: "Laptop", price: 999, stock: 15, category: "Electronics" },
{ id: "2", name: "Mouse", price: 29, stock: 50, category: "Electronics" },
{ id: "3", name: "Desk", price: 299, stock: 8, category: "Furniture" }
];
export async function getProducts(category?: string): Promise<Product[]> {
if (category) {
return products.filter(p => p.category === category);
}
return products;
}
export async function getProduct(id: string): Promise<Product | null> {
return products.find(p => p.id === id) || null;
}
export async function updateStock(id: string, quantity: number): Promise<boolean> {
const product = products.find(p => p.id === id);
if (product && product.stock >= quantity) {
product.stock -= quantity;
return true;
}
return false;
}
Step 2: Create MCP Server
// mcp-server.ts
import { MCPServer } from '@modelcontextprotocol/sdk';
import { getProducts, getProduct, updateStock } from './database';
const server = new MCPServer({
name: 'Product Database MCP',
version: '1.0.0',
description: 'Access to product inventory database'
});
// Define available tools
server.addTool({
name: 'list_products',
description: 'List all products, optionally filtered by category',
inputSchema: {
type: 'object',
properties: {
category: {
type: 'string',
description: 'Filter by product category (optional)',
enum: ['Electronics', 'Furniture', 'Clothing']
}
}
},
handler: async (input) => {
const products = await getProducts(input.category);
return {
products,
count: products.length
};
}
});
server.addTool({
name: 'get_product_details',
description: 'Get detailed information about a specific product',
inputSchema: {
type: 'object',
properties: {
productId: {
type: 'string',
description: 'The unique product ID'
}
},
required: ['productId']
},
handler: async (input) => {
const product = await getProduct(input.productId);
if (!product) {
throw new Error(`Product with ID ${input.productId} not found`);
}
return product;
}
});
server.addTool({
name: 'check_availability',
description: 'Check if a product is available in requested quantity',
inputSchema: {
type: 'object',
properties: {
productId: { type: 'string' },
quantity: { type: 'number', minimum: 1 }
},
required: ['productId', 'quantity']
},
handler: async (input) => {
const product = await getProduct(input.productId);
if (!product) {
return { available: false, reason: 'Product not found' };
}
return {
available: product.stock >= input.quantity,
currentStock: product.stock,
requested: input.quantity
};
}
});
// Start server
server.listen(3000);
console.log('MCP Server running on port 3000');
Step 3: Connect AI Model to MCP
// ai-client.ts
import Anthropic from '@anthropic-ai/sdk';
import { MCPClient } from '@modelcontextprotocol/sdk';
const anthropic = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY
});
const mcpClient = new MCPClient({
serverUrl: 'http://localhost:3000'
});
async function chatWithMCP(userMessage: string) {
// Get available tools from MCP server
const tools = await mcpClient.listTools();
// Create message with Claude
const response = await anthropic.messages.create({
model: 'claude-3-5-sonnet-20241022',
max_tokens: 1024,
tools: tools,
messages: [{
role: 'user',
content: userMessage
}]
});
// Handle tool calls
if (response.stop_reason === 'tool_use') {
const toolUse = response.content.find(block => block.type === 'tool_use');
if (toolUse) {
// Execute tool via MCP
const toolResult = await mcpClient.executeTool(
toolUse.name,
toolUse.input
);
// Continue conversation with tool result
const finalResponse = await anthropic.messages.create({
model: 'claude-3-5-sonnet-20241022',
max_tokens: 1024,
messages: [
{ role: 'user', content: userMessage },
{ role: 'assistant', content: response.content },
{
role: 'user',
content: [{
type: 'tool_result',
tool_use_id: toolUse.id,
content: JSON.stringify(toolResult)
}]
}
]
});
return finalResponse.content[0].text;
}
}
return response.content[0].text;
}
// Example usage
async function main() {
const response = await chatWithMCP(
"What electronics do you have in stock under $50?"
);
console.log(response);
}
main();
Advanced MCP Patterns
1. Authentication and Security
server.addMiddleware({
name: 'auth',
handler: async (context, next) => {
const apiKey = context.headers['x-api-key'];
if (!apiKey || !await validateApiKey(apiKey)) {
throw new Error('Unauthorized');
}
// Add user context
context.user = await getUserFromApiKey(apiKey);
return next();
}
});
2. Rate Limiting
import { RateLimiter } from 'limiter';
const limiter = new RateLimiter({
tokensPerInterval: 100,
interval: 'minute'
});
server.addMiddleware({
name: 'rateLimit',
handler: async (context, next) => {
const remaining = await limiter.removeTokens(1);
if (remaining < 0) {
throw new Error('Rate limit exceeded');
}
return next();
}
});
3. Caching
import NodeCache from 'node-cache';
const cache = new NodeCache({ stdTTL: 600 }); // 10 minutes
server.addTool({
name: 'expensive_query',
handler: async (input) => {
const cacheKey = JSON.stringify(input);
// Check cache first
const cached = cache.get(cacheKey);
if (cached) {
return { ...cached, cached: true };
}
// Execute expensive operation
const result = await expensiveOperation(input);
// Cache result
cache.set(cacheKey, result);
return result;
}
});
4. Error Handling
server.addErrorHandler({
handler: async (error, context) => {
// Log error
console.error('MCP Error:', error);
// Send to monitoring service
await monitoringService.logError({
error: error.message,
stack: error.stack,
context: {
tool: context.toolName,
input: context.input,
user: context.user
}
});
// Return user-friendly error
return {
error: true,
message: 'An error occurred processing your request',
code: error.code || 'INTERNAL_ERROR'
};
}
});
Production Best Practices
1. Use TypeScript for Type Safety
// types.ts
export interface MCPToolInput {
[key: string]: string | number | boolean | object;
}
export interface MCPToolOutput {
[key: string]: any;
}
export interface MCPTool {
name: string;
description: string;
inputSchema: JSONSchema;
handler: (input: MCPToolInput) => Promise<MCPToolOutput>;
}
2. Implement Health Checks
server.addHealthCheck({
name: 'database',
handler: async () => {
try {
await database.ping();
return { healthy: true };
} catch (error) {
return { healthy: false, error: error.message };
}
}
});
3. Add Monitoring and Logging
import { Logger } from 'winston';
const logger = new Logger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'mcp-error.log', level: 'error' }),
new winston.transports.File({ filename: 'mcp-combined.log' })
]
});
server.addMiddleware({
name: 'logging',
handler: async (context, next) => {
const start = Date.now();
logger.info('MCP Request', {
tool: context.toolName,
input: context.input,
user: context.user?.id
});
try {
const result = await next();
const duration = Date.now() - start;
logger.info('MCP Response', {
tool: context.toolName,
duration,
success: true
});
return result;
} catch (error) {
const duration = Date.now() - start;
logger.error('MCP Error', {
tool: context.toolName,
duration,
error: error.message,
stack: error.stack
});
throw error;
}
}
});
4. Version Your Tools
server.addTool({
name: 'get_product_v2',
version: '2.0.0',
description: 'Get product details with enhanced metadata',
handler: async (input) => {
// New implementation
}
});
// Keep old version for backwards compatibility
server.addTool({
name: 'get_product',
version: '1.0.0',
deprecated: true,
deprecationMessage: 'Use get_product_v2 instead',
handler: async (input) => {
// Old implementation
}
});
Common Use Cases
1. Database Integration
- Customer data lookup
- Order management
- Inventory queries
- Analytics and reporting
2. API Integration
- Weather data
- Stock prices
- News feeds
- Social media data
3. Internal Tools
- Ticket systems (Jira, Linear)
- Documentation (Confluence, Notion)
- Communication (Slack, Teams)
- Code repositories (GitHub, GitLab)
4. Custom Business Logic
- Pricing calculations
- Recommendation engines
- Compliance checks
- Data validation
Troubleshooting
Common Issues
1. Tool Not Found
// Ensure tool is properly registered
console.log(await mcpClient.listTools());
2. Authentication Failures
// Verify API keys and permissions
console.log('Auth headers:', context.headers);
3. Timeout Errors
// Increase timeout for slow operations
server.setTimeout(30000); // 30 seconds
4. Schema Validation Errors
// Use JSON Schema validator
import Ajv from 'ajv';
const ajv = new Ajv();
const validate = ajv.compile(inputSchema);
if (!validate(input)) {
console.error('Validation errors:', validate.errors);
}
Testing Your MCP Integration
// mcp-server.test.ts
import { describe, it, expect } from 'vitest';
import { MCPClient } from '@modelcontextprotocol/sdk';
describe('MCP Server', () => {
const client = new MCPClient({ serverUrl: 'http://localhost:3000' });
it('should list all products', async () => {
const result = await client.executeTool('list_products', {});
expect(result.products).toBeInstanceOf(Array);
expect(result.count).toBeGreaterThan(0);
});
it('should get product details', async () => {
const result = await client.executeTool('get_product_details', {
productId: '1'
});
expect(result.id).toBe('1');
expect(result.name).toBeDefined();
});
it('should handle invalid product ID', async () => {
await expect(
client.executeTool('get_product_details', { productId: 'invalid' })
).rejects.toThrow('Product with ID invalid not found');
});
});
Deployment
Docker
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
EXPOSE 3000
CMD ["node", "dist/mcp-server.js"]
Docker Compose
version: '3.8'
services:
mcp-server:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/products
- API_KEY=your_api_key
depends_on:
- db
db:
image: postgres:15
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=products
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
Next Steps
- Explore MCP Examples Repository
- Join MCP Community Discord
- Read MCP Specification
- Build your own MCP integrations!
Conclusion
MCP integration opens up endless possibilities for enhancing AI capabilities with real-time data and tools. Start with simple integrations and gradually build more sophisticated systems as you learn.
Ready to build your first MCP integration? Share what you're building in the comments!
Related Articles:
Tags
Enjoyed this article?
Get more AI productivity tips and prompt engineering insights delivered to your inbox weekly.