pi-delegate-mcp

Servidor MCP que transforma o agente de codificação pi em um worker de segundo plano controlável - delegue uma tarefa, redirecione-a durante a execução e mantenha o contexto dele fora do seu.

Documentação

██████╗ ██╗    ██████╗ ███████╗██╗     ███████╗ ██████╗  █████╗ ████████╗███████╗
██╔══██╗██║    ██╔══██╗██╔════╝██║     ██╔════╝██╔════╝ ██╔══██╗╚══██╔══╝██╔════╝
██████╔╝██║    ██║  ██║█████╗  ██║     █████╗  ██║  ███╗███████║   ██║   █████╗
██╔═══╝ ██║    ██║  ██║██╔══╝  ██║     ██╔══╝  ██║   ██║██╔══██║   ██║   ██╔══╝
██║     ██║    ██████╔╝███████╗███████╗███████╗╚██████╔╝██║  ██║   ██║   ███████╗
╚═╝     ╚═╝    ╚═════╝ ╚══════╝╚══════╝╚══════╝ ╚═════╝ ╚═╝  ╚═╝   ╚═╝   ╚══════╝
                            ███╗   ███╗ ██████╗██████╗
                            ████╗ ████║██╔════╝██╔══██╗
                            ██╔████╔██║██║     ██████╔╝
                            ██║╚██╔╝██║██║     ██╔═══╝
                            ██║ ╚═╝ ██║╚██████╗██║
                            ╚═╝     ╚═╝ ╚═════╝╚═╝

npm node license

Servidor MCP que expõe o agente de codificação pi como um trabalhador delegável e direcionável.

Aponte o Claude Code (ou qualquer host MCP) para ele e delegue trabalho a qualquer um dos ~38 provedores do pi (DeepSeek, Grok, GLM, Kimi, Qwen, Codex, OpenRouter, llama.cpp local), mantendo o contexto do sub-agente fora da sua conversa principal.

Para que serve

Seu harness principal roda em um modelo caro, com uma janela de contexto que você se importa. Muito do que ele faz não precisa desse modelo e prejudica ativamente esse contexto: pesquisar um repositório em busca de cada ponto de chamada, ler um arquivo de 2000 linhas para responder uma pergunta, auditar o que um refactor deixou para trás.

Entregue esse trabalho a um delegado:

  • Custo. O trabalho braçal roda em DeepSeek, GLM, Kimi, Qwen ou llama.cpp local. Você paga preços de fronteira apenas pelo raciocínio que realmente precisa deles.
  • Contexto. O delegado lê os arquivos com seu próprio orçamento e retorna um resultado. Os 200 KB que ele leu nunca entram na sua conversa.
  • Raio de impacto. Delegados são somente leitura por padrão (read, grep, find, ls), aplicado na construção da sessão. Um modelo barato fazendo trabalho exploratório não pode tocar sua árvore, a menos que você o opte.

O delegado é sempre o agente pi. Codex, Grok, DeepSeek e os demais fornecem o modelo por trás dele; isto não é um wrapper em torno de seus CLIs.

Por que pi, e não opencode ou um wrapper de CLI?

Um delegado só é direcionável se dois canais permanecerem abertos: você deve poder redirecioná-lo no meio da tarefa, e ele deve poder perguntar algo a você e bloquear até que você responda. A maioria das formas de dirigir um agente de codificação a partir de outro programa fecha ambos.

pi -p / wrappers de CLIopencode SDKeste servidor
Roda em processonão (subprocesso)não (cliente HTTP para opencode serve)sim (createAgentSession)
Redirecionar uma execução em andamentonãoabort apenassteer
Agente pode perguntar algo a vocênão (ctx.hasUI falso)não na API de sessãostatusanswer *
Modelo por chamadanãosimargumento model

pi -p e --mode json definem ctx.hasUI = false. Um delegado iniciado dessa forma é fire-and-forget por construção: ele não pode levantar uma pergunta, e você não pode redirecioná-lo.

O SDK do opencode é um cliente tipado para um processo de servidor separado: createOpencode() inicia opencode serve e fala HTTP com ele. Design limpo, mas significa um segundo processo para supervisionar, e a superfície de sessão que ele expõe (prompt, abort, revert, messages) não tem direcionamento no meio da execução e nenhum caminho para o agente perguntar algo ao chamador.

O pi fornece createAgentSession como uma biblioteca incorporável. Este servidor mantém o objeto de sessão em processo, então session.steer() pode enviar uma mensagem após a chamada de ferramenta atual e antes da próxima chamada de modelo, e um uiContext sintético captura as perguntas do agente e as estaciona para answer. Nada é terceirizado; nada precisa ser supervisionado.

* Perguntas vêm de extensões do pi, então esse canal está aberto apenas para delegados gerados com extensions: true. Veja Pesquisa na web e outras ferramentas de extensão.

(A tabela compara o canal de delegação, não o sandboxing; o opencode tem sua própria configuração de permissões. Veja Somente leitura por padrão para o que este servidor aplica e não aplica.)

Ferramentas

FerramentaPropósito
initChame primeiro. Relata modelos alcançáveis, ferramentas permitidas e como dirigir um delegado. Toda outra ferramenta recusa até que ela rode uma vez.
spawnDelega em segundo plano. Retorna sessionId imediatamente. Use por padrão.
spawn_batchDistribui até 10 delegados em uma chamada. Validado como lote, então nada inicia se uma tarefa for ruim.
runDelega e bloqueia até terminar. Apenas para perguntas rápidas.
statusEstado, turnos, ferramentas usadas, texto mais recente e perguntas pendentes.
steerRedireciona um agente em execução. Chega após sua chamada de ferramenta atual.
follow_upDá a um delegado concluído outro turno. Ele mantém tudo o que leu, então você não reexplica a tarefa.
answerResponde a uma pergunta levantada por status. Só alcançável com extensions: true, pois apenas extensões podem perguntar.
abortPara uma sessão; a saída parcial permanece legível.
modelsLista modelos que este delegado pode usar.
sessionsLista sessões, em execução e concluídas. Filtre por state, expanda com verbose.
forgetRemove uma sessão concluída do histórico, liberando seu id.

Instalação

Requer Node.js 22.19+ e uma instalação funcional do pi que tenha sido autenticada uma vez (pi, depois /login).

Claude Code

claude mcp add pi -e PI_DELEGATE_MODEL=openrouter/stealth/ox-alpha -- npx -y pi-delegate-mcp

Qualquer host MCP, via .mcp.json

{
  "mcpServers": {
    "pi": {
      "command": "npx",
      "args": ["-y", "pi-delegate-mcp"],
      "env": { "PI_DELEGATE_MODEL": "openrouter/stealth/ox-alpha" },
      "timeout": 1800000
    }
  }
}

npx resolve o pacote a cada inicialização. Para fixá-lo, instale globalmente e chame o binário diretamente:

npm install -g pi-delegate-mcp
{ "mcpServers": { "pi": { "command": "pi-delegate-mcp", "timeout": 1800000 } } }

Mantenha a chave do servidor curta, pois ela prefixa cada nome de ferramenta (mcp__pi__spawn).

A partir do código-fonte

git clone https://github.com/howznguyen/pi-delegate-mcp && cd pi-delegate-mcp
npm install && npm run build && npm link

Primeira execução

Peça ao seu agente para delegar algo. Ele chama init uma vez para aprender o que este servidor pode alcançar, depois spawn:

{ "id": "audit-01", "label": "who still imports onnxruntime",
  "prompt": "Search this repo for anything still importing onnxruntime and list the files.",
  "cwd": "/path/to/repo" }
{ "sessionId": "audit-01", "state": "running", "model": "opencode-go/deepseek-v4-flash",
  "activeTools": ["read", "grep", "find", "ls"] }

spawn retorna imediatamente. Consulte com status para o rastreamento ordenado de ferramentas e a resposta, ou sessions quando vários estiverem em andamento. Se init falhar, ele diz exatamente o que está faltando: pi não instalado, nenhum provedor autenticado ou um escopo de modelo que não corresponde a nada.

Os nomes de modelos nos exemplos abaixo são ilustrativos. Execute models para ver o que sua própria instalação do pi pode realmente alcançar.

Rastreabilidade

spawn e run ambos aceitam seu próprio id e um label de texto livre:

{
  "id": "search-audit-01",
  "label": "what ONNX removal left behind",
  "prompt": "...",
  "model": "opencode-go/deepseek-v4-flash"
}

Ids são [A-Za-z0-9._:-], 1-64 caracteres, devem começar alfanuméricos e devem ser únicos entre sessões ativas. Omita para um UUID.

Sessões concluídas permanecem legíveis via status e sessions em vez de desaparecerem, então você pode voltar e verificar o que um delegado realmente fez. As PI_DELEGATE_HISTORY mais recentes (padrão 50) são mantidas; forget remove uma antecipadamente.

status retorna um rastreamento ordenado de toolCalls: cada ferramenta que o delegado executou, com argumentos e tempo. Adicione verbose: true para ids de chamada e resultados:

{
  "seq": 1,
  "id": "call_467b4bb4…",
  "name": "bash",
  "state": "ok",
  "ms": 10,
  "args": "{\"command\":\"echo hello-trace\"}",
  "result": "hello-trace\n"
}

Argumentos e resultados são truncados (PI_DELEGATE_TRACE_ARGS, PI_DELEGATE_TRACE_RESULT) com o comprimento descartado registrado, então um único read de um arquivo grande não pode inundar seu contexto.

Dando a um delegado outro turno

Um delegado concluído não está gasto. O pi mantém sua sessão em memória, então follow_up re-prompta o mesmo agente com tudo o que ele já leu ainda em contexto:

{ "sessionId": "search-audit-01", "prompt": "Now check whether the build files reference it too" }
{ "sessionId": "search-audit-01", "state": "running", "turnsSoFar": 1 }

O delegado continua de onde parou. Ele ainda mantém os arquivos que leu no primeiro turno, então a segunda pergunta custa uma chamada de modelo em vez de uma sessão nova relendo o repositório.

Esta é a forma barata de ter uma conversa com um delegado. Gerar um novo significa reexplicar a tarefa e pagar para ele reler os mesmos arquivos, e sua resposta chega sem nenhum do raciocínio que levou até lá.

follow_up recusa um delegado que ainda está trabalhando, porque redirecionar um no meio da tarefa é para isso que steer serve. Os dois não são intercambiáveis: steer chega entre chamadas de ferramenta em um agente em execução, follow_up inicia um novo turno em um concluído.

Distribuindo em lote

spawn_batch inicia um lote inteiro em uma chamada. As tarefas herdam o model, cwd, tools e extensions do nível do lote, e os substituem individualmente onde precisam:

{
  "idPrefix": "audit",
  "model": "opencode-go/deepseek-v4-flash",
  "cwd": "/repo",
  "tools": ["ls"],
  "tasks": [
    { "prompt": "What still imports onnxruntime?", "label": "imports" },
    { "prompt": "Which build files still reference ONNX?", "label": "build" },
    {
      "prompt": "Any ONNX model files left on disk?",
      "label": "artifacts",
      "model": "opencode-go/ox-alpha-free"
    }
  ]
}

Isso os nomeia audit-01, audit-02, audit-03 e retorna em alguns milissegundos, pois iniciar um delegado não espera ele pensar.

O lote é validado antes de qualquer coisa começar: formato do id, ids duplicados dentro do lote, ids já ativos, ferramentas bloqueadas e cada nome de modelo. Uma tarefa ruim falha a chamada e não inicia nada. Meia distribuição é o pior resultado, porque você paga pelos delegados que iniciaram e ainda precisa descobrir quais não iniciaram.

Consulte o lote inteiro com uma chamada sessions em vez de uma status por delegado. Reduza para status apenas para o delegado que você realmente quer ler. steer e abort permanecem por sessão.

Escolhendo um modelo por chamada

model em qualquer chamada substitui PI_DELEGATE_MODEL. Um nome não resolvível é um erro grave, nunca um fallback silencioso para o modelo padrão, porque um fallback silencioso é como você acaba cobrando um modelo que nunca pediu.

Quais nomes resolvem é decidido pelo próprio escopo enabledModels do pi, que este servidor aplica em vez de meramente exibir:

opencode-go/deepseek-v4-flash  -> ok      (listed in enabledModels)
opencode-go/glm-5.3            -> refused (out of scope)
knowns-hub/claude-opus         -> ok      (custom provider, see below)

Provedores personalizados ignoram o escopo. Qualquer modelo servido por um provedor declarado em ~/.pi/agent/models.json é oferecido mesmo quando enabledModels não o nomeia, com a justificativa de que declarar um provedor manualmente já é uma intenção de usá-lo. É por isso que a lista pode ser muito mais longa que enabledModels: três entradas no escopo mais dois provedores personalizados podem facilmente significar quinze modelos oferecidos. init diz isso explicitamente em models.scopeNote quando se aplica.

Duas opções mudam isso:

Efeito
PI_DELEGATE_STRICT_SCOPE=1Honra enabledModels exatamente. A exceção de provedor personalizado é descartada.
PI_DELEGATE_IGNORE_SCOPE=1Descarta o escopo completamente. Todo modelo autenticado é utilizável.

Chame models para ver o que é realmente alcançável sob a configuração em vigor.

Linha de status

O Claude Code permite exatamente um comando statusLine, então pi-delegate-statusline envolve o que você já executa e anexa um segmento mostrando os delegados deste workspace:

{
  "statusLine": {
    "type": "command",
    "command": "PI_DELEGATE_STATUSLINE_WRAP=ccstatusline pi-delegate-statusline",
    "refreshInterval": 10
  }
}

Remova PI_DELEGATE_STATUSLINE_WRAP para imprimir apenas o segmento do pi.

π ▸ audit engine·t1·12s audit index·t2·8s   running, with turn counts and elapsed time
π ▸ migrate·t7·3m04s ?1 waiting             one delegate is blocked on a question
π ✓2                                        finished, nothing running

Quais delegados pertencem a qual sessão

Filtrar por diretório não é suficiente: duas sessões do Claude Code abertas no mesmo repositório mostrariam os delegados uma da outra. A atribuição usa a linhagem de processos. O host MCP inicia um servidor por sessão, então o servidor registra process.ppid, o pid do host. A linha de status, iniciada por esse mesmo host, percorre sua própria ancestralidade e mantém apenas os arquivos de estado cujo hostPid encontra ali. Mesmo repositório, duas sessões, sem interferência. O filtro de diretório permanece como fallback para arquivos de estado gravados antes disso existir.

O estado vive em $XDG_STATE_HOME/pi-delegate-mcp/<pid>.json (PI_DELEGATE_STATE_DIR para realocar). Os arquivos são podados quando o processo desaparece, ESRCH apenas, já que EPERM significa que o processo está vivo sob outro usuário. Os servidores também saem por conta própria quando o stdin fecha ou o pid do host desaparece, então um host que morre sem fechar o transporte não deixa nada para trás.

Somente leitura por padrão

As ferramentas são bloqueadas para read, grep, find, ls na construção da sessão. Qualquer outra coisa é recusada antes mesmo de uma sessão ser criada.

Para ampliar isso, nomeie as ferramentas extras no servidor:

"env": { "PI_DELEGATE_ALLOW_TOOLS": "bash" }

ou PI_DELEGATE_ALLOW_WRITE=1 para permitir tudo.

bash não é um meio-termo. O pi não inclui sistema de permissões, então um delegado com bash pode gravar arquivos, excluí-los e acessar a rede independentemente de write e edit estarem na sua lista. Recusar esses dois enquanto permite bash registra sua intenção; não impõe nada. Os prompts de permissão e hooks do Claude Code nunca veem o que o pi faz. Se você precisar de um limite real, execute este servidor dentro de um contêiner.

Busca na web e outras ferramentas de extensão

As ferramentas próprias do pi são read, grep, find, ls, bash, powershell, write, edit. Não há busca nem fetch entre elas. Essas vêm das extensões do pi, que registram suas próprias ferramentas, e um delegado pode usá-las.

Defina extensions: true na chamada e permita os nomes das ferramentas no servidor:

"env": { "PI_DELEGATE_ALLOW_TOOLS": "web_search,fetch_content" }
{ "prompt": "Find the current Node LTS version and tell me just the number",
  "extensions": true, "tools": ["read", "grep", "find", "ls", "web_search"] }
{ "seq": 1, "name": "web_search", "state": "ok", "ms": 2568,
  "args": "{\"query\":\"latest stable Node.js LTS version\",\"numResults\":5}" }

É assim que você dá a um delegado alcance de rede sem entregar bash. web_search pode buscar e nada mais, e passa pela mesma allowlist que qualquer outra ferramenta, então o padrão somente leitura permanece inalterado para chamadas que não o solicitam.

Quais ferramentas existem dependem do que o usuário que executa o servidor tem instalado. pi-web-access fornece web_search, fetch_content, source_check e get_search_content. pi-mcp-adapter faz a ponte entre os servidores MCP em ~/.pi/agent/mcp.json e os expõe como mcp. O pi não tem cliente MCP próprio, então essa extensão é a única rota para um.

extensions: true confia em toda extensão instalada, não apenas na que você queria. Elas carregam como um conjunto, rodam com todos os privilégios do processo deste servidor, e algumas abrem sockets e timers que sobrevivem à sessão. Ative por chamada, para os delegados que precisam, em vez de deixar ativado por padrão. Também custa tempo real de inicialização, por isso fica desligado a menos que seja solicitado.

Configuração

Variável de ambientePadrãoSignificado
PI_DELEGATE_MODELpadrão do piModelo usado quando uma chamada omite model
PI_DELEGATE_ALLOW_TOOLSnão definidoLista separada por vírgulas de ferramentas extras a permitir, ex.: bash
PI_DELEGATE_ALLOW_WRITEnão definido1 permite todas as ferramentas
PI_DELEGATE_HISTORY50Sessões concluídas mantidas para revisão
PI_DELEGATE_TRACE_ARGS400Máx. de caracteres dos argumentos de ferramenta mantidos no trace
PI_DELEGATE_TRACE_RESULT600Máx. de caracteres dos resultados de ferramenta mantidos no trace
PI_DELEGATE_BATCH_MAX10Teto de tarefas por chamada de spawn_batch
PI_DELEGATE_LIST_CAP60Acima disso, init resume modelos por provedor em vez de listá-los
PI_DELEGATE_STATE_DIRdiretório de estado XDGOnde o estado da linha de status é publicado
PI_DELEGATE_STATUSLINE_WRAPnão definidoComando da linha de status a envolver e anexar
PI_DELEGATE_STATUSLINE_LOGnão definidoArquivo para anexar um timestamp a cada renderização da linha de status, para depuração
PI_DELEGATE_PROGRESS_MS15000Intervalo de notificação de progresso durante run
PI_DELEGATE_IGNORE_SCOPEnão definido1 ignora o escopo de enabledModels do pi, permitindo qualquer modelo configurado
PI_DELEGATE_STRICT_SCOPEnão definido1 honra enabledModels exatamente, removendo o bypass de provedor personalizado
PI_CODING_AGENT_DIR~/.pi/agentDe onde o auth.json e a configuração do pi são lidos

Trabalho de longa duração

O SDK MCP TypeScript usa por padrão um timeout de requisição de 60 segundos, que uma tarefa real vai estourar. Três defesas, em ordem de preferência:

  1. Use spawn + status. Nada bloqueia, então nenhum timeout se aplica.
  2. run emite notificações periódicas de progresso, que resetam o timeout do host.
  3. Aumente o teto com "timeout" em .mcp.json ou MCP_TOOL_TIMEOUT no ambiente.

CLAUDE_AUTO_BACKGROUND_TASKS=1 faz o Claude Code colocar chamadas MCP longas em segundo plano após ~2 minutos. Note que notificações de progresso são descartadas uma vez que a chamada vai para segundo plano, então escolha (1) ou (3), não ambos.

Autenticação

O servidor não lida com credenciais. O pi autentica a si mesmo a partir de ~/.pi/agent/auth.json, depois variáveis de ambiente. Hosts MCP frequentemente iniciam servidores com um ambiente reduzido, então prefira auth.json (execute pi uma vez e /login) em vez de exportar chaves em um perfil de shell.

Desenvolvimento

npm install
npm run build       # tsc, src/*.ts -> dist/
npm run typecheck   # tsc --noEmit, strict
npm run test:ci     # offline: boots the server over stdio and lists its tools
npm test            # full suite: needs a logged-in pi, makes real model calls

test:ci é o que o CI executa e o que prepublishOnly usa como porta de entrada, porque não precisa de credenciais e nem de rede. npm test aciona delegados reais contra provedores reais, então custa dinheiro e só funciona onde pi foi autenticado.

CaminhoO que vive ali
src/config.tsCada variável de ambiente, lida em um só lugar
src/permissions.tsA allowlist de ferramentas e a porta que a impõe
src/registry.tsMapa de sessões, reivindicação de id, evicção de histórico
src/tools/Um módulo por grupo de ferramentas MCP
src/pi/Tudo que toca o SDK do pi
src/statusline/Publicação de arquivos de estado e o binário da linha de status

Lançamentos são dirigidos por tags. npm version patch && git push --follow-tags executa o build e os testes, depois publica via OIDC trusted publishing, então nenhum token npm é armazenado em qualquer lugar do repositório.

Issues e pull requests são bem-vindos. Se você está relatando um delegado que se comportou mal, o trace toolCalls de status com verbose: true é a coisa útil para anexar.

Trabalho anterior

abatilo/pi-mcp-bridge segue o caminho mais simples: iniciar pi --mode json -p --session-id <uuid> e deixar o pi persistir sessões no disco, então a ponte não guarda estado algum. Elegante, e vale a leitura. Ele troca direção, perguntas e controle de ferramentas para chegar lá.

Licença

MIT