ブログ • AI / Agent
2026 AI Agent 技術スタック:LLM、MCP、Function Calling、JSON Schema はどう組み合わせる?
AI Agent を組むと、ドキュメントに LLM、MCP、Function Calling、JSON Schema が同時に出てきます。四つとも「モデルにツールを呼ばせる」ように見え、四択になりがちです。
四つの代替案ではありません。LLM は推論し、Function Calling は意思決定を実行可能な JSON にシリアライズし、MCP は外部サービスに接続して構造化結果を返し、JSON Schema は三層を貫く契約言語です。
本記事では次を説明します。
- 各層が持つべきもの、持ってはいけないもの
- 一度のツール呼び出しがモデルから MCP へ行き、戻る流れ
- Function Calling の parameters と MCP の inputSchema を揃える方法
- 実装できる参照アーキテクチャと、層を取り違えたときの壊れ方
- この経路上の JSON を JSONNote でローカルデバッグする方法
まずこれを覚えてください:2026 年の Agent スタックは「JSON が要るか」ではなく「意思決定・実行・契約をどの層に置くか」を議論します。モデルが考え、Function Calling がシリアライズし、MCP が実行し、JSON Schema が各ホップを検証します。以下、責務 → データフロー → 各層 → 組み合わせ → 取り違えの順です。
四層は何をするか
先に責務を分けます。混ざるとデバッグは「モデルが悪い」「MCP が壊れた」になり、多くはどちらでもなく境界が無いだけです。
| 層 | 責務 | 入力 | 出力 |
|---|---|---|---|
| LLM | 推論し、計画し、次の一手を決める | 会話、ツール結果、システムプロンプト | 会話を続けるか、「このツールを呼ぶ」 |
| Function Calling | 「どのツール、どの引数」を API オブジェクトにする | tools array + conversation | tool_calls[].arguments JSON |
| MCP | ツール発見、実行、構造化結果の返却 | tools/call + arguments | structuredContent JSON |
| JSON Schema | 各ホップの JSON を記述し検証する | schema 文書 | 成功 / 失敗理由 |
一言:LLM が考え、Function Calling が言い、MCP が行い、JSON Schema が検証する。
JSON Schema の書き方、Tool Calling と Structured Output の違いはAI Agent に JSON Schema が必要な理由を参照。本記事は四層のつなぎ方だけを扱います。
一度のツール呼び出しのデータフロー
「チケット番号で照会」を例にします。ユーザーが「T-1042 を見て」と言っても、Runtime はその文をチケットシステムに投げません。次の六ステップを踏みます。
-
1
Runtime がリクエストを組み立てる
会話、システムプロンプト、tools 配列を LLM API に送ります。各 tool の parameters は JSON Schema です。
-
2
モデルが Function Calling する
返すのは name=get_ticket と JSON 文字列の arguments であり、「調べてください」という自然文ではありません。
-
3
Runtime が入参を検証する
json.loads のあと、同じ schema で validate します。欠落や型違いは再試行か拒否。汚れた引数を下流に送らない。
-
4
MCP が実行する
検証済み arguments を MCP tools/call に入れます。Server が照会し structuredContent を返します。
-
5
Runtime が出参を検証する
structuredContent は MCP outputSchema に適合する必要があります。通ってからモデルへ戻します。
-
6
モデルが最終回答を出す
自然言語でも、別 schema の Structured Output で最終 JSON を縛ってもよい。
validate は二回です。モデルの乱参を一度、Server の乱返却を一度。どちらかを省くと、次層は汚れたデータを事実として扱います。
LLM:推論と意思決定だけ
LLM はスタックで唯一「考える」層です。文脈を読み、回答・追加質問・ツール呼び出しを決めます。データベース、決済 API、ファイルシステムには直接触れません。
2026 年、各社のモデル API は同じ意味論に揃っています。tools を渡し、モデルは tool_calls か最終テキストを返します。
OpenAI はFunction Calling / Responses API、Anthropic はTool Use、DeepSeek は OpenAI のフィールド名に合わせます。差はフィールド名と Strict 対応であり、「構造化すべきか」ではありません。
LLM を実行器にすると、秘密が prompt に入り、副作用を監査できません。実行は MCP に残し、モデルは「誰を、どの引数で呼びたいか」だけを出します。
Function Calling:モデル API 層のツール選択
Function Calling はプロトコルではなく、モデル API の応答形です。次の一手は機械可読の「関数名 + 引数」でなければならず、散文ではありません。
{
"model": "gpt-5",
"messages": [
{"role": "user", "content": "Look up ticket T-1042"}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_ticket",
"description": "Fetch a support ticket by id",
"parameters": {
"type": "object",
"properties": {
"ticketId": {
"type": "string",
"pattern": "^T-[0-9]+$"
}
},
"required": ["ticketId"],
"additionalProperties": false
}
}
}
]
}
返る arguments は文字列であり、パース済みオブジェクトではありません。Runtime が自分で loads + validate します。Strict Schema でも同じです。API 層の conform は業務検証の代わりになりません。これは2026 Structured Outputの response_format と同じ種類の問題で、拘束対象が最終回答からツール引数に変わっただけです。
Function Calling はここで終わります。ツールの実装や結果の形は知りません。arguments を誰に渡すかは Runtime の仕事です。
MCP:Agent と外界のプロトコル
MCP(Model Context Protocol)は Agent Runtime と外部サービスの標準通話です。ツール一覧、呼び出し、構造化結果。2026-07-28 で session ハンドシェイクは消え、各リクエストは自己記述なので、Server は普通の HTTP と同様に負荷分散できます。背景はAI Conference 2026 の注目点.
{
"name": "get_ticket",
"description": "Fetch a support ticket by id",
"inputSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"ticketId": {
"type": "string",
"pattern": "^T-[0-9]+$"
}
},
"required": ["ticketId"]
},
"outputSchema": {
"type": "object",
"properties": {
"ticketId": { "type": "string" },
"status": { "type": "string", "enum": ["open", "pending", "closed"] },
"assignee": { "type": ["string", "null"] }
},
"required": ["ticketId", "status"]
}
}
Runtime は Function Calling で検証済みの arguments をそのまま tools/call に入れます。Server の structuredContent は outputSchema を通る必要があります。Client 側でもう一度 validate——データ源がモデルから Server に変わっただけで、工学上の問題は同じです。
公式ツール仕様:MCP Tools。inputSchema の根は依然 object。2026-07-28 以降、outputSchema は任意の JSON 値でよい。
JSON Schema:三層を貫く契約
JSON Schema は第四の「機能モジュール」ではなく、他三層が共有する記述言語です。現在の主流は2020-12.
| 出現位置 | フィールド | 何を拘束するか | 誰が検証するか |
|---|---|---|---|
| Function Calling | tools[].function.parameters | モデルが生成した arguments | Runtime(MCP 呼び出し前) |
| Structured Output | response_format.json_schema | モデルの最終回答 | Runtime(業務へ渡す前) |
| MCP 入力 | inputSchema | tools/call の arguments | Runtime + Server |
| MCP 出力 | outputSchema | structuredContent | Runtime(モデルへ戻す前) |
組み合わせで最も多い落とし穴:parameters と inputSchema を別々に書き、フィールド名や enum がずれる。モデルは A で生成し、Server は B で検証し、「モデルがいつもツールに失敗する」ように見えます。
正しいやり方は一つの契約、二か所へのマウント。同じ schema オブジェクトから parameters と inputSchema を生成します。最終回答も下流に渡すなら Structured Output 用を別に用意し、ツール入力と混ぜない。
参照アーキテクチャ:四層のつなぎ方
本番の既定のつなぎ方は次のとおりです。ローカル関数や自前 HTTP API を当面 MCP の代わりにできますが、境界は動かさないでください。
| コンポーネント | すること | しないこと |
|---|---|---|
| LLM API | 推論し、tool_calls または最終 JSON を返す | 秘密を持たず、内部システムへ直接アクセスしない |
| Agent Runtime | tools を組み立て、検証し、MCP へ経路し、回填し、ループする | 自然言語を引数にせず、validate を飛ばさない |
| MCP Server | 副作用を実行し、outputSchema に合う JSON を返す | prompt を解析せず、モデルの代わりに計画しない |
| Schema カタログ | 単一ソース。API tools と MCP の両方に供給 | 二か所に「ほぼ同じ」JSON を手書きしない |
schema = catalog.get("get_ticket") # single source
req.tools = [{ "type": "function",
"function": { "name": "get_ticket",
"parameters": schema.input } }]
resp = llm.chat(req)
args = json.loads(resp.tool_calls[0].arguments)
validate(args, schema.input) # stop bad model args
content = mcp.call("get_ticket", args) # MCP executes
validate(content.structuredContent, schema.output)
req.messages.append(tool_result(content)) # feed back, next turn
ループは何周でもよい。モデルは structuredContent を見て次の tool か最終回答を決めます。Schema カタログは変わりません。増えるのは messages に積もるツール結果です。
層を取り違えるとどうなるか
四層が組み合わさるのは、各層が一件だけをするからです。責務が隣へ滑ると、障害はモデル能力の問題に見えます。
| 誤り | 表面上 | 実際の結果 |
|---|---|---|
| LLM に直接 HTTP を打たせる | フレームワークが一つ減り、早く出せる | 秘密が文脈に入り、監査できず、モデルを替えるたびに呼び出しを書き直す |
| MCP を Function Calling の代わりに使う | プロトコルにも name / arguments がある | モデルは tools 一覧を見ないので、安定して選べない |
| prompt だけで JSON を求め、schema を書かない | デモは動く | 回帰できず、フィールドが流れ、下流の parse が毎日壊れる |
| parameters と inputSchema を別々に書く | どちらも「動く」 | enum / 必須がずれ、ランダムな tool 失敗になる |
| モデル出力を信じ、validate を飛ばす | Strict / JSON Mode を入れた | 欠落、切り詰め、型エラーが業務に直撃する |
判断の口訣:モデルは意思決定 JSON だけを出し、実行は MCP(または明示したバックエンド)だけ、出入りに schema が要る。
JSONNote でこの経路をデバッグする
この経路には少なくとも三つの JSON があります。モデル arguments、MCP structuredContent、最終 Structured Output。どれもローカルで format / validate / diff でき、Key もサンプルもアップロード不要です。
-
1
arguments を整形する
tool_calls.arguments をJSON フォーマッタに貼り、まず構文壊れと切り詰めを除外します。
-
2
同じ schema で検証する
parameters / inputSchema と arguments をJSON Schemaに貼る。失敗点が Runtime の拒否位置です。
-
3
二回の呼び出しを比較する
prompt や schema を一版変えたら、JSON Diffで arguments や structuredContent のどのフィールドが動いたかを見ます。
-
4
デバッグ現場を共有する
URL Hash 共有で tool call をリンクに書き、同僚が開けば再現できます。データはサーバを通りません。
よくある質問
Function Calling と MCP は必ず併用しますか?
必須ではありません。ローカル関数や HTTP API もツールバックエンドになれます。MCP の価値は発見・呼び出し・structuredContent の標準化。Function Calling は name と arguments を選ばせるだけです。よく組み合わせますが、単独でも成立します。
JSON Schema は何通書けばよいですか?
最低二通。ツール入力(Function Calling の parameters と MCP inputSchema を揃える)と、ツール出力または最終回答(MCP outputSchema または Structured Output の response_format)。モデル側と MCP 側で別契約を書かないでください。
MCP なしで Function Calling だけでも足りますか?
単体デモなら足ります。複数 Agent / ランタイムで再利用し、水平拡張するなら、MCP を省くと tools/list・認証・構造化返却を自作することになります。2026 年の既定は Function Calling で選び、MCP で実行です。
モデルが JSON を返したのに、Runtime で検証は必要ですか?
必要です。Function Calling の arguments も MCP の structuredContent も、欠落・型違い・構文壊れが起き得ます。API 層の conform は業務正しさではありません。検証は Runtime の責任です。
まとめ
2026 年の Agent 技術スタックは三文に収まります。
LLM が意思決定し、Function Calling がシリアライズし、MCP が実行する。JSON Schema は三層共用の契約であり、第四のオプションではない。
入力 schema と出力 schema を一つに固定し、モデル API と MCP Server に同じものを載せます。Runtime は両ホップで validate します。モデルとプロトコルはまだ変わります。契約と検証層が Agent をデモから本番へ連れていく防波堤です。その JSON をデバッグするとき、JSONNote のローカル format / schema / diff が使えます。