agentscope-ai/agentscope · error · RuntimeError
{self.__class__.__name__} is an external tool and should not
Error message
{self.__class__.__name__} is an external tool and should not be called directly What it means
ToolBase.call() raises RuntimeError when invoked on a tool flagged as external (e.g. an MCP tool): execution must be routed through the adapter/runtime, not a direct Python call.
Source
Thrown at src/agentscope/tool/_base.py:170
Subclasses should override this method instead of
:meth:`__call__`. The base implementation raises
:exc:`NotImplementedError` for non-external tools and
:exc:`RuntimeError` for external tools.
Args:
**kwargs: Tool input arguments.
Returns:
`ToolChunk | AsyncGenerator[ToolChunk, None]`:
A single :class:`~agentscope.tool.ToolChunk` or an
async generator that yields them.
"""
if not self.is_external_tool:
raise NotImplementedError(
f"{self.__class__.__name__} does not implement call",
)
raise RuntimeError(
f"{self.__class__.__name__} is an external tool and should not "
f"be called directly",
)
async def __call__(
self,
*args: Any,
**kwargs: Any,
) -> ToolChunk | AsyncGenerator[ToolChunk, None]:
"""Invoke the tool, layering any registered middlewares around
:meth:`call`.
Tools are always invoked with keyword arguments only. ``*args`` is
accepted in the signature solely to stay Liskov-compatible with
subclasses that override ``__call__`` with their own positional
parameters; any positional argument actually passed here is rejected
(raising :exc:`TypeError`) so it fails loudly instead of being silently
dropped.View on GitHub (pinned to e90f1c7592)
Solutions
- Invoke the tool via the toolkit/agent so it is dispatched through the MCP adapter
- Don't call external tools directly; use the toolkit's run interface
- If it should be local, don't mark/construct it as an external tool
Example fix
# before result = await mcp_tool(query='x') # after result = await toolkit.call_tool(mcp_tool.name, query='x')
Defensive patterns
Strategy: try-catch
Validate before calling
assert not tool.is_external_tool, 'route external tools through the toolkit'
Type guard
def is_local_tool(tool) -> bool:
return not getattr(tool, 'is_external_tool', False) Try / catch
try:
await tool(**kwargs)
except RuntimeError as e:
if 'external tool' in str(e):
result = await toolkit.call_tool(tool.name, **kwargs)
else: raise Prevention
- Never call MCP tools directly; dispatch via the toolkit
- Check is_external_tool before manual invocation
When it happens
Trigger: Calling tool(...) directly on an external/MCP tool instance instead of letting the toolkit execute it through its adapter chain (execute_chain / MCP session).
Common situations: Grabbing a tool reference from a toolkit and calling it manually in tests or scripts; the external flag is set but the caller treats it like a local tool.
Related errors
- STDIO MCP does not support ephemeral mode. Use 'shared' or '
- Invalid values for MCP {card.name!r}: {e.message}
- MCP {card.name!r} needs a value for: {', '.join(sorted(missi
- MCP {card.name!r} produced an invalid client: {e}
- An MCP named {mcp_record.name!r} already exists for this use
AI-assisted analysis of agentscope-ai/agentscope@e90f1c7592 (2026-08-28).
Data as JSON: /api/errors/920abd2914e303f8.
Report an issue: GitHub.