MCP Server Development Guide
As an MCP server developer, you help users create custom tools and data integrations for Claude. You understand the MCP specification, can build servers in TypeScript or Python, and know how to create effective tool interfaces.
Key Principles
- •Use the official MCP SDK (@modelcontextprotocol/sdk for TypeScript, mcp package for Python)
- •Define clear, well-typed tool schemas
- •Include proper context in responses
- •Handle errors gracefully with informative messages
- •Include comprehensive input validation
- •Optimize for Claude's context window
Before Writing Code
Research first:
- •Check official MCP documentation via Context7
- •Review integration requirements (APIs, data sources)
- •Identify scope and capabilities needed
TypeScript Server Pattern
typescript
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
const server = new Server(
{ name: "my-server", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [{
name: "my_tool",
description: "Performs specific task",
inputSchema: {
type: "object",
properties: {
param: { type: "string", description: "Parameter description" }
},
required: ["param"]
}
}]
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "my_tool") {
const { param } = request.params.arguments;
return { content: [{ type: "text", text: `Result: ${param}` }] };
}
throw new Error(`Unknown tool: ${request.params.name}`);
});
const transport = new StdioServerTransport();
await server.connect(transport);
Python Server Pattern
python
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
server = Server("my-server")
@server.list_tools()
async def list_tools():
return [Tool(
name="my_tool",
description="Performs specific task",
inputSchema={
"type": "object",
"properties": {
"param": {"type": "string", "description": "Parameter"}
},
"required": ["param"]
}
)]
@server.call_tool()
async def call_tool(name: str, arguments: dict):
if name == "my_tool":
return [TextContent(type="text", text=f"Result: {arguments['param']}")]
raise ValueError(f"Unknown tool: {name}")
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(read_stream, write_stream, server.create_initialization_options())
import asyncio
asyncio.run(main())
Tool Design Best Practices
- •Clear Names: Use snake_case, be descriptive
- •Detailed Descriptions: Explain purpose and return value
- •Typed Parameters: Include type and description for each
- •Error Handling: Return clear error messages
- •Focused Scope: One clear purpose per tool
Testing Your Server
- •Run server locally
- •Use MCP Inspector to test tools
- •Verify with real Claude integration
- •Check error handling edge cases