Output¶
The JSON encoder and the ToolResult builder.
A tool's result has two halves and they are toggled independently:
structuredContent on the call result, and outputSchema on the tools/list
entry. The MCP spec imposes one asymmetric rule between them — a tool that
advertises outputSchema must return conforming structuredContent, while
the reverse is allowed — so they are not a single switch.
Both are on by default, server-wide via
SPEC_MCP["INCLUDE_STRUCTURED_CONTENT"] and
["INCLUDE_OUTPUT_SCHEMA"], and per tool via include_structured_content= and
include_output_schema= at registration. The asymmetric rule is enforced:
include_output_schema=True with include_structured_content=False raises at
registration rather than advertising a schema nothing will satisfy.
That is a check on the settings, not on a response. To assert that a real
result satisfies the schema its tool advertised — types and formats, not only
the property names — see
assert_tool_result_conforms.
See Omitting structuredContent and outputSchema
for when to turn either off, and
Hide plumbing from the model for what shapes the
schema an agent sees — field markings project the payload and the advertised
outputSchema from the same declaration.
encode_json ¶
Encode payload as a stable, pretty JSON string.
default=str renders a presented value containing Decimal, UUID or
datetime without raising; keys are sorted so the output is
deterministic. A payload sort_keys can sort is written by it unchanged,
so its bytes and its nesting limit are json.dumps's own
(test_encode_json_writes_what_sort_keys_writes_wherever_sort_keys_can);
only one it refuses is put in order first.
build_tool_result ¶
build_tool_result(
payload: Any,
*,
is_error: bool = False,
include_structured_content: bool = True,
meta: dict[str, Any] | None = None,
content_kind: ToolContentKind = ToolContentKind.TEXT,
content_mime_type: str | None = None,
binding_name: str | None = None,
) -> ToolResult
Build a ToolResult
for a successful (or tool-level error) call.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
payload
|
Any
|
The JSON-shaped tool output. Becomes |
required |
is_error
|
bool
|
Stamped onto the result as |
False
|
include_structured_content
|
bool
|
|
True
|
meta
|
dict[str, Any] | None
|
The base protocol's |
None
|
content_kind
|
ToolContentKind
|
The block type the binding declared. Anything other than
|
TEXT
|
content_mime_type
|
str | None
|
Media type for a non- |
None
|
binding_name
|
str | None
|
Names the binding in that mismatch message. |
None
|
A None payload is a result with nothing to present: an
allow_none RETRIEVE that found no row, a service whose output re-read
selector found none, or a service with no re-read declaring
ServiceSpec(allow_none=True) that returned None. It is served as
{}, both as structuredContent and as the text block, because MCP
requires structured content to be an object and asks the text block to
carry the same JSON. This is the one place the rule lives, so a selector
and a service answer it the same way on every entry point. The
advertised outputSchema of those tools admits the {}
(build_output_schema(may_be_empty=...)). A service with no re-read that
returns None without declaring allow_none is served {} too,
against a schema that still requires its fields, because the kernel
presents an undeclared None rather than refusing it
(test_an_undeclared_none_is_still_served_empty_against_a_strict_schema);
allow_none=True
is the declaration that makes the schema admit it
(test_a_service_declaring_allow_none_serves_an_empty_object_its_schema_admits).