Skip to main content
Glama
README.md
# modelmux

> An MCP server that lets Claude Code, Codex, and Perplexity call each other as tools — directly from your terminal, mid-session, without switching windows. Supports text, code, images, and PDFs passed by file path.

**Author:** Jason R. Woodcock
**Version:** 2.0.0
**License:** Apache-2.0 — see [LICENSE](LICENSE)

Instead of copy-pasting between AI tools, you can say things like:

```
Ask Codex to review this function and compare it to your approach
```
```
Use the broker to get Claude and Codex opinions on this architecture, then synthesize a recommendation
```
```
Ask Claude to review /Users/you/project/auth.php for security vulnerabilities
```
```
Use the broker to analyse /Users/you/docs/architecture.pdf and suggest improvements
```
```
Ask Perplexity what the current best practices are for WordPress REST API auth
```

---

## How it works

modelmux is a small [MCP (Model Context Protocol)](https://modelcontextprotocol.io) server. Both Claude Code and Codex support MCP natively — when you register modelmux, it appears as a set of callable tools inside both agents. When you invoke one of those tools, modelmux makes a direct API call to the target AI and returns the response inline into your current session.

**It runs on-demand only.** The modelmux process starts when you open Claude Code or Codex, and stops when you close them. Nothing runs in the background.

**It uses API keys, not subscription quota.** Calls go through your Anthropic/OpenAI/Perplexity API accounts at pay-per-token rates — completely separate from your Claude Code or Codex subscription limits. Light use (a few cross-reviews per day) typically costs under $5/month across all three APIs.

---

## Prerequisites

- macOS (tested on MacBook Air and Mac Mini; Apple Silicon and Intel both work)
- [Node.js](https://nodejs.org) v18 or higher
- [Claude Code](https://claude.ai/code) and/or [Codex CLI](https://developers.openai.com/codex) installed
- API keys for the services you want to use (you can skip any you don't need)

---

## Installation

### Quick install

```bash
git clone https://github.com/JRWoodcock/modelmux.git
cd modelmux
bash install.sh
```

The installer will:
1. Check for Node.js 18+
2. Prompt you for API keys and save them to `~/.zshrc`
3. Register modelmux with Claude Code and Codex automatically
4. Add a Codex tool-routing rule (see [Codex: "ask Claude" opens an app](#codex-ask-claude-opens-an-app-instead-of-calling-modelmux))
5. Run a connectivity test against each API

Then restart Claude Code and/or Codex to pick up the new MCP server.

> **Using the Claude or Codex Desktop app?** Both work, with two caveats the
> installer handles automatically: there's no `claude`/`codex` command on your
> PATH (the installer finds each app's bundled binary instead), and the apps
> can't read API keys from `~/.zshrc` (so the keys are baked into the server's
> config at registration). After installing, **fully quit the app with Cmd+Q** —
> not just closing the window — then reopen it. In Claude, modelmux appears under
> **Settings → MCP** as a *user* server (available in every project). See
> [Troubleshooting](#troubleshooting) if it doesn't show up.

### Install from npm

The server is also published to npm. This gives you the `modelmux` command
without cloning, but does not set up API keys or register the server — run those
steps yourself (see [API keys](#api-keys) and the registration commands below).

```bash
npm install -g @jrwoodcock/modelmux

# Register the installed command with each agent:
claude mcp add modelmux -- modelmux
codex mcp add modelmux -- modelmux
```

The package name is scoped (`@jrwoodcock/modelmux`), but the installed command
is just `modelmux`.

### Installing on multiple Macs

Same steps on each machine. The installer creates `~/.modelmux/` locally from the cloned repo. Your API keys are stored in `~/.zshrc` on each machine separately — enter them fresh on each install (or sync your dotfiles if you already do that).

```bash
# On your second Mac:
git clone https://github.com/JRWoodcock/modelmux.git
cd modelmux
bash install.sh
```

### Manual registration (if the installer skipped a tool)

If Claude Code or Codex wasn't found during install, register manually after installing them.

**Claude Code (CLI):** register at user scope and pass your keys as `-e` values, so the server has them no matter how the host is launched. The server name (`modelmux`) must come **before** the `-e` flags — `-e` is variadic and would otherwise consume the name:
```bash
claude mcp add -s user modelmux \
  -e ANTHROPIC_API_KEY="sk-ant-..." \
  -e OPENAI_API_KEY="sk-..." \
  -e PERPLEXITY_API_KEY="pplx-..." \
  -- node ~/.modelmux/src/server.js
```

**Claude Desktop app (no `claude` on PATH):** the app bundles the CLI — call it directly with the same arguments:
```bash
"$HOME/Library/Application Support/Claude/claude-code/"*/claude.app/Contents/MacOS/claude \
  mcp add -s user modelmux -e ANTHROPIC_API_KEY="sk-ant-..." -e OPENAI_API_KEY="sk-..." \
  -e PERPLEXITY_API_KEY="pplx-..." -- node ~/.modelmux/src/server.js
```

> **Why embed the keys?** A Desktop app is launched from the Dock, so it does
> **not** load `~/.zshrc` — a server it spawns can't see API keys exported there.
> Passing the keys at registration (`-e` for Claude, `--env` for Codex) stores
> them in the MCP config so they're always found. Trade-off: if you later rotate
> a key in `~/.zshrc`, re-run the registration (or edit the entry) so the stored
> copy is updated too. The CLI tools inherit keys from your shell, but embedding
> them is harmless and keeps both paths identical.

**Codex (CLI):** Codex uses `--env` (not `-e`):
```bash
codex mcp add \
  --env ANTHROPIC_API_KEY="sk-ant-..." \
  --env OPENAI_API_KEY="sk-..." \
  --env PERPLEXITY_API_KEY="pplx-..." \
  modelmux -- node ~/.modelmux/src/server.js
```

**Codex Desktop app (no `codex` on PATH):** the app bundles the CLI — call it directly:
```bash
~/.codex/plugins/.plugin-appserver/codex \
  mcp add --env ANTHROPIC_API_KEY="sk-ant-..." --env OPENAI_API_KEY="sk-..." \
  --env PERPLEXITY_API_KEY="pplx-..." modelmux -- node ~/.modelmux/src/server.js
```

> **Tip:** if a Desktop app can't launch the server (it shows as failed/not
> connected), use the **absolute path to node** instead of bare `node` — apps
> launched from the Dock may not have your `node` on PATH (common with nvm).
> Find it with `command -v node`, e.g. `/Users/you/.nvm/versions/node/vXX/bin/node`.

After registering, **fully quit and reopen** the app (Cmd+Q, not just closing the window) so it loads the new server.

---

## API keys

The installer prompts for these interactively. To set or update them manually:

```bash
# Add to ~/.zshrc
export ANTHROPIC_API_KEY="sk-ant-..."    # console.anthropic.com/settings/keys
export OPENAI_API_KEY="sk-..."           # platform.openai.com/api-keys
export PERPLEXITY_API_KEY="pplx-..."     # perplexity.ai/settings/api
```

Then `source ~/.zshrc` and restart Claude Code / Codex.

You only need keys for the tools you plan to use. The `broker` tool's synthesis step uses Claude, so `ANTHROPIC_API_KEY` is the most important one.

### Updating keys (Desktop apps)

Because the Desktop apps embed your keys in their MCP config (they don't read `~/.zshrc`), changing a key in `~/.zshrc` is not enough — the embedded copy goes stale. After rotating a key, re-sync both apps in one command:

```bash
bash ~/.modelmux/update-keys.sh        # or ./update-keys.sh from the cloned repo
```

It reloads the keys from `~/.zshrc` and re-registers modelmux with Claude and Codex (whichever it finds). Restart the apps (Cmd+Q, then reopen) afterward. Terminal CLIs don't need this — they read `~/.zshrc` directly.

The re-registration is safe: Codex is updated in place, and Claude (which must be removed before re-adding) is verified and retried, so a transient failure is reported loudly rather than silently leaving an app unregistered.

### Optional model overrides

```bash
export ANTHROPIC_MODEL="claude-sonnet-4-6"   # default
export OPENAI_MODEL="gpt-4o"                  # default
export PERPLEXITY_MODEL="sonar-pro"           # default
```

---

## Tools

Once registered, both Claude Code and Codex can call these tools naturally during a conversation:

| Tool | Description |
|---|---|
| `ask_claude` | Send a prompt to Claude (Anthropic API), optionally with a file |
| `ask_codex` | Send a prompt to OpenAI (GPT-4o by default), optionally with a file |
| `ask_perplexity` | Send a prompt to Perplexity with live web search, optionally with a text/code file |
| `broker` | Query multiple AIs in parallel with the same prompt and file, optionally synthesize into one response |

All tools accept:
- `prompt` — required, the question or instruction
- `system` — optional system prompt to set the AI's role (e.g. `"You are a security expert reviewing code for vulnerabilities"`)
- `file` — optional absolute or relative path to a file on disk (see file support below)

The `broker` tool also accepts:
- `targets` — array of `"claude"`, `"codex"`, `"perplexity"` (default: `["claude", "codex"]`)
- `synthesize` — `true` (default) to get a synthesized summary, `false` for raw side-by-side responses

### File support

Pass any local file by path and modelmux will read, encode, and send it to the target AI automatically.

| File type | Extensions | ask_claude | ask_codex | ask_perplexity | broker |
|---|---|---|---|---|---|
| Code / text | `.js` `.ts` `.php` `.py` `.md` `.txt` `.json` `.css` `.html` `.sql` and more | ✅ | ✅ | ✅ | ✅ all targets |
| Images | `.png` `.jpg` `.jpeg` `.gif` `.webp` | ✅ vision API | ✅ vision API | ⚠️ skipped with note | ✅ Claude + Codex only |
| PDFs | `.pdf` | ✅ document API | ✅ inline base64 | ⚠️ skipped with note | ✅ Claude + Codex only |

When a broker call includes an image or PDF and Perplexity is a target, Perplexity receives a transparent note that a binary file was attached but not forwarded — it answers on the text prompt alone rather than silently failing.

**Size limit:** 20 MB per file. A clear error is returned if the limit is exceeded.

---

## Usage examples

Use these prompts naturally inside Claude Code or Codex — the agent will call the appropriate tool automatically.

### Cross-review (text prompt)

```
Ask Codex to review this function and tell me if it agrees with your approach:

function parseDate(str) {
  return new Date(str);
}
```

### Cross-review (file path)

```
Ask Claude to review /Users/you/project/auth.php for security vulnerabilities
```

```
Ask Codex to refactor /Users/you/project/utils.js and suggest a cleaner approach
```

### Image analysis

```
Ask Claude to describe the UI layout in /Users/you/Desktop/mockup.png and
suggest accessibility improvements
```

```
Use the broker to get both Claude and Codex opinions on the architecture
diagram at /Users/you/docs/system-diagram.png
```

### PDF review

```
Ask Claude to summarise the key decisions in /Users/you/docs/proposal.pdf
```

```
Use the broker to analyse /Users/you/docs/architecture.pdf and identify
any risks, with Claude and Codex each giving their perspective
```

### Parallel opinions with synthesis

```
Use the broker to get both Claude and Codex opinions on whether I should use
PostgreSQL or MongoDB for a WordPress plugin that stores form submissions.
```

### Raw side-by-side (no synthesis)

```
Use the broker with synthesize=false to compare how Claude and Codex would
approach rate-limiting a REST API endpoint.
```

### All three AIs

```
Use the broker with targets ["claude", "codex", "perplexity"] to research
the current best MCP servers for browser automation and give me a recommendation.
```

### Web-grounded research

```
Ask Perplexity what the latest WordPress security patches cover and whether
any affect the WooCommerce REST API.
```

### Role-scoped review

```
Ask Claude to review /Users/you/project/login.php as a security expert
looking specifically for SQL injection and authentication bypass vulnerabilities
```

---

## Testing your setup

```bash
node ~/.modelmux/src/test.js
```

This checks:
- Connectivity to each configured API (Claude, OpenAI, Perplexity)
- Local file read/write access

Run it any time you want to verify your keys are working or after moving to a new machine.

---

## Cost

modelmux calls bypass your Claude Code and Codex subscription quotas entirely. Each call is billed at standard API rates against your API accounts:

| Usage pattern | Estimated monthly cost |
|---|---|
| A few text cross-reviews per day | ~$2–5 across all APIs |
| Broker calls several times per day | ~$10–20 |
| Heavy all-day usage | Could exceed $40 |

**File calls cost more than text-only calls.** Images and PDFs are base64-encoded before being sent, which significantly increases token count. A one-page PDF or a medium-resolution image can use 10–50x the tokens of a plain text prompt. Use file-based calls when you genuinely need the AI to see the file; paste code as text when that's sufficient.

The `broker` tool with `synthesize=true` makes two Claude calls per invocation (one for the query, one for synthesis). Use `ask_claude` or `ask_codex` individually when you only need one opinion.

You need a small amount of credit loaded on each API account — even $5 on each lasts a long time at light usage. These are separate from your claude.ai and ChatGPT subscriptions:
- **Anthropic Console:** console.anthropic.com
- **OpenAI Platform:** platform.openai.com

---

## File layout

```
modelmux/
├── install.sh          ← run this on each Mac
├── update-keys.sh      ← re-sync API keys into the Desktop apps after rotating
├── lib/
│   └── common.sh       ← shared shell helpers (host detection, key flags)
├── package.json
├── package-lock.json
├── README.md
├── CLAUDE.md           ← guidance for AI coding agents working on the repo
├── LICENSE             ← Apache-2.0
├── NOTICE              ← attribution notice (Apache-2.0)
├── .gitignore
└── src/
    ├── server.js       ← the MCP server (stdio transport, no dependencies)
    └── test.js         ← API connectivity smoke test
```

`server.js` has no npm dependencies — it uses only Node.js built-ins and the native `fetch` API (available since Node 18). No `npm install` needed.

---

## Troubleshooting

**Tools don't appear in Claude Code or Codex**
Restart the app after registration. MCP servers are loaded at startup — and for the Desktop app you must fully quit it (Cmd+Q), not just close the window.

**Nothing shows up under MCP servers in the Claude or Codex Desktop app**
The installer's auto-registration only runs if it can find a `claude`/`codex` command. The Desktop apps have neither on your PATH, so on older installs the step was skipped. Re-run `bash install.sh` (it now detects each app's bundled binary), or register manually using the **Desktop app** commands under [Manual registration](#manual-registration-if-the-installer-skipped-a-tool). In Claude it registers at **user** scope, so it appears as a *user* server (available everywhere), not a *local* one.

**"X_API_KEY not set" error**
In a **terminal**-launched tool, run `source ~/.zshrc` first (or open a fresh terminal). In a **Desktop app** (Claude or Codex), exporting keys in `~/.zshrc` is not enough — the app doesn't read your shell profile. Register modelmux with the keys passed as env values (`-e` for Claude, `--env` for Codex; see [Manual registration](#manual-registration-if-the-installer-skipped-a-tool)); the installer does this for you.

**Server shows as failed / won't launch in a Desktop app**
The app may not have `node` on its PATH (common when node is managed by nvm). Re-register using the **absolute path to node** instead of bare `node` — find it with `command -v node`. The installer already does this automatically.

**Codex: "ask Claude" opens an app instead of calling modelmux**
Codex's Computer Use plugin can read "ask Claude" as "open the Claude app on screen" rather than calling modelmux's `ask_claude` tool. The installer adds a routing rule to `~/.codex/AGENTS.md` so "ask `<model>`" phrasing prefers the modelmux tools while leaving Computer Use untouched for everything else — **restart Codex** after install for it to take effect. The rule is fenced by a `<!-- modelmux:tool-routing -->` marker (added once, never overwrites your own notes); delete that marked block to undo. You can always force a tool explicitly: *"use the modelmux `ask_claude` tool to …"*.

**Connectivity test fails**
Check that the key is correct and that your API account has credit loaded. Perplexity requires a paid API plan (separate from Perplexity Pro).

**Registered but getting no response**
Run `claude mcp list` or `codex mcp list` to confirm `modelmux` appears. If it does, try the test script to isolate which API is failing.

**"File not found" error**
Use an absolute path (starting with `/`) rather than a relative path. In Claude Code or Codex sessions the working directory may not be where you expect. Example: `/Users/you/project/auth.php` rather than `./auth.php`.

**"File too large" error**
The 20 MB limit applies to the raw file size before base64 encoding. For large PDFs, consider splitting them or copying the relevant pages to a new file first.

**Image or PDF not being understood by the AI**
Confirm the file extension is one of the supported types. If you're using a `.jpeg` extension it will work — both `.jpg` and `.jpeg` are recognised. For PDFs, very large or scanned-only documents (no embedded text layer) may produce poor results.

**Perplexity says it can't see my image/PDF**
This is expected — Perplexity's API does not support vision or document inputs. The broker sends Perplexity a text note explaining the file was skipped. Use `ask_claude` or `ask_codex` directly for image and PDF analysis.

**Want to unregister**
```bash
claude mcp remove modelmux
codex mcp remove modelmux
rm -rf ~/.modelmux
```
Then remove the API key exports from `~/.zshrc` if desired.

---

## Contributing

Pull requests welcome. The server is intentionally minimal — a single file with no npm dependencies. Keep it that way if you can. Contributions are accepted under the Apache-2.0 license (per section 5 of the license).

If you add support for a new file type or AI provider, update the file support matrix in this README and add a test case to `src/test.js`.

---

## Author

Jason R. Woodcock

---

## License

Licensed under the **Apache License, Version 2.0**.
Copyright © 2026 Jason R. Woodcock.

You are free to use, modify, and redistribute this software, including for
commercial purposes. In return, the license requires that you:

- retain the copyright, license, and attribution notices (see [NOTICE](NOTICE));
- state any significant changes you make to the files; and
- include a copy of the license with any redistribution.

It also includes an explicit patent grant from contributors. See the
[LICENSE](LICENSE) and [NOTICE](NOTICE) files for the full terms.

TDQS

A4.1/5.0

Scored across 4 tools

Disambiguation5/5

Each tool has a distinct purpose: ask_claude for Claude AI, ask_codex for OpenAI, ask_perplexity for web-grounded answers, and broker for parallel queries and synthesis. No overlap.

Naming Consistency5/5

All individual query tools use the 'ask_' prefix, and the combined tool is named 'broker', following a clear and consistent pattern.

Tool Count5/5

Four tools is appropriate for a server that provides access to multiple AI models and a broker, covering the core functionality without being excessive or insufficient.

Completeness5/5

The tool set covers all essential operations: querying each model individually (Claude, Codex, Perplexity) and a synthesized broker. No obvious gaps.

Maintenance

ActivityInactive
ResponsivenessNo issues