MCP para agentes de programação

text mcp: permita que agentes de programação naveguem, pressionem teclas e leiam quadros pelo Model Context Protocol.

text mcp executa seu diretório app/ sem interface gráfica, no mesmo processo, e o disponibiliza pelo Model Context Protocol para que agentes de programação possam usá-lo sem DevTools. O agente navega, pressiona teclas, lê quadros como texto simples e chama manipuladores de rota.

bunx --bun text mcp              # serve ./app over stdiobunx --bun text mcp apps/console # another project

text mcp precisa do Bun. Quando iniciado com Node, ele se executa novamente com Bun se bun estiver no seu PATH, e falha com TX019 caso contrário. Os clientes MCP o iniciam para você, então raramente é necessário executá-lo manualmente.

Configuração

Cada cliente inicia text mcp a partir do diretório do seu projeto. Use bunx --bun para que os arquivos de rota sejam executados como TypeScript.

OpenCode

opencode.jsonjson
{  "$schema": "https://opencode.ai/config.json",  "mcp": {    "textjs": {      "type": "local",      "command": ["bunx", "--bun", "text", "mcp"],      "enabled": true    }  }}

Claude Code

claude mcp add textjs -- bunx --bun text mcp

Ou inclua a configuração em um commit para o time:

.mcp.jsonjson
{  "mcpServers": {    "textjs": { "command": "bunx", "args": ["--bun", "text", "mcp"] }  }}

Cursor

.cursor/mcp.jsonjson
{  "mcpServers": {    "textjs": { "command": "bunx", "args": ["--bun", "text", "mcp"] }  }}

VS Code

.vscode/mcp.jsonjson
{  "servers": {    "textjs": { "type": "stdio", "command": "bunx", "args": ["--bun", "text", "mcp"] }  }}

Para um projeto em um subdiretório, anexe o caminho: ["--bun", "text", "mcp", "apps/console"].

Ferramentas

FerramentaDescrição
list_routesPáginas e manipuladores de rota em app/, e a localização atual
navigateAdicionar ou substituir um caminho, ou ir para back / forward
read_frameO quadro como texto, com rota, tamanho, foco e cursor. styles: true adiciona cores e atributos por span
pressCombinações de teclas: "down", "ctrl+k", "shift+tab", "tab tab enter", "down*3"
typeDigitar texto com uma tecla por caractere (até 2.000; use paste para mais). submit: true pressiona Enter depois
pasteUm evento de colagem, como ⌘V
scrollRolagem com a roda do mouse por rows (e cols). Um <ScrollBox> rola com a roda sem foco do teclado
resizeDefinir o tamanho da grade em colunas e linhas
inspect_treeA árvore de componentes com limites na tela, além de cada id que pode receber foco
findLimites de cada correspondência e o componente sob ela
wait_forEsperar um texto aparecer ou desaparecer, ou o app ficar ocioso
logsSaída capturada de console.* e erros do React
fetchChamar um manipulador route.ts no mesmo processo. Os corpos são lidos até maxBytes; os fluxos de eventos, por streamMs, e depois cancelados
reloadImportar o app novamente após você editar arquivos

As ferramentas de entrada retornam o novo quadro, então o agente não precisa de um read_frame separado após cada tecla. O quadro atual e a lista de rotas também estão disponíveis como os recursos text://frame e text://routes.

Uma interação típica:

→ navigate { "path": "/deployments" }→ press { "keys": "down down enter" }← route: /deployments/dpl_6m3ty8  (route /deployments/[id])  title: "Deployment dpl_6m3ty8 · Edge Console"  size: 100×32  frame: 6  idle  focus: _txr_c_ ScrollBox [4,12 92×18]    ▼ Edge Console   Overview   Deployments   Logs   Settings    ← Deployments  /  dpl_6m3ty8   Ready    Production  …

Quadros e ociosidade

Antes de ler um quadro, qualquer ferramenta espera o app ficar ocioso: nenhum quadro agendado, nenhum fetch em andamento e nenhum commit ou quadro por 50 ms. Assim, o agente vê o resultado da sua entrada, não um quadro no meio de uma atualização.

Um <Spinner> ou um gráfico que se atualiza uma vez por segundo ainda conta como ocioso entre as atualizações, então páginas com animações continuam legíveis. Um app que renderiza com intervalos menores que 50 ms nunca fica ocioso. As leituras desistem após 1,5 segundos (settleMs altera isso), enviam o quadro mais recente e o marcam como busy. wait_for sem texto espera a ociosidade, com seu próprio quietMs, e retorna um erro se ela nunca ocorrer.

As coordenadas são células com índice a partir de zero: x é a coluna, y é a linha. Caracteres largos, como CJK e emojis, ocupam duas colunas.

Erros e logs

stdout transporta o protocolo. A saída de console.* do seu app, chamadas avulsas de process.stdout.write e erros do React vão para logs, numerados para que um agente possa pedir apenas novas entradas com since. Quando erros foram registrados desde a última leitura, o cabeçalho do próximo quadro informa isso.

As falhas retornam como erros de ferramenta, nunca como uma interrupção do processo. Uma ferramenta que excede o tempo limite é cancelada antes da execução da próxima chamada, então uma chamada lenta nunca digita no quadro da chamada seguinte. Erros com um código do Text.js o mantêm, por exemplo, TX007 para uma tecla desconhecida. Se o app falhar ao carregar, cada ferramenta explica o motivo. Corrija o arquivo e chame reload.

Opções

OpçãoDescrição
--path <pathname>Primeira rota (padrão /)
--cols / --rowsTamanho da grade (padrão: viewport em text.config.ts, caso contrário 100×32)
--theme <name>Predefinição de tema
--no-configIgnorar text.config.ts
--verboseEscrever também os logs capturados em stderr

Como funciona

text mcp executa seu app da mesma forma que text run: cada módulo de rota é importado de antemão, o App Router usa histórico em memória e chamadas fetch("/api/…") de mesma origem executam seus manipuladores no mesmo processo. Os quadros vão para uma grade de células fora da tela em vez de um terminal. Páginas renderizadas no servidor (export const runtime = "server") e seus componentes async function renderizam no mesmo processo, como em text dev, e o quadro é lido assim que eles concluem.

reload desmonta o app, remove todos os módulos do projeto do cache de módulos do Bun (incluindo arquivos fora de app/), examina app/ novamente e remonta na localização atual.

Para incorporar o servidor ao seu próprio processo ou usar outro transporte:

import { createTextMcpServer } from "@textjs/app/mcp";import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";const { server, controller } = createTextMcpServer({ root: process.cwd() });await server.connect(new StdioServerTransport());void controller.start();

Limites

  • O app roda no Bun, não em um navegador. window e document são indefinidos, como em text run.
  • Cada servidor executa um app com uma grade. Cada cliente MCP inicia seu próprio servidor.
  • text mcp executa sua própria cópia do app sem interface gráfica. Ele não pode se conectar a uma aba do navegador aberta com text dev.
Editar esta página no GitHub