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().
Summary
When MCP tools are loaded through
Agent(mcp_config=...),MCPToolDefinition.to_openai_tool()exports the correct MCP input schema for Chat Completions, butto_responses_tool()exposes the internalMCPToolAction.datawrapper.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:
Reproduction
This reproduces through the normal MCP config flow:
Actual output
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: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.MCPToolDefinitiondoes not appear to overrideto_responses_tool(). Therefore it inheritsToolDefinition.to_responses_tool(), which usesself.action_type. For MCP tools,self.action_typeis the generic internal wrapper:So the Responses API tool schema exposes
datainstead of the MCP server'sinputSchema.Expected behavior
MCPToolDefinition.to_responses_tool()should expose the same MCP input schema asto_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 internalMCPToolAction.datawrapper.Notes
This appears to still be present on current
main:MCPToolDefinitionoverridesto_openai_tool()but notto_responses_tool().