<p align="center"> <img src="docs/forge-demo.gif" alt="Forge Demo" width="800" /> </p> <p align="center"> <img src="docs/screenshot-terminal.png" alt="Terminal View with Changes Panel" width="800" /> </p> <p align="center"> <img src="docs/screenshot-chats.png" alt="Chat Browser" width="800" /> </p>
Why
AI coding agents (Claude Code, Codex, etc.) typically run one command at a time. Forge gives them persistent terminals — run your React frontend, Java API, and Postgres migrations in parallel, monitor all three, and only read what changed. Full-stack work without the bottleneck.
Works with any MCP-compatible client — Claude Code, Codex, Gemini CLI, or your own agent.
Key differentiators:
- Real PTY via
node-pty(same lib as VS Code terminal) — interactive programs, colors, TUI apps all work - Incremental reads — ring buffer with per-consumer cursors means each
read_terminalonly returns NEW output, saving context window tokens - Clean screen reads —
@xterm/headlessrenders the terminal server-side, soread_screenreturns exactly what a human would see (no ANSI escape codes) - Multi-agent orchestration — spawn Claude, Codex, and Gemini sub-agents, session groups, output multiplexing, event subscriptions, and templates for managing multiple concurrent sessions
- Web dashboard — real-time Preact-based browser UI to watch what your agents are doing across all terminals, browse past chat sessions, and monitor activity
- Zero config — single
npxcommand or HTTP MCP endpoint
Install
# bun (recommended)
bun install -g forge-terminal-mcp
# npm (requires Node.js ≥ 18)
npm install -g forge-terminal-mcp
# Or standalone binary (no Node.js required)
curl -fsSL https://forgemcp.dev/install.sh | shAfter install, the forge command is available globally:
forge start # Start the server
forge start -d # Start as background daemon
forge start -d --dashboard --port 3141 # With web dashboardUpdate
# bun
bun update -g forge-terminal-mcp
# npm
npm update -g forge-terminal-mcp
# Standalone binary — re-run the install script
curl -fsSL https://forgemcp.dev/install.sh | sh
# Desktop app updates automatically on restartQuick Start
1. Add to Your Agent
<details open> <summary><strong>Claude Code</strong></summary># Recommended: auto-starts daemon and registers HTTP transport
forge setup --agent claude-code
# Or manually:
forge start -d # Start daemon in background
claude mcp add --transport http forge http://127.0.0.1:3141/mcp</details> <details> <summary><strong>Codex</strong></summary>Note: Always use HTTP transport so Claude Code connects to the running daemon. This ensures sessions appear in the dashboard and avoids conflicts from isolated processes.
# Add Forge as HTTP MCP server
codex mcp add forge --url http://127.0.0.1:3141/mcp
# Verify
codex mcp list
codex mcp get forgeCodex stores this in ~/.codex/config.toml:
[mcp_servers.forge]
url = "http://127.0.0.1:3141/mcp"# Start Forge daemon first
npx forge-terminal-mcp start -d --dashboard --port 3141
# Add Forge as HTTP MCP server
gemini mcp add --transport http forge http://127.0.0.1:3141/mcpStart the daemon (choose one launch mode), then point any MCP client at the HTTP endpoint:
# If forge is on PATH (global install or npm link)
forge start -d
# From this repo (local clone)
node dist/cli.js start -d
# Without install (published package)
npx forge-terminal-mcp start -d{
"mcpServers": {
"forge": {
"type": "http",
"url": "http://127.0.0.1:3141/mcp"
}
}
}Restart your agent and Forge tools are available.
Important: Claude Code, Codex, and Gemini CLI load MCP servers at process start. If you add/remove servers, restart the current agent session.
2. Smoke Test (60s)
# Codex MCP registration
codex mcp list
codex mcp get forge
# Forge daemon status
node dist/cli.js statusExpected:
codex mcp listshowsforgeas enablednode dist/cli.js statusreports running andhttp://127.0.0.1:3141
Troubleshooting
| Symptom | Fix |
|---|---|
forge: command not found | Use node dist/cli.js ... from the repo root, or npx forge-terminal-mcp .... |
No MCP servers configured yet in Codex | Run codex mcp add forge --url http://127.0.0.1:3141/mcp, then restart Codex. |
listen EPERM ... 127.0.0.1:3141 | Run Forge in an environment that allows local port binding, or use a different port with --port and update the MCP URL to match. |
| A message appears typed but agent does not answer | The input may be queued; press Enter (or use submit=true when writing programmatically). |
| MCP server is configured but tools do not appear | Restart the current agent session so MCP servers reload. |
3. Use It
Your agent now has access to 23 tools across 7 categories:
Session Lifecycle
create_terminal → Spawn a PTY session with optional name, tags, buffer size
create_from_template → Spawn from a built-in template (shell, next-dev, vite-dev, etc.)
spawn_claude → Launch a Claude Code sub-agent in a dedicated session
spawn_codex → Launch a Codex sub-agent in a dedicated session
spawn_gemini → Launch a Gemini CLI sub-agent in a dedicated session
close_terminal → Kill a session and free resources
close_group → Close all sessions matching a tag
list_terminals → List sessions, optionally filtered by tag
list_templates → Show available session templatesI/O
write_terminal → Send input (appends newline by default)
read_terminal → Read NEW output since last read (incremental)
read_screen → Get rendered viewport as clean text (no ANSI)
read_multiple → Batch read from up to 20 sessions at once
send_control → Send Ctrl+C, arrow keys, Tab, Enter, etc.
resize_terminal → Change terminal dimensionsSearch & Wait
grep_terminal → Regex search across a session's output buffer
wait_for → Block until output matches a pattern or process exitsExecution
run_command → Run a command to completion, return output, auto-cleanupEvents
subscribe_events → Get notified when a session exits or matches a pattern
unsubscribe_events → Cancel an event subscriptionAgent Delegation
delegate_task → Delegate a task to another agent — oneshot or interactive multi-turnOps
health_check → Server version, uptime, session count, memory usage
get_session_history → Tool call history for agent sessions
clear_history → Clear persisted stale session entriesExample Conversations
You: Start a Next.js dev server and run the test suite in parallel
Agent: (uses
create_from_templatewith "next-dev",wait_for"Ready", then creates a second session fornpm test, usesread_multipleto poll both)
You: Spin up 3 sub-agents to research different parts of the codebase
Agent: (uses
spawn_claudethree times with tag "research", monitors withlist_terminalsfiltered by tag, cleans up withclose_group)
You: Build and test, just give me the result
Agent: (uses
run_commandwithnpm run build && npm test— creates terminal, waits for exit, returns output, auto-cleans up)
Best Practices
run_command vs create_terminal
Use run_command when you want a result and don't need the session afterwards:
- Build steps (
npm run build,cargo build) - Test runs (
npm test,pytest) - Install commands (
npm install,pip install) - One-off scripts that exit cleanly
Use create_terminal when you need an ongoing session:
- Dev servers (
npm run dev,vite,next dev) - Watchers (
npm run watch,tsc --watch) - REPLs or interactive processes
- Long-running processes you'll poll with
read_terminal
# Good — build is a one-shot task
run_command({ command: "npm run build && npm test" })
# Good — dev server needs to stay alive
create_terminal({ command: "npm run dev", name: "dev-server" })
wait_for({ id, pattern: "ready on" })waitForExit vs pattern matching
Use pattern matching (default) when the process stays alive after printing the signal:
wait_for({ id, pattern: "Server running on port 3000" })
# returns as soon as the line appears — process keeps runningUse waitForExit: true when the process exits naturally and you want all output:
wait_for({ id, pattern: ".", waitForExit: true })
# waits for the process to finish, returns everythingfromSession for sub-agents
When spawning a sub-agent to work on the same project, use fromSession instead of hardcoding paths:
# Instead of this (brittle):
spawn_claude({ prompt: "...", cwd: "/Users/me/projects/my-app" })
# Do this (inherits cwd from current session):
spawn_claude({ prompt: "...", fromSession: currentSessionId })This ensures the sub-agent works in the correct directory even when Forge is used across different machines or worktrees.
Worktrees for parallel agents
When running multiple agents on the same codebase, use worktree: true to isolate changes:
spawn_claude({ prompt: "Add auth", worktree: true, branch: "feature/auth" })
spawn_claude({ prompt: "Add payments", worktree: true, branch: "feature/payments" })
# Both agents work in parallel without stepping on each otherTools Reference
create_terminal
| Parameter | Type | Default | Description |
|---|---|---|---|
command | string | User's $SHELL | Command to run |
args | string[] | [] | Command arguments |
cwd | string | Process cwd | Working directory |
env | object | {} | Additional env vars (merged with process env) |
cols | number | 120 | Terminal width |
rows | number | 24 | Terminal height |
name | string | — | Human-readable session name |
tags | string[] | — | Tags for filtering/grouping (max 10) |
bufferSize | number | Server default | Ring buffer size in bytes (1 KB – 10 MB) |
Returns session info including the id used by all other tools.
create_from_template
| Parameter | Type | Default | Description |
|---|---|---|---|
template | string | required | Template name (see list_templates) |
cwd | string | — | Working directory override |
env | object | — | Additional env vars |
name | string | Template name | Session name override |
Built-in templates:
| Template | Command | Tags | Wait For |
|---|---|---|---|
shell | $SHELL | shell | — |
…