Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

U-Pool

Provider switcher for Claude Code and Codex.

Keep every endpoint, API key, and model in one place. Switch the active provider with a click. Probe reachability before you start working.

Python backend · Next.js UI · native OS webview — no Electron, no Node at runtime.

Stars Forks Python 3.11+ Version 0.6.0

Screenshot

U-Pool providers view


Why U-Pool?

Switching between Anthropic, OpenRouter, DeepSeek, Azure, xAI, and custom relays usually means editing config files by hand. U-Pool turns that into a desktop app:

  • One list of providers for Claude Code and Codex
  • One click to make a provider live — in the config file and in the Windows environment the CLIs read
  • Health checks that measure latency without spending tokens
  • Atomic writes, and a backup beside every file U-Pool changes

Features

Surgical writes A switch changes only the keys U-Pool owns — plugins, theme, hooks, MCP servers and project trust stay where they are
Windows environment The variables the CLIs actually read are set in HKCU\Environment too, and only names U-Pool set are ever deleted
Atomic writes Temp file + os.replace; crash mid-write cannot leave half-written config
Backups One copy beside the original — settings.json.backup — refreshed on every change, switchable off in Settings
First-run import Existing Claude / Codex config becomes a provider instead of being overwritten
Reachability probe Plain GET /models — latency only, no completions, no token cost
Presets CodeFast, Yunwu, DeepSeek, Kimi, OpenRouter, MiniMax, Z.ai, Azure, xAI, Custom, and more
Official mode Hand control back to vendor login by clearing U-Pool-managed keys
Permission switches Per-provider checkboxes for bypass mode, auto-accept edits, project MCP trust, Codex approvals/sandbox and live web search
Claude Desktop A third tab, preview only — providers you add there are saved, nothing is written yet
In-app updates Settings shows a pulsing Update button when a newer release is out, downloads it and swaps itself
Launch at sign-in Windows on/off switch — one HKCU\...\Run entry, removed again when you turn it off

How it works

~/.u-pool/config.json is the source of truth. On every switch U-Pool 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 env block: base URL, auth token / API key, models, extras
Claude Code ~/.claude/settings.json Only 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.<slug>] table
Codex ~/.codex/config.toml Only while a checkbox is ticked: 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 Whatever the provider's env_key is named, plus OPENAI_BASE_URL

What a switch leaves alone

0.5.0 rebuilt ~/.claude/settings.json and ~/.codex/config.toml from the provider record alone. That did stop the pile-up it was aimed at — an orphan [model_providers.*] table, a stale ANTHROPIC_AUTH_TOKEN sitting beside the new one — but it also took everything else in those files with it.

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. enabledPlugins, extraKnownMarketplaces, theme, effortLevel, model, hooks, statusLine and permissions.allow / deny survive a Claude Code switch. [mcp_servers.*], [plugins.*], [projects.*] trust levels, notify, [windows], [features], [shell_environment_policy] and your comments survive a Codex switch — a key whose value has not changed is not even re-rendered, so its trailing comment stays put.

Two things are still owned whole, because that is exactly where the pile-up happened:

  • the env block in settings.json — rebuilt from the provider every switch, removed entirely for an official one
  • [model_providers] in config.toml — only the active provider's table survives; the others are dropped and named in the toast

~/.codex/auth.json is written from scratch, because merging is what let a key an older build wrote sit there for months. If the file holds a ChatGPT login instead of an API key, the whole original is copied to ~/.u-pool/codex-login.json first and restored verbatim when you switch back to the official provider.

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

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. Claude Desktop and any shell you open are in the same position. 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. If a name U-Pool needs already exists it takes that name over, and the value it replaced goes to the backup first. Switching to the official provider clears the whole namespace U-Pool claimed and nothing else.

Settings → Windows environment lists what is managed for the current app with masked values, marks anything set outside U-Pool, and opens the Windows editor. Off Windows the whole feature is inert.

Backups

Every file U-Pool is about to change is copied once, beside the original, as <filename>.backupsettings.json.backup, config.toml.backup, auth.json.backup. One current copy, overwritten each time, not a history. Registry values cannot sit beside anything, so their pre-change values go to ~/.u-pool/backups/environment.backup.json, where a null means the name did not exist.

The switch in Settings → Backups turns it off, and with it off nothing is written at all. There is no restore screen: the copies are plain files, and putting one back is a rename.

Claude Desktop

Claude Desktop is a third tab in 0.6.0 and writes nothing. Providers you add there are saved and can be made current, and a notice above the list says the configuration was not written.

The reason it is preview rather than finished: Claude Desktop has no base-URL setting, so the only lever is the operating system environment — and that is the same ANTHROPIC_* namespace Claude Code already owns. Writing it here would move Claude Code's endpoint too. %APPDATA%\Claude\claude_desktop_config.json is never opened; it is your MCP servers and preferences, not provider config.

Advanced options

Each checkbox in Add provider → Advanced options writes exactly one key while it is ticked. These keys are U-Pool's, so unticking a box deletes its key on the next switch rather than leaving it behind.

Checkbox App Written
Bypass permission prompts Claude Code permissions.defaultMode = "bypassPermissions" — the settings form of --dangerously-skip-permissions
Skip the bypass warning screen Claude Code permissions.skipDangerousModePermissionPrompt = true
Auto-accept file edits Claude Code permissions.defaultMode = "acceptEdits"
Trust MCP servers from the project Claude Code enableAllProjectMcpServers = true
Bypass approvals & sandbox Codex approval_policy = "never" + sandbox_mode = "danger-full-access" — the pair --dangerously-bypass-approvals-and-sandbox sets
Live web search Codex web_search = "live" at the root (the [tools] boolean form is a no-op in Codex)

A provider with bypass on is badged Bypass in the list, so you can see it without opening the form. Switching to the official entry clears all of it.

Updates (Windows)

Settings → Updates asks api.github.com for the latest release at most once every six hours, and remembers the answer so an offline launch still knows about it. When there is something newer the Update button pulses; pressing it downloads the release zip, checks it against the length and — when GitHub published one — the SHA-256, unpacks it beside the install folder, and hands over to a small detached script. That script waits for U-Pool to exit, renames the old folder aside, renames the new one into place and starts it. If the second rename fails the first is undone, so a failed update leaves the version you had.

Two situations are refused rather than half-applied, with the reason shown next to an Open release page button instead: running from a source checkout (git pull + pip install -e . + npm run build is the update path there), and an install under Program Files, where Windows will not let U-Pool replace itself without admin rights. Move the folder somewhere you own and the in-place install works.

You can turn the check off entirely with the switch in the same section.

Launch at sign-in (Windows)

Settings → Startup writes one REG_SZ value named U-Pool under HKCU\Software\Microsoft\Windows\CurrentVersion\Run — no admin rights, no scheduled task, no shortcut. The remembered choice lives in ~/.u-pool/settings.json, and the entry is re-pointed at the current build on every launch so a moved bundle cannot leave a dead command behind.

If you switch U-Pool off under Task Manager → Startup apps, Windows keeps that decision: U-Pool reports "switched off by Windows" and offers a shortcut to the Windows page instead of quietly overriding you.

┌───────────────────────────────────────────────┐
│  Next.js (static export)  ── window.pywebview │  UI
├───────────────────────────────────────────────┤
│  Api  →  Store  →  Adapters  →  live files    │  Python
└───────────────────────────────────────────────┘

Quick start

Requirements: Python 3.11+, Node 20+

pip install -e ".[dev]"
cd ui && npm install && npm run build && cd ..
python -m upool

UI hot reload

# terminal 1
cd ui && npm run dev

# terminal 2
UPOOL_DEV_URL=http://localhost:3000 python -m upool

Open http://localhost:3000 in a browser without the Python bridge for an in-memory mock preview.

Tests

python -m pytest
cd ui && npm run build

Tests redirect HOME into a temp folder — your real Claude / Codex configs are never touched.

Windows bundle

python scripts/build.py            # → dist/U-Pool/U-Pool.exe + dist/U-Pool-<ver>-win64.zip
python scripts/build.py --skip-ui  # reuse an existing ui/out

Onedir (not onefile) for faster startup. The zip and the SHA256SUMS.txt printed alongside it are what the in-app updater downloads, so both have to be attached to the GitHub release as assets.

Environment variables

Variable Purpose
UPOOL_DEV_URL Load the UI from a dev server instead of the bundled export
UPOOL_DEBUG=1 Open the webview DevTools
UPOOL_HOME Move U-Pool’s own state directory
UPOOL_FAKE_HOME Redirect ~/.claude / ~/.codex lookups (used by tests)

Tech stack

  • Backend — Python 3.11+, pywebview, tomlkit
  • UI — Next.js 16 (static export), React 19, Tailwind 4, TypeScript
  • Ship — setuptools, pytest, PyInstaller

Star History

Star History Chart


Made with care by @UEdizaslan

About

Desktop provider switcher for Claude Code and Codex. Keep every endpoint, API key, and model in one place, switch the active provider in one click, and probe latency before you start. Atomic writes and rolling backups keep your configs safe. Python + Next.js in a native OS webview.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages