agentgateway.call_mcp_tool flattens CallToolResult to str, dropping structuredContent and other MCP fields
@betinacosta đang làm issue này rồi.
Từ ngày 1/9/2026.
- Ngôn ngữ chính
- Python
- Star
- 28
- Fork
- 42
- Merge trung bình
- 2 ngày 13 giờ
- Pull request đã merge (30 ngày)
- 35
Mô tả
Describe the Bug
sap_cloud_sdk.agentgateway.AgentGatewayClient.call_mcp_tool is typed -> str and both internal flow paths (_customer.py::call_mcp_tool_customer and _lob.py::call_mcp_tool_lob) discard almost everything on the returned MCP CallToolResult before crossing the SDK boundary:
# src/sap_cloud_sdk/agentgateway/_customer.py (main branch, lines 571-576)
# Same shape in _lob.py at lines 480-484.
result = await session.call_tool(tool.name, kwargs)
if not result.content:
logger.warning("Tool '%s' returned empty content", tool.name)
return ""
first = result.content[0]
return str(getattr(first, "text", ""))
Dropped fields:
structuredContent— the MCP-native channel for schema-typed structured tool output. Servers set this alongsidecontentso consumers can distinguish human-readable text from typed data.content[1:]— every content block after the first (multi-block responses lose everything but block 0).isError— success and error results are both returned asstr, indistinguishable at the type level._meta— arbitrary MCP metadata.
The reference converter converters.py::mcp_tool_to_langchain builds a LangChain StructuredTool whose coroutine returns this flattened str directly to LangChain, so downstream consumers using the reference converter also lose these fields.
Why it matters: langchain_mcp_adapters (a sibling library in the LangChain ecosystem) preserves structuredContent on the LangChain side by wrapping it into MCPToolArtifact on ToolMessage.artifact via response_format="content_and_artifact". Consumers that go through sap_cloud_sdk.agentgateway for its auth / mTLS / destination-resolution / tenant-routing plumbing pay for it by losing the structured channel. Concrete downstream impacts:
- Agents emitting structured tool payloads as A2A
Part(root=DataPart(...))cannot recoverstructuredContentfrom astrreturn. The workaround is tojson.loadsthe returned string, which only succeeds when the MCP server happens to duplicate its structured payload intocontent[0].text. - Any MCP tool that returns multiple
contentblocks (text + image, or multiple text blocks) is truncated to block 0. - Callers cannot check
isErrorat the type level — success and error results have the same return type.
Steps to Reproduce
- Configure an MCP server that returns a
CallToolResultwith bothcontent(text) andstructuredContent(dict) populated. This is the standard MCP shape for schema-typed tool output. - Invoke via
agent_gateway_client.call_mcp_tool(tool, ...). - Observe that the return value is
str— the first content block'stext.structuredContent,content[1:],isError, and_metaare unrecoverable from the return.
Expected Behavior
call_mcp_tool (or an equivalent method exposed by the SDK) allows consumers to access the full CallToolResult, preserving content, structuredContent, isError, and _meta. This lets consumers implement the same content_and_artifact split that langchain_mcp_adapters provides for the direct-MCP path.
Two possible shapes for the fix (maintainers know the compatibility surface best):
Option A — additive, non-breaking. Add a new method returning the raw CallToolResult, keep the existing call_mcp_tool unchanged:
async def call_mcp_tool_raw(
self,
tool: MCPTool,
user_token: str | Callable[[], str] | None = None,
app_tid: str | None = None,
**kwargs,
) -> mcp.types.CallToolResult:
...
Update converters.py::mcp_tool_to_langchain (or ship a second reference converter) to call call_mcp_tool_raw and build a StructuredTool with response_format="content_and_artifact", matching the langchain_mcp_adapters shape.
Option B — breaking, cleaner long-term. Change call_mcp_tool to return CallToolResult; update the reference converter accordingly. Requires a major version bump and a migration note.
Option A seems preferable given call_mcp_tool is a documented public API with a stable signature and existing consumers would need to migrate.
Used Versions
- Python version:
3.14.3(bug is Python-version-independent — logic is in the SDK) - SAP Cloud SDK for Python version:
0.29.1observed. Verified the same flattening logic is still onmainat time of filing:agw_client.py:485declares-> str, and_customer.py:571-576/_lob.py:480-484contain the flatten-to-content[0].textcode. - Framework version:
langchain-mcp-adapters==0.2.2(for cross-reference with the sibling library's_convert_call_tool_resultbehavior)
Code Examples
# Consumer-side workaround currently in use — reconstructs a partial
# CallToolResult by parsing the flattened string. Works when the MCP
# server duplicates its payload into content[0].text; loses information
# when it doesn't.
raw_string = await agw_client.call_mcp_tool(tool, ...)
try:
parsed = json.loads(raw_string)
structured = parsed if isinstance(parsed, dict) else None
except json.JSONDecodeError:
structured = None
result = CallToolResult(
content=[TextContent(type="text", text=raw_string)],
structuredContent=structured,
isError=False, # Cannot actually determine — SDK dropped it
)
Affected Development Phase
Development
Impact
Impaired
Related
- MCP protocol spec on
CallToolResult/structuredContent: https://modelcontextprotocol.io/specification/ langchain_mcp_adapters._convert_call_tool_result— how the sibling library preserves the split: https://github.com/langchain-ai/langchain-mcp-adapters/blob/main/langchain_mcp_adapters/tools.py
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Đánh giá
Issue này chưa được đánh giá.