Skip to content

fix(models): forward tool output schema on the LiteLLM path - #6815

Open
schlaepf wants to merge 1 commit into
google:mainfrom
schlaepf:fix/litellm-tool-output-schema
Open

fix(models): forward tool output schema on the LiteLLM path#6815
schlaepf wants to merge 1 commit into
google:mainfrom
schlaepf:fix/litellm-tool-output-schema

Conversation

@schlaepf

Copy link
Copy Markdown

Fixes #6784

Description

An MCP tool's declared outputSchema never reaches the model when the agent is backed by LiteLlm.

_function_declaration_to_tool_param() in src/google/adk/models/lite_llm.py builds the tool payload from name, description and parameters only. It ignores both function_declaration.response and function_declaration.response_json_schema, so the output schema is silently dropped before the request is built — even when MCPTool._get_declaration() has populated it correctly. The user-visible symptom is that the model cannot describe or rely on a tool's result shape, the same symptom as #2828 (fixed for the Gemini path in c8e5340).

Setting ADK_ENABLE_JSON_SCHEMA_FOR_FUNC_DECL=1 does not help here: the declaration is built correctly, but the LiteLLM conversion still discards it, so the payload is byte-identical with the flag on and off.

Approach

OpenAI-compatible chat completions tool definitions have no standard field for the schema of a tool's result. Rather than inventing a non-standard key inside the tool payload, this PR renders the output schema into the tool description, which is forwarded to the model.

A new private helper _append_response_schema_to_description():

  • reads response_json_schema (preferred) or response (converted via the existing _schema_to_dict),
  • renders it as compact JSON with sort_keys=True for deterministic output, behind the label Returns a JSON object conforming to this schema: on a new line,
  • returns the description unchanged when no output schema is declared,
  • returns just the rendering when the original description is empty.

The change is confined to the description; parameters and every other part of the payload are untouched. Declarations without an output schema produce a byte-identical payload, so existing behaviour is preserved.

Testing plan

Unit tests

Four tests added to tests/unittests/models/test_litellm.py, following the existing test_function_declaration_to_tool_param* conventions:

Test Covers
..._with_response_json_schema raw response_json_schema is rendered; parameters unchanged
..._with_response_schema types.Schema response is rendered
..._without_response_schema description byte-identical (regression guard)
..._response_schema_without_description empty description yields only the rendering
$ uv run pytest tests/unittests/models/test_litellm.py -q
391 passed in 2.40s

Formatting verified with pyink --check --config pyproject.toml on both files (clean).

Before / after

Running the reproduction script from #6784 against this branch:

Before — output schema absent from the payload:

declaration.response_json_schema is set: True

"function": {
  "name": "get_widget",
  "description": "Return a widget.",
  "parameters": {...}
}

output schema present in payload: False

After — the declared result shape reaches the model:

{
  "type": "function",
  "function": {
    "name": "get_widget",
    "description": "Return a widget.\nReturns a JSON object conforming to this schema: {\"properties\": {\"data\": {\"description\": \"preformatted text\", \"type\": \"string\"}, \"status\": {\"enum\": [\"success\", \"error\"], \"type\": \"string\"}}, \"required\": [\"status\", \"data\"], \"type\": \"object\"}",
    "parameters": {
      "type": "object",
      "properties": {"service": {"type": "string"}},
      "required": ["service"]
    }
  }
}

output schema present in payload: True

The fix applies regardless of the JSON_SCHEMA_FOR_FUNC_DECL feature flag, since it reads whichever of response / response_json_schema the declaration carries.

An MCP tool's declared outputSchema never reached the model when the
agent was backed by LiteLlm. _function_declaration_to_tool_param() built
the tool payload from name, description and parameters only, ignoring
both response_json_schema and response, so the output schema was
silently dropped even when the declaration carried it.

OpenAI-compatible chat completions tool definitions have no standard
field for a tool result schema, so the schema is rendered into the tool
description, which is forwarded to the model. Declarations without an
output schema keep their description byte-identical.

Fixes google#6784
@google-cla

google-cla Bot commented Aug 19, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

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.

MCP tool outputSchema is dropped on the LiteLLM path

2 participants