Custom MCP Server Development Guide
Build your own MCP servers using TypeScript (FastMCP) and Python to unlock private data for AI agents.
Custom MCP Server Development Guide
Overview
The Model Context Protocol (MCP) allows AI agents to interact with external data and tools in a standardized way. While many pre-built servers exist, building a custom MCP server is the key to unlocking your private data and proprietary business logic for AI agents.
This guide provides a comprehensive walkthrough for developing a custom MCP server using both TypeScript (FastMCP) and Python.
🏗️ MCP Architecture Basics
An MCP server acts as a bridge between the MCP Host (e.g., Claude Desktop, Cursor) and your Resources/Tools.
graph LR
A[MCP Host] -- JSON-RPC --> B[MCP Server]
B -- Tool Call --> C[Local/Remote API]
C -- Result --> B
B -- Response --> A
Key Concepts
- Resources: Read-only data sources (e.g., a file, a database table).
- Tools: Executable functions that can change state (e.g., "send_email", "create_ticket").
- Prompts: Pre-defined templates that guide the LLM on how to use the server.
🛠️ Implementation: TypeScript (FastMCP)
FastMCP is the fastest way to build servers in TypeScript. It abstracts away the low-level JSON-RPC complexity.
1. Project Setup
mkdir my-mcp-server && cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node ts-node
npx tsc --init
2. Creating the Server
import { FastMCP } from "@modelcontextprotocol/sdk/fastmcp";
import { z } from "zod";
const server = new FastMCP("My Custom Server");
// Define a Tool
server.addTool({
name: "get_weather",
description: "Get current weather for a city",
parameters: z.object({
city: z.string().describe("The city name"),
}),
execute: async ({ city }) => {
// In real use, call a weather API
return {
content: [{ type: "text", text: `The weather in ${city} is sunny and 25°C.` }]
};
},
});
// Define a Resource
server.addResource({
uri: "config://settings",
name: "Server Settings",
description: "The current configuration of the MCP server",
execute: async () => {
return {
contents: [{ type: "text", text: "Setting 1: Enabled\nSetting 2: Disabled" }]
};
},
});
server.start();
🐍 Implementation: Python
The Python SDK is ideal for data-heavy servers or those integrating with ML libraries.
1. Project Setup
pip install mcp
2. Creating the Server
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("My Python Server")
@mcp.tool()
def calculate_metrics(data: list[float]) -> str:
"""Calculate basic statistics for a list of numbers."""
avg = sum(data) / len(data)
return f"The average value is {avg:.2f}"
@mcp.resource("data://stats")
def get_stats() -> str:
"""Return the latest system stats."""
return "CPU: 12%, RAM: 45%"
if __name__ == "__main__":
mcp.run()
🚀 Deployment & Integration
1. Local Testing
Run your server and use the MCP Inspector to verify tools and resources:
npx @modelcontextprotocol/inspector node dist/index.js
2. Configuring Claude Desktop
Add your server to claude_desktop_config.json:
Windows: %APPDATA%/Claude/claude_desktop_config.json
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"my-custom-server": {
"command": "node",
"args": ["C:/path/to/my-mcp-server/dist/index.js"]
}
}
}
🏆 Best Practices
- Detailed Descriptions: The LLM relies on your tool descriptions to decide when to call them. Be explicit.
- Input Validation: Always use Zod (TS) or type hints (Python) to ensure input quality.
- Error Handling: Return clear error messages in the response so the LLM can attempt to fix the call.
- Security: Never expose sensitive API keys in the server code; use environment variables.
