terminal-agent

terminal-agent

Local daemon that bridges AI assistants to the local filesystem with read-only commands and optional write mode, enforcing strict security filters to prevent credential leakage.

Category
访问服务器

README

terminal-agent

Local daemon for the terminal MCP bridge. It connects outbound over a WebSocket to wss://mcp.lishuyu.app/mcp/terminal/ws, receives tool calls that originate in claude.ai (or chatgpt.com), runs them against the local file system, and returns results — after sanitizing every byte so that no token or credential can ever reach chat history.

claude.ai ──(MCP, OAuth)──▶ mcp.lishuyu.app/mcp/terminal
                                   │  TerminalBridge DO
                                   │  WebSocket (this agent dials in, PSK auth)
                                   ▼
                            terminal-agent ──▶ local files
                                   │ ┌────────────────┐
                                   │ │ secret filter  │ ← every output, before it leaves
                                   │ └────────────────┘

The hard rule

Defense in depth, every layer on the machine, before any byte leaves:

  1. Read containment — read_file/search_files/list_directory are confined to allowed_read_dirs (default ~/Codes), checked after resolving symlinks. Files outside the allowlist (/etc, ~/.config, ~/.ssh, another project) are simply unreadable. This is the primary boundary.
  2. Path blocklist — within the allowed roots, credential files (.env*, .ssh/**, *.pem, *.key, *secret*, *credential*, .aws/**, .kube/**, .pgpass, …) are still refused (case-insensitive, symlink-resolved). Always on, not user-disableable.
  3. sanitize() — content regexes for sk-…, ghp_…, JWTs, PEM blocks, 64+ hex, password: …, URL credentials, etc. + your secret_literals, applied to all tool output and error messages. Over-redaction (a git SHA gets [REDACTED]) is preferred to any leak.
  4. Write confinement — writes are confined to allowed_write_dirs, realpath-checked, with symlinked leaves/dirs rejected.
  5. No shell + a tight read-only command whitelist (see below).

Tools

Read-only (default mode):

tool what
terminal_status is a machine connected, and in which mode
search_files(pattern, path?, glob?) ripgrep, file:line:match, ≤50 matches
read_file(path, line_start?, line_end?) line-numbered; head+tail for big files; ≤200-line ranges
list_directory(path?, max_depth?) tree, excludes node_modules/.git/…
run_command(command) single read-only command, no shell (no pipes/redirection/substitution). Whitelist is metadata-only: git metadata subcommands (log/status/rev-parse/ls-files/…, with patch flags -p/-u/-L/--patch and write/exec flags rejected), plus ls, stat, wc, du, df, ps, uptime, uname, … Binaries that read file contents (cat/grep/head/file/git show/diff/blame/cat-file/git log -p) or spawn processes / write files (find/rg/tree) are excluded — use read_file/search_files. Every path arg is blocklist-checked and confined to allowed_read_dirs.

Read-write / bypass (opt-in via switch_mode):

tool what
write_file(path, content, mode?) confined to allowed_write_dirs; for handing off a HANDOFF.md/spec to local Claude Code
switch_mode(mode, confirmation_code) enter read-write/bypass; needs the 6-digit code printed in THIS terminal at startup

run_command deliberately does not whitelist python -c / node -e / perl -e: those are arbitrary code execution and would defeat read-only. Opt in per-binary via extra_read_binaries, or use bypass mode, only if you accept the risk. Even in bypass mode the agent runs shell-free and rejects pipes/redirection/$()/backticks — for a real pipeline, use a real terminal.

switch_mode is the anti-prompt-injection gate

The agent generates a random 6-digit code on startup. Entering read-write or bypass is a two-step gate:

  1. Request — the cloud calls switch_mode(mode) without a code. The agent fires a macOS notification (notify_on_switch: true) carrying the code to your Mac, and tells the cloud "ask the operator for the code."
  2. Confirm — you read the code off the notification and relay it; the cloud calls switch_mode(mode, confirmation_code) to apply.

A prompt-injected assistant cannot see your Mac's notifications (or your terminal), so it cannot self-escalate — you hand it the code only when you want to enable writes. The code rotates on every restart. (Off macOS, or with notify_on_switch: false, the code is read from the startup banner/log.)

Elevation is temporary. read-write/bypass auto-reverts to read-only after elevation_timeout_ms (default 10 min; each switch resets the clock), and you get a notification when it does. A forgotten elevation can't stay open — and a launchd restart also resets to the configured mode: (read-only). Set elevation_timeout_ms: 0 to keep it manual.

Setup

Requires Bun (recommended) or Node ≥ 20.

cd ~/Codes/terminal-agent
bun install
cp config.example.yaml config.yaml      # edit machine/cwd/allowed_write_dirs

# The pre-shared token must equal the Worker's TERMINAL_TOKEN secret.
export TERMINAL_AGENT_TOKEN='…'         # put in ~/.zshrc or the launchd plist
bun run src/index.ts

On a successful connect you'll see [ws] connected … registered. In claude.ai, add the connector https://mcp.lishuyu.app/mcp/terminal, then call terminal_status to confirm the machine is online.

config.yaml

See config.example.yaml. Key fields: server, token (${ENV} is substituted), machine, mode, default_cwd, max_output_bytes, command_timeout_ms, allowed_write_dirs, blocked_paths (extra globs, added to the hard floor), secret_literals, extra_read_binaries.

Run at login (launchd)

Copy com.lishuyu.terminal-agent.plist.example to ~/Library/LaunchAgents/com.lishuyu.terminal-agent.plist, fill in the absolute paths, your token, and the Bun binary path, then:

launchctl load ~/Library/LaunchAgents/com.lishuyu.terminal-agent.plist
launchctl start com.lishuyu.terminal-agent
# logs → the StandardOut/StandardError paths in the plist

The confirmation code is in the agent's StandardOut log; grep it there when you need to switch_mode.

Tests

bun test          # secret-filter + command-whitelist unit tests
bun run type-check

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选