Skip to content
Back to skills

Explain Modelmux

ASecurity

Use when someone wants to understand how modelmux works — why a proxy is needed, what signals it routes on, and how the cascade keeps the orchestrator on Claude while diverting chosen subagents.

  • 2 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 5, 2026
ai-agentsapi

Works with

  • claude code
  • cli
  • api

Security analysis

A100/100

Scanned September 5, 2026

npx -y skills add armenr/modelmux --skill explain-modelmux --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Explain Modelmux?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Explain Modelmux
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/armenr-explain-modelmux/badge)](https://www.skillsdirectory.com/skills/armenr-explain-modelmux)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: explain-modelmux
description: Use when someone wants to understand how modelmux works — why a proxy is needed, what signals it routes on, and how the cascade keeps the orchestrator on Claude while diverting chosen subagents.
---

# How modelmux works

Explain the mental model, adapting depth to the audience. The whole system is
small and readable — point people at the actual files.

## The constraint

Claude Code picks its upstream from **one global** environment variable,
`ANTHROPIC_BASE_URL`. There's no per-subagent model setting on the client side.
So if you want *some* agents on Claude and *others* on a different model, the
decision has to happen **outside** the client — in a proxy that every request
flows through.

## The proxy

`src/server.ts` is a `Bun.serve` reverse proxy. For each request it:

1. Extracts routing **signals** (`src/signals.ts`).
2. Picks a target via a first-match **cascade** (`src/route.ts` + `routes.toml`).
3. Rewrites auth for the chosen upstream (`src/upstreams.ts`) and forwards it.
   SSE streams pass straight through.

Two upstreams: **Anthropic** (`api.anthropic.com`, "passthrough" = keep the
model Claude Code asked for) and **OpenRouter** (its Anthropic-compatible
endpoint, so no request translation is needed).

## The signals (why it routes by header, not model name)

From each request (`src/signals.ts`):

- **`x-claude-code-agent-id`** — present *only* on subagent requests, so it marks
  `isSubagent`. This is the key signal, and routing on it sidesteps a Claude Code
  bug where a subagent's requested model can silently fall back to the parent's.
- **`x-app`** — `cli` for foreground, `cli-bg` for background work.
- **`<<route:alias>>`** — an explicit tag placed in an agent's system prompt.
- **Work-type** signals derived from the request: `background` (`x-app: cli-bg`),
  `longContext` (estimated tokens > `longContextThreshold`), `think` (an
  extended-thinking block), `webSearch` (a web-search tool).

## The cascade

`route()` walks `routes.toml`'s `routes` top-to-bottom, first match wins:

1. **Tag rules** — `<<route:flagship|max|reasoner|review|claude-review>>` →
   that model. `<<route:control>>` → `orchestrator` (stays on Claude).
2. **`workType: background`** → the `cheap` model.
3. **`anySubagent`** → the `flagship` model (any *untagged* subagent).
4. **Default** → `orchestrator` (Anthropic passthrough).

All four work types (`background`, `longContext`, `think`, `webSearch`) are
implemented in `route.ts`, but only `background` is enabled in the shipped
`routes.toml` — the others are commented-out examples an operator can turn on
(e.g. send big-context requests to a roomier model). That's what
`longContextThreshold` configures; it does nothing until a `longContext` rule
exists.

The **main loop has no `x-claude-code-agent-id`**, so it never matches a subagent
rule — it falls to the default and **stays on Claude**. That's the core promise:
smart orchestrator on Claude, chosen subagents on cheaper/other models.

## The menu

`routes.toml` maps friendly **aliases** (`flagship`, `cheap`, …) to
`<upstream>:<slug>`. Swap a model in one place, or override at runtime with
`MUX_MODEL_<ALIAS>`. See the `switch-models` skill for day-to-day changes.

## Verifying it

Every decision is appended to `decisions.jsonl` with `isSubagent`, `matchedRule`,
`upstream`, and `resolvedModel` — the ground truth for "did this actually route
where I think?"

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.