Build MCP Server (SDK v2 Alpha)
Build and maintain MCP servers on the v2 alpha split-package SDK: @modelcontextprotocol/server, @modelcontextprotocol/client, @modelcontextprotocol/core, plus /node, /express, /hono adapters. ESM-only, Node 20+, Zod v4. Status as of 2026-05-09: latest npm tag is 2.0.0-alpha.2 — pin exact, plan rollback.
When to use
Trigger this skill if any of these are true:
- Building a brand-new MCP server and the user picks v2, "the alpha", or split packages.
package.json already depends on @modelcontextprotocol/server, @modelcontextprotocol/client, or @modelcontextprotocol/core.
- Existing code uses
new McpServer(...) from @modelcontextprotocol/server and server.registerTool(...) with the high-level API.
- Tool/resource/prompt handlers use
(args, ctx) with ctx.mcpReq.signal, ctx.mcpReq.log(), ctx.mcpReq.notify(), or ctx.http?.authInfo.
- HTTP work uses
NodeStreamableHTTPServerTransport from @modelcontextprotocol/node, or createMcpExpressApp() / createMcpHonoApp() from the official adapters.
- Schemas are full
z.object({...}) from zod/v4, not raw-shape shorthand.
Do NOT use this skill if any of these are true:
package.json depends on the single-package @modelcontextprotocol/sdk (v1) — use build-mcp-server-sdk-v1 instead.
- *Handlers use
(args, extra) with extra.sendNotification, extra.authInfo, or extra.signal — that is v1; use build-mcp-server-sdk-v1.*
- The job is porting an existing v1 server to v2 — use
convert-mcp-sdk-v1-to-v2 (covers package split, import rewrite, extra → ctx mapping, OAuth replacement, staging strategy).
- *The project uses the
mcp-use wrapper or @hono/mcp community middleware — use build-mcp-use-server, or migrate before applying official adapter patterns.*
- The user wants an agentic-quality / hardening / context-budget audit, not SDK correctness — pair this skill with the relevant
build-mcp-* reference for protocol patterns.
Detect v2 vs v1
Run tree -L 3 and read package.json. v2 fingerprints (any one is sufficient):
| Signal |
Where |
Means |
@modelcontextprotocol/server (or /client, /core, /node, /express, /hono) |
package.json dependencies |
v2 split package |
import { McpServer, StdioServerTransport } from "@modelcontextprotocol/server" |
source |
v2 server entrypoint |
Handler signature (args, ctx) => … and ctx.mcpReq.* |
source |
v2 ServerContext |
import * as z from "zod/v4" |
source |
v2 Zod v4 path |
"type": "module" + Node 20+ |
package.json / engines |
v2 ESM-only target |
v1 anti-fingerprints (treat as wrong skill, redirect):
@modelcontextprotocol/sdk single package → build-mcp-server-sdk-v1
extra.sendNotification, extra.authInfo, extra.signal → build-mcp-server-sdk-v1
SSEServerTransport → v1 only; v2 removed it
Core rules
- Always use
McpServer from @modelcontextprotocol/server. The low-level Server class is deprecated for direct use.
- Always use
registerTool / registerResource / registerPrompt. Positional overloads were removed in v2.
- Always pass full Zod v4 schemas (
z.object({...})). Raw shapes are a v1 pattern; if a current alpha still accepts them, treat that as a migration shim, not the target.
- Always import HTTP transport from
@modelcontextprotocol/node (e.g. NodeStreamableHTTPServerTransport). SSEServerTransport is removed.
- For Express, use
@modelcontextprotocol/express (createMcpExpressApp()). For Hono, use @modelcontextprotocol/hono. Do not silently substitute the community @hono/mcp package.
- Server-side OAuth is removed from the SDK. Wire authentication at the HTTP layer (Passport, custom Bearer middleware,
jose) and forward auth into ctx.http?.authInfo. Treat any @modelcontextprotocol/server-auth-legacy as planned/open until npm publish is confirmed.
- ESM-only. No CommonJS dual-publish. Node.js 20+ required.
- Pin alpha versions exactly (
--save-exact); never use ^ ranges across alphas.
Workflow
1 — Detect what exists
Inspect package.json and src/. Decide: existing v2 server (go to 2A), new v2 server (go to 2B), or wrong skill (redirect per When to use and stop).
2A — Maintain or fix an existing v2 server
Read the implementation. Verify:
- Context usage:
ctx.mcpReq.signal, ctx.mcpReq.log(), ctx.mcpReq.notify(), ctx.http?.authInfo. Flag any extra.* access — that is v1 leakage.
- Schemas: full
z.object() (not raw shapes) for new code. outputSchema present whenever the tool returns structuredContent.
- Transport:
NodeStreamableHTTPServerTransport from @modelcontextprotocol/node for HTTP; StdioServerTransport from @modelcontextprotocol/server for stdio.
- Framework:
createMcpExpressApp() or createMcpHonoApp() for HTTP framework wiring (DNS rebinding protection lives in the adapter).
- Annotations:
readOnlyHint, destructiveHint, idempotentHint, openWorldHint set deliberately for tools with side effects.
Then make the requested change (add tool, fix bug, add auth middleware, etc.).
2B — Scope a new v2 server
Decide:
- Wraps what? API, database, filesystem, CLI, or in-process logic.
- Transport?
stdio for local; Streamable HTTP for remote/multi-client.
- Framework? Express or Hono if HTTP — both have first-party adapters.
- Auth? External AS + middleware; SDK no longer hosts an authorization server.
3 — Choose the implementation branch
| Scenario |
Read |
| New stdio server |
references/guides/quick-start.md |
| New HTTP server (Express) |
references/guides/transports.md + references/guides/framework-adapters.md |
| New HTTP server (Hono) |
references/guides/transports.md + references/guides/framework-adapters.md |
| Add tools |
references/guides/tools-and-schemas.md |
| Add resources or prompts |
references/guides/resources-and-prompts.md |
| Add auth middleware |
references/guides/authentication.md |
| Build an MCP client |
references/guides/client-api.md |
| Sampling, elicitation, sessions, shutdown |
references/guides/context-and-lifecycle.md |
| Working server examples |
references/examples/server-recipes.md |
| Production hardening |
references/patterns/production-patterns.md |
| Deploy (Docker, serverless, Workers) |
references/patterns/deployment.md |
| Avoid common mistakes / v1 leakage |
references/patterns/anti-patterns.md |
4 — Preflight setup
5 — Build
Default sequence:
- Construct
McpServer with { name, version } and optional { instructions, capabilities }.
- Define Zod v4 schemas:
z.object({ field: z.string() }) (full schemas, not raw shapes).
- Register tools with
server.registerTool(name, config, handler) — inputSchema, annotations, handler (args, ctx) => CallToolResult.
- Register resources with
server.registerResource() if exposing data.
- Register prompts with
server.registerPrompt() if providing templates.
- Construct transport, then
await server.connect(transport).
- Handle graceful shutdown (
SIGINT/SIGTERM → await server.close()).
6 — Validate
- Local checks first:
npm run build, focused tests if present.
- stdio:
npx @anthropic-ai/mcp-inspector npx tsx src/index.ts.
- HTTP: start server; probe with
curl or Inspector.
- Live CLI smoke: if
mcpc is installed, hand off to test-by-mcpc-cli. Minimum sequence: initialize → tools/list → one successful call → one invalid-arg call returning isError: true.
- Schemas: invalid input → tool error (
isError: true), not a thrown protocol error.
- Context: confirm
ctx.mcpReq is the access path, never extra.
Quick start — minimal v2 stdio server
import { McpServer, StdioServerTransport } from "@modelcontextprotocol/server";
import * as z from "zod/v4";
const server = new McpServer(
{ name: "my-server", version: "1.0.0" },
{ instructions: "A helpful server" }
);
server.registerTool("greet", {
title: "Greet User",
description: "Greet a user by name",
inputSchema: z.object({ name: z.string().describe("The user's name") }),
annotations: { readOnlyHint: true, destructiveHint: false },
}, async ({ name }, ctx) => {
await ctx.mcpReq.log("info", `Greeting ${name}`);
return { content: [{ type: "text" as const, text: `Hello, ${name}!` }] };
});
const transport = new StdioServerTransport();
await server.connect(transport);
Core API summary
McpServer
new McpServer(
{ name: string, version: string, description?: string, icons?: Icon[] },
{ capabilities?: ServerCapabilities, instructions?: string }
)
server.connect(transport: Transport): Promise<void>
server.close(): Promise<void>
server.registerTool(name, config, handler): RegisteredTool
server.registerResource(name, uri | template, config, handler): RegisteredResource
server.registerPrompt(name, config, handler): RegisteredPrompt
server.sendToolListChanged(): void
server.sendResourceListChanged(): void
server.sendPromptListChanged(): void
server.sendLoggingMessage(params): Promise<void>
server.isConnected(): boolean
server.experimental.tasks // ExperimentalMcpServerTasks
registerTool config
{
title?: string,
description?: string,
inputSchema?: AnySchema, // z.object({...}) — full Zod v4 schema
outputSchema?: AnySchema, // enables structuredContent validation
annotations?: ToolAnnotations,
_meta?: Record<string, unknown>,
}
ServerContext (handler second argument)
// Tool handler: (args, ctx) => CallToolResult
// No-arg tool: (ctx) => CallToolResult
ctx.sessionId?: string
ctx.mcpReq.id: RequestId
ctx.mcpReq.method: string
ctx.mcpReq.signal: AbortSignal
ctx.mcpReq._meta?: RequestMeta
ctx.mcpReq.send(request, schema, options?): Promise<Result>
ctx.mcpReq.notify(notification): Promise<void>
ctx.mcpReq.log(level, data, logger?): Promise<void>
ctx.mcpReq.elicitInput(params): Promise<ElicitResult>
ctx.mcpReq.requestSampling(params): Promise<CreateMessageResult>
ctx.http?.authInfo?: AuthInfo
ctx.http?.req?: RequestInfo
ctx.http?.closeSSE?(): void
ctx.http?.closeStandaloneSSE?(): void
ctx.task?.id?: string
ctx.task?.store?: RequestTaskStore
Error handling
import { ProtocolError, ProtocolErrorCode } from "@modelcontextprotocol/core";
// Hard protocol errors:
throw new ProtocolError(ProtocolErrorCode.InvalidParams, "Bad input");
// Soft tool errors (LLM can self-correct):
return { content: [{ type: "text", text: "Error: not found" }], isError: true };
Decision rules
- Use full
z.object({...}) for every new tool schema. Raw shapes are v1 style; even if accepted, do not target them.
- Prefer
isError: true for recoverable failures — the LLM self-corrects from soft errors.
- Prefer
ctx.mcpReq.log() over console.error() so logs reach the client.
- Prefer
ctx.mcpReq.elicitInput() over hand-rolled ctx.mcpReq.send() for user input requests.
- Use
createMcpExpressApp() / createMcpHonoApp() instead of raw Express/Hono setup — DNS rebinding is handled inside.
- Set every relevant
annotations field deliberately; fill all four when safety or side-effects matter.
Guardrails
- Never write new v2-native code with raw Zod shapes — always full
z.object().
- Never use
extra.sendNotification / extra.authInfo / extra.signal — those are v1; the v2 access path is ctx.mcpReq.* and ctx.http?.authInfo.
- Never import from
@modelcontextprotocol/sdk — that is the v1 single package; in v2 you import from /server, /client, /core, /node, /express, /hono.
- Never use
SSEServerTransport — removed in v2; use Streamable HTTP.
- Never implement server-side OAuth in the SDK — removed in v2; integrate at the HTTP layer.
- Never use CommonJS — v2 is ESM-only.
- Never run on Node < 20.
- Never use
^ ranges for alpha packages — pin exact and plan rollback.
Compatibility and adoption note
v2 is pre-release alpha as of 2026-05-09. The latest npm split packages are at 2.0.0-alpha.2; main-branch PRs labeled v2.0.0-bc may not yet be published. Most production servers should remain on v1.x until v2 cuts a non-alpha stable release.
In practice:
- Pin alpha versions exactly (no
^); alphas can break between patches.
- Plan rollback before deploying — keep the v1 branch deployable.
- The
@modelcontextprotocol/sdk meta-package remains v1 on npm unless fresh npm view proves otherwise.
@modelcontextprotocol/server-auth-legacy is planned/open; treat it as unpublished until npm view succeeds.
- Verify each MCP host (Claude Desktop, Cursor, Cline, custom) end-to-end on v2 features before depending on them.
Output contract
Report v2 server work with:
- Target path and detected channel/version.
- Transport (stdio, Streamable HTTP) and framework (none, Express, Hono).
- Tools, resources, and prompts added or changed.
- Auth shape (none, Bearer middleware, Passport, jose, external AS).
- Validation rung reached and exact commands run.
- Alpha-risk caveats and rollback status.
1---2name: build-mcp-server-sdk-v23description: Use if building MCP servers on @modelcontextprotocol/server v2 alpha — split packages, registerTool.4---56# Build MCP Server (SDK v2 Alpha)78Build and maintain MCP servers on the v2 alpha **split-package SDK**: `@modelcontextprotocol/server`, `@modelcontextprotocol/client`, `@modelcontextprotocol/core`, plus `/node`, `/express`, `/hono` adapters. ESM-only, Node 20+, Zod v4. Status as of 2026-05-09: latest npm tag is `2.0.0-alpha.2` — pin exact, plan rollback.910## When to use1112Trigger this skill if any of these are true:1314- *Building a brand-new MCP server and the user picks v2, "the alpha", or split packages.*15- *`package.json` already depends on `@modelcontextprotocol/server`, `@modelcontextprotocol/client`, or `@modelcontextprotocol/core`.*16- *Existing code uses `new McpServer(...)` from `@modelcontextprotocol/server` and `server.registerTool(...)` with the high-level API.*17- *Tool/resource/prompt handlers use `(args, ctx)` with `ctx.mcpReq.signal`, `ctx.mcpReq.log()`, `ctx.mcpReq.notify()`, or `ctx.http?.authInfo`.*18- *HTTP work uses `NodeStreamableHTTPServerTransport` from `@modelcontextprotocol/node`, or `createMcpExpressApp()` / `createMcpHonoApp()` from the official adapters.*19- *Schemas are full `z.object({...})` from `zod/v4`, not raw-shape shorthand.*2021Do NOT use this skill if any of these are true:2223- *`package.json` depends on the single-package `@modelcontextprotocol/sdk` (v1) — use **`build-mcp-server-sdk-v1`** instead.*24- *Handlers use `(args, extra)` with `extra.sendNotification`, `extra.authInfo`, or `extra.signal` — that is v1; use **`build-mcp-server-sdk-v1`**.*25- *The job is **porting** an existing v1 server to v2 — use **`convert-mcp-sdk-v1-to-v2`** (covers package split, import rewrite, `extra → ctx` mapping, OAuth replacement, staging strategy).*26- *The project uses the `mcp-use` wrapper or `@hono/mcp` community middleware — use **`build-mcp-use-server`**, or migrate before applying official adapter patterns.*27- *The user wants an agentic-quality / hardening / context-budget audit, not SDK correctness — pair this skill with the relevant `build-mcp-*` reference for protocol patterns.*2829## Detect v2 vs v13031Run `tree -L 3` and read `package.json`. v2 fingerprints (any one is sufficient):3233| Signal | Where | Means |34|---|---|---|35| `@modelcontextprotocol/server` (or `/client`, `/core`, `/node`, `/express`, `/hono`) | `package.json` dependencies | v2 split package |36| `import { McpServer, StdioServerTransport } from "@modelcontextprotocol/server"` | source | v2 server entrypoint |37| Handler signature `(args, ctx) => …` and `ctx.mcpReq.*` | source | v2 ServerContext |38| `import * as z from "zod/v4"` | source | v2 Zod v4 path |39| `"type": "module"` + Node 20+ | `package.json` / engines | v2 ESM-only target |4041v1 anti-fingerprints (treat as **wrong skill**, redirect):4243- `@modelcontextprotocol/sdk` single package → `build-mcp-server-sdk-v1`44- `extra.sendNotification`, `extra.authInfo`, `extra.signal` → `build-mcp-server-sdk-v1`45- `SSEServerTransport` → v1 only; v2 removed it4647## Core rules4849- Always use `McpServer` from `@modelcontextprotocol/server`. The low-level `Server` class is deprecated for direct use.50- Always use `registerTool` / `registerResource` / `registerPrompt`. Positional overloads were removed in v2.51- Always pass full Zod v4 schemas (`z.object({...})`). Raw shapes are a v1 pattern; if a current alpha still accepts them, treat that as a migration shim, not the target.52- Always import HTTP transport from `@modelcontextprotocol/node` (e.g. `NodeStreamableHTTPServerTransport`). `SSEServerTransport` is removed.53- For Express, use `@modelcontextprotocol/express` (`createMcpExpressApp()`). For Hono, use `@modelcontextprotocol/hono`. Do not silently substitute the community `@hono/mcp` package.54- Server-side OAuth is removed from the SDK. Wire authentication at the HTTP layer (Passport, custom Bearer middleware, `jose`) and forward auth into `ctx.http?.authInfo`. Treat any `@modelcontextprotocol/server-auth-legacy` as planned/open until npm publish is confirmed.55- ESM-only. No CommonJS dual-publish. Node.js 20+ required.56- Pin alpha versions exactly (`--save-exact`); never use `^` ranges across alphas.5758## Workflow5960### 1 — Detect what exists6162Inspect `package.json` and `src/`. Decide: **existing v2 server** (go to 2A), **new v2 server** (go to 2B), or **wrong skill** (redirect per *When to use* and stop).6364### 2A — Maintain or fix an existing v2 server6566Read the implementation. Verify:6768- Context usage: `ctx.mcpReq.signal`, `ctx.mcpReq.log()`, `ctx.mcpReq.notify()`, `ctx.http?.authInfo`. Flag any `extra.*` access — that is v1 leakage.69- Schemas: full `z.object()` (not raw shapes) for new code. `outputSchema` present whenever the tool returns `structuredContent`.70- Transport: `NodeStreamableHTTPServerTransport` from `@modelcontextprotocol/node` for HTTP; `StdioServerTransport` from `@modelcontextprotocol/server` for stdio.71- Framework: `createMcpExpressApp()` or `createMcpHonoApp()` for HTTP framework wiring (DNS rebinding protection lives in the adapter).72- Annotations: `readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint` set deliberately for tools with side effects.7374Then make the requested change (add tool, fix bug, add auth middleware, etc.).7576### 2B — Scope a new v2 server7778Decide:79801. **Wraps what?** API, database, filesystem, CLI, or in-process logic.812. **Transport?** `stdio` for local; `Streamable HTTP` for remote/multi-client.823. **Framework?** Express or Hono if HTTP — both have first-party adapters.834. **Auth?** External AS + middleware; SDK no longer hosts an authorization server.8485### 3 — Choose the implementation branch8687| Scenario | Read |88|---|---|89| New stdio server | `references/guides/quick-start.md` |90| New HTTP server (Express) | `references/guides/transports.md` + `references/guides/framework-adapters.md` |91| New HTTP server (Hono) | `references/guides/transports.md` + `references/guides/framework-adapters.md` |92| Add tools | `references/guides/tools-and-schemas.md` |93| Add resources or prompts | `references/guides/resources-and-prompts.md` |94| Add auth middleware | `references/guides/authentication.md` |95| Build an MCP client | `references/guides/client-api.md` |96| Sampling, elicitation, sessions, shutdown | `references/guides/context-and-lifecycle.md` |97| Working server examples | `references/examples/server-recipes.md` |98| Production hardening | `references/patterns/production-patterns.md` |99| Deploy (Docker, serverless, Workers) | `references/patterns/deployment.md` |100| Avoid common mistakes / v1 leakage | `references/patterns/anti-patterns.md` |101102### 4 — Preflight setup103104- [ ] Node.js 20+ installed105- [ ] If existing: run `bash scripts/check-mcp-server-v2-version.sh` from the project root (see `scripts/check-mcp-server-v2-version.sh.md`); unsafe alpha ranges must fail106- [ ] `npm install --save-exact @modelcontextprotocol/server@2.0.0-alpha.2`107- [ ] `npm install zod@^4`108- [ ] HTTP also: `npm install --save-exact @modelcontextprotocol/node@2.0.0-alpha.2`109- [ ] Express also: `npm install --save-exact @modelcontextprotocol/express@2.0.0-alpha.2 express`110- [ ] Hono also: `npm install --save-exact @modelcontextprotocol/hono@2.0.0-alpha.2 hono`111- [ ] `"type": "module"` in `package.json`112- [ ] TypeScript 5+, `"module": "Node16"`, `"moduleResolution": "Node16"`113114### 5 — Build115116Default sequence:1171181. Construct `McpServer` with `{ name, version }` and optional `{ instructions, capabilities }`.1192. Define Zod v4 schemas: `z.object({ field: z.string() })` (full schemas, not raw shapes).1203. Register tools with `server.registerTool(name, config, handler)` — `inputSchema`, `annotations`, handler `(args, ctx) => CallToolResult`.1214. Register resources with `server.registerResource()` if exposing data.1225. Register prompts with `server.registerPrompt()` if providing templates.1236. Construct transport, then `await server.connect(transport)`.1247. Handle graceful shutdown (`SIGINT`/`SIGTERM` → `await server.close()`).125126### 6 — Validate127128- Local checks first: `npm run build`, focused tests if present.129- **stdio:** `npx @anthropic-ai/mcp-inspector npx tsx src/index.ts`.130- **HTTP:** start server; probe with `curl` or Inspector.131- **Live CLI smoke:** if `mcpc` is installed, hand off to `test-by-mcpc-cli`. Minimum sequence: initialize → `tools/list` → one successful call → one invalid-arg call returning `isError: true`.132- **Schemas:** invalid input → tool error (`isError: true`), not a thrown protocol error.133- **Context:** confirm `ctx.mcpReq` is the access path, never `extra`.134135## Quick start — minimal v2 stdio server136137```typescript138import { McpServer, StdioServerTransport } from "@modelcontextprotocol/server";139import * as z from "zod/v4";140141const server = new McpServer(142 { name: "my-server", version: "1.0.0" },143 { instructions: "A helpful server" }144);145146server.registerTool("greet", {147 title: "Greet User",148 description: "Greet a user by name",149 inputSchema: z.object({ name: z.string().describe("The user's name") }),150 annotations: { readOnlyHint: true, destructiveHint: false },151}, async ({ name }, ctx) => {152 await ctx.mcpReq.log("info", `Greeting ${name}`);153 return { content: [{ type: "text" as const, text: `Hello, ${name}!` }] };154});155156const transport = new StdioServerTransport();157await server.connect(transport);158```159160## Core API summary161162### McpServer163164```typescript165new McpServer(166 { name: string, version: string, description?: string, icons?: Icon[] },167 { capabilities?: ServerCapabilities, instructions?: string }168)169170server.connect(transport: Transport): Promise<void>171server.close(): Promise<void>172server.registerTool(name, config, handler): RegisteredTool173server.registerResource(name, uri | template, config, handler): RegisteredResource174server.registerPrompt(name, config, handler): RegisteredPrompt175server.sendToolListChanged(): void176server.sendResourceListChanged(): void177server.sendPromptListChanged(): void178server.sendLoggingMessage(params): Promise<void>179server.isConnected(): boolean180server.experimental.tasks // ExperimentalMcpServerTasks181```182183### registerTool config184185```typescript186{187 title?: string,188 description?: string,189 inputSchema?: AnySchema, // z.object({...}) — full Zod v4 schema190 outputSchema?: AnySchema, // enables structuredContent validation191 annotations?: ToolAnnotations,192 _meta?: Record<string, unknown>,193}194```195196### ServerContext (handler second argument)197198```typescript199// Tool handler: (args, ctx) => CallToolResult200// No-arg tool: (ctx) => CallToolResult201202ctx.sessionId?: string203ctx.mcpReq.id: RequestId204ctx.mcpReq.method: string205ctx.mcpReq.signal: AbortSignal206ctx.mcpReq._meta?: RequestMeta207ctx.mcpReq.send(request, schema, options?): Promise<Result>208ctx.mcpReq.notify(notification): Promise<void>209ctx.mcpReq.log(level, data, logger?): Promise<void>210ctx.mcpReq.elicitInput(params): Promise<ElicitResult>211ctx.mcpReq.requestSampling(params): Promise<CreateMessageResult>212ctx.http?.authInfo?: AuthInfo213ctx.http?.req?: RequestInfo214ctx.http?.closeSSE?(): void215ctx.http?.closeStandaloneSSE?(): void216ctx.task?.id?: string217ctx.task?.store?: RequestTaskStore218```219220### Error handling221222```typescript223import { ProtocolError, ProtocolErrorCode } from "@modelcontextprotocol/core";224225// Hard protocol errors:226throw new ProtocolError(ProtocolErrorCode.InvalidParams, "Bad input");227228// Soft tool errors (LLM can self-correct):229return { content: [{ type: "text", text: "Error: not found" }], isError: true };230```231232## Decision rules233234- Use full `z.object({...})` for every new tool schema. Raw shapes are v1 style; even if accepted, do not target them.235- Prefer `isError: true` for recoverable failures — the LLM self-corrects from soft errors.236- Prefer `ctx.mcpReq.log()` over `console.error()` so logs reach the client.237- Prefer `ctx.mcpReq.elicitInput()` over hand-rolled `ctx.mcpReq.send()` for user input requests.238- Use `createMcpExpressApp()` / `createMcpHonoApp()` instead of raw Express/Hono setup — DNS rebinding is handled inside.239- Set every relevant `annotations` field deliberately; fill all four when safety or side-effects matter.240241## Guardrails242243- Never write new v2-native code with raw Zod shapes — always full `z.object()`.244- Never use `extra.sendNotification` / `extra.authInfo` / `extra.signal` — those are v1; the v2 access path is `ctx.mcpReq.*` and `ctx.http?.authInfo`.245- Never import from `@modelcontextprotocol/sdk` — that is the v1 single package; in v2 you import from `/server`, `/client`, `/core`, `/node`, `/express`, `/hono`.246- Never use `SSEServerTransport` — removed in v2; use Streamable HTTP.247- Never implement server-side OAuth in the SDK — removed in v2; integrate at the HTTP layer.248- Never use CommonJS — v2 is ESM-only.249- Never run on Node < 20.250- Never use `^` ranges for alpha packages — pin exact and plan rollback.251252## Compatibility and adoption note253254v2 is pre-release alpha as of 2026-05-09. The latest npm split packages are at `2.0.0-alpha.2`; main-branch PRs labeled `v2.0.0-bc` may not yet be published. Most production servers should remain on v1.x until v2 cuts a non-alpha stable release.255256In practice:257258- **Pin alpha versions exactly** (no `^`); alphas can break between patches.259- **Plan rollback** before deploying — keep the v1 branch deployable.260- **The `@modelcontextprotocol/sdk` meta-package** remains v1 on npm unless fresh `npm view` proves otherwise.261- **`@modelcontextprotocol/server-auth-legacy`** is planned/open; treat it as unpublished until `npm view` succeeds.262- **Verify each MCP host** (Claude Desktop, Cursor, Cline, custom) end-to-end on v2 features before depending on them.263264## Output contract265266Report v2 server work with:2672681. Target path and detected channel/version.2692. Transport (stdio, Streamable HTTP) and framework (none, Express, Hono).2703. Tools, resources, and prompts added or changed.2714. Auth shape (none, Bearer middleware, Passport, jose, external AS).2725. Validation rung reached and exact commands run.2736. Alpha-risk caveats and rollback status.