
Desktop provider switcher for Claude Code and Codex. Keep every endpoint, API key, and model in one
Desktop provider switcher for Claude Code and Codex. Keep every endpoint, API key, and model in one
One place for every AI-coding endpoint, key and model — switch the active account with a click.
Claude Code, Claude Desktop, Codex, Hermes, OpenCode and now a pool of Cursor accounts, each in its own tab. For anyone who runs more than one endpoint against these tools and is tired of hand-editing settings.json, config.toml and the Windows environment to move between them.
Python backend · Next.js UI · native OS webview — no Electron, no Node at runtime.
The 0.8.0 Cursor pool — six tabs, one account in use, one browser-cookie row with Use disabled, one expired.
Download: grab U-Pool-0.8.0-win64.zip from the latest release, unzip it somewhere you own (not Program Files — Windows will not let the app replace itself there on update) and run U-Pool.exe. It updates itself from then on. Building from source is below.
Platforms: the app, every config-file write and the Cursor pool are cross-platform. Three integrations are Windows-only — the HKCU\Environment writes, launch at sign-in, and the in-app updater — and say so where they appear.
Switching between Anthropic, OpenRouter, DeepSeek, Azure, xAI, a Cursor account and custom relays usually means editing config files — and, on Windows, environment variables — by hand. U-Pool turns that into a desktop app: one list per tool, one click to make an account live, and a health check that measures latency without spending a token. Every write is surgical (it changes only the keys U-Pool owns) and atomic (temp file + os.replace), with a copy left beside every file it touches.
The four that are the whole point:
settings.json / config.toml stay exactly where they are.HKCU\Environment, so a switch writes there too and broadcasts the change; only names U-Pool set are ever removed.GET at the provider's models endpoint. Latency only, no completions, no token cost.The rest, by theme
Safety
| Atomic writes | Temp file + os.replace; a crash mid-write cannot leave a half-written config |
| Backups | One copy beside the original — settings.json.backup and friends — refreshed each change, switchable off |
| First-run import | Whatever is already configured for each of the five provider apps becomes a provider, instead of being overwritten |
| Refuses a broken file | A config that will not parse stops the switch rather than being clobbered |
The apps
| Claude Desktop | A tab, preview only — providers are saved and can be tested, but nothing is written yet |
| Hermes & OpenCode | Their own config writers — config.yaml spliced section by section, opencode.json merged key by key |
| CLI versions | The header reports the installed Claude Code and Codex versions, read off the machine and cached |
| Delete all sessions | Two red buttons that erase each CLI's transcripts and prompt history — and nothing else in those folders |
Windows integration
| In-app updates | A pulsing Update button when a newer release is out; it downloads, verifies and swaps itself |
| Launch at sign-in | One HKCU\...\Run entry, added and removed by a switch |
Per-provider options
| Presets | Curated templates per tool — CodeFast, Yunwu, DeepSeek, Kimi, OpenRouter, MiniMax, Z.ai, Azure, xAI, Custom and more (each tool has its own catalogue) |
| Official mode | Hand control back to the vendor login by clearing everything U-Pool manages |
| Permission switches | Per-provider checkboxes: bypass mode, skip-dangerous prompt, auto-accept edits, project MCP trust, Codex approvals/sandbox, live web search |
┌───────────────────────────────────────────────┐
│ Next.js (static export) ── window.pywebview │ UI
├───────────────────────────────────────────────┤
│ Api → Store → Adapters → live files │ Python
└───────────────────────────────────────────────┘~/.u-pool/config.json is the source of truth. On every switch U-Pool hands the chosen record to an adapter, which changes only the keys it owns in each target and leaves the rest of the file as it found it. The files are not the whole story: the CLIs also read variables straight out of the Windows environment, so a switch writes there too.
| App | Target | What a switch writes |
|---|---|---|
| Claude Code | ~/.claude/settings.json |
the owned names in the env block: base URL, auth token / API key, models, extras |
| Claude Code | ~/.claude/settings.json |
while a checkbox is ticked: permissions.defaultMode, permissions.skipDangerousModePermissionPrompt, enableAllProjectMcpServers |
| Claude Code | HKCU\Environment |
the same names again: ANTHROPIC_BASE_URL, ANTHROPIC_AUTH_TOKEN or ANTHROPIC_API_KEY, ANTHROPIC_MODEL, ANTHROPIC_SMALL_FAST_MODEL, extras |
| Claude Desktop | — | nothing yet; the provider is saved and marked current, and you get a notice saying so |
| Codex | ~/.codex/config.toml |
model_provider, model, one [model_providers.] table, and (per checkbox) approval_policy, sandbox_mode, web_search |
| Codex | ~/.codex/auth.json |
written from scratch: {"OPENAI_API_KEY": "…"} when that is the provider's env key, otherwise {} |
| Codex | HKCU\Environment |
the provider's env_key, plus OPENAI_BASE_URL |
| Hermes | %LOCALAPPDATA%\hermes\config.yaml |
one entry under providers:, plus model.provider and model.default |
| OpenCode | ~/.config/opencode/opencode.json |
one entry under provider, plus $schema and the top-level model |
| Cursor | %APPDATA%\Cursor\...\state.vscdb |
eight named cursorAuth/* + glass.lastSignedInAuthId rows, and nothing else in the database |
Hermes, OpenCode and Cursor read their credentials from their own files, so none of them writes to HKCU\Environment.
What a switch leaves alone
Since 0.6.0 the write is surgical: U-Pool reads the file, replaces the handful of keys it owns, and puts the rest back untouched. Two things are still owned whole, because that is exactly where a pile-up used to happen — the owned names in the env block of settings.json, and the active provider's [model_providers] table in config.toml (the dead tables are dropped and named in the toast). Everything else — enabledPlugins, theme, hooks, statusLine, permissions.allow/deny, [mcp_servers.*], [projects.*] trust, your comments — survives. On Codex a key whose value has not changed is not even re-rendered, so its trailing comment stays put.
The full reasoning lives in docs/superpowers/specs/2026-07-31-upool-0.6.0-design.md. And a file that will not parse stops the switch instead of being overwritten: preserving what U-Pool did not write means reading it first.
Windows environment, and Backups
Codex resolves its key by name — config.toml says env_key = "codefast" and Codex then looks for codefast in its process environment, which no file U-Pool writes can supply. So a switch also writes HKCU\Environment and broadcasts WM_SETTINGCHANGE, so a program started afterwards sees the change without a sign-out. U-Pool records the names it sets in ~/.u-pool/env-owned.json and deletes only those — a variable you set by hand is never removed, and switching to the official provider clears the namespace U-Pool claimed and nothing else. Settings → Windows environment lists what is managed, masks the values, marks anything set outside U-Pool, and opens the Windows editor. Off Windows the whole feature is inert.
Every file U-Pool is about to change is copied once beside the original as .backup — settings.json.backup, config.toml.backup, auth.json.backup, state.vscdb.backup. One current copy, overwritten each time, not a history; registry values go to ~/.u-pool/backups/environment.backup.json instead. The switch in Settings → Backups turns copies off — and with it off nothing is copied at all, including before a Cursor switch, the riskiest write in the app. Putting a backup back is a rename.
A Cursor account is not a provider — no base URL, no API key, no model, just a session token — so it gets its own tab rather than a provider form. The five existing apps are untouched.
Add account is one paste box (or a file picker for .txt / .csv / .json, appended to the box) that takes whatever you have: a Netscape cookies.txt line, a whole Cookie: header, a bare WorkosCursorSessionToken=… (or __Secure-next-auth.session-token / next-auth.session-token), a raw user_…::token encoded or not, an email,token CSV row, or JSON at any nesting. Paste one line or two hundred; unrecognised lines are counted and skipped. Accounts are keyed on the user id inside the cookie, not the email, so re-pasting a rotated cookie refreshes that account in place. A credential with no user_…:: half — a bare JWT, or a CSV row whose token is a bare JWT — is skipped even though the email beside it is readable; that is the usual reason a line lands in the skipped count.
Every account refreshes in the background on launch (there is also Refresh all and a per-card refresh), against the endpoints cursor.com's own dashboard calls — /api/auth/me for name, email and avatar, /api/auth/stripe for the plan, /api/usage-summary (or the legacy /api/usage?user=… for a request-quota plan) for the meter. A figure that was never learned renders as —; one that stops arriving keeps its last value rather than blanking, because a Cursor outage must not wipe a working card. A cookie cursor.com rejects marks the row Expired and disables its button, but a timeout, a 5xx or a non-JSON body leaves the row alone.
A cookie exported from a browser reads the account, but it is a web token — and writing one into state.vscdb makes the desktop app reject it and sign itself out. Only the session token Cursor writes when you sign into an account in the app itself signs the desktop client in. So:
session token you paste directly.Use closes Cursor (only if it is running), copies state.vscdb beside itself, writes eight keys, and starts Cursor again (only if U-Pool was the on
No open issues yet, or sync has not completed.