Hello MCP - Your First Server¶
Build your first MCP server in 5 minutes! This tutorial creates a simple server that demonstrates core MCP concepts.
What You'll Learn¶
- ποΈ Basic MCP server structure
- π§ How to define and expose tools
- π‘ Client-server communication
- π§ͺ Testing your implementation
What You'll Build¶
A "Hello World" MCP server that: - Responds to greetings in different languages - Performs simple calculations - Demonstrates error handling
Prerequisites¶
- Python 3.8+ or Node.js 16+
- Basic command line knowledge
- 5 minutes of your time
Step-by-Step Guide¶
Step 1: Set Up Your Project¶
Step 2: Create Your Server¶
Create server.py
:
from mcp.server import Server, NotificationOptions
from mcp.server.models import InitializationOptions
import mcp.server.stdio
import mcp.types as types
# Create a server instance
server = Server("hello-mcp")
@server.list_tools()
async def handle_list_tools() -> list[types.Tool]:
"""List available tools."""
return [
types.Tool(
name="say_hello",
description="Say hello in different languages",
inputSchema={
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name to greet"
},
"language": {
"type": "string",
"enum": ["english", "spanish", "french", "japanese"],
"description": "Language for greeting"
}
},
"required": ["name"]
}
),
types.Tool(
name="calculate",
description="Perform basic arithmetic",
inputSchema={
"type": "object",
"properties": {
"operation": {
"type": "string",
"enum": ["add", "subtract", "multiply", "divide"],
"description": "Math operation"
},
"a": {
"type": "number",
"description": "First number"
},
"b": {
"type": "number",
"description": "Second number"
}
},
"required": ["operation", "a", "b"]
}
)
]
@server.call_tool()
async def handle_call_tool(
name: str, arguments: dict | None
) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
"""Execute a tool and return results."""
if name == "say_hello":
user_name = arguments.get("name", "friend")
language = arguments.get("language", "english")
greetings = {
"english": f"Hello, {user_name}!",
"spanish": f"Β‘Hola, {user_name}!",
"french": f"Bonjour, {user_name}!",
"japanese": f"γγγ«γ‘γ―γ{user_name}γγ!"
}
return [types.TextContent(
type="text",
text=greetings.get(language, greetings["english"])
)]
elif name == "calculate":
operation = arguments.get("operation")
a = arguments.get("a", 0)
b = arguments.get("b", 0)
if operation == "add":
result = a + b
elif operation == "subtract":
result = a - b
elif operation == "multiply":
result = a * b
elif operation == "divide":
if b == 0:
raise ValueError("Division by zero!")
result = a / b
else:
raise ValueError(f"Unknown operation: {operation}")
return [types.TextContent(
type="text",
text=f"Result: {result}"
)]
else:
raise ValueError(f"Unknown tool: {name}")
async def run():
"""Run the MCP server."""
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
write_stream,
InitializationOptions(
server_name="hello-mcp",
server_version="0.1.0",
capabilities=server.get_capabilities(
notification_options=NotificationOptions(),
experimental_capabilities={},
),
),
)
if __name__ == "__main__":
import asyncio
asyncio.run(run())
Create server.ts
:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
ListToolsRequestSchema,
CallToolRequestSchema,
Tool,
TextContent,
} from "@modelcontextprotocol/sdk/types.js";
// Create server instance
const server = new Server(
{
name: "hello-mcp",
version: "0.1.0",
},
{
capabilities: {
tools: {},
},
}
);
// Define available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "say_hello",
description: "Say hello in different languages",
inputSchema: {
type: "object",
properties: {
name: {
type: "string",
description: "Name to greet",
},
language: {
type: "string",
enum: ["english", "spanish", "french", "japanese"],
description: "Language for greeting",
},
},
required: ["name"],
},
},
{
name: "calculate",
description: "Perform basic arithmetic",
inputSchema: {
type: "object",
properties: {
operation: {
type: "string",
enum: ["add", "subtract", "multiply", "divide"],
description: "Math operation",
},
a: {
type: "number",
description: "First number",
},
b: {
type: "number",
description: "Second number",
},
},
required: ["operation", "a", "b"],
},
},
] satisfies Tool[],
};
});
// Handle tool execution
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "say_hello") {
const userName = args?.name || "friend";
const language = args?.language || "english";
const greetings: Record<string, string> = {
english: `Hello, ${userName}!`,
spanish: `Β‘Hola, ${userName}!`,
french: `Bonjour, ${userName}!`,
japanese: `γγγ«γ‘γ―γ${userName}γγ!`,
};
return {
content: [
{
type: "text",
text: greetings[language] || greetings.english,
} as TextContent,
],
};
}
if (name === "calculate") {
const operation = args?.operation;
const a = Number(args?.a) || 0;
const b = Number(args?.b) || 0;
let result: number;
switch (operation) {
case "add":
result = a + b;
break;
case "subtract":
result = a - b;
break;
case "multiply":
result = a * b;
break;
case "divide":
if (b === 0) throw new Error("Division by zero!");
result = a / b;
break;
default:
throw new Error(`Unknown operation: ${operation}`);
}
return {
content: [
{
type: "text",
text: `Result: ${result}`,
} as TextContent,
],
};
}
throw new Error(`Unknown tool: ${name}`);
});
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Hello MCP server running on stdio");
}
main().catch(console.error);
Step 3: Test Your Server¶
Create test_client.py
:
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def test_server():
"""Test the Hello MCP server."""
server_params = StdioServerParameters(
command="python",
args=["server.py"]
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
# Initialize connection
await session.initialize()
# List available tools
tools = await session.list_tools()
print("Available tools:")
for tool in tools:
print(f" - {tool.name}: {tool.description}")
# Test say_hello
print("\nTesting say_hello:")
result = await session.call_tool(
"say_hello",
arguments={"name": "World", "language": "spanish"}
)
print(f" Result: {result.content[0].text}")
# Test calculate
print("\nTesting calculate:")
result = await session.call_tool(
"calculate",
arguments={"operation": "multiply", "a": 7, "b": 6}
)
print(f" Result: {result.content[0].text}")
if __name__ == "__main__":
asyncio.run(test_server())
Run the test:
Create test_client.ts
:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
async function testServer() {
const transport = new StdioClientTransport({
command: "node",
args: ["server.js"],
});
const client = new Client(
{
name: "test-client",
version: "1.0.0",
},
{
capabilities: {},
}
);
await client.connect(transport);
// List available tools
const tools = await client.listTools();
console.log("Available tools:");
tools.tools.forEach((tool) => {
console.log(` - ${tool.name}: ${tool.description}`);
});
// Test say_hello
console.log("\nTesting say_hello:");
const helloResult = await client.callTool({
name: "say_hello",
arguments: { name: "World", language: "spanish" },
});
console.log(` Result: ${helloResult.content[0].text}`);
// Test calculate
console.log("\nTesting calculate:");
const calcResult = await client.callTool({
name: "calculate",
arguments: { operation: "multiply", a: 7, b: 6 },
});
console.log(` Result: ${calcResult.content[0].text}`);
await client.close();
}
testServer().catch(console.error);
Compile and run:
Expected Output¶
Available tools:
- say_hello: Say hello in different languages
- calculate: Perform basic arithmetic
Testing say_hello:
Result: Β‘Hola, World!
Testing calculate:
Result: Result: 42
Understanding the Code¶
Server Structure¶
- Import MCP SDK - Core functionality
- Create Server Instance - Named "hello-mcp"
- Define Tools - Using decorators/handlers
- Implement Tool Logic - Process requests
- Start Server - Listen on stdio transport
Key Concepts Demonstrated¶
Tool Definition¶
Tools are defined with: - Name: Unique identifier - Description: Human-readable purpose - Input Schema: JSON Schema for validation
Error Handling¶
The server handles errors gracefully: - Invalid tool names - Missing parameters
- Division by zero
Content Types¶
MCP supports multiple content types. This example uses TextContent
, but you can also return: - ImageContent
- Images - EmbeddedResource
- Files or data
Extending Your Server¶
Try these modifications:
1. Add More Languages¶
greetings = {
"english": f"Hello, {user_name}!",
"spanish": f"Β‘Hola, {user_name}!",
"french": f"Bonjour, {user_name}!",
"japanese": f"γγγ«γ‘γ―γ{user_name}γγ!",
"german": f"Hallo, {user_name}!",
"italian": f"Ciao, {user_name}!",
}
2. Add New Tools¶
@server.list_tools()
async def handle_list_tools():
return existing_tools + [
types.Tool(
name="get_time",
description="Get current time in different timezones",
inputSchema={
"type": "object",
"properties": {
"timezone": {
"type": "string",
"description": "Timezone (e.g., 'UTC', 'EST')"
}
}
}
)
]
3. Add Resources¶
@server.list_resources()
async def handle_list_resources():
return [
types.Resource(
uri="memory://greetings",
name="Greeting History",
description="History of greetings sent"
)
]
Common Issues¶
"Command not found"¶
- Ensure Python/Node is in your PATH
- Check virtual environment is activated
"Module not found"¶
- Install the MCP SDK:
pip install mcp
- Check you're in the correct directory
"Connection refused"¶
- Ensure server is running
- Check for port conflicts
Next Steps¶
Congratulations! You've built your first MCP server. Here's what to explore next:
- Database Connector - Work with real data
- Implementation Guide - Production best practices
- API Reference - Deep dive into the protocol
Run This Tutorial Online¶
Don't want to install anything? Try it in your browser:
What You Accomplished
- β Built a working MCP server
- β Defined and implemented tools
- β Tested with a client
- β Understood core MCP concepts