Skip to content

Commit 131a781

Browse files
niiish32x越鸿
andauthored
Fix/nsh mcp bind error 20250309 (#154)
Co-authored-by: 越鸿 <nishenghao.nsh@oceanbase.com>
1 parent 26c28bd commit 131a781

2 files changed

Lines changed: 84 additions & 7 deletions

File tree

  • packages

packages/derisk-core/src/derisk/agent/resource/manage.py

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@
1818
logger = logging.getLogger(__name__)
1919

2020

21+
def _get_mcp_not_found_error():
22+
"""Return MCPNotFoundError class, or None when the package is unavailable."""
23+
try:
24+
from derisk_serve.agent.resource.tool.mcp_collect import MCPNotFoundError
25+
return MCPNotFoundError
26+
except ImportError:
27+
return None
28+
29+
2130
def _is_valid_resource_instance(obj: Any) -> bool:
2231
"""Check if object is a valid resource instance (Resource or ToolBase)."""
2332
if isinstance(obj, Resource):
@@ -267,10 +276,40 @@ def build_resource_by_type(
267276
if return_resource
268277
else {"name": real_resource_name}
269278
)
270-
raise ValueError(
271-
f"Resource {real_resource_name} not found in {type_unique_key}"
272-
)
273-
# return cast(Resource, inst_items[0].resource_instance)
279+
# Not found in registered instances, try to create dynamically
280+
# This handles cases like MCP rebind where old instance exists
281+
# but code/name has changed
282+
single_item = item[0]
283+
try:
284+
parameter_cls = single_item.get_parameter_class()
285+
param = parameter_cls.from_dict(
286+
resource_value if v2_resource else agent_resource.to_dict(),
287+
ignore_extra_fields=True,
288+
)
289+
param_dict = param.to_dict()
290+
if not return_resource:
291+
return param_dict
292+
param_dict["system_app"] = self.system_app
293+
resource_inst = single_item.resource_cls(**param_dict)
294+
return resource_inst
295+
except ValueError as e:
296+
# Check if this is an MCPNotFoundError (MCP deleted/recreated).
297+
mcp_not_found_cls = _get_mcp_not_found_error()
298+
if mcp_not_found_cls and isinstance(e, mcp_not_found_cls):
299+
logger.warning(
300+
f"Skipping unavailable MCP resource [{single_item.key}]: {e}. "
301+
f"Please re-bind the MCP in the application configuration."
302+
)
303+
return None
304+
logger.warning(f"Failed to build resource {single_item.key}: {str(e)}")
305+
raise ValueError(
306+
f"Failed to build resource {single_item.key}: {str(e)}"
307+
)
308+
except Exception as e:
309+
logger.warning(f"Failed to build resource {single_item.key}: {str(e)}")
310+
raise ValueError(
311+
f"Failed to build resource {single_item.key}: {str(e)}"
312+
)
274313
elif len(inst_items) > 1:
275314
raise ValueError(
276315
f"Multiple instances of resource {type_unique_key} found, "
@@ -290,6 +329,21 @@ def build_resource_by_type(
290329
param_dict["system_app"] = self.system_app
291330
resource_inst = single_item.resource_cls(**param_dict)
292331
return resource_inst
332+
except ValueError as e:
333+
# Check if this is an MCPNotFoundError (MCP deleted/recreated).
334+
# Catch it here before the generic re-raise so the rest of the
335+
# agent resources can still load.
336+
mcp_not_found_cls = _get_mcp_not_found_error()
337+
if mcp_not_found_cls and isinstance(e, mcp_not_found_cls):
338+
logger.warning(
339+
f"Skipping unavailable MCP resource [{single_item.key}]: {e}. "
340+
f"Please re-bind the MCP in the application configuration."
341+
)
342+
return None
343+
logger.warning(f"Failed to build resource {single_item.key}: {str(e)}")
344+
raise ValueError(
345+
f"Failed to build resource {single_item.key}: {str(e)}"
346+
)
293347
except Exception as e:
294348
logger.warning(f"Failed to build resource {single_item.key}: {str(e)}")
295349
raise ValueError(
@@ -320,13 +374,17 @@ def build_resource(
320374
for resource in agent_resources
321375
]
322376
)
377+
# Filter out None entries (e.g. MCPs that were deleted/recreated)
378+
dependencies = [d for d in dependencies if d is not None]
323379
# dependencies: List[Resource] = execute_parallel_sync([(self.build_resource_by_type, resource.type, resource) for resource in agent_resources])
324380
# dependencies: List[Resource] = []
325381
# for resource in agent_resources:
326382
# resource_inst = cast(
327383
# Resource, self.build_resource_by_type(resource.type, resource)
328384
# )
329385
# dependencies.append(resource_inst)
386+
if not dependencies:
387+
return None
330388
if len(dependencies) == 1:
331389
return dependencies[0]
332390
else:
@@ -356,13 +414,17 @@ async def a_build_resource(
356414
for resource in agent_resources
357415
]
358416
)
359-
# Summary Agent不放入resource中
417+
# Filter out None entries (e.g. MCPs that were deleted/recreated)
418+
# and Summary Agents that should not be included in the resource pack
360419
dependencies = [
361420
dependency
362421
for dependency in dependencies
363-
if not await is_summary_agent_resource(dependency)
422+
if dependency is not None
423+
and not await is_summary_agent_resource(dependency)
364424
]
365425

426+
if not dependencies:
427+
return None
366428
if len(dependencies) == 1:
367429
return dependencies[0]
368430
else:

packages/derisk-serve/src/derisk_serve/agent/resource/tool/mcp_collect.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@
3232
CFG = Config()
3333

3434

35+
class MCPNotFoundError(ValueError):
36+
"""Raised when an MCP service cannot be found by its code.
37+
38+
This typically means the MCP was deleted and recreated (new code) but the
39+
agent/app configuration still references the old code. It is intentionally
40+
a subclass of ValueError so existing callers that catch ValueError are
41+
unaffected, while new code can catch this specific type to apply
42+
graceful-degradation logic (e.g. skip the missing resource instead of
43+
aborting the whole agent load).
44+
"""
45+
46+
3547
def get_mcp_list(**kwargs) -> List[Dict]:
3648
logger.info(f"get_mcp_list:{kwargs}")
3749
from derisk_serve.mcp.service.service import Service as McpService
@@ -161,7 +173,10 @@ def from_dict(
161173

162174
mcp_info: Optional[MCPResponse] = get_mcp_info(mcp_code)
163175
if not mcp_info:
164-
raise ValueError(f"无法找到当前mcp服务[{mcp_code}].")
176+
raise MCPNotFoundError(
177+
f"无法找到当前mcp服务[{mcp_code}],该MCP可能已被删除或code已变更,"
178+
f"请在应用配置中重新绑定MCP资源。"
179+
)
165180

166181
copied_data["mcp_servers"] = mcp_info.sse_url
167182
copied_data["headers"] = mcp_info.sse_headers

0 commit comments

Comments
 (0)