코딩 에이전트용 MCP
text mcp: 코딩 에이전트가 Model Context Protocol로 탐색하고 키를 누르며 프레임을 읽게 하세요.
text mcp는 app/ 디렉터리를 프로세스 내에서 헤드리스로 실행하고 모델 컨텍스트 프로토콜로 제공해 코딩 에이전트가 DevTools 없이 사용할 수 있게 해요. 에이전트는 탐색하고, 키를 누르고, 프레임을 일반 텍스트로 읽고, 라우트 핸들러를 호출해요.
bunx --bun text mcp # serve ./app over stdiobunx --bun text mcp apps/console # another projecttext mcp에는 Bun이 필요해요. Node로 시작하면 PATH에 bun이 있을 때 Bun으로 다시 실행하고, 없으면 TX019로 실패해요. MCP 클라이언트가 대신 시작하므로 직접 실행할 일은 거의 없어요.
설정
각 클라이언트는 프로젝트 디렉터리에서 text mcp를 시작해요. 라우트 파일이 TypeScript로 실행되도록 bunx --bun을 사용하세요.
OpenCode
{ "$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또는 팀이 사용할 수 있도록 커밋하세요:
{ "mcpServers": { "textjs": { "command": "bunx", "args": ["--bun", "text", "mcp"] } }}Cursor
{ "mcpServers": { "textjs": { "command": "bunx", "args": ["--bun", "text", "mcp"] } }}VS Code
{ "servers": { "textjs": { "type": "stdio", "command": "bunx", "args": ["--bun", "text", "mcp"] } }}하위 디렉터리에 있는 프로젝트라면 경로를 덧붙이세요: ["--bun", "text", "mcp", "apps/console"].
도구
| 도구 | 설명 |
|---|---|
list_routes | app/의 페이지와 라우트 핸들러, 현재 위치 |
navigate | 경로 추가 또는 교체, back / forward 이동 |
read_frame | 라우트, 크기, 포커스, 커서가 포함된 텍스트 프레임. styles: true로 스팬별 색상과 속성 추가 |
press | 키 조합: "down", "ctrl+k", "shift+tab", "tab tab enter", "down*3" |
type | 문자마다 키 하나로 텍스트 입력(최대 2,000자, 초과 시 paste 사용). submit: true로 입력 후 Enter 누르기 |
paste | ⌘V와 같은 붙여넣기 이벤트 1회 |
scroll | rows(및 cols)만큼 마우스 휠 스크롤. <ScrollBox>는 키보드 포커스 없이 휠로 스크롤 가능 |
resize | 열과 행으로 그리드 크기 설정 |
inspect_tree | 화면 경계와 포커스 가능한 모든 id가 포함된 컴포넌트 트리 |
find | 각 일치 항목의 경계와 해당 위치의 컴포넌트 |
wait_for | 텍스트가 나타나거나 사라질 때까지, 또는 앱이 유휴 상태가 될 때까지 대기 |
logs | 캡처된 console.* 출력과 React 오류 |
fetch | 프로세스 내에서 route.ts 핸들러 호출. 본문은 maxBytes까지, 이벤트 스트림은 streamMs 동안 읽은 뒤 취소 |
reload | 파일 수정 후 앱 다시 가져오기 |
입력 도구는 새 프레임을 반환하므로 에이전트가 키를 누를 때마다 별도로 read_frame을 호출할 필요가 없어요. 현재 프레임과 라우트 목록은 text://frame과 text://routes 리소스로도 제공해요.
일반적인 요청과 응답:
→ 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 …프레임과 유휴 상태
모든 도구는 프레임을 읽기 전에 앱이 유휴 상태가 될 때까지 기다려요. 예약된 프레임과 진행 중인 fetch가 없고, 50ms 동안 커밋이나 프레임이 없는 상태예요. 따라서 에이전트는 업데이트 도중의 프레임 대신 입력 결과를 보게 돼요.
<Spinner>나 1초마다 갱신되는 차트도 갱신 사이에는 유휴 상태로 간주하므로 애니메이션이 있는 페이지도 읽을 수 있어요. 50ms보다 짧은 간격으로 렌더링하는 앱은 유휴 상태가 되지 않아요. 읽기는 1.5초 후 대기를 포기하고(settleMs로 변경 가능), 최신 프레임을 내보낸 뒤 busy로 표시해요. 텍스트 없이 호출한 wait_for는 자체 quietMs를 기준으로 유휴 상태를 기다리고, 유휴 상태가 되지 않으면 오류를 반환해요.
좌표는 0부터 시작하는 셀 단위예요. x는 열, y는 행이에요. CJK 문자와 이모지처럼 폭이 넓은 문자는 2개 열을 차지해요.
오류와 로그
stdout은 프로토콜을 전달해요. 앱의 console.* 출력, 의도치 않은 process.stdout.write 호출, React 오류는 대신 logs로 전달되며, 에이전트가 since로 새 항목만 요청할 수 있도록 번호가 붙어요. 마지막 읽기 이후 오류가 기록되면 다음 프레임 헤더에 표시돼요.
실패는 도구 오류로 반환되며 프로세스가 중단되지는 않아요. 시간이 초과된 도구는 다음 호출이 실행되기 전에 취소되므로 느린 호출이 다음 호출의 프레임에 입력하는 일은 없어요. Text.js 코드가 있는 오류는 해당 코드를 유지해요. 예를 들어 알 수 없는 키에는 TX007이 붙어요. 앱을 불러오지 못하면 모든 도구가 이유를 설명해요. 파일을 수정하고 reload를 호출하세요.
옵션
| 옵션 | 설명 |
|---|---|
--path <pathname> | 첫 라우트(기본값 /) |
--cols / --rows | 그리드 크기(기본값: text.config.ts의 viewport, 없으면 100×32) |
--theme <name> | 테마 프리셋 |
--no-config | text.config.ts 무시 |
--verbose | 캡처된 로그를 stderr에도 기록 |
작동 방식
text mcp는 text run과 같은 방식으로 앱을 실행해요. 모든 라우트 모듈을 미리 가져오고, App Router는 메모리 히스토리를 사용하며, 동일 출처의 fetch("/api/…") 호출은 프로세스 내에서 핸들러를 실행해요. 프레임은 터미널 대신 화면 밖의 셀 그리드로 전달돼요. 서버에서 렌더링된 페이지(export const runtime = "server")와 해당 페이지의 async function 컴포넌트는 text dev처럼 프로세스 내에서 렌더링되며, 처리가 완료되면 프레임을 읽어요.
reload는 앱을 마운트 해제하고, Bun의 모듈 캐시에서 모든 프로젝트 모듈(app/ 밖의 파일 포함)을 지운 뒤, app/을 다시 스캔하고 현재 위치에서 다시 마운트해요.
자체 프로세스에 서버를 내장하거나 다른 전송 방식을 사용하려면 다음과 같이 하세요:
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();제약 사항
- 앱은 브라우저가 아닌 Bun에서 실행돼요.
text run과 마찬가지로window와document는 정의되어 있지 않아요. - 각 서버는 그리드 1개로 앱 1개를 실행해요. 각 MCP 클라이언트는 자체 서버를 시작해요.
text mcp는 앱의 자체 헤드리스 사본을 실행해요.text dev로 열린 브라우저 탭에는 연결할 수 없어요.