MCP Server Development Guide
Build high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services.
Core Design Principles
Build for Workflows, Not Just APIs
Principle Why
Consolidate operations Single tool for complete tasks
Return high-signal data Agents have limited context
Provide format options "concise" vs "detailed" modes
Use human-readable IDs Not technical codes
Make errors actionable Guide toward correct usage
Key concept: Don't just wrap API endpoints. Design tools that enable complete workflows agents actually need.
Development Phases
Phase 1: Research
Step Action
Study MCP Protocol Read modelcontextprotocol.io/llms-full.txt
Study SDK docs Python or TypeScript SDK README
Study target API Read ALL available documentation
Create implementation plan Before writing code
Phase 2: Design
Decision Options
Language Python (FastMCP) or TypeScript
Tool granularity Atomic vs workflow-oriented
Response format JSON, Markdown, or both
Error handling What errors can occur, how to recover
Phase 3: Implementation
Component Purpose
Input validation Pydantic (Python) or Zod (TypeScript)
Tool descriptions Clear, with examples
Error messages Include suggested next steps
Response formatting Consistent across tools
Phase 4: Testing
Critical: MCP servers are long-running processes. Never run directly in main process.
Approach How
Evaluation harness Recommended
tmux session Run server separately
Timeout wrapper timeout 5s python server.py
MCP Inspector Official debugging tool
Tool Annotations
Annotation Meaning Default
readOnlyHint Doesn't modify state false
destructiveHint Can cause damage true
idempotentHint Repeated calls safe false
openWorldHint Interacts externally true
Key concept: Annotations help the LLM decide when and how safely to use tools.
Input Design
Validation Patterns
Pattern Use Case
Required fields Core parameters
Optional with defaults Convenience parameters
Enums Limited valid values
Min/max constraints Numeric bounds
Pattern matching Format validation (email, URL)
Parameter Naming
Good Bad Why
user_email
e
Self-documenting
limit
max_results_to_return
Concise but clear
include_archived
ia
Descriptive boolean
Response Design
Format Options
Format Use Case
JSON Programmatic use, structured data
Markdown Human readability, reports
Hybrid JSON in markdown code blocks
Response Guidelines
Guideline Why
~25,000 token limit Context constraints
Truncate with indicator Don't silently cut
Support pagination limit and offset params
Include metadata Total count, has_more
Error Handling
Error Message Structure
Element Purpose
What failed Clear description
Why it failed Root cause if known
How to fix Suggested next action
Example Correct usage
Key concept: Error messages should guide the agent toward correct usage, not just diagnose problems.
Quality Checklist
Code Quality
Check Description
No duplicated code Extract shared logic
Consistent formats Similar ops return similar structure
Full error handling All external calls wrapped
Type coverage All inputs/outputs typed
Comprehensive docstrings Every tool documented
Tool Quality
Check Description
Clear descriptions Model knows when to use
Good examples In docstring
Sensible defaults Reduce required params
Consistent naming Group related with prefixes
Best Practices
Practice Why
One tool = one purpose Clear mental model
Comprehensive descriptions LLM selection accuracy
Include examples in docstrings Show expected usage
Return actionable errors Enable self-correction
Test with actual LLM Real-world validation
Version your server Track compatibility
Resources
-
MCP Protocol: https://modelcontextprotocol.io/
-
Python SDK: https://github.com/modelcontextprotocol/python-sdk
-
TypeScript SDK: https://github.com/modelcontextprotocol/typescript-sdk