この記事ではMCPに特化して解説します。MCP(Model Context Protocol)全般は MCPサーバーの作り方2026完全ガイド をご覧ください。

「MCP is all you need」とは何か:講演の背景

2026年4月、Pydanticの作者Samuel ColvinがAI Engineerカンファレンスで行った15分間の講演「MCP is all you need」がX上で大きな注目を集めた。

Pydanticは月3.6億ダウンロード(1秒あたり140回)という規模で使われているPythonデータバリデーションライブラリだ。

Colvinは2023年にPydanticを会社化し、現在は次の3つのプロダクトを展開している:

プロダクト 概要
Pydantic Python向けデータバリデーションライブラリ(OSS)
PydanticAI Pydanticの設計原則で作られたPythonエージェントフレームワーク
Pydantic Logfire AIアプリ向けオブザーバビリティプラットフォーム(商用)

加えて、ColvinはMCP Python SDKのco-maintainerでもある。つまり、今回の講演はMCPの設計者に近い立場からの「正しい使い方」の解説だ。

タイトルはJakub Löwit氏の過去講演「Pydantic is all you need」「Pydantic is still all you need」をオマージュしたものであり、核心にある主張は同じだ:「みんなが複雑にしすぎている。シンプルな道具で十分なことが多い。」


MCPの3つのプリミティブと「自律エージェント」での使い分け

MCPには3つの基本プリミティブがある:

graph LR A["MCPの3プリミティブ"] --> B["Prompts
(プロンプトテンプレート)"] A --> C["Resources
(ファイル・データ)"] A --> D["Tools
(関数呼び出し)"] B -->|"主な対象"| E["Cursor / Claude Desktop
型のコーディングエージェント"] C -->|"主な対象"| E D -->|"主な対象"| F["自律エージェント
(Pythonコード)"]

ColvinがこのトークでスコープにしているのはCursorやClaude Desktopのような「コーディングエージェント」ではなく、自分でコードを書いて動かす自律エージェントだ。

この文脈では、PromptとResourcesはあまり関係せず、Tool calling(ツール呼び出し)が圧倒的に重要になる。

なぜOpenAPIではなくMCPか?

よく聞かれる質問に「MCPって結局OpenAPIじゃダメなの?」がある。Colvinが挙げた理由:

MCPがOpenAPIで置き換えられない4つの理由

  1. Dynamic tools:エージェント実行中にツールが動的に増減できる
  2. Logging:ツール実行中に途中経過をクライアントへ返せる
  3. Sampling:MCP サーバーからクライアント経由でLLMを呼べる
  4. Stdio transport:サブプロセスとして起動しstdin/stdoutで通信できる

中でもSamplingは最も強力で最も誤解されやすい機能だとColvinは強調している。


最重要機能「Sampling」の仕組みを完全理解する

Samplingは「MCP界で最も命名が混乱している機能」とColvin自身が認めている。 しかし、理解すれば自律エージェントのアーキテクチャが根本的に変わる。

Samplingなしの問題点

MCPなしの典型的なマルチエージェント構成では、すべてのエージェントが個別にLLMアクセスを必要とする

graph TD A["メインエージェント
(LLM: GPT-4o)"] -->|"ツール呼び出し"| B["サブエージェントA
(LLM: 別途必要)"] A -->|"ツール呼び出し"| C["サブエージェントB
(LLM: 別途必要)"] B -->|"さらに呼び出し"| D["ツール群"] C -->|"さらに呼び出し"| D style B fill:#f9a,stroke:#f00 style C fill:#f9a,stroke:#f00

問題は3つある:

  • リモートMCPサーバーがLLMを使うとコスト管理が困難
  • 各サブエージェントのAPIキー設定が必要
  • LLMプロバイダーが増えると認証・課金が複雑化

SamplingでLLM呼び出しを委譲する

Samplingは「MCPサーバーがクライアント経由でLLMを使う仕組み」だ:

sequenceDiagram participant App as アプリ(クライアント) participant LLM as LLM(GPT-4o等) participant Server as MCPサーバー App->>LLM: エージェントクエリ実行 LLM-->>App: ツール呼び出し要求(MCPサーバーA) App->>Server: ツール実行 Server->>App: LLM呼び出しリクエスト(Sampling) App->>LLM: Samplingリクエストをプロキシ LLM-->>App: Sampling応答 App-->>Server: LLM応答を転送 Server-->>App: ツール結果返却 App->>LLM: 次のステップへ

MCPサーバー(ツール)が自身のLLMを持たなくても、メインエージェントのLLMを「間借り」できる。

PydanticAIはこのSamplingをクライアントとしてもサーバーとしてもサポートしている(講演時点ではPRマージ直前)。


PydanticAI + FastMCPの実装デモ:PyPIダウンロード統計エージェント

ColvinはPydanticAI + FastMCPを使ったデモを紹介した。BigQueryのPyPI公開データセットからパッケージのダウンロード数を調べるリサーチエージェントだ。

MCPサーバー側:FastMCPでツール定義

# pypi_mcp_server.py
from mcp.server.fastmcp import FastMCP
from pydantic_ai import Agent
from pydantic_ai.models.openai import OpenAIModel
from google.cloud import bigquery

mcp = FastMCP("pypi-stats-server")

@mcp.tool()
async def pypi_downloads(
    ctx: MCPContext,
    question: str,
) -> str:
    """PyPIパッケージのダウンロード統計をBigQueryで照会する。
    
    ユーザーの自然言語の質問をBigQuery SQLに変換し、
    PyPI公開データセットを照会してダウンロード数を返す。
    """
    # MCPコンテキスト経由でログ送信(ツール実行中にクライアントへ返る)
    await ctx.log("info", f"BigQuery照会を開始: {question}")
    
    # 内部でPydanticAIエージェントを動かす(Samplingを使用)
    result = await _run_query_agent(ctx, question)
    return result

async def _run_query_agent(ctx: MCPContext, question: str) -> str:
    """内部エージェント:クライアントのLLMをSampling経由で使用"""
    agent = Agent(
        model=ctx.sampling,  # メインエージェントのLLMを借用
        system_prompt=BIGQUERY_SYSTEM_PROMPT,
        result_type=QueryResult,
        result_validator=validate_and_run_query,
    )
    result = await agent.run(question)
    return format_as_xml(result.data)

if __name__ == "__main__":
    mcp.run()  # デフォルトはstdio transport

OutputValidatorでSQLエラーを自動リトライ

Colvinのデモで最も注目すべきポイントは「エラー時の自動リトライ」だ。

from pydantic_ai import ModelRetry

async def validate_and_run_query(
    ctx: RunContext[MCPContext],
    result: str
) -> str:
    """生成されたSQLを検証・実行。失敗時はModelRetryでLLMに再生成を依頼"""
    # LLMがmarkdownコードブロックで囲んだ場合は除去
    sql = strip_markdown_fences(result)
    
    # テーブル名の検証
    if WRONG_TABLE in sql:
        raise ModelRetry(
            f"テーブル名が間違っています。正しくは: {CORRECT_TABLE}"
        )
    
    # BigQueryでSQLを実際に実行
    client = bigquery.Client()
    try:
        rows = list(client.query(sql).result())
        return format_rows_as_xml(rows)
    except Exception as e:
        # 実行失敗時は自動リトライ
        raise ModelRetry(f"SQLエラー: {e}\n再度試してください。")

ModelRetryraiseするだけで、PydanticAIがLLMに「やり直し」を指示する。これはPydanticのバリデーション設計思想をエージェントに応用したものだ。

メインエージェント側:MCPサーバーをツールとして登録

# main_agent.py
from pydantic_ai import Agent
from pydantic_ai.mcp import MCPServerStdio
from datetime import date

# MCPサーバーをサブプロセスとして起動・登録
research_agent = Agent(
    model="openai:gpt-4o",
    mcp_servers=[
        MCPServerStdio("python", ["pypi_mcp_server.py"])
    ],
    system_prompt=f"""
    あなたはPyPIパッケージの調査エージェントです。
    今日の日付: {date.today()}
    """,
)

# 実行
result = await research_agent.run(
    "Pydanticは今年何回ダウンロードされましたか?"
)
print(result.data)
# → "Pydantic は 2026年に約 1.6 billion 回ダウンロードされています。"

コンテキストウィンドウ最適化:ツール内推論パターン

Colvinが強調したもう一つの重要ポイントが「ツール内でLLM推論を実行することでメインエージェントのコンテキストを節約する」設計だ。

やりがちなアンチパターン:全コンテキストをメインエージェントに積み込む
BigQueryのスキーマ情報・SQL生成ルール・テーブル定義をすべてメインエージェントのシステムプロンプトに含めると、そのツールを使わない場面でも常にコンテキストを消費し続ける。

推奨パターンとアンチパターンの比較:

  アンチパターン 推奨パターン
SQLスキーマ情報 メインエージェントのプロンプトに含める MCPサーバー内部に隠蔽
SQL生成ロジック ツールの説明文に詰め込む サーバー内エージェントが担当
エラーハンドリング メインエージェントが判断 ModelRetryで自動処理
コンテキスト消費 ツール呼び出しの有無に関わらず消費 実際の呼び出し時のみ消費

「ツール説明文にたくさんの情報を詰め込むと、モデルはあまり好まない。それ以上に、常にコンテキストウィンドウを無駄遣いする」 — Samuel Colvin

MCPサーバー内でSamplingを使って内部エージェントを動かすことで、メインエージェントは「自然言語でツールに質問する」だけでよくなる。


MCPのLogging機能:ツール実行中のリアルタイム進捗

MCPにはctx.log()というLogging機能がある。これはツール実行が完了する前にクライアントへ情報を送れる仕組みだ。

@mcp.tool()
async def long_running_research(ctx: MCPContext, query: str) -> str:
    await ctx.log("info", "データソースを検索中...")
    results_a = await search_source_a(query)
    
    await ctx.log("info", f"ソースAから{len(results_a)}件取得。分析中...")
    analysis = await analyze(results_a)
    
    # progressノーティフィケーションも使える
    await ctx.report_progress(current=50, total=100)
    
    await ctx.log("info", "最終レポートを生成中...")
    return generate_report(analysis)

元々はCursorのようなUIで「まだ処理中ですよ」と伝えるために設計されたが、Webアプリケーションでのストリーミング更新にも活用できる。数分かかる処理でも、ユーザーに進捗を見せながら実行できる。


Pydantic Logfireでのオブザーバビリティ:MCP呼び出しの可視化

ColvinはPydantic Logfireを使ったトレーシングのデモも見せた。エージェント実行の全フローが可視化される:

graph LR A["メインエージェント
(GPT-4o呼び出し)"] -->|"ツール呼び出し判断"| B["MCPクライアント処理"] B -->|"pypi_downloads実行"| C["MCPサーバー"] C -->|"Sampling: SQL生成"| D["内部エージェント
(クライアント経由でLLM)"] D -->|"BigQuery実行"| E["クエリ結果(XML)"] E -->|"ツール結果返却"| A A -->|"最終回答生成"| F["ユーザーへの回答"]

Logfireのダッシュボードでは:

  • どのレイヤーでLLM呼び出しが起きたか
  • 実際に生成されたSQLクエリ
  • 各ステップのレイテンシ
  • Samplingのクライアント⇔サーバー往復

…がすべてトレースとして記録される。MCPのオブザーバビリティはMCPサーバーの作り方2026年完全ガイドでも触れているが、Logfireと組み合わせることで特に強力になる。


まとめ:Colvinが示したMCPの正しい設計原則

Colvinの講演から抽出できる設計原則をまとめる:

Samuel Colvinが示したMCPアーキテクチャの4原則

  1. ツール内に推論を閉じ込める:メインエージェントのコンテキストを汚染しない
  2. Samplingでコスト集約:すべてのLLM呼び出しをメインクライアント経由に集約
  3. ModelRetryでエラー自動回復:バリデーションとリトライをLLMに委ねる
  4. Loggingで進捗をストリーミング:長時間処理でもユーザー体験を損なわない

MCPを単なる「ツール呼び出しプロトコル」として使うのは勿体ない。Samplingを活用したマルチエージェント設計は、コスト・複雑性・コンテキスト効率の3点で従来のアーキテクチャを凌駕する。

AIエージェントフレームワーク比較2026年版でもPydanticAIは注目フレームワークの一つとして取り上げているが、MCP+Sampling対応が整った今、実用プロジェクトへの導入を検討する価値がある。


参照ソース