60e0ffc959
Upgrade checks / Notify on failure (push) Has been cancelled
Upgrade checks / Close issue on success (push) Has been cancelled
Schema Crash Test / Real-world schema crash test (232K schemas) (push) Has been cancelled
Run static analysis / static_analysis (push) Has been cancelled
Tests / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Tests / Tests with lowest-direct dependencies (push) Has been cancelled
Tests / MCP conformance tests (push) Has been cancelled
Tests / Integration tests (push) Has been cancelled
Tests / Package install smoke (push) Has been cancelled
Upgrade checks / Static analysis (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Upgrade checks / Integration tests (push) Has been cancelled
Update MCPServerConfig Schema / update-config-schema (push) Has been cancelled
Update SDK Documentation / update-sdk-docs (push) Has been cancelled
81 lines
2.9 KiB
Plaintext
81 lines
2.9 KiB
Plaintext
---
|
|
title: Approval
|
|
sidebarTitle: Approval
|
|
description: Human-in-the-loop approval gates for agent actions
|
|
icon: shield-check
|
|
tag: NEW
|
|
---
|
|
|
|
import { VersionBadge } from '/snippets/version-badge.mdx'
|
|
|
|
<VersionBadge version="3.2.0" />
|
|
|
|
`Approval` adds a human-in-the-loop confirmation step to any server. The LLM presents what it's about to do, the user approves or rejects via buttons, and the decision flows back into the conversation as a message.
|
|
|
|
<Frame>
|
|
<img src="/apps/images/app-approval.png" alt="The Approval provider shown in Goose, with a payment confirmation card and Approve/Cancel buttons" />
|
|
</Frame>
|
|
|
|
```python
|
|
from fastmcp import FastMCP
|
|
from fastmcp.apps.approval import Approval
|
|
|
|
mcp = FastMCP("My Server")
|
|
mcp.add_provider(Approval())
|
|
```
|
|
|
|
This registers a single tool:
|
|
|
|
| Tool | Visibility | Purpose |
|
|
|------|-----------|---------|
|
|
| `request_approval` | Model | Shows an approval card, sends the user's decision back as a message |
|
|
|
|
The LLM calls `request_approval` with a summary (and optional details) whenever it's about to take a significant action. The user sees a card with Approve and Reject buttons. Clicking either sends a message back into the conversation via `SendMessage`, which triggers the LLM's next turn.
|
|
|
|
The message looks like it came from the user:
|
|
|
|
```
|
|
"Deploy v3.2 to production" — I selected: Approve
|
|
```
|
|
|
|
<Note>
|
|
Approval is an advisory gate, not an enforcement mechanism. The conversation isn't blocked while the card is open — the user can keep typing, and a determined LLM could proceed without waiting. Think of it as a strong UX signal that encourages confirmation, not a security boundary. For hard enforcement, implement approval logic server-side in your tool implementations.
|
|
</Note>
|
|
|
|
## Configuration
|
|
|
|
The constructor sets defaults; the LLM can override all of these per-call via tool arguments.
|
|
|
|
```python
|
|
Approval(
|
|
name="Approval", # App name
|
|
title="Approval Required", # Card heading
|
|
approve_text="Approve", # Approve button label
|
|
reject_text="Reject", # Reject button label
|
|
approve_variant="default", # "default", "destructive", "success", "info"
|
|
reject_variant="outline", # same options plus "outline"
|
|
)
|
|
```
|
|
|
|
The LLM can customize each invocation:
|
|
|
|
```python
|
|
request_approval(
|
|
summary="Delete 47 files from /tmp",
|
|
details="This cannot be undone.",
|
|
title="Destructive Action",
|
|
approve_text="Delete",
|
|
approve_variant="destructive",
|
|
reject_text="Keep files",
|
|
)
|
|
```
|
|
|
|
## How it works
|
|
|
|
When the user clicks a button, two things happen:
|
|
|
|
1. `SendMessage` pushes the decision into the conversation as a user message
|
|
2. `SetState("decided", True)` replaces the buttons with "Response sent."
|
|
|
|
The tool description instructs the LLM to stop and wait for the "I selected:" message before proceeding. If approved, it continues. If rejected, it acknowledges and asks how to proceed.
|