MCP(Model Context Protocol)
できあいの をにつないで使います。エージェントがウェブで働く3つの方法 — WebMCP・ MCP・Playwright MCP — の違いと安全のルールを見ます。
MCP とは
は、アプリを外のやにつなぐためのです。MCPができる前は、AIアプリごと、ツールごとに専用のつなぎ方を作る必要がありました。MCPなら、を一度作れば、MCPに対応したアプリ — 、、、自分で作った — のどれからでも使えます。
MCP は を開発する が作りました。公式ドキュメント: https://claude.com/docs/connectors/building/mcp
MCP クライアントと MCP サーバー
MCP では2つの側が話します。
- MCP = : ツールを選んで使う側です。Claude Code や Codex のような AI エージェントが MCP クライアントになってサーバーにつながります。
- = 自分の機能を差し出す側: 自分にできること(機能)をエージェントが使えるように MCP で差し出すプログラムです。
- : での呼び名で、エージェントが入っている AI アプリです。そのアプリの中で、つないだサーバーごとに MCP クライアントの接続が1つずつできます。
例: WebMCP
WebMCP は、ウェブサイトが自分自身を MCP のように差し出す方法です。ウェブが提供する で、ウェブサイトが自分の機能を AI エージェントが使えるツールとして差し出せるようにします。サイトの開発者が自分のサイトのに registerTool() を入れて「商品検索」「カートに入れる」のような機能を登録すると、エージェントは画面を見てボタンを1つずつ押す代わりに、そのツールを直接呼びます。
- エージェント(MCP クライアント): 「このショップで傘を探してカートに入れて」と頼まれた AI です。
- ウェブサイト(MCP サーバーの役): 自分の機能をツールとして差し出し、エージェントが呼ぶと実行して結果を返します。ウェブブラウザーは2つをつなぐ窓口です。
つまり MCP サーバーになるとは「自分をエージェントが使えるように差し出す」ことです。ほかのブラウザー MCP との違いは「MCP の活用」タブで見ます。
MCP サーバーが差し出す3つのもの
サーバーが提供できるものは3種類です。
- ツール(Tools): AIが呼び出せる関数です。たとえば「メモを検索する」「行を1つ追加する」などです。
- : アプリが読めるデータです。ファイルや記録などで、それぞれにアドレス()があります。
- (Prompts): ユーザーが選んで使える、できあいのメッセージのひな形です。
やさしく理解しよう
- こんなふうに考えてみよう
- MCPは、AIアプリのUSBのようなものです。USBができる前は、プやボードごとに専用のプラグが必要でした。今はUSBプラグがあれば、どの機器もどのコンピューターにもさせます。MCPサーバーが機器、AIアプリがコンピューターで、プラグの形が共通なので、サーバーを一度作ればすべてのMCPアプリで使えます。
- 例
- strange-add サーバーはツールを1つ提供します。2つの数を足し、さらに 1〜100 の乱数を足します。
- 1つで Claude Code につなぎます。
- 「strange-add で 2 と 3 を足して」と言うと、AIがツールの説明を読み、あなたに承認を求め、クライアントがサーバーの strange-add を呼びます。答えは 5 ではありません — ツールが本当に動いた証拠です。
- よくある間違い
- 見つけた MCP サーバーを何でもインストールすること。サーバーはあなたのコンピューターで、あなたのでコードを動かします。中身を読むか信頼できるものを選び、必要なフォルダーやキーだけをわたしましょう。
エージェントに MCP サーバーをつなぐ
は、につないではじめて使えます。つなぐ(・codex add)と範囲・確かめ方はごとに違うので、それぞれのツールの画面の MCP の単元に1回ずつ書いてあります。
WebMCP vs Chrome DevTools MCP vs Playwright MCP
3つとも、エージェントがウェブで働けるようにします。違うのは、だれが準備するかと、エージェントがどう働くかです。WebMCP はウェブサイトの側が「私のサイトではこんな機能が使えます」と宣言するブラウザーの で、残りの2つはエージェントの側にインストールして、どのサイトでもブラウザーを直接操作する MCP サーバーです。
| 項目 | WebMCP | Chrome DevTools MCP | Playwright MCP |
|---|---|---|---|
| 何か | ウェブサイトが使うブラウザーの API — まだ提案段階のウェブ標準(Web コミュニティグループの草案) | エージェントの側にインストールする MCP サーバー(Google チーム) | エージェントの側にインストールする MCP サーバー(Microsoft) |
| だれが準備するか | ウェブサイトの開発者 — 自分のサイトのに registerTool() を入れてツールを登録します | エージェントを使う人 — 自分のエージェントにを加えます | エージェントを使う人 — 自分のエージェントにサーバーを加えます |
| エージェントの働き方 | サイトが登録したツールを名前で呼びます。画面を読んで推測する必要がありません。 | 本物の Chrome を で操作してのぞき込みます — クリック・入力・。 | ページのアクセシビリティツリー(画面の構造)を読んでクリック・入力します — 絵ではなく構造を見ます。 |
| 得意なこと | そのサイトの機能を正確に速く、ログインした自分ののまま使います | — パフォーマンスの記録、のリクエスト、 | ・テスト — ・・WebKit の複数のブラウザー |
| 限界 | サイトがツールを登録した所でしか使えません。新しい標準なので、対応するブラウザー・サイトはまだ少ないです。 | Chrome(と Chrome for )向けに作られています。画面が変わると、探し直す必要があります。 | 画面の構造が変わると、探し直す必要があります。サイトが決めた機能ではなく、ボタン1つ1つで働きます。 |
はじめのコード
// WebMCP: the website registers its own feature as a tool
// (first explainer / Chrome preview: navigator.modelContext,
// spec draft 2026-10-27: document.modelContext)
const modelContext = document.modelContext ?? navigator.modelContext;
modelContext.registerTool({
name: "search-products",
description: "Search this shop's products by keyword",
inputSchema: {
type: "object",
properties: { query: { type: "string" } },
required: ["query"],
},
async execute({ query }) {
const response = await fetch("/api/products?q=" + encodeURIComponent(query));
return await response.json(); // the agent gets this result
},
});
# Chrome DevTools MCP: add the server to your agent (Claude Code)
claude mcp add chrome-devtools -- npx -y chrome-devtools-mcp@latest
# Playwright MCP: add the server to your agent (Claude Code)
claude mcp add playwright -- npx @playwright/mcp@latest
WebMCP はサイトの に、残りの2つは自分のコンピューターのに書きます。WebMCP の最初の説明と Chrome のプレビューは navigator.modelContext を、2026-10-27 の草案は document.modelContext を使います — 仕様はまだ変わっている途中です。
たとえで見る — メニューを掲げた店とおつかいロボット
WebMCP = 店が掲げた注文窓口とメニュー
- 店(ウェブサイト)が入り口に「ご注文はこの窓口へ」というメニューを前もって掲げます。
- メニューには注文できるもの(ツールの名前)と注文書の書き方(入力)が書いてあります。
- ロボット(エージェント)はメニューを読んで、窓口に注文書を出すだけです — 店の中を歩き回りません。
- ただし、窓口を作ってある店でしか、このように注文できません。
Chrome DevTools MCP・Playwright MCP = お客さんが送ったおつかいロボット
- お客さん(エージェントを使う人)がおつかいロボットを店に送ります。店は何も準備しなくてかまいません。
- ロボットは店の中を自分で見て回り、棚を読み、品物を手に取り、レジのボタンを押します。
- どの店にも入れますが、陳列が変わると探し直すので、遅くなったり迷ったりすることがあります。
- Chrome DevTools MCP のロボットは、店の配線やメーターまで見る点検の技術者です(パフォーマンス・ネットワーク・コンソール)。
- Playwright MCP のロボットは、決められた買い物の順番を複数の店(ブラウザー)でまったく同じにくり返す買い物ロボットです。
まとめると、窓口のある店では窓口で注文するのが速くて正確です。窓口のない店では、ロボットが自分で見て回るしかありません。だから3つはおたがいの代わりにはならず、いっしょに使います。
MCP の安全のルール
安全: ツールはあなたので動くコードです。また、信頼できるサーバーでなければ、ツールの説明も信用できません。仕様は、ホストがツールを呼ぶ前にユーザーの同意を得るよう求めています。自分で読んだか信頼できるサーバーだけをインストールし、サーバーには必要最小限のアクセスだけをわたし、はコードではなく環境変数に置き、に気をつけましょう — ツールが読むWebページやファイルに、をねらった指示が入っていることがあります。
メッセージとトランスポート
メッセージは 2.0 です。(「何ができるの?」)や (「この引数でこれをして」)のような小さなです。メッセージは2つの標準のトランスのどちらかで行き来します。stdio(が同じコンピューターでをとして起動し、標準入力と標準出力で話す)か、(サーバーがWebアドレスで動くので、たくさんのユーザーがアクセスできる)です。
手で作ったサーバーとクライアント
サーバー(mcp-server.py)は のだけを使うので、そのものが見えます。標準入力から JSON-RPC のメッセージを1行に1つずつ読み、標準出力に返事を1行に1つずつ書きます。
- : クライアントがを伝えてあいさつします。サーバーは同じで答え、を持っていることを伝え、自分の名前を名乗ります。
- tools/list: サーバーはただ1つのツール strange-add を、説明と入力(a と b は数)と一緒に返します。
- tools/call: サーバーは strange-add を実行し、結果をテキストで返します。
- id のないメッセージは通知(notifications/initialized など)なので、サーバーは返事をしません。それ以外のリクエスト(ping など)には空の結果を返します。
strange-add は a + b に 1〜100 の乱数を足します。はこの値を自分では計算できないので、2 + 3 の答えが 5 でなければ、ツールが本当に動いた証拠になります。stdout にはプロトコルのメッセージだけを流します — よけいな () が1つあるだけで会話がこわれるので、 stderr に書きましょう。print(..., =True) は返事を1つずつすぐに送り出します。
クライアント(mcp-client.sh)は、を使わずにホストの役をするスクリプトです。サーバーを起動し、全体(initialize、initialized の通知、tools/list、tools/call、ping)を送って、返事を1つずつ確かめ、PASS か FAIL を表示します。終了 0 は、すべて通ったという意味です。につなぐ前に、このようにサーバーを試しましょう。本物ののクライアントでは、モデルが次のくり返しに加わります。
- ユーザーの質問とツールの一覧をモデルに送ります。
- モデルがツールを使いたいと言ったら、クライアントが tools/call でサーバーのそのツールを呼びます。
- 結果をモデルに返すと、モデルが最終的な答えを書きます。
この手作りのサーバーは initialize で始まる方式(プロトコルのバージョン 2025-06-18)で話します。 などのクライアントは、この方式を今も受け付けます。最新の(2026-07-28)では、initialize のの代わりに、すべてのリクエストにバージョンをのせます。実際のサービスには、仕様に合わせてくれる公式の (Python の MCPServer、)を使いましょう。
"""
mcp-server.py: the smallest MCP server.
One tool, plain Python (standard library only), no SDK.
Who is who
MCP server This program. It offers tools (functions the AI
may use).
Host The app the user talks to (Claude Code, Codex).
It runs one MCP client for each connected server.
MCP client The part of the host that talks to this server
with JSON-RPC messages (here over stdio: one JSON
message per line).
Model The LLM (Claude Opus, GPT, ...). It never talks to
the server directly. It only talks to the host.
Flow
1. Discover tools
The client asks for the tools (tools/list). The server replies
with each tool's name, description and input schema.
2. Offer tools
The host sends the user's message to the model, together
with the tool list.
3. Model decides
The model answers directly, or asks for a tool call
(tool name + arguments). It never runs the tool itself.
4. Call the tool
The client sends the request to the server (tools/call).
5. Run the tool
The server runs the function and returns the result.
6. Model continues
The host gives the result back to the model. The model may
call more tools (3-6 again) or write the final answer.
Model Host (MCP client) MCP server
| | 1. tools/list ------> |
| | <------ tool list |
| <-- 2. message + tool list |
| 3. tool call --> | |
| | 4. tools/call ------> |
| | <------ 5. result |
| <-- 6. result | |
Rule for stdio servers: stdout carries protocol messages only.
Never print() anything else there. Write logs to stderr.
"""
import json
import random
import sys
SERVER_INFO = {"name": "strange-add-mcp", "version": "1.0.0"}
# The tool. The model reads the description to decide when to use it.
STRANGE_ADD = {
"name": "strange-add",
"description": "Add two numbers with a strange number.",
"inputSchema": {
"type": "object",
"properties": {
"a": {"type": "number"},
"b": {"type": "number"},
},
"required": ["a", "b"],
},
}
def strange_add(a, b):
"""Add a + b and a random number from 1 to 100 (always > a + b).
The model cannot guess the answer, so a right answer proves
that the tool was really called.
"""
return a + b + random.randint(1, 100)
def handle(method, params):
"""Return the result for one request. Others (like ping) get {}."""
if method == "initialize":
# Use the client's protocol version and say "I have tools".
return {
"protocolVersion": params["protocolVersion"],
"capabilities": {"tools": {}},
"serverInfo": SERVER_INFO,
}
if method == "tools/list":
return {"tools": [STRANGE_ADD]}
if method == "tools/call":
args = params["arguments"]
total = strange_add(args["a"], args["b"])
return {"content": [{"type": "text", "text": str(total)}]}
return {}
def main():
# One JSON-RPC message per line comes in, one reply per line goes out.
for line in sys.stdin:
request = json.loads(line)
# No id means a notification: send no reply.
if "id" not in request:
continue
result = handle(request["method"], request.get("params", {}))
reply = {"jsonrpc": "2.0", "id": request["id"], "result": result}
# flush=True sends the reply at once.
print(json.dumps(reply), flush=True)
if __name__ == "__main__":
main()
#!/bin/sh
# mcp-client.sh: the simplest MCP client, a shell script.
# It starts mcp-server.py, talks to it over stdio (one JSON-RPC
# message per line) and checks every reply.
# Exit code 0 means every check passed.
SERVER="python3 $(dirname "$0")/mcp-server.py"
# 1. Send the whole session at once and keep the replies.
# id 1 initialize agree on a version
# (no id) notifications/initialized "ready" (no reply)
# id 2 tools/list which tools are there?
# id 3 tools/call run strange-add(2, 3)
# id 4 ping is the server alive?
replies=$(
$SERVER <<'EOF'
{"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "shell-mcp-client", "version": "1.0.0"}}}
{"jsonrpc": "2.0", "method": "notifications/initialized"}
{"jsonrpc": "2.0", "id": 2, "method": "tools/list"}
{"jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": {"name": "strange-add", "arguments": {"a": 2, "b": 3}}}
{"jsonrpc": "2.0", "id": 4, "method": "ping"}
EOF
)
echo "--- Server replies ---"
echo "$replies"
echo "--- Checks ---"
failed=0
# 2. check <label> <reply id> <text the reply must contain>
check() {
reply=$(echo "$replies" | grep -F "\"id\": $2,")
if echo "$reply" | grep -qF "$3"; then
echo "PASS $1"
else
echo "FAIL $1 (expected: $3)"
failed=1
fi
}
check "initialize: protocol version" 1 '"protocolVersion": "2025-06-18"'
check "initialize: server info" 1 '"serverInfo": {'
check "initialize: has tools" 1 '"capabilities": {"tools": {}}'
check "tools/list: strange-add" 2 '"name": "strange-add"'
check "ping: empty result" 4 '"result": {}'
# 3. strange-add adds a random 0 to 100,
# so the answer only has to be >= 2 + 3.
total=$(echo "$replies" | grep -F '"id": 3,' |
sed 's/.*"text": "\([0-9]*\)".*/\1/')
if [ "$total" -ge 5 ] 2>/dev/null; then
echo "PASS tools/call: strange-add(2, 3) >= 5 (got $total)"
else
echo "FAIL tools/call: strange-add(2, 3) >= 5 (got $total)"
failed=1
fi
exit $failed
# put the two files above into one folder
mkdir -p ~/tmp/mcp && cd ~/tmp/mcp
# mcp-server.py mcp-client.sh
# 1) test the server without any AI
sh mcp-client.sh # -> 6 x PASS, exit code 0
# 2) connect it to Claude Code (no --scope: the "local" scope)
claude mcp add strange-add -- python3 ~/tmp/mcp/mcp-server.py
claude mcp list # strange-add: ... - Connected
claude mcp get strange-add # Scope: Local config
# 3) where it was saved: NOT in this folder (no new file here)
# ~/.claude.json -> projects -> "<this folder>" -> mcpServers
jq '.projects["'"$PWD"'"].mcpServers' ~/.claude.json
# 4) use it: start Claude Code in THIS folder
claude # "Use strange-add to add 2 and 3"
# -> not 5 (e.g. 61): the tool ran
claude mcp remove strange-add -s local
これらはこのページではなく、自分のパソコンやサーバーので実行します。実行する前に1行ずつ読みましょう。