docs: refresh translations, and translate pages in parallel (#3458)
This commit is contained in:
parent
08a3bc8eaf
commit
fd66270926
6 changed files with 4821 additions and 2277 deletions
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [9e7b9a1710e5aeba, b74ca4c1d2ddddee, fa8714e61bf90c5a, 04db67a886b7271c, 857690fb8f876800]
|
||||
sections: [9e7b9a1710e5aeba, 66a2e9acc9101d54, d3d25aa802f5145a, 04db67a886b7271c, 857690fb8f876800]
|
||||
tool: 1
|
||||
---
|
||||
# キャッシュヒント {#caching-hints}
|
||||
|
|
@ -27,7 +27,7 @@ translation:
|
|||
|
||||
低レベルの `Server` では、ハンドラーが結果を手作業で組み立てます。`ttl_ms` と `cache_scope` は結果モデルの単なるフィールドです。これらを明示的に設定したハンドラーは、フィールド単位で常にコンストラクターのマップより優先されます。
|
||||
|
||||
```python title="server.py" hl_lines="10 16"
|
||||
```python title="server.py" hl_lines="11 17"
|
||||
--8<-- "docs_src/caching/tutorial002.py"
|
||||
```
|
||||
|
||||
|
|
@ -41,10 +41,24 @@ translation:
|
|||
|
||||
2026-07-28 のセッションでは、`Client` がヒントに自動で従います。組み込みのレスポンスキャッシュがあり、デフォルトで有効です。`ttlMs` を持って届いた結果は保存され、その TTL 内に同一の呼び出しがあれば、ラウンドトリップなしでキャッシュから返されます。ヒントを「持たない」結果はキャッシュされません。ヒントのない結果には `CacheConfig.default_ttl_ms` が適用され、そのデフォルトは `0`(すぐに古くなる)です。そのため、何も宣言しないサーバーには、これまでとまったく同じ呼び出しごとのトラフィックが届きます。
|
||||
|
||||
```python title="client.py" hl_lines="33 35 38"
|
||||
これが起こる様子を見るには、前のセクションの `server.py` を uvicorn で起動してください(最後の行が ASGI アプリを組み立てています)。ハンドラーは実際に実行されるたびに 1 行出力します。
|
||||
|
||||
```console
|
||||
uvicorn server:app --port 8000
|
||||
```
|
||||
|
||||
```python title="client.py" hl_lines="20 23 28"
|
||||
--8<-- "docs_src/caching/tutorial003.py"
|
||||
```
|
||||
|
||||
2 つ目のターミナルから `python client.py` を実行してください。最初の結果が持っていたヒント、つまりハンドラーの `ttlMs` とマップの `cacheScope` が並んで出力されます。
|
||||
|
||||
```text
|
||||
1000 public
|
||||
```
|
||||
|
||||
残りはサーバー側のターミナルを見ればわかります。uvicorn のリクエストログの合間に、`tools/list served` が 3 回現れます。
|
||||
|
||||
呼び出し 4 回、取得 3 回です。2 回目の呼び出しは新鮮なエントリーを見つけ、サーバーに到達しませんでした。(注入した)クロックを TTL の先へ進めたことで、3 回目は再び取得しました。4 回目は `cache_mode="refresh"` を指定しています。このキーワード引数はキャッシュ対象の 5 つのメソッド(`list_tools`、`list_prompts`、`list_resources`、`list_resource_templates`、`read_resource`)にあります。
|
||||
|
||||
* `"use"`(デフォルト)は、新鮮なエントリーがあればそれを返し、なければ取得して保存します。
|
||||
|
|
@ -53,7 +67,7 @@ translation:
|
|||
|
||||
`"use"` より上位にルールが 1 つあります。**`meta` を持つ呼び出しは必ずサーバーに到達します。**`meta` を設定したリクエスト(進捗トークンやトレーシング用フィールドなど)は実際のリクエスト送信を前提にしているため、`cache_mode="use"` では `"refresh"` として扱われます。キャッシュの読み込みは省略され、取得した結果は引き続きキャッシュのエントリーを置き換えます。`"bypass"` と明示的な `"refresh"` はいつもどおりに動作します。
|
||||
|
||||
キャッシュを完全に無効にするには、`Client(server, cache=None)` で構築してください。すべての呼び出しが再びラウンドトリップになり、`cache_mode` は受け付けられるものの何もしません。
|
||||
キャッシュを完全に無効にするには、`Client` の構築時に `cache=None` を渡してください。すべての呼び出しが再びラウンドトリップになり、`cache_mode` は受け付けられるものの何もしません。
|
||||
|
||||
スコープも自動的に尊重されます。`"private"` のエントリーはキャッシュの「パーティション」(後述)をキーにし、`"public"` のエントリーはより広い共有を選べます。そして、名指しされたエントリーについては**通知が TTL に勝ちます**。`list_changed` 通知は対応するキャッシュ済みの一覧を破棄し、`resources/updated` はその URI と完全に一致するキーで保存されたキャッシュ済みの読み込み結果を、どれだけ新鮮でも破棄します。2026-07-28 の接続では、これらの通知は `client.listen(...)` で開く `subscriptions/listen` ストリームに届き、破棄はウォッチャーがイベントを見る前に完了します。詳しくは **[サブスクリプション](subscriptions.md)** を参照してください。
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [adf3c545b5be46b6, 916cd3ab1c03f461, e9be7a8d0eb0a456, 565890a636288ecf, 6af7e49db9129ec3, 06b0238c174186af, 90c6043be435fcb0]
|
||||
sections: [adf3c545b5be46b6, 916cd3ab1c03f461, 32ef568335dd95a7, 565890a636288ecf, 6af7e49db9129ec3, 06b0238c174186af, 0abc5ea5cb7ff6b3]
|
||||
tool: 1
|
||||
---
|
||||
# クライアントのコールバック {#client-callbacks}
|
||||
|
|
@ -58,7 +58,7 @@ result.content # [TextContent(type='text', text='Card issued to Ada Lovelace.')
|
|||
こちらから `tools/call` が 1 回、サーバーからの折り返しの `elicitation/create` が 1 回、それに答えるのがこちらの関数です。すべてが 1 回のツール呼び出しの中で完結します。
|
||||
|
||||
!!! info
|
||||
`Client(...)` の呼び出しにある `mode="legacy"` は、実際に働いています。デフォルトでは `Client(...)` は新しいプロトコルの経路をネゴシエートしますが、その経路にはサーバーからクライアントへのリクエストのためのバックチャネル(back-channel)がありません。コールバックが動く前に `ctx.elicit` が失敗します。これを決めるのはトランスポートではなく、ネゴシエートされたプロトコルです。インメモリでも URL 越しでも同じです。クライアントがこうしたリクエストに答える必要があるときは、必ず `mode="legacy"` を指定してください。このページの裏にあるテストはすべてそうしています。詳しくは **[プロトコルバージョン](../protocol-versions.md)** を参照してください。
|
||||
`Client(...)` の呼び出しにある `mode="legacy"` は、実際に働いています。デフォルトでは `Client(...)` は新しいプロトコルの経路をネゴシエートしますが、その経路にはサーバーからクライアントへのリクエストのためのバックチャネル(back-channel)がありません。コールバックが動く前に `ctx.elicit` が失敗します。これを決めるのはトランスポートではなく、ネゴシエートされたプロトコルです。クライアントがこうしたリクエストに答える必要があるときは、必ず `mode="legacy"` を指定してください。このページの裏にあるテストはすべてそうしています。詳しくは **[プロトコルバージョン](../protocol-versions.md)** を参照してください。
|
||||
|
||||
2026-07-28 のセッションでもコールバックが使われなくなるわけではなく、呼ばれ方が変わります。ツールが `ElicitRequest` を含む `InputRequiredResult` を返すと、`Client` はそのエントリを同じ `elicitation_callback` に振り分け、呼び出しを再試行してくれます。この流れは **[マルチラウンドトリップ(multi-round-trip)リクエスト](../handlers/multi-round-trip.md)** で説明しています。
|
||||
|
||||
|
|
@ -139,4 +139,4 @@ result.structured_content # {'result': ['elicitation']}
|
|||
* `sampling_callback` と `list_roots_callback` も同じように動きますが、非推奨の機能のためのものです。新しいサーバーは代わりにマルチラウンドトリップリクエストを使います。
|
||||
* `logging_callback` と `message_handler` は通知を受け取ります。何も宣言しません。
|
||||
|
||||
`Client(...)` の第 1 引数はトランスポートのオブジェクトです。すべての種類は **[クライアントのトランスポート](transports.md)** で扱っています。
|
||||
`Client(...)` の第 1 引数でトランスポートが決まります。すべての種類は **[クライアントのトランスポート](transports.md)** で扱っています。
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [ebef1e7a0df854f4, 8355cfaf1f76c9d5, 8e79141fc2985342, 46bdb07c7537e8a5, 80ce41579825a6fa, 5f0fa90494de8f65, 83d10514eaa62fa5, 9190555aa39a5d28, 84a4c9d8bf14dddb, 927d71cf40b58c30]
|
||||
sections: [ebef1e7a0df854f4, 7e16449f66e7dfd6, eeb0682f7d2a1079, 5713f0196a34e6e7, 0e844597859e4248, 3a97d9195ddcc92e, 1da08c483e59c141, 84702cc6e0a1fd42, 8dee7a31c86ffc5c, 83a5bce168ef23d7]
|
||||
tool: 1
|
||||
---
|
||||
# Client {#the-client}
|
||||
|
|
@ -11,13 +11,23 @@ translation:
|
|||
|
||||
## 最初のクライアント {#your-first-client}
|
||||
|
||||
```python title="client.py" hl_lines="14-18"
|
||||
クライアントには、対話する相手のサーバーが必要です。このページのすべてのスニペットが接続するのが、この Bookshop です。`server.py` として保存し、HTTP で起動したままにしてください。
|
||||
|
||||
```python title="server.py"
|
||||
--8<-- "docs_src/client/tutorial001.py"
|
||||
```
|
||||
|
||||
冒頭のサーバーは、接続先を用意するためだけにあります。クライアントはハイライトされた 5 行です。
|
||||
```console
|
||||
uv run mcp run server.py --transport streamable-http
|
||||
```
|
||||
|
||||
* `Client(mcp)` には**サーバーオブジェクトそのもの**を渡しています。これがインメモリのトランスポートです。サブプロセスもポートも HTTP もありません。このページのすべての例、そして作成するすべてのテストが、この方法で接続します。
|
||||
これでサーバーは `http://localhost:8000/mcp` で提供されます。クライアントは別のプログラムです。`client.py` として保存し、2 つ目のターミナルで `python client.py` を実行してください。
|
||||
|
||||
```python title="client.py" hl_lines="7-11"
|
||||
--8<-- "docs_src/client/tutorial001_client.py"
|
||||
```
|
||||
|
||||
* `Client("http://localhost:8000/mcp")` には **URL** を渡しているので、いま起動したサーバーに Streamable HTTP で接続します。
|
||||
* `async with` が**ライフサイクル**です。入ると接続してネゴシエーションを行い、出ると切断します。`connect()` / `close()` のペアはなく、ブロックが終わった後の `Client` は再利用できません。
|
||||
* ブロックの中では、接続に関する情報がすでに通常のプロパティとして揃っています。
|
||||
|
||||
|
|
@ -25,10 +35,10 @@ translation:
|
|||
|
||||
`Client` は位置引数を 1 つ取り、その型からトランスポートを決定します。
|
||||
|
||||
* `MCPServer`(または低レベルの `Server`)のインスタンス:**プロセス内**で接続します。
|
||||
* URL 文字列(`Client("http://localhost:8000/mcp")`):Streamable HTTP。本番向けの経路です。
|
||||
* `StdioServerParameters`:**サブプロセス**として起動するコマンドで、その stdin と stdout を通じて対話します。
|
||||
* URL 文字列(`Client("http://localhost:8000/mcp")`):Streamable HTTP。デプロイで使うトランスポートです。
|
||||
* `StdioServerParameters`:ローカルの**サブプロセス**として起動するコマンドで、その stdin と stdout を通じて対話します。
|
||||
* **トランスポート**:`async with ... as (read, write)` できるものなら何でも。たとえば、自分の HTTP クライアントをラップする `streamable_http_client(url, http_client=...)` です。
|
||||
* `MCPServer`(または低レベルの `Server`)のインスタンス:**プロセス内**で接続します。サブプロセスもポートもありません。これはテスト向けで、**[テスト](../get-started/testing.md)** がこれを土台にしています。
|
||||
|
||||
このページの残りの内容は、4 つのどれでも同じです。ヘッダー、サブプロセス、タイムアウト、そして `Transport` プロトコルについては、専用のページ **[クライアントのトランスポート](transports.md)** があります。
|
||||
|
||||
|
|
@ -48,11 +58,11 @@ translation:
|
|||
|
||||
## ツールの一覧取得 {#listing-tools}
|
||||
|
||||
```python title="client.py" hl_lines="15-20"
|
||||
```python title="client.py" hl_lines="8-13"
|
||||
--8<-- "docs_src/client/tutorial002.py"
|
||||
```
|
||||
|
||||
`list_tools()` は `ListToolsResult` を返し、ツールは `.tools` に入っています。それぞれが、ホストがモデルに渡す完全な定義です。
|
||||
`list_tools()` は `ListToolsResult` を返し、ツールは `.tools` に入っています。それぞれが、ホストがモデルに渡す完全な定義です。最初のツールは次のとおりです。
|
||||
|
||||
```python
|
||||
tool.name # 'search_books'
|
||||
|
|
@ -76,6 +86,8 @@ tool.description # 'Search the catalog by title or author.'
|
|||
|
||||
このスキーマには、UI が引数フォームを描画するのに必要なものも、モデルが有効な引数を生成するのに必要なものも、すべて含まれています。
|
||||
|
||||
2 つ目のツール `lookup_book` は `title=` なしで登録されているので、その `tool.title` は `None` です。
|
||||
|
||||
!!! tip
|
||||
`title` は省略可能なので、人間にツールを見せる UI はどちらかを選ぶ必要があります。`title` があればそれを、なければ `name` を使います。`from mcp.shared.metadata_utils import get_display_name` がまさにそれを行い、ツール、リソース、リソーステンプレート、プロンプトに対応しています。
|
||||
|
||||
|
|
@ -83,7 +95,7 @@ tool.description # 'Search the catalog by title or author.'
|
|||
|
||||
`call_tool(name, arguments)` はツールを実行し、`CallToolResult` を返します。
|
||||
|
||||
```python title="client.py" hl_lines="27-34"
|
||||
```python title="client.py" hl_lines="9-16"
|
||||
--8<-- "docs_src/client/tutorial003.py"
|
||||
```
|
||||
|
||||
|
|
@ -131,7 +143,7 @@ result.is_error # False
|
|||
|
||||
リソースの動詞は組になっています。一覧取得が 2 通り、読み取りが 1 通りです。
|
||||
|
||||
```python title="client.py" hl_lines="22-31"
|
||||
```python title="client.py" hl_lines="9-18"
|
||||
--8<-- "docs_src/client/tutorial004.py"
|
||||
```
|
||||
|
||||
|
|
@ -141,11 +153,11 @@ result.is_error # False
|
|||
|
||||
`read_resource` は `contents` を返します。これは `TextResourceContents` または `BlobResourceContents` のリストです。考え方はツールのコンテンツと同じで、`isinstance` で絞り込んでから `.text`(または `.blob`)を読みます。
|
||||
|
||||
クライアントは、リソースが変更されたときに通知を受けることもできます。2025 年世代の接続では `subscribe_resource(uri)` / `unsubscribe_resource(uri)` がそれにあたります。ただしこのメソッドのペアは `MCPServer` が実装していないため、2026-07-28 の通信上(これらの動詞はもう存在しません)ではリクエストに `-32601`、*Method not found* が返ります。2026 年の代替は `subscriptions/listen` ストリームで、こちらは `MCPServer` が実際に提供しています(そこでは `server_capabilities.resources.subscribe` が `True` です)。これを `client.listen(...)` で消費する方法は、このセクションの **[サブスクリプション](subscriptions.md)** のページで説明しています。
|
||||
クライアントは、リソースが変更されたときに通知を受けることもできます。2025 年世代の接続では `subscribe_resource(uri)` / `unsubscribe_resource(uri)` がそれにあたります。このメソッドのペアは `MCPServer` が実装していないため、2026-07-28 の通信上(これらの動詞はもう存在しません)ではリクエストに `-32601`、*Method not found* が返ります。2026 年の代替は `subscriptions/listen` ストリームで、こちらは `MCPServer` が実際に提供しています(そこでは `server_capabilities.resources.subscribe` が `True` です)。これを `client.listen(...)` で消費する方法は、このセクションの **[サブスクリプション](subscriptions.md)** のページで説明しています。
|
||||
|
||||
## プロンプト {#prompts}
|
||||
|
||||
```python title="client.py" hl_lines="15-20"
|
||||
```python title="client.py" hl_lines="8-13"
|
||||
--8<-- "docs_src/client/tutorial005.py"
|
||||
```
|
||||
|
||||
|
|
@ -170,7 +182,7 @@ message.content # TextContent(type='text', text='Recommend one poetry book from
|
|||
|
||||
補完ハンドラーを持つサーバーは、ユーザーの入力に合わせてプロンプトやリソーステンプレートの引数を自動補完できます。
|
||||
|
||||
```python title="client.py" hl_lines="27-31"
|
||||
```python title="client.py" hl_lines="9-13"
|
||||
--8<-- "docs_src/client/tutorial006.py"
|
||||
```
|
||||
|
||||
|
|
@ -183,21 +195,21 @@ message.content # TextContent(type='text', text='Recommend one poetry book from
|
|||
|
||||
`list_*` メソッドはどれも `cursor=` キーワードを取り、結果はどれも `next_cursor` を持ちます。`next_cursor` が `None` なら、すべて取得済みです。
|
||||
|
||||
```python title="client.py" hl_lines="22-30"
|
||||
```python title="client.py" hl_lines="7-15"
|
||||
--8<-- "docs_src/client/tutorial007.py"
|
||||
```
|
||||
|
||||
このループはどのサーバーに対しても正しく動きます。`MCPServer` はすべてを 1 ページで返すので、`next_cursor` は `None` になり、ループは 1 回だけ実行されます。ほとんどのコードがこのループを書かないのはそのためです。実際にページ分割するサーバーと、カーソルが従うルールについては **[ページネーション](../advanced/pagination.md)** を参照してください。
|
||||
`list_all_tools` はどのサーバーに対しても正しく動きます。`MCPServer` はすべてを 1 ページで返すので、`next_cursor` は `None` になり、ループは 1 回だけ実行されます。ほとんどのコードがこのループを書かないのはそのためです。実際にページ分割するサーバーと、カーソルが従うルールについては **[ページネーション](../advanced/pagination.md)** を参照してください。
|
||||
|
||||
## テストでの利用 {#in-tests}
|
||||
|
||||
プロセスもポートも使わない `Client(mcp)` は、それだけでサーバーのテストハーネスになります。
|
||||
このページの `client.py` はどれも、HTTP 経由で `server.py` に到達していました。テストではネットワークを省き、`Client` にサーバーオブジェクトそのものを渡します。`from server import mcp` としてから `Client(mcp)` です。プロセスもポートも不要で、上記のメソッドはどれも同じように動きます。
|
||||
|
||||
そのために用意されたコンストラクターのフラグが 1 つあります。`Client(mcp, raise_exceptions=True)` です。効果があるのはインメモリ接続のときだけで、その説明と、それを中心にしたパターン全体の組み立ては **[テスト](../get-started/testing.md)** のページにあります。
|
||||
そのために用意されたコンストラクターのフラグが 1 つあります。`Client(mcp, raise_exceptions=True)` です。効果があるのはプロセス内接続のときだけで、その説明と、それを中心にしたパターン全体の組み立ては **[テスト](../get-started/testing.md)** のページにあります。
|
||||
|
||||
## まとめ {#recap}
|
||||
|
||||
* `Client(x)` は、サーバーオブジェクトにはインメモリで、URL 文字列には Streamable HTTP で、それ以外にはトランスポート経由で接続します。
|
||||
* `Client(x)` は、URL 文字列には Streamable HTTP で接続し、`StdioServerParameters` にはサブプロセスを起動し、トランスポートには直接入り、テストではサーバーオブジェクトそのものを受け取ります。
|
||||
* `async with` がライフサイクルのすべてです。その中では `server_capabilities` と `protocol_version` にすでに値が入っており、サーバーが提供していれば `server_info` と `instructions` も同様です。
|
||||
* `list_tools()` で各ツールの `name`、`title`、`description`、`input_schema` が得られます。
|
||||
* `call_tool()` はモデル向けの `content`、コード向けの `structured_content`、そして `is_error` を返します。例外を送出するツールは、例外ではなく結果として返ってきます。
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [c6899d3892bd9fa0, 79372cff3cc48a88, 63878d29e87c3e73, 13175843d3588af4, e7e2b9fd516f77de, 758f06399b513c1f, a05d7278487d610b]
|
||||
sections: [c6899d3892bd9fa0, 79372cff3cc48a88, c2dae1ebe2ebd543, 13175843d3588af4, df06056fb16b3846, 758f06399b513c1f, a05d7278487d610b]
|
||||
tool: 1
|
||||
---
|
||||
# OAuth クライアント {#oauth-clients}
|
||||
|
|
@ -77,18 +77,20 @@ translation:
|
|||
|
||||
`Client` が初めてリクエストを送ると、サーバーは `401` を返します。そこからプロバイダーが引き継ぎます。
|
||||
|
||||
1. **発見。** `WWW-Authenticate` ヘッダーを読み、サーバーの Protected Resource Metadata を `/.well-known/oauth-protected-resource` から取得します。そこからこのリソースを保護している認可サーバーを知り、「その」サーバーのメタデータを取得します。
|
||||
1. **発見。** `WWW-Authenticate` ヘッダーを読み、サーバーの Protected Resource Metadata を `/.well-known/oauth-protected-resource` から取得します。そこからこのリソースを保護している認可サーバーを知り、「その」サーバーのメタデータを取得します。(リソースメタデータを公開していない古いサーバーには、代わりにそのサーバー自身のオリジンで認可サーバーのメタデータを問い合わせます。)どちらの場合も、メタデータは取得対象のサーバーを `issuer` として名指ししていなければなりません。それ以外は拒否されます。
|
||||
2. **登録。** ストレージに何もなければ、`OAuthClientMetadata` を使って動的に登録し、結果を保存します。
|
||||
3. **認可。** PKCE のペアと `state` を生成し、認可 URL を組み立て、`redirect_handler` を await します。続いて、コードを受け取るために `callback_handler` を await します。
|
||||
4. **交換。** コードを `OAuthToken` と引き換えて保存し、元のリクエストを `Authorization: Bearer ...` 付きで再送します。
|
||||
|
||||
それ以降は静かになります。トークンはストレージから取り出され、期限切れのアクセストークンはリフレッシュトークンで更新されます。そのどれもうまくいかないときだけ、フローをもう一度実行します。
|
||||
|
||||
これらのリクエストすべてに共通するトランスポートのルールが 1 つあります。外側の MCP リクエストと同じく、リダイレクトに従うのは同じオリジンにとどまり、かつメソッドを保つ場合(たとえば末尾スラッシュの 307/308)だけです。それ以外のリダイレクトは、その URL が応答しなかったものとして扱います。
|
||||
|
||||
これらを自分で書く必要はまったくありませんでした。残るキーワード引数は 2 つ(`client_metadata_url` と `validate_resource_url`)で、このファイルではどちらも不要です。知っておく価値があるのは `client_metadata_url` のほうで、下に専用のセクションがあります。
|
||||
|
||||
### 試してみる {#try-it}
|
||||
|
||||
このドキュメントの例のほとんどは、インメモリの `Client(server)` で確認できます。これは違います。このフローの要点は HTTP の `401` であり、インメモリのクライアントとサーバーのあいだには HTTP がありません。
|
||||
テストで使うインメモリの `Client(server)` は、ここでは役に立ちません。このフローの要点は HTTP の `401` であり、インメモリのクライアントとそのサーバーのあいだには HTTP がないからです。
|
||||
|
||||
リポジトリには実際に動くバージョンが同梱されています。`examples/servers/simple-auth/` はスタンドアロンの認可サーバーと保護された MCP サーバーを動かし、`examples/clients/simple-auth-client/` はこのページのクライアントを小さな CLI に育てたものです。その README に 2 つのコマンドが載っています。サーバーを起動し、それに対してクライアントを実行すれば、4 つのステップが進んでいくのを見られます。
|
||||
|
||||
|
|
@ -106,13 +108,14 @@ URL は HTTPS で、ルート以外のパスを持っている必要がありま
|
|||
|
||||
`ClientCredentialsOAuthProvider` は同じ `httpx2.Auth` で、人間がいないだけです。
|
||||
|
||||
```python title="client.py" hl_lines="4 27-33"
|
||||
```python title="client.py" hl_lines="4 27-34"
|
||||
--8<-- "docs_src/oauth_clients/tutorial002.py"
|
||||
```
|
||||
|
||||
変わった点は次のとおりです。
|
||||
|
||||
* `OAuthClientMetadata` もハンドラーもありません。`client_id` と `client_secret` を渡すと、プロバイダーはそれらを中心に最小限の `client_credentials` 登録を組み立て、動的登録を完全に省きます。
|
||||
* `issuer` には、そのクレデンシャルを発行した認可サーバーを指定します。そのサーバーの `/.well-known/oauth-authorization-server` ドキュメントが返す `issuer` の値を使ってください。発見は上と同じように実行されますが、トークンリクエストは「その」発行者のメタデータからしか組み立てられません。MCP サーバーがそれ以外の場所を指していれば、フローは代わりに `OAuthFlowError` で止まります。省略するのは非推奨で、3.0 では必須になります(**[非推奨の機能](../deprecated.md#deprecated-sdk-helpers)** を参照してください)。それまでのあいだ、プロバイダーは警告を出したうえで、発見で見つかった認可サーバーをそのまま使います。
|
||||
* `scope` はスペース区切りの文字列で、OAuth の通信上の形式です。
|
||||
* その先はすべて同じです。同じ `TokenStorage`、同じ `httpx2.AsyncClient(auth=...)`、同じ `streamable_http_client` です。
|
||||
|
||||
|
|
@ -123,7 +126,7 @@ URL は HTTPS で、ルート以外のパスを持っている必要がありま
|
|||
|
||||
!!! info
|
||||
`mcp.client.auth.extensions.client_credentials` にはもう 1 つプロバイダーがあります。
|
||||
**`PrivateKeyJWTOAuthProvider`** は、共有シークレットの代わりに JWT で認証するクライアント向けです(`private_key_jwt`、つまり鍵ペアやワークロードアイデンティティの方式)。パターンは同じで、1 つ構築して `auth=` に載せます。同じモジュールには、そのアサーションを組み立てる 2 つのヘルパー、`SignedJWTParameters` と `static_assertion_provider` も含まれています。
|
||||
**`PrivateKeyJWTOAuthProvider`** は、共有シークレットの代わりに JWT で認証するクライアント向けです(`private_key_jwt`、つまり鍵ペアやワークロードアイデンティティの方式)。パターンは同じで、1 つ構築して(同じく省略可能な `issuer` を取ります)`auth=` に載せます。同じモジュールには、そのアサーションを組み立てる 2 つのヘルパー、`SignedJWTParameters` と `static_assertion_provider` も含まれています。
|
||||
|
||||
人間がいない状況はもう 1 つあります。クライアントが企業に属していて、どの MCP サーバーに到達してよいかをユーザーではなくその企業のアイデンティティプロバイダーが決める場合です。これは独自の信頼モデルを持つ別のグラントで、専用のページ **[アイデンティティアサーション](identity-assertion.md)** があります。
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [09c857a25a9dc37a, 43bc6a76a243a50e, 0a716022a88768df, 4b7f78042bfcfff7, c112662e61b03315, 58974ba1f489a8b4, d18adbdbb835ea73]
|
||||
sections: [09c857a25a9dc37a, 43bc6a76a243a50e, 0a716022a88768df, 4b7f78042bfcfff7, c112662e61b03315, 58974ba1f489a8b4, ed4d17e894864056]
|
||||
tool: 1
|
||||
---
|
||||
# セッショングループ {#session-groups}
|
||||
|
|
@ -76,7 +76,7 @@ translation:
|
|||
## まとめ {#recap}
|
||||
|
||||
* `ClientSessionGroup` は多数のサーバー接続を保持し、それらのツール、リソース、プロンプトをそれぞれ 1 つの `dict` にまとめます。
|
||||
* サーバーごとに `connect_to_server(params)` を呼びます。受け取るのはトランスポートのパラメーターであり、`Client` が受け取るサーバーオブジェクトや URL ではありません。
|
||||
* サーバーごとに `connect_to_server(params)` を呼びます。受け取るのはトランスポートのパラメーターであり、`Client` が受け取る URL や `Transport` ではありません。
|
||||
* `group.call_tool(name, arguments)` は、所有するサーバーへのルーティングを代わりに行います。
|
||||
* 名前はグループ全体で一意でなければなりません。`search` ツールを持つ 2 つのサーバーは、そのままでは共存できません。
|
||||
* `component_name_hook=` は登録されるすべての名前を書き換えます。dict のキーは変わりますが、実際に送信される名前は変わりません。
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [9cac816674181eb0, 0700f337babcd4dd, 2bde0dd58cdf00f5, 40b4916d82eaf1d4, 3d0832f39b0d7059, dfa4446556badef0, 5bd93be2ab2ecb9c]
|
||||
sections: [9cac816674181eb0, 7c157764133fea1f, 40b4916d82eaf1d4, 10d151f2cc75317f, 3d0832f39b0d7059, 92742ba36533633d, 0aeca6145e7bd302]
|
||||
tool: 1
|
||||
---
|
||||
# クライアントのトランスポート {#client-transports}
|
||||
|
|
@ -11,30 +11,15 @@ translation:
|
|||
|
||||
それぞれの「サーバー」側(`mcp.run()` が何をするのか、何をデプロイするのか)については、**[サーバーの実行](../run/index.md)** を参照してください。
|
||||
|
||||
## インメモリ {#in-memory}
|
||||
|
||||
サーバーオブジェクトそのものを渡します。
|
||||
|
||||
```python title="client.py" hl_lines="14"
|
||||
--8<-- "docs_src/client_transports/tutorial001.py"
|
||||
```
|
||||
|
||||
サブプロセスも、ポートも、通信路を流れるバイト列もありません。クライアントとサーバーは同じプロセス内の 2 つのオブジェクトですが、呼び出しは本物のプロトコル層を通ります。`search_books` は、HTTP 越しの場合とまったく同じように一覧に載り、検証され、呼び出されます。
|
||||
|
||||
そのため、これは同時に 2 つの役割を果たします。
|
||||
|
||||
* **テストハーネス。** このドキュメントの例はすべてこの方法で実行されており、**[テスト](../get-started/testing.md)** のページはこのパターンを中心に組み立てられています。
|
||||
* **組み込み用の API。** サーバーを自分で構築するアプリケーションなら、そのツールを呼び出すのにネットワーク越しの経路は必要ありません。
|
||||
|
||||
## Streamable HTTP {#streamable-http}
|
||||
|
||||
URL の文字列を渡すと **Streamable HTTP** になります。デプロイ時に使うトランスポートです。
|
||||
URL の文字列を渡すと **Streamable HTTP** になります。デプロイ時に使うトランスポートであり、最初に選ぶべきトランスポートです。
|
||||
|
||||
```python title="client.py" hl_lines="5"
|
||||
--8<-- "docs_src/client_transports/tutorial002.py"
|
||||
```
|
||||
|
||||
本番用のクライアントはこれですべてです。`Client` は URL を `streamable_http_client(...)` で包み、MCP に必要な設定を施した `httpx2.AsyncClient` の上に載せてくれます。具体的には `follow_redirects=True`、connect/write/pool のタイムアウトが 30 秒、そしてサーバーがレスポンスストリームを開いたままにすることがあるため read のタイムアウトが 300 秒です。
|
||||
本番用のクライアントはこれですべてです。`Client` は URL を `streamable_http_client(...)` で包み、MCP に必要な設定を施した `httpx2.AsyncClient` の上に載せてくれます。具体的には、connect/write/pool のタイムアウトが 30 秒、そしてサーバーがレスポンスストリームを開いたままにすることがあるため read のタイムアウトが 300 秒です。
|
||||
|
||||
!!! check
|
||||
構築しただけの `Client` は接続されて**いません**。構築時に行われるのはトランスポートの選択だけで、実際に開くのは `async with` です。入る前に接続に手を伸ばすと、SDK がそのことを教えてくれます。
|
||||
|
|
@ -49,7 +34,7 @@ URL の文字列を渡すと **Streamable HTTP** になります。デプロイ
|
|||
|
||||
`Authorization` ヘッダー、Cookie、プロキシ、mTLS、あるいは別のタイムアウトが必要になったら、`httpx2.AsyncClient` を自分で組み立てて `streamable_http_client` に渡します。
|
||||
|
||||
```python title="client.py" hl_lines="8-14"
|
||||
```python title="client.py" hl_lines="8-13"
|
||||
--8<-- "docs_src/client_transports/tutorial003.py"
|
||||
```
|
||||
|
||||
|
|
@ -70,7 +55,25 @@ TLS について 1 点。`httpx2` は、同梱の CA リストではなく、オ
|
|||
HTTP に関わるものはすべて、渡す 1 つの `httpx2.AsyncClient` に集約されています。
|
||||
|
||||
!!! info
|
||||
`httpx2` はおなじみの `httpx` の API をそのまま保っているので、`httpx` を知っていれば、認証、プロキシ、イベントフック、リトライ、接続数の制限のやり方はすでに知っていることになります。SDK はその上に何も足さず、何も引きません。OAuth が差し込まれるのもここです。`httpx2.AsyncClient(auth=OAuthClientProvider(...))` のように書きます。そのフロー全体については **[OAuth クライアント](oauth-clients.md)** を参照してください。
|
||||
`httpx2` はおなじみの `httpx` の API をそのまま保っているので、`httpx` を知っていれば、認証、プロキシ、イベントフック、リトライ、接続数の制限のやり方はすでに知っていることになります。SDK はその上に何も足さず、何も引きません。唯一の例外が[リダイレクトの扱い](#redirects)です。OAuth が差し込まれるのもここです。`httpx2.AsyncClient(auth=OAuthClientProvider(...))` のように書きます。そのフロー全体については **[OAuth クライアント](oauth-clients.md)** を参照してください。
|
||||
|
||||
### リダイレクト {#redirects}
|
||||
|
||||
トランスポートは渡された URL に接続し、そのオリジンにだけ接続します。
|
||||
|
||||
* 同じスキーム、ホスト、ポートにとどまる `307`/`308` のリダイレクトには従います。同じホスト上での `http://` → `https://` も同様です。よくある `/mcp` → `/mcp/` という末尾スラッシュのリダイレクトはこれでカバーされます。
|
||||
* それ以外の場所へのリダイレクトには従い**ません**。呼び出しは次のエラーで失敗します。
|
||||
|
||||
```text
|
||||
MCPError: Redirect to https://other.example.com/mcp not followed; use that URL as the endpoint if it is the intended server
|
||||
```
|
||||
|
||||
その URL が意図したサーバーなら、設定にその URL を書いてください。そうでなければ、サーバーか、その前段にあるプロキシの設定が誤っています。
|
||||
|
||||
これは、渡すどの `httpx2.AsyncClient` にも当てはまります。その `follow_redirects` の設定は、MCP のリクエストについてはどちらの方向にも参照されません。SDK の OAuth プロバイダーも、自身のリクエストに同じ規則を適用します。
|
||||
|
||||
!!! tip
|
||||
`Redirect to http://… not followed: it would downgrade this HTTPS endpoint to plain HTTP` は、サーバーが自身の知らない TLS 終端プロキシの背後にあり、`http://` のリダイレクトを発行していることを意味します。これはサーバー側で直すか(**[デプロイとスケール](../run/deploy.md#behind-a-tls-terminating-proxy)**)、メッセージが示すとおりの正確な `https://…/` の URL を使うことで解決します。
|
||||
|
||||
## stdio {#stdio}
|
||||
|
||||
|
|
@ -91,6 +94,18 @@ TLS について 1 点。`httpx2` は、同梱の CA リストではなく、オ
|
|||
|
||||
API キーを必要とするサーバーは、そこでキーを見つけられません。`env=` で明示的に渡してください。それらの変数は許可リストの上にマージされます。上の例で `BOOKSHOP_API_KEY` がしているのがまさにそれです。
|
||||
|
||||
## インメモリ {#in-memory}
|
||||
|
||||
テストでは、デプロイするものも起動するものもありません。サーバーオブジェクトそのものを渡します。
|
||||
|
||||
```python hl_lines="14"
|
||||
--8<-- "docs_src/client_transports/tutorial001.py"
|
||||
```
|
||||
|
||||
サブプロセスも、ポートも、通信路を流れるバイト列もありません。クライアントとサーバーは同じプロセス内の 2 つのオブジェクトですが、呼び出しは本物のプロトコル層を通ります。`search_books` は、HTTP 越しの場合とまったく同じように一覧に載り、検証され、呼び出されます。**[テスト](../get-started/testing.md)** のページは、このパターンを中心に組み立てられています。
|
||||
|
||||
同じ形は組み込み用の API としても使えます。サーバーを自分で構築するアプリケーションなら、ネットワーク越しの経路なしにそのツールを呼び出せます。
|
||||
|
||||
## SSE {#sse}
|
||||
|
||||
`mcp.client.sse` の `sse_client(url)` は、Streamable HTTP に取って代わられた HTTP トランスポートです。まだこれを話すサーバーと対話するには、同じように `Client(sse_client("http://localhost:8000/sse"))` と包みます。そして、新しいものをこの上に作らないでください。
|
||||
|
|
@ -99,15 +114,16 @@ TLS について 1 点。`httpx2` は、同梱の CA リストではなく、オ
|
|||
|
||||
`Client` から見れば、上記はすべて同じものです。
|
||||
|
||||
**トランスポート**とは、`(read, write)` というメッセージストリームのペアを yield する非同期コンテキストマネージャーのことです。正式には `mcp.client` の `Transport` プロトコルです。`Client` は引数を型で解決します。サーバーオブジェクトならインプロセスで接続し、`str` なら `streamable_http_client(url)` になり、`StdioServerParameters` なら `stdio_client(params)` になり、それ以外は直接トランスポートとして入ります。この最後の規則があるからこそ、`stdio_client(...)`、`streamable_http_client(...)`、`sse_client(...)` はすべて同じ場所に収まり、自分で独自のものを書くこともできます。
|
||||
**トランスポート**とは、`(read, write)` というメッセージストリームのペアを yield する非同期コンテキストマネージャーのことです。正式には `mcp.client` の `Transport` プロトコルです。`Client` は引数を型で解決します。`str` なら `streamable_http_client(url)` になり、`StdioServerParameters` なら `stdio_client(params)` になり、サーバーオブジェクトならインプロセスで接続し、それ以外は直接トランスポートとして入ります。この最後の規則があるからこそ、`stdio_client(...)`、`streamable_http_client(...)`、`sse_client(...)` はすべて同じ場所に収まり、自分で独自のものを書くこともできます。
|
||||
|
||||
## まとめ {#recap}
|
||||
|
||||
* `Client(mcp)`(サーバーオブジェクト)はインメモリで接続します。テストと組み込みに使ってください。
|
||||
* `Client("http://.../mcp")`(URL)は、本番用のトランスポートである Streamable HTTP で接続します。
|
||||
* ヘッダー、認証、プロキシ、タイムアウトは、`streamable_http_client(url, http_client=...)` に渡す `httpx2.AsyncClient` に設定します。`headers=` キーワードはありません。
|
||||
* リダイレクトに従うのは、URL 自身のオリジン内(末尾スラッシュの `307`/`308`)と、同じホスト上の `http`→`https` だけです。それ以外は `Redirect to … not followed` で失敗します。最終的な URL を設定してください。
|
||||
* stdio は `Client(StdioServerParameters(...))` です。自分で `stdio_client(...)` に包むのは、子プロセスの stderr をリダイレクトしたいときだけです。
|
||||
* サブプロセスが受け取るのは自分の環境ではなく、許可リストに基づく環境です。`env=` でそこに追加します。
|
||||
* `Client(mcp)`(サーバーオブジェクト)はインメモリで接続します。テストで使うか、サーバーを構築したアプリケーションにそのサーバーを組み込むために使ってください。
|
||||
* トランスポートとは、`async with x as (read, write)` と書けるものすべてです。`Client` は、サーバーオブジェクトでも URL でも `StdioServerParameters` でもないものを、そのままこのプロトコルに渡します。
|
||||
* `Client` の構築でトランスポートが選ばれ、`async with` でそれが開かれます。
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue