Add provider-declared OAuth bootstrap; run provider setup before registration#26
Merged
Conversation
OAuth bootstrap: a provider YAML can now declare a one-time browser consent flow with a top-level oauth: block (type: google) naming its client_secret.json, the token file to mint, and the scopes. The new oauth_bootstrap module reuses the existing authorization_code machinery — flows register in AuthCodeTokenStore._pending_flows tagged with a kind, URLs publish to the pending-auth banner, and the shared /oauth/callback dispatches non-REST flows back to it — and builds the consent URL with PKCE, access_type=offline, and prompt=consent. The exchanged token is written in Credentials.to_json() format so provider code keeps using Credentials.from_authorized_user_file() unchanged, with refresh handled by the google libs at call time. Wiring: a startup oauth-warmup thread surfaces missing tokens in the banner; POST /api/oauth-bootstrap (and a 🔐 Authorize button with a read-only summary in the provider editor) restarts the flow on demand; /api/tools exposes per-provider token status for a list badge; the oauth block round-trips through the structured editor and is validated (including a client_secret_file existence check that points at the Files manager). Also: bootstrap_provider now runs requirements/setup before registration, eliminating the noisy failed-first-attempt tracebacks for code providers that import their declared requirements at module level (the previous install-then-retry behavior is no longer needed). 41 new tests cover the flow begin/complete, token-file format (loaded back via google-auth), refresh-token salvage and consent-hint error, callback dispatch (with REST regression), warm-up, endpoints, round-trip, and validation. https://claude.ai/code/session_01WnK1rtXGHDCNpsycAvxFqC
…he README
The file manager and tool tester shipped without README coverage, and
the OpenAI-compatible /v1/tools endpoints were only mentioned as a port
label. Adds Web UI subsections for both features (including the
MCPPROXY_MAX_UPLOAD_BYTES cap and the restart-to-populate registry
note) and documents the GET /v1/tools + POST /v1/tools/{name}/invoke
endpoints they build on.
https://claude.ai/code/session_01WnK1rtXGHDCNpsycAvxFqC
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two follow-ups from running the gmail-filters code provider:
google.oauth2.credentials.Credentials.from_authorized_user_file()loads. No more runningInstalledAppFlowon a laptop and copyinggmail_token.jsoninto the container.ModuleNotFoundErrortracebacks for code providers that import their declared requirements at module level.OAuth bootstrap (
oauth:block)How it works
oauth_bootstrap.pymodule reuses the existing authorization_code machinery: flows register inAuthCodeTokenStore._pending_flowstaggedkind: "google", consent URLs publish intopending_rest_auth(so the existing yellow banner shows them with zero polling changes), and the sharedGET /oauth/callbackdispatches non-REST flows back via a two-line hook inrest_provider.py.access_type=offline, andprompt=consent(what makes Google issue a refresh_token).client_secret.jsonis parsed for bothinstalledandwebclient types.Credentials.to_json()format, so provider code keeps callingCredentials.from_authorized_user_file()unchanged and the google libs refresh at call time. If Google omits the refresh_token (prior silent consent), an existing one is salvaged from the old token file, else a clear error explains the revoke-and-reconsent fix.oauth-warmupthread surfaces missing tokens in the banner before the first failed tool call (gated byMCPPROXY_WARM_REMOTElike the other warm-ups).UI / API
POST /api/oauth-bootstrap {name}begins/restarts the flow on demand./api/toolsexposes per-provider token status.oauth:block round-trips through the structured editor and is validated (type/paths/scopes, plus aclient_secret_fileexistence check that points at the Files manager).prompt=consentcaveat.Setup-before-register
bootstrap_provider()now runsrun_provider_setup()first, thenregister_provider()— both steps already ran sequentially before the MCP server starts, so startup time is unchanged. A setup failure still doesn't block registration (tools advertise and surface errors at call time), and the previous install-then-retry path is no longer needed.Testing
google-auth), refresh-token salvage and the consent-hint error, callback dispatch with a REST-flow regression test, warm-up behavior, the new endpoint, structured round-trip, and validation./api/oauth-bootstrap→/oauth/callback→ token file loadable byCredentials.from_authorized_user_file()→ banner cleared → list badge flips.node --check; the Dockerfile guard test caught and verified the new module's COPY line.https://claude.ai/code/session_01WnK1rtXGHDCNpsycAvxFqC
Generated by Claude Code