Skip to content

fix: ResponseLimitingMiddleware no longer breaks outputSchema tools#3756

Merged
jlowin merged 5 commits intomainfrom
fix/response-limiting-output-schema
Apr 5, 2026
Merged

fix: ResponseLimitingMiddleware no longer breaks outputSchema tools#3756
jlowin merged 5 commits intomainfrom
fix/response-limiting-output-schema

Conversation

@jlowin
Copy link
Copy Markdown
Member

@jlowin jlowin commented Apr 3, 2026

When ResponseLimitingMiddleware truncates a response from a tool with outputSchema, the MCP SDK rejects it because structured_content is gone but the schema still expects it. This is intentional — truncated text can't satisfy a structured schema, and passing structured_content through unmodified would defeat the middleware's size-limiting purpose.

The fix ensures truncated results always carry meta, which makes to_mcp_result() return a CallToolResult. The SDK passes CallToolResult through without outputSchema validation, so the truncated text reaches the client cleanly.

mcp = FastMCP()
mcp.add_middleware(ResponseLimitingMiddleware(max_size=1_000))

@mcp.tool()
def big_answer() -> Answer:
    return Answer(text="x" * 2_000)

# Previously raised: "outputSchema defined but no structured output returned"
# Now returns truncated text successfully

Closes #3717

jlowin added 3 commits April 2, 2026 08:48
Per the MCP Apps spec, McpUiToolMeta only defines resourceUri and
visibility. CSP belongs on the UIResourceMeta (the renderer resource),
where we already set it correctly via _ensure_prefab_renderer.

Closes #3735
When truncating, set meta on the ToolResult so it goes through the
CallToolResult path, bypassing MCP SDK outputSchema validation that
would reject the truncated (text-only) response.

Closes #3717
@jlowin jlowin added the bug Something isn't working. Reports of errors, unexpected behavior, or broken functionality. label Apr 3, 2026
@marvin-context-protocol marvin-context-protocol Bot added the server Related to FastMCP server implementation or server-side functionality. label Apr 3, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 071745281f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

)

return self._truncate_to_result(text)
return self._truncate_to_result(text, meta=result.meta)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Drop oversized metadata when returning truncated results

Passing result.meta into _truncate_to_result preserves the original metadata blob even after the response was flagged as over max_size, so the middleware can still return payloads far above the configured limit when _meta is large (for example, a tool returning a large debug object in ToolResult.meta). This regresses the middleware’s core size-limiting guarantee because only content is truncated while the oversized metadata is forwarded unchanged.

Useful? React with 👍 / 👎.

@jlowin jlowin merged commit 4bbc4ee into main Apr 5, 2026
8 checks passed
@jlowin jlowin deleted the fix/response-limiting-output-schema branch April 5, 2026 14:36
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cc3fefb116

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +100 to +103
return ToolResult(
content=[TextContent(type="text", text=truncated)],
meta=meta if meta is not None else {},
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid returning schema-less CallToolResult on truncation

Setting meta unconditionally here forces ToolResult.to_mcp_result() down the CallToolResult path even when truncation removed structured_content. For tools that advertise outputSchema, MCP clients still validate successful responses and reject missing structured content, so Client.call_tool() remains broken for oversized schema tools (now surfacing as a runtime validation failure instead of a normal tool error).

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working. Reports of errors, unexpected behavior, or broken functionality. server Related to FastMCP server implementation or server-side functionality.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ResponseLimitingMiddleware truncation breaks tools with outputSchema

1 participant