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

  1. Diff the requested tool name list against surface.list_tools('mcp_descriptor') names before calling start_thread and fix or drop the unknown names
  2. If the tool should exist, register it in the ToolRegistry that the ToolSurface was constructed with
  3. Update stale references after a tool rename, keeping the registry the single source of truth
  4. 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

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


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/e4a14be53d09b0b7. Report an issue: GitHub.