Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions openhands-sdk/openhands/sdk/mcp/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import mcp.types
from litellm import ChatCompletionToolParam
from openai.types.responses import FunctionToolParam
from pydantic import Field, ValidationError

from openhands.sdk.logger import get_logger
Expand Down Expand Up @@ -299,3 +300,33 @@ def to_openai_tool(
add_security_risk_prediction=add_security_risk_prediction,
action_type=mcp_action_type,
)

def to_responses_tool(
self,
add_security_risk_prediction: bool = False,
action_type: type[Schema] | None = None,
) -> FunctionToolParam:
"""Convert a Tool to a Responses API function tool.

For MCP, we dynamically create the action_type (type: Schema)
from the MCP tool input schema, and pass it to the parent method.
It will use the .model_fields from this pydantic model to
generate the Responses-compatible tool schema.

Args:
add_security_risk_prediction: Whether to add a `security_risk` field
to the action schema for LLM to predict. This is useful for
tools that may have safety risks, so the LLM can reason about
the risk level before calling the tool.
"""
if action_type is not None:
raise ValueError(
"MCPTool.to_responses_tool does not support overriding action_type"
)

assert self.name == self.mcp_tool.name
mcp_action_type = _create_mcp_action_type(self.mcp_tool)
return super().to_responses_tool(
add_security_risk_prediction=add_security_risk_prediction,
action_type=mcp_action_type,
)
13 changes: 13 additions & 0 deletions tests/sdk/mcp/test_create_mcp_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,19 @@ def test_create_mcp_tools_http_schema_validation(http_mcp_server: MCPTestServer)
assert "a" in params["required"]
assert "b" in params["required"]

responses_schema = add_tool.to_responses_tool()
responses_params = responses_schema["parameters"]
assert isinstance(responses_params, dict)
responses_properties = responses_params["properties"]
assert isinstance(responses_properties, dict)
responses_required = responses_params["required"]
assert isinstance(responses_required, list)
assert responses_properties["a"]["type"] == "integer"
assert responses_properties["b"]["type"] == "integer"
assert "a" in responses_required
assert "b" in responses_required
assert "data" not in responses_properties


def test_create_mcp_tools_transport_inferred_from_url(http_mcp_server: MCPTestServer):
"""Test that transport type is inferred when not explicitly specified."""
Expand Down
32 changes: 32 additions & 0 deletions tests/sdk/mcp/test_mcp_security_risk.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,38 @@ def test_mcp_tool_to_openai_with_security_risk():
)


def test_mcp_tool_to_responses_with_security_risk():
"""Test that MCP Responses schema includes security_risk correctly."""
mcp_tool_def = mcp.types.Tool(
name="fetch_fetch",
description="Fetch a URL",
inputSchema={
"type": "object",
"properties": {"url": {"type": "string", "description": "URL to fetch"}},
"required": ["url"],
},
)

mock_client = MockMCPClient()
tools = MCPToolDefinition.create(mcp_tool=mcp_tool_def, mcp_client=mock_client)
tool = tools[0]

responses_tool = tool.to_responses_tool(add_security_risk_prediction=True)

params = responses_tool["parameters"]
assert isinstance(params, dict)
properties = params["properties"]
assert isinstance(properties, dict)
required = params.get("required", [])
assert isinstance(required, list)

assert "url" in properties
assert "security_risk" in properties
assert "data" not in properties
assert "url" in required
assert "security_risk" not in required


def test_mcp_tool_action_from_arguments_with_security_risk():
"""Test that action_from_arguments works correctly with security_risk popped.

Expand Down
Loading