最近、Claude や ChatGPT、VS Code などの AI ツールの設定画面で「MCP サーバ」という言葉をよく見かけるようになりました。社内のシステムやファイルを AI から使えるようにする仕組みだと説明されますが、REST API がすでにあるのに、なぜ別の仕組みが必要なのかはピンとこないところです。
この記事では、社内の勤怠システムに「明日、有給を申請しておいて」と AI に頼む場面を例に、MCP(Model Context Protocol)がない場合とある場合を比べながら、MCP サーバが何をしているのかを図で説明します。REST API と JSON がなんとなくわかる方を想定しています。
- AI(LLM)は API を自分で呼ばず、「このツールを呼んで」と返すだけであること
- MCP がないと、AI アプリとシステムの組み合わせの数だけ連携が必要になること
- MCP サーバは既存の API の前に置く「AI 向けの窓口」で、作る量は思ったより少ないこと
- MCP サーバを使うときに、セキュリティで押さえておきたいこと
AI は自分で API を呼んでいない
まず、AI が外部のシステムを使うとき、裏で何が起きているかを確認します。ChatGPT や Claude の中心にある LLM(大規模言語モデル)は、文章を受け取って文章を返すプログラムです。LLM が勤怠システムに HTTP リクエストを送ることはありません。
LLM にできるのは、「この依頼なら、apply_leave というツールを date=2026-10-10 で呼べばよい」と返すところまでです。実際に API を呼ぶのは、LLM を使っている AI アプリ(チャット画面やエディタの拡張機能など)です。この仕組みは Function Calling や Tool Use と呼ばれています。
LLM が正しいツールを選べるように、AI アプリは依頼と一緒に「ツールの説明書」を LLM に渡します。説明書には、ツールの名前、何をするツールなのかの説明、引数の形(JSON Schema)を書きます。
つまり、AI から勤怠システムを使えるようにするには、次の2つを誰かが用意する必要があります。
| 用意するもの | 中身 | 勤怠システムの例 |
|---|---|---|
| ツールの説明書 | LLM に見せる名前、説明、引数の形 | apply_leave:有給を申請する。引数は date(YYYY-MM-DD)と reason(任意) |
| 呼び出す処理 | LLM の返事を受けて、実際に API を呼ぶコード | POST /api/v1/leave にトークンを付けて送り、結果を LLM に返す |
MCP がない世界:連携が掛け算で増える
MCP がない場合、この「説明書」と「呼び出す処理」は、AI アプリの中に書きます。社内向けのチャットボットを作るなら、チャットボットのコードに勤怠 API 用の説明書と呼び出し処理を書く、という形です。
1つのアプリだけなら、これで困りません。困るのは AI アプリが増えたときです。開発チームは VS Code の AI 機能から、営業は Claude のデスクトップアプリから、同じ勤怠システムを使いたいとします。説明書の書き方も、呼び出し処理を組み込む方法も製品ごとに違うので、アプリごとに同じような連携を作り直すことになります。
システムの側も勤怠だけでは終わりません。経費精算、ファイル、チケット管理と増えていくと、必要な連携は「AI アプリの数 × システムの数」になります。勤怠 API の項目名が1つ変わっただけで、すべての AI アプリの連携を直すことになります。
MCP は、AI アプリと外部のシステムをつなぐ方法を1つに決めた共通の取り決め(プロトコル)です。AI アプリは MCP の話し方を1回覚えればよく、システムの側は MCP サーバを1つ用意すれば、MCP に対応したどの AI アプリからも使えます。必要な連携は「AI アプリの数 + システムの数」になります。
公式ドキュメントでは、MCP を AI アプリ用の USB-C ポートにたとえています。USB-C ならパソコンとどのメーカーの周辺機器でもつながるように、MCP に対応した AI アプリとシステムは、組み合わせを気にせずにつなげられます。
MCP は 2024 年 11 月に Anthropic が公開したオープンな仕様です。2025 年 12 月には、Linux Foundation の下に作られた Agentic AI Foundation(AAIF)に寄贈されました。AAIF は Anthropic、Block、OpenAI が共同で設立し、Google、Microsoft、AWS なども支援しています。Claude、ChatGPT、VS Code、Cursor など、多くの AI アプリが MCP に対応しています。
MCP でつなぐときの登場人物
MCP では、つなぐ側とつながれる側を次のように呼びます。
| 登場人物 | 役割 | 例 |
|---|---|---|
| ホスト | ユーザーが使う AI アプリ。画面や LLM とのやり取りを受け持つ | Claude Desktop、VS Code |
| MCP クライアント | ホストの中の部品。MCP サーバ1つにつき1つ作られ、そのサーバとやり取りする | ホストが自動で作る |
| MCP サーバ | ツールなどを MCP の形で公開するプログラム。中で既存のシステムを呼ぶ | 勤怠 MCP サーバ、ファイル MCP サーバ |
| 既存のシステム | 今まで通りの API やデータ。MCP のために作り直す必要はない | 勤怠システムの REST API |
「サーバ」という名前ですが、専用の機械を用意するとは限りません。ユーザーの PC の中で、AI アプリが起動する小さなプログラムとして動く MCP サーバも多くあります(後で説明します)。
使う側は設定を書くだけ
MCP サーバがあれば、AI アプリの側でコードを書く必要はありません。Claude Desktop の場合は、設定ファイル(claude_desktop_config.json)の mcpServers に、MCP サーバの起動方法を書くだけです。
claude_desktop_config.json(Claude Desktop の例)
{
"mcpServers": {
"kintai": {
"command": "node",
"args": ["C:\\mcp\\kintai\\build\\index.js"],
"env": {
"KINTAI_TOKEN": "(勤怠 API のトークン)"
}
}
}
}
Claude Desktop は起動時にこの MCP サーバを起動し、使えるツールを問い合わせます。設定ファイルの名前や書き方はアプリによって少しずつ違いますが、書く内容は「どの MCP サーバを、どう起動するか(リモートの場合はどの URL にあるか)」です。
勤怠 API の URL も、項目名も、リクエストの組み立て方も、AI アプリの側には出てきません。それらはすべて MCP サーバの中にあります。
中で流れているメッセージ
AI アプリ(MCP クライアント)と MCP サーバの間では、JSON-RPC 2.0 という形式の JSON メッセージがやり取りされます。method に「何をしてほしいか」を書き、id で要求と応答を対応付ける、シンプルな形式です。有給の申請を頼んだときの流れは次のとおりです。
まず、AI アプリは MCP サーバに tools/list を送り、使えるツールの一覧をもらいます。
AI アプリ → MCP サーバ(tools/list)
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}
MCP サーバ → AI アプリ
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{
"name": "apply_leave",
"description": "社内勤怠システムに有給休暇を申請する。date は YYYY-MM-DD 形式。",
"inputSchema": {
"type": "object",
"properties": {
"date": { "type": "string", "description": "休む日(YYYY-MM-DD)" },
"reason": { "type": "string", "description": "理由(任意)" }
},
"required": ["date"]
}
}
]
}
}
応答の中身は、さきほどの「ツールの説明書」そのものです。AI アプリはこれを LLM に渡します。MCP がない場合は人が AI アプリに書き込んでいた説明書を、MCP では MCP サーバ自身が答えてくれる、ということです。
LLM が「apply_leave を呼ぶ」と返したら、AI アプリは tools/call を送ります。
AI アプリ → MCP サーバ(tools/call)
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "apply_leave",
"arguments": { "date": "2026-10-10", "reason": "私用" }
}
}
MCP サーバ → AI アプリ
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"content": [
{ "type": "text", "text": "申請を受け付けました。申請ID: 98765" }
]
}
}
MCP サーバはこの要求を受けて勤怠 API を呼び、結果をテキストで返します。AI アプリはその結果を LLM に渡し、LLM がユーザーへの返事を作ります。
ここに載せたメッセージは、説明に必要な項目だけを残した例です。実際のメッセージには、プロトコルのバージョンなどの項目も付きます。どの MCP サーバでも、使うメソッドは同じ tools/list と tools/call で、変わるのはツールの名前と引数だけです。
REST API との関係
ここまでで、「REST API があるのに、なぜ MCP が必要なのか」の答えが見えてきます。REST API と MCP は競合するものではなく、使う相手が違います。
| 比べるところ | REST API | MCP |
|---|---|---|
| 主な使い手 | 人が書いたプログラム | AI アプリ(何を呼ぶかは LLM が決める) |
| 使い方の知り方 | 開発者が API 仕様書を読んでコードを書く | tools/list で、説明書をその場で受け取る |
| 呼び出し方 | API ごとに URL、メソッド、項目名が違う | どのサーバでも tools/call にツール名と引数を入れる |
| 何を呼ぶかが決まるとき | 開発者がコードを書いたとき | 会話の中で、依頼の内容に応じて |
REST API は、仕様書を読んだ開発者が「この画面のこのボタンでこの API を呼ぶ」と決めて使うものです。一方、AI アプリでは、どのツールを呼ぶかを会話の中で LLM が決めます。そのためには、ツールの一覧と説明書をその場で受け取れて、どのツールでも同じ形で呼べる必要があります。MCP はその部分を決めた取り決めです。
MCP サーバの中では、今まで通り REST API を呼びます。MCP は REST API を置き換えるものではなく、既存の API の前に「AI 向けの窓口」を足すもの、と考えるとわかりやすいと思います。
MCP サーバを作るのは大変?
「AI アプリの側が楽になるのはわかったが、その手間を MCP サーバに移しただけでは?」と思うかもしれません。確かに、勤怠 API を呼ぶ処理は MCP サーバに書きます。ただし書くのは1回だけで、その1回で MCP に対応したすべての AI アプリから使えるようになります。
もう1つ、JSON-RPC のメッセージの組み立てや tools/list への応答といった MCP の取り決めの部分は、公式の SDK(TypeScript、Python、Java、Go など)が受け持ちます。開発者が書くのは、ほぼ「説明書」と「呼び出す処理」だけです。
次は、TypeScript の公式 SDK(@modelcontextprotocol/server パッケージ)で、勤怠 API 用の MCP サーバを書いた例です。
src/index.ts(TypeScript SDK の例)
import { McpServer } from "@modelcontextprotocol/server";
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
import { z } from "zod";
// ① サーバの名前とバージョン
const server = new McpServer({ name: "kintai", version: "1.0.0" });
// ② ツールの登録(ここが tools/list で返る説明書になる)
server.registerTool(
"apply_leave",
{
description: "社内勤怠システムに有給休暇を申請する。date は YYYY-MM-DD 形式。",
inputSchema: z.object({
date: z.string().describe("休む日(YYYY-MM-DD)"),
reason: z.string().optional().describe("理由(任意)"),
}),
},
// ③ tools/call を受けたときの処理(既存の REST API を呼ぶ)
async ({ date, reason }) => {
const res = await fetch("https://kintai.example.local/api/v1/leave", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.KINTAI_TOKEN}`,
},
body: JSON.stringify({ target_date: date, note: reason ?? "私用" }),
});
if (!res.ok) {
return {
content: [{ type: "text", text: `申請できませんでした(HTTP ${res.status})` }],
isError: true,
};
}
const data = (await res.json()) as { request_id: string };
return {
content: [{ type: "text", text: `申請を受け付けました。申請ID: ${data.request_id}` }],
};
},
);
// ④ 標準入出力(stdio)でメッセージを受け付ける
await server.connect(new StdioServerTransport());
| 部分 | 書くこと |
|---|---|
| ① サーバの名前 | MCP サーバの名前とバージョン |
| ② ツールの登録 | ツール名、description(何をするツールか)、inputSchema(引数の形)。この内容がそのまま tools/list の応答になる |
| ③ 呼び出す処理 | 勤怠 API に POST し、結果を content に入れて返す。失敗したときは isError: true を付けて理由を返すと、LLM が引数を直して呼び直せる |
| ④ 起動 | 標準入出力でメッセージを受け付ける。JSON-RPC の読み書きは SDK が行う |
tools/list の応答にあった name、description、inputSchema が、コードの ② にそのまま対応しているのがわかると思います。tools/call を受けたときにどの処理を動かすかの振り分けや、引数が inputSchema に合っているかのチェックも、SDK がこの登録内容をもとに行います。
description や引数の説明は、LLM がツールを選ぶときと、引数を作るときの手がかりになります。「いつ使うツールか」「日付はどの形式か」を具体的に書いておくと、LLM が正しい引数で呼びやすくなります。
標準入出力でやり取りする MCP サーバでは、標準出力(stdout)がメッセージの通り道です。console.log() などで標準出力に書くとメッセージが壊れるので、ログは console.error() などで標準エラー出力(stderr)に出します。
ツールのほかに公開できるもの
MCP サーバが公開できるものは、ツールのほかに2つあります。勤怠システムに当てはめると、次のようになります。
| 種類 | 中身 | 使うことを決めるのは | 勤怠システムでの例 |
|---|---|---|---|
| Tools(ツール) | 呼ぶと何かを実行する関数 | LLM | 有給を申請する、残日数を調べる |
| Resources(リソース) | 参考資料として読むデータ。URI で指定する | AI アプリ(ユーザーが選ぶことも多い) | 就業規則の文書、今月の勤務表 |
| Prompts(プロンプト) | 決まった頼み方のひな形 | ユーザー | 「/休暇申請」を選ぶと、日付と理由を聞いてから申請する |
最初はツールだけ覚えておけば十分です。ツールは「LLM が自分の判断で使う」、リソースは「AI アプリが参考資料として LLM に渡す」、プロンプトは「ユーザーが選んで使う定型の頼み方」と、使うことを決める主体が違う点だけ押さえておくと、仕様を読むときに迷いません。
ローカルの MCP サーバとリモートの MCP サーバ
MCP のメッセージ(JSON-RPC)を運ぶ方法は2つあります。
| 項目 | stdio | Streamable HTTP |
|---|---|---|
| 動く場所 | ユーザーの PC の中 | サーバやクラウドの上 |
| つながり方 | AI アプリが MCP サーバを子プロセスとして起動し、標準入出力でやり取りする | HTTP の POST でメッセージを送る。応答を少しずつ返すときは SSE(Server-Sent Events)を使う |
| 認証 | ネットワークを通らないので、MCP の認可の仕組みは使わない。外部 API のトークンは環境変数などで渡す | HTTP の認証を使う。トークンの取得には OAuth が推奨されている |
| 向いている使い方 | PC のファイル操作、自分だけで使うツール | 社内システムや SaaS を、多くの利用者に提供する |
どちらの方法でも、中を流れる JSON-RPC のメッセージ(tools/list や tools/call)は同じです。ネットワークでたとえると、上位のプロトコルはそのままで、下の伝送路だけを選べるようになっている形です。
セキュリティで押さえておきたいこと
MCP サーバをつなぐということは、LLM の判断で外部のシステムを操作できるようにする、ということです。便利になる分、何を許すかを先に考えておく必要があります。MCP の仕様でも、ツールの実行は人が確認して拒否できるようにすべき(SHOULD)とされています。
| 観点 | 考え方 |
|---|---|
| 人の確認 | 申請、送信、削除など書き込みを伴うツールは、実行前にユーザーが内容を見て許可する。多くの AI アプリは、ツールを実行する前に確認を求める仕組みを持っている |
| 最小権限 | MCP サーバに渡すトークンは、そのツールに必要な権限だけにする。参照しかしないなら読み取り専用のトークンにする |
| サーバ側のチェック | MCP サーバは、入力の検証、アクセス制御、呼び出し回数の制限を行う(仕様で MUST)。LLM が作った引数をそのまま信用しない |
| MCP サーバの出所 | ローカルの MCP サーバは、ユーザーの権限で PC 上で動くプログラム。出所のわからないものは入れない |
| 結果に紛れ込む指示 | ツールが取ってきたメールや Web ページに「〇〇へ送信せよ」のような文章があると、LLM がそれを指示と取り違えることがある(プロンプトインジェクション)。読み取り用のツールと送信用のツールを同時に使わせるときは特に注意する |
| 認証情報の置き場所 | 設定ファイルの env に書いたトークンは、そのまま平文で残る。ファイルのアクセス権を絞り、設定ファイルを人に渡さない |
ファイアウォールのポリシーを「何を許可するか」から考えるのと同じで、MCP サーバも「AI に何を許可するか」を決めてからつなぐ、という順番で考えると整理しやすいと思います。
まとめ
| 項目 | ポイント |
|---|---|
| AI と API | LLM は「このツールをこの引数で呼んで」と返すだけ。API を呼ぶのは AI アプリ |
| MCP がない場合 | AI アプリごとに説明書と呼び出す処理を作るので、連携は「アプリの数 × システムの数」 |
| MCP がある場合 | システムごとに MCP サーバを1つ作れば、MCP に対応したどの AI アプリからも使える。連携は「アプリの数 + システムの数」 |
| MCP サーバ | 既存の API の前に置く AI 向けの窓口。tools/list で説明書を返し、tools/call で API を呼ぶ |
| REST API との関係 | 置き換えではない。MCP サーバの中で今まで通り REST API を呼ぶ |
| 作る手間 | 説明書と呼び出す処理を書けば、JSON-RPC の部分は SDK が受け持つ |
| つなぎ方 | PC の中なら stdio、ネットワーク越しなら Streamable HTTP。メッセージは同じ |
| セキュリティ | 書き込みは人が確認、トークンは最小権限、信頼できる MCP サーバだけを使う |
- 公式の確認ツール MCP Inspector で MCP サーバにつなぎ、
tools/listで返ってくる説明書を自分の目で見る - 公式のリファレンス実装(ファイル操作の MCP サーバなど)を AI アプリにつなぎ、どんな依頼のときにツールが呼ばれるかを見る
- 普段使っている API を1つ選び、ツールが1つだけの MCP サーバを作ってみる
コメント