Skip to content

Commit af151b4

Browse files
authored
fix: optional mcp tools (#171)
1 parent fc0ebb8 commit af151b4

5 files changed

Lines changed: 37 additions & 6 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "sap-cloud-sdk"
3-
version = "0.27.0"
3+
version = "0.27.1"
44
description = "SAP Cloud SDK for Python"
55
readme = "README.md"
66
license = "Apache-2.0"

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: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,34 @@ 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+
from pydantic import BaseModel
102+
103+
assert result.args_schema is not None and isinstance(result.args_schema, type) and issubclass(result.args_schema, BaseModel)
104+
fields = result.args_schema.model_fields
105+
assert fields["eventid"].is_required(), "eventid should be required"
106+
assert not fields["showdeclinedreason"].is_required(), "showdeclinedreason should be optional"
107+
assert not fields["datafetchmode"].is_required(), "datafetchmode should be optional"
108+
81109
def test_handles_input_schema_without_properties(self):
82110
"""Handle MCPTool with input schema but no properties."""
83111
tool = MCPTool(

tests/core/integration/telemetry/_agent.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ def build_langgraph_agent():
1717
from typing import Annotated
1818

1919
from langchain_core.messages import BaseMessage
20-
from langchain_litellm import ChatLiteLLM # ty: ignore[unresolved-import]
21-
from langgraph.graph import END, StateGraph # ty: ignore[unresolved-import]
22-
from langgraph.graph.message import add_messages # ty: ignore[unresolved-import]
20+
from langchain_litellm import ChatLiteLLM
21+
from langgraph.graph import END, StateGraph
22+
from langgraph.graph.message import add_messages
2323
except ImportError:
2424
pytest.skip("langchain-litellm or langgraph not installed")
2525

tests/core/integration/telemetry/test_telemetry_bdd.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ def _llm_call():
127127
Uses ChatLiteLLM so Traceloop's LangChain instrumentor fires and emits a span
128128
with gen_ai.usage.* token counts.
129129
"""
130-
from langchain_litellm import ChatLiteLLM # ty: ignore[unresolved-import]
130+
from langchain_litellm import ChatLiteLLM
131131
from langchain_core.messages import HumanMessage as LCHumanMessage
132132
model_name = os.environ.get("AICORE_MODEL", "anthropic--claude-4.5-sonnet")
133133
llm = ChatLiteLLM(model=f"sap/{model_name}")

0 commit comments

Comments
 (0)