Skip to content

context_server: Add OAuth support to HTTP transport#44638

Closed
erenatas wants to merge 2 commits intozed-industries:mainfrom
erenatas:erenatas/mcp-oauth-support
Closed

context_server: Add OAuth support to HTTP transport#44638
erenatas wants to merge 2 commits intozed-industries:mainfrom
erenatas:erenatas/mcp-oauth-support

Conversation

@erenatas
Copy link

@erenatas erenatas commented Dec 11, 2025

This change implements RFC 6749 OAuth 2.0 Authorization Code flow with PKCE
(RFC 7636) and enables OAuth support including
re-authentication and session handling for Streamable HTTP transport MCP servers.

Authentication Flow

Initial Connection (no token)

  1. Client sends request without Authorization header
  2. Server returns 401 with WWW-Authenticate header containing:
    • resource_metadata URL for OAuth discovery
    • requested scopes
  3. Client discovers OAuth endpoints via .well-known/oauth-authorization-server
  4. Client performs dynamic client registration (if supported)
  5. Client opens browser for user authorization with PKCE challenge
  6. Local callback server receives authorization code
  7. Client exchanges code for access_token + refresh_token
  8. Tokens are persisted to system keychain (with file fallback)
  9. Original request is retried with Bearer token

Subsequent Requests (cached token)

  1. OAuthManager.access_token() returns cached token
  2. If token is expired/expiring (within 30s), refresh is attempted
  3. If refresh fails, request proceeds without auth → 401 triggers re-login

Re-authentication (token expired during session)

  1. Request with expired token returns 401 + WWW-Authenticate
  2. handle_unauthorized() parses header and calls OAuthManager
  3. OAuthManager performs full login flow (browser auth)
  4. Session is proactively re-initialized (new Mcp-Session-Id)
  5. Original request is retried with new token

Notes

  • OAuth is ONLY triggered by 401 response, never proactively
  • Manual Authorization: Bearer <token> headers bypass OAuth entirely
  • Token refresh happens transparently before requests
  • Session re-initialization after re-auth avoids 422 errors
  • Tokens are scoped per server URL for multi-server support

Implementation Details

Typed Error System

  • Add ContextServerError enum with variants: AuthRequired, Connection, Protocol, Other
  • Update ContextServerStatus::Error to use typed ContextServerError
  • Propagate typed errors through transport_error() in Client, Protocol, and ContextServer

Explicit Authentication Flow

  • Prevent automatic browser opening for authentication - require user action
  • Add authenticate() method for explicit user-triggered auth
  • Add needs_authentication() to check if auth is required
  • Add logout() to clear tokens and disable server
  • Clear session ID on re-authentication to avoid stale session errors
  • Add allow_auto_reauthentication setting for opt-in silent token refresh

UI Changes on agent_ui

  • Add "Authenticate" button in Agent Settings for servers needing auth
  • Add "Logout" button when authenticated (disables server on logout)
  • Show server as disabled with error message when auth expires
  • Add "Authenticate" button in configure_context_server_modal when auth required
  • Restart server automatically after successful authentication

Settings

  • Add allow_auto_reauthentication to ContextServerSettings
  • Added protocol version to "2025-06-18" to have compatibility with MCP servers, the existing functionality over Zed does not have breaking change between the previous and this one.

Tested against a real OAuth enabled MCP server

Closes #43162

Release Notes:

  • MCP: OAuth support for Streamable HTTP Transport

@cla-bot
Copy link

cla-bot bot commented Dec 11, 2025

We require contributors to sign our Contributor License Agreement, and we don't have @erenatas on file. You can sign our CLA at https://zed.dev/cla. Once you've signed, post a comment here that says '@cla-bot check'.

@erenatas
Copy link
Author

@cla-bot check

@cla-bot
Copy link

cla-bot bot commented Dec 11, 2025

We require contributors to sign our Contributor License Agreement, and we don't have @erenatas on file. You can sign our CLA at https://zed.dev/cla. Once you've signed, post a comment here that says '@cla-bot check'.

@cla-bot
Copy link

cla-bot bot commented Dec 11, 2025

The cla-bot has been summoned, and re-checked this pull request!

@erenatas
Copy link
Author

@cla-bot check

@cla-bot
Copy link

cla-bot bot commented Dec 11, 2025

We require contributors to sign our Contributor License Agreement, and we don't have @erenatas on file. You can sign our CLA at https://zed.dev/cla. Once you've signed, post a comment here that says '@cla-bot check'.

@cla-bot
Copy link

cla-bot bot commented Dec 11, 2025

The cla-bot has been summoned, and re-checked this pull request!

@erenatas
Copy link
Author

@cla-bot check

@cla-bot cla-bot bot added the cla-signed The user has signed the Contributor License Agreement label Dec 11, 2025
@cla-bot
Copy link

cla-bot bot commented Dec 11, 2025

The cla-bot has been summoned, and re-checked this pull request!

@erenatas erenatas force-pushed the erenatas/mcp-oauth-support branch from 33c59e8 to f824271 Compare December 11, 2025 15:39
@erenatas erenatas force-pushed the erenatas/mcp-oauth-support branch 3 times, most recently from 9f2d708 to 2136852 Compare December 11, 2025 21:14
@SomeoneToIgnore SomeoneToIgnore added the area:ai Improvement related to Agent Panel, Edit Prediction, Copilot, or other AI features label Dec 11, 2025
@tzutoo
Copy link

tzutoo commented Dec 12, 2025

It seems the tool call timeout setting not working with http transport support.

@maxdeviant maxdeviant changed the title feat(mcp): OAuth support on Streamable HTTP mcp: Add OAuth support to streamable HTTP Dec 12, 2025
@maxdeviant maxdeviant changed the title mcp: Add OAuth support to streamable HTTP context_server: Add OAuth support to HTTP transport Dec 12, 2025
@erenatas
Copy link
Author

It seems the tool call timeout setting not working with http transport support.

Can you open an issue for this? It sounds like a different issue compared to what this PR tries to address to.

@agu-z
Copy link
Contributor

agu-z commented Dec 16, 2025

@erenatas Thanks for the contribution! I haven’t had a chance to dig into this deeply yet, but here are some initial thoughts:

  1. We should surface the server’s authentication status in the MCP section of the Agent Panel settings.
  2. When logged in, the user should be able to explicitly log out, and vice versa.
  3. We shouldn’t open the browser without an explicit user action. Instead:
    a. The user should trigger it from agent settings
    b. Optional: Display a little callout under the "MCP Servers" section in the ... menu that triggers auth on click.
    c. Show a one-time toast per server when it’s first installed and authentication is required. We can persist whether we have shown this already in the KV store.
  4. We should store the token in the system keychain rather than in a file.

@agu-z agu-z moved this from Community PRs to In progress in Quality Week – December 2025 Dec 16, 2025
@erenatas
Copy link
Author

@erenatas Thanks for the contribution! I haven’t had a chance to dig into this deeply yet, but here are some initial thoughts:

  1. We should surface the server’s authentication status in the MCP section of the Agent Panel settings.

  2. When logged in, the user should be able to explicitly log out, and vice versa.

  3. We shouldn’t open the browser without an explicit user action. Instead:

    • The user should trigger it from agent settings
    • Optional: Display a little callout under the "MCP Servers" section in the ... menu that triggers auth on click.
    • Show a one-time toast per server when it’s first installed and authentication is required. We can persist whether we have show this already in the KV store.
  4. We should store the token in the system keychain rather than in a file.

Thanks a lot for comments! Would you prefer getting them done in this PR or as a follow up considering the size of the change? I can work on it this week.

@agu-z
Copy link
Contributor

agu-z commented Dec 16, 2025

@erenatas Awesome! We could split this, but I don’t want to merge before addressing points 3 and 4.
In particular, point 3 implies there must be some UI to initiate authentication (for example, via the affordances described in points 1 and 2). The mechanisms in 3.b and 3.c, however, can be implemented independently.

This change implements RFC 6749 OAuth 2.0 Authorization Code flow with PKCE ([RFC 7636](https://oauth.net/2/pkce/)) and enables OAuth support including re-authentication and session handling for Streamable HTTP transport  MCP servers.

1. Client sends request without Authorization header
2. Server returns 401 with WWW-Authenticate header containing:
   - resource_metadata URL for OAuth discovery
   - requested scopes
3. Client discovers OAuth endpoints via .well-known/oauth-authorization-server
4. Client performs dynamic client registration (if supported)
5. Client opens browser for user authorization with PKCE challenge
6. Local callback server receives authorization code
7. Client exchanges code for access_token + refresh_token
8. Tokens are persisted to ~/.config/zed/oauth_tokens.json
9. Original request is retried with Bearer token

1. OAuthManager.access_token() returns cached token
2. If token is expired/expiring (within 30s), refresh is attempted
3. If refresh fails, request proceeds without auth → 401 triggers re-login

1. Request with expired token returns 401 + WWW-Authenticate
2. handle_unauthorized() parses header and calls OAuthManager
3. OAuthManager performs full login flow (browser auth)
4. Session is proactively re-initialized (new Mcp-Session-Id)
5. Original request is retried with new token

- OAuth is ONLY triggered by 401 response, never proactively
- Manual `Authorization: Bearer <token>` headers bypass OAuth entirely
- Token refresh happens transparently before requests
- Session re-initialization after re-auth avoids 422 errors
- Tokens are scoped per server URL for multi-server support

Signed-off-by: Eren Atas <eren_atas@hotmail.com>
@erenatas erenatas force-pushed the erenatas/mcp-oauth-support branch from 2136852 to 7634561 Compare December 17, 2025 23:22
@erenatas
Copy link
Author

@erenatas Thanks for the contribution! I haven’t had a chance to dig into this deeply yet, but here are some initial thoughts:

  1. We should surface the server’s authentication status in the MCP section of the Agent Panel settings.
  2. When logged in, the user should be able to explicitly log out, and vice versa.
  3. We shouldn’t open the browser without an explicit user action. Instead:
    a. The user should trigger it from agent settings
    b. Optional: Display a little callout under the "MCP Servers" section in the ... menu that triggers auth on click.
    c. Show a one-time toast per server when it’s first installed and authentication is required. We can persist whether we have shown this already in the KV store.
  4. We should store the token in the system keychain rather than in a file.
  1. This is implemented, in modal where a new MCP server is implemented, and also in the settings page. If for example existing token expires during session, this will also be reflected in settings menu.
  2. User can authenticate/logout under MCP settings in the ... menu. If user authenticates, MCP server will be enabled automatically, vice versa with logout.
  3. I implemented auto reauth flag (disabled by default), otherwise reauth flow during a session will break, but we don't want this to happen. In case this setting is disabled, MCP server error will return authentication error to tool call error: Screenshot 2025-12-16 at 23 46 50
  4. This is done. I was most afraid of this part as I've never done something like this, but fortunately the core bits were already there. If user logs out, we remove the stored secret from keychain, or in the beginning of reauth flow.

@erenatas
Copy link
Author

Hey @agu-z can you approve workflow so I can fix if any check fails?

@agu-z
Copy link
Contributor

agu-z commented Dec 19, 2025

Done! Excited about your changes. Sorry, I couldn't review today, I was too busy. I'll take a look tomorrow.

@erenatas
Copy link
Author

Done! Excited about your changes. Sorry, I couldn't review today, I was too busy. I'll take a look tomorrow.

No worries :) Thanks for having a look!

@agu-z
Copy link
Contributor

agu-z commented Dec 19, 2025

@erenatas I tried this with a few servers earlier today, and none of them were able to connect. I also think the UI needs significant work. While reviewing the code, I realized there are several architectural choices I’d approach differently, so I started working on an alternative implementation and I’m already fairly far along. Given that, I don’t think it makes sense to continue with this PR.

Thanks for the effort regardless, and sorry it didn’t pan out.

@agu-z agu-z closed this Dec 19, 2025
@github-project-automation github-project-automation bot moved this from In progress to Done in Quality Week – December 2025 Dec 19, 2025
@erenatas
Copy link
Author

@erenatas I tried this with a few servers earlier today, and none of them were able to connect. I also think the UI needs significant work. While reviewing the code, I realized there are several architectural choices I’d approach differently, so I started working on an alternative implementation and I’m already fairly far along. Given that, I don’t think it makes sense to continue with this PR.

Thanks for the effort regardless, and sorry it didn’t pan out.

No problem. I would be glad If you have some time to provide feedback regarding what would be your architectural choices instead of my PR, so I would learn. Thanks for looking into it nonetheless.

Also would be happy if you share the PR you are working on, would be happy to examine. Thanks in advance!

@italomaia
Copy link

italomaia commented Dec 26, 2025

Would it not have been better to guide the contributor in this case on what a better approach would look like? Something to consider, as it may lead to recurring contributions.

@erenatas
Copy link
Author

Hey @agu-z , it's been a while. Did you have any chance to look into this? If not, if you can provide me your feedback about how this work should be handled (or what I should not do), I can update it.

cc: @maxdeviant @zelenenka

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:ai Improvement related to Agent Panel, Edit Prediction, Copilot, or other AI features cla-signed The user has signed the Contributor License Agreement

Projects

Development

Successfully merging this pull request may close these issues.

MCP remote server (streamable HTTP) auth flow doesn't trigger

5 participants