MCP Server Examples
Integration examples and common patterns for the MCP Server.
Table of Contents
- Express Integration
- Direct Tool Calls
- Claude Desktop Configuration
- Agent Queries
- Custom Middleware
- Error Handling Patterns
Express Integration
Basic MCP Route
import express from 'express';
import {
toolHandlers,
buildSecurityContext,
smartRouter,
RouteType,
handleAgentQuery,
ActionTypes
} from './mcp/index.js';
const app = express();
app.use(express.json());
// MCP command endpoint
app.post('/api/mcp/command', authMiddleware, async (req, res) => {
try {
const { query } = req.body;
const user = req.user;
// Build security context
const securityContext = await buildSecurityContext({
email: user.email,
userId: user.userId,
apiKey: user.apiKey,
isAdmin: user.isAdmin,
timezone: user.timezone
});
// Analyze query to determine routing
const analysis = smartRouter.analyze(query);
let result;
switch (analysis.route) {
case RouteType.DIRECT:
case RouteType.SIMPLE:
// Execute tool directly
const handler = toolHandlers[analysis.tool];
result = await handler({
...analysis.params,
apiKey: user.apiKey,
userId: user.userId,
_userContext: securityContext
});
break;
case RouteType.AGENT:
// Use agent for complex queries
result = await handleAgentQuery(query, securityContext, {
maxIterations: 7,
timeout: 55000
});
break;
default:
result = { success: false, error: 'Unable to process query' };
}
res.json({
success: result.success,
data: result.data || result,
actionType: result.actionType || ActionTypes.INFO_ONLY
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
app.listen(3001);
SSE Transport for External Clients
import { toolHandlers, buildSecurityContext } from './mcp/index.js';
// SSE endpoint for MCP protocol
app.get('/api/mcp/sse', async (req, res) => {
// Validate MCP token
const token = req.headers['x-api-key'];
const tokenInfo = await validateMCPToken(token);
if (!tokenInfo.valid) {
return res.status(401).json({ error: 'Invalid token' });
}
// Set SSE headers
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
// Build security context from token
const securityContext = await buildSecurityContext({
email: tokenInfo.createdBy,
userId: cleanUserId(tokenInfo.createdBy),
apiKey: tokenInfo.brandOrgId
});
// Send initial connection event
res.write(`data: ${JSON.stringify({ type: 'connected' })}\n\n`);
// Handle incoming tool calls via query params or POST
// Implementation depends on your MCP protocol version
});
Direct Tool Calls
Query Rooms
import { toolHandlers, buildSecurityContext } from '@muzie/mcp-server';
async function getRecentMeetings(userContext) {
const securityContext = await buildSecurityContext(userContext);
const result = await toolHandlers.query_rooms({
limit: 10,
sortBy: 'createdAt',
sortOrder: 'desc',
apiKey: userContext.apiKey,
userId: userContext.userId,
_userContext: securityContext
});
if (result.success) {
console.log(`Found ${result.total} meetings`);
result.rooms.forEach(room => {
console.log(`- ${room.name} (${room.participants.length} participants)`);
});
}
return result;
}
Search Content
async function searchMeetings(query, userContext) {
const securityContext = await buildSecurityContext(userContext);
const result = await toolHandlers.search_content({
query: query,
limit: 10,
contentTypes: ['transcript'],
threshold: 0.6,
apiKey: userContext.apiKey,
userId: userContext.userId,
_userContext: securityContext
});
return result.results.map(r => ({
room: r.roomName,
snippet: r.snippet,
relevance: Math.round(r.relevance * 100) + '%'
}));
}
// Usage
const results = await searchMeetings('budget discussion', userContext);
Get Network Statistics
async function getNetworkOverview(userContext) {
const securityContext = await buildSecurityContext(userContext);
const result = await toolHandlers.get_graph_stats({
apiKey: userContext.apiKey,
userId: userContext.userId,
_userContext: securityContext
});
if (result.success) {
const stats = result.stats;
console.log(`
Network Overview:
- Total collaborators: ${stats.totalCollaborators}
- Total meetings: ${stats.totalMeetings}
- Total time: ${Math.round(stats.totalDuration / 3600)} hours
- Avg meeting: ${Math.round(stats.avgMeetingDuration / 60)} minutes
Top Collaborators:
${stats.topCollaborators.map(c => `- ${c.email}: ${c.meetings} meetings`).join('\n')}
`);
}
return result;
}
Create a Schedule
async function scheduleMeeting(meetingDetails, userContext) {
const securityContext = await buildSecurityContext(userContext);
const result = await toolHandlers.create_schedule({
title: meetingDetails.title,
description: meetingDetails.description,
scheduledStart: meetingDetails.startTime,
scheduledEnd: meetingDetails.endTime,
attendees: meetingDetails.attendees,
sendInvites: true,
apiKey: userContext.apiKey,
userId: userContext.userId,
_userContext: securityContext
});
if (result.success) {
console.log(`Meeting scheduled: ${result.schedule.title}`);
console.log(`Join URL: ${result.schedule.joinUrl}`);
}
return result;
}
// Usage
await scheduleMeeting({
title: 'Project Review',
description: 'Quarterly project review meeting',
startTime: '2026-01-15T14:00:00Z',
endTime: '2026-01-15T15:00:00Z',
attendees: ['john@example.com', 'sarah@example.com']
}, userContext);
Claude Desktop Configuration
Basic Configuration
Create ~/.config/claude-desktop/config.json:
{
"mcpServers": {
"muzie-meetings": {
"command": "node",
"args": ["/path/to/mcp-server/src/server.js"],
"env": {
"MONGODB_URI": "mongodb://localhost:27017/muzie",
"OPENAI_API_KEY": "sk-...",
"USER_EMAIL": "your@email.com",
"API_KEY": "your_org_id"
}
}
}
}
With Environment File
{
"mcpServers": {
"muzie-meetings": {
"command": "node",
"args": ["/path/to/mcp-server/src/server.js"],
"env": {
"DOTENV_CONFIG_PATH": "/path/to/.env"
}
}
}
}
Multiple Environments
{
"mcpServers": {
"muzie-production": {
"command": "node",
"args": ["/path/to/mcp-server/src/server.js"],
"env": {
"MONGODB_URI": "mongodb+srv://prod.example.com/muzie",
"OPENAI_API_KEY": "sk-...",
"USER_EMAIL": "your@email.com",
"API_KEY": "prod_org_id"
}
},
"muzie-development": {
"command": "node",
"args": ["/path/to/mcp-server/src/server.js"],
"env": {
"MONGODB_URI": "mongodb://localhost:27017/muzie",
"OPENAI_API_KEY": "sk-...",
"USER_EMAIL": "your@email.com",
"API_KEY": "dev_org_id"
}
}
}
}
Agent Queries
Simple Agent Query
import { handleAgentQuery, shouldUseAgent } from '@muzie/mcp-server';
async function askQuestion(question, userContext) {
if (!shouldUseAgent(question)) {
console.log('Simple query, using direct tools instead');
return handleSimpleQuery(question, userContext);
}
const result = await handleAgentQuery(question, userContext, {
maxIterations: 7,
timeout: 55000
});
return {
answer: result.response,
steps: result.steps.length,
tokensUsed: result.tokensUsed
};
}
// Usage
const answer = await askQuestion(
"Find my meetings with John this month and tell me what topics we discussed",
userContext
);
Agent with Progress Callback
async function agentWithProgress(query, userContext, onProgress) {
const result = await handleAgentQuery(query, userContext, {
maxIterations: 10,
timeout: 60000,
onStep: (step) => {
// Called after each step
onProgress({
type: step.type,
tool: step.tool,
progress: `Step ${step.index + 1}`
});
}
});
return result;
}
// Usage with progress reporting
await agentWithProgress(
"Analyze my meeting patterns",
userContext,
(progress) => {
console.log(`${progress.progress}: ${progress.type}`);
if (progress.tool) {
console.log(` Tool: ${progress.tool}`);
}
}
);
Batch Agent Queries
async function batchQueries(queries, userContext) {
const results = [];
for (const query of queries) {
try {
const result = await handleAgentQuery(query, userContext, {
maxIterations: 5,
timeout: 30000
});
results.push({ query, success: true, response: result.response });
} catch (error) {
results.push({ query, success: false, error: error.message });
}
}
return results;
}
// Usage
const results = await batchQueries([
"How many meetings did I have last week?",
"Who do I collaborate with most?",
"What topics come up most in my meetings?"
], userContext);
Custom Middleware
Authentication Middleware
import { buildSecurityContext } from '@muzie/mcp-server';
function mcpAuthMiddleware(dbConnection) {
return async (req, res, next) => {
// Get token from header
const token = req.headers['x-api-key'] ||
req.headers['authorization']?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ error: 'MCP token required' });
}
// Validate token
const tokenInfo = await dbConnection.validateMCPToken(token);
if (!tokenInfo.valid) {
return res.status(401).json({ error: tokenInfo.error });
}
// Build security context
req.mcpContext = await buildSecurityContext({
email: tokenInfo.createdBy,
userId: cleanUserId(tokenInfo.createdBy),
apiKey: tokenInfo.brandOrgId,
permissions: tokenInfo.permissions
});
next();
};
}
// Usage
app.use('/api/mcp', mcpAuthMiddleware(dbConnection));
Permission Check Middleware
function requirePermission(permission) {
return (req, res, next) => {
const { permissions } = req.mcpContext;
if (!permissions?.includes(permission)) {
return res.status(403).json({
error: `Missing permission: ${permission}`
});
}
next();
};
}
// Usage
app.post('/api/mcp/search',
mcpAuthMiddleware(dbConnection),
requirePermission('mcp:search:execute'),
searchHandler
);
Rate Limiting Middleware
import rateLimit from 'express-rate-limit';
const mcpRateLimiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 30, // 30 requests per minute
keyGenerator: (req) => req.mcpContext?.apiKey || req.ip,
message: {
success: false,
error: 'Rate limit exceeded',
retryAfter: 60
}
});
app.use('/api/mcp', mcpRateLimiter);
Error Handling Patterns
Comprehensive Error Handler
import { MCPAuthorizationError } from '@muzie/mcp-server';
function mcpErrorHandler(error, req, res, next) {
// Log the error
console.error('MCP Error:', error.message);
// Authorization errors
if (error instanceof MCPAuthorizationError) {
return res.status(403).json({
success: false,
error: 'Access denied',
code: 'ACCESS_DENIED',
details: error.details
});
}
// Validation errors
if (error.name === 'ValidationError') {
return res.status(400).json({
success: false,
error: 'Invalid parameters',
code: 'INVALID_PARAMS',
details: error.errors
});
}
// Timeout errors
if (error.message.includes('timeout')) {
return res.status(504).json({
success: false,
error: 'Request timeout',
code: 'TIMEOUT'
});
}
// Default error
res.status(500).json({
success: false,
error: 'Internal server error',
code: 'INTERNAL_ERROR'
});
}
app.use(mcpErrorHandler);
Retry Logic
async function withRetry(fn, maxRetries = 3, delay = 1000) {
let lastError;
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
lastError = error;
// Don't retry authorization errors
if (error instanceof MCPAuthorizationError) {
throw error;
}
// Wait before retry
if (i < maxRetries - 1) {
await new Promise(r => setTimeout(r, delay * (i + 1)));
}
}
}
throw lastError;
}
// Usage
const result = await withRetry(
() => toolHandlers.search_content({ query: 'budget', ...params }),
3,
1000
);
Graceful Degradation
async function searchWithFallback(query, userContext) {
const securityContext = await buildSecurityContext(userContext);
const params = {
apiKey: userContext.apiKey,
userId: userContext.userId,
_userContext: securityContext
};
try {
// Try semantic search first
return await toolHandlers.search_content({
query,
limit: 10,
...params
});
} catch (error) {
console.warn('Semantic search failed, falling back to text search');
// Fall back to simple text query
return await toolHandlers.query_rooms({
roomName: query, // Use as text filter
limit: 10,
...params
});
}
}
Related Documentation
- Getting Started - Quick start guide
- API Reference - Complete API documentation
- Tools Reference - All primitive tools
- Architecture - System design
- Security - Security context and authorization
- Agent Guide - Using the agent for complex queries