Building Production Model Context Protocol (MCP) Servers in 2026

By DevUtils Architecture Team • November 11, 2026 • 9 min read

The Model Context Protocol (MCP) has evolved from an open-source standard created by Anthropic into the industry-wide integration foundation stewarded by the Linux Foundation's Agentic AI Foundation. Often described as the "USB-C port for AI", MCP provides a unified standard that allows developer IDEs (Cursor, VS Code, Claude Code) and autonomous terminal agents to seamlessly interact with local databases, cloud APIs, and developer tools.

1. MCP Transports: Stdio vs Streamable HTTP

MCP supports two core communication transports:

  • Stdio Transport: The AI host process spawns your server executable as a child process and communicates via JSON-RPC 2.0 lines over standard input (stdin) and standard output (stdout). This is optimal for local CLI utilities, Git tools, and file-based scripts.
  • Streamable HTTP Transport: Built for remote microservices and team-shared tools. Operates over standard HTTP(S) with streaming responses, allowing horizontal scaling behind reverse proxies without persistent WebSocket bottlenecks.

2. Building a Custom Stdio MCP Server in Node.js

Creating an MCP server using the official TypeScript/JavaScript SDK takes less than 50 lines of code:

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";

const server = new Server(
  { name: "dev-analytics-server", version: "1.0.0" },
  { capabilities: { tools: {} } }
);

// 1. Declare Available Tools
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: "query_pageviews",
      description: "Query active user pageviews by date range and country",
      inputSchema: {
        type: "object",
        properties: {
          daysAgo: { type: "number", description: "Number of past days to query" },
          country: { type: "string", description: "Target ISO country code" }
        },
        required: ["daysAgo"]
      }
    }
  ]
}));

// 2. Handle Tool Invocations
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === "query_pageviews") {
    const { daysAgo, country } = request.params.arguments;
    const metrics = await fetchAnalyticsData(daysAgo, country);
    return {
      content: [{ type: "text", text: JSON.stringify(metrics, null, 2) }]
    };
  }
  throw new Error(`Tool not found: ${request.params.name}`);
});

// 3. Connect via Standard I/O Transport
const transport = new StdioServerTransport();
await server.connect(transport);

3. Critical Production Security Guidelines

Because MCP servers execute system actions requested by LLM agents, you must implement strict security boundaries:

  • Never Log to stdout: All debugging information must be routed to stderr (e.g. console.error). Writing non-JSON data to stdout corrupts the JSON-RPC message framing and crashes the client connection.
  • Validate with Zod: Always strictly validate input parameters using schemas to prevent command injection (CWE-78) and path traversal (CWE-22).
  • Scope Credential Access: Pass API tokens and database URLs via environment variables injected through mcp_config.json rather than embedding secrets in source code.

Inspect and Validate JSON-RPC Payloads

Debugging MCP tool schemas and JSON-RPC payloads? Format, validate, and minify your tool definitions client-side using our free JSON Formatter & Validator.