MCPツールのoutputSchemaは必要?structuredContentとの違いを解説

MCPツールのoutputSchemaは必要?structuredContentとの違いを解説 AI開発

MCP Toolを実装するとき、inputSchemaは比較的分かりやすい機能です。Toolへ、

渡す引数の例
{
  "query": "RTX 5070",
  "limit": 10
}

のような引数を渡すため、その入力形式をinputSchemaで定義します。inputSchema自体の書き方はMCPツールのinputSchemaはどう書く?で解説しています。一方、Toolの戻り値には、

戻り値に関わる仕組み
outputSchema
structuredContent
content

という似た名前の仕組みがあり、違いが分かりにくいところです。

結論から言えば、outputSchemaは必須ではありません。文章を返すだけの単純なToolなら、contentだけでも実装できます。しかし、Toolの結果をMCP Client側のプログラムから、

確実に扱いたい値の例
temperature
userId
items
total
status

のような値として確実に扱いたいなら、outputSchemaとstructuredContentを利用する価値があります。

outputSchemaは「このToolはどのような構造のデータを返すのか」という契約です。structuredContentは、実際のTool実行結果として返される構造化データです。そしてcontentは、テキストや画像など、主にモデルが利用するContent Blockです。MCP Python SDKの現行ドキュメントでも、contentはモデル向け、structured_contentはClient Application向け、output_schemaは両者をつなぐ事前の契約として説明されています。

この記事では、MCP ToolのoutputSchemaとstructuredContentの違い、どのようなToolで必要になるのか、contentと両方返す理由、JSON Schemaの実例まで解説します。

スポンサーリンク

outputSchemaはToolの「戻り値の型」を定義する

inputSchemaがToolへ渡すArgumentsを定義するのに対して、outputSchemaはToolから返ってくる構造化データを定義します。たとえば天気を取得するToolがあるとします。

Tool定義の例
{
  "name": "get_weather",
  "description": "指定した都市の現在の天気を取得します",
  "inputSchema": {
    "type": "object",
    "properties": {
      "city": {
        "type": "string"
      }
    },
    "required": [
      "city"
    ]
  },
  "outputSchema": {
    "type": "object",
    "properties": {
      "temperature": {
        "type": "number"
      },
      "condition": {
        "type": "string"
      },
      "humidity": {
        "type": "integer"
      }
    },
    "required": [
      "temperature",
      "condition",
      "humidity"
    ]
  }
}

このTool Definitionを見ればMCP Clientは、Toolを実行する前から、temperatureはnumber、conditionはstring、humidityはintegerという出力形式を理解できます。現在のMCP TypeScript SDKでも、outputSchemaはToolがCallToolResult.structuredContentとして返すデータの構造を記述するOptionalなJSON Schemaとして定義されています。つまりoutputSchemaは実際の結果ではありません。「結果はこの形になる」という事前定義です。

structuredContentは実際に返ってきたデータ

一方のstructuredContentは、tools/callを実行した結果に含まれる実データです。先ほどの天気Toolなら、実行結果は概念的に次のようになります。

Tool実行結果の例
{
  "content": [
    {
      "type": "text",
      "text": "東京は24.5℃、晴れ、湿度58%です。"
    }
  ],
  "structuredContent": {
    "temperature": 24.5,
    "condition": "sunny",
    "humidity": 58
  }
}

outputSchemaはtemperatureがnumber、conditionがstring、humidityがintegerという構造を宣言します。structuredContentには、その型に従った実際の値が入ります。MCP Clientは、ToolがoutputSchemaを公開している場合、返されたstructuredContentをそのSchemaに対してValidationできます。現在のTypeScript SDKではClient側でStructured Outputを自動Validationする機能も実装されています。

contentはstructuredContentとは役割が違う

さらにcontentがあります。MCPのTool ResultにおけるcontentはContent Blockの配列です。たとえばTextなら、

Text Contentの例
{
  "type": "text",
  "text": "東京は24.5℃です。"
}

です。ほかにもImage、Audio、Embedded Resource、Resource Linkなどを返せます。Python SDKでは、contentは主にモデルが読む情報、structured_contentはClient Applicationがプログラムとして扱うデータという役割で説明されています。つまり、

3つの役割
content
→ AIモデルが理解しやすい情報

structuredContent
→ アプリケーションが処理しやすいJSON

outputSchema
→ structuredContentの構造定義

と考えると分かりやすくなります。

なぜ同じ結果をcontentとstructuredContentの両方で返すのか

天気が24.5℃なら{"temperature": 24.5}だけ返せば十分に見えるかもしれません。しかしモデル側では、「temperatureという値が24.5」だけより、「東京の現在気温は24.5℃です。」というText Contentのほうがそのまま回答へ利用しやすい場合があります。一方、Client Applicationでは文章を解析して24.5を取り出したくありません。そのため、Structured Dataも欲しくなります。

MCPではこの2種類を同時に返せます。

両方返す例
{
  "content": [
    {
      "type": "text",
      "text": "東京の現在気温は24.5℃です。"
    }
  ],
  "structuredContent": {
    "temperature": 24.5
  }
}

この設計ならモデルにもApplicationにも使いやすくなります。MCP SDKのドキュメントでも、Structured Contentを返すToolは互換性のために同じデータをSerialized JSONなどのTextContentでも提供することが推奨されています。古いClientやStructured Contentを利用しないClientでも結果を取得できるためです。

outputSchemaは必須ではない

すべてのMCP ToolへoutputSchemaを付ける必要はありません。たとえば「今日のシステム状況を人間向けの文章で説明する」Toolを考えます。結果が「APIとデータベースは正常です。バックグラウンドジョブが2件遅延しています。」というHuman-readable Textだけで十分なら、Structured Outputを用意する必要はありません。

Python SDKなら、

server.py
from mcp.server import MCPServer

mcp = MCPServer("Status")


@mcp.tool(structured_output=False)
def system_report() -> str:
    return (
        "APIとデータベースは正常です。"
        "バックグラウンドジョブが2件遅延しています。"
    )

のようにStructured Outputを無効にできます。この場合はoutput_schemaも生成されず、structured_contentも返されません。contentだけが返されます。したがって「MCP Toolには必ずoutputSchemaを書く」という理解は正しくありません。

outputSchemaが向いているのはプログラムから結果を使うTool

逆に、結果を後続処理で利用したいToolではoutputSchemaが便利です。たとえば商品検索Toolが「RTX 5070搭載PCが3件見つかりました。」という文章だけを返すとします。Client Applicationが商品一覧をUIへ表示したい場合、文章から商品名や価格を解析しなければなりません。

Structured Outputなら、

Structured Outputの例
{
  "items": [
    {
      "id": "pc-001",
      "name": "Gaming PC A",
      "price": 189800
    },
    {
      "id": "pc-002",
      "name": "Gaming PC B",
      "price": 199800
    }
  ],
  "total": 2
}

と返せます。そしてoutputSchemaを、

outputSchemaの例
{
  "type": "object",
  "properties": {
    "items": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "price": {
            "type": "integer"
          }
        },
        "required": [
          "id",
          "name",
          "price"
        ]
      }
    },
    "total": {
      "type": "integer"
    }
  },
  "required": [
    "items",
    "total"
  ]
}

とします。これならClientは文章解析をせず、items・total・priceへ直接アクセスできます。

outputSchemaがあると戻り値の破壊的変更を検出しやすい

Structured Outputには、Application IntegrationだけでなくAPI Contractとしてのメリットもあります。たとえば最初は{"userId": "123", "name": "Taro"}を返していたToolが、実装変更によって{"id": 123, "displayName": "Taro"}を返すようになったとします。

Schemaがなければ、JSONとしてはどちらも正常です。しかしClient側ではuserIdがないため壊れる可能性があります。outputSchemaを、

契約としてのoutputSchema
{
  "type": "object",
  "properties": {
    "userId": {
      "type": "string"
    },
    "name": {
      "type": "string"
    }
  },
  "required": [
    "userId",
    "name"
  ]
}

としておけば、戻り値が契約から外れたことをValidationで検出できます。MCPのStructured Outputでは、Schemaを宣言したServerはStructured ResultをそのSchemaへ適合させる必要があり、Client側もValidationすることが推奨されています。

TypeScript SDKではoutputSchemaとstructuredContentをセットで返す

TypeScript SDKでは、たとえば次のように実装できます。

server.ts
server.registerTool(
  "measure",
  {
    description: "文字列の長さを取得します",

    inputSchema: {
      name: z.string()
    },

    outputSchema: {
      name: z.string(),
      length: z.number()
    }
  },

  async ({ name }) => {
    const output = {
      name,
      length: name.length
    };

    return {
      content: [
        {
          type: "text",
          text: JSON.stringify(output)
        }
      ],

      structuredContent: output
    };
  }
);

ここではoutputSchemaが期待する構造を定義し、structuredContentへ実データを入れています。現在のTypeScript SDK v2では、利用するSchema LibraryからoutputSchemaを生成でき、Serverが結果を返す前にstructuredContentをSchemaへValidationできます。inputSchemaと同様、Zod SchemaからJSON Schemaを導出する仕組みです。

Python SDKなら戻り値の型から自動生成できる

Python SDKのHigh-level APIでは、手動でoutputSchemaを書く必要がないケースもあります。たとえば、

server.py
from pydantic import BaseModel, Field
from mcp.server import MCPServer


mcp = MCPServer("Weather")


class WeatherData(BaseModel):
    temperature: float = Field(
        description="気温。攝氏"
    )

    condition: str

    humidity: int


@mcp.tool()
def get_weather(
    city: str
) -> WeatherData:
    return WeatherData(
        temperature=24.5,
        condition="sunny",
        humidity=58
    )

とします。現在のMCP Python SDKでは戻り値のType Annotationからoutput_schemaを生成し、Tool Definitionとしてtools/listへ公開できます。Toolを呼ぶとSDKがcontent・structured_contentの両方を生成し、返されたPython値をSchemaに対してValidationします。つまり単純なHigh-level Toolなら、outputSchemaを手書きするより、戻り値の型を正しく定義するほうがHandlerとSchemaの不一致を防ぎやすくなります。

Python SDKではスカラーもStructured Outputにできる

たとえば気温だけを返すToolです。

server.py
@mcp.tool()
def get_temperature(
    city: str
) -> int:
    return 24

intはObjectではありません。現在のPython SDKのHigh-level APIでは、このようなScalar Returnを{"result": 24}というObjectへ自動的にWrapします。生成されるoutput_schemaは概念的に、

生成されるoutput_schema
{
  "type": "object",
  "properties": {
    "result": {
      "type": "integer"
    }
  },
  "required": [
    "result"
  ]
}

となります。実行結果は、contentが"24"、structured_contentが{"result": 24}となります。ここは「MCP Protocol自体の制限」と「Python SDKのHigh-level APIの便利な変換」を分けて理解する必要があります。

2026-07-28ではoutputSchemaのRootはObject限定ではない

現在の2026-07-28 MCPでは、inputSchemaのRootは引き続きObjectでなければなりません。一方、outputSchemaにはこの制限がありません。2026-07-28でTool SchemaがFull JSON Schema 2020-12へ拡張され、outputSchemaのRootにはObjectだけでなくArray、String、Number、oneOfなども使用できるようになりました。同時にstructuredContentもObjectだけではなく、任意のJSON Valueを返せるようになっています。

たとえばArrayを直接返すSchemaも書けます。

Array Rootの例
{
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "id": {
        "type": "string"
      },
      "name": {
        "type": "string"
      }
    },
    "required": [
      "id",
      "name"
    ]
  }
}

structuredContentには、

返せる値
[
  {
    "id": "1",
    "name": "Alice"
  },
  {
    "id": "2",
    "name": "Bob"
  }
]

を返せます。

2025-11-25以前との互換性には注意する

ここは古いMCP Clientもサポートする場合に重要です。2025-11-25世代ではoutputSchemaとstructuredContentはObject Shapeを前提としていました。2026-07-28でこの制限が解除されています。

そのため、[{"id": 1}, {"id": 2}]というBare ArrayをStructured Contentとして返すToolは、2026-07-28対応Clientなら扱えても古いClientでは問題になる可能性があります。幅広いClientをサポートしたいなら、

Envelopeで包む
{
  "items": [
    {
      "id": 1
    },
    {
      "id": 2
    }
  ]
}

のようにObjectへ包む設計が互換性を確保しやすくなります。SDKやそのバージョンによっては、Object以外のRootを持つoutputSchema自体を正規化・無効化する実装もあるため、古いClientや古いSDKも対象にする場合は、Object Rootを前提にした設計のほうが安全です。

outputSchemaではoneOfや$refも使える

2026-07-28ではFull JSON Schema 2020-12になったため、出力側でも複雑なSchemaを定義できます。たとえば成功時と失敗時で構造を分けるなら、

oneOfで分岐する例
{
  "oneOf": [
    {
      "type": "object",
      "properties": {
        "success": {
          "const": true
        },
        "data": {
          "type": "object"
        }
      },
      "required": [
        "success",
        "data"
      ]
    },
    {
      "type": "object",
      "properties": {
        "success": {
          "const": false
        },
        "message": {
          "type": "string"
        }
      },
      "required": [
        "success",
        "message"
      ]
    }
  ]
}

のように書くこともProtocol上は可能です。$defsと$refで共通構造を再利用することもできます。ただしTool ErrorについてはMCPにisErrorがあるため、単純な失敗まで何でも独自のsuccess: false Schemaへ詰め込む必要はありません。

Tool ErrorではstructuredContentを信用しない

MCPのTool ResultにはisErrorがあります。Toolが正常に実行できなかった場合、isError = trueとして返せます。Python SDKのClient Documentationでも、Tool Errorの場合には通常contentへエラー内容が入り、structured_contentはNoneになります。Client側ではStructured Contentを利用する前にis_errorを確認することが推奨されています。

たとえば、

client.py
result = await client.call_tool(
    "lookup_book",
    {
        "title": "Solaris"
    }
)

if result.is_error:
    handle_error(result.content)
else:
    process(result.structured_content)

という形です。outputSchemaは主に正常なTool Resultの契約として考えると分かりやすいでしょう。

contentだけでよいToolもある

たとえば「READMEを要約する」Toolがあるとします。戻り値が「このプロジェクトはFastAPIを使用したAPIサーバーです。開発環境ではPostgreSQLを利用します。」という文章だけなら、Applicationが項目ごとに値を利用する必要がないかもしれません。この場合、outputSchemaなし・structuredContentなし・contentのみでも十分です。

画像生成ToolならImageContent、音声ToolならAudioContentを中心に返す設計もあります。Python SDKではContent BlockやImage、Audioなどを返すToolについて、Structured Outputの自動生成を行わない構成があります。つまり、Structured Outputを使わないことが古い設計というわけではありません。Toolの用途に合わせて選びます。

検索結果やAPIレスポンスにはoutputSchemaが向いている

たとえば検索Toolが「3件見つかりました。1. Product A 10000円 2. Product B 12000円 3. Product C 15000円」というTextだけを返すと、モデルは理解できます。しかしUIへ商品カードを表示したいClientでは不便です。

Structured Outputなら、

検索結果の例
{
  "items": [
    {
      "id": "a",
      "name": "Product A",
      "price": 10000
    },
    {
      "id": "b",
      "name": "Product B",
      "price": 12000
    }
  ],
  "total": 2
}

と返せます。MCP ClientはoutputSchemaを見て事前に型を把握でき、実際の結果ではstructuredContent.itemsをそのままUIへ渡せます。検索API、Database Query、Metrics取得、商品情報、ユーザー情報など、後続処理へ使うデータではStructured Outputのメリットが大きくなります。

「JSON文字列」とstructuredContentは同じではない

次のTool Resultを考えます。

JSON文字列をTextで返す例
{
  "content": [
    {
      "type": "text",
      "text": "{\"temperature\":24.5,\"condition\":\"sunny\"}"
    }
  ]
}

見た目はJSONですが、MCP Protocol上はTextContentです。Clientは文字列として受け取ります。値として利用するには、Clientの言語でJSONをParseしなければなりません。Structured Contentなら、

structuredContentで返す例
{
  "structuredContent": {
    "temperature": 24.5,
    "condition": "sunny"
  }
}

となります。Clientは文字列Parseをする必要がありません。さらにoutputSchemaがあるため、その構造を事前に知りValidationできます。つまり「contentにJSON文字列を書く」ことと「structuredContentを返す」ことは同じではありません。

structuredContentだけ返せばcontentはいらない?

最新Protocolだけを前提にした自前Clientなら、structuredContentだけでも目的を達成できるケースがあります。しかし互換性を重視するなら、contentも返すほうが安全です。MCPのStructured Outputでは、Structured Contentを返すToolは後方互換性のためにSerialized JSONなどをTextContentにも含めることが推奨されています。

たとえば、

両方返す構成
{
  "content": [
    {
      "type": "text",
      "text": "{\"temperature\":24.5,\"condition\":\"sunny\"}"
    }
  ],
  "structuredContent": {
    "temperature": 24.5,
    "condition": "sunny"
  }
}

という構成です。High-level SDKでは、この二重化を自動的に行ってくれる場合があります。

outputSchemaと実装を二重管理しない

Low-level MCP Serverでは、自分でSchemaとResultを組み立てます。たとえばPythonなら、

server.py
SEARCH_BOOKS = Tool(
    name="search_books",

    input_schema={
        "type": "object",
        "properties": {
            "query": {
                "type": "string"
            }
        },
        "required": [
            "query"
        ]
    },

    output_schema={
        "type": "object",
        "properties": {
            "matches": {
                "type": "integer"
            },
            "query": {
                "type": "string"
            }
        },
        "required": [
            "matches",
            "query"
        ]
    }
)

とします。Tool Resultでは、

server.py
return CallToolResult(
    content=[
        TextContent(
            type="text",
            text="3 books found"
        )
    ],

    structured_content={
        "matches": 3,
        "query": query
    }
)

のようにします。Low-level APIではoutput_schemaの宣言もstructured_contentの生成も開発者側の責任になります。そのためSchemaではmatchesとしているのにResultでcountを返すといった不一致が起きないよう注意が必要です。High-level SDKで型から自動生成できるなら、その方法を利用するほうが管理しやすい場合があります。

outputSchemaを付けるならValidationまで行う

outputSchemaを書くだけで満足すると、実装変更によって実際のResultがSchemaからずれる可能性があります。重要なのは、Schemaを宣言するだけでなく、返却時にSchemaへ適合していることを検証することです。

TypeScript SDK v2では、ClientのcallTool()がTool Definitionを取得済みならStructured OutputをoutputSchemaに対してValidationできます。またServer側でも利用するSchema Libraryに応じたValidationが行われます。SchemaをAPI Contractとして使うなら、Testでも実際のTool ResultがSchemaに一致することを確認すると安全です。

outputSchemaを巨大にしすぎない

inputSchemaと同様、JSON Schemaは複雑にしようと思えばかなり複雑にできます。たとえば巨大なoneOf・anyOf・allOf・$defs・$refを組み合わせることもできます。しかしTool Resultを利用するClientにとって、本当にその複雑さが必要なのか考えたほうがよいでしょう。

たとえば1つのToolが検索結果・統計・エラー・認証情報・デバッグ情報・UI設定をすべて別形式で返すより、責務を分けたほうが扱いやすい場合があります。Structured Outputの目的は、複雑なSchemaを書くことではありません。Clientが結果を安定して利用できる契約を作ることです。

MCPツールのoutputSchemaに関するよくある質問

QoutputSchemaはすべてのToolに必要ですか

A必要ありません。モデルが読む文章だけを返すToolならcontentだけで十分です。Tool ResultをClient側のプログラムから値として利用したい場合にoutputSchemaとstructuredContentを追加する価値があります。

QstructuredContentとcontentのJSON文字列は同じ意味ですか

A違います。contentへJSON文字列をTextとして書いても、Clientから見ればただの文字列でありParseが必要です。structuredContentはJSON値として直接返され、outputSchemaがあれば事前に構造を検証できます。

QoutputSchemaのRootはinputSchemaと同じくobjectにする必要がありますか

A2026-07-28仕様では制限が異なります。inputSchemaのRootは引き続きobjectである必要がありますが、outputSchemaはarray・string・number・oneOfなどもRootに使えます。ただし古いClientやSDKの中にはobject以外のoutputSchemaを想定していないものもあるため、幅広い互換性が必要ならobjectで包む設計が無難です。

QPython SDKで整数や文字列だけを返すToolでもStructured Outputになりますか

Aなります。High-level APIでは戻り値の型注釈がint やstrなどのScalarの場合、{“result”: 値}という形のObjectへ自動的にラップされ、それに対応するoutput_schemaが生成されます。

QTool Errorのときstructured_contentは使えますか

A通常はNoneになります。Tool Errorの場合はcontentにエラー内容が入り、structured_contentは提供されません。Client側ではまずis_error(またはisError)を確認し、エラーでない場合にのみstructured_contentを利用するようにしてください。

MCPツールのoutputSchemaは「必要なときだけ使う」でよい

MCP ToolのoutputSchemaは必須ではありません。モデルに読ませるHuman-readable Textを返すだけなら、contentだけでも十分です。一方、Tool ResultをClient Applicationからプログラムとして利用したい場合は、Structured Outputを使う価値があります。その場合、outputSchemaで結果の構造を宣言し、structuredContentへ実際のJSON Dataを返します。モデルにも結果を分かりやすく伝えたい場合や古いClientとの互換性を考えるなら、contentにもTextまたはSerialized JSONを含めます。

整理すると、outputSchemaはStructured Resultの契約、structuredContentは契約に従った実際の結果、contentはモデルが利用するText・ImageなどのContent Block、という違いです。

2026-07-28 MCPではoutputSchemaがFull JSON Schema 2020-12へ拡張され、Object以外にもArray、String、Number、oneOfなどをRootとして利用できるようになりました。structuredContentも任意のJSON Valueを返せます。ただし2025-11-25世代ではObject Shapeが前提だったため、古いClientもサポートするなら{"items": [...]}のようなObject Envelopeを維持する設計も検討するとよいでしょう。

MCP ToolでoutputSchemaを使うか迷ったら、「この結果をモデルが文章として読むだけなのか、それともClientのコードが値として利用するのか」を基準にすると判断しやすくなります。モデルが読むだけならcontentで十分です。Applicationも利用するなら、outputSchemaとstructuredContentを追加するのがおすすめです。inputSchemaの書き方はMCPツールのinputSchemaはどう書く?もあわせて参照してください。