iterm-mcp
Provides tools for controlling iTerm2 tabs, including listing, peeking contents, dispatching text, focusing, and sending keystrokes.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@iterm-mcplist my iTerm tabs"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
iterm-mcp
An MCP server that exposes iTerm2 tab control as native tools — designed for the conductor pattern where one Claude/Codex session drives sibling tabs.
Tools
Tool | What it does |
| Sanity check that the server is reachable |
| Enumerate iTerm tabs with runtime detection (claude / codex / ssh / shell) and resume-UUID extraction |
| Read tab contents, optionally tail-limited to last N lines |
| Write text into a tab with 3-tier submit escalation (CR+LF → keystroke → file-drop) |
| Bring a tab to the foreground |
| Send a raw |
Three-tier dispatch escalation
tabs_dispatch is the workhorse. Submitting text reliably across local-shell, claude, codex, and SSH-into-remote tabs needs different mechanisms, so the tool escalates:
Tier 1 —
crlf: AppleScriptwrite text+ CR + LF. Fast and quiet; works for short text into local claude tabs.Tier 2 —
keystroke: Focus the tab, then have System Events deliverkeystroke return. Required for codex tabs and remote ssh sessions where Tier 1 unreliably submits.Tier 3 —
fallback: Write the intended dispatch to~/.claude/plans/pending-dispatches/<ts>-w<W>t<T>.md. Operator-recoverable.
escalation: "auto" (default) tries Tier 1, falls through to Tier 2 on failure, drops to Tier 3 if both fail. You can also force a tier explicitly: crlf, keystroke, or fallback.
Refuse-self
tabs_dispatch reads ~/.claude/plans/inter-agent-sync/conductor-active.txt and refuses dispatch when:
target window/tab matches the conductor's own tab, or
target tab's resume UUID is in
also_refuse_self_for_resume_uuids, ortarget tab's resume UUID equals the conductor's
session_id.
This prevents the conductor from ever dispatching into itself (which would cause a feedback loop).
Screenshot
(placeholder — drop a screenshot of
tabs_listoutput in a claude session here)
Install
git clone https://github.com/CondorCommodore/iterm-mcp.git
cd iterm-mcp
npm install
npm run build
claude mcp add iterm-mcp -s user -- node $(pwd)/dist/server.jsOn first dispatch, macOS will prompt to grant Accessibility permission to your terminal / Claude Code app so that System Events can deliver keystrokes. Grant it in System Settings → Privacy & Security → Accessibility.
Smoke test
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"smoke","version":"0"}}}' | node dist/server.jsYou should see a JSON-RPC initialize result on stdout and [iterm-mcp] server ready on stderr.
Test
npm test # vitest run tests/unit
npm run test:watch # vitest watchUnit tests mock osascript / ps invocation and verify both the AppleScript shape we send and our result parsing.
Troubleshooting
error TS2589: Type instantiation is excessively deep and possibly infinite
The MCP SDK's McpServer.registerTool uses extremely deep generic inference over Zod schemas. With zod ^3.25 and @modelcontextprotocol/sdk ^1.29 together, TypeScript hits its instantiation-depth ceiling and refuses to compile.
We work around it in src/server.ts with a thin wrapper that erases the SDK's generics:
const reg: (name: string, config: any, cb: any) => void = (n, c, h) => {
(server as any).registerTool(n, c, h);
};Runtime behavior is identical — only the compile-time type check is short-circuited. When the SDK ships a less-recursive overload, the wrapper can be retired.
osascript: command not found
iterm-mcp only runs on macOS. AppleScript and System Events are macOS-only.
Dispatch silently no-ops on a codex or ssh tab
That's Tier 1 (crlf) being unreliable on those runtimes. Either let escalation: "auto" fall through to Tier 2, or pin escalation: "keystroke" directly.
"Not authorized to send keystrokes"
System Events needs Accessibility permission for the parent process that spawns osascript (your terminal, or Claude Code itself). Grant it under System Settings → Privacy & Security → Accessibility.
Refuse-self firing on the wrong tab
Check that ~/.claude/plans/inter-agent-sync/conductor-active.txt reflects the conductor's actual window/tab. The file is key=value lines: conductor_window, conductor_tab, session_id, also_refuse_self_for_resume_uuids (comma-separated).
License
MIT
This server cannot be installed
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/CondorCommodore/iterm-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server