Skip to content

Commit c81e452

Browse files
committed
improve MCP error logging and fix duplicate auth call
1 parent c9a25c2 commit c81e452

5 files changed

Lines changed: 50 additions & 21 deletions

File tree

‎src/sap_cloud_sdk/agentgateway/_customer.py‎

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,14 @@
2323
import httpx
2424
from mcp import ClientSession
2525
from mcp.client.streamable_http import streamable_http_client
26+
from mcp.shared.exceptions import McpError
2627

2728
from sap_cloud_sdk.agentgateway._dependencies_resolver import (
2829
EnvironmentDependenciesResolver,
2930
IntegrationDependenciesResolver,
3031
)
3132
from sap_cloud_sdk.agentgateway._models import (
32-
AGWJsonRpcError,
33+
JsonRpcError,
3334
CustomerCredentials,
3435
IntegrationDependency,
3536
MCPTool,
@@ -685,7 +686,7 @@ def _log_mcp_server_error(ord_id: str, exc: BaseException) -> None:
685686
body = exc.response.text
686687
except httpx.ResponseNotRead:
687688
body = None
688-
rpc_error = AGWJsonRpcError.parse(body) if body else None
689+
rpc_error = JsonRpcError.parse(body) if body else None
689690
if rpc_error:
690691
logger.error(
691692
"Failed to load tools from %s — %s returned HTTP %d [JSON-RPC %d]: %s",
@@ -703,8 +704,21 @@ def _log_mcp_server_error(ord_id: str, exc: BaseException) -> None:
703704
exc.response.status_code,
704705
body[:500] if body else "(response body not available)",
705706
)
707+
elif isinstance(exc, McpError):
708+
logger.error(
709+
"Failed to load tools from %s — JSON-RPC %d: %s",
710+
ord_id,
711+
exc.error.code,
712+
exc.error.message,
713+
)
706714
else:
707-
logger.error("Failed to load tools from %s — skipping", ord_id, exc_info=exc)
715+
logger.error(
716+
"Failed to load tools from %s — %s: %s",
717+
ord_id,
718+
type(exc).__name__,
719+
exc,
720+
exc_info=exc,
721+
)
708722

709723

710724
async def get_mcp_tools_customer(

‎src/sap_cloud_sdk/agentgateway/_lob.py‎

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import httpx
1414
from mcp import ClientSession
1515
from mcp.client.streamable_http import streamable_http_client
16+
from mcp.shared.exceptions import McpError
1617
from sap_cloud_sdk.destination import (
1718
create_client as create_destination_client,
1819
ConsumptionLevel,
@@ -29,7 +30,7 @@
2930
list_a2a_fragments,
3031
)
3132
from sap_cloud_sdk.agentgateway._models import (
32-
AGWJsonRpcError,
33+
JsonRpcError,
3334
Agent,
3435
AgentCard,
3536
AgentCardFilter,
@@ -307,7 +308,7 @@ def _log_mcp_server_error(fragment_name: str, exc: BaseException) -> None:
307308
body = exc.response.text
308309
except httpx.ResponseNotRead:
309310
body = None
310-
rpc_error = AGWJsonRpcError.parse(body) if body else None
311+
rpc_error = JsonRpcError.parse(body) if body else None
311312
if rpc_error:
312313
logger.error(
313314
"Failed to load tools from fragment '%s' — %s returned HTTP %d [JSON-RPC %d]: %s",
@@ -325,10 +326,19 @@ def _log_mcp_server_error(fragment_name: str, exc: BaseException) -> None:
325326
exc.response.status_code,
326327
body[:500] if body else "(response body not available)",
327328
)
329+
elif isinstance(exc, McpError):
330+
logger.error(
331+
"Failed to load tools from fragment '%s' — JSON-RPC %d: %s",
332+
fragment_name,
333+
exc.error.code,
334+
exc.error.message,
335+
)
328336
else:
329337
logger.error(
330-
"Failed to load tools from fragment '%s' — skipping",
338+
"Failed to load tools from fragment '%s' — %s: %s",
331339
fragment_name,
340+
type(exc).__name__,
341+
exc,
332342
exc_info=exc,
333343
)
334344

‎src/sap_cloud_sdk/agentgateway/_models.py‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,24 +98,24 @@ class CustomerCredentials:
9898

9999

100100
@dataclass
101-
class AGWJsonRpcError:
101+
class JsonRpcError:
102102
"""Parsed JSON-RPC error from an Agent Gateway response.
103103
104104
AGW returns HTTP 200 with a JSON-RPC error body when the request is
105-
structurally valid but the server encountered an error, e.g.:
106-
107-
{"jsonrpc": "2.0", "error": {"code": -32603, "message": "Internal Server Error"}}
105+
structurally valid but the server encountered an error.
106+
107+
Example: {"jsonrpc": "2.0", "error": {"code": -32603, "message": "Internal Server Error"}}
108108
109109
Attributes:
110-
code: JSON-RPC error code (e.g. -32603 for internal error).
110+
code: JSON-RPC error code.
111111
message: Human-readable error message from AGW.
112112
"""
113113

114114
code: int
115115
message: str
116116

117117
@classmethod
118-
def parse(cls, text: str) -> "AGWJsonRpcError | None":
118+
def parse(cls, text: str) -> "JsonRpcError | None":
119119
try:
120120
data = json.loads(text)
121121
error = data.get("error", {})

‎src/sap_cloud_sdk/agentgateway/agw_client.py‎

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -281,10 +281,12 @@ async def get_user_auth(
281281
self._config.timeout,
282282
self._token_cache,
283283
)
284-
return AuthResult(
284+
result = AuthResult(
285285
access_token=token,
286286
gateway_url=credentials.gateway_url,
287287
)
288+
logger.info("User auth token obtained — gateway: '%s'", result.gateway_url)
289+
return result
288290

289291
# Check for transparent mode
290292
if detect_transparent_credentials():
@@ -299,10 +301,12 @@ async def get_user_auth(
299301
self._config.timeout,
300302
self._token_cache,
301303
)
302-
return AuthResult(
304+
result = AuthResult(
303305
access_token=token,
304306
gateway_url=credentials.gateway_url,
305307
)
308+
logger.info("User auth token obtained — gateway: '%s'", result.gateway_url)
309+
return result
306310

307311
tenant = self._resolve_tenant_subdomain()
308312
token, gateway_url = await fetch_user_auth(
@@ -311,7 +315,9 @@ async def get_user_auth(
311315
token_cache=self._token_cache,
312316
gateway_url_cache=self._gateway_url_cache,
313317
)
314-
return AuthResult(access_token=token, gateway_url=gateway_url)
318+
result = AuthResult(access_token=token, gateway_url=gateway_url)
319+
logger.info("User auth token obtained — gateway: '%s'", result.gateway_url)
320+
return result
315321

316322
except AgentGatewaySDKError:
317323
raise
@@ -437,10 +443,6 @@ async def list_mcp_tools(
437443

438444
# LoB flow - requires tenant_subdomain
439445
tenant = self._resolve_tenant_subdomain()
440-
if user_token:
441-
auth = await self.get_user_auth(user_token)
442-
else:
443-
auth = await self.get_system_auth()
444446
return await get_mcp_tools_lob(
445447
tenant,
446448
auth.access_token,
@@ -590,7 +592,10 @@ async def call_mcp_tool(
590592
tool, auth.access_token, self._config.timeout, **kwargs
591593
)
592594

593-
auth = await self.get_user_auth(user_token)
595+
if not user_token:
596+
raise AgentGatewaySDKError(
597+
"user_token is required for LoB tool invocation."
598+
)
594599
return await call_mcp_tool_lob(
595600
tool, auth.access_token, self._config.timeout, **kwargs
596601
)

‎tests/agentgateway/unit/test_agw_client.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,7 @@ async def test_lob_flow_with_user_token_uses_user_auth(self):
563563

564564
await agw_client.list_mcp_tools(user_token="user-jwt")
565565

566-
assert mock_user_auth.call_count == 2
566+
assert mock_user_auth.call_count == 1
567567
mock_lob.assert_called_once_with(
568568
"my-tenant", "user-token-xyz", 60.0, filter=None
569569
)

0 commit comments

Comments
 (0)