Skip to content
Back to AI Coding

Building MCP Servers for AI Tools

Expose custom tools and resources to AI clients via the Model Context Protocol.

MCP DevelopmentAdvanced

Why

MCP launched in November 2024; by December 2025 it had 97M+ monthly SDK downloads, and by September 2026 the npm SDK alone reached ~204M monthly downloads (5x growth in 9 months) with 10,000+ published servers. The Model Context Protocol (MCP) is an open standard that lets AI assistants (Claude, Cursor, etc.) interact with external tools, databases, and APIs.

Building an MCP server makes your custom capabilities available to any MCP-compatible client — no per-client integration needed. This is the bridge between AI reasoning and your real-world systems.

When

Build an MCP server when: you want AI to query your database or API; you need custom code execution (e.g., run tests, deploy); you want to share team-specific tools across AI clients; or you're integrating internal services with AI workflows. Don't build one for simple HTTP API calls — a prompt with curl examples is simpler.

How

1. MCP architecture: - Server: exposes "tools" (functions AI can call) and "resources" (data AI can read) - Transport: stdio (local) or SSE (remote) - Protocol: JSON-RPC 2.0 over the transport 2. Scaffold with the official SDK: npm install @modelcontextprotocol/sdk 3. Define a tool: - name: unique identifier (e.g., "query_database") - description: natural language description for the AI to understand when to use it - inputSchema: JSON Schema for parameters - handler: async function that executes the tool 4. Define a resource: - uri: identifier (e.g., "config://app/settings") - handler: returns the resource content 5. Run the server: - Local: stdio transport, launched by the AI client - Remote: SSE transport, hosted as an HTTP service 6. Register with the AI client (e.g., Claude Desktop config): Add your server to the client's MCP config file.

Example

Minimal MCP server with one tool (TypeScript): import { Server } from "@modelcontextprotocol/sdk/server/Server.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; const server = new Server( { name: "weather-server", version: "1.0.0" }, { capabilities: { tools: {} } } ); server.setRequestHandler("tools/list", async () => ({ tools: [{ name: "get_weather", description: "Get current weather for a city", inputSchema: { type: "object", properties: { city: { type: "string" } }, required: ["city"], }, }], })); server.setRequestHandler("tools/call", async (req) => { if (req.params.name === "get_weather") { const { city } = req.params.arguments; // ... fetch weather data ... return { content: [{ type: "text", text: `Weather in ${city}: 22C, sunny` }] }; } }); const transport = new StdioServerTransport(); await server.connect(transport); Claude can now call get_weather({ city: "Shanghai" }) in any conversation.

Common Problems

  • ⚠Error: Cannot find module '@modelcontextprotocol/sdk' → run `npm install @modelcontextprotocol/sdk zod`; the SDK requires Node.js 18+.
  • ⚠Transport errors: stdio servers must not write to stdout (use stderr for logs) — stdout is the protocol channel
  • ⚠Tool call returns `{"error": "Invalid params"}` → validate the input schema with zod; common cause is missing required field or wrong type.
  • ⚠Error: Cannot find module '@modelcontextprotocol/sdk' → run `npm install @modelcontextprotocol/sdk zod`; the SDK requires Node.js 18+.
  • ⚠"Error: transport closed" / server not listed by client → verify the stdio/HTTP transport matches on both sides; check `npx @modelcontextprotocol/inspector` output.

Best Practices

  • ✓Write clear tool descriptions — the AI uses them to decide when to call your tool
  • ✓Use strict JSON Schema with 'required' fields — reduces AI hallucination of optional params
  • ✓Return structured content (not just strings) — helps the AI parse results correctly
  • ✓Log to stderr only — stdout is reserved for the MCP protocol in stdio transport
  • ✓Test with the MCP Inspector (npx @modelcontextprotocol/inspector) before deploying
  • ✓Document each tool with a usage example in the description for better AI utilization

FAQ

What is the Model Context Protocol (MCP)?

MCP is an open JSON-RPC standard for AI assistants to call external tools and read resources. A server exposes tools (functions) and resources (data); any MCP-compatible client (Claude, Cursor) can use them without per-client integration.

stdio vs SSE transport: which to use?

Use stdio for local servers launched by the client (simplest, no network). Use SSE for remote/shared servers hosted as an HTTP service that multiple clients or team members reach over the network. Start with stdio in development.

How do I register an MCP server with Claude Desktop?

Add an entry under `mcpServers` in your claude_desktop_config.json with the server's launch command (stdio) or URL (SSE), then restart Claude Desktop. Verify with the MCP Inspector (`npx @modelcontextprotocol/inspector`) first.

Why must stdio servers log to stderr only?

In stdio transport, stdout is the JSON-RPC protocol channel. Any console.log to stdout corrupts the message stream and breaks the client. Use console.error (stderr) for all logging.

How do I handle errors in an MCP tool?

Return { isError: true, content: [{ type: 'text', text: '...' }] } instead of throwing. Throwing crashes the client session; a typed error result keeps the client alive and lets the AI retry or report the failure.