external-agents
Servidor MCP único que roteia entre múltiplos provedores de LLM para menor custo, menos limites de taxa e mais tokens disponíveis.
Documentação
@mrrlin-dev/external-agents — Servidor MCP Multi-LLM
Dê ao seu agente de codificação um pool de 40+ modelos mais baratos para delegar trabalho. Reduza sua conta em 10-100×.
Changelog: CHANGELOG.md

O problema que isto resolve
Você usa Claude Code ou Codex o dia todo. A maior parte do que ele faz — ler arquivos para responder uma pergunta, rodar uma suíte de testes, renomear um símbolo em 30 arquivos, resumir um diff — não precisa de um modelo de fronteira. Mas tudo isso é cobrado a preços de fronteira, em uma única conta, contra um único limite de taxa.
Enquanto isso, você provavelmente já tem um punhado de baldes de cota separados e quase sempre gratuitos parados: uma chave do Google AI Studio, uma chave do Groq, os modelos :free do OpenRouter, quaisquer CLIs agênticas em que você está logado.
external-agents transforma isso em um único pool para o qual seu agente principal pode despachar. Ele escolhe um provedor saudável por chamada, faz round-robin entre os baldes e, quando um retorna 429, muda para um provedor diferente e respeita o tempo real de reset desse provedor.
Também é um substrato limpo para painéis estilo LLM-Council: uma chamada pick_agents dá a você N escolhas de N provedores distintos, para que um júri de modelos não seja secretamente o mesmo modelo quatro vezes.
O que isto está otimizando
Cada decisão de design aqui responde a cinco objetivos. Eles são a razão da existência do projeto, e são mensuráveis — quando uma mudança piora um desses números, isso é uma regressão, independentemente do que mais ela melhorou.
- Um assento que é distribuído está vivo.
pickretornar um agente é uma afirmação de que um despacho para ele pode ter sucesso agora. Um agente que nunca respondeu uma vez não deve ser oferecido como se pudesse. - Um prompt que é enviado cabe. O teto real do assento escolhido — janela de contexto, tokens por minuto, o que sobrar da janela atual — tem que comportar o prompt inteiro antes de ele sair. Um HTTP 413 ou um 429 por limite de tokens é um bug de roteamento, não azar.
- Mais sucessos, menos falhas. Um despacho falho é uma rodada de trabalho jogada fora, e dentro de um painel de consenso é uma voz perdida — a execução recebe um veredito mais fino, não apenas um mais lento.
- A carga se espalha pelos modelos vivos de um nível. Nenhuma chave carrega um nível inteiro enquanto suas irmãs ficam ociosas, e um agente quebrado não deve ser reoferecido mais rápido que um funcionando só porque falhar é rápido.
- Limites de provedor são gastos, não admirados. Um nível gratuito que reseta sem uso toda noite é tokens jogados no lixo. O pool deve se aproximar do teto de cada balde em vez de ficar a um por cento dele.
Os objetivos 1, 2 e 5 precisam todos da mesma coisa, e vale a pena afirmar claramente: o provedor
te diz a resposta em cada resposta individual. x-ratelimit-limit-tokens,
x-ratelimit-remaining-tokens, x-ratelimit-reset-* — o teto real para sua chave e
quanto dele resta, tanto no sucesso quanto na falha. Uma entrada de registro é um palpite sobre
isso; um cabeçalho de resposta é uma medição. Então a regra que este código segue é: observação
vence declaração, e um limite descoberto ao ser rejeitado é um limite que foi registrado tarde
demais.
Esses números são observados em vez de afirmados — veja Observando o pool para regressões.
🚀 Configuração em 2 minutos
curl -fsSL https://raw.githubusercontent.com/mrrlin-dev/external-agents/main/install.sh | bash
O script instala o pacote, registra o servidor MCP com Claude Code e/ou Codex (o que ele encontrar) e abre um painel local onde você cola chaves de provedor inline:

Então reinicie seu cliente MCP. Seu agente agora tem as ferramentas.
Ou configure manualmente (três comandos)
npm install -g @mrrlin-dev/external-agents
# Register with whichever host(s) you use
claude mcp add external-agents external-agents-mcp
codex mcp add external-agents -- external-agents-mcp
# Set up keys
external-agents ui # opens http://127.0.0.1:4711
Requer Node ≥ 20. Funciona em macOS e Linux; Windows via WSL.
Quanto preciso configurar antes que isso seja útil?
Nada, se você já está logado em um CLI agêntico. Entradas respaldadas por uma assinatura que você já tem — claude, codex, cursor-agent, ollama, opencode, kiro-cli, agy — não precisam de chave de API; são utilizáveis no momento em que o binário está no seu PATH e logado.
Todo o resto é incremental. Cada chave que você cola acende mais do pool, e nenhuma delas é obrigatória:
| Cole isto | Obtenha | Custo |
|---|---|---|
GEMINI_API_KEY (Google AI Studio) | Gemini Flash | Nível gratuito, sem cartão |
GROQ_API_KEY | Llama 3.3 70B, gpt-oss 120B/20B, Llama 3.1 8B | Nível gratuito, sem cartão |
OPENROUTER_API_KEY | 5 modelos :free incl. Nemotron Ultra | Nível gratuito, sem cartão |
DEEPSEEK_API_KEY | DeepSeek v4 flash + v4 pro (raciocinador) | Pré-pago, precisa de uma pequena recarga |
O cadastro de cada um leva cerca de um minuto. O painel linka direto para a página certa e tem uma caixa de colar ao lado.
O que seu agente obtém
Duas ferramentas MCP, disponíveis automaticamente após a configuração:
-
dispatch(agent_id, prompt)— executa um prompt em um membro específico do pool. Repete automaticamente em um provedor diferente se o primeiro estiver com limite de taxa, e respeita o tempo de reset real do provedor em vez de um padrão inventado de 1 hora.Passe
cwd(um diretório existente — um worktree git, por exemplo) e um CLI direto inspecionará e editará arquivos no local.cwdnão concede acesso ao sistema de arquivos para modelos baseados em HTTP; dê a eles contexto comfilesem vez disso. Quandocwdé um repositório git, a listafilesque volta é o conjunto alterado pelo git, não a árvore inteira.Um
cwdque é um repositório git também recebe um curto cabeçalho de proveniência prefixado ao prompt — branch, commit e assunto, divergência em relação ao upstream, se o worktree está sujo — e os mesmos fatos voltam para você comorepo. É isso que impede um trabalhador apontado para um checkout desatualizado de produzir um relatório preciso sobre código que não está mais lá e tê-lo lido como alucinação. É somente leitura e nunca busca. Se você quiser que isso seja uma pré-condição rígida em vez de uma nota,external-agents dispatch --require-base origin/mainse recusa a despachar completamente quando o checkout não contém aquela ref — saída 6 para checkout errado, 2 para erro de uso. Estar à frente da ref é aceitável; a base é um piso, não uma verificação de igualdade. -
pick_agents(n, min_distinct_providers)— peça N agentes saudáveis de N provedores diferentes. Este é o primitivo para fan-out: revisão estilo júri, verificações de autoconsistência, seu próprio loop de consenso.
Ambas as ferramentas carregam a orientação de roteamento abaixo em suas descrições, para que qualquer modelo que leia o esquema em tempo de execução adote o mesmo viés.
Tudo também está disponível no terminal — external-agents pick, dispatch, status, stats, audit — se você preferir scriptar em vez de passar pelo MCP. Execute external-agents sem argumentos para a lista completa.
O que está no pool
28 entradas incluídas, 25 habilitadas por padrão. O resto são upgrades pagos que ficam desligados até você optar por eles.
| Provedor | Entradas | O que você precisa |
|---|---|---|
| Google AI Studio | Gemini 3.6 Flash; Gemini 3.1 Pro (desligado — sem nível gratuito) | GEMINI_API_KEY, nível gratuito |
| Groq | gpt-oss 120B, gpt-oss 20B, Qwen3.6 27B | GROQ_API_KEY, nível gratuito |
| OpenRouter | 5 modelos :free incl. Nemotron Ultra & Super, Gemma 4, gpt-oss 20B | OPENROUTER_API_KEY, nível gratuito |
| Antigravity | Gemini Flash/Pro, Claude Sonnet 4.6, Claude Opus 4.6, gpt-oss 120B | CLI agy, logado |
| Anthropic | Claude Opus 4.8, Sonnet 5, Haiku 4.5 | Assinatura CLI claude |
| Codex | GPT-5.4 (padrão do CLI) e GPT-5.4-mini | Assinatura CLI codex |
| Ollama Cloud | gpt-oss 20B, gpt-oss 120B | CLI ollama |
| DeepSeek | v4-flash, v4-pro (ambos desligados até você adicionar uma chave) | DEEPSEEK_API_KEY, pré-pago |
| cursor-agent / opencode / kiro-cli | um revisor CLI agêntico cada | o respectivo CLI |
Tem um segundo projeto Google? O Google AI Studio pode limitar a taxa de um projeto inteiro de uma vez, separadamente do limite por minuto de cada modelo — então uma segunda chave da mesma conta é um balde genuinamente independente, não uma repetição da primeira. O botão "+ Adicionar outra chave" do painel clona os modelos do provedor sob um novo slug (google → google2 → google3…) e o armazena no seu overlay local, onde permanece removível. O mesmo se aplica a qualquer provedor baseado em chave aqui.
O modelo de nível forte do Google é a única entrada incluída que está desligada por padrão: o Gemini 3.1 Pro tem uma cota de nível gratuito de zero, então alcançá-lo requer faturamento habilitado. Ele permanece incluído para que você possa ativá-lo se for o que deseja. Se você quiser um modelo forte de graça, o pool tem nove — Nemotron Ultra e Super no OpenRouter, gpt-oss 120B no Groq e Ollama, e Claude Opus / Gemini Pro via Antigravity.
O DeepSeek vem desabilitado porque sua API é pré-paga — sem chave e sem saldo ele não pode responder nada, então fica fora do seu pool até você adicionar DEEPSEEK_API_KEY, momento em que ambas as entradas se ativam.
Cerebras (removido em 0.13.0) e Z.ai (removido em 0.22.0) não estão mais incluídos — ambos precisam de configuração de provedor pago. Adicione-os localmente com add-model se você tiver um plano.
Faltando um provedor? Sugira — o painel tem um formulário que abre uma issue pré-preenchida.
Mantendo o pool honesto
Provedores descontinuam modelos, níveis gratuitos rotacionam, chaves expiram. O registro incluído diz o que existe; apenas uma chamada real diz o que sua conta ainda pode alcançar.
external-agents audit # every enabled entry with an HTTP transport
external-agents audit --provider google # just one bucket
external-agents audit --include-disabled # include switched-off entries too
Uma ida e volta por entrada, concorrente por provedor para você não estourar limites de taxa, e os vereditos são gravados em state.json — para que o painel e o despacho reflitam imediatamente a verdade do terreno:
✓ healthy— chave funciona, modelo existe⚠ needs_auth— 401/403, cole ou atualize a chave✗ model_unavailable— chave está ok, este modelo não está no seu nível⏳ rate_limited— atingiu o limite atual, vai se recuperar? errored_transient— algo deu errado uma vez; expira sozinho após 15 minutos! probe_error— o comando de sondagem não pôde rodar aqui (geralmentePATH). Não diz nada sobre o agente, então nada é gravado
audit também varre os diretórios temporários deste pacote quando termina, relatando o que foi. Esses diretórios guardam o generated.md de cada despacho — a resposta completa do modelo, em texto puro — e o SO só os recupera após cerca de um mês. A janela padrão é de 3 dias; EXTERNAL_AGENTS_TEMP_RETENTION_DAYS a altera, e um valor negativo desliga a varredura. Nada fora dos prefixos deste pacote é tocado, symlinks são pulados em vez de seguidos, qualquer coisa em um sistema de arquivos diferente (um ponto de montagem) é deixada em paz, e nada modificado nos últimos 15 minutos é removido seja qual for a janela — para que um despacho em execução agora não possa perder seu workdir mesmo se a janela estiver zerada.
Entradas desligadas são puladas por padrão: elas não podem ser despachadas de qualquer forma, e para um provedor pré-pago auditar uma gasta dinheiro real para aprender nada. external-agents status mostra uma coluna use para que um healthy verde ao lado de uma entrada desligada não possa ser lido como "disponível".
No dia a dia, external-agents ui é a mesma informação que uma página: estado do provedor ao vivo, uso e uma caixa de colar por provedor. Ele vincula apenas ao loopback. Entradas individuais têm um interruptor liga/desliga (external-agents toggle <id> --disabled) se você quiser uma fora de rotação sem deletar nada.
Quando algo falha e você quer saber por quê
external-agents stats mantém uma prévia de 400 caracteres do último erro por agente — suficiente para o painel, raramente suficiente para consertar algo. A prévia é uma cauda, então um CLI que imprime um banner e depois lança uma exceção tem o banner cortado e a exceção cortada fora.
O log de falhas sidecar é a outra metade. Ele está desligado por padrão e não registra nada até você ativá-lo:
external-agents failures on
A partir daí, toda tentativa falha é anexada por completo a ~/.local/state/external-agents/failures.jsonl — um objeto JSON por linha:
- dispatch — stdout completo, stderr completo, o argv exato, o cwd, a requisição HTTP e o corpo da resposta não truncado do provedor
- audit e credential verify — a saída bruta da sondagem que a dica limita a 200 caracteres
- sondagem somente leitura — incluindo o caso em que um comando declarado somente leitura gravou no canário
- recusas pré-dispatch — agente desconhecido, agente desabilitado, incompatibilidade de
--require-base, nenhum candidato a escalonamento. Essas nunca chegam ao log de dispatch, e são as mais difíceis de reconstruir depois: nada foi gerado, então não há código de saída para encontrar.
Cada linha também carrega a classificação extraída dessa saída (needs_auth, quota_exhausted, model_unavailable, harness_failure), para que um modelo que leia o arquivo possa distinguir "sua chave está errada" de "este modelo não existe mais" de "seu PATH está quebrado" sem precisar deduzir isso novamente.
Esse é o uso pretendido. O log é escrito para ser colado:
external-agents failures tail 50 # raw JSONL — hand it to a model and ask what to fix
external-agents failures status # is it on, how big, which agents fail most
external-agents failures off
external-agents failures clear
A chave fica em ~/.local/state/external-agents/config.json, não no pacote — então npm i -g @mrrlin-dev/external-agents@latest não pode silenciosamente desativá-la novamente. EXTERNAL_AGENTS_FAILURE_LOG=1 (ou =0) substitui o arquivo para uma única execução; EXTERNAL_AGENTS_FAILURE_LOG_FILE aponta o destino para outro lugar.
Tudo permanece no seu disco — o arquivo é 0600 e nada é transmitido para lugar algum. Segredos são removidos na entrada: cada valor de ambiente em formato de chave que este processo está segurando é apagado por correspondência exata (também na forma escapada, para a passagem que percorre a linha serializada), além de uma passagem de padrão para tokens que ele nunca segurou, uma passagem de forma para uma senha embutida em uma string de conexão e uma passagem final sobre a linha serializada. Quais nomes contam como formato de chave é uma lista, e uma lista só é tão completa quanto as convenções que alguém pensou — KEY, TOKEN, SECRET, AUTH, PAT, PSK e seus vizinhos estão nela.
A ferramenta não registra seu prompt — prompt_text é descartado e o posicional do prompt no argv se torna uma contagem de bytes; --with-prompts opta por participar novamente. Isso não é o mesmo que prometer que nenhum texto do prompt está no arquivo: muitos CLIs ecoam o prompt no stdout, e raw.stdout é capturado por completo, que é o objetivo do destino. Leia o arquivo antes de colá-lo em algum lugar onde você não colaria o prompt.
O outro log: dispatch-log.jsonl
Ao lado dele, há um segundo arquivo, muito menor — uma linha de ~300 bytes por dispatch, sem texto de prompt, sem fluxos brutos, gravado quer a chamada tenha sido bem-sucedida ou não. Esse está sempre ativo e permanece assim: é onde get_stats, doctor e o livro-razão de limites observados obtêm seus números, e todo defeito que este pool corrigiu nessa área foi encontrado lendo-o, em vez de ler o código. Não há chave, porque um pool que parou silenciosamente de se medir parece exatamente um saudável.
O que ele não pode fazer é crescer para sempre no seu disco:
-
Retenção é de 30 dias, e é medida em dias, não em bytes, de propósito. Toda pergunta que alguém faz a este arquivo é uma pergunta sobre tempo —
--since 24h, a janela de permissão medida dedoctor— e um limite de bytes responde a essas apenas por coincidência de quão ocupado você estava: um mês tranquilo mantém um ano de linhas mortas, uma semana movimentada descarta o extremo distante de uma janela sobre a qual você ainda estava perguntando. Nada gera erro em nenhuma direção, o que torna os bytes o eixo errado.EXTERNAL_AGENTS_DISPATCH_LOG_RETENTION_DAYSaltera a janela;EXTERNAL_AGENTS_DISPATCH_LOG_MAX_BYTESé um reforço de 32 MiB para uma rajada que ultrapassa a regra de idade dentro de uma janela, e ele informa no stderr quando faz a poda.A poda acontece quando a linha mais antiga está cerca de um quinto de janela atrasada, não no momento em que cruza a linha — então o arquivo se estabiliza entre 30 e 36 dias e é reescrito a cada poucos dias, em vez de a cada dispatch. Apenas um processo poda por vez. Um bloqueio abandonado é recuperado verificando se seu titular ainda está em execução — nunca por sua aparência de idade, porque a idade de um bloqueio não pode distinguir uma poda abandonada de uma lenta. No único caso em que a vivacidade erra (um pid reciclado), a ferramenta avisa, com o comando para limpá-lo, em vez de adivinhar.
-
EXTERNAL_AGENTS_DISPATCH_LOG_FILEo aponta para outro lugar — a mesma substituição quefailures.jsonltem. -
O arquivo é
0600(reverificado a cada gravação, não apenas na criação), e o único campo de texto livre em uma linha — a prévia de erro de 400 caracteres mantida em falhas — passa pela mesma redação que o sidecar.
Adicionando seu próprio modelo
Um endpoint interno, um modelo beta, qualquer coisa não incluída:
external-agents add-model \
--id kimi-k2-instruct \
--provider groq \
--model moonshotai/kimi-k2-instruct \
--url https://api.groq.com/openai/v1/chat/completions \
--env GROQ_API_KEY \
--tags free,fast
Isso grava em ~/.local/state/external-agents/agents.local.yaml, em camadas sobre o registro incluído — o mesmo id substitui, um novo id anexa. Atualizações de pacote nunca o sobrescrevem. Passo a passo completo: docs/adding-a-provider.md.
Monitorando o pool quanto a regressões
Os cinco objetivos acima são verificados, não presumidos:
external-agents doctor # last 24h
external-agents doctor --since 7d # a wider window
external-agents doctor --json # machine-readable, same checks
Uma verificação por objetivo, cada uma com a evidência que permite verificar ou descartar e o comando que a corrige. O código de saída é 1 apenas em um achado de alta gravidade e 0 caso contrário, então é seguro executar sem supervisão e só alerta quando algo realmente quebrou.
| Verificação | Objetivo | Meio |
|---|---|---|
oversized_dispatch | 2 | Um HTTP 413 aconteceu. Com tetos medidos, isso deveria ser inalcançável. |
unmeasured_seat | 2 | Um assento HTTP habilitado não tem teto, declarado ou observado — nada pode protegê-lo. |
never_answered | 1 | Um agente foi despachado repetidamente e nunca uma vez teve sucesso. |
success_rate | 3 | A janela caiu abaixo do piso. |
tier_imbalance | 4 | Um assento está tomando muito mais do que sua parte de um nível. |
idle_bucket | 5 | Uma permissão conhecida está ficando sem uso, e nada diz que a família está limitada em outro lugar. |
Todos os dias, sem ser solicitado
Aponte um agendador para ele. doctor é a metade testada — limites, evidências, um remédio por achado, um código de saída — e o que o executa em um temporizador é a outra metade. Uma tarefa agendada do Claude Code funciona bem, porque a parte interessante de uma verificação diária não é executar o comando, mas decidir o que em sua saída vale acordar alguém:
Run `external-agents audit` then `external-agents doctor --since 24h --json`.
Report only findings with severity "high", plus anything that changed since
yesterday. If nothing is high and nothing changed, reply with one line.
Execute audit antes de doctor, e essa ordem é o design: audit é um ping de max_tokens: 1 por entrada HTTP, e a resposta da sondagem carrega o teto real de limite de taxa do provedor — então a passagem de medição repara o achado mais comum em vez de apenas relatá-lo. Um vigia que corrige o que pode vale a pena manter; um que apenas reclama é silenciado.
Filosofia de roteamento — seja inteligente, não extravagante
pick_agents tem como padrão tier: "weak" de propósito. A maioria das tarefas não precisa de um modelo de fronteira.
Edições de arquivo único, refatorações, código de cola, resumos, conversões de formato, correções de bugs bem definidas, docstrings, casos de teste — um Gemini Flash, Groq gpt-oss, DeepSeek ou modelo :free do OpenRouter obtém a mesma resposta correta que Claude Opus ou Codex Pro, mais rápido e por uma fração do custo.
Recorra ao nível forte (Claude Opus, Codex, DeepSeek Reasoner, Nemotron Ultra) quando a tarefa for genuinamente uma destas:
- Depuração em várias etapas com causa raiz incerta
- Decisões de arquitetura ou formato de API
- Algoritmos novos, transformações com muita matemática
- Requisitos ambíguos que o modelo precisa desambiguar
Se um agente de nível fraco errar, o primeiro movimento é afiar a especificação, não escalar o nível. escalate_to_pro é uma alavanca de nova tentativa, não um padrão — recorrer a um modelo maior esconde falhas de engenharia de prompt atrás de computação cara, e você pagará por isso em toda chamada subsequente também.
Relacionado: --effort <level> controla a profundidade de raciocínio onde o provedor suporta. Use high para planejamento, design e revisão; deixe desligado para edições mecânicas. Veja docs/effort.md para a tabela verificada por agente.
FAQ
Você envia minhas chaves de API para algum lugar?
Não. As chaves vivem em ~/.local/state/external-agents/keys.env (modo 0600) e são lidas no ambiente do servidor MCP. O painel que as aceita vincula-se apenas ao loopback, nunca a uma interface de rede. Tokens de assinatura permanecem onde seu próprio CLI os colocou (codex login, claude login) — este pacote nunca os lê ou move. Nada é transmitido para lugar algum, exceto para o provedor para o qual você está despachando.
Posso usar isso sem nenhuma chave de API?
Sim, se você estiver conectado a pelo menos um CLI agêntico — claude, codex, cursor-agent, ollama, opencode, kiro-cli ou agy. Essas entradas são baseadas em assinatura e não precisam de configuração de chave. Provedores de API de nível gratuito se acumulam em cima sempre que você quiser adicioná-los.
Adicionei uma chave, mas o painel ainda diz "não definida".
Recarregue a página — desde 0.39.0, o painel e o servidor MCP releem o armazenamento de chaves a cada requisição, então uma chave adicionada de um terminal aparece na próxima sondagem. Se persistir, o valor provavelmente está sendo sombreado pela mesma variável exportada no seu próprio shell, que sempre vence sobre a armazenada.
Como ele lida com 429s?
Toda chamada real atualiza o estado a partir dos cabeçalhos da resposta e do corpo do erro. O resfriamento usa o próprio tempo de redefinição do provedor, analisado de x-ratelimit-reset-*, Retry-After e cargas de erro. Se o Google diz que a cota redefine em 42 horas, ele espera 42 horas em vez de adivinhar uma hora e martelar uma parede.
Como claude mcp add encontra external-agents-mcp?
npm i -g cria um link simbólico de external-agents-mcp no seu diretório bin global (geralmente /opt/homebrew/bin no macOS, /usr/local/bin no Linux), que está no seu PATH. claude mcp add grava essa string literal em ~/.claude.json, e o Claude Code o gera como um processo filho — resolução comum de PATH. Sem hospedagem, sem daemon, sem consulta de registro.
Licença
MIT. Problemas e pull requests são bem-vindos.