Skip to content

[FEATURE] Add optional security metadata to tool definitions #2154

@srbhsrkr

Description

@srbhsrkr

Problem Statement

The SDK's tool system has zero security classification. Every registered tool — whether it reads a file or deletes a database — is treated identically by the event loop. There is no way to declare a tool's safety profile, and no built-in permission gate before tool execution.

What exists today:

  • @tool decorator supports only: func, description, inputSchema, name, context. No security parameters.
  • ToolSpec TypedDict has only: description, inputSchema, name, outputSchema. No permission fields.
  • AgentTool base class has: tool_name, tool_spec, tool_type, supports_hot_reload, is_dynamic. No security properties.
  • BeforeToolCallEvent with cancel_tool field enables permission gating via hooks, but requires users to implement all classification logic externally.
  • MCPClient.ToolFilters filters tools at load time by name pattern, but no runtime permission checks.

What's missing:

  • No way for tools to self-declare: "I'm read-only", "I'm destructive", "I need confirmation".
  • No mechanism for hooks to reason about tool safety without hardcoding tool-name-to-permission mappings.
  • MCP tools have no runtime authorization beyond load-time name filtering.

Why this matters:

  • Tool misuse is OWASP ci: update sphinx-autodoc-typehints requirement from <2.0.0,>=1.12.0 to >=1.12.0,<4.0.0 #5 for agentic applications. The SDK already has the hook infrastructure (BeforeToolCallEvent + cancel_tool). What's missing is the metadata on tools for hooks to reason about, and a reference implementation that users can adopt.
  • Without metadata, every permission hook must hardcode tool-name lists. Metadata makes permission policies declarative and portable.

Related Issues

Proposed Solution

Phase 1: Security metadata on tools (non-breaking)

Add optional fields to @tool decorator and AgentTool:

@tool(read_only=True)
def list_files(directory: str) -> list[str]:
    """List files in a directory."""
    ...

@tool(destructive=True, requires_confirmation=True)
def delete_file(path: str) -> str:
    """Permanently delete a file."""
    ...

Add corresponding optional properties to AgentTool with safe defaults:

class AgentTool(ABC):
    @property
    def is_read_only(self) -> bool:
        """Whether this tool only reads state without modification."""
        return False  # safe default: assume not read-only

    @property
    def is_destructive(self) -> bool:
        """Whether this tool performs irreversible actions."""
        return False

    @property
    def requires_confirmation(self) -> bool:
        """Whether this tool should require user confirmation before execution."""
        return False

Phase 2: Reference PermissionPolicy hook (optional, opt-in)

Provide a reference PermissionPolicy hook that uses the metadata:

class PermissionPolicy(HookProvider):
    """Reference implementation: gates destructive tools via cancel_tool."""

    def before_tool_call(self, event: BeforeToolCallEvent):
        tool = event.selected_tool
        if tool.is_destructive or tool.requires_confirmation:
            event.cancel_tool = "This tool requires confirmation."

Users opt in by adding hooks=[PermissionPolicy()] to their Agent.

Trade-offs

Concern Analysis
MCP tools can't use @tool decorator MCPAgentTool can override is_read_only / is_destructive with defaults based on tool name heuristics, or expose a tool_permissions mapping on MCPClient.
Defaults matter is_read_only=False and is_destructive=False are safe defaults. An unclassified tool is "unknown safety" — neither auto-approved nor auto-blocked.
Doesn't this duplicate hooks? Hooks enable custom logic, but without metadata on the tool, every hook must hardcode tool-name-to-permission mappings. Metadata makes policies declarative and portable across projects.
Breaking change risk Zero. All new fields are optional with backward-compatible defaults. Existing @tool functions and AgentTool subclasses work unchanged.

References

  • @tool decorator: src/strands/tools/decorator.py
  • ToolSpec: src/strands/types/tools.py
  • AgentTool: src/strands/tools/tools.py
  • BeforeToolCallEvent: src/strands/hooks/events.py
  • Hook dispatch in executor: src/strands/tools/executors/_executor.py

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