This PR addresses issues discussed in #20858 — OAuth 2.1 MCP tools set as model defaults fail silently and tokens can expire mid-session with no proactive renewal.
No pre-flight check: Users have no way to know that one of their default OAuth 2.1 tools is not yet authenticated. The chat silently fails with "Failed to connect to MCP server" and no prompt to authorize.
No mid-session reauth: If a refresh token expires for any reason during a conversation, the pre-flight check cannot catch it. There is currently no mechanism to re-authenticate without leaving the chat.
Out of scope: This PR does not address the API/headless path (raised by @jk-f5 in #20858) — when OAuth 2.1 MCP tools are called programmatically without a browser UI. That scenario requires a different approach and should be handled in a separate PR.
Solution
Pre-flight toast (before sending a message):
When a user selects a model with unauthenticated OAuth MCP tools, a persistent toast.warning appears with an "Authorize" button
Clicking opens the OAuth flow in a popup window — the user never leaves the chat
On success, the popup signals the parent via localStorage, the toast auto-dismisses, and the tool becomes available
Mid-session reauth toast (during a conversation):
If a 401 is detected from an MCP server during message processing, the backend emits an oauth_reauth_toast event
The frontend shows the same toast+popup flow, allowing the user to re-authorize without losing their conversation
Pull Request Checklist
Target branch:dev
Description: provided above
Changelog: see below
Dependencies: no new dependencies
Testing: manual tests performed, see steps and screenshots below
Code review: self-reviewed
Design & Architecture: no new settings introduced; uses existing patterns (Banner component, periodic_* task pattern, get_sessions_by_user_id pattern)
Pre-flight auth: Selected a model with default OAuth MCP tools → toast appears → clicked "Authorize" → popup opens → completed OAuth → popup closes → toast auto-dismisses → tool works
Mid-session reauth: Manually deleted OAuth session tokens from the database to simulate token expiration → sent a message using the MCP tool → mid-session toast appears → clicked "Authorize" → completed OAuth → tool reconnects and works
Non-invasive UX: Verified that the chat remains fully usable even when the OAuth toast is displayed — users can continue chatting, and the toast does not block any interaction
Approach inspired by @Lemmons ' interrupt-based flow from their branch. Simplified to use toasts instead of modal dialogs, and duck-typed 401 detection instead of a custom exception class.
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.
Note
Deleting the CLA section will lead to immediate closure of your PR and it will not be merged in.
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.
## 📋 Pull Request Information
**Original PR:** https://github.com/open-webui/open-webui/pull/22341
**Author:** [@MedlockM](https://github.com/MedlockM)
**Created:** 3/6/2026
**Status:** 🔄 Open
**Base:** `dev` ← **Head:** `fix/mcp-oauth-default-tools-and-token-refresh`
---
### 📝 Commits (4)
- [`d20fcde`](https://github.com/open-webui/open-webui/commit/d20fcdee087459c36c688be563952d0b2d7f7848) refactor: simplify MCP OAuth 2.1 401 detection in middleware
- [`3c1c4e1`](https://github.com/open-webui/open-webui/commit/3c1c4e100cce6e99ecd06a6317b918fc8ab7c870) refactor: simplify MCP OAuth callback redirect logic
- [`0420874`](https://github.com/open-webui/open-webui/commit/0420874806840edcb2821d12b0fa0846f640c938) refactor: remove dead code from OAuth popup flow
- [`6fe57a8`](https://github.com/open-webui/open-webui/commit/6fe57a88a76ec474dd8035bb781ebff135c21cc2) Merge branch 'dev' into fix/mcp-oauth-default-tools-and-token-refresh
### 📊 Changes
**6 files changed** (+153 additions, -5 deletions)
<details>
<summary>View changed files</summary>
📝 `backend/open_webui/main.py` (+6 -0)
📝 `backend/open_webui/utils/mcp/client.py` (+3 -2)
📝 `backend/open_webui/utils/middleware.py` (+34 -0)
📝 `backend/open_webui/utils/oauth.py` (+9 -2)
📝 `src/lib/components/chat/Chat.svelte` (+91 -1)
📝 `src/routes/+layout.svelte` (+10 -0)
</details>
### 📄 Description
> This PR addresses issues discussed in [#20858](https://github.com/open-webui/open-webui/discussions/20858) — OAuth 2.1 MCP tools set as model defaults fail silently and tokens can expire mid-session with no proactive renewal.
## Problem
Two issues identified in #20858:
1. **No pre-flight check**: Users have no way to know that one of their default OAuth 2.1 tools is not yet authenticated. The chat silently fails with "Failed to connect to MCP server" and no prompt to authorize.
2. **No mid-session reauth**: If a refresh token expires for any reason during a conversation, the pre-flight check cannot catch it. There is currently no mechanism to re-authenticate without leaving the chat.
**Out of scope**: This PR does not address the API/headless path (raised by @jk-f5 in #20858) — when OAuth 2.1 MCP tools are called programmatically without a browser UI. That scenario requires a different approach and should be handled in a separate PR.
## Solution
**Pre-flight toast (before sending a message):**
- When a user selects a model with unauthenticated OAuth MCP tools, a persistent toast.warning appears with an "Authorize" button
- Clicking opens the OAuth flow in a popup window — the user never leaves the chat
- On success, the popup signals the parent via localStorage, the toast auto-dismisses, and the tool becomes available
**Mid-session reauth toast (during a conversation):**
- If a 401 is detected from an MCP server during message processing, the backend emits an oauth_reauth_toast event
- The frontend shows the same toast+popup flow, allowing the user to re-authorize without losing their conversation
## Pull Request Checklist
- [x] **Target branch:** `dev`
- [x] **Description:** provided above
- [x] **Changelog:** see below
- [x] **Dependencies:** no new dependencies
- [x] **Testing:** manual tests performed, see steps and screenshots below
- [x] **Code review:** self-reviewed
- [x] **Design & Architecture:** no new settings introduced; uses existing patterns (`Banner` component, `periodic_*` task pattern, `get_sessions_by_user_id` pattern)
- [x] **Git Hygiene:** single atomic commit, rebased on `dev`
- [x] **Title Prefix:** `fix:`
## Testing
Tested against 4 OAuth 2.1 MCP servers (Atlassian, Zapier, Notion, Linear).
- **Pre-flight auth**: Selected a model with default OAuth MCP tools → toast appears → clicked "Authorize" → popup opens → completed OAuth → popup closes → toast auto-dismisses → tool works
- **Mid-session reauth**: Manually deleted OAuth session tokens from the database to simulate token expiration → sent a message using the MCP tool → mid-session toast appears → clicked "Authorize" → completed OAuth → tool reconnects and works
- **Non-invasive UX**: Verified that the chat remains fully usable even when the OAuth toast is displayed — users can continue chatting, and the toast does not block any interaction
---
# Changelog Entry
### Fixed
| File | Change |
|------|--------|
| `src/lib/components/chat/Chat.svelte` | Pre-flight toast logic, mid-session `oauth_reauth_toast` handler, OAuth popup storage listener |
| `src/routes/+layout.svelte` | Popup detection: signal parent via localStorage and close |
| `backend/open_webui/utils/middleware.py` | Detect 401 in ExceptionGroup from MCP servers, emit `oauth_reauth_toast` event |
| `backend/open_webui/utils/mcp/client.py` | Defensive `disconnect()` fix |
| `backend/open_webui/utils/oauth.py` | `return_url` parameter in `handle_callback` for popup redirect |
| `backend/open_webui/main.py` | Pass `return_url` through authorize/callback endpoints |
---
### Screenshots or Videos
**Video of the pre-flight oauth process**
[Capture vidéo du 2026-03-13 19-57-15.webm](https://github.com/user-attachments/assets/80af1c48-cb38-4e9f-b54f-73130ab68f43)
**Video of the mid-session oauth process**
[Capture vidéo du 2026-03-13 18-39-58.webm](https://github.com/user-attachments/assets/71d413f7-5b2e-4b24-b988-c3ccea231f59)
**Multiple unauthenticated tools — stacked toasts:**
<img width="1859" height="1081" alt="image" src="https://github.com/user-attachments/assets/be915bd7-57f5-415a-a714-88ad3fbc2e00" />
### Credit
Approach inspired by @Lemmons ' interrupt-based flow from [their branch](https://github.com/open-webui/open-webui/compare/main...Lemmons:open-webui:Lemmons/openwebui-oauth2.1-fix). Simplified to use toasts instead of modal dialogs, and duck-typed 401 detection instead of a custom exception class.
### Contributor License Agreement
<!--
🚨 DO NOT DELETE THE TEXT BELOW 🚨
Keep the "Contributor License Agreement" confirmation text intact.
Deleting it will trigger the CLA-Bot to INVALIDATE your PR.
-->
By submitting this pull request, I confirm that I have read and fully agree to the [Contributor License Agreement (CLA)](https://github.com/open-webui/open-webui/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT), and I am providing my contributions under its terms.
> [!NOTE]
> Deleting the CLA section will lead to immediate closure of your PR and it will not be merged in.
---
<sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
📋 Pull Request Information
Original PR: https://github.com/open-webui/open-webui/pull/22341
Author: @MedlockM
Created: 3/6/2026
Status: 🔄 Open
Base:
dev← Head:fix/mcp-oauth-default-tools-and-token-refresh📝 Commits (4)
d20fcderefactor: simplify MCP OAuth 2.1 401 detection in middleware3c1c4e1refactor: simplify MCP OAuth callback redirect logic0420874refactor: remove dead code from OAuth popup flow6fe57a8Merge branch 'dev' into fix/mcp-oauth-default-tools-and-token-refresh📊 Changes
6 files changed (+153 additions, -5 deletions)
View changed files
📝
backend/open_webui/main.py(+6 -0)📝
backend/open_webui/utils/mcp/client.py(+3 -2)📝
backend/open_webui/utils/middleware.py(+34 -0)📝
backend/open_webui/utils/oauth.py(+9 -2)📝
src/lib/components/chat/Chat.svelte(+91 -1)📝
src/routes/+layout.svelte(+10 -0)📄 Description
Problem
Two issues identified in #20858:
No pre-flight check: Users have no way to know that one of their default OAuth 2.1 tools is not yet authenticated. The chat silently fails with "Failed to connect to MCP server" and no prompt to authorize.
No mid-session reauth: If a refresh token expires for any reason during a conversation, the pre-flight check cannot catch it. There is currently no mechanism to re-authenticate without leaving the chat.
Out of scope: This PR does not address the API/headless path (raised by @jk-f5 in #20858) — when OAuth 2.1 MCP tools are called programmatically without a browser UI. That scenario requires a different approach and should be handled in a separate PR.
Solution
Pre-flight toast (before sending a message):
Mid-session reauth toast (during a conversation):
Pull Request Checklist
devBannercomponent,periodic_*task pattern,get_sessions_by_user_idpattern)devfix:Testing
Tested against 4 OAuth 2.1 MCP servers (Atlassian, Zapier, Notion, Linear).
Changelog Entry
Fixed
src/lib/components/chat/Chat.svelteoauth_reauth_toasthandler, OAuth popup storage listenersrc/routes/+layout.sveltebackend/open_webui/utils/middleware.pyoauth_reauth_toasteventbackend/open_webui/utils/mcp/client.pydisconnect()fixbackend/open_webui/utils/oauth.pyreturn_urlparameter inhandle_callbackfor popup redirectbackend/open_webui/main.pyreturn_urlthrough authorize/callback endpointsScreenshots or Videos
Video of the pre-flight oauth process
Capture vidéo du 2026-03-13 19-57-15.webm
Video of the mid-session oauth process
Capture vidéo du 2026-03-13 18-39-58.webm
Multiple unauthenticated tools — stacked toasts:

Credit
Approach inspired by @Lemmons ' interrupt-based flow from their branch. Simplified to use toasts instead of modal dialogs, and duck-typed 401 detection instead of a custom exception class.
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.