ZhuLinsen/daily_stock_analysis · error · CodexAppServerError
tool_not_found
tool_not_found
Error message
DSA tool is not registered: {name} What it means
dynamic_tool_specs() converts ToolSurface MCP descriptors into App Server dynamic-tool specs. It builds a name->descriptor map from surface.list_tools('mcp_descriptor') and looks up each requested tool name; a miss means the name was never registered on the ToolSurface, so it raises code 'tool_not_found'. This is a caller-side consistency error: the thread's tool_names list and the registry disagree.
Source
Thrown at src/agent/codex_app_server_transport.py:159
if isinstance(reasoning_tokens, int) and not isinstance(reasoning_tokens, bool) and reasoning_tokens >= 0:
usage["completion_tokens_details"] = {"reasoning_tokens": reasoning_tokens}
return usage if "total_tokens" in usage else None
def controlled_environment(source: Optional[Dict[str, str]] = None) -> Dict[str, str]:
"""Return the allowlisted environment inherited by Codex."""
environment = os.environ if source is None else source
return {name: environment[name] for name in _ALLOWED_ENV_NAMES if environment.get(name)}
def dynamic_tool_specs(surface: ToolSurface, names: Iterable[str]) -> list[dict]:
"""Convert ToolSurface MCP descriptors into App Server dynamic tools."""
descriptors = {item["name"]: item for item in surface.list_tools("mcp_descriptor")}
specs = []
for name in names:
descriptor = descriptors.get(name)
if descriptor is None:
raise CodexAppServerError("tool_not_found", f"DSA tool is not registered: {name}")
specs.append(
{
"type": "function",
"name": descriptor["name"],
"description": descriptor["description"],
"inputSchema": descriptor["inputSchema"],
}
)
return specs
class CodexAppServerTransport:
"""Bidirectional JSONL client for one ephemeral App Server process."""
def __init__(
self,
command: Sequence[str],
*,View on GitHub (pinned to 5159bd72e8)
Solutions
- Diff the requested tool name list against surface.list_tools('mcp_descriptor') names before calling start_thread and fix or drop the unknown names
- If the tool should exist, register it in the ToolRegistry that the ToolSurface was constructed with
- Update stale references after a tool rename, keeping the registry the single source of truth
- Add a unit test asserting the backend's allowlisted tool names are a subset of registered MCP descriptors
Example fix
# before
names = ["stock_analysis", "typo_tool"]
thread = client.start_thread(tool_names=names, ...)
# after
registered = {d["name"] for d in surface.list_tools("mcp_descriptor")}
unknown = [n for n in names if n not in registered]
if unknown:
raise ValueError(f"unregistered tools requested: {unknown}")
thread = client.start_thread(tool_names=names, ...) Defensive patterns
Strategy: validation
Validate before calling
registered = {d["name"] for d in surface.list_tools("mcp_descriptor")}
unknown = [n for n in requested_tool_names if n not in registered]
if unknown:
raise ValueError(f"tool names not registered on ToolSurface: {unknown}") Type guard
def all_tools_registered(surface: ToolSurface, names: list[str]) -> bool:
registered = {d["name"] for d in surface.list_tools("mcp_descriptor")}
return set(names) <= registered Try / catch
try:
client.start_thread(tool_names=names, ...)
except CodexAppServerError as exc:
if exc.code == "tool_not_found":
names = prune_to_registered(surface, names) # or fail loudly
raise Prevention
- Derive the tool allowlist from surface.list_tools() instead of hardcoding names
- Add a contract test: allowlisted names ⊆ registered MCP descriptor names
- On tool renames, update every caller in the same change
When it happens
Trigger: Passing a tool name to start_thread/run_turn that is not registered in the ToolRegistry backing the ToolSurface; renaming or removing a tool from the registry while callers still request the old name; typos or case mismatch in the tool name string; a registry populated in a different process than the one serving the request.
Common situations: A tool is renamed during a refactor but the Codex backend's allowlist constant is not updated; tests construct a partial registry and request the full tool list; distributed setups where tool registration happens lazily and has not completed when the thread starts.
Related errors
- capability_unsupported
- unsupported_mcp_name
- Backend executable not found: ${backendPath}
- Invalid share image record ID
- Desktop backend URL is unavailable
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/e4a14be53d09b0b7.
Report an issue: GitHub.