Skip to content

MCP tools from mcp_config expose internal data wrapper in Responses API tool schema #3380

@LuneZ99

Description

@LuneZ99

Summary

When MCP tools are loaded through Agent(mcp_config=...), MCPToolDefinition.to_openai_tool() exports the correct MCP input schema for Chat Completions, but to_responses_tool() exposes the internal MCPToolAction.data wrapper.

This makes Responses API models generate arguments wrapped under data, while the agent later validates against the actual MCP input schema and rejects the call.

Environment

Reproduced with latest PyPI versions as of 2026-05-25:

openhands-sdk==1.23.0
litellm==1.86.0
fastmcp==3.3.1

Reproduction

This reproduces through the normal MCP config flow:

from openhands.sdk.mcp import create_mcp_tools

config = {
    "mcpServers": {
        "tavily": {
            "url": "https://mcp.tavily.com/mcp/?tavilyApiKey=<redacted>"
        }
    }
}

client = create_mcp_tools(config, timeout=30)
try:
    tavily = next(t for t in client.tools if t.name == "tavily_search")

    print(tavily.mcp_tool.inputSchema.get("required"))
    print(list(tavily.mcp_tool.inputSchema.get("properties", {}).keys()))

    chat_schema = tavily.to_openai_tool()["function"]["parameters"]
    responses_schema = tavily.to_responses_tool()["parameters"]

    print("chat required:", chat_schema.get("required"))
    print("chat properties:", list(chat_schema.get("properties", {}).keys()))

    print("responses required:", responses_schema.get("required"))
    print("responses properties:", list(responses_schema.get("properties", {}).keys()))
finally:
    client.sync_close()

Actual output

tool_names = ['tavily_search', 'tavily_extract', 'tavily_crawl', 'tavily_map', 'tavily_research']

mcp_input_required = ['query']
mcp_input_props = [
  'query', 'max_results', 'search_depth', 'topic', 'time_range',
  'include_images', 'include_image_descriptions', 'include_raw_content',
  'include_domains', 'exclude_domains', 'country', 'include_favicon',
  'start_date', 'end_date', 'exact_match'
]

chat_required = ['query']
chat_props = [
  'summary', 'query', 'max_results', 'search_depth', 'topic', ...
]

responses_required = None
responses_props = ['summary', 'data']

Impact

Responses API models are encouraged by the exposed schema to call the MCP tool like this:

{
  "summary": "Search official BLS schedule",
  "data": {
    "query": "site:bls.gov Employment Situation release schedule 2026",
    "topic": "general",
    "max_results": 5
  }
}

But the agent validates the arguments against the real MCP schema, which expects top-level query, so execution fails:

Error validating tool 'tavily_search':
query Field required
data Extra inputs are not permitted
Parameters provided: ['data']

In production this caused GPT-5 Responses-path conversations to get stuck after repeated MCP validation errors. Chat Completions models did not fail because they use to_openai_tool(), which exports the correct schema.

Root cause

MCPToolDefinition.to_openai_tool() appears to use the dynamic MCP input schema.

MCPToolDefinition does not appear to override to_responses_tool(). Therefore it inherits ToolDefinition.to_responses_tool(), which uses self.action_type. For MCP tools, self.action_type is the generic internal wrapper:

class MCPToolAction(Action):
    data: dict[str, Any]

So the Responses API tool schema exposes data instead of the MCP server's inputSchema.

Expected behavior

MCPToolDefinition.to_responses_tool() should expose the same MCP input schema as to_openai_tool().

For the Tavily MCP example above, the Responses API tool schema should include top-level query, max_results, topic, etc., and should not expose the internal MCPToolAction.data wrapper.

Notes

This appears to still be present on current main: MCPToolDefinition overrides to_openai_tool() but not to_responses_tool().

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions