terminal-use
Permite que los agentes de IA manejen una terminal real: escribir, presionar teclas, hacer clic, leer la pantalla y tomar capturas de pantalla.
Documentación
terminal-use
Un servidor MCP que permite a un agente de IA usar una terminal real como lo haría una persona: escribir, presionar teclas, hacer clic, leer la pantalla, tomar una captura de pantalla.

Cada fotograma anterior fue dibujado por el propio renderizador de capturas de pantalla de terminal-use; el pie de cada uno es la llamada a la herramienta que lo produjo.
La mayoría de las herramientas de shell para agentes ejecutan un comando y devuelven su salida. Eso falla con cualquier cosa interactiva: vim, htop, un REPL, un instalador que hace preguntas, git rebase -i, una sesión SSH. terminal-use le da al agente un shell en un pseudo-terminal real, renderizado por un emulador de terminal real, de modo que los programas interactivos y de pantalla completa funcionan y el agente ve lo que tú verías.
- PTY real, emulador real — los colores, el movimiento del cursor y la pantalla alternativa se interpretan, no se pasan como códigos de escape.
- Texto y píxeles — lee la pantalla como texto plano, o como PNG cuando el diseño y el color importan.
- Espera correctamente — se bloquea hasta que un comando realmente termina, o hasta que aparece cierto texto.
- Observa en paralelo — conecta tu propia terminal a cualquier sesión y escribe junto al agente.
Inicio rápido
Requiere Node.js 20.19 o superior, en macOS, Linux o Windows.
Claude Code
claude mcp add terminal-use --scope user -- npx -y terminal-use
Otros clientes MCP (Claude Desktop, Cursor y cualquier otro que acepte un comando):
{
"mcpServers": {
"terminal-use": {
"command": "npx",
"args": ["-y", "terminal-use"]
}
}
}
Inicia una nueva sesión en tu cliente y pídele que haga algo en una terminal, por ejemplo: "Abre vim, escribe un haiku en /tmp/haiku.txt, guarda y sal, y luego muéstrame una captura de pantalla de cat-lo."
Las opciones del servidor van después del comando: npx -y terminal-use --cols 100 --rows 40.
| Opción | Predeterminado | |
|---|---|---|
--shell <path> | $SHELL o /bin/bash; PowerShell en Windows | Shell para ejecutar en nuevas sesiones |
--cwd <path> | donde se inició el servidor | Directorio de trabajo para nuevas sesiones |
--cols <n> / --rows <n> | 120 / 30 | Tamaño de terminal |
--scrollback <n> | 5000 | Líneas de historial conservadas |
--login | desactivado | Iniciar shells como shells de inicio de sesión (ver más abajo) |
"command not found" dentro de una sesión
Si los programas que funcionan en tu propia terminal (node, brew, pyenv…) faltan dentro de una sesión, el servidor probablemente heredó un entorno vacío. Eso ocurre cuando el cliente MCP se inicia desde el Dock o un lanzador en lugar de una terminal: tu PATH se configura mediante los archivos de perfil de tu shell (~/.zprofile, ~/.bash_profile, ~/.profile), y nadie los ha leído.
Un shell de inicio de sesión lee esos archivos. Actívalo para cada sesión con la bandera de servidor --login, o para una sola sesión con login: true en terminal_create. Está desactivado por defecto porque hace que cada shell tarde más en iniciarse y ejecuta lo que tu perfil ejecute. No tiene efecto en PowerShell ni en cmd.exe, que no tienen modo de inicio de sesión.
Herramientas
Toda herramienta excepto terminal_create y terminal_list toma el sessionId que devuelve terminal_create.
| Herramienta | Qué hace |
|---|---|
terminal_create | Iniciar una sesión: un shell interactivo, o un programa con command. Opcionalmente label, cols, rows, shell, cwd, env, login, scrollback, theme. |
terminal_list | Listar sesiones. |
terminal_destroy | Terminar una sesión. |
terminal_type | Escribir texto. \n presiona Enter. paste: true lo envía como un solo pegado. |
terminal_press | Presionar una tecla o combinación: Enter, Ctrl+C, ArrowUp, Shift+Tab, Alt+Enter, F5… |
terminal_wait | Esperar a que el comando en ejecución termine, a que aparezca una expresión regular, o a que la salida se calle. |
terminal_read | Leer la pantalla o el historial como texto. |
terminal_screenshot | Renderizar la pantalla como PNG. |
terminal_click | Hacer clic izquierdo en una celda, con un paso de vista previa. |
terminal_scroll | Girar la rueda del ratón sobre una celda. |
terminal_batch | Enviar varias entradas en una sola llamada y obtener la pantalla una vez. |
terminal_resize | Cambiar el tamaño de la terminal. |
terminal_reset | Limpiar la pantalla y el historial, o reiniciar el shell con hardReset: true. |
Docker
El repositorio tiene un Dockerfile para ejecutar el servidor en un contenedor:
docker build -t terminal-use .
docker run -i --rm -v "$PWD":/workspace terminal-use
En un cliente MCP, usa docker como comando con run -i --rm terminal-use como sus argumentos. Las terminales que recibe el agente son entonces shells dentro del contenedor, no en tu máquina: solo ve lo que montas, que es el punto si quieres mantenerlo aislado. Conectarse desde tu propia terminal no está disponible en esta configuración.
La habilidad
terminal-use incluye una Habilidad de Agente: una guía breve para el agente sobre cómo usar bien estas herramientas — cuándo usar una sesión de comandos, cómo esperar, cómo leer lo que está seleccionado — además de recetas para vim, paginadores, REPLs, programas guiados por menús, avisos de ssh y pruebas de extremo a extremo de una TUI. Vive en skills/terminal-use.
-
Los hosts que cargan habilidades desde servidores MCP la obtienen automáticamente. El servidor implementa la extensión de Habilidades MCP (
io.modelcontextprotocol/skills): la habilidad se lista medianteskills/listy sus archivos se sirven como recursosskill://terminal-use/.... Pocos hosts soportan esto todavía. -
Los hosts que cargan habilidades desde disco pueden instalar los mismos archivos. Para Claude Code:
cp -r "$(npx -y terminal-use skills-dir)/terminal-use" ~/.claude/skills/
La habilidad es opcional. Sin ella, el agente aún tiene las descripciones de las herramientas y las instrucciones integradas del servidor.
Sesiones de shell y sesiones de comandos
Por defecto, una sesión es un shell interactivo: escribe comandos en él como lo harías en un aviso.
Pasa command a terminal_create para ejecutar un solo programa en la terminal en su lugar:
{"command": "npm test -- --watch", "cwd": "/path/to/project", "env": {"CI": "1"}}
El comando pasa por el shell de la sesión (sh -c, PowerShell -Command o cmd /c), por lo que las comillas, las tuberías y la redirección funcionan como en el aviso de ese shell. La entrada va directamente al programa. Cuando sale:
- su estado de salida se informa (por la llamada que estaba en curso, por
terminal_waity enterminal_list); - la pantalla final permanece legible con
terminal_readyterminal_screenshot; - las herramientas de entrada devuelven un error con el estado de salida en lugar de reiniciar nada;
terminal_resetconhardReset: truelo ejecuta de nuevo, yterminal_destroylo elimina.
Este es el modo para probar una CLI o TUI: lánzala, condúcela, comprueba cómo terminó.
Observar y escribir en paralelo
terminal_create devuelve un comando que puedes ejecutar en tu propia terminal para unirte a la sesión:
node /path/to/terminal-use/bin/terminal-use.js attach 3 --socket /tmp/terminal-use-501/41234-3.sock
Ves lo que ve el agente y puedes escribir en el mismo shell. Funciona como una sesión tmux compartida: varias personas pueden conectarse a la vez, y Ctrl+] desconecta.
- Obtienes la pantalla actual y el historial reciente al conectar, no una terminal en blanco.
--resizehace que la sesión siga el tamaño de tu ventana. Por defecto, la sesión mantiene el suyo propio.--socketelige el servidor. Cada cliente MCP ejecuta su propio terminal-use, y todos numeran las sesiones desde 1; sin--socket,attach <id>funciona cuando solo un servidor en ejecución tiene ese id y lista los candidatos en caso contrario.- En Windows, la sesión se alcanza mediante una tubería con nombre en lugar de un archivo de socket; el comando que se te da funciona igual.
- Los sockets son por usuario (
0600, dentro de un directorio0700bajo el directorio temporal del sistema). Cualquiera que pueda conectarse obtiene un shell como tú, por lo que no se exponen más allá de eso.
Cómo funciona
agent ──MCP──▶ terminal-use ──▶ node-pty ──▶ your shell
│
└─ @xterm/headless ◀── everything the shell prints
│
├─ terminal_read (text)
└─ terminal_screenshot (PNG)
node-pty ejecuta el shell en un pseudo-terminal. Todo lo que imprime se alimenta a un emulador xterm.js sin cabeza, y el búfer de ese emulador es la única fuente de verdad: tanto las lecturas como las capturas de pantalla provienen de él, por lo que el agente obtiene la pantalla renderizada en lugar de un flujo de códigos de escape.
Detalles
Esperar cosas
terminal_type, terminal_press y terminal_click devuelven una vez que la salida ha estado en silencio por un momento (idleMs, 200 ms por defecto) y nunca esperan más de 10 segundos. Eso sirve para pulsaciones de teclas. No sirve para una compilación, que puede estar en silencio durante un buen rato mucho antes de terminar. Para cualquier cosa lenta, continúa con terminal_wait:
- Predeterminado: esperar a que el comando termine. terminal-use pregunta al kernel qué proceso posee el primer plano de la terminal. Un shell entrega la terminal a cada comando que ejecuta y la recupera después, así que cuando el shell la posee de nuevo, el aviso ha vuelto. Esto no necesita integración de shell ni análisis de avisos, y funciona para comandos que no imprimen nada.
pattern: esperar a que una expresión regular coincida con la pantalla. Para cosas que nunca salen (Listening on port), avisos de REPL o un estado particular de una TUI.^y$coinciden al inicio y final de línea; un(?i)o(?s)inicial establece más banderas.until: "quiet": esperar a que la salida se detenga durantequietMs(1 s por defecto). La misma regla que usan las herramientas de escritura, sin el límite de 10 segundos.
En una sesión de comandos, el modo predeterminado espera a que el programa salga e informa su estado de salida.
timeoutMs tiene un valor predeterminado de 30 segundos (máximo 10 minutos). Un tiempo de espera agotado no es un error: la respuesta dice que el comando sigue en ejecución, y puedes esperar de nuevo.
En Windows no hay forma de preguntar quién posee la terminal, así que en una sesión de shell el modo predeterminado espera dos segundos de silencio en su lugar, y dice que es una suposición. Prefiere un pattern allí, o ejecuta el programa como una sesión de comandos, donde esperar a que salga funciona en todas las plataformas.
Límites que vale la pena conocer: los trabajos en segundo plano (cmd &) no cuentan como en ejecución. Dentro de un programa anidado como ssh o un REPL, el shell externo no recupera el primer plano hasta que ese programa sale, así que usa pattern o until: "quiet" allí. No se informa ningún estado de salida; ejecuta echo $?.
Leer la pantalla
terminal_read devuelve texto en páginas del tamaño de la pantalla: page: 0 es la pantalla actual, page: 1 la anterior, y así sucesivamente hacia atrás por el historial.
Las líneas que la terminal envolvió en su borde derecho se vuelven a unir en la única línea que el programa imprimió (joinWrapped: false da una línea por fila de pantalla).
El texto no puede mostrar color, por lo que no puede mostrar qué entrada de menú está seleccionada. Por eso, las lecturas terminan con una lista de lo que está resaltado en pantalla — texto en vídeo inverso o sobre un color de fondo — con la fila y las columnas que terminal_click toma:
Highlighted on screen (reverse video or background color; screen row, columns):
row 7, cols 3-18: "Unstaged changes"
Si la mayor parte de la pantalla son paneles de colores, la lista se reemplaza por un puntero a terminal_screenshot. Apágalo con highlights: false.
El cursor se marca con ▌. En una celda vacía simplemente ocupa el lugar del espacio en blanco. Sobre un carácter, se inserta delante de ese carácter, lo que desplaza el resto de esa línea una columna a la derecha; el encabezado dice sobre qué carácter está. El marcador se omite mientras el programa oculta el cursor (la mayoría de los programas de pantalla completa lo hacen), y cursor: false devuelve el texto sin tocar.
Capturas de pantalla
terminal_screenshot renderiza la pantalla con la JetBrains Mono incluida. Esto es lo que devuelve una llamada, aquí con una selección visual en vim:

Las sesiones toman un theme al crearse: dark (predeterminado), light, solarized-dark o solarized-light.
JetBrains Mono cubre latín, griego, cirílico, dibujo de cajas y símbolos comunes. Los emojis, el texto chino/japonés y coreano recurren a fuentes ya presentes en el sistema, porque incluirlas añadiría decenas de megabytes:
| Emoji | CJK | Hangul | |
|---|---|---|---|
| macOS | Apple Color Emoji | Hiragino Sans GB / PingFang | Apple SD Gothic Neo |
| Windows | Segoe UI Emoji | Microsoft YaHei / Yu Gothic | Malgun Gothic |
| Linux | Noto Color Emoji | Noto Sans CJK | Noto Sans CJK |
macOS los incluye de serie, y Windows tiene la fuente de emojis (las fuentes de Asia Oriental vienen con las funciones de idioma correspondientes). En Debian o Ubuntu, instálalos con apt install fonts-noto-color-emoji fonts-noto-cjk. Sin ellos (una imagen Docker mínima, por ejemplo), esos caracteres se muestran como cuadros vacíos en las capturas de pantalla. Los iconos de Nerd Font y Powerline se muestran como cuadros en todas partes. terminal_read no se ve afectado y siempre devuelve los caracteres reales. |
Escritura, pegado y procesamiento por lotes
terminal_type envía caracteres como pulsaciones de teclas. Para texto multilínea que va a un editor, un REPL o un prompt de shell, añade paste: true: los programas que admiten pegado entre corchetes lo reciben como un solo pegado y lo insertan literalmente, sin autoindentación ni ejecución de cada línea a medida que llega. Los programas que no lo admiten reciben los caracteres simples.
terminal_batch envía una lista de entradas en una sola llamada y devuelve la pantalla una vez al final, lo que ahorra un viaje de ida y vuelta por pulsación de tecla cuando los pasos ya se conocen:
{
"sessionId": 1,
"actions": [
{"type": "press", "key": "ArrowDown", "count": 3},
{"type": "press", "key": "Enter"},
{"type": "wait", "pattern": "Commit message"},
{"type": "type", "text": "Fix typo"},
{"type": "press", "key": "Ctrl+S"}
]
}
Las acciones son type, paste, press, click, scroll y wait (un ms fijo, o un pattern para que aparezca). El lote se verifica antes de enviar cualquier cosa y se detiene en la primera acción que falla, informando hasta dónde llegó.
Clic y desplazamiento
- Solo botón izquierdo. Las coordenadas están indexadas desde 1;
(1, 1)es la celda superior izquierda. previewestá activado por defecto. Una vista previa devuelve una captura de pantalla con un anillo dibujado alrededor de la celda objetivo y no envía ningún clic. Repite la llamada conpreview: falsepara hacer clic. Los programas de pantalla completa a menudo tienen acciones destructivas a un clic de distancia, por lo que vale la pena el paso adicional.terminal_scrollgira la rueda sobre una celda. Los programas que rastrean el mouse reciben eventos de rueda allí, por lo que el panel bajo el puntero se desplaza; los programas de pantalla completa que no lo hacen (less,man) reciben teclas de flecha, como en un terminal normal. En un prompt de shell no hay nada que desplazar: lee la salida anterior conterminal_readypage.- Un clic real necesita que el programa haya activado el informe de mouse —
vimconset mouse=a,fzf,lazygit,htopy la mayoría de las TUI modernas lo hacen. En un prompt de shell simple, la llamada devuelve un error en lugar de imprimir códigos de escape en tu línea de comandos.
Ciclo de vida de la sesión
- Las sesiones son independientes. Las llamadas a una sesión se ejecutan en orden; las llamadas a diferentes sesiones no se bloquean entre sí.
- Si el shell de una sesión de shell sale, la sesión permanece inactiva durante seis horas, o se alcanza el límite de 50 sesiones, la sesión se cierra pero su id permanece reservado durante 30 días. La siguiente llamada a ese id inicia un shell nuevo con el mismo tamaño, shell, directorio de trabajo y tema, y devuelve un aviso que indica lo que sucedió, incluida la última pantalla que imprimió el shell anterior, si salió por sí solo. El comando en esa llamada no se ejecuta; envíalo de nuevo si aún lo quieres.
- Inactivo significa sin llamadas de herramientas y sin salida. Un servidor de desarrollo que aún está imprimiendo no está inactivo.
terminal_destroytermina una sesión definitivamente; su id no se reserva.- Cuando el cliente MCP se desconecta o el servidor se detiene, cada shell se cierra y cada socket de conexión se elimina.
Soporte de protocolo
Construido sobre el SDK oficial de MCP TypeScript (v2). A través de stdio, habla tanto la revisión 2026-07-28 del protocolo, que es sin estado, como las revisiones anteriores basadas en handshake; el primer mensaje del cliente decide cuál.
Sin estado se refiere al protocolo, no a los terminales: las sesiones viven en el proceso del servidor y se abordan mediante el sessionId que pasas en cada llamada. El servidor también proporciona instrucciones de uso, títulos y sugerencias de comportamiento para cada herramienta, cancelación, actualizaciones de progreso desde terminal_wait y la extensión de Skills descrita anteriormente.
Soporte de plataforma
| macOS (arm64, x64) | Compatible; probado en CI |
| Linux (arm64, x64) | Compatible; probado en CI en Node 20, 22 y 24 |
| Windows (x64) | Compatible; probado en CI contra PowerShell y cmd.exe |
En Windows, las sesiones se ejecutan a través de ConPTY, el shell predeterminado es Windows PowerShell, y dos cosas difieren: terminal_wait no puede detectar que un comando de shell ha terminado (ver Esperando cosas), y login no hace nada.
Las dependencias nativas (node-pty, @napi-rs/canvas) incluyen binarios precompilados, por lo que no se necesita compilador para instalar.
Desarrollo
git clone https://github.com/computer-agent-labs/terminal-use.git
cd terminal-use
yarn install
yarn build # compile src/ to dist/
yarn test # build, then run the full suite
Otros scripts:
yarn dev # run the server straight from source
yarn lint # eslint
yarn typecheck # tsc, no output
yarn test:unit # unit tests only
yarn test:windows # what CI runs on Windows: shell-independent tests + tests/windows
yarn smoke # quick end-to-end check without an MCP client
yarn demo # regenerate docs/demo.gif (needs ffmpeg, vim and python3)
Para apuntar tu cliente MCP a un checkout local, compílalo y usa la ruta al script bin:
claude mcp add terminal-use --scope user -- node "$PWD/bin/terminal-use.js"
El código está organizado por capas: src/pty (spawning, codificación de teclas y mouse), src/emulator (el buffer xterm, renderizado, espera), src/session (un terminal), src/attach (el socket al que te conectas) y src/mcp (las herramientas). La habilidad del agente está en skills/.
Seguridad
terminal-use le da al cliente conectado un shell en tu máquina, ejecutándose como tú, sin sandbox. Conéctalo solo a clientes en los que confiarías con un terminal, y usa la configuración de aprobación de herramientas de tu cliente para controlar qué se ejecuta sin aviso. No abre puertos de red; los sockets de conexión están restringidos a tu propio usuario. Consulta SECURITY.md para obtener detalles y saber cómo informar una vulnerabilidad.
Contribuciones
Las issues y pull requests son bienvenidas. CONTRIBUTING.md cubre la configuración, la ejecución de las pruebas y cómo se ve una buena pull request. Este proyecto sigue el Contributor Covenant.
Licencia
MIT. Las fuentes JetBrains Mono incluidas están licenciadas bajo la SIL Open Font License.