はじめの一歩
このページは機械翻訳です
このドキュメントの翻訳版は英語版のページから自動的に生成されており、正式な内容はこのページの英語版です。
翻訳におかしな点を見つけましたか?翻訳の仕組みと問題の報告方法を参照してください。
ランディングページ はテンポよく進みます。サーバーを書いて、実行して、ツールを呼ぶ。
このページではもっとゆっくり、サーバーが公開できる 3 種類すべてを扱いながら、途中で出てくるものにひとつずつ名前をつけていきます。
ホスト、クライアント、サーバー
ここから先のすべてのページに登場する 3 つの言葉です。
- ホスト は LLM アプリケーションです。Claude、IDE、エージェントランタイムなど。ユーザーが話しかけている相手がホストです。
- クライアント はホストの中に存在し、MCP を話します。ホストは接続先のサーバーごとにクライアントを 1 つ実行します。
- サーバー はこの SDK で作るものです。クライアントに対していろいろなものを公開します。モデルと直接やり取りすることはありません。
書くのはサーバーです。ホストは誰か他の人のプロダクトです。SDK は Client も提供します。これはサーバーをテストするために使うもので、このページの後半にも登場します。
3 つのプリミティブ
サーバーが公開できるものはちょうど 3 種類です。その違いは 誰が使うことを決めるか にあります。
| プリミティブ | 制御するのは | 何であるか | 例 |
|---|---|---|---|
| ツール | モデル | アクションを実行するためにモデルが呼び出す関数 | API 呼び出し、データベースへの書き込み |
| リソース | アプリケーション | ホストがモデルのコンテキストに読み込むデータ | ファイルの内容、API のレスポンス |
| プロンプト | ユーザー | ユーザーが名前で呼び出す再利用可能なメッセージテンプレート | スラッシュコマンド、メニュー項目 |
この分け方の肝は「制御するのは」の列です。ツールが実行されるのは モデル が呼ぶと決めたからです。リソースが添付されるのは アプリケーション がモデルに必要だと判断したからです。プロンプトが実行されるのは ユーザー がそれを選んだからです。
Info
Web API を作ったことがあれば、直感はほぼそのまま使えます。リソース は GET
(データを読み込むだけで何も変更しない)、ツール は POST(処理を行い、
副作用があるかもしれない)です。プロンプト に対応する HTTP の概念はありません。
ユーザーが名前を指定して実行する保存済みクエリに近いものです。
1 つのサーバーに 3 つすべて
from mcp.server import MCPServer
mcp = MCPServer("Demo")
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers."""
return a + b
@mcp.resource("greeting://{name}")
def greeting(name: str) -> str:
"""Greet someone by name."""
return f"Hello, {name}!"
@mcp.prompt()
def summarize(text: str) -> str:
"""Summarize a piece of text in one sentence."""
return f"Summarize the following text in one sentence:\n\n{text}"
3 つの普通の関数と、3 つのデコレーター。登録作業はこのデコレーターだけで完結します。
@mcp.tool()はaddを ツール にします。@mcp.resource("greeting://{name}")はgreetingを リソーステンプレート にします。URI の{name}が関数のパラメーターに対応します。@mcp.prompt()はsummarizeを プロンプト にします。返した文字列がユーザーメッセージになります。
それ以外(名前、説明、引数のスキーマ)は、SDK が関数そのものから読み取ります。関数名、docstring、型ヒントです。別途宣言したものは一切ありません。
Tip
SDK の 2 つの側にはそれぞれ別のインポートパスがあります。from mcp import Client と
from mcp.server import MCPServer です。from mcp import MCPServer は存在しません。
試してみる
MCP Inspector で実行します。
uv run mcp dev server.py
表示された URL を開いてください。Inspector にはプリミティブごとにタブがあります。順番に見ていきましょう。
ツール。 項目は 1 つ、Add two numbers. と説明された add です。フォームには a 用の必須の整数フィールドと、b 用のフィールドがあります。値を入れて呼び出すと、結果は 3 になります。Inspector はこのフォームを a: int, b: int から組み立てました。他のクライアントもすべて同じことをします。
リソース。 Resources の一覧は空です。greeting は Resource Templates の下にあります。greeting://{name} にはパラメーターがあるため、誰かが name を与えるまで一覧に出せる具体的なリソースが存在しないからです。World を渡して読み取ってみてください。
Hello, World!
プロンプト。 項目は 1 つ、必須の text 引数を 1 つ持つ summarize です。適当なテキストを渡して取得すると、role: user とレンダリング済みの文字列を内容に持つメッセージが 1 つ返ってきます。プロンプトとはそれだけのものです。メッセージを組み立てる関数です。
Inspector はサーバーを stdio で実行しました。MCP サーバーが話せるトランスポートのひとつです。どれを使うかはまだ選ばなくて構いません。それは サーバーの実行 のページの話です。
ケイパビリティ
Inspector には 3 つのタブがありました。3 つあるとどうやって分かったのでしょうか。
クライアントが接続すると、サーバーは自身の ケイパビリティ を宣言します。どの種類のリクエストに応答するか、という宣言です。クライアントはそれを見て、そもそも何を尋ねるかを決めます。この宣言を書いた覚えはないはずです。MCPServer が代わりに宣言してくれます。
自分で確認してみましょう。SDK の Client はサーバーオブジェクトを直接受け取り、メモリ内 で接続します(サブプロセスもポートも不要です)。
import asyncio
from mcp import Client
from server import mcp
async def main() -> None:
async with Client(mcp) as client:
print(client.server_capabilities.model_dump(exclude_none=True))
asyncio.run(main())
{'prompts': {'list_changed': True}, 'resources': {'subscribe': True, 'list_changed': True}, 'tools': {'list_changed': True}}
この辞書がサーバーの宣言した ケイパビリティ です。接続してきたクライアントが最初に知る情報でもあります。
| ケイパビリティ | クライアントが呼べるようになるもの |
|---|---|
tools |
tools/list、tools/call |
resources |
resources/list、resources/templates/list、resources/read |
prompts |
prompts/list、prompts/get |
MCPServer は 3 つのプリミティブすべてを提供するので、3 つとも必ず宣言されます。
ここに無いものにも注目してください。completions(リソーステンプレートやプロンプトの引数の自動補完)には自分で書くハンドラーが必要です。このサーバーにはそれが無いので、ケイパビリティも現れず、行儀のよいクライアントは尋ねてきません。オプションの機能はすべてこのルールに従います。登録すればケイパビリティが現れます。補完 がそれを示します。
Info
Client(mcp) は、このドキュメントのすべての例をテストするのに使っているメモリ内クライアントそのものであり、
自分のサーバーをテストするときにも使うものです。これには 1 ページ割いています。テスト を参照してください。
書かなかったもの
このページを振り返ってみましょう。書いたのは小さな Python 関数 3 つです。次のものは 書いていません。
- JSON Schema。
a: int, b: intがaddのスキーマ そのもの です。 - リクエストハンドラー。
tools/list、resources/read、prompts/get、すべて代わりに処理されます。 - ケイパビリティの宣言。
MCPServerが作ってくれます。 - プロトコルのコード 1 行すら。バージョンのネゴシエーション、JSON-RPC のフレーミング、ケイパビリティの交換、そのすべては
mcp devとClient(mcp)の内側で起きていて、目にすることはありませんでした。
この比率こそが SDK の存在意義です。
まとめ
- ホスト は LLM アプリ、クライアント はその MCP を話す側、サーバー は自分が作るものです。
- ツールは モデル が制御し、リソースは アプリケーション が制御し、プロンプトは ユーザー が制御します。
- プリミティブごとにデコレーターが 1 つ。
@mcp.tool()、@mcp.resource(uri)、@mcp.prompt()。名前、説明、スキーマは関数から取られます。 {param}を含む URI はリソース テンプレート になり、具体的なリソースとは別に一覧されます。- サーバーの ケイパビリティ は代わりに宣言され、クライアントはサーバーが宣言したものしか尋ねません。
Client(mcp)はサーバーオブジェクトにメモリ内で接続します。初日から使えるテスト環境です。
次は 実際のホストに接続する です。このサーバーを Claude Desktop や IDE の中で実際に動かします。その次は テスト。1 ページ、メモリ内クライアント 1 つで、動いているかどうかを推測する必要はもうなくなります。そのあとはプリミティブごとに 1 ページずつ、モデルが動かすものから始めます。ツール です。