Skip to content

Commit ca41adb

Browse files
committed
refactor(tool): enhance MCP endpoint parsing and header handling
This change extends the `_parse_protocol_spec_mcp_url` method to also extract headers from the protocol specification, updates related methods to handle this new information, and merges headers appropriately with higher precedence given to those specified in the protocol spec. Co-developed-by: Aone Copilot <noreply@alibaba-inc.com> Signed-off-by: Sodawyx <sodawyx@126.com>
1 parent 39b32f8 commit ca41adb

File tree

3 files changed

+271
-52
lines changed

3 files changed

+271
-52
lines changed

agentrun/tool/__tool_async_template.py

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -197,16 +197,16 @@ def _get_tool_type(self) -> Optional[ToolType]:
197197
return None
198198
return None
199199

200-
def _parse_protocol_spec_mcp_url(self) -> Tuple[str, str]:
201-
"""从 protocol_spec 解析 MCP 服务器 URL 和 session_affinity / Parse MCP server URL and session_affinity from protocol_spec
200+
def _parse_protocol_spec_mcp_url(self) -> Tuple[str, str, Dict[str, str]]:
201+
"""从 protocol_spec 解析 MCP 服务器 URL、session_affinityheaders / Parse MCP server URL, session_affinity and headers from protocol_spec
202202
203203
用于 MCP_REMOTE + proxy_enabled=false 场景,从 protocol_spec JSON 中提取
204-
第一个 mcpServers entry 的 url 和 transportType
205-
Used for MCP_REMOTE + proxy_enabled=false scenario, extracts url and
206-
transportType from the first mcpServers entry in protocol_spec JSON.
204+
第一个 mcpServers entry 的 url、transportTypeheaders
205+
Used for MCP_REMOTE + proxy_enabled=false scenario, extracts url,
206+
transportType and headers from the first mcpServers entry in protocol_spec JSON.
207207
208208
Returns:
209-
Tuple[str, str]: (mcp_url, session_affinity)
209+
Tuple[str, str, Dict[str, str]]: (mcp_url, session_affinity, headers)
210210
211211
Raises:
212212
ValueError: protocol_spec 为空、格式不合法或缺少必要字段时抛出
@@ -253,20 +253,26 @@ def _parse_protocol_spec_mcp_url(self) -> Tuple[str, str]:
253253
else:
254254
session_affinity = "MCP_SSE"
255255

256-
return url, session_affinity
256+
# 解析 headers(可选字段)/ Parse headers (optional field)
257+
raw_headers = first_server.get("headers")
258+
spec_headers: Dict[str, str] = {}
259+
if raw_headers and isinstance(raw_headers, dict):
260+
spec_headers = {str(k): str(v) for k, v in raw_headers.items()}
261+
262+
return url, session_affinity, spec_headers
257263

258264
def _get_mcp_endpoint(
259265
self, config: Optional[Config] = None
260-
) -> Optional[Tuple[str, str]]:
261-
"""获取 MCP 数据链路 URL 和 session_affinity / Get MCP data endpoint URL and session_affinity
266+
) -> Optional[Tuple[str, str, Dict[str, str]]]:
267+
"""获取 MCP 数据链路 URL、session_affinityspec headers / Get MCP data endpoint URL, session_affinity and spec headers
262268
263-
MCP_REMOTE + proxy_enabled=false 时从 protocol_spec 解析 URL 和 session_affinity
264-
其他场景使用 data_endpoint 拼接,session_affinity 从 mcp_config 获取。
265-
For MCP_REMOTE with proxy disabled, parses URL and session_affinity from protocol_spec.
266-
Otherwise constructs URL from data_endpoint and gets session_affinity from mcp_config.
269+
MCP_REMOTE + proxy_enabled=false 时从 protocol_spec 解析 URL、session_affinityheaders
270+
其他场景使用 data_endpoint 拼接,session_affinity 从 mcp_config 获取,headers 为空
271+
For MCP_REMOTE with proxy disabled, parses URL, session_affinity and headers from protocol_spec.
272+
Otherwise constructs URL from data_endpoint and gets session_affinity from mcp_config, headers empty.
267273
268274
Returns:
269-
Optional[Tuple[str, str]]: (endpoint_url, session_affinity) 或 None
275+
Optional[Tuple[str, str, Dict[str, str]]]: (endpoint_url, session_affinity, spec_headers) 或 None
270276
"""
271277
is_mcp_remote_without_proxy = (
272278
self.create_method == "MCP_REMOTE"
@@ -292,8 +298,13 @@ def _get_mcp_endpoint(
292298
return (
293299
f"{data_endpoint}/tools/{effective_name}/mcp",
294300
session_affinity,
301+
{},
295302
)
296-
return f"{data_endpoint}/tools/{effective_name}/sse", session_affinity
303+
return (
304+
f"{data_endpoint}/tools/{effective_name}/sse",
305+
session_affinity,
306+
{},
307+
)
297308

298309
async def list_tools_async(
299310
self, config: Optional[Config] = None
@@ -320,7 +331,7 @@ async def list_tools_async(
320331
)
321332
return []
322333

323-
mcp_endpoint, session_affinity = endpoint_result
334+
mcp_endpoint, session_affinity, spec_headers = endpoint_result
324335

325336
# MCP_REMOTE + proxy_enabled=false 时直连外部服务,不走 RAM 鉴权
326337
# Only skip RAM auth for MCP_REMOTE with proxy disabled (direct external connection)
@@ -329,11 +340,15 @@ async def list_tools_async(
329340
and not pydash.get(self, "mcp_config.proxy_enabled", False)
330341
)
331342

343+
# 合并 headers:protocol_spec 中的 headers 优先级更高
344+
# Merge headers: protocol_spec headers take precedence
332345
cfg = Config.with_configs(config)
346+
merged_headers = {**(cfg.get_headers() or {}), **spec_headers}
347+
333348
session = ToolMCPSession(
334349
endpoint=mcp_endpoint,
335350
session_affinity=session_affinity,
336-
headers=cfg.get_headers(),
351+
headers=merged_headers,
337352
config=cfg,
338353
use_ram_auth=not is_mcp_remote_without_proxy,
339354
)
@@ -386,7 +401,7 @@ async def call_tool_async(
386401
f"MCP endpoint not available for tool {self.name}"
387402
)
388403

389-
mcp_endpoint, session_affinity = endpoint_result
404+
mcp_endpoint, session_affinity, spec_headers = endpoint_result
390405

391406
# MCP_REMOTE + proxy_enabled=false 时直连外部服务,不走 RAM 鉴权
392407
# Only skip RAM auth for MCP_REMOTE with proxy disabled (direct external connection)
@@ -395,11 +410,15 @@ async def call_tool_async(
395410
and not pydash.get(self, "mcp_config.proxy_enabled", False)
396411
)
397412

413+
# 合并 headers:protocol_spec 中的 headers 优先级更高
414+
# Merge headers: protocol_spec headers take precedence
398415
cfg = Config.with_configs(config)
416+
merged_headers = {**(cfg.get_headers() or {}), **spec_headers}
417+
399418
session = ToolMCPSession(
400419
endpoint=mcp_endpoint,
401420
session_affinity=session_affinity,
402-
headers=cfg.get_headers(),
421+
headers=merged_headers,
403422
config=cfg,
404423
use_ram_auth=not is_mcp_remote_without_proxy,
405424
)

agentrun/tool/tool.py

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -222,16 +222,16 @@ def _get_tool_type(self) -> Optional[ToolType]:
222222
return None
223223
return None
224224

225-
def _parse_protocol_spec_mcp_url(self) -> Tuple[str, str]:
226-
"""从 protocol_spec 解析 MCP 服务器 URL 和 session_affinity / Parse MCP server URL and session_affinity from protocol_spec
225+
def _parse_protocol_spec_mcp_url(self) -> Tuple[str, str, Dict[str, str]]:
226+
"""从 protocol_spec 解析 MCP 服务器 URL、session_affinityheaders / Parse MCP server URL, session_affinity and headers from protocol_spec
227227
228228
用于 MCP_REMOTE + proxy_enabled=false 场景,从 protocol_spec JSON 中提取
229-
第一个 mcpServers entry 的 url 和 transportType
230-
Used for MCP_REMOTE + proxy_enabled=false scenario, extracts url and
231-
transportType from the first mcpServers entry in protocol_spec JSON.
229+
第一个 mcpServers entry 的 url、transportTypeheaders
230+
Used for MCP_REMOTE + proxy_enabled=false scenario, extracts url,
231+
transportType and headers from the first mcpServers entry in protocol_spec JSON.
232232
233233
Returns:
234-
Tuple[str, str]: (mcp_url, session_affinity)
234+
Tuple[str, str, Dict[str, str]]: (mcp_url, session_affinity, headers)
235235
236236
Raises:
237237
ValueError: protocol_spec 为空、格式不合法或缺少必要字段时抛出
@@ -278,20 +278,26 @@ def _parse_protocol_spec_mcp_url(self) -> Tuple[str, str]:
278278
else:
279279
session_affinity = "MCP_SSE"
280280

281-
return url, session_affinity
281+
# 解析 headers(可选字段)/ Parse headers (optional field)
282+
raw_headers = first_server.get("headers")
283+
spec_headers: Dict[str, str] = {}
284+
if raw_headers and isinstance(raw_headers, dict):
285+
spec_headers = {str(k): str(v) for k, v in raw_headers.items()}
286+
287+
return url, session_affinity, spec_headers
282288

283289
def _get_mcp_endpoint(
284290
self, config: Optional[Config] = None
285-
) -> Optional[Tuple[str, str]]:
286-
"""获取 MCP 数据链路 URL 和 session_affinity / Get MCP data endpoint URL and session_affinity
291+
) -> Optional[Tuple[str, str, Dict[str, str]]]:
292+
"""获取 MCP 数据链路 URL、session_affinityspec headers / Get MCP data endpoint URL, session_affinity and spec headers
287293
288-
MCP_REMOTE + proxy_enabled=false 时从 protocol_spec 解析 URL 和 session_affinity
289-
其他场景使用 data_endpoint 拼接,session_affinity 从 mcp_config 获取。
290-
For MCP_REMOTE with proxy disabled, parses URL and session_affinity from protocol_spec.
291-
Otherwise constructs URL from data_endpoint and gets session_affinity from mcp_config.
294+
MCP_REMOTE + proxy_enabled=false 时从 protocol_spec 解析 URL、session_affinityheaders
295+
其他场景使用 data_endpoint 拼接,session_affinity 从 mcp_config 获取,headers 为空
296+
For MCP_REMOTE with proxy disabled, parses URL, session_affinity and headers from protocol_spec.
297+
Otherwise constructs URL from data_endpoint and gets session_affinity from mcp_config, headers empty.
292298
293299
Returns:
294-
Optional[Tuple[str, str]]: (endpoint_url, session_affinity) 或 None
300+
Optional[Tuple[str, str, Dict[str, str]]]: (endpoint_url, session_affinity, spec_headers) 或 None
295301
"""
296302
is_mcp_remote_without_proxy = (
297303
self.create_method == "MCP_REMOTE"
@@ -317,8 +323,13 @@ def _get_mcp_endpoint(
317323
return (
318324
f"{data_endpoint}/tools/{effective_name}/mcp",
319325
session_affinity,
326+
{},
320327
)
321-
return f"{data_endpoint}/tools/{effective_name}/sse", session_affinity
328+
return (
329+
f"{data_endpoint}/tools/{effective_name}/sse",
330+
session_affinity,
331+
{},
332+
)
322333

323334
async def list_tools_async(
324335
self, config: Optional[Config] = None
@@ -345,7 +356,7 @@ async def list_tools_async(
345356
)
346357
return []
347358

348-
mcp_endpoint, session_affinity = endpoint_result
359+
mcp_endpoint, session_affinity, spec_headers = endpoint_result
349360

350361
# MCP_REMOTE + proxy_enabled=false 时直连外部服务,不走 RAM 鉴权
351362
# Only skip RAM auth for MCP_REMOTE with proxy disabled (direct external connection)
@@ -354,11 +365,15 @@ async def list_tools_async(
354365
and not pydash.get(self, "mcp_config.proxy_enabled", False)
355366
)
356367

368+
# 合并 headers:protocol_spec 中的 headers 优先级更高
369+
# Merge headers: protocol_spec headers take precedence
357370
cfg = Config.with_configs(config)
371+
merged_headers = {**(cfg.get_headers() or {}), **spec_headers}
372+
358373
session = ToolMCPSession(
359374
endpoint=mcp_endpoint,
360375
session_affinity=session_affinity,
361-
headers=cfg.get_headers(),
376+
headers=merged_headers,
362377
config=cfg,
363378
use_ram_auth=not is_mcp_remote_without_proxy,
364379
)
@@ -405,7 +420,7 @@ def list_tools(self, config: Optional[Config] = None) -> List[ToolInfo]:
405420
)
406421
return []
407422

408-
mcp_endpoint, session_affinity = endpoint_result
423+
mcp_endpoint, session_affinity, spec_headers = endpoint_result
409424

410425
# MCP_REMOTE + proxy_enabled=false 时直连外部服务,不走 RAM 鉴权
411426
# Only skip RAM auth for MCP_REMOTE with proxy disabled (direct external connection)
@@ -414,11 +429,15 @@ def list_tools(self, config: Optional[Config] = None) -> List[ToolInfo]:
414429
and not pydash.get(self, "mcp_config.proxy_enabled", False)
415430
)
416431

432+
# 合并 headers:protocol_spec 中的 headers 优先级更高
433+
# Merge headers: protocol_spec headers take precedence
417434
cfg = Config.with_configs(config)
435+
merged_headers = {**(cfg.get_headers() or {}), **spec_headers}
436+
418437
session = ToolMCPSession(
419438
endpoint=mcp_endpoint,
420439
session_affinity=session_affinity,
421-
headers=cfg.get_headers(),
440+
headers=merged_headers,
422441
config=cfg,
423442
use_ram_auth=not is_mcp_remote_without_proxy,
424443
)
@@ -471,7 +490,7 @@ async def call_tool_async(
471490
f"MCP endpoint not available for tool {self.name}"
472491
)
473492

474-
mcp_endpoint, session_affinity = endpoint_result
493+
mcp_endpoint, session_affinity, spec_headers = endpoint_result
475494

476495
# MCP_REMOTE + proxy_enabled=false 时直连外部服务,不走 RAM 鉴权
477496
# Only skip RAM auth for MCP_REMOTE with proxy disabled (direct external connection)
@@ -480,11 +499,15 @@ async def call_tool_async(
480499
and not pydash.get(self, "mcp_config.proxy_enabled", False)
481500
)
482501

502+
# 合并 headers:protocol_spec 中的 headers 优先级更高
503+
# Merge headers: protocol_spec headers take precedence
483504
cfg = Config.with_configs(config)
505+
merged_headers = {**(cfg.get_headers() or {}), **spec_headers}
506+
484507
session = ToolMCPSession(
485508
endpoint=mcp_endpoint,
486509
session_affinity=session_affinity,
487-
headers=cfg.get_headers(),
510+
headers=merged_headers,
488511
config=cfg,
489512
use_ram_auth=not is_mcp_remote_without_proxy,
490513
)
@@ -542,7 +565,7 @@ def call_tool(
542565
f"MCP endpoint not available for tool {self.name}"
543566
)
544567

545-
mcp_endpoint, session_affinity = endpoint_result
568+
mcp_endpoint, session_affinity, spec_headers = endpoint_result
546569

547570
# MCP_REMOTE + proxy_enabled=false 时直连外部服务,不走 RAM 鉴权
548571
# Only skip RAM auth for MCP_REMOTE with proxy disabled (direct external connection)
@@ -551,11 +574,15 @@ def call_tool(
551574
and not pydash.get(self, "mcp_config.proxy_enabled", False)
552575
)
553576

577+
# 合并 headers:protocol_spec 中的 headers 优先级更高
578+
# Merge headers: protocol_spec headers take precedence
554579
cfg = Config.with_configs(config)
580+
merged_headers = {**(cfg.get_headers() or {}), **spec_headers}
581+
555582
session = ToolMCPSession(
556583
endpoint=mcp_endpoint,
557584
session_affinity=session_affinity,
558-
headers=cfg.get_headers(),
585+
headers=merged_headers,
559586
config=cfg,
560587
use_ram_auth=not is_mcp_remote_without_proxy,
561588
)

0 commit comments

Comments
 (0)