Skip to content

fix: return JSON objects from list tools so outputSchema matches the payload - #5

Merged
eliasbiondo merged 1 commit into
mainfrom
fix/structured-output-objects
Oct 24, 2026
Merged

eliasbiondo merged 1 commit into
mainfrom
fix/structured-output-objects

Conversation

@eliasbiondo

Copy link
Copy Markdown
Owner

Fixes #1

Root cause

Five tools (reddit_search, reddit_search_subreddit, reddit_get_subreddit_posts, reddit_get_user, reddit_get_user_posts) returned a bare list. For non-object return values FastMCP:

  • advertises a wrapped output schema, {"type": "object", "properties": {"result": {"type": "array", ...}}, "x-fastmcp-wrap-result": true}, and puts {"result": [...]} in structuredContent;
  • but keeps the text content as the bare JSON array, and sends no content blocks at all when the list is empty.

mcpo builds its FastAPI response model from outputSchema (an object with a result field) but returns the JSON-decoded text content. That's either a list, or [] when there is no content. mcpo versions that annotate the handler with the strict response model (e.g. 0.0.14/0.0.15) then raise ResponseValidationError: Input should be a valid dictionary or object to extract fields from, 'input': [], which is exactly the traceback in the issue. reddit_get_post already returned a dict, so it was unaffected.

Change

List tools now return objects, so the text content, the structured content, and the output schema are the same JSON object:

Tool Result
reddit_search, reddit_search_subreddit {"results": [...]}
reddit_get_subreddit_posts, reddit_get_user_posts {"posts": [...]}
reddit_get_user {"items": [...]}
reddit_get_post unchanged (post object)

The return types are TypedDicts, so outputSchema declares the key, e.g. {"properties": {"posts": {"type": "array", ...}}, "required": ["posts"], "type": "object"}, with no wrap marker. Tool descriptions and the README tools table say what each tool returns.

Behavior change: clients that read the list directly from these tools now need to read it from the key instead. LLM clients read the JSON text, so they handle this on their own. Clients that read structuredContent were already getting {"result": [...]} and now get {"posts": [...]} etc.

Also fixed the type: ignore code on McpSerializer.serialize (call-overload, not arg-type), so mypy src is clean.

Verification

  • New tests/test_mcp_tools.py runs an in-process fastmcp.Client against the real registrars with a mocked RedditPort. For every tool, with and without results, it checks: the object outputSchema has no x-fastmcp-wrap-result; structuredContent passes jsonschema validation against outputSchema; and the decoded text content is the same object and also validates. 11 of these 13 tests fail on main and all pass here.
  • End to end with real mcpo, using a stdio stub server that runs these registrars on mocked data:
    • mcpo 0.0.15 (FastAPI 0.115.12, Python 3.11): on main, every list tool returns HTTP 500 with the same model_attributes_type ... 'input': [] error as the issue. On this branch, all tools return HTTP 200, with and without results.
    • mcpo 0.0.20: HTTP 200 on this branch. Its handler annotation is Union[ResponseModel, Any], which is more lenient.
  • ruff check, ruff format --check, pytest (58 passed), mypy src all clean.

…payload

Tools that returned a bare list made FastMCP advertise a wrapped
{"result": [...]} outputSchema while the text content stayed a bare JSON
array (or no content at all for an empty list). MCP-to-OpenAPI bridges
such as mcpo build their response model from outputSchema but return the
decoded text content, so every list tool failed with a FastAPI
ResponseValidationError ("Input should be a valid dictionary").

List tools now return objects: search tools {"results": [...]},
listing tools {"posts": [...]}, and reddit_get_user {"items": [...]}.
@eliasbiondo
eliasbiondo merged commit 30fe6aa into main Oct 24, 2026
2 checks passed
@eliasbiondo
eliasbiondo deleted the fix/structured-output-objects branch October 24, 2026 23:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

error using /w mcpo and OpenWebUI

1 participant