Skip to content

MCP Server Architecture

!!! info "TL;DR" The MCP server uses plain HTTP + JSON with a tool registry pattern. Deployed on Vercel as Next.js route handlers. Health check at /api/mcp/health.

Transport Layer

The MCP server exposes the Model Context Protocol over plain HTTP with JSON request and response bodies. This approach is Vercel-compatible and requires no persistent connection.

Endpoints

Endpoint Method Purpose
/api/mcp/tools POST Execute tool calls
/api/mcp/health GET Health check and server info

Request Format

Tool calls are plain JSON, not JSON-RPC:

{
  "tool": "create_survey",
  "arguments": {
    "title": "Q1 Product Feedback",
    "questions": [
      {
        "type": "multiple_choice",
        "text": "What feature would you like next?",
        "options": ["Dark mode", "API access", "Mobile app"]
      }
    ]
  }
}

Response Format

Successful responses wrap the tool's return value in result, alongside a correlation request_id:

{
  "result": {
    "survey_id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "draft",
    "url": "https://acme.canviq.app/en/surveys/550e8400-e29b-41d4-a716-446655440000"
  },
  "request_id": "mcp_2b1a9c7e-0f3d-4a8e-9b21-6e2c4d8f1a30"
}

Error responses use RFC 7807 Problem Details (Content-Type: application/problem+json). See the MCP Tools API reference for the full request/response contract, error codes, and authentication details.

Tool Registry Pattern

The server uses a plugin-style registry for tools. Each tool is a class that implements the McpTool interface:

interface McpTool {
  name: string
  description: string
  requiredScopes: string[]
  schema: z.ZodSchema
  execute(params: unknown, context: ExecutionContext): Promise<unknown>
}

Tools register themselves at startup:

// lib/mcp/tools/index.ts
export const toolRegistry = new Map<string, McpTool>([
  ['create_survey', new CreateSurveyTool()],
  ['update_survey', new UpdateSurveyTool()],
  ['list_surveys', new ListSurveysTool()],
  // ... more tools
])

This pattern makes adding new tools straightforward. Drop a file in lib/mcp/tools/, export the class, and it's automatically available.

Execution Context

Every tool execution receives a context object:

interface ExecutionContext {
  agentId: string
  requestId: string
  scopes: string[]
  rateLimit: RateLimitInfo
  metadata: Record<string, unknown>
}

This context flows through the execution pipeline:

  1. Authentication — Verify API key, load agent identity
  2. Authorization — Check scopes against tool's required scopes
  3. Rate limiting — Decrement quota in Upstash Redis
  4. Validation — Parse params with tool's Zod schema
  5. Execution — Call tool's execute() method
  6. Audit logging — Record action in agent_audit_logs
  7. Response — Return result or error

Health Check

The /api/mcp/health endpoint returns server status:

{
  "status": "ok",
  "version": "1.0.0",
  "timestamp": "2026-07-11T15:30:00.000Z"
}

Use this for monitoring and integration tests.

Deployment

The MCP server deploys as part of the main Next.js app on Vercel. No separate service required. Environment variables:

  • UPSTASH_REDIS_REST_URL — Rate limiting backend
  • UPSTASH_REDIS_REST_TOKEN — Redis auth
  • ANTHROPIC_API_KEY — Claude for sentiment analysis

Edge runtime is not used. MCP tools often call Supabase which requires Node.js runtime.

What's Next