03. SDK で書き直す

同じサーバーを公式 SDK で書き、何が省けたのかを確かめる

📅 作成: 2026-09-07 / 更新: 2026-09-07

この資料の内容

  1. 用意する
  2. 20 行で同じものになる
  3. 何が省けたか
  4. 入力の形は zod で書く
  5. SDK が黙って足すもの

用意する

入れるもの

npm init -y
npm install @modelcontextprotocol/sdk

zod は SDK が依存しているので一緒に入りますが、自分のコードから直接使うので明示しておきます。

{
	"dependencies": {
		"@modelcontextprotocol/sdk": "^1.30.0",
		"zod": "^4.5.4"
	}
}

この資料を書いた時点の版

もの確かめ方
Node.jsv26.8.1node --version
npm11.19.0npm --version
SDK1.30.0npm view @modelcontextprotocol/sdk version
zod4.5.4npm ls zod
自分が書くところ ツールの名前・説明・入力の形・中身の処理 SDK が受け持つところ JSON-RPC の組み立て・initialize の応答・入力の検証・エラーの形式・通知の宛先 stdio / HTTP(トランスポート)
02 章で手書きした部分の大半が、真ん中の層に収まる。

20 行で同じものになる

samples/03-sdk/server.mjs の全文です。02 章の 90 行と同じことをします。

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';

const server = new McpServer({
	name: 'sdk',
	version: '1.0.0',
});

server.registerTool(
	'add',
	{
		title: '足し算',
		description: '2 つの数を足す',
		// 入力の形は zod で書く。JSON Schema には SDK が変換して送る
		inputSchema: {
			a: z.number().describe('1 つめの数'),
			b: z.number().describe('2 つめの数'),
		},
	},
	async ({ a, b }) => ({
		content: [{ type: 'text', text: String(a + b) }],
	}),
);

// stdio に繋ぐ。ここで初めて標準入出力を読み書きし始める
await server.connect(new StdioServerTransport());

02 章と同じ手順で動かせます。

{"result":{"protocolVersion":"2025-11-25","capabilities":{"tools":{"listChanged":true}},"serverInfo":{"name":"sdk","version":"1.0.0"}},"jsonrpc":"2.0","id":1}
{"result":{"content":[{"type":"text","text":"5"}]},"jsonrpc":"2.0","id":3}
02 章(手書き) 90 03 章(SDK) 27 行 減ったのは「毎回同じ部分」 initialize の応答、id の対応付け、 通知の判別、エラーの形、入力の検証 ツールの中身は 1 行も減っていない
SDK が肩代わりするのは、どのサーバーにも共通の定型部分だけ。

何が省けたか

02 章で書いていたことSDK では
initialize への応答不要。McpServer が答える
通知(id 無し)の判別不要。SDK が振り分ける
capabilities の申告不要。registerTool を呼べば tools が付く
JSON の組み立てと改行不要。トランスポートが行う
引数の型の検査不要。zod の定義から SDK が検証する
-32601 などのエラー不要。知らないメソッドには SDK が返す
ツールの中身自分で書く
tools/call a: "あ", b: 3 SDK が検証 zod の定義と突き合わせる 通れば 通らなければ 自分のコードが呼ばれる 引数は型が保証されている 自分のコードは呼ばれない SDK が isError で返す
型が違う引数は自分のコードまで届かない。02 章で書いた typeof の検査が要らなくなる。

検証は SDK に任せ、意味の検査は自分でやります。「数値であること」は SDK が見ますが、「0 で割ろうとしている」は自分で見て isError で返します。型は SDK、業務の都合は自分と覚えておくと分けやすくなります。

入力の形は zod で書く

inputSchema には zod の定義を渡します。SDK がこれを JSON Schema に変換してホストへ送ります。

inputSchema: {
	a: z.number().describe('1 つめの数'),
	b: z.number().describe('2 つめの数'),
}

実際に送られる JSON Schema です。

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "a": { "type": "number", "description": "1 つめの数" },
    "b": { "type": "number", "description": "2 つめの数" }
  },
  "required": ["a", "b"]
}

よく使う書き方

やりたいこと書き方備考
文字列z.string()空を禁じるなら .min(1)
整数の範囲z.number().int().min(1).max(50)範囲は説明よりも効く
省略できるz.string().optional()required から外れる
既定値z.number().default(10)省略時にこの値が入る
選択肢z.enum(['箇条書き', '文章'])モデルが値を迷わない
配列z.array(z.string())札の一覧など
説明を足す.describe('探す語')必ず書く
zod の定義 z.number().describe(...) SDK JSON Schema tools/list で送られる モデルが読む これだけを頼りに呼ぶ describe を省くと、モデルは名前と型だけで推測することになる
モデルはツールの実装を見ない。渡るのは名前・説明・スキーマだけ。

説明文はプロンプトの一部です。descriptiondescribe はモデルに直接読まれます。「いつ使うか」「何を渡すか」を、初めて見る人に説明するつもりで書いてください。ここが雑だと、モデルは正しく呼べません。

SDK が黙って足すもの

tools/list の応答を実際に見ると、書いた覚えのない項目が入っています。

{
  "name": "add",
  "title": "足し算",
  "description": "2 つの数を足す",
  "inputSchema": { ... },
  "execution": { "taskSupport": "forbidden" }
}

execution.taskSupport は SDK が付けたものです。「このツールはタスクとして実行できない」という意味で、09. タスク で扱います。今は気にしなくて構いませんが、覚えのない項目が出ても壊れてはいない、と知っておくと安心です。

capabilities も自動で付く

registerTool を呼べば toolsregisterResource を呼べば resourcescapabilities に足されます。02 章で手書きしていた申告は要りません。

"capabilities": { "tools": { "listChanged": true } }
registerTool(...) registerResource(...) registerPrompt(...) initialize の応答に入る capabilities { "tools": {...}, "resources": {...}, "prompts": {...} } 登録したものだけが並ぶ
持っていない機能を名乗ることはない。ホストは並んだものだけを使う。

例外があります。タスク(09 章)だけは自分で capabilities に書く必要があります。実験的な機能なので、自動では付きません。忘れると「タスクに対応していない」と断られます。

次の章へ

サーバーは書けました。次はこれを Claude Code・Codex・Antigravity に登録して、実際に呼ばせます。3 つとも登録の仕方が違います。