Conversation
Claude Desktop and its Cowork mode can route through Databricks AI Gateway, but unlike Claude Code (a CLI ug spawns, whose lifetime bounds the refresh proxy) Desktop is a standalone GUI with a static config file. Writing tokens into that file leaves them to go stale — the Databricks OAuth token expires in ~1h. So `ug claude-cowork` keeps the refresh-proxy model: - Establishes both auth sessions: the Databricks OAuth session (so the proxy can mint swap credentials) and the Anthropic subscription OAuth via `claude setup-token` (long-lived, relayed by the proxy). - Starts the loopback refresh proxy, which live-refreshes the Databricks credential into the X-Databricks-AI-Gateway-Token swap header and stamps the fixed Anthropic Authorization + the Databricks-Model-Provider-Service routing header on every request. The Desktop client's own credential is irrelevant: the proxy drops it and owns the upstream auth. - Writes a Claude Desktop gateway config pointing at that proxy, holding no real tokens, so nothing on disk can go stale. - Stays in the foreground to keep the proxy alive while Desktop is used. The proxy gains two backward-compatible knobs to support a non-Claude-Code client: `extra_headers` (fixed headers the proxy owns and stamps per request) and `strip_client_headers` (client headers to drop so a stray x-api-key can't reach Anthropic alongside the injected Authorization). Experimental: the Desktop gateway-config schema (Claude-3p configLibrary) is not a public contract; keys are the observed-working set and may drift. Testing: `uv run ruff check` clean; new tests for config rendering, per-workspace config path, setup-token/env auth resolution, and the two proxy knobs; full non-e2e suite green (`uv run pytest -k "not e2e"`). Co-authored-by: Isaac <no-reply@databricks.com>
A missing `databricks` binary raised a raw FileNotFoundError out of
get_databricks_token (which _ensure_databricks_session only guarded with
`except RuntimeError`), so the launch crashed with a traceback instead of a
clear message. Check `shutil.which("databricks")` up front with an install
hint, and also catch FileNotFoundError on the probe so a CLI that vanishes
after the check still falls through to the explicit login.
Co-authored-by: Isaac <no-reply@databricks.com>
…h Desktop
Dropping a file in configLibrary/ wasn't enough — Desktop only sees an entry
listed in configLibrary/_meta.json ("entries") and only uses the one set as
"appliedId". So the gateway never came up. Register the ug entry and apply it,
preserving existing entries (e.g. the user's Default) and restoring the prior
appliedId on exit so Desktop's next restart doesn't boot into the dead-proxy
config.
Also correct the config schema against a Desktop-authored entry: the discovery
toggle is modelDiscoveryEnabled:false (not the invented disableDeploymentModeChooser),
and chatTabEnabled/coworkTabEnabled/modelPrefer1mContext aren't real keys — drop
them. Write the file mode 0600 to match Desktop's own entries. Credentials/headers
stay out of the file; the proxy injects them.
Co-authored-by: Isaac <no-reply@databricks.com>
The launch parked the main thread on signal.pause() while a daemon thread served
the proxy. signal.pause() returns on ANY caught signal, so a stray SIGCHLD/SIGPIPE/
SIGWINCH during the first inference woke it, the main thread fell through, and the
proxy was torn down mid-session ("connection broke as soon as I did an inference").
Serve on the main thread with server.serve_forever(), which unwinds only via
shutdown() or the KeyboardInterrupt SIGINT raises, and drop the now-unused signal
and threading imports.
Co-authored-by: Isaac <no-reply@databricks.com>
…dispatch Add `--open/--no-open` (default on) so `ug claude-cowork` brings Desktop up with no manual step. Since Desktop reads configLibrary only at startup, a running instance is gracefully quit (a normal Quit AppleEvent) and reopened. Platform dispatch, because Desktop is macOS/Windows-only: - macOS: quit-if-running + `open -b com.anthropic.claudefordesktop` (implemented, verified). - WSL: the app is the Windows one — print guidance (interop launch is a follow-up needing a real WSL box). - Windows: print guidance (follow-up needing a Windows box). - native Linux: no Desktop exists — point at `--proxy-only` + tunnel to a Mac/Windows Desktop. Co-authored-by: Isaac <no-reply@databricks.com>
"Claude Desktop" is the app (Cowork is a mode within it), so name the command after the app. Renames the command + handler and the user-facing strings/UA. The internal configLibrary entry-id seed is left unchanged (with a comment) so existing registered entries keep their stable ids across the rename. Also drops a dangling `--proxy-only` reference from the Linux message (that flag isn't implemented). Co-authored-by: Isaac <no-reply@databricks.com>
masonc08
marked this pull request as ready for review
September 15, 2026 00:03
Drop comments that narrate what the code already says and compress the verbose module/constant docstrings; keep only the non-obvious rationale (the signal.pause regression, the _meta.json visibility contract, the credential-swap header drops). No behavior change. Co-authored-by: Isaac <no-reply@databricks.com>
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.
What & why
Claude Desktop and its Cowork mode can route through Databricks AI Gateway, but unlike Claude Code (a CLI
ugspawns, whose lifetime bounds the refresh proxy) Desktop is a standalone GUI with a static config file. Writing tokens into that file leaves them to go stale — the Databricks OAuth token expires in ~1h. Soug claude-coworkkeeps the refresh-proxy modelug claudealready uses.ug claude-cowork --provider <catalog.schema.name>:claude setup-token(long-lived, relayed by the proxy).CLAUDE_CODE_OAUTH_TOKENis honored to skip the browser flow.X-Databricks-AI-Gateway-Tokenswap header and stamps the fixed AnthropicAuthorization+ theDatabricks-Model-Provider-Servicerouting header on every request. The Desktop client's own credential/auth-scheme is irrelevant: the proxy drops it and owns the upstream auth.Claude-3p/configLibrary, one stable entry per workspace) pointing at that proxy — holding no real tokens, so nothing on disk can go stale.The proxy gains two backward-compatible knobs to support a non-Claude-Code client:
extra_headers— fixed headers the proxy owns and stamps per request (the injectedAuthorization+ the MPS routing header).strip_client_headers— client headers to drop, so a strayx-api-keycan't reach Anthropic alongside the injectedAuthorization.Status / caveats
Experimental. The Desktop gateway-config schema (
Claude-3pconfigLibrary) is not a public contract; the keys here are the observed-working set and may drift across Desktop releases. Discovery relies on--modelpopulatinginferenceModelsso Desktop skips itsGET /v1/modelsprobe, which the relayed path does not serve yet (a companion gateway route change is in flight).Follow-ups: promote the foreground proxy to a background daemon so Desktop can run independently; refresh the Anthropic OAuth in-proxy; drop the manual
--modellist once relayed/v1/modelsships.How do you know it works?
uv run ruff check/ruff formatclean.setup-token/env auth resolution, and the two proxy knobs (extra_headersoverwrite +strip_client_headers).uv run pytest -k "not e2e") except one pre-existing PTY test (test_restores_model_captured_immediately_before_switch) that fails identically on cleanmain.ug claude-cowork --helpregisters and parses.This pull request and its description were written by Isaac.