Dev Tools·
advanced
·15 min read·Apr 4, 2026How to Build a Custom MCP Server from Scratch
Learn how to create your own MCP server using the TypeScript or Python SDK. Expose custom tools and resources for AI agents to use.
custom serverTypeScriptPythonSDKdevelopmenttools
Build a Custom MCP Server from Scratch
When existing MCP servers don't cover your use case, you can build your own. This guide walks through creating a custom MCP server using the official SDKs.
Choosing Your Language
MCP provides official SDKs for:
- ▸TypeScript/Node.js — code
@modelcontextprotocol/sdk - ▸Python — packagecode
mcp
TypeScript Server Example
Project Setup
bash
mkdir my-mcp-server
cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/nodeBasic Server Structure
typescript
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { z } from 'zod';
// Create the server
const server = new Server(
{ name: 'my-custom-server', version: '1.0.0' },
{ capabilities: { tools: {} } }
);
// Define available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'get_weather',
description: 'Get current weather for a location',
inputSchema: {
type: 'object',
properties: {
city: { type: 'string', description: 'City name' },
},
required: ['city'],
},
},
],
};
});
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === 'get_weather') {
const { city } = request.params.arguments;
// Your custom logic here
const weather = await fetchWeather(city);
return {
content: [
{ type: 'text', text: JSON.stringify(weather) },
],
};
}
throw new Error(`Unknown tool: ${request.params.name}`);
});
// Start the server
const transport = new StdioServerTransport();
await server.connect(transport);Python Server Example
python
import asyncio
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
app = Server("my-custom-server")
@app.list_tools()
async def list_tools():
return [
Tool(
name="get_weather",
description="Get current weather",
inputSchema={
"type": "object",
"properties": {
"city": {"type": "string"}
},
"required": ["city"]
}
)
]
@app.call_tool()
async def call_tool(name, arguments):
if name == "get_weather":
weather = await fetch_weather(arguments["city"])
return [TextContent(type="text", text=str(weather))]
async def main():
async with stdio_server() as (read, write):
await app.run(read, write, app.create_initialization_options())
asyncio.run(main())Adding Resources
Resources expose data that AI agents can read:
typescript
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: [
{
uri: 'myapp://config',
name: 'Application Config',
mimeType: 'application/json',
},
],
};
});Testing Your Server
Use the MCP Inspector:
bash
npx @modelcontextprotocol/inspector your-server-commandPublishing
Publish to npm for easy distribution:
bash
npm publishThen users can configure it as:
json
{
"mcpServers": {
"my-server": {
"command": "npx",
"args": ["-y", "my-mcp-server-package"]
}
}
}