Skip to content

feat(mcp): add browser OAuth for remote servers - #5316

Merged
chengyongru merged 9 commits into
HKUDS:mainfrom
chengyongru:codex/mcp-oauth
Aug 10, 2026
Merged

feat(mcp): add browser OAuth for remote servers#5316
chengyongru merged 9 commits into
HKUDS:mainfrom
chengyongru:codex/mcp-oauth

Conversation

@chengyongru

@chengyongru chengyongru commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add browser-based OAuth for remote Streamable HTTP and SSE MCP servers using the official MCP SDK
  • add one-click Xmind, Notion, and Linear presets, plus OAuth support for imported/custom MCP configs
  • let custom HTTP/SSE servers choose None, OAuth, or Headers directly in the Apps UI
  • make the Apps catalog explicitly describe MCP tools and show clear Connect / Connecting / Connected states
  • persist scoped MCP credentials outside config.json and hot-reload tools after authorization
  • register nanobot branding metadata so provider consent pages can show the project icon and website
  • support OAuth from a remotely accessed plain-HTTP WebUI with a guided full-callback-URL paste flow

UX

The primary flow is Apps -> MCP -> Connect -> provider consent.

  • HTTPS and localhost WebUIs return to nanobot automatically without copying anything
  • a remote plain-HTTP WebUI registers a loopback callback, explains that the localhost page will not
    load, and presents one field for pasting the complete URL from the browser address bar; users do
    not need to identify or extract the authorization code
  • an explicit Cancel action remains available throughout authorization, including after the user
    closes the OAuth popup
  • popup recovery appears only when popup creation is actually blocked; COOP-isolated login tabs are
    no longer mistaken for closed popups
  • the catalog, active connection controls, callback form, and custom MCP form remain within bounds
    from 320px upward; wider two-column layouts only activate when the settings pane has enough usable
    width
  • successful connect/remove/config reload actions are reflected by card state instead of redundant
    implementation-detail notices
  • saving a custom OAuth server treats its initial authorization-required reload result as expected,
    while unrelated or genuine connection failures remain visible
  • failures, restart requirements, and explicit Test results remain visible

Security and compatibility

  • keeps MCP OAuth separate from provider OAuth because their lifecycle and storage contracts differ
  • uses PKCE, one-use state, bounded five-minute gateway flows, and an exact callback path
  • validates pasted callbacks against the active flow's exact scheme, authority, path, and state before
    consuming the one-use state
  • sends a pasted callback through an authenticated WebSocket mutation payload rather than a URL query
    or unauthenticated HTTP mutation
  • validates configured URLs and every OAuth discovery, registration, token, refresh, and redirect
    request with the existing SSRF guard and pinned DNS transport
  • stores credentials atomically outside config, scoped by server name and URL fingerprint, and never
    returns tokens in WebUI payloads
  • avoids browser/discovery side effects during background startup
  • preserves existing bearer-header/PAT MCP configs; OAuth inference only applies to exact known URLs
    without explicit headers

Verification

  • 153 passed — MCP OAuth, MCP tool, preset, settings route, and WebSocket HTTP regression tests
  • 986 passed — complete WebUI test suite
  • basedpyright — 0 errors, 0 warnings, 0 notes
  • ruff check and bun run lint — passed
  • bun run build — passed
  • isolated real gateway/browser checks verified custom None/OAuth/Headers setup, a real save and refresh,
    suppression of the expected pre-authorization reload failure, and no horizontal overflow at 320px
    or desktop width; browser console remained at 0 errors and 0 warnings
  • live Xmind handoff verified popup-close cancellation; live Linear consent verified the 180x180
    nanobot logo and project website

The live smoke tests intentionally stopped before third-party account consent. Callback exchange,
persistence, reconnect, expiry, cancellation, and hot-reload paths are covered by deterministic tests.

Closes #5297

@chengyongru chengyongru added priority: p2 Normal backlog: minor bug, enhancement, docs, cleanup, edge case, or unvalidated proposal. feature webui security test documentation Improvements or additions to documentation labels Aug 10, 2026
@chengyongru
chengyongru force-pushed the codex/mcp-oauth branch 2 times, most recently from 7a6da99 to fedc6ee Compare August 10, 2026 08:38
@chengyongru
chengyongru merged commit 8e77f3f into HKUDS:main Aug 10, 2026
6 checks passed
@chengyongru

Copy link
Copy Markdown
Collaborator Author

ux待优化, 但是这个pr已经不适合再扩张了

@chengyongru
chengyongru deleted the codex/mcp-oauth branch August 10, 2026 15:44
fuutott added a commit to fuutott/nanobot that referenced this pull request Aug 14, 2026
…actor, exec/security hardening

Absorbed from HKUDS/nanobot (b3b0517..221e8a4):
- feat(mcp) HKUDS#5316: native browser OAuth for remote MCP servers (mcp_oauth.py +
  webui/mcp_oauth_api.py; auth: "oauth" marker; MCP SDK OAuthClientProvider).
- refactor HKUDS#5343: move MCP lifecycle out of AgentLoop into an MCPProvider;
  from_config now takes a caller-owned tool_registry; close_mcp -> aclose.
- Security: exec.allowPatterns shell-chain bypass fix, exec path-guard hardening
  (double-slash / redirection / named-user home), Docker privilege-drop
  hardening, stop leaking API keys into subprocess env, web credential-URL
  redaction (keep creds out of logs / Jina reader / SW caches).
- WebUI: tabbed pane workbench (HKUDS#5322), MCP management dialog, PWA support,
  native folder picker, settings split by domain (HKUDS#5323), session drag polish.
- Providers: DeepSeek V4 Pro Responses; gemini tool-history across model switch.
- Session: history stored outside workspace, symlink-migration rejection,
  storage relocation.

Local customisations — MCP OAuth now COEXISTS with upstream's native flow
(upstream is browser-only; our fork keeps the headless path for containers):
- schema.py: unified auth field to `OAuthConfig | Literal["oauth"] | None`
  ("oauth" -> upstream browser flow; OAuthConfig object -> our headless
  device_code/client_credentials).
- mcp.py: re-applied _resolve_oauth_token bearer injection inside upstream's new
  open_single_server, selected by auth-type; upstream's oauth_auth httpx.Auth
  handles the "oauth" marker. request_headers now also feeds streamableHttp.
- loop.py: _oauth_refresh_loop reads self.tools_config.mcp_servers (post-HKUDS#5343
  self._mcp_servers is gone), gated to isinstance(cfg.auth, OAuthConfig); kept
  our from_config subagent factory + tool_registry param together.
- Dropped orphaned fork reload_single_server (its module-level deps were removed
  by HKUDS#5343); the refresh loop now refreshes tokens on disk and the live session
  picks them up on next reconnect (reactive, matching upstream). Removed its
  two fork tests; fixed test_oauth_refresh_task to use tools_config.
- docker-compose.yml: kept SYS_ADMIN + unconfined apparmor/seccomp for the
  bubblewrap sandbox (upstream's no-new-privileges model breaks bwrap; we run
  as nanobottie via direct USER, not upstream's entrypoint drop).
- conftest.py: kept fork plugin-path setup alongside upstream's session
  isolation fixture. strip_history / web exclusive-timeout / utf-8-sig / mcp_auth
  CLI / plugin channels all preserved.

Verified: ruff clean; host sweep ~4270 passed. One expected Windows-only
failure (test_equivalent_workspace_paths_share_one_store: WinError 1314 on an
unguarded symlink() in a new upstream test — passes on Linux).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation feature priority: p2 Normal backlog: minor bug, enhancement, docs, cleanup, edge case, or unvalidated proposal. security test webui

Projects

None yet

Development

Successfully merging this pull request may close these issues.

希望mcp增加oauth网页授权功能

1 participant