Skip to content

Commit 98ca4d5

Browse files
committed
fix: optional mcp tools
1 parent fc0ebb8 commit 98ca4d5

2 files changed

Lines changed: 29 additions & 1 deletion

File tree

src/sap_cloud_sdk/agentgateway/converters.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,10 @@ async def run(**kwargs) -> str:
7474

7575
# Build args schema from input_schema
7676
properties = mcp_tool.input_schema.get("properties", {})
77-
fields: dict[str, Any] = {k: (str, ...) for k in properties}
77+
required = set(mcp_tool.input_schema.get("required", []))
78+
fields: dict[str, Any] = {
79+
k: (str, ...) if k in required else (str | None, None) for k in properties
80+
}
7881
args_schema = create_model(f"{mcp_tool.name}_args", **fields) if fields else None
7982

8083
return StructuredTool.from_function(

tests/agentgateway/unit/test_converters.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,31 @@ def test_handles_empty_input_schema(self):
7878
assert result.name == "simple_tool"
7979
assert result.args_schema is not None
8080

81+
def test_optional_fields_not_required_in_args_schema(self):
82+
"""Fields absent from 'required' must be optional in the generated Pydantic model."""
83+
tool = MCPTool(
84+
name="get_supplier_bid",
85+
server_name="ariba",
86+
description="Gets all supplier bids for the specified event",
87+
input_schema={
88+
"type": "object",
89+
"required": ["eventid"],
90+
"properties": {
91+
"eventid": {"description": "Unique identifier of the event"},
92+
"showdeclinedreason": {"description": "Show supplier decline reason"},
93+
"datafetchmode": {"description": "Level of detail for the response"},
94+
},
95+
},
96+
url="https://example.com/mcp",
97+
)
98+
99+
result = mcp_tool_to_langchain(tool, AsyncMock(return_value="result"), lambda: "token")
100+
101+
fields = result.args_schema.model_fields
102+
assert fields["eventid"].is_required(), "eventid should be required"
103+
assert not fields["showdeclinedreason"].is_required(), "showdeclinedreason should be optional"
104+
assert not fields["datafetchmode"].is_required(), "datafetchmode should be optional"
105+
81106
def test_handles_input_schema_without_properties(self):
82107
"""Handle MCPTool with input schema but no properties."""
83108
tool = MCPTool(

0 commit comments

Comments
 (0)