Skip to content
Merged
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
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "sap-cloud-sdk"
version = "0.58.3"
version = "0.58.4"
description = "SAP Cloud SDK for Python"
readme = "README.md"
license = "Apache-2.0"
Expand Down
14 changes: 14 additions & 0 deletions src/sap_cloud_sdk/agentgateway/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ def mcp_input_schema(tool: Any) -> dict[str, Any]:
return schema or {}


def mcp_output_schema(tool: Any) -> dict[str, Any] | None:
"""Return the tool's output schema across mcp 1.x/2.x, or None if absent.

MCP Builder tools advertise an ``outputSchema`` describing the shape of the
structured result. The mcp client exposes it as ``output_schema`` (snake) or
``outputSchema`` (camel) depending on version. Returns None when the tool has
no output schema so callers can distinguish "no schema" from an empty one.
"""
schema = getattr(tool, "output_schema", None)
if schema is None:
schema = getattr(tool, "outputSchema", None)
return schema or None


def mcp_is_error(result: Any) -> bool:
"""Return the tool-call error flag across mcp 1.x/2.x, defaulting to False."""
flag = getattr(result, "is_error", None)
Expand Down
2 changes: 2 additions & 0 deletions src/sap_cloud_sdk/agentgateway/_customer.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from sap_cloud_sdk.agentgateway._compat import (
mcp_input_schema,
mcp_is_error,
mcp_output_schema,
mcp_server_name,
)
from sap_cloud_sdk.agentgateway._dependencies_resolver import (
Expand Down Expand Up @@ -682,6 +683,7 @@ async def _list_server_tools(
server_name=server_name,
description=t.description or "",
input_schema=mcp_input_schema(t),
output_schema=mcp_output_schema(t),
url=url,
)
for t in tools
Expand Down
2 changes: 2 additions & 0 deletions src/sap_cloud_sdk/agentgateway/_lob.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from sap_cloud_sdk.agentgateway._compat import (
mcp_input_schema,
mcp_is_error,
mcp_output_schema,
mcp_server_name,
)
from sap_cloud_sdk.agentgateway._models import (
Expand Down Expand Up @@ -400,6 +401,7 @@ async def list_server_tools(
server_name=server_name,
description=t.description or "",
input_schema=mcp_input_schema(t),
output_schema=mcp_output_schema(t),
url=dest_url,
fragment_name=fragment_name,
)
Expand Down
3 changes: 3 additions & 0 deletions src/sap_cloud_sdk/agentgateway/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ class MCPTool:
server_name: MCP server name from server_info.name
description: Tool description
input_schema: JSON schema for tool input parameters
output_schema: JSON schema for the tool's structured result, if the MCP
server advertises one (MCP Builder tools do). None when absent.
url: MCP endpoint URL
fragment_name: Destination fragment name (used for auth lookup)
"""
Expand All @@ -54,6 +56,7 @@ class MCPTool:
input_schema: dict[str, Any]
url: str
fragment_name: str | None = None
output_schema: dict[str, Any] | None = None


@dataclass
Expand Down
22 changes: 21 additions & 1 deletion src/sap_cloud_sdk/agentgateway/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from __future__ import annotations

import json
from typing import TYPE_CHECKING, Any, Callable

from pydantic import Field, create_model
Expand Down Expand Up @@ -64,6 +65,23 @@
)


def _augment_description_with_output_schema(
description: str, output_schema: dict[str, Any] | None
) -> str:
"""Append a tool's output schema to its description for the LLM.

LangChain only serializes a tool's name, description, and args schema into the
spec sent to the model; there is no field that carries an MCP ``outputSchema``.
So the only way the LLM can see the expected result shape is to fold it into
the description. Tools without an output schema are returned unchanged.
"""
if not output_schema:
return description
rendered = json.dumps(output_schema, indent=2)
suffix = f"Output schema (JSON):\n{rendered}"
return f"{description}\n\n{suffix}" if description else suffix


def _resolve_type(json_type: Any) -> tuple[type, bool]:
"""Return (python_type, is_nullable) from a JSON Schema ``type`` value.

Expand Down Expand Up @@ -179,6 +197,8 @@ async def run(**kwargs) -> str:
return StructuredTool.from_function(
coroutine=run,
name=mcp_tool.name,
description=mcp_tool.description,
description=_augment_description_with_output_schema(
mcp_tool.description, mcp_tool.output_schema
),
args_schema=args_schema,
)
Loading
Loading