← Discover MCPs and Agents
m
MCPAI & MLGitHub

multiplexor

Free-tier subagent routing for AI CLIs. The routing policy for endy.

Links

README

From the repo.

multiplexor

The routing policy for free-tier AI CLIs. Pairs with endy when you also need a runtime, tmux orchestration, and cross-agent handoff.

You have a primary agent doing the heavy work. You also have Gemini CLI, OpenCode, CommandCode (cmd, Kimi K2.6), and maybe a local Ollama model sitting idle. multiplexor connects them. It lets your main agent delegate tasks to any installed AI CLI -- automatically picking the best one, falling back when quotas run dry, and keeping everything local.

No proxies. No daemon. No MCP layer. Just a small Python command that knows which CLIs you have installed, scores them, picks one, runs it, and moves on. Designed to pair with endy so the same router that picks the next CLI for a fresh delegate also picks the next agent for an endy handoff.


The problem this solves

Free-tier AI CLIs are powerful but limited. Gemini CLI gives you access to Gemini models at no cost. OpenCode includes its own quota. But once one provider gets exhausted, you are stuck waiting or switching manually.

multiplexor fixes the switching part. It treats your installed CLIs as a pool of subagents and routes work through them in order of priority and availability. When one runs dry, you mark it exhausted with multiplexor next and it immediately tries the next best option. When all free providers are gone, Ollama runs locally as a last resort.

This is not about bypassing limits. It is about making sure you never sit idle when another provider is available and ready.


How it works

The router has the same scoring logic across every entry point — what changes is who calls it and whether work runs as a side effect.

  1. You install multiplexor and run multiplexor init to create your config.
  2. The config declares which CLIs you have, their tier (free, included, local, paid), and a priority score.
  3. The router computes score = priority + tier_bonus, filters out exhausted or missing providers, and picks the highest-ranked one.

Four ways to consume that decision:

Entry pointWhat it doesUsed by
multiplexor delegate "task"Pick best + run the task headlessly. Output to stdout.Your primary agent, scripts
multiplexor nextMark the last provider exhausted + launch the next one interactively.You, mid-session
multiplexor next-provider [PREV]Pure query: mark PREV exhausted, print the next name, exit. No launch.endy's ENDY_HANDOFF_RESOLVER hook
multiplexor status --json [name]Read-only snapshot of every provider's tier headroom as JSON.endy state, dashboards

next-provider and status --json are the contracts that make endy route handoffs automatically — see "Pairing with endy" below.

primary agent
     |
     v
multiplexor delegate "review this PR"            (or: multiplexor next-provider <prev>)
     |
     v
  router scores providers:
     gemini    score 130  (priority 100 + free bonus 30)  <-- picked
     opencode  score 115  (priority 90  + included bonus 25)
     ollama    score  15  (priority 10  + local bonus 5)   (fallback only)

Pairing with endy

multiplexor decides which CLI should pick up next. endy is the runtime that runs each CLI in a detached tmux window, captures its output to .logs/, and provides endy handoff — a one-command transfer of an in-flight coding task from one agent to another with full context.

Wire them together by setting endy's resolver hook to the dedicated binary multiplexor installs for this purpose:

export ENDY_HANDOFF_RESOLVER=multiplexor-next-provider
endy handoff <task-id>                  # --to becomes optional

When you call endy handoff without --to, endy invokes multiplexor-next-provider <prev-agent> <task-id> <cwd> and uses the agent name it prints to stdout. Under the hood that wrapper just calls multiplexor next-provider, which:

  1. Marks <prev-agent> as exhausted (so it does not get re-selected).
  2. Computes the highest-scored eligible provider with the regular priority + tier_bonus rules (same logic as multiplexor status).
  3. Prints the chosen name to stdout. Exits non-zero (silently, stderr only) when nothing is eligible, so endy falls back to requiring an explicit --to.

A standalone test, no endy required:

$ multiplexor-next-provider gemini task-123 /tmp/proj
opencode
$ multiplexor status | head -5
1. opencode
   tier: included
   ...

endy state (the per-spawn environment snapshot) reads multiplexor's view of the world via multiplexor status --json [agent], which returns a machine-parseable shape including exhausted_seconds_remaining for each provider — see docs/usage.md.

Useful flags on multiplexor next-provider (or its -next-provider shim) for tuning the integration:

FlagWhat it does
--no-markPure query, do not touch state (good for dry-runs / health checks).
`--mode interactiveask`
--verboseEmit score + tier + alternatives to stderr (stdout stays a single name, safe for the resolver).
--for endySkip providers endy cannot drive headlessly (e.g. ollama).

You can also use either tool alone:

  • multiplexor without endymultiplexor delegate "task" runs a single task on the best-scored CLI. No tmux, no logs directory, no handoff chain.
  • endy without multiplexorendy handoff <id> --to <agent> is a one-shot manual handoff. You pick the next agent yourself.

Together you get continuity: when one tier runs out, the routing policy decides who is next and the runtime carries the task across without you re-typing the prompt.


Install

For users:

pipx install endy-multiplexor      # cleanest: isolated env, binaries on PATH
# or:
uv tool install endy-multiplexor   # same outcome via uv
# or:
pip install --user endy-multiplexor

The PyPI distribution name is endy-multiplexor (the plain multiplexor name on PyPI is owned by an unrelated 2020 websockets package). The Python module is still import multiplexor; the CLIs are still multiplexor and multiplexor-next-provider. If you install endy via endy install, multiplexor is bootstrapped automatically — you do not need this step separately.

For developers / contributing to multiplexor itself:

git clone https://github.com/trentisiete/multiplexor
cd multiplexor
pip install -e .              # editable install from the working tree
python3 -m unittest discover -s tests

Requires Python 3.11+ and at least one supported CLI in your PATH.


Quickstart

multiplexor init          # create user config
multiplexor doctor        # verify everything is detected
multiplexor status        # see current ranking
multiplexor delegate "review this repository and list concrete risks"

When the current provider gets exhausted:

multiplexor next          # mark last provider exhausted, launch next

Other commands you will use:

multiplexor                          # launch best interactive provider
multiplexor reset                    # clear all exhaustion marks
multiplexor next                     # mark current exhausted + launch next
multiplexor next-provider [PREV]     # pure query: print next agent name, no launch
multiplexor delegate "task"          # headless subagent run
multiplexor ask "prompt"             # alias for delegate
multiplexor status                   # human view of every provider's state
multiplexor status --json            # machine view (envelope, all providers)
multiplexor status --json gemini     # machine view (single provider, bare dict)
multiplexor --dry-run                # show what would run
multiplexor --provider gemini        # force a specific provider

Piping works too:

git diff | multiplexor delegate "review these changes"

Providers

The default config ships with these providers:

ProviderTierPriorityDefault StateNotes
Gemini CLIfree100enabledMain subagent, headless capable
OpenCodeincluded90enabledGood secondary option
Ollamalocal10enabledFallback only, runs locally
Qwenpaid95disabledOptional
Hermespaid80disabledOptional
cmdpaid60disabledCommandCode / Kimi K2.6 — cheapest paid option
Codexpaid40disabledOptional
Claudepaid30disabledOptional

Paid providers are disabled by default because the v1 focus is free-tier delegation. Enable them in your config if you want them in the routing pool.

Scoring formula:

score = priority + tier_bonus

Tier bonuses: free=30, included=25, local=5, paid=0. Gemini CLI with its base priority of 100 and free bonus of 30 scores 130, always winning unless exhausted.


Configuration

Run multiplexor init to create your config at:

  • Linux/macOS: ~/.config/multiplexor/config.yaml
  • Windows: %USERPROFILE%\.multiplexor\config.yaml

A provider entry needs only a few fields:

providers:
  gemini:
    enabled: true
    tier: free
    priority: 100
    command: "gemini"
    interactive_command: ["gemini", "--skip-trust", "--approval-mode=yolo"]
    ask_command: ["gemini", "--skip-trust", "--approval-mode=yolo", "-p", ""]
    ask_stdin: true

The Provider class handles everything else: detection from PATH, command construction, scoring, and prompt substitution. Adding a new provider means adding a config block. No code changes required.

The fallback YAML parser (used when PyYAML is not installed in the venv) also accepts inline flow-dict syntax — tiers: { free: { bonus: 30 } } and providers: { gemini: { enabled: false } } parse the same as their indented forms. PyYAML is not a dependency; install it only if you want full YAML support (anchors, multi-line strings, etc.).

Key fields:

  • enabled: include or skip this provider
  • tier: determines the bonus added to priority
  • priority: base ranking score
  • command: executable name for PATH detection
  • interactive_command / ask_command: command templates for each mode
  • ask_stdin: send task through stdin instead of argv
  • fallback_only: only use when no normal provider is eligible
  • default_model: required for Ollama

State and exhaustion

State is stored locally as state.json next to your config. It tracks only two things:

  • The last provider that ran
  • Temporary exhaustion marks (with an expiration timestamp)

It does not store credentials, API keys, prompts, or anything sensitive. The exhaustion cooldown defaults to 24 hours and is configurable.

multiplexor next    # marks last_provider as exhausted
multiplexor reset   # clears all exhaustion marks

Security model

multiplexor runs commands with shell=False. No shell injection surface. Prompts go through stdin by default so they never appear in process arguments visible to ps.

The default delegate commands use each CLI's official allow-all permission mode:

  • Gemini: --skip-trust --approval-mode=yolo
  • OpenCode: --dangerously-skip-permissions

This is intentional. A delegated CLI can edit files and run commands. Only use this in repositories where you are comfortable with that behavior. First-time authentication and setup still belong to each CLI. multiplexor does not store or inject any credentials.

It does not:

  • bypass rate limits or quotas
  • scrape provider credit balances
  • modify any provider's internal configuration
  • run a proxy, daemon, web server, or MCP server

Limitations

v1 operates on what it can detect: installed commands and explicit exhaustion state. It cannot read your exact Gemini quota or predict when a provider will fail. If a CLI hangs waiting for interactive setup, the configured timeout (default 120s) kills it and tries the next provider.

Provider-specific setup hints and per-provider timeout overrides are planned.


Testing

python3 -m unittest discover -s tests

Tests use mocked commands. No real CLIs or credentials needed.


Roadmap

  • Clearer per-provider setup hints when a CLI fails to run
  • Optional per-provider timeout overrides in config
  • Examples for adding custom local providers

Docs

  • Usage - command examples and patterns
  • Configuration - provider config, scoring, and state
  • Security - threat model and operational notes

Related

  • endy - the runtime. tmux + .logs/
    • cross-agent handoff command. Use multiplexor as its routing policy.

Collected info

  • 1 stars
  • Language: Python
  • Source updated: 8/22/2026

Config for your environment

Replace {MCP_ENDPOINT_URL} with this MCP’s endpoint URL (from its repo or docs above). No API key — you connect directly.

Tool

OS

Config file: ~/.cursor/mcp.json

{
  "mcpServers": {
    "mcp-server": {
      "url": "{MCP_ENDPOINT_URL}"
    }
  }
}

Paste into mcpServers in the config file. Restart Cursor after saving.

If this MCP is also published on mcpchannel.ai, you can subscribe from Browse and use the gateway config there instead.