DEV Community

Can Koylan
Can Koylan

Posted on

MCP in Production: Building Enterprise-Grade AI Integrations

MCP in Production: Building Enterprise-Grade AI Integrations

The Model Context Protocol (MCP) has emerged as the standard for connecting AI assistants to external data sources and tools. But moving from prototype to production requires more than just implementing the protocol—it demands robust error handling, security considerations, and scalable architecture.

What is MCP?

MCP is an open protocol that standardizes how applications provide context to LLMs. Think of it as a USB-C port for AI applications—universal, standardized, and extensible.

Production Considerations

1. Authentication & Security

// Implement API key validation
const validateRequest = (req: Request): boolean => {
  const apiKey = req.headers.get(x27x-api-keyx27);
  return apiKey === process.env.MCP_API_KEY;
};

// Rate limiting
const rateLimiter = new Map<string, number>();
const checkRateLimit = (clientId: string): boolean => {
  const now = Date.now();
  const lastRequest = rateLimiter.get(clientId) || 0;
  if (now - lastRequest < 1000) return false;
  rateLimiter.set(clientId, now);
  return true;
};
Enter fullscreen mode Exit fullscreen mode

2. Error Handling

class MCPError extends Error {
  constructor(
    public code: string,
    message: string,
    public details?: unknown
  ) {
    super(message);
  }
}

// Structured error responses
const handleError = (error: unknown): MCErrorResponse => {
  if (error instanceof MCPError) {
    return {
      error: {
        code: error.code,
        message: error.message,
        details: error.details
      }
    };
  }
  return {
    error: {
      code: x27INTERNAL_ERRORx27,
      message: x27An unexpected error occurredx27
    }
  };
};
Enter fullscreen mode Exit fullscreen mode

3. Resource Management

// Connection pooling for database MCP servers
class ConnectionPool {
  private pool: Pool;

  constructor() {
    this.pool = new Pool({
      max: 20,
      idleTimeoutMillis: 30000,
      connectionTimeoutMillis: 2000
    });
  }

  async query(sql: string, params: unknown[]): Promise<QueryResult> {
    const client = await this.pool.connect();
    try {
      return await client.query(sql, params);
    } finally {
      client.release();
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Real-World Implementation

Here is a complete MCP server for a PostgreSQL database:

import { Server } from x27@modelcontextprotocol/sdk/server/index.jsx27;
import { StdioServerTransport } from x27@modelcontextprotocol/sdk/server/stdio.jsx27;
import { Pool } from x27pgx27;

const pool = new Pool({ connectionString: process.env.DATABASE_URL });

const server = new Server(
  {
    name: x27postgres-mcp-serverx27,
    version: x271.0.0x27
  },
  {
    capabilities: {
      resources: {},
      tools: {}
    }
  }
);

// List available tables as resources
server.setRequestHandler(ListResourcesRequestSchema, async () => {
  const result = await pool.query(
    "SELECT table_name FROM information_schema.tables WHERE table_schema = x27publicx27"
  );

  return {
    resources: result.rows.map(row => ({
      uri: `postgres:///${row.table_name}`,
      name: row.table_name,
      mimeType: x27application/jsonx27
    }))
  };
});

// Execute read-only queries via tools
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name !== x27queryx27) {
    throw new Error(`Unknown tool: ${request.params.name}`);
  }

  const sql = request.params.arguments?.sql as string;

  // Security: Only allow SELECT statements
  if (!sql.trim().toLowerCase().startsWith(x27selectx27)) {
    throw new Error(x27Only SELECT queries are allowedx27);
  }

  const result = await pool.query(sql);

  return {
    content: [{
      type: x27textx27,
      text: JSON.stringify(result.rows, null, 2)
    }]
  };
});

const transport = new StdioServerTransport();
await server.connect(transport);
Enter fullscreen mode Exit fullscreen mode

Monitoring & Observability

// Add structured logging
import { logger } from x27./loggerx27;

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const startTime = Date.now();

  logger.info({
    event: x27tool_call_startedx27,
    tool: request.params.name,
    timestamp: new Date().toISOString()
  });

  try {
    const result = await executeTool(request);

    logger.info({
      event: x27tool_call_completedx27,
      tool: request.params.name,
      duration_ms: Date.now() - startTime,
      status: x27successx27
    });

    return result;
  } catch (error) {
    logger.error({
      event: x27tool_call_failedx27,
      tool: request.params.name,
      duration_ms: Date.now() - startTime,
      error: error.message
    });
    throw error;
  }
});
Enter fullscreen mode Exit fullscreen mode

Deployment Patterns

Docker Containerization

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
USER node
CMD [x27nodex27, x27dist/server.jsx27]
Enter fullscreen mode Exit fullscreen mode

Environment Configuration

# docker-compose.yml
version: x273.8x27
services:
  mcp-server:
    build: .
    environment:
      - DATABASE_URL=${DATABASE_URL}
      - MCP_API_KEY=${MCP_API_KEY}
      - NODE_ENV=production
    restart: unless-stopped
    healthcheck:
      test: [x27CMDx27, x27curlx27, x27-fx27, x27http://localhost:3000/healthx27]
      interval: 30s
      timeout: 10s
      retries: 3
Enter fullscreen mode Exit fullscreen mode

Testing Your MCP Server

import { Client } from x27@modelcontextprotocol/sdk/client/index.jsx27;
import { InMemoryTransport } from x27@modelcontextprotocol/sdk/inMemory.jsx27;

describe(x27MCP Serverx27, () => {
  it(x27should list resourcesx27, async () => {
    const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();

    const client = new Client({ name: x27test-clientx27, version: x271.0.0x27 });
    await client.connect(clientTransport);

    const resources = await client.listResources();
    expect(resources.resources).toBeDefined();
  });
});
Enter fullscreen mode Exit fullscreen mode

Conclusion

MCP is transforming how we build AI-powered applications. By following production best practices—security, error handling, monitoring, and testing—you can build robust integrations that scale with your needs.

The protocolx27s simplicity is its strength, but production deployment requires the same rigor as any enterprise system. Start with the basics, add observability early, and iterate based on real usage patterns.


Have you implemented MCP in production? Share your experience in the comments.

Top comments (0)