docs: publish translated docs in twelve languages and the tool that maintains them (#3280)
This commit is contained in:
parent
14aa889992
commit
52ad0a8876
5 changed files with 89737 additions and 54 deletions
139
i18n/ja/pages/get-started/first-steps.md
Normal file
139
i18n/ja/pages/get-started/first-steps.md
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
---
|
||||
translation:
|
||||
sections: [0d6c05bcbf836bf3, 59a7b14eeefc68c1, 7114d8d6daba203f, e8bbb56a98ba7bc9, 5138010f6159901c, f78da7c7c363d4c6, 220a939cab348686]
|
||||
tool: 1
|
||||
---
|
||||
# 最初のステップ {#first-steps}
|
||||
|
||||
**[トップページ](../index.md)** は駆け足です。サーバーを書き、実行し、ツールを呼び出します。
|
||||
|
||||
このページではじっくり進めます。サーバーが公開できる 3 種類のものをすべて取り上げ、途中で出てくるものすべてに名前を付けていきます。
|
||||
|
||||
## ホスト、クライアント、サーバー {#host-client-and-server}
|
||||
|
||||
ここから先、どのページにも登場する言葉が 3 つあります。
|
||||
|
||||
* **ホスト**は LLM アプリケーションです。Claude、IDE、エージェントランタイムなどがこれにあたります。ユーザーが対話している相手です。
|
||||
* **クライアント**はホストの中にあり、MCP を話します。ホストは、接続するサーバーごとにクライアントを 1 つずつ動かします。
|
||||
* **サーバー**は、この SDK で作るものです。クライアントに対して何かを公開します。モデルと直接やり取りすることは決してありません。
|
||||
|
||||
自分で書くのはサーバーです。ホストは別の誰かが作る製品です。SDK には `Client` も用意されています。サーバーのテストに使うもので、このページの後半にも登場します。
|
||||
|
||||
## 3 つのプリミティブ {#the-three-primitives}
|
||||
|
||||
サーバーが公開するものは、ちょうど 3 種類です。それらを分けるのは、**誰が使うと決めるのか**という点です。
|
||||
|
||||
| プリミティブ | 制御する主体 | どんなものか | 例 |
|
||||
|---------------|-----------------|-----------------------------------------------------|------------------------------------|
|
||||
| **ツール** | モデル | アクションを起こすためにモデルが呼び出す関数 | API 呼び出し、データベースへの書き込み |
|
||||
| **リソース** | アプリケーション | ホストがモデルのコンテキストに読み込むデータ | ファイルの内容、API のレスポンス |
|
||||
| **プロンプト** | ユーザー | ユーザーが名前で呼び出す、再利用可能なメッセージテンプレート | スラッシュコマンド、メニュー項目 |
|
||||
|
||||
「制御する主体」こそが、この区分の核心です。ツールが実行されるのは、**モデル**が呼び出すと決めたからです。リソースが添付されるのは、**アプリケーション**がモデルに必要だと判断したからです。プロンプトが実行されるのは、**ユーザー**が選んだからです。
|
||||
|
||||
!!! info
|
||||
Web API を作ったことがあれば、勘どころはもうほとんどつかめています。**リソース**は `GET`(データを読み込み、何も変更しない)で、**ツール**は `POST`(処理を行い、副作用を持つことがある)です。**プロンプト**に HTTP の対応物はありません。ユーザーが名前を指定して実行する、保存済みのクエリに近いものです。
|
||||
|
||||
## 1 つのサーバーで 3 つすべて {#one-server-all-three}
|
||||
|
||||
```python title="server.py" hl_lines="6 12 18"
|
||||
--8<-- "docs_src/first_steps/tutorial001.py"
|
||||
```
|
||||
|
||||
ごく普通の関数が 3 つ、デコレーターが 3 つです。どのデコレーターも、それだけで登録が完結します。
|
||||
|
||||
* `@mcp.tool()` は `add` を**ツール**にします。
|
||||
* `@mcp.resource("greeting://{name}")` は `greeting` を**リソーステンプレート**にします。URI の中の `{name}` が関数のパラメーターです。
|
||||
* `@mcp.prompt()` は `summarize` を**プロンプト**にします。返した文字列がユーザーメッセージになります。
|
||||
|
||||
それ以外のもの(名前、説明、引数のスキーマ)は、SDK が関数そのものから読み取ります。関数名、docstring、型ヒントからです。どれも別途宣言してはいません。
|
||||
|
||||
!!! tip
|
||||
SDK の 2 つの半分には、インポートパスも 2 つあります。`from mcp import Client` と `from mcp.server import MCPServer` です。`from mcp import MCPServer` はありません。
|
||||
|
||||
### 試してみる {#try-it}
|
||||
|
||||
MCP Inspector で実行してください。
|
||||
|
||||
```console
|
||||
uv run mcp dev server.py
|
||||
```
|
||||
|
||||
出力された URL を開いてください。Inspector にはプリミティブごとにタブが 1 つずつあります。順に見ていきましょう。
|
||||
|
||||
**Tools** タブには項目が 1 つあります。`add` で、説明は *Add two numbers.* です。フォームには必須の整数フィールドが 2 つあり、1 つは `a` 用、もう 1 つは `b` 用です。値を入力して呼び出すと、結果は `3` です。Inspector はこのフォームを `a: int, b: int` から組み立てました。ほかのどのクライアントも同じことをします。
|
||||
|
||||
**Resources** タブでは、*Resources* の一覧は空です。`greeting` は **Resource Templates** の下にあります。`greeting://{name}` にはパラメーターがあり、誰かが `name` を指定するまでは一覧に載せられる単体のリソースが存在しないからです。`World` を指定して読み取ると、こう返ってきます。
|
||||
|
||||
```text
|
||||
Hello, World!
|
||||
```
|
||||
|
||||
**Prompts** タブにも項目が 1 つあります。`summarize` で、必須の引数 `text` を 1 つだけ取ります。適当なテキストを渡して取得すると、`role: user` を持ち、レンダリングされた文字列を内容とするメッセージが 1 つ返ってきます。プロンプトとはそれだけのものです。メッセージを組み立てる関数にすぎません。
|
||||
|
||||
Inspector はサーバーを **stdio** で実行しました。MCP サーバーが話せるトランスポートの 1 つです。トランスポートを選ぶのはまだ先で、そのためのページが **[サーバーの実行](../run/index.md)** です。
|
||||
|
||||
## ケイパビリティ {#capabilities}
|
||||
|
||||
Inspector にはタブが 3 つありました。3 つあると、どうやってわかったのでしょうか。
|
||||
|
||||
クライアントが接続すると、サーバーは自身の**ケイパビリティ**を宣言します。どの系統のリクエストに応答するか、ということです。クライアントはこの宣言をもとに、そもそも何を要求するかを決めます。この宣言を自分で書いてはいません。`MCPServer` が代わりに宣言します。
|
||||
|
||||
自分の目で確かめてみましょう。SDK の `Client` はサーバーオブジェクトをそのまま受け取り、**インメモリ**で接続します(サブプロセスもポートも使いません)。
|
||||
|
||||
```python
|
||||
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())
|
||||
```
|
||||
|
||||
```text
|
||||
{'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`(リソーステンプレートとプロンプトの引数の自動補完)には自分で書くハンドラーが必要ですが、このサーバーにはありません。そのためこのケイパビリティは宣言されず、行儀のよいクライアントなら要求もしません。オプションのものはすべてこのルールに従います。登録すればケイパビリティが現れます。**[補完](../servers/completions.md)** のページがそれを実証しています。
|
||||
|
||||
!!! info
|
||||
`Client(mcp)` は、このドキュメントのすべてのサンプルをテストしているのと同じインメモリクライアントで、自分のサーバーをテストするときにもこれを使います。まるごと 1 ページを割いています。**[テスト](testing.md)** です。
|
||||
|
||||
## 書かなかったもの {#what-you-did-not-write}
|
||||
|
||||
このページを振り返ってみてください。書いたのは小さな Python 関数 3 つです。次のものは書いて**いません**。
|
||||
|
||||
* JSON Schema。`a: int, b: int` がそのまま `add` のスキーマです。
|
||||
* リクエストハンドラー。`tools/list`、`resources/read`、`prompts/get` は、すべて代わりに処理されます。
|
||||
* ケイパビリティの宣言。`MCPServer` が代わりに作りました。
|
||||
* プロトコルのコードを 1 行も。バージョンのネゴシエーション、JSON-RPC のフレーミング、ケイパビリティの交換は、すべて `mcp dev` と `Client(mcp)` の内部で行われ、目にすることはありませんでした。
|
||||
|
||||
この比率こそが、この SDK の存在意義です。
|
||||
|
||||
## まとめ {#recap}
|
||||
|
||||
* **ホスト**は LLM アプリ、**クライアント**はそのうち MCP を話す部分、**サーバー**は自分で作るものです。
|
||||
* ツールは**モデル**が、リソースは**アプリケーション**が、プロンプトは**ユーザー**が制御します。
|
||||
* デコレーターはプリミティブごとに 1 つです。`@mcp.tool()`、`@mcp.resource(uri)`、`@mcp.prompt()`。名前、説明、スキーマは関数から取られます。
|
||||
* `{param}` を含む URI はリソース**テンプレート**を作り、具体的なリソースとは別に一覧表示されます。
|
||||
* サーバーの**ケイパビリティ**は代わりに宣言され、クライアントはサーバーが宣言したものだけを要求します。
|
||||
* `Client(mcp)` はサーバーオブジェクトにインメモリで接続します。初日から使えるテストハーネスです。
|
||||
|
||||
次は **[実際のホストに接続する](real-host.md)** です。このサーバーを Claude Desktop や IDE の中で、本当に動かします。その次は **[テスト](testing.md)** です。1 ページ、インメモリクライアント 1 つで、動くかどうかを当て推量することはもうありません。そのあとは各プリミティブに専用のページがあり、まずはモデルが動かすもの、**[ツール](../servers/tools.md)** から始まります。
|
||||
53
i18n/ja/pages/get-started/index.md
Normal file
53
i18n/ja/pages/get-started/index.md
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
---
|
||||
translation:
|
||||
sections: [ed4a756b4c53c585, 97e2fb315b7fe398, 4d04f1c6f4bf6c1d, 577d73078fc62baf]
|
||||
tool: 1
|
||||
---
|
||||
# はじめに {#get-started}
|
||||
|
||||
MCP が初めてでも、この SDK が初めてでも、ここから始めてください。ここにあるページでは、何もない状態から、テスト済みの動くサーバーができあがるまでを案内します。[SDK をインストール](installation.md)し、[最初のサーバー](first-steps.md)を作り、[実際のホストに接続](real-host.md)して、インメモリクライアントで[テスト](testing.md)します。
|
||||
|
||||
## コードを実行する {#run-the-code}
|
||||
|
||||
コードブロックはすべて、そのままコピーして使えます。どれも完結した、実際に動くファイルです。
|
||||
|
||||
一緒に進めるには、コードブロックを `server.py` に貼り付けて、MCP Inspector で開いてください。
|
||||
|
||||
```console
|
||||
uv run mcp dev server.py
|
||||
```
|
||||
|
||||
コードを自分で書き(またはコピーし)、編集し、ローカルで実行することを**強くおすすめします**。自分のエディターで使ってみてこそ、肝心な点が実感できます。書く量がどれほど少ないか、自動補完が効くこと、そして何も実行しないうちに型チェックがミスを見つけてくれることです。
|
||||
|
||||
## 推測に頼る必要はない {#you-will-not-be-guessing}
|
||||
|
||||
このドキュメントの例はすべて、SDK 自身のリポジトリの [`docs_src/`](https://github.com/modelcontextprotocol/python-sdk/tree/main/docs_src) 以下に置かれた完結したファイルです。そのどれもが、SDK のテストスイートによって**インメモリクライアント**を通じて実行されています。
|
||||
|
||||
```python
|
||||
import pytest
|
||||
from mcp import Client
|
||||
|
||||
from server import mcp
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_add() -> None:
|
||||
async with Client(mcp) as client:
|
||||
result = await client.call_tool("add", {"a": 1, "b": 2})
|
||||
assert result.structured_content == {"result": 3}
|
||||
```
|
||||
|
||||
サブプロセスも、ポートも、トランスポートもありません。`Client(mcp)` がサーバーオブジェクトに直接接続します。
|
||||
|
||||
SDK への変更によってこれらのページの例が壊れた場合、ページが壊れるより先に CI が赤くなります。ここで読むコードが、実際に動くコードそのものです。
|
||||
|
||||
この仕組みは[テスト](testing.md)で実際に使うことになります。自分のサーバーをテストする方法も、まさにこれです。
|
||||
|
||||
## 次に進む先 {#where-to-go-next}
|
||||
|
||||
サーバーが動くようになれば、残りのドキュメントは講座ではなくリファレンスです。どのページも単独で完結しているので、必要なところへ直接進んでください。
|
||||
|
||||
* サーバーが公開するもの(ツール、リソース、プロンプト)は **[サーバー](../servers/index.md)** です。
|
||||
* 登録した関数の中で使えるものは **[ハンドラーの中で](../handlers/index.md)** です。
|
||||
* クライアントから使えるようにする方法(stdio、HTTP、既存の FastAPI アプリ)は **[サーバーの実行](../run/index.md)** です。
|
||||
* 反対側、つまり MCP サーバーを「使う」側のアプリケーションを作る方法は **[クライアント](../client/index.md)** です。
|
||||
45
i18n/ja/pages/get-started/installation.md
Normal file
45
i18n/ja/pages/get-started/installation.md
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
translation:
|
||||
sections: [6e2f9bab94d5ed36, 8cf653388f69e28b, 6fd9ea2f65de0df6]
|
||||
tool: 1
|
||||
---
|
||||
# インストール {#installation}
|
||||
|
||||
Python SDK は PyPI 上で [`mcp`](https://pypi.org/project/mcp/) として公開されています。**Python 3.10 以上**が必要です。
|
||||
|
||||
このドキュメントは、現在の安定版リリースラインである **v2** について説明しています。
|
||||
|
||||
=== "uv"
|
||||
|
||||
```bash
|
||||
uv add "mcp[cli]"
|
||||
```
|
||||
|
||||
=== "pip"
|
||||
|
||||
```bash
|
||||
pip install "mcp[cli]"
|
||||
```
|
||||
|
||||
!!! note "v1 から移行する場合"
|
||||
v2 は破壊的変更を含むメジャーバージョンです。変更点はすべて **[移行ガイド](../migration.md)** にまとめてあります。自分のパッケージが `mcp` に依存していて、まだ移行の準備ができていない場合は、バージョンの上限として `<2` を付けておいてください(たとえば `mcp>=1.28,<2`)。そうすれば、バージョンを固定しない依存解決でも 1.x 系にとどまります。
|
||||
|
||||
## インストールされるもの {#what-gets-installed}
|
||||
|
||||
SDK を使うだけなら、以下の内容を知っている必要はありません。それぞれの依存関係が何のためにあるのか気になる場合のために、まとめておきます。
|
||||
|
||||
* `mcp-types`:すべてのプロトコル型(リクエスト、結果、コンテンツブロック)を独立したパッケージにしたもので、SDK と足並みをそろえてバージョン管理されます。`mcp` に依存するコードは、`mcp.types` というエイリアス経由でこれをインポートします(このドキュメントに出てくる `from mcp.types import ...` はすべてそうです)。`mcp_types` を直接インポートするのは、SDK なしで `mcp-types` をインストールするプロジェクトだけにしてください。
|
||||
* [`anyio`](https://anyio.readthedocs.io/):非同期ランタイムです。SDK 全体が anyio を前提に書かれているので、`asyncio` でも `trio` でも動きます。
|
||||
* [`pydantic`](https://docs.pydantic.dev/):`mcp.types` のあらゆるモデルの土台であり、スキーマの生成と検証もすべて担っています。
|
||||
* [`httpx2`](https://pypi.org/project/httpx2/):Streamable HTTP と SSE のクライアント側トランスポートを支える HTTP クライアントで、Server-Sent Events のサポートを内蔵しています。
|
||||
* [`starlette`](https://www.starlette.io/)、[`uvicorn`](https://www.uvicorn.org/)、[`sse-starlette`](https://pypi.org/project/sse-starlette/)、[`python-multipart`](https://pypi.org/project/python-multipart/):HTTP のサーバー側トランスポートです。
|
||||
* [`jsonschema`](https://pypi.org/project/jsonschema/):ツールの構造化出力を、宣言された出力スキーマに照らして検証します。
|
||||
* [`pyjwt[crypto]`](https://pyjwt.readthedocs.io/):認可のための OAuth トークン処理を担います。
|
||||
* [`opentelemetry-api`](https://opentelemetry-python.readthedocs.io/):軽量な API だけです。そのため、OpenTelemetry の SDK とエクスポーターを自分でインストールしない限り、この SDK のトレーシングミドルウェアにコストは発生しません。
|
||||
* [`typing-extensions`](https://typing-extensions.readthedocs.io/) と [`typing-inspection`](https://pypi.org/project/typing-inspection/):Python 3.10 でも新しい型付け機能を使えるようにします。
|
||||
* [`pywin32`](https://pypi.org/project/pywin32/):Windows 専用で、`stdio` のサブプロセス管理に使われます。
|
||||
|
||||
## オプションの extras {#optional-extras}
|
||||
|
||||
* `mcp[cli]` は、`mcp` コマンドラインツール(`mcp dev`、`mcp run`、`mcp install`)のために [`typer`](https://typer.tiangolo.com/) と [`python-dotenv`](https://pypi.org/project/python-dotenv/) を追加します。開発中は入れておきたいところですが、デプロイしたサーバーでは必要ないかもしれません。
|
||||
* `mcp[rich]` は、サーバーのログを見やすくするために [`rich`](https://rich.readthedocs.io/) を追加します。
|
||||
168
i18n/ja/pages/get-started/real-host.md
Normal file
168
i18n/ja/pages/get-started/real-host.md
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
---
|
||||
translation:
|
||||
sections: [3c4f2f06b4e978b6, 22520eecae3d1961, f4e1709db18d635a, 2eb57992049671d9, 1ba83e9af37cc1b4, 4822586344b08d9e, 1c93afef72478992, b6b448f9eddd51dc, fe55370fd931815b]
|
||||
tool: 1
|
||||
---
|
||||
# 実際のホストに接続する {#connect-to-a-real-host}
|
||||
|
||||
**ホスト**とは、サーバーが最終的にその中で動くことになるアプリケーションのことです。Claude Desktop、Claude Code、IDE などがそうです。ユーザーがやり取りする相手はホストです。その内部では、MCP **クライアント**がサーバーを子プロセスとして起動し、そのプロセスの stdin と stdout を介してサーバーと通信します。
|
||||
|
||||
つまり、ホストに接続するためにやることは 1 つだけです。**サーバーを起動するコマンド**をホストに伝えます。このページに出てくるもの(2 つの CLI コマンドと 3 つの JSON ファイル)はすべて、その同じコマンドの置き場所が違うだけです。
|
||||
|
||||
## 1 つのサーバー、すべてのホスト {#one-server-every-host}
|
||||
|
||||
```python title="server.py" hl_lines="3 33-34"
|
||||
--8<-- "docs_src/real_host/tutorial001.py"
|
||||
```
|
||||
|
||||
ツール 2 つとリソース 1 つが、1 つのファイルに収まっています。このファイルについて、以降のどのホストにも関わる点が 3 つあります。
|
||||
|
||||
* 引数なしの `mcp.run()` は **stdio** サーバーを起動します。ブロックし、stdin でプロトコルメッセージを読み、stdout に書き出します。これが、このページのどのホストも話すトランスポートです。ホストはこのファイルを子プロセスとして起動し、その 2 本のパイプを所有します。だからこそ、接続は常に「これがコマンドです」と伝えるだけで済みます。ポートを選ぶことはなく、どこかのポートで待ち受けるものもありません。
|
||||
* `run()` は `if __name__ == "__main__":` の下にあります。以降のものはすべてこのファイルを実行するのではなく**インポート**するので、ガードのない `run()` だと、何かがモジュールを読み込んだ瞬間にサーバーが起動してしまいます。
|
||||
* サーバーオブジェクトは `mcp` という名前のモジュールレベルのグローバル変数です。これは `mcp run` が探す名前です(`server` と `app` でも動きます)。別の名前を付けた場合は、`mcp run server.py:bookshop` のように明示的に指定します。
|
||||
|
||||
このページの Python はこれが最後の 1 行です。ここから下はすべてホストの設定です。
|
||||
|
||||
## 起動コマンド {#the-launch-command}
|
||||
|
||||
以降のどのホストにも同じコマンドを渡します。
|
||||
|
||||
```bash
|
||||
uv run --with "mcp[cli]" mcp run /absolute/path/to/server.py
|
||||
```
|
||||
|
||||
どのホストにも 1 つのコマンドで済むのは、`uv run --with` がその場で SDK を新しい環境へ解決してくれるからです。どのディレクトリからでも動き、プロジェクトも、有効化すべき仮想環境も要りません。このことがほかのどこよりもここで効いてくるのは、ホストがサーバーを起動するのがシェルからではなく、ほぼ空の環境でホスト自身の作業ディレクトリからだからです。
|
||||
|
||||
このコマンドは、`mcp install` が Claude Desktop の設定に書き込んでくれるコマンドでもあります(後述)。そのため、手で入力するものとツールが生成するものは、ツールが付け加える正確なバージョン固定を除いて一致します。
|
||||
|
||||
!!! tip "ホストが `uv` を見つけられない場合"
|
||||
ホストは最小限の `PATH` でサーバーを起動するため、そこに `uv` が入っていないことがあります。`uv` とだけ書いた部分を、`which uv`(macOS/Linux)または `where uv`(Windows)で得られる絶対パスに置き換えてください。`mcp install` が書き込むのもまさにこの形です。
|
||||
|
||||
!!! note "このページはローカルの話"
|
||||
ここで扱うものはすべて、ホストと同じマシン上でサーバーを動かします。ホストがファイルを stdio 経由で起動する形です。個人用のツールや 1 台のマシンで使うツールなら、まさにこれが正解です。ファイルを持って**いない**人たちにサーバーを渡すには、コマンドではなく **URL** を配ります。つまり、同じ `mcp` オブジェクトを Streamable HTTP で提供します。**[サーバーの実行](../run/index.md)** はその判断を 1 つの表にまとめており、**[デプロイとスケール](../run/deploy.md)** はそこから実際のホスト名に至るまでの道のりです。
|
||||
|
||||
また、ホストとは内部に MCP クライアントを持つアプリケーションにすぎないので、自分の Python コードがホストの役を演じることもできます。**[クライアントのトランスポート](../client/transports.md)** ではこの同じファイルを `stdio_client(...)` でサブプロセスとして起動し、**[テスト](testing.md)** ではプロセスを一切使わずにメモリ内で接続します。
|
||||
|
||||
## Claude Desktop {#claude-desktop}
|
||||
|
||||
SDK が代わりに設定してくれる唯一のホストです。
|
||||
|
||||
```bash
|
||||
uv run mcp install server.py
|
||||
```
|
||||
|
||||
これだけです。`mcp install` はファイルをインポートしてサーバーの名前を読み取り、Claude Desktop の設定ファイルを探し出し、そこに起動コマンドを書き込みます。その過程でパスを絶対パスに変換してくれるので、自分で変換する必要はありません。
|
||||
|
||||
謎めいたところは何もありません。書き込まれるエントリは次のとおりです。
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"Bookshop": {
|
||||
"command": "/absolute/path/to/uv",
|
||||
"args": [
|
||||
"run",
|
||||
"--frozen",
|
||||
"--with",
|
||||
"mcp[cli]==2.0.0",
|
||||
"mcp",
|
||||
"run",
|
||||
"/absolute/path/to/server.py"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
これは前の節の起動コマンドに 3 つの要素を加えたものです。`uv` への絶対パス、たまたま近くにあるロックファイルを `uv` が書き換えることのないようにする `--frozen`、そしてインストール済みの `mcp` のバージョンへの正確な固定です。書き込み先は `claude_desktop_config.json` で、このファイルは次の場所にあります。
|
||||
|
||||
* **macOS**:`~/Library/Application Support/Claude/claude_desktop_config.json`
|
||||
* **Windows**:`%APPDATA%\Claude\claude_desktop_config.json`
|
||||
|
||||
このファイルは手で書くこともできます。`mcp install` があるのは、手で書くときにありがちなミス(相対パス)を避けるためです。
|
||||
|
||||
Claude Desktop を(ウィンドウだけでなく)完全に終了し、もう一度開いてください。
|
||||
|
||||
!!! warning
|
||||
Claude Desktop の設定「ディレクトリ」がまだ存在しない場合、`mcp install` は `Claude app not found` で失敗します。Claude Desktop をインストールして一度起動してください。ディレクトリはそのときに作られます。
|
||||
|
||||
!!! tip
|
||||
Claude Desktop はサーバーを自身のプロセスで起動するので、シェルの環境変数はそこにはありません。`uv run mcp install server.py -v API_KEY=abc123`(または `-f .env`)とすると、それらがエントリの `env` フィールドに記録されます。`--name` はエントリ名を上書きします。デフォルトはサーバーの `name` です。
|
||||
|
||||
## Claude Code {#claude-code}
|
||||
|
||||
編集するファイルはありません。`claude` CLI でサーバーを登録してください。`--` の後ろはすべて起動コマンドです。
|
||||
|
||||
```bash
|
||||
claude mcp add bookshop -- uv run --with "mcp[cli]" mcp run /absolute/path/to/server.py
|
||||
```
|
||||
|
||||
Claude Code のセッション内で `/mcp` を実行し、`bookshop` が接続されていてそのツールが一覧表示されることを確認してください。
|
||||
|
||||
## Cursor {#cursor}
|
||||
|
||||
プロジェクトのルートに `.cursor/mcp.json` を作成してください。
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"bookshop": {
|
||||
"command": "uv",
|
||||
"args": ["run", "--with", "mcp[cli]", "mcp", "run", "/absolute/path/to/server.py"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Claude Desktop が使うのと同じ `mcpServers` キーの下に、同じ `command` と `args` を置きます。サーバーは Cursor の MCP 設定に表示され、両方のツールが一覧に並びます。
|
||||
|
||||
## VS Code {#vs-code}
|
||||
|
||||
プロジェクトのルートに `.vscode/mcp.json` を作成してください。
|
||||
|
||||
```json
|
||||
{
|
||||
"servers": {
|
||||
"bookshop": {
|
||||
"type": "stdio",
|
||||
"command": "uv",
|
||||
"args": ["run", "--with", "mcp[cli]", "mcp", "run", "/absolute/path/to/server.py"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Cursor のファイルとの違いは 2 つだけです。ラッパーのキーが `mcpServers` ではなく `servers` であること、そして各エントリが `type` を宣言することです。信頼を確認するプロンプトを承認すると、コマンドパレットの **MCP: List Servers** に `bookshop` が実行中として表示されます。
|
||||
|
||||
!!! note
|
||||
VS Code 1.99 以降と、サインイン済みの **GitHub Copilot** 拡張機能が必要です(Copilot Free で十分です)。また、Copilot Chat は **Agent** モードでなければなりません。ほかのモードはツールを呼び出さないからです。
|
||||
|
||||
## 表示されないとき {#it-doesnt-show-up}
|
||||
|
||||
ホストの設定に手を付ける前に、起動コマンドを自分で実行してみてください。
|
||||
|
||||
```bash
|
||||
uv run --with "mcp[cli]" mcp run /absolute/path/to/server.py
|
||||
```
|
||||
|
||||
何も表示されず、コマンドも戻ってきません。この沈黙は正しい動作です。stdio サーバーは、ホストが先に stdin で話しかけてくるのを待っています(止めるには `Ctrl-C`)。本当のバグはトレースバックや即座の終了のほうで、こうして実行すれば、ホスト越しに推測する代わりにそれを直接読めます。
|
||||
|
||||
このコマンドがじっと待機するようになったら、残る原因はほぼ決まって次の 3 つのどれかです。
|
||||
|
||||
* **相対パス。** ホストがサーバーを起動するのは、登録したときのディレクトリではなく、ホスト自身の作業ディレクトリからです。`/absolute/path/to/server.py` が必要なところに `server.py` と書くのが、飛び抜けて多い失敗です。ホストが `uv` も見つけられないなら、そのパスも絶対パスにする必要があります。
|
||||
* **ホストがまだ古い設定で動いている。** ホストは起動時に設定を読み込みます。特に Claude Desktop は、`claude_desktop_config.json` の編集を反映させるには、ウィンドウを閉じるだけでなく「完全に終了」してから開き直す必要があります。
|
||||
* **退避される期間の外で、何かが stdout に届いた。** stdio では、stdout がプロトコルそのものです。SDK はサービス中、フラッシュされた余計な出力を stderr に退避させます。しかし、それ以前に stdout へフラッシュされた出力(echo するラッパースクリプトや、バッファリングなしのプロセスでのインポート時の `print()`)や、インタープリター終了時に書き出されるバッファ済みの `print()` は別です。これらは壊れたメッセージをホストに渡してしまい、ホストは接続を切ります。ログ出力にはデフォルトの `logging` 設定を使ってください。その stderr ハンドラーはレコードごとにフラッシュします。独自のハンドラーも stdout を避ける必要があります。詳しくは **[ロギング](../handlers/logging.md)** を参照してください。
|
||||
|
||||
Claude Desktop はサーバーごとにログを残します。`mcp-server-<NAME>.log` がサーバーの stderr で、接続についての `mcp.log` と並んで、macOS では `~/Library/Logs/Claude`、Windows では `%APPDATA%\Claude\logs` の下にあります。
|
||||
|
||||
この 3 つに当てはまらない場合は、**[トラブルシューティング](../troubleshooting.md)** のページを参照してください。
|
||||
|
||||
## まとめ {#recap}
|
||||
|
||||
* **ホスト**(Claude Desktop や IDE)は MCP クライアントを動かし、そのクライアントがサーバーを子プロセスとして stdio 経由で起動します。接続とは、起動コマンドを 1 つ渡すことです。
|
||||
* そのコマンドは `uv run --with "mcp[cli]" mcp run /absolute/path/to/server.py` です。有効化する venv は不要で、どのディレクトリからでも動きます。
|
||||
* **Claude Desktop** は、`mcp install` が代わりに設定してくれる唯一のホストです。その同じコマンド(`uv` への絶対パス、`--frozen`、インストール済みバージョンへの正確な固定を加えたもの)を `claude_desktop_config.json` に書き込むので、自分で書く必要はありません。
|
||||
* **Claude Code** は `claude mcp add bookshop -- <launch command>` です。**Cursor** は `mcpServers` の下に書く `.cursor/mcp.json` です。**VS Code** は `servers` の下に書く `.vscode/mcp.json` で、各エントリに `type` を付けます。
|
||||
* どこでも絶対パスを使い、設定を編集したらホストを再起動し、SDK 以外のものには決して stdout に書き込ませないでください。
|
||||
|
||||
このページのどのホストも、同じファイルに同じコマンドで接続しました。そのファイルが何を「公開」できるかが、このドキュメントの残りのテーマです。**[ツール](../servers/tools.md)**、**[リソース](../servers/resources.md)**、そして stdio 以外のあらゆるトランスポートを扱う **[サーバーの実行](../run/index.md)** へと続きます。
|
||||
96
i18n/ja/pages/get-started/testing.md
Normal file
96
i18n/ja/pages/get-started/testing.md
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
---
|
||||
translation:
|
||||
sections: ['4926721070127497', c52a1de2b6b32f40, 2e410b412c25f314, 627195f7159e24ef]
|
||||
tool: 1
|
||||
---
|
||||
# テスト {#testing}
|
||||
|
||||
Python SDK には、**インメモリトランスポート**を備えた `Client` クラスが付属しています。サーバーオブジェクトを渡せば、そのサーバーに直接接続します。
|
||||
|
||||
サブプロセスも、ポートも要りません。トランスポートすら使いません。FastAPI の `TestClient` と同じ発想です。
|
||||
|
||||
## 基本的な使い方 {#basic-usage}
|
||||
|
||||
ツールを 1 つだけ持つシンプルなサーバーがあるとします。
|
||||
|
||||
```python title="server.py"
|
||||
--8<-- "docs_src/testing/tutorial001.py"
|
||||
```
|
||||
|
||||
以下のテストを実行するには、追加の(開発用)依存関係が 2 つ必要です。
|
||||
|
||||
=== "uv"
|
||||
|
||||
```bash
|
||||
uv add --dev pytest inline-snapshot
|
||||
```
|
||||
|
||||
=== "pip"
|
||||
|
||||
```bash
|
||||
pip install pytest inline-snapshot
|
||||
```
|
||||
|
||||
!!! info
|
||||
このドキュメントは、[`pytest`](https://docs.pytest.org/en/stable/) をすでに知っていることを前提にしています。
|
||||
|
||||
[`inline-snapshot`](https://15r10nk.github.io/inline-snapshot/latest/) は、以下のテストで結果オブジェクト全体を 1 行でアサートするために使っているライブラリです。テストの出力を、コードにあるとおりの `snapshot(...)` リテラルとして記録します。使いたくない場合は import を削除し、ほかのテストと同じように、関心のあるフィールド(`result.content[0].text == "3"`)をアサートしてください。
|
||||
|
||||
テストは次のとおりです。
|
||||
|
||||
```python title="test_server.py"
|
||||
import pytest
|
||||
from inline_snapshot import snapshot
|
||||
from mcp import Client
|
||||
from mcp.types import CallToolResult, TextContent
|
||||
|
||||
from server import mcp
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def anyio_backend(): # (1)!
|
||||
return "asyncio"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def client(): # (2)!
|
||||
async with Client(mcp, raise_exceptions=True) as c:
|
||||
yield c
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_call_add_tool(client: Client):
|
||||
result = await client.call_tool("add", {"a": 1, "b": 2})
|
||||
# Drop the server identity stamp in `_meta`; it is not what this test is about.
|
||||
result.meta = None
|
||||
assert result == snapshot(
|
||||
CallToolResult(
|
||||
content=[TextContent(type="text", text="3")],
|
||||
structured_content={"result": 3},
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
1. `trio` を使っている場合は、代わりに `"trio"` を返してください。詳しくは [anyio のドキュメント](https://anyio.readthedocs.io/en/stable/testing.html#specifying-the-backends-to-run-on) を参照してください。
|
||||
2. このフィクスチャは接続済みのクライアントを yield します。`client` を受け取るテストごとに、同じサーバーへの新しいインメモリ接続が用意されます。
|
||||
|
||||
これで準備完了です。あとはテストを拡張して、さらに多くのシナリオをカバーしていけます。
|
||||
|
||||
## なぜ `raise_exceptions=True` なのか {#why-raise_exceptionstrue}
|
||||
|
||||
問題が起こりうる場所は 2 種類あり、このフラグが関わるのはそのうちの一方だけです。
|
||||
|
||||
**ツール**の内部で発生した例外は、プロトコル上の失敗ではありません。`is_error=True` の付いた通常の結果になり、モデルがそのメッセージを読みます。`raise_exceptions` はこの挙動を変えません。指定してもしなくても、`call_tool` は同じ `is_error=True` の結果を返します。これについては専用のページがあります。**[エラーの処理](../servers/handling-errors.md)** を参照してください。
|
||||
|
||||
ツール本体の**外側**で起きた失敗は事情が異なります。`Client(mcp)` で得られる接続では、クライアントの目に触れる前に、サーバーがその失敗を汎用の `"Internal server error"` にサニタイズします。予期しないクラッシュの詳細は、リモートの呼び出し側に決して漏らしてはいけないからです。しかしテストでは、これはまさに望まない挙動です。そして `raise_exceptions=True` が変えるのはまさにこの点で、テストからはサニタイズ後のメッセージではなく本来のメッセージが見えるようになります。
|
||||
|
||||
テストでは有効にしたままにしてください。本番コードでは意味を持ちません。
|
||||
|
||||
## デフォルトはインプロセス {#in-process-by-default}
|
||||
|
||||
!!! note
|
||||
`Client(mcp)` はインプロセスで接続し、デフォルトでは**プロトコルの世代を問いません**。サーバーを調べ、適切なプロトコル経路を選びます。テストがレガシー固有のセマンティクス(サンプリングやエリシテーション(elicitation)のプッシュ、`message_handler`)を検証する場合は `mode="legacy"` に固定し、その場合は `raise_exceptions=True` を外してください。レガシー接続はそもそもサニタイズを行わず、このフラグを付けると失敗がテストの中ではなくサーバータスクの中で再送出されてしまうからです。
|
||||
|
||||
この 1 行こそが、このドキュメントが「掲載している例は動く」と約束できる理由でもあります。すべてのサンプルファイルは SDK 自身のテストスイートで実行されており、そのほぼすべてがまさにこのクライアントを経由しています。SDK が自分自身に対して使っているのと同じツールを使っているわけです。
|
||||
|
||||
これで、きちんと動く、テスト済みのサーバーが手元にあります。実際のアプリケーション(Claude Desktop や IDE)に組み込む方法は **[実際のホストに接続する](real-host.md)** に、それ以外の提供方法はすべて **[サーバーの実行](../run/index.md)** にまとまっています。
|
||||
Loading…
Reference in a new issue