LLM アプリ開発 #11 MCP でツールを接続する

読了 5分

第6回第10回で、ツールを直接定義して Claude につなぎました。ところが GitHub、Slack、データベースのようによく使う対象なら、毎回ツール定義を手で書くのは面倒です。MCP はこの接続を標準化した規約です。今回は MCP とは何でなぜ使うのか、そして Claude を MCP サーバーに接続する方法を扱います。

MCP とは #

MCP(Model Context Protocol)は、LLM がツールやデータにアクセスする方法を標準化した規約です。USB を思い浮かべると分かりやすいです。機器ごとに違う接続端子を使っていた時代は、機器とケーブルをいちいち合わせる必要がありましたが、USB という標準ができてから、一つの方式で全部つなげるようになりました。MCP がツール接続でそういう役割を果たします。

構造は二つの側に分かれます。

  • MCP サーバー — ある機能やデータをツールとして公開する側です。GitHub、ファイルシステム、データベースのような対象ごとに MCP サーバーがあります。
  • MCP クライアント — そのサーバーに接続してツールを取り込んで使う側です。私たちのアプリがクライアントになります。

肝心なのはこうです。誰かが GitHub 用の MCP サーバーを一度作っておけば、そのサーバーに接続するすべてのアプリが、GitHub のツールを手で定義し直さずそのまま取り込んで使います。

なぜ MCP を使うのか #

第6回の方式でもツールはいくらでも作れます。ところが MCP には二つの利点があります。

  • 再利用 — すでに作られた MCP サーバーがたくさんあります。GitHub、Slack、ファイルシステム、さまざまなデータベース用のサーバーが公開されていて、取り込んで接続するだけで済みます。ツール定義や実際の呼び出しコードを自分で書く必要がありません。
  • 標準 — ツールを一度 MCP サーバーにしておけば、MCP に対応するどのアプリでも使えます。私たちのアプリだけでなく、他のツールや環境でも同じサーバーが動きます。

直接ツールを定義する第6回の方式と MCP は対立しません。私たちのアプリだけにある特殊な機能は第6回のように直接作り、GitHub のような汎用機能は MCP サーバーを取り込んで使う、という具合に混ぜます。

Claude を MCP サーバーに接続する #

SDK は、MCP サーバーのツールを取り込んで Claude に接続するヘルパーを提供します。MCP サーバーに接続してツールの一覧を受け取り、第10回で見たツールランナーにそのまま渡す方式です。

mcp_connect.py
from anthropic import AsyncAnthropic
from anthropic.lib.tools.mcp import async_mcp_tool
from mcp import ClientSession
from mcp.client.stdio import stdio_client, StdioServerParameters

client = AsyncAnthropic()

async def main():
    # MCP サーバーに接続する
    async with stdio_client(StdioServerParameters(command="mcp-server")) as (read, write):
        async with ClientSession(read, write) as mcp:
            await mcp.initialize()

            # サーバーが提供するツールの一覧を取得する
            tools = await mcp.list_tools()

            # 取得したツールをツールランナーに渡す
            runner = client.beta.messages.tool_runner(
                model="claude-sonnet-4-6",
                max_tokens=1024,
                messages=[{"role": "user", "content": "使えるツールで作業を処理してください。"}],
                tools=[async_mcp_tool(t, mcp) for t in tools.tools],
            )
            async for message in runner:
                print(message)

mcp.list_tools() でサーバーが公開したツールの一覧を受け取り、async_mcp_tool でそのツールを Claude が使える形に変えてツールランナーに渡します。私たちはツールの名前や入力スキーマを直接書いていません。サーバーが教えてくれるからです。第6回で手で書いた tools の定義が、MCP ではサーバー接続に置き換わります。

リモートの MCP サーバーに接続する #

上の例は、私たちが直接実行した MCP サーバーに接続しました。ところが、インターネットにすでに立っているリモートの MCP サーバーに、Claude を直接接続することもできます。このときは呼び出しに mcp_servers でサーバーのアドレスを伝えます。

mcp_remote.py
response = client.beta.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    mcp_servers=[
        {"type": "url", "name": "my-tools", "url": "https://example.com/mcp"},
    ],
    betas=["mcp-client-2025-11-20"],
    messages=[{"role": "user", "content": "MCP のツールで作業を処理してください。"}],
)

サーバーのアドレスだけ渡せば、Claude がそのサーバーのツールを自分で取り込んで使います。ローカルでサーバーを実行する必要も、ツールの一覧を自分で取得する必要もありません。ただし認証が必要なサーバーなら認証情報も一緒に渡す必要があり、その方法はサーバーごとに異なります。この機能もベータなので、適用する前に最新のドキュメントを確認します。

注記
MCP クライアント機能は現在ベータで、mcp パッケージが必要です(pip install anthropic[mcp])。MCP サーバーを直接実行せずリモートの MCP サーバーに接続する方法など、細部は速く発展しているので、実際に適用する前に最新のドキュメントを確認するのがよいです。この記事の目的は「ツールをサーバー接続で取り込む」という発想を理解することにあります。

よくつまずくところ #

  • MCP がツールを置き換えると誤解する — MCP はツールを接続する標準にすぎず、第6回のツール呼び出しをなくすものではありません。内部的には同じツール呼び出しの流れが回ります。
  • 信頼できないサーバーに接続する — MCP サーバーは私たちのアプリにツールを公開します。出所の不明なサーバーに接続すると、そのサーバーが何をするのか分かりにくいです。信頼できるサーバーだけ使います。
  • 認証をコードに埋め込む — GitHub のようなサーバーは認証が必要です。トークンをコードに直接書かず、第1回のように環境変数や別の認証情報ストアで扱います。

まとめ #

今回は、ツール接続の標準である MCP を扱いました。

  • MCP は LLM がツールやデータにアクセスする方法を標準化した規約で、サーバーはツールを公開し、クライアントはそれを取り込んで使います。
  • すでに作られた MCP サーバーを接続すれば、ツール定義を直接書かずそのまま再利用できます。
  • アプリ独自の特殊機能は直接ツールとして作り、汎用機能は MCP で取り込む、という具合に混ぜます。

これでツール、検索、メモリ、エージェント、MCP まで核心のかけらが集まりました。次回の「LLM アプリ開発 #12 コスト・評価・観測」では、作ったアプリを実際に運用するときに必要なもの、つまりコストを減らし品質を測り動作を見ていく方法を扱います。

X