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 alongside content so 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 as str, 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 recover structuredContent from a str return. The workaround is to json.loads the returned string, which only succeeds when the MCP server happens to duplicate its structured payload into content[0].text.
- Any MCP tool that returns multiple
content blocks (text + image, or multiple text blocks) is truncated to block 0.
- Callers cannot check
isError at the type level — success and error results have the same return type.
Steps to Reproduce
- Configure an MCP server that returns a
CallToolResult with both content (text) and structuredContent (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's text. structuredContent, content[1:], isError, and _meta are 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.1 observed. Verified the same flattening logic is still on main at time of filing: agw_client.py:485 declares -> str, and _customer.py:571-576 / _lob.py:480-484 contain the flatten-to-content[0].text code.
- Framework version:
langchain-mcp-adapters==0.2.2 (for cross-reference with the sibling library's _convert_call_tool_result behavior)
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
Describe the Bug
sap_cloud_sdk.agentgateway.AgentGatewayClient.call_mcp_toolis typed-> strand both internal flow paths (_customer.py::call_mcp_tool_customerand_lob.py::call_mcp_tool_lob) discard almost everything on the returned MCPCallToolResultbefore crossing the SDK boundary: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_langchainbuilds a LangChainStructuredToolwhose coroutine returns this flattenedstrdirectly 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) preservesstructuredContenton the LangChain side by wrapping it intoMCPToolArtifactonToolMessage.artifactviaresponse_format="content_and_artifact". Consumers that go throughsap_cloud_sdk.agentgatewayfor its auth / mTLS / destination-resolution / tenant-routing plumbing pay for it by losing the structured channel. Concrete downstream impacts: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.contentblocks (text + image, or multiple text blocks) is truncated to block 0.isErrorat the type level — success and error results have the same return type.Steps to Reproduce
CallToolResultwith bothcontent(text) andstructuredContent(dict) populated. This is the standard MCP shape for schema-typed tool output.agent_gateway_client.call_mcp_tool(tool, ...).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 fullCallToolResult, preservingcontent,structuredContent,isError, and_meta. This lets consumers implement the samecontent_and_artifactsplit thatlangchain_mcp_adaptersprovides 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 existingcall_mcp_toolunchanged:Update
converters.py::mcp_tool_to_langchain(or ship a second reference converter) to callcall_mcp_tool_rawand build aStructuredToolwithresponse_format="content_and_artifact", matching thelangchain_mcp_adaptersshape.Option B — breaking, cleaner long-term. Change
call_mcp_toolto returnCallToolResult; update the reference converter accordingly. Requires a major version bump and a migration note.Option A seems preferable given
call_mcp_toolis a documented public API with a stable signature and existing consumers would need to migrate.Used Versions
3.14.3(bug is Python-version-independent — logic is in the SDK)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.langchain-mcp-adapters==0.2.2(for cross-reference with the sibling library's_convert_call_tool_resultbehavior)Code Examples
Affected Development Phase
Development
Impact
Impaired
Related
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