MCPの一覧取得と呼び出し結果を、ローカル実例で見分ける
MCPサーバーから取得したツール一覧に名前が出ていても、そのツールの処理が成功したかは呼び出しの結果で確かめます。今回は固定データだけを読むサーバーを用意し、一覧取得・正常な呼び出し・意図したツールエラーを実際に比べました。
この記事は、MCPの基本を知り、結果のどこを読めばよいか迷う開発初心者向けです。モデルや外部APIは使いません。各回のInspectorプロセスが返したJSONと終了コードを読み、接続前の失敗とツール処理の失敗も区別します。
一覧取得と呼び出しは、別の結果を見る
Toolsの仕様は、一覧を得るtools/listと、処理を呼ぶtools/callを分けています。一覧に名前や入力スキーマが出ることは、その引数で処理を実行し終えた証拠ではありません。
InspectorのCLI資料では、--methodで要求を指定します。ツールの応答がisError: trueの場合、CLIは結果を出したうえで終了コード5を返すと説明されています。以下では、この組み合わせを自作サーバーで確認します。
固定データのサーバーで3つの結果を比べる
確認環境はWindows、Node.js 24.15.0、Inspector 2.12.0、Server SDKとCore 2.3.1、zod 4.4.3です。次のコマンドは、同じ直接依存の版を指定する例です。今回の検証では、その版をpackage.jsonに固定してインストールしました。パッケージ取得にはネットワークを使いますが、後のサーバー処理は固定データだけを扱います。
npm install --ignore-scripts --save-exact @modelcontextprotocol/[email protected] @modelcontextprotocol/[email protected] @modelcontextprotocol/[email protected] [email protected]
次をfixture.mjsとして保存します。登録するツールはlookup_termだけです。MCPを渡すと固定の説明を返し、それ以外ならisError: trueを返します。ファイルの読み書きや外部APIの呼び出しは、このツールにはありません。
import { McpServer } from '@modelcontextprotocol/server';
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
import { z } from 'zod';
const server = new McpServer({ name: 'raclie-local-terms', version: '1.0.0' });
server.registerTool('lookup_term', {
description: 'Read a definition from a fixed in-memory dictionary.',
inputSchema: z.object({ term: z.string() })
}, async ({ term }) => {
if (term !== 'MCP') return {
content: [{ type: 'text', text: 'Term not found in this fixture.' }],
isError: true
};
return {
content: [{ type: 'text', text: 'MCP is Model Context Protocol.' }],
isError: false
};
});
await server.connect(new StdioServerTransport());
続いて次をrun-example.mjsへ保存します。試験用の設定は新しい一時ディレクトリに置き、このプロセスだけで使います。既存のサーバー一覧や認証設定を編集する手順ではありません。秘密情報の保管に関する公式説明に従い、試験ではメモリー保管を指定しました。
import { mkdtempSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join, resolve } from 'node:path';
import { spawnSync } from 'node:child_process';
const profile = mkdtempSync(join(tmpdir(), 'mcp-local-example-'));
writeFileSync(join(profile, 'client.json'), '{}\n');
const env = { ...process.env,
MCP_STORAGE_DIR: profile,
MCP_CLIENT_CONFIG_PATH: join(profile, 'client.json'),
MCP_CATALOG_PATH: join(profile, 'catalog.json'),
MCP_INSPECTOR_OAUTH_STATE_PATH: join(profile, 'oauth.json'),
MCP_INSPECTOR_SECRET_STORE: 'memory',
MCP_INSPECTOR_PERSIST_TOKENS: 'none'
};
const cli = resolve('node_modules/@modelcontextprotocol/inspector/clients/launcher/build/index.js');
const common = [cli, '--cli', process.execPath, resolve('fixture.mjs'),
'--protocol-era', 'auto', '--stored-auth-only', '--format', 'json'];
for (const args of [
['--method', 'tools/list'],
['--method', 'tools/call', '--tool-name', 'lookup_term', '--tool-arg', 'term=MCP'],
['--method', 'tools/call', '--tool-name', 'lookup_term', '--tool-arg', 'term=UNKNOWN']
]) {
const result = spawnSync(process.execPath, [...common, ...args], { env, encoding: 'utf8' });
console.log(JSON.stringify({ request: args, exitCode: result.status,
stdout: result.stdout, stderr: result.stderr, spawnError: result.error?.message }));
}
同じフォルダーでnode run-example.mjsを実行します。stdioで起動したサーバーへ、一覧取得、term=MCP、term=UNKNOWNの順に要求します。今回得られた結果は次のとおりです。
| 要求 | 結果の要点 | Inspectorの終了コード |
|---|---|---|
| tools/list | lookup_termの名前と入力スキーマを取得 | 0 |
| tools/call・term=MCP | 固定の説明文、isError: false | 0 |
| tools/call・term=UNKNOWN | 未登録の説明文、isError: true | 5 |
JSONの結果と終了コードを一緒に読む
上のコードが表示するexitCodeは、子プロセスとして起動したInspectorの終了コードです。stdoutには、そのInspectorが出したJSONが文字列として入っています。外側の実行用コードは3件を続けて処理するので、端末で最後に見える実行用コード自身の終了コードとは区別してください。
正常な呼び出しでは、stdout内のresult.isErrorがfalseで、説明文も返りました。未登録の用語では、説明文があってもresult.isErrorはtrue、exitCodeは5です。この例では、結果の文字列があるだけで成功と判断すると、意図したツールエラーを見落とします。
未登録時のエラーは、こちらがツール内で意図して返したものです。認証エラーや通信切断を発生させた試験ではありません。また、stderrには成功した回にもメモリー保管の案内が出ました。stderrが空かどうかだけを成功条件にはしませんでした。
確認した環境と、適用できる範囲
最初はプロトコル版の選択をmodernに固定しましたが、このサーバー設定は2026-07-28を提示せず、版の交渉で失敗しました。そこでautoへ変更すると、接続確認用のinitializeは2025-11-25を返しました。上の3件は、この自動交渉で実行した結果です。
版の交渉に失敗した最初の回は、ツール処理のエラーを検証できた回には数えません。接続段階と処理段階を分けることで、同じ「失敗」でも確認する場所が変わります。SDK全体の対応版を断定する結果にも広げません。
確認日は2026年10月28日です。Inspectorの固定版資料、CLI資料と設定資料を参照し、実際に入った版とpackage-lockも記録しました。他のOS、HTTP接続、OAuth、実際のモデルによるツール選択、本番サーバーは検証していません。
CLIの用途や画面との使い分けは、MCP Inspectorの紹介で説明しています。今回はそこから進み、一覧の取得、呼び出し結果、Inspectorの終了コードを別々に読む例を確認しました。