MCPCheatsheet

MCP Server Development Cheatsheet📋

Everything you need to build MCP servers that expose tools, resources, and prompts to AI assistants. Covers both the TypeScript (@modelcontextprotocol/sdk) and Python (mcp) SDKs with production-ready patterns. Bookmark this and start building.

Quick Reference

NameSyntaxDescription
Create Servernew McpServer({ name, version })Initialize an MCP server instance with a name and version string.
Define Toolserver.tool(name, schema, handler)Register a tool with a Zod schema for parameters and an async handler function.
Define Resourceserver.resource(name, uri, handler)Expose a readable resource at a URI pattern for clients to fetch.
Define Promptserver.prompt(name, schema, handler)Register a reusable prompt template with optional parameters.
Stdio Transportnew StdioServerTransport()Connect server via stdin/stdout for local process communication.
SSE Transportnew SSEServerTransport('/messages', res)Connect server via Server-Sent Events for HTTP-based communication.
Streamable HTTPnew StreamableHTTPServerTransport({ sessionIdGenerator })Modern HTTP transport with session management and bidirectional streaming.
Resource Templateserver.resource(name, new ResourceTemplate(pattern, { list }), handler)Define dynamic resources with URI template patterns like 'users://{id}'.
Python Decorator@mcp.tool()Python decorator syntax to register a function as an MCP tool.
Run Servermcp.run(transport='stdio')Start the Python MCP server with the specified transport.

Setup

TypeScript Server Setup

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'

Initialize a TypeScript MCP server with stdio transport. Install with: npm install @modelcontextprotocol/sdk zod

typescript

Tips

  • Always import from the specific subpath: /server/mcp.js, /server/stdio.js
  • The server instance is reusable across multiple transport connections

Python Server Setup

from mcp.server.fastmcp import FastMCP

Initialize a Python MCP server using the FastMCP high-level API. Install with: pip install mcp

python

Tips

  • FastMCP uses decorators and type hints for automatic schema generation
  • Docstrings become the tool/resource descriptions shown to the AI

Package Configuration

package.json + tsconfig.json

Minimal package.json and tsconfig.json for a TypeScript MCP server project.

json

Tips

  • type: module is required for ES module imports used by the SDK
  • The bin field makes your server installable as an npx command
  • Use NodeNext module resolution for proper .js extension handling

Tools

Basic Tool Definition (TypeScript)

server.tool(name, schema, handler)

Define a tool with typed parameters using Zod schemas. The handler receives validated parameters and returns content.

typescript

Tips

  • Use .describe() on each Zod field to help the AI understand what to pass
  • Return type: text for plain text, type: image for base64-encoded images
  • Tool names should be verb-noun format: get-weather, create-issue, search-docs

Tool with Error Handling

return { content, isError: true }

Return isError: true to signal tool execution failure to the AI without crashing the server.

typescript

Tips

  • Always validate inputs before executing dangerous operations
  • isError: true tells the AI the tool call failed so it can retry or adjust
  • Never throw exceptions from tool handlers; return error content instead

Python Tool with Type Hints

@mcp.tool()

Python tools use type annotations for schema generation. The Context parameter provides logging and progress reporting.

python

Tips

  • Annotated[type, description] generates the parameter schema automatically
  • The Context parameter is injected automatically when present in the signature
  • Use ctx.info(), ctx.warning(), ctx.error() for structured logging

Resources

Static Resource

server.resource(name, uri, handler)

Expose a static resource at a fixed URI. Resources are read-only data the AI can fetch on demand.

typescript

Tips

  • Use custom URI schemes like config://, db://, docs:// for clarity
  • Resources are read-only; use tools for write operations
  • Set appropriate mimeType: application/json, text/plain, text/markdown

Dynamic Resource Template

new ResourceTemplate(pattern, { list })

Resource templates expose parameterized resources. The list callback enables discovery of available resources.

typescript

Tips

  • URI template variables like {userId} are extracted and passed to the handler
  • The list callback is optional but enables AI to browse available resources
  • Limit list results to avoid overwhelming the context window

Python Resource

@mcp.resource(uri)

Python resources use decorator syntax with URI patterns. Template variables are passed as function parameters.

python

Tips

  • Return strings directly; FastMCP handles content wrapping
  • URI template variables map to function parameters by name
  • Resources are great for exposing database schemas, config, docs, and metrics

Prompts

Basic Prompt Template

server.prompt(name, schema, handler)

Prompts are reusable message templates the AI can invoke. They can read files and build complex multi-turn messages.

typescript

Tips

  • Prompts return an array of messages, not tool results
  • Use prompts for complex, reusable instructions that would be tedious to type each time
  • Prompts can include both user and assistant messages for few-shot examples

Multi-Turn Prompt

messages: [{ role, content }]

Multi-turn prompts seed conversations with example exchanges to guide the AI's response style and depth.

typescript

Tips

  • Assistant messages in prompts act as few-shot examples
  • Keep prompt templates focused on one task for reusability
  • Optional parameters let you build flexible templates

Python Prompt

@mcp.prompt()

Python prompts return either a string (single user message) or a list of message dicts for multi-turn templates.

python

Tips

  • Return a string for simple single-message prompts
  • Return a list of dicts with role/content for multi-turn prompts
  • Default parameter values make prompts easier to use

Transport

Stdio Transport

new StdioServerTransport()

Stdio transport communicates over stdin/stdout. Best for local tools launched as child processes by the AI client.

typescript

Tips

  • Stdio is the default and most common transport for local servers
  • The client launches your server as a subprocess
  • All console.log output goes to stderr to avoid corrupting the JSON-RPC stream

SSE Transport (HTTP)

new SSEServerTransport('/messages', res)

SSE transport exposes the MCP server over HTTP. Useful for remote servers and web-based clients.

typescript

Tips

  • GET /sse establishes the SSE connection, POST /messages sends client requests
  • Consider authentication middleware for production deployments
  • SSE is being superseded by Streamable HTTP transport in newer versions

Streamable HTTP Transport

new StreamableHTTPServerTransport({ sessionIdGenerator })

Streamable HTTP is the modern transport supporting sessions, bidirectional streaming, and stateless operation.

typescript

Tips

  • Supports both stateful (with sessions) and stateless modes
  • POST handles client requests, GET opens SSE stream, DELETE ends sessions
  • Preferred over SSE for new projects; supports full HTTP semantics

Testing

MCP Inspector

npx @modelcontextprotocol/inspector

The MCP Inspector is a visual testing tool that connects to your server and lets you call tools, read resources, and invoke prompts interactively.

bash

Tips

  • The Inspector opens a web UI where you can test each capability
  • Use -e to pass environment variables your server needs
  • Test every tool with edge case inputs before publishing

Programmatic Testing (TypeScript)

InMemoryTransport.createLinkedPair()

Use InMemoryTransport for fast unit tests without spawning processes. Create linked client-server pairs for direct communication.

typescript

Tips

  • InMemoryTransport avoids process overhead and is ideal for CI
  • Test both success and error cases for each tool
  • Mock external dependencies (APIs, databases) in unit tests

Claude Desktop Config

claude_desktop_config.json

Register your MCP server with Claude Desktop for end-to-end testing. Restart Claude Desktop after editing.

json

Tips

  • Always use absolute paths for command and args
  • Environment variables in env are passed to the server process
  • Check Claude Desktop logs if the server fails to connect

Common Patterns

REST API Wrapper Server

typescript

A complete MCP server that wraps the GitHub REST API. Demonstrates the common pattern of wrapping an authenticated API with typed tools: one for reading (list-issues) and one for writing (create-issue). Uses environment variables for secrets and includes error handling.

Database Query Tool Server

python

A Python MCP server that provides database access through a resource (schema inspection) and a tool (read-only queries). The schema resource lets the AI understand table structures, while the query tool enforces SELECT-only access. Uses asyncpg for PostgreSQL with connection pooling.

File System Server

typescript

A file system MCP server with path traversal protection. Exposes files as browsable resources with a template pattern, plus tools for searching and reading files. The safePath function prevents escaping the allowed root directory. This is the foundation pattern for any server that needs to give AI access to local files.

Watch Out For

console.log in a stdio server corrupts the JSON-RPC stream because stdout is used for protocol messages

Use console.error for all logging in stdio servers. The MCP protocol uses stdout exclusively for JSON-RPC messages. Any non-protocol data on stdout will cause parsing errors and disconnect the client.

Tool handlers that throw exceptions crash the server instead of reporting the error to the AI

Always wrap tool handler logic in try/catch and return { content: [{ type: 'text', text: error.message }], isError: true } instead of throwing. This keeps the server running and lets the AI handle the error gracefully.

Forgetting to use .js extensions in TypeScript imports causes 'module not found' errors at runtime with NodeNext resolution

Always use .js extensions in import paths even in .ts files: import { foo } from './utils.js'. TypeScript with NodeNext module resolution requires this because the compiled JS files need .js extensions.

Resource list callbacks that return thousands of items overwhelm the AI's context window and cause slow responses

Limit list callback results to 50-100 items. Implement pagination or filtering in the list callback. For large datasets, provide a search tool instead of listing everything as resources.

Python FastMCP tools with synchronous functions block the event loop, causing timeouts on concurrent requests

Use async def for all tool and resource handlers that perform I/O (network requests, file reads, database queries). FastMCP runs on asyncio, so blocking calls freeze the entire server until they complete.

Dive Deeper