使用说明
PTY-MCP:AI代理的交互式终端控制
“终端剧作家”-让AI代理完全控制交互式终端会话。
为什么存在
定期执行shell(subprocess, child_process)无法处理:
- 交互式提示(凭证、Y/N确认、,
Read-Host) - 需要Ctrl+C停止的程序
- 维护状态的REPL(Python、Node、PowerShell会话)
- 全屏TUI应用程序
- 命令如下
Enter-PSSession启动交互式子会话
此MCP服务器为代理提供 眼睛 (屏幕缓冲区)和 手 (键盘输入)像人类一样使用终端。
工具
核心工具
| 工具 | 说明 |
|---|---|
spawn_session |
启动新终端(pwsh、cmd、bash) |
send_keys |
键入文本或按特殊键 |
send_line |
新 发送文本+Enter(最常见的操作) |
get_snapshot |
读取当前屏幕缓冲区 |
get_buffer_range |
新 读取缓冲区行(包括回滚) |
get_buffer_info |
新 读取缓冲区元数据(长度、视口、光标) |
get_cursor |
新 获取光标位置(x,y) |
resize |
更改端子尺寸 |
list_sessions |
列出活动会话 |
close_session |
终止会话 |
等待工具(用于同步)
| 工具 | 说明 |
|---|---|
wait_for_content |
新 等待特定文本/图案出现 |
wait_for_idle |
新 等待屏幕停止更改 |
wait_for_buffer_lines |
新 等待缓冲区行数增加 |
正确使用
⚠️ 重要提示:使用“Enter”键,而不是“\n”
这是一个 真实终端,不是命令执行器。你必须像人类一样与它互动:
❌ WRONG - sends literal newline characters, triggers continuation prompt
send_keys({ keys: "pwd\nGet-Date\n" })
✅ CORRECT - type command, press Enter, wait, read output
send_keys({ keys: "pwd" })
send_keys({ keys: "Enter" })
get_snapshot() // read the output
send_keys({ keys: "Get-Date" })
send_keys({ keys: "Enter" })
get_snapshot() // read the output
基本工作流程(推荐)
1. spawn_session({ command: "pwsh.exe", args: ["-NoProfile", "-NoLogo"] })
→ Returns: { session_id: "abc-123", pid: 12345 }
2. wait_for_content({ session_id: "abc-123", pattern: "PS .*>", is_regex: true })
→ Waits for prompt, returns screen content
3. send_line({ session_id: "abc-123", text: "Get-Date" })
→ Types command + presses Enter (one call!)
4. wait_for_content({ session_id: "abc-123", pattern: "PS .*>", is_regex: true })
→ Waits for next prompt (command finished)
→ Returns screen with output
5. close_session({ session_id: "abc-123" })
→ Clean up when done
替代方案:手动控制
1. spawn_session({ command: "pwsh.exe", args: ["-NoProfile", "-NoLogo"] })
2. get_snapshot({ session_id: "abc-123" })
→ See: "PS C:\Users\jou>" (prompt is ready)
3. send_keys({ session_id: "abc-123", keys: "Get-Date" })
→ Types the command
4. send_keys({ session_id: "abc-123", keys: "Enter" })
→ Executes the command
5. wait_for_idle({ session_id: "abc-123" })
→ Wait for output to finish
6. get_snapshot({ session_id: "abc-123" })
→ See the output
特殊钥匙
使用这些键名 send_keys:
| 密钥 | 名称 |
|---|---|
| 进入 | "Enter" |
| 选项卡 | "Tab" |
| 逃跑 | "Escape" |
| 后台空间 | "Backspace" |
| 删除 | "Delete" |
| 箭头键 | "Up", "Down", "Left", "Right" |
| 首页/尾页 | "Home", "End" |
| 向上/向下翻页 | "PageUp", "PageDown" |
| 功能键 | "F1" 通过 "F12" |
关键点修改器(Ctrl、Alt、Shift)
// Ctrl+C to interrupt
send_keys({ keys: "c", modifiers: { ctrl: true } })
// Ctrl+D to send EOF
send_keys({ keys: "d", modifiers: { ctrl: true } })
// Ctrl+L to clear screen
send_keys({ keys: "l", modifiers: { ctrl: true } })
// Ctrl+Right to move word forward
send_keys({ keys: "Right", modifiers: { ctrl: true } })
// Ctrl+Shift+Right to select word
send_keys({ keys: "Right", modifiers: { ctrl: true, shift: true } })
例子
运行命令并获取输出
spawn_session({ command: "pwsh.exe", args: ["-NoProfile", "-NoLogo"] })
// Wait for prompt...
get_snapshot() // Should see "PS ...>"
send_keys({ keys: "Get-Process | Select -First 3" })
send_keys({ keys: "Enter" })
// Wait for output...
get_snapshot() // See the process list
close_session({ session_id: "..." })
处理交互式提示(读取主机)
send_keys({ keys: "$name = Read-Host 'Enter your name'" })
send_keys({ keys: "Enter" })
get_snapshot() // See: "Enter your name: "
send_keys({ keys: "Alice" })
send_keys({ keys: "Enter" })
get_snapshot() // Back to prompt
send_keys({ keys: "Write-Host \"Hello, $name\"" })
send_keys({ keys: "Enter" })
get_snapshot() // See: "Hello, Alice"
中断长时间运行的命令
send_keys({ keys: "Start-Sleep -Seconds 300" })
send_keys({ keys: "Enter" })
// Oops, that's too long...
send_keys({ keys: "c", modifiers: { ctrl: true } }) // Ctrl+C
get_snapshot() // Back to prompt
浏览命令历史记录
send_keys({ keys: "Get-Date" })
send_keys({ keys: "Enter" })
// ... do other things ...
send_keys({ keys: "Up" }) // Previous command
get_snapshot() // See "Get-Date" on command line
send_keys({ keys: "Enter" }) // Run it again
使用等待工具(推荐)
// Wait for PowerShell prompt
wait_for_content({ session_id, pattern: "PS .*>", is_regex: true })
// Wait for specific output
send_line({ session_id, text: "Get-Process explorer" })
wait_for_content({ session_id, pattern: "explorer" })
// Wait for command to finish (screen stops changing)
send_line({ session_id, text: "Get-Process | Sort-Object CPU -Descending" })
wait_for_idle({ session_id, timeout: 10000 })
get_snapshot({ session_id })
// Wait with timeout
wait_for_content({
session_id,
pattern: "Password:",
timeout: 30000 // 30 seconds
})
调试TUI(vim、htop等)
// Get raw ANSI escape sequences for debugging
get_snapshot({ session_id, include_ansi: true })
→ Returns: { content: "...", rawAnsi: "\x1b[1;1H\x1b[2J..." }
读取回溯(长输出)
send_line({ session_id, text: "Get-Content big.log" })
wait_for_idle({ session_id, timeout: 10000 })
// Read last 200 lines from buffer
get_buffer_range({ session_id, start: -200, count: 200 })
确定性读取(缓冲元数据)
const info = get_buffer_info({ session_id })
// info.total is the buffer length (including scrollback)
get_buffer_range({ session_id, start: info.total - 200, count: 200 })
等待长时间输出(行数)
send_line({ session_id, text: "1..500 | ForEach-Object { \"Line $_\" }" })
wait_for_buffer_lines({ session_id, min_delta: 500, timeout: 10000 })
get_buffer_range({ session_id, start: -50, count: 50, exclude_pattern: "PS ", is_regex: false })
快照格式
get_snapshot 返回:
{
"content": " 1 | PS C:\\Users\\jou> Get-Date\n 2 | Tuesday, January 28, 2025\n 3 | \n...",
"cursor": { "x": 20, "y": 2 },
"rows": 24,
"cols": 80
}
content:带有行号的屏幕缓冲区(如cat -n)cursor:当前光标位置rows/cols:端子尺寸
AI代理提示
- 始终等待提示 在发送下一个命令之前
- 一次一个命令 -键入、输入、等待、读取
- 使用
get_snapshot大量地 -你的眼睛盯着航站楼 - 使用Ctrl+C 如果某物挂起或进入意外状态
- 检查是否有提示 喜欢
[Y/N],Password:,>>(续) - 结束会议 当释放资源时
安装
npm install
npm run build
添加到MCP配置中:
# Codex CLI
codex mcp add pty-mcp -- node /path/to/dist/index.js
# Claude Code
claude mcp add pty-mcp -- node /path/to/dist/index.js
发展
npm test # Run all tests (116 tests)
npm run typecheck # Type checking
npm run lint # Linting
npm run build # Build for production
