Implementation:Microsoft Agent framework Tool Decorator With Approval
| Knowledge Sources | |
|---|---|
| Domains | Agent_Architecture, Safety |
| Last Updated | 2026-02-11 17:00 GMT |
Overview
The @tool decorator with approval_mode support, used to register Python functions as agent-callable tools while declaring whether each tool invocation requires human approval before execution.
Description
The @tool decorator transforms a standard Python function into an agent-compatible tool definition. When the approval_mode parameter is specified, it attaches approval metadata to the tool so the agent runtime can enforce human-in-the-loop gating at invocation time. The decorator preserves the full @tool signature (name, description, schema overrides) while adding the approval dimension. At runtime, when the LLM issues a call to a tool decorated with approval_mode="always_require", the agent emits a function_approval_request event and suspends execution until the approval decision is received.
Usage
Import the tool decorator from the top-level agent_framework package. Apply it to any function that should be exposed to the LLM as a callable tool. Set approval_mode="always_require" on tools that perform destructive, irreversible, or sensitive operations. Leave approval_mode unset or set to "never_require" for safe, read-only tools.
Code Reference
Source Location
- Repository: agent-framework
- File: python/packages/core/agent_framework/_tools.py
- Lines: L903-1049
Signature
def tool(
func: Callable[..., Any] | None = None,
*,
name: str | None = None,
description: str | None = None,
schema: dict[str, Any] | None = None,
strict: bool | None = None,
approval_mode: Literal["always_require", "never_require"] | None = None,
) -> Callable[..., Any]:
Import
from agent_framework import tool
I/O Contract
Inputs (Decorator Parameters)
| Name | Type | Required | Description |
|---|---|---|---|
| func | None | No | The function to decorate. When None, the decorator is being called with keyword arguments and returns a partial decorator.
|
| name | None | No | Override the tool name exposed to the LLM. Defaults to the function name. |
| description | None | No | Override the tool description. Defaults to the function's docstring. |
| schema | None | No | Override the auto-generated JSON schema for the tool's parameters. |
| strict | None | No | Enable strict schema validation for tool call arguments. |
| approval_mode | None | No | Controls whether invocation of this tool requires human approval. "always_require" causes the agent to emit a function_approval_request and pause. "never_require" or None allows automatic execution.
|
Outputs
| Name | Type | Description |
|---|---|---|
| decorated_function | Callable[..., Any] |
The original function with attached tool metadata (name, description, schema, approval_mode) accessible by the agent runtime. |
Usage Examples
Basic Tool With Approval Required
from typing import Annotated
from agent_framework import tool
@tool(approval_mode="always_require")
def delete_record(record_id: Annotated[str, "Record to delete"]) -> str:
"""Delete a database record."""
return f"Deleted {record_id}"
When the LLM calls delete_record, the agent runtime will emit a function_approval_request containing the tool name and arguments, and will not execute the function until approval is granted.
Tool Without Approval (Auto-Execute)
from typing import Annotated
from agent_framework import tool
@tool(approval_mode="never_require")
def get_weather(city: Annotated[str, "City name"]) -> str:
"""Get current weather for a city."""
return f"Weather in {city}: 72F, sunny"
This tool executes immediately when called by the LLM, with no approval gate.
Combining Approved and Auto-Executed Tools in an Agent
from typing import Annotated
from agent_framework import Agent, tool
@tool(approval_mode="always_require")
def delete_record(record_id: Annotated[str, "Record to delete"]) -> str:
"""Delete a database record."""
return f"Deleted {record_id}"
@tool
def list_records() -> str:
"""List all available records. Safe read-only operation."""
return "record-1, record-2, record-3"
agent = Agent(
client=client,
name="RecordManager",
instructions="You manage database records. Always list records before deleting.",
tools=[delete_record, list_records],
)
In this agent, list_records auto-executes while delete_record requires human approval.
Using Custom Name and Approval Together
from typing import Annotated
from agent_framework import tool
@tool(name="remove_user", approval_mode="always_require")
def delete_user_account(
user_id: Annotated[str, "The user ID to remove"],
reason: Annotated[str, "Reason for account removal"],
) -> str:
"""Permanently remove a user account and all associated data."""
return f"User {user_id} removed. Reason: {reason}"
The tool is exposed to the LLM as remove_user (not delete_user_account), and requires human approval before execution.
Related Pages
Implements Principle
- Principle:Microsoft_Agent_framework_Tool_Approval_Configuration
- Heuristic:Microsoft_Agent_framework_Tool_Approval_Mode_Production
Sources
| Type | Name | URL |
|---|---|---|
| Repo | Microsoft Agent Framework | https://github.com/microsoft/agent-framework |