Skip to main content
< All Topics
Print

MCP Server Development

name: mcp-server-development

description: Building, testing, and deploying MCP (Model Context Protocol) servers with tool definitions, resource providers, authentication, transport configuration, and client compatibility testing. Covers the MCP protocol specification, TypeScript and Python SDK usage, tool and resource schema design, stdio and HTTP transport, OAuth authentication, and testing strategies. Use when building MCP servers, defining MCP tools, implementing resource providers, or testing MCP client compatibility.

MCP Server Development

Instructions

MCP Protocol Fundamentals

  1. Core concepts:
  • Server: exposes tools, resources, and prompts to AI clients
  • Client: an AI application (Claude, Cursor, etc.) that connects to servers
  • Tools: functions the AI can invoke with parameters and receive results
  • Resources: read-only data the AI can access (files, API responses, database records)
  • Prompts: reusable prompt templates the server provides to clients
  • Transport: communication layer (stdio for local, HTTP/SSE for remote)
  1. Protocol flow:

   Client → initialize → Server (capabilities exchange)
   Client → tools/list → Server (discover available tools)
   Client → tools/call → Server (invoke a tool)
   Client → resources/list → Server (discover resources)
   Client → resources/read → Server (fetch a resource)

Server Implementation (TypeScript)

  1. Project setup:

   npm init -y
   npm install @modelcontextprotocol/sdk zod
   npm install -D typescript @types/node
  1. Basic server structure:

   import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
   import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
   import { z } from "zod";

   const server = new McpServer({
     name: "my-server",
     version: "1.0.0",
   });

   server.tool(
     "get_weather",
     "Get current weather for a location",
     {
       location: z.string().describe("City name or zip code"),
       units: z.enum(["fahrenheit", "celsius"]).default("fahrenheit"),
     },
     async ({ location, units }) => {
       const weather = await fetchWeather(location, units);
       return {
         content: [{ type: "text", text: JSON.stringify(weather) }],
       };
     }
   );

   const transport = new StdioServerTransport();
   await server.connect(transport);
  1. Resource provider:

   server.resource(
     "config://app/settings",
     "Application settings",
     async (uri) => ({
       contents: [{
         uri: uri.href,
         mimeType: "application/json",
         text: JSON.stringify(await loadSettings()),
       }],
     })
   );

Server Implementation (Python)

  1. Project setup:

   pip install mcp
  1. Basic server structure:

   from mcp.server.fastmcp import FastMCP

   mcp = FastMCP("my-server")

   @mcp.tool()
   def get_weather(location: str, units: str = "fahrenheit") -> str:
       """Get current weather for a location."""
       weather = fetch_weather(location, units)
       return json.dumps(weather)

   @mcp.resource("config://app/settings")
   def get_settings() -> str:
       """Application settings."""
       return json.dumps(load_settings())
  1. Run with stdio:

   python -m mcp.server.stdio my_server:mcp

Tool Schema Design

  1. Naming conventions:
  • Use snake_case for tool names: get_weather, create_event, search_documents
  • Use verb-noun pattern: get_, create_, update_, delete_, search_, list_
  • Be specific: search_recipes not search
  1. Description quality:
  • First sentence: what the tool does in one line
  • Include: when to use this tool, what it returns
  • Mention limitations or requirements
  • Example: “Search for recipes by ingredient, cuisine, or dietary restriction. Returns up to 10 matching recipes with titles, ingredients, and cook times. Requires at least one search parameter.”
  1. Parameter design:
  • Use descriptive parameter names with .describe() annotations
  • Set sensible defaults where possible
  • Use enums for constrained values
  • Mark truly required parameters as required; make everything else optional
  • Keep parameter count under 8; group related params into objects if more
  1. Return format:
  • Return structured data as JSON strings in text content
  • For errors, use isError: true in the response
  • Include metadata (result count, pagination info) alongside data
  • Keep responses under 10KB for optimal client handling

Transport Configuration

Stdio (Local)

  • Default for local development and desktop clients
  • Server runs as a child process of the client
  • Communication via stdin/stdout; stderr for logging
  • Client configuration (Cursor/Claude Desktop):

  {
    "mcpServers": {
      "my-server": {
        "command": "node",
        "args": ["path/to/server.js"],
        "env": { "API_KEY": "value" }
      }
    }
  }

HTTP + SSE (Remote)

  • For remote/shared servers
  • HTTP POST for client-to-server messages
  • Server-Sent Events for server-to-client messages
  • Requires authentication for production use

Authentication

  1. Local servers (stdio): inherit the user’s environment; no additional auth needed
  2. Remote servers (HTTP): implement OAuth 2.0 or API key authentication
  3. API key pattern:
  • Accept via environment variable (stdio) or Authorization header (HTTP)
  • Never include API keys in tool responses
  1. OAuth pattern:
  • Implement the MCP auth flow for OAuth-capable clients
  • Support token refresh for long-running sessions

Testing Strategy

  1. Unit tests: test each tool function in isolation with mocked dependencies
  2. Integration tests: use the MCP Inspector tool:

   npx @modelcontextprotocol/inspector node path/to/server.js
  • Verify tool listing returns correct schemas
  • Test each tool with valid and invalid inputs
  • Verify error handling for edge cases
  1. Client compatibility testing:
  • Test with Claude Desktop (stdio transport)
  • Test with Cursor (stdio transport)
  • Test with the MCP Inspector (interactive testing)
  1. Load testing (for remote servers):
  • Concurrent tool calls
  • Large response handling
  • Connection timeout behavior

Deployment

  1. Local (stdio):
  • Distribute as an npm package or Python package
  • Users configure in their client’s MCP settings
  • Include clear setup instructions in README
  1. Remote (HTTP):
  • Deploy behind a reverse proxy with TLS
  • Implement rate limiting per client
  • Monitor server health and tool latency
  • Log tool invocations for debugging (exclude sensitive parameters)
  1. Versioning:
  • Semantic versioning for the server package
  • Backward-compatible tool changes (new optional params) in minor versions
  • Breaking changes (renamed tools, removed params) in major versions
  • Document breaking changes in CHANGELOG

Inputs Required

  • Tools to expose (name, parameters, return types, data sources)
  • Resources to provide (URIs, data sources)
  • Transport type (stdio for local, HTTP for remote)
  • Authentication requirements
  • Target clients (Claude Desktop, Cursor, custom)
  • Deployment target (npm package, Docker container, hosted service)

Output Format

MCP Server Project Structure


my-mcp-server/
  src/
    index.ts              — server entry point and transport setup
    tools/
      weather.ts          — weather tool implementation
      calendar.ts         — calendar tool implementation
    resources/
      settings.ts         — settings resource provider
    auth/
      oauth.ts            — OAuth handler (if remote)
    utils/
      validation.ts       — parameter validation helpers
  tests/
    tools.test.ts         — tool unit tests
    integration.test.ts   — MCP Inspector-based tests
  package.json
  tsconfig.json
  README.md               — setup instructions and tool documentation

Tool Documentation Template


## Tool: get_weather

**Description**: Get current weather for a location.

**Parameters**:
| Name | Type | Required | Description |
|------|------|----------|-------------|
| location | string | yes | City name or zip code |
| units | enum | no | "fahrenheit" (default) or "celsius" |

**Returns**: JSON with temperature, conditions, humidity, wind speed.

**Example**:
Input: { "location": "Atlanta, GA", "units": "fahrenheit" }
Output: { "temperature": 78, "conditions": "Partly cloudy", ... }

Anti-Patterns

  • Exposing too many tools — clients have context limits; expose 5–15 focused tools rather than 50 generic ones
  • Vague tool descriptions — the AI uses descriptions to decide when to call a tool; vague descriptions cause misuse
  • Returning raw API responses — transform data into clean, relevant structures; don’t dump raw third-party API JSON
  • Missing error handling — tools that throw unhandled exceptions crash the server; always return structured errors
  • Hardcoding credentials in server code — use environment variables; document required env vars in README
  • Skipping the MCP Inspector — interactive testing catches issues that unit tests miss; always test with the Inspector
  • Mixing stdio logging with protocol output — log to stderr only; stdout is reserved for MCP protocol messages
  • Not documenting tool behavior — every tool needs a description, parameter docs, and example in the README
Table of Contents