0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Claude API の MCP コネクタ(mcp_servers)でリモート MCP サーバーを直接呼ぶ実装手順 — mcp_toolset の書き忘れで 400・beta ヘッダー刷新・ツール全読み込み、3つのハマりどころ【2026】

0
Posted at

はじめに / 対象と前提

リモートの MCP サーバー(Model Context Protocol サーバー)を、Claude Code などのクライアントを経由せず Messages API から直接 呼びたい場面がある。バックエンドのワーカーから「社内の MCP サーバーのツールを Claude に使わせたい」といったケースだ。

Claude API にはこれ専用の MCP コネクタ がある。mcp_servers にサーバーの URL を書くと、Anthropic 側がそのサーバーへ接続してツールを呼んでくれる。自分でツールループを書かなくていい。

  • 想定読者:Claude API でツール実行を組んだことがあり、MCP を自前のバックエンドから使いたい人
  • 前提環境:Python 3.10+ / anthropic SDK 1.x / モデルは claude-opus-5
  • 前提:呼び出し先は URL でアクセスできるリモート MCP サーバー。ローカルの stdio サーバーはこの方式では呼べない(後述)

自分は記憶していた古い書き方でそのまま投げて 400 を食らった。以下はその実録。

TL;DR

  • mcp_serverstoolsmcp_toolset は 2 つで 1 セット。片方だけだと validation error で落ちる
  • beta フラグは mcp-client-2025-11-20。記憶にある mcp-client-2025-04-04tool_configuration は旧仕様
  • 何もしないと サーバーの全ツール定義が毎リクエストの入力トークンに乗るdefault_config + configs で allowlist 化して絞る

手順 / 動かし方

1. 最小構成

MCP コネクタは beta なので、client.messages ではなく client.beta.messages を使う。ここを間違えると mcp_servers が未知パラメータ扱いになる。

import anthropic

client = anthropic.Anthropic()  # ANTHROPIC_API_KEY を環境変数から読む

response = client.beta.messages.create(
    model="claude-opus-5",
    max_tokens=16000,
    betas=["mcp-client-2025-11-20"],
    mcp_servers=[
        {
            "type": "url",
            "url": "https://mcp.example.internal/sse",
            "name": "example-mcp",
        }
    ],
    tools=[
        {"type": "mcp_toolset", "mcp_server_name": "example-mcp"},
    ],
    messages=[
        {"role": "user", "content": "在庫が 10 個を切っている商品を一覧にして"}
    ],
)

for block in response.content:
    print(block.type)

必須は 3 点。betas にフラグを付ける / mcp_servers に接続先を定義する(name は自分で決める識別子)/ toolsmcp_toolset を入れて 同じ namemcp_server_name で指す。

実行するとブロックがこう並ぶ。

thinking
mcp_tool_use
mcp_tool_result
text

mcp_tool_use / mcp_tool_resultサーバー側で完結して返ってくる。通常の tool_use と違い、自分でループを回して結果を返す必要がない。stop_reasontool_use にならず、1 リクエストで答えまで返る。

2. 認証付きサーバーに繋ぐ

トークンが要るサーバーは authorization_token を足す。当然だがベタ書きはしない。

import os

mcp_servers = [
    {
        "type": "url",
        "url": "https://mcp.example.internal/sse",
        "name": "example-mcp",
        "authorization_token": os.environ["EXAMPLE_MCP_TOKEN"],
    }
]

このトークンは Anthropic のサーバーから MCP サーバーへ送られる。裏を返すと、社外に出したくないエンドポイントには使えない

3. 結果ブロックを取り出す

型で分岐して拾う。

for block in response.content:
    if block.type == "mcp_tool_use":
        print("call:", block.name, block.input)
    elif block.type == "mcp_tool_result":
        # is_error が立つことがあるので必ず見る
        print("result:", block.content, "error:", block.is_error)
    elif block.type == "text":
        print(block.text)

MCP サーバー側でツールが失敗しても 例外は飛ばない。HTTP 200 で返ってきて is_error が立つだけなので、try/except だけ書いて満足していると失敗を取りこぼす。Web 検索などのサーバーサイドツールと同じ挙動だ。

ハマりどころ

1. mcp_servers だけ書くと 400 で落ちる

一番最初に踏んだのがこれ。tools を省いて投げると、

anthropic.BadRequestError: Error code: 400 - invalid_request_error

「サーバーを登録したんだから使ってくれるだろう」と思うが、そうはならない。tools 側に mcp_toolset を置いて、そのサーバーを明示的に参照する必要がある。

ルールは 2 つ。

  • mcp_server_namemcp_serversname完全一致 させる
  • mcp_servers に登録したサーバーは、すべて ちょうど 1 つの toolset から参照されていること

サーバーを 2 つ繋ぐなら toolset も 2 つ書く。片方を忘れると同じエラーになる。

mcp_servers=[
    {"type": "url", "url": "https://a.example/sse", "name": "srv-a"},
    {"type": "url", "url": "https://b.example/sse", "name": "srv-b"},
],
tools=[
    {"type": "mcp_toolset", "mcp_server_name": "srv-a"},
    {"type": "mcp_toolset", "mcp_server_name": "srv-b"},  # これを忘れると 400
],

エラー文からは「toolset が足りない」とまで読み取れないので、400 が出たらまず 両者の数と名前が一致しているか を数えるのが早い。

2. beta フラグと tool_configuration が古い

記憶や古い記事に残っている書き方が、そのままだと通らない。

古い書き方 現在
mcp-client-2025-04-04 mcp-client-2025-11-20
tool_configuration をサーバー定義の中に置く toolsmcp_toolsetdefault_config / configs

「サーバー定義の内側でツールを絞る」構造から「toolset 側で絞る」構造に変わっている。ここを混ぜると、フラグは通ったのに絞り込みだけ無視される、という分かりにくい状態になる。Go SDK なら定数も anthropic.AnthropicBetaMCPClient2025_11_20 が現行で、...2025_04_04 は非推奨。

確認方法:response.usage.input_tokens を見て、絞ったつもりなのに数字が減っていなければ旧仕様が無視されている。

3. 何もしないと全ツールが入力トークンに乗る

mcp_toolset を素で書くと、そのサーバーが公開している 全ツールの定義(名前 + 説明 + JSON Schema)がリクエストに載る。5 個なら気にならないが、40〜50 個あるサーバーだと入力トークンが一気に膨らむ。毎リクエスト課金されるので地味に効く。

対策は allowlist モード。default_config で全部オフにして、使うものだけ configs で開ける。

tools=[
    {
        "type": "mcp_toolset",
        "mcp_server_name": "example-mcp",
        "default_config": {"enabled": False},   # まず全部オフ
        "configs": {                            # 使うものだけオン
            "search_inventory": {"enabled": True},
            "get_product": {"enabled": True},
        },
    }
],

効果は response.usage.input_tokens を絞る前後で比べれば分かる。副次効果として ツールの選択精度も上がる。似た名前のツールが 40 個並ぶより 2 個のほうが迷わない。「動くけど時々違うツールを呼ぶ」症状が出ていたら、まずここを疑うといい。

背景・補足

この方式を選ばないほうがいいケース

MCP コネクタは「Anthropic のサーバーが MCP サーバーへ接続しにいく」構造なので、向き不向きがある。

やりたいこと MCP コネクタ 自前 MCP クライアント
リモート(URL)サーバーを手軽に繋ぐ
ローカルの stdio サーバーを繋ぐ
MCP の Prompts / Resources も使う
外部に出せないエンドポイント

後者をやりたいなら、Python SDK に MCP をツール定義へ変換するヘルパー(anthropic.lib.tools.mcp)があり、pip install "anthropic[mcp]" で入る。接続を自分のプロセスが持つので、ローカルサーバーも社内限定エンドポイントも扱える。

プラットフォーム可用性

MCP コネクタは Claude API では beta として使える が、クラウドプロバイダ経由だと状況が違う。Amazon Bedrock と Google Vertex AI では現時点で利用できない。「ローカルでは動いたのに Bedrock 経由の本番で落ちる」事故になりやすいので、デプロイ先を決める前に対応表を確認しておきたい。

まとめ

  • MCP コネクタは mcp_servers + toolsmcp_toolsetセットで 書く。片方だけは 400
  • beta フラグは mcp-client-2025-11-20。絞り込みは tool_configuration から toolset 側の default_config / configs へ移った
  • デフォルトでは全ツールが入力トークンに乗る。全部オフにしてから使うものだけ開けると、コストもツール選択精度も改善する
  • ツールのエラーは例外ではなく is_error で返る。必ずブロックを見る
  • ローカル stdio サーバーや外に出せないエンドポイントは、この方式ではなく自前の MCP クライアントで
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?