Tutorial

EXAMPLES

MCP Server Examples

Integration examples and common patterns for the MCP Server.

Table of Contents


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