Skip to content

Commit a19b002

Browse files
remove namespaced name
Signed-off-by: Prashant <prashant.rakheja@sap.com>
1 parent 45a9003 commit a19b002

7 files changed

Lines changed: 14 additions & 130 deletions

File tree

src/sap_cloud_sdk/agentgateway/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# Discover tools
1616
tools = await agw_client.list_mcp_tools()
1717
for tool in tools:
18-
print(f"{tool.namespaced_name}: {tool.description}")
18+
print(f"{tool.name}: {tool.description}")
1919
2020
# Invoke a tool
2121
# Note: kwargs like "order_id" are tool-specific input parameters.

src/sap_cloud_sdk/agentgateway/_models.py

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
"""Data models for Agent Gateway MCP tools."""
22

3-
import hashlib
4-
import re
53
from dataclasses import dataclass
64
from typing import Any
75

@@ -30,37 +28,6 @@ class MCPTool:
3028
url: str
3129
fragment_name: str | None = None
3230

33-
@property
34-
def namespaced_name(self) -> str:
35-
"""Unique tool name safe for LLM APIs: sanitized, namespaced, max 64 chars.
36-
37-
LLM tool-calling APIs (Anthropic, OpenAI) require names matching
38-
^[a-zA-Z0-9-_]+$ with a max length of 64 characters.
39-
40-
This property combines server_name and tool name to avoid collisions
41-
across multiple MCP servers, then sanitizes and enforces the limit.
42-
43-
Examples:
44-
Short names pass through unchanged:
45-
"myserver__list_orders" (21 chars) → "myserver__list_orders"
46-
47-
Invalid chars are replaced with underscores:
48-
"my.server:v1__get/data" → "my_server_v1__get_data"
49-
50-
Names over 64 chars are truncated with a hash suffix for uniqueness:
51-
"sales_order_mcp_demo__get_supplier_operational_eval_scores_by_region" (70 chars)
52-
→ "sales_order_mcp_demo__get_supplier_operational_eval_s_a3b7c9d1" (64 chars)
53-
54-
Two servers with the same tool name remain distinct:
55-
"server_a__get_metadata" vs "server_b__get_metadata"
56-
"""
57-
raw = f"{self.server_name}__{self.name}"
58-
sanitized = re.sub(r"[^a-zA-Z0-9\-_]", "_", raw)
59-
if len(sanitized) <= 64:
60-
return sanitized
61-
suffix = hashlib.sha256(sanitized.encode()).hexdigest()[:8]
62-
return f"{sanitized[:55]}_{suffix}"
63-
6431

6532
@dataclass
6633
class IntegrationDependency:

src/sap_cloud_sdk/agentgateway/agw_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ async def list_mcp_tools(
142142
```python
143143
tools = await agw_client.list_mcp_tools()
144144
for tool in tools:
145-
print(f"{tool.namespaced_name}: {tool.description}")
145+
print(f"{tool.name}: {tool.description}")
146146
```
147147
"""
148148
try:

src/sap_cloud_sdk/agentgateway/converters.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
"""Converters for MCPTool to framework-specific tools.
1+
"""Reference converter for MCPTool to LangChain StructuredTool.
22
3-
This module provides converters to transform MCPTool objects into
4-
tools compatible with popular agent frameworks.
3+
This module provides a sample converter as a starting point.
4+
End-users have full flexibility to write their own converters with
5+
custom tool naming, argument schemas, or framework integrations.
56
"""
67

78
from __future__ import annotations
@@ -23,6 +24,9 @@ def mcp_tool_to_langchain(
2324
) -> StructuredTool:
2425
"""Convert MCPTool to LangChain StructuredTool.
2526
27+
This is a reference implementation. End-users can write their own
28+
converter with custom naming, argument handling, or framework bindings.
29+
2630
Args:
2731
mcp_tool: MCPTool object from list_mcp_tools().
2832
call_tool: Callable to invoke the MCP tool (e.g., agw_client.call_mcp_tool).
@@ -72,12 +76,12 @@ async def run(**kwargs) -> str:
7276
properties = mcp_tool.input_schema.get("properties", {})
7377
fields: dict[str, Any] = {k: (str, ...) for k in properties}
7478
args_schema = (
75-
create_model(f"{mcp_tool.namespaced_name}_args", **fields) if fields else None
79+
create_model(f"{mcp_tool.name}_args", **fields) if fields else None
7680
)
7781

7882
return StructuredTool.from_function(
7983
coroutine=run,
80-
name=mcp_tool.namespaced_name,
84+
name=mcp_tool.name,
8185
description=mcp_tool.description,
8286
args_schema=args_schema,
8387
)

src/sap_cloud_sdk/agentgateway/user-guide.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ agw_client = create_client()
2727
tools = await agw_client.list_mcp_tools()
2828

2929
for tool in tools:
30-
print(f"{tool.namespaced_name}: {tool.description}")
30+
print(f"{tool.name}: {tool.description}")
3131

3232
# Invoke a tool with user principal propagation
3333
result = await agw_client.call_mcp_tool(

tests/agentgateway/unit/test_converters.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def test_creates_structured_tool(self):
2727

2828
result = mcp_tool_to_langchain(tool, call_tool, get_user_token)
2929

30-
assert result.name == "s4hana__create_order"
30+
assert result.name == "create_order"
3131
assert result.description == "Create a purchase order"
3232
assert result.coroutine is not None
3333

@@ -75,7 +75,7 @@ def test_handles_empty_input_schema(self):
7575

7676
result = mcp_tool_to_langchain(tool, call_tool, lambda: "token")
7777

78-
assert result.name == "server__simple_tool"
78+
assert result.name == "simple_tool"
7979
assert result.args_schema is not None
8080

8181
def test_handles_input_schema_without_properties(self):

tests/agentgateway/unit/test_models.py

Lines changed: 0 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -6,93 +6,6 @@
66
class TestMCPTool:
77
"""Tests for MCPTool dataclass."""
88

9-
def test_namespaced_name(self):
10-
"""Test namespaced_name property."""
11-
tool = MCPTool(
12-
name="create_order",
13-
server_name="s4hana_procurement",
14-
description="Create purchase order",
15-
input_schema={"type": "object"},
16-
url="https://example.com/mcp",
17-
)
18-
19-
assert tool.namespaced_name == "s4hana_procurement__create_order"
20-
21-
def test_namespaced_name_with_special_chars(self):
22-
"""Test namespaced_name sanitizes invalid characters to underscores."""
23-
tool = MCPTool(
24-
name="get-item",
25-
server_name="my-server",
26-
description="Get item",
27-
input_schema={},
28-
url="https://example.com/mcp",
29-
)
30-
31-
assert tool.namespaced_name == "my-server__get-item"
32-
33-
def test_namespaced_name_sanitizes_dots_and_colons(self):
34-
"""Test that dots, colons, slashes are replaced with underscores."""
35-
tool = MCPTool(
36-
name="get/data",
37-
server_name="my.server:v1",
38-
description="Get data",
39-
input_schema={},
40-
url="https://example.com/mcp",
41-
)
42-
43-
assert tool.namespaced_name == "my_server_v1__get_data"
44-
45-
def test_namespaced_name_truncates_long_names(self):
46-
"""Test that names over 64 chars are truncated to 55 + hash suffix."""
47-
tool = MCPTool(
48-
name="get_supplier_operational_eval_scores_by_region",
49-
server_name="sales_order_mcp_demo",
50-
description="Long name tool",
51-
input_schema={},
52-
url="https://example.com/mcp",
53-
)
54-
55-
result = tool.namespaced_name
56-
assert len(result) == 64
57-
# First 55 chars are preserved from the sanitized name
58-
assert result[:55] == "sales_order_mcp_demo__get_supplier_operational_eval_sco"
59-
# Followed by underscore and 8-char hash
60-
assert result[55] == "_"
61-
assert len(result[56:]) == 8
62-
63-
def test_namespaced_name_short_names_unchanged(self):
64-
"""Test that short valid names pass through without modification."""
65-
tool = MCPTool(
66-
name="list_orders",
67-
server_name="myserver",
68-
description="List orders",
69-
input_schema={},
70-
url="https://example.com/mcp",
71-
)
72-
73-
assert tool.namespaced_name == "myserver__list_orders"
74-
75-
def test_namespaced_name_uniqueness_on_truncation(self):
76-
"""Test that two different long names produce different truncated results."""
77-
tool_a = MCPTool(
78-
name="get_supplier_operational_eval_scores_by_region_east",
79-
server_name="sales_order_mcp_demo",
80-
description="Tool A",
81-
input_schema={},
82-
url="https://example.com/mcp",
83-
)
84-
tool_b = MCPTool(
85-
name="get_supplier_operational_eval_scores_by_region_west",
86-
server_name="sales_order_mcp_demo",
87-
description="Tool B",
88-
input_schema={},
89-
url="https://example.com/mcp",
90-
)
91-
92-
assert tool_a.namespaced_name != tool_b.namespaced_name
93-
assert len(tool_a.namespaced_name) == 64
94-
assert len(tool_b.namespaced_name) == 64
95-
969
def test_create_tool_with_all_fields(self):
9710
"""Test MCPTool creation with all fields."""
9811
tool = MCPTool(

0 commit comments

Comments
 (0)