P7 Intermediate
Build an MCP Server
Create tools with createSdkMcpServer() and connect to agents.
SDK APIs
createSdkMcpServer()
tool()
mcpServers
mcp__*__*
Exercise 1: In-Process MCP Server
Create a weather MCP server using createSdkMcpServer() and tool(), then wire it into an agent.
Starter Code
exercise-1.ts
import { query, createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
import { z } from "zod";
// Step 1: Create an in-process MCP server with tools
const weatherServer = createSdkMcpServer({
name: "weather",
tools: [
tool("get_weather", "Get current weather for a city", {
city: z.string().describe("City name"),
units: z.enum(["celsius", "fahrenheit"]).default("celsius"),
}, async ({ city, units }) => {
// Simulated weather data
const data: Record<string, { temp: number; condition: string }> = {
"tokyo": { temp: 22, condition: "Partly cloudy" },
"london": { temp: 14, condition: "Rainy" },
"new york": { temp: 28, condition: "Sunny" },
"sydney": { temp: 19, condition: "Overcast" },
};
const weather = data[city.toLowerCase()] ?? { temp: 20, condition: "Unknown" };
const temp = units === "fahrenheit" ? weather.temp * 9/5 + 32 : weather.temp;
return { content: [{ type: "text", text: `${city}: ${temp}deg${units === "fahrenheit" ? "F" : "C"}, ${weather.condition}` }] };
}),
tool("get_forecast", "Get 3-day weather forecast for a city", {
city: z.string().describe("City name"),
}, async ({ city }) => {
return { content: [{ type: "text", text: `${city} forecast: Today: 22C Sunny, Tomorrow: 19C Cloudy, Day 3: 17C Rain` }] };
})
]
});
// Step 2: Connect the server to an agent
const response = query({
prompt: "What's the weather like in Tokyo and London? Also get me Tokyo's 3-day forecast.",
options: {
mcpServers: { "weather": weatherServer }
}
});
// Step 3: Stream messages and track tool calls
const toolCalls: string[] = [];
for await (const message of response) {
if (message.type === "tool_call") {
toolCalls.push(`${message.tool_name}(${JSON.stringify(message.tool_input)})`);
}
if (message.type === "result") {
console.log(message.content);
}
}
// Tools are namespaced as mcp__weather__get_weather and mcp__weather__get_forecast
console.log("\n[Tools Used]");
for (const call of toolCalls) {
console.log(` > ${call}`);
}
Your task: Run this code and observe the tool calls. Then add a third tool get_alerts that returns weather alerts for a region. Verify the agent uses all three tools appropriately.
Exercise 2: External MCP Server
Connect to a stdio-based external MCP server and verify its status with mcpServerStatus().
Exercise Code
exercise-2.ts
import { query } from "@anthropic-ai/claude-agent-sdk";
// Connect to an external stdio-based MCP server
// Example: GitHub MCP server via npx
const response = query({
prompt: "List the 5 most recent issues in the anthropics/claude-code repo",
options: {
mcpServers: {
"github": {
type: "stdio", // Required: "stdio" | "http" | "sse"
command: "npx",
args: ["-y", "@modelcontextprotocol/server-github"],
env: {
GITHUB_PERSONAL_ACCESS_TOKEN: process.env.GITHUB_TOKEN!,
},
},
},
},
});
// Stream and collect the result
for await (const message of response) {
if (message.type === "result") {
console.log(message.content);
}
}
// Check server health and available tools
const status = await response.mcpServerStatus();
for (const [name, info] of Object.entries(status)) {
console.log(`\n[Server] ${name}`);
console.log(` Status: ${info.connected ? "Connected" : "Disconnected"}`);
console.log(` Tools: ${info.tools.map(t => t.name).join(", ")}`);
console.log(` Latency: ${info.latency_ms}ms`);
}
// Alternative: filesystem MCP server
const fsResponse = query({
prompt: "List all markdown files in the docs/ directory",
options: {
mcpServers: {
"filesystem": {
type: "stdio",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-filesystem", "./docs"],
},
},
},
});
for await (const message of fsResponse) {
if (message.type === "result") {
console.log(message.content);
}
}
Your task: Set up your GITHUB_TOKEN and run this code. Inspect the mcpServerStatus() output. Then try connecting to a different MCP server (e.g., the filesystem server) and verify its tool list.
Exercise 3: Multi-Server Agent
Connect 3 MCP servers simultaneously and build an agent that orchestrates tools across all of them.
Challenge
exercise-3.ts
import { query, createSdkMcpServer, tool } from "@anthropic-ai/claude-agent-sdk";
import { z } from "zod";
// Server 1: Database (in-process)
const dbServer = createSdkMcpServer({
name: "database",
tools: [
tool("query_users", "Query user records from the database", {
filter: z.string().optional().describe("Filter string to match against user data")
}, async ({ filter }) => {
const users = [
{ id: 1, name: "Alice", email: "alice@example.com", city: "Tokyo" },
{ id: 2, name: "Bob", email: "bob@example.com", city: "London" },
{ id: 3, name: "Carol", email: "carol@example.com", city: "New York" },
];
const filtered = filter
? users.filter(u => JSON.stringify(u).toLowerCase().includes(filter.toLowerCase()))
: users;
return { content: [{ type: "text", text: JSON.stringify(filtered) }] };
})
]
});
// Server 2: Weather (in-process)
const weatherServer = createSdkMcpServer({
name: "weather",
tools: [
tool("get_weather", "Get weather for a city", {
city: z.string().describe("City name")
}, async ({ city }) => {
return { content: [{ type: "text", text: `${city}: 22C, Sunny` }] };
})
]
});
// Server 3: Notifications (in-process)
const notifyServer = createSdkMcpServer({
name: "notifications",
tools: [
tool("send_email", "Send an email notification", {
to: z.string().email().describe("Recipient email address"),
subject: z.string().describe("Email subject"),
body: z.string().describe("Email body content")
}, async ({ to, subject }) => {
return { content: [{ type: "text", text: `Email sent to ${to}: "${subject}"` }] };
})
]
});
// Connect all 3 servers to one agent
const response = query({
prompt: `For each user in the database:
1. Look up the weather in their city
2. Send them a personalized email with their local weather forecast
Report what you did.`,
options: {
mcpServers: {
"database": dbServer,
"weather": weatherServer,
"notifications": notifyServer
}
}
});
// Stream and analyze tool usage
const serverUsage = new Map<string, number>();
for await (const message of response) {
if (message.type === "tool_call") {
// Tool names are prefixed: mcp__database__query_users, mcp__weather__get_weather, etc.
const server = message.tool_name.split("__")[1];
serverUsage.set(server, (serverUsage.get(server) ?? 0) + 1);
}
if (message.type === "result") {
console.log(message.content);
}
}
console.log("\n[Server Usage]");
for (const [server, count] of serverUsage) {
console.log(` ${server}: ${count} calls`);
}