Chapter 04 · Section III · 12 min read
4.3 TypeScript SDK: query, messages, SDK MCP
The TypeScript SDK — the query function, the message types you'll pattern-match on, and how to expose local functions as MCP tools without spawning a server.
The TypeScript SDK gives you the same headless Claude Code power as the Python one, plus a Node-native ergonomic and — the small superpower — the ability to expose in-process functions as MCP tools directly. Reach for it when your integration lives in a Node/Bun app.
Installation
npm install @anthropic-ai/claude-code-sdk
Node 20+.
Minimal example
import { query, type Message } from "@anthropic-ai/claude-code-sdk";
for await (const msg of query({
prompt: "Summarise src/README.md",
options: {
model: "claude-sonnet-4-6",
permissionMode: "acceptEdits",
},
})) {
if (msg.type === "assistant") {
console.log(msg.content);
}
}
query is the one-shot entry point. It returns an async iterator of Message objects.
The message types
The Message union covers everything you might yield:
type Message =
| { type: "assistant"; content: string; /* ... */ }
| { type: "user"; content: ToolResult[]; /* ... */ }
| { type: "result"; subtype: "success" | "error"; /* ... */ }
| { type: "system"; /* metadata */ };
Pattern-match on msg.type. TypeScript narrows the union so you get typed access to whichever branch you are in.
switch (msg.type) {
case "assistant":
console.log(msg.content);
break;
case "result":
console.log(`cost $${msg.total_cost_usd}`);
break;
}
SDK MCP servers
The killer feature: expose local TypeScript functions as MCP tools without running a separate server process.
import { query, createSdkMcpServer, tool } from "@anthropic-ai/claude-code-sdk";
const analytics = createSdkMcpServer({
name: "analytics",
tools: [
tool({
name: "count_users_since",
description: "Return the number of users signed up since a date.",
input: z.object({ since: z.string() }),
async run({ since }) {
return { count: await db.users.countSince(since) };
},
}),
],
});
for await (const msg of query({
prompt: "how many new users in the last 30 days?",
options: { mcpServers: [analytics] },
})) {
/* ... */
}
Claude sees mcp__analytics__count_users_since as a tool; when it calls it, your function runs in-process. No subprocess, no serialisation across a pipe, no separate lifecycle to manage.
When to reach for SDK MCP
- Your service already has typed clients for the data Claude needs — reuse them.
- Latency matters — subprocess spawn + stdio has overhead.
- You want tight control over what Claude can call — the function signature is the schema.
Long-lived sessions
For interactive-shaped applications, hold the query iterator open and feed it new prompts through the SDK’s session-management primitives (version-dependent — check the docs for Session / resume).
Check your understanding
Quick check
—