mirror of
https://github.com/open-webui/open-webui.git
synced 2026-06-02 06:46:55 -05:00
[PR #21678] [CLOSED] feat: add human-in-the-loop tool approval system #97236
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/open-webui/open-webui/pull/21678
Author: @MaderHatt3r
Created: 2/21/2026
Status: ❌ Closed
Base:
dev← Head:feat/human-in-the-loop📝 Commits (3)
a24db4efeat: add human-in-the-loop tool approval system00c3b40refactor: load builtin tool categories from backend APIee7a78astyle: run prettier and black formatters📊 Changes
18 files changed (+2214 additions, -83 deletions)
View changed files
📝
backend/open_webui/config.py(+10 -1)📝
backend/open_webui/routers/auths.py(+4 -0)📝
backend/open_webui/routers/tools.py(+11 -1)📝
backend/open_webui/socket/main.py(+222 -0)📝
backend/open_webui/utils/middleware.py(+312 -2)➕
backend/open_webui/utils/tool_approval.py(+211 -0)📝
backend/open_webui/utils/tools.py(+96 -1)📝
src/lib/apis/tools/index.ts(+32 -0)📝
src/lib/components/admin/Settings/General.svelte(+8 -0)📝
src/lib/components/chat/Chat.svelte(+11 -0)📝
src/lib/components/chat/MessageInput.svelte(+3 -0)📝
src/lib/components/chat/MessageInput/IntegrationsMenu.svelte(+517 -29)➕
src/lib/components/chat/ToolApprovalModal.svelte(+290 -0)📝
src/lib/components/workspace/Models/BuiltinTools.svelte(+17 -49)➕
src/lib/stores/toolApproval.ts(+322 -0)📝
src/lib/types/index.ts(+30 -0)➕
src/lib/utils/toolApprovalChannel.ts(+80 -0)📝
src/routes/+layout.svelte(+38 -0)📄 Description
Add a comprehensive tool approval system that requires explicit user consent before AI agents execute tools, with granular control at both the parent tool and child function level.
Backend:
BUILTIN_TOOL_CATEGORIEScanonical mapping andGET /tools/builtin/categoriesendpoint for dynamic builtin tool category metadataFrontend:
Contribution Note:
This feature was initially developed by @Barbaronno and extended with granular per-function/per-tool permissions, YOLO mode, an always-allowed revocation UI, and unrecognized tool call warnings.
This submitted commit adds @Barbaronno as a co-author.
See the original PR at: https://github.com/open-webui/open-webui/pull/16913
Pull Request Checklist
Note to first-time contributors: Please open a discussion post in Discussions to discuss your idea/fix with the community before creating a pull request, and describe your changes before submitting a pull request.
This is to ensure large feature PRs are discussed with the community first, before starting work on it. If the community does not want this feature or it is not relevant for Open WebUI as a project, it can be identified in the discussion before working on the feature and submitting the PR.
First-time Contributor Acknowledgement
See the discussion at: https://github.com/open-webui/open-webui/discussions/16701
Before submitting, make sure you've checked the following:
devbranch. PRs targetingmainwill be immediately closed.devto ensure no unrelated commits (e.g. frommain) are included. Push updates to the existing PR branch instead of closing and reopening.Changelog Entry
Description
Adds a human-in-the-loop (HITL) tool approval system that requires explicit user consent before AI agents execute tools during chat. When enabled, every tool call pauses and presents a modal asking the user to Allow Once, Always Approve, or Decline. Approval granularity operates at both the individual function level (e.g.
read_file) and the parent tool level (e.g.mcp:desktop_commander). A YOLO Mode option is available for users who want to auto-approve all tool calls without prompts. All approval state is per-chat and in-memory — nothing is persisted to disk or database.Added
ENABLE_TOOL_APPROVALconfiguration setting — toggleable via environment variable or Admin Settings > General. Defaults totrue.ToolApprovalManagerbackend class (backend/open_webui/utils/tool_approval.py) managing pending approvals, always-approved permissions, and YOLO mode state, all in-memory and per-chat.backend/open_webui/socket/main.py) for real-time approval responses, always-approved CRUD, YOLO mode management, and pending approval recovery.ToolApprovalModalfrontend component with four actions: Allow Once, Always Approve (per-function), Always Approve (per-tool), and Decline.toolApprovalSvelte store (src/lib/stores/toolApproval.ts) for socket-based frontend state management.BroadcastChannel(src/lib/utils/toolApprovalChannel.ts) to prevent duplicate approval modals across browser tabs.src/lib/types/index.ts).BUILTIN_TOOL_CATEGORIESdict intools.pyas single source of truth for builtin tool category names, descriptions, and function mappings.GET /tools/builtin/categoriesAPI endpoint serving builtin tool metadata to authenticated users.Changed
BuiltinTools.svelteadmin component fetches categories from backend API instead of hardcoding.IntegrationsMenunow accepts achatIdprop and displays YOLO Mode and Always Allowed entries when a chat is active.MessageInputpasseschatIddown toIntegrationsMenu.Chat.sveltepasseschatIdto the message input component.+layout.sveltehandlestool:approval_requestandtool:approval_statussocket events globally.routers/auths.py) expose and acceptENABLE_TOOL_APPROVAL.tool_idfield for parent tool resolution.Deprecated
Removed
Fixed
Security
Breaking Changes
Additional Information
ToolApprovalManagersingleton) and resets on server restart. No database migrations or schema changes.ENABLE_TOOL_APPROVALenv var can be set tofalseto disable the feature entirely, bypassing all approval checks.read_file), parent tools by their registered ID (e.g.mcp:desktop_commander,direct_server:http://...).Screenshots or Videos
Scenario 1.a — YOLO a single tool in a new chat
Find documentation for aws strands.Result: No permission was asked to use web search tool.
Scenario 1.b — Disable YOLO in the same chat
Great! Now find documentation for dynamodb.Result: Approval modal prompts for
search_web/builtin:search_web.Scenario 1.c — Always allow function
Result: Search completed and function added to always allow list.
Scenario 1.d — Always allow tool
Great! Now generate an image of a cat riding a donkey.Result: Result: Always allow menu now shows the always allowed function as well as the tool.
All functions approveddisplayed for tool-level approvalScenario 1.e — Revoke function access
Result: Modal prompt for allowing a web search.
Scenario 1.f — Cross-tab coordination
Note: These aren't the only test I preformed, but it became tedious to document it all in this format.
I have tested allow once.
I have tested decline. It suggests to the tool that the user declined it. I have seen this redirect the llm mid stream. For instance it tried to access the knowledge base and I declined it and it did a web search instead.
I have tested with several functions under one tool and confirmed that the allow always ones allow, but not the individual functions for the same tool that aren't in the always allow list.
I have tested it with the subagents tool. Note, this isn't supported at this time. It seemed to ignore permissions.
Error found during testing: Sometimes opening the menu to observe a recently changed setting wouldn't show the change until closing the menu and opening again. Fixed by awaiting the async operation. Updated existing feature commit and pushed. Re-tested.
Bonus screenshot:

Contributor License Agreement
By submitting this pull request, I confirm that I have read and fully agree to the Contributor License Agreement (CLA), and I am providing my contributions under its terms.
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.