クリーム色の背景、色付き表示、高コントラスト、大きな文字のほうが読みやすいですか?配色と文字を選ぶと、すべてのページに一度で反映されます。

無料レッスン

initialize のハンドシェイク

このレッスンのあとで: initialize の要求に答え、サーバーにできることを知らせることができる。

70分のレッスンコマンド9件AI コーディングエージェントのラボ:opencode でコードを説明・修正し、その結果を確かめる

サーバーは initialize にどう答えるか

ハンドシェイクは、MCPのクライアントとサーバーが最初に交わすやり取りです。クライアントが、使いたいバージョンを添えて initialize を送ります。サーバーは、使うバージョンと、できることを答えます。

クライアントは、サーバーが「ある」と言ったものだけを使います。言いすぎるサーバーは、クライアントを壊します。

このレッスンのゴール

メモ

initialize の要求に答え、サーバーにできることを知らせることができる。

キーワード

ことばいみれい
ハンドシェイクセッションを始める最初のやり取り
プロトコルのバージョン規則の版を示す日付2024-11-05
capabilityサーバーが持つと知らせる機能のまとまりtools
状態サーバーが覚えていること: new、waiting、ready
折り合う両方が話せる一つのバージョンに決める

学生が入力するセルは、このプレビューでは空白です。

サーバーは知っている版を選ぶ

クライアントがバージョンを名指しします。サーバーは、話せるバージョンのリストを持っています。求められたものがリストにあれば、それで答えます。

なければ、このサーバーはコード -32602 で断ります。求められた版と自分のリストをエラーに入れるので、クライアントはやり直せます。

例: 三つのクライアントが求める

クライアントが求める版サーバーが話せる版サーバーの答え
2024-11-052025-03-26, 2024-11-052024-11-05 を使う result
2025-03-262025-03-26, 2024-11-052025-03-26 を使う result
1999-01-012025-03-26, 2024-11-05二つのリストを添えたエラー -32602

学生が入力するセルは、このプレビューでは空白です。

サーバーはできることだけ知らせる

initialize の result には capabilities が入ります。それぞれが、サーバーの持つメソッドのまとまりです。tools、resources、prompts、logging があります。

旗が付くものもあります。listChanged は、一覧が変わったらクライアントに知らせるという意味です。このサーバーは知らせないので false です。知らせていないまとまりのメソッドには、-32601 を返します。

よくある間違い

注意

念のため、すべての capability を知らせる人は多いです。するとクライアントは、サーバーにないメソッドを呼びます。知らせるのは、書いて試したものだけにします。知らせることは、約束です。

モデル用の補助を書く

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' 'async function ask(messages, sample, max) {' '  try {' '    const res = await fetch(base + \"/v1/chat/completions\", { method: \"POST\", headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" }, body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 80 }), signal: AbortSignal.timeout(30000) });' '    if (res.ok === false) throw new Error(\"status \" + res.status);' '    const data = await res.json();' '    return { text: data.choices[0].message.content, recorded: false };' '  } catch (error) {' '    console.log(\"記録済みサンプル: モデルが答えませんでした (\" + error.message + \")。\");' '    return { text: sample, recorded: true };' '  }' '}' 'module.exports = { ask };' > model.js"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

サーバーを書く(1)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const SUPPORTED = [\"2025-03-26\", \"2024-11-05\"];' 'let state = \"new\";' 'function send(m) { process.stdout.write(JSON.stringify(Object.assign({ jsonrpc: \"2.0\" }, m)) + \"\n\"); }' 'function reply(id, result) { send({ id: id, result: result }); }' 'function fail(id, code, text, data) { send({ id: id, error: { code: code, message: text, data: data } }); }' > server2.js"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

サーバーを書く(2)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function initialise(msg) {' '  const asked = (msg.params || {}).protocolVersion;' '  if (SUPPORTED.indexOf(asked) < 0) return fail(msg.id, -32602, \"Unsupported protocol version\", { requested: asked, supported: SUPPORTED });' '  state = \"waiting\";' '  reply(msg.id, { protocolVersion: asked, capabilities: { tools: { listChanged: false } }, serverInfo: { name: \"lunch\", version: \"0.2\" } });' '}' >> server2.js"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

サーバーを書く(3)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function handle(msg) {' '  if (msg.id === undefined) {' '    if (msg.method === \"notifications/initialized\" && state === \"waiting\") state = \"ready\";' '    return;' '  }' '  if (msg.method === \"ping\") return reply(msg.id, {});' '  if (msg.method === \"initialize\") return state === \"new\" ? initialise(msg) : fail(msg.id, -32600, \"Already initialised\");' '  if (state === \"new\" || state === \"waiting\") return fail(msg.id, -32600, \"Not initialised\");' >> server2.js"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

サーバーを書く(4)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' '  if (msg.method === \"tools/list\") return reply(msg.id, { tools: [] });' '  fail(msg.id, -32601, \"Method not found: \" + msg.method);' '}' 'require(\"readline\").createInterface({ input: process.stdin }).on(\"line\", function (line) {' '  let msg;' '  try { msg = JSON.parse(line); } catch (error) { return fail(null, -32700, \"Parse error\"); }' '  handle(msg);' '});' >> server2.js"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

台本のクライアントを書く(1)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const server = require(\"child_process\").spawn(\"node\", [\"server2.js\"]);' 'const init = function (id, version) { return { id: id, method: \"initialize\", params: { protocolVersion: version, capabilities: {}, clientInfo: { name: \"demo\", version: \"0.1\" } } }; };' 'const script = [' '  { id: 1, method: \"tools/list\" },' '  init(2, \"1999-01-01\"),' '  init(3, \"2024-11-05\"),' '  { id: 4, method: \"tools/list\" },' '  { method: \"notifications/initialized\" },' '  { id: 5, method: \"tools/list\" },' '  { id: 6, method: \"resources/list\" },' '];' > client2.js"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

台本のクライアントを書く(2)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const seen = [];' 'require(\"readline\").createInterface({ input: server.stdout }).on(\"line\", function (line) {' '  console.log(\"<- \" + line);' '  seen.push(JSON.parse(line));' '  if (seen.length === 6) server.stdin.end();' '});' 'script.forEach(function (m) {' '  const full = Object.assign({ jsonrpc: \"2.0\" }, m);' '  console.log(\"-> \" + JSON.stringify(full));' '  server.stdin.write(JSON.stringify(full) + \"\n\");' '});' >> client2.js"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

台本のクライアントを書く(3)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'server.on(\"close\", async function () {' '  const caps = JSON.stringify(seen[2].result.capabilities);' '  const r = await ask([{ role: \"user\", content: \"次の capabilities を持つサーバーにできることを、一文で書いてください: \" + caps }], \"ツールの一覧を返せます。その一覧が変わることは知らせません。\", 60);' '  console.log(\"モデル: \" + r.text);' '});' >> client2.js"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

クライアントをサーバーに向けて動かす

コマンド
docker exec -w /home/node/project agent node client2.js

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

台本が送るもの

七つのメッセージが決まった順に入ります。まず、早すぎる一覧と、悪い版。次に、良い版と、二度目の早すぎる一覧。

そのあと、準備完了の通知、通る一覧、知らせていないメソッドを送ります。最後に、モデルが capabilities の意味を一文で書きます。モデルがないと「記録済みサンプル」になります。

六つの答え

要求答え
1 tools/listエラー -32600、Not initialised
2 initialize、1999-01-01エラー -32602、話せる二つの版つき
3 initialize、2024-11-05tools とサーバー名を含む result
4 tools/listエラー -32600、まだ準備前
5 tools/list空の一覧の result
6 resources/listエラー -32601、知らせていない

学生が入力するセルは、このプレビューでは空白です。

このラボとソースについて: AI コーディングエージェントのラボ:opencode でコードを説明・修正し、その結果を確かめる

授業で使いますか?このモジュールを学生に割り当てると、教員用ガイド、自動採点つきの課題、学生ごとのラボを使えます。