Skip to content

claude-agent-sdk: honoring the per-agent tools: allowlist needs a permission-mode redesign, not a name mapping #347

Description

Summary

ClaudeAgentSdkProvider declares workflow_tools_passthrough=False, so config/validator.py rejects any non-empty per-agent tools: list and _resolve_tool_config refuses one at execute time. #335 wired up MCP servers but deliberately left this carve-out in place, because closing it is not the name-mapping exercise it first appears to be.

Why it isn't just a name mapping

The obvious idea is to translate Conductor's <server>__<tool> names (mcp/manager.py:197, documented at docs/mcp-tools.md:150) into the CLI's mcp__<server>__<tool> and pass them as allowed_tools. Three things break that:

  1. allowed_tools is not an availability filter. Per the SDK's own docstrings (types.py:1581-1600), tools selects the built-in tool set, allowed_tools is a permission auto-approve list, and disallowed_tools is the only field that removes a tool from the model's context. Putting an allowlist in allowed_tools does not prevent anything from being called.

  2. permission_mode="bypassPermissions" makes it a no-op anyway. That is what the provider sets today on the default path (granting the claude_code preset without prompting). Under bypassPermissions every tool is auto-approved regardless of allowed_tools, so an allowlist would be silently ignored — exactly the "silently granting different tools than declared is a security regression" failure the current refusal exists to prevent.

  3. Only MCP-shaped names are translatable at all. A workflow-level tools: entry can be an arbitrary name (e.g. web_search in examples/research-assistant.yaml:54) with no corresponding CLI tool ID. So even a correct mapping would only ever cover part of the allowlist, making workflow_tools_passthrough=True a partial truth in a boolean descriptor — which AGENTS.md explicitly forbids ("lying in the descriptor undermines the framework").

Possible directions

  • permission_mode="dontAsk" + allowed_tools — non-approved tools are denied, but they stay visible in the model's context, so the model can waste turns attempting them. Also changes default behavior for existing users.
  • A can_use_tool callback (ClaudeAgentOptions.can_use_tool) — the most precise enforcement point, and the only one that can deny per call with a reason.
  • disallowed_tools — needs the full tool list up front, which requires an MCP handshake Conductor does not perform for this provider.

Any of these needs a decision about what happens to non-MCP-shaped names, and whether the descriptor needs to become finer-grained than a single boolean.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions