jonghklee

teammate-mcp

Community jonghklee
Updated

Inter-agent Q&A MCP server: let Claude Code and Codex CLI ask each other questions through your iTerm panes.

teammate-mcp

Let Claude Code and OpenAI Codex panes talk to each other —by label, by iTerm session name, or by session id.N agents, M agents, mix-and-match. No daemon. No .config per project.

┌──────────── iTerm window ─────────────┐
│ claude  (left)        codex  (right)  │
│ ───────────────────   ─────────────── │
│ > implement quoter    > [teammate-mcp │
│   I'll ask Codex...     ASK ... what  │
│   ⏺ Codex answered:     is 2+2?]      │
│      4                  • 4           │
└───────────────────────────────────────┘

teammate-mcp is a tiny MCP server that gives every loaded CLI asmall toolbox:

  • ask(target, question, timeout) — primary. target is a label, aniTerm session name, or a session-id prefix.
  • list_panes() — every live pane + its label/name/id/job/cwd.
  • register_self(label) — attach a label to the calling pane at runtime.
  • broadcast(message, targets=[...]) — push to multiple panes at once.
  • ask_codex / ask_claude — legacy 1:1 shortcuts; still work whenexactly one of each CLI is running.

The server uses the iTerm2 Python APIto push the prompt into the target pane and read the reply back via aunique marker (<<DONE_…>>).

Why?

Existing multi-agent harnesses fall into two camps:

  1. Heavyweight: a daemon, per-project config files, opaque sessionstate. Great until something breaks at 2 AM and you can't see why.
  2. Single-process: one model orchestrating sub-agents internally,so the user only sees the final answer.

teammate-mcp aims for a third option: the two agents are visiblyrunning in your terminal next to each other, you can read bothtranscripts in real time, and the only "infrastructure" is a fewhundred lines of Python that pushes text and reads screens.

Verified bidirectional round trip

Captured live during development on macOS 14, iTerm 3.6.8,Claude Code 2.1.119 + Opus 4.7, Codex 0.125.0:

{"event":"ask.enqueue","id":"…c5d085","from_":"claude","to":"codex","len":49}
{"event":"ask.send",   "id":"…c5d085","to":"codex","session_id":"7E39032F-…"}
{"event":"ask.complete","id":"…c5d085","answer_len":3}

The ask.sendask.complete interval was 3.0 seconds for aprompt of "What is two plus two? Answer with the digit only" — the bulkof which is Codex thinking time, not the bridge. Five consecutive runsall closed the loop in 1.5 – 4.5 seconds.

Six independent timing reports captured in tests/results/ areincluded in the repo so you can audit the numbers yourself.

Quick start

1. Install

git clone https://github.com/jonghklee/teammate-mcp.git
cd teammate-mcp
uv venv
uv pip install -e .

2. Register the server with both CLIs

# Claude Code
claude mcp add teammate -s user -- $PWD/.venv/bin/teammate-mcp serve

# Codex
codex  mcp add teammate           -- $PWD/.venv/bin/teammate-mcp serve

3. Open the panes

You have two options:

Option A — let bin/team open a fresh iTerm window for you:

./bin/team

Option B — use any iTerm window you already have open. Just runclaude in one pane and codex in another. teammate-mcp finds themby process name; no labels needed.

4. (One-time) Hand the agents the operating rules

Drop templates/AGENTS.md into your project root. Both Claude Codeand Codex will pick it up automatically (it's the convention theyboth follow). The file tells them how and when to call each other.

5. (Optional) Add per-pane labels for N:M setups

For more than one agent of either type, label each pane beforelaunching its CLI:

# pane 1
export TEAMMATE_LABEL=plan
claude

# pane 2
export TEAMMATE_LABEL=worker
codex

# pane 3
export TEAMMATE_LABEL=tester
codex --yolo

The MCP server auto-registers each pane to its label on startup.Then from any pane:

ask("worker",  "implement foo()")
ask("tester",  "write tests for foo()")
ask("plan",    "review this design")    # from worker, asking back

You can also address a pane by the iTerm session name (cmd+I) or byany prefix of its UUID — ask("Worker A", …) or ask("7B5B0D11", …).

6. (Optional) Show the label in your status bar

./bin/install-statusline

Adds a statusLine block to ~/.claude/settings.json and a precmdhook to ~/.zshrc that updates the iTerm tab title from$TEAMMATE_LABEL. Both Claude (native statusLine) and Codex (tabtitle) show the label visibly. Idempotent; backs up your existingsettings.

7. Try it

In the Claude pane:

Ask the worker pane what timezone library it prefers in Python.

You'll see Claude call ask, the question appear in the workerpane, Codex respond there, and Claude relay the answer.

How it works

┌──────────────────────────────────────────────────────┐
│  Claude pane              Codex pane                  │
│  ─────────────            ─────────────               │
│   user prompt              [teammate-mcp ASK …]       │
│        │ tool call              ▲                     │
│        ▼                        │ async_send_text     │
│  ┌──────────────┐               │                     │
│  │ teammate-mcp │  ─────────────┘                     │
│  │  (FastMCP)   │  ◄────── async_get_screen_contents  │
│  └──────────────┘                                     │
│        │                                              │
│        └─► returns extracted answer to Claude         │
└──────────────────────────────────────────────────────┘

For each ask_codex (or ask_claude) call:

  1. Generate a unique marker, enqueue the message in the on-disk queue(pending/inflight/ atomic rename).
  2. Locate the target pane:
    • prefer TEAMMATE_<UPPER>_SESSION_ID env override
    • otherwise enumerate all live processes (ps-style), find anyclaude or codex process, read its TERM_SESSION_ID env var,and match that against iTerm's session list. This works throughtmux, login shells, and pyenv wrappers — anywhere theenvironment variable is inherited.
    • fall back to jobName / commandLine matching with cwdpreference.
  3. async_send_text the prompt + a request to terminate the replywith the marker.
  4. Poll async_get_screen_contents for the marker. Because the promptwe typed contains the marker text (it gets echoed in the pane), theserver requires the marker to appear twice before treating thereply as complete.
  5. Slice the answer between the two marker occurrences, logask.complete, return the answer to the caller.

What "no config" actually means

There is exactly one thing to configure (once): the MCP registrationin step 2 above. After that, any iTerm window with claude+codex panesjust works — including windows that were already open before youinstalled teammate-mcp.

You never write a .teammate.toml, you never teammate start, younever have to remember which session id is which.

Testing

uv pip install -e ".[dev]"
pytest                              # 18 unit + integration tests
python scripts/auto_demo.py         # full end-to-end demo (spawns iTerm)

The unit tests cover the queue, ANSI/marker handling, server moduleimport, and the iTerm session-discovery logic with mocks. Theend-to-end demo opens a real iTerm window and exercises a Claude →Codex → Claude round trip; it requires both CLIs to be logged in andwill incur their normal API charges.

Per-run timing reports are written to tests/results/*.jsonl. Theones already committed to the repo are real, not synthetic.

Troubleshooting

"iTerm Python API is not enabled" — Settings → General → Magic →"Enable Python API" ✓. The first time teammate-mcp connects, iTermalso prompts for permission; click Allow.

"asyncio.run() cannot be called from a running event loop" — you'reon a teammate-mcp older than 0.1.0. Pull main; the tools are nowdeclared async.

"Tool returned an answer that's just my own prompt echo" — theprompt-target pane is running the wrong CLI (e.g., the lookup picked asibling pane that had the same process running). Pin the paneexplicitly:

export TEAMMATE_CLAUDE_SESSION_ID=<unique id from iTerm>
export TEAMMATE_CODEX_SESSION_ID=<unique id from iTerm>

(You can read each pane's unique id fromWindow menu → Window Settings → Identifier, or via AppleScript.)

"Marker not detected within timeout" — the agent on the other endforgot to emit <<DONE_…>>. Add an explicit reminder in yourAGENTS.md. The bundled template already includes this.

License

MIT — see LICENSE.

Acknowledgments

This project crystallised from conversations on top of public researchinto how Claude Code and Codex are being run in 2026:

  • Anthropic's Plan-Generate-Verify and Initializer + Coding Agentharness papers (Rajasekaran 2026-03; Justin Young 2025-11).
  • IndyDevDan's claude-code-hooks-masteryfor the observability patterns.
  • OthmanAdi's planning-with-filesfor the "structured files bridge sessions, not chat history" idea.
  • Boris Cherny's "verification loop" rule from hisHow I use Claude Code thread.
  • Geoffrey Huntley's Ralph Wiggumloop for the "fresh context per turn" intuition.

The implementation owes its iTerm Python API patterns to the iTerm2docs at https://iterm2.com/python-api/.

한국어 요약

CCB 같은 사전 설정 없이 claude / codex가 서로에게 질문할 수 있게해주는 작은 MCP 서버입니다.

  • iTerm 두 페인에 그냥 claudecodex를 띄우기만 하면 됩니다.라벨도, config도, daemon도 없습니다.
  • iTerm Python API로 상대 페인을 자동 탐지(실행 프로세스 + 환경변수TERM_SESSION_ID 매칭)합니다 — tmux 안에서 띄워도 작동합니다.
  • 메시지는 push, 응답은 polling으로 받고, 모든 round trip은~/.teammate-mcp/logs/<날짜>.jsonl에 기록됩니다.
  • 실측 round-trip 시간: 2 + 2 = 4 질문 기준 send → complete 3.0초(대부분 Codex thinking 시간).

설치는 위 영문 Quick start 1~3단계, 사용법은 그냥 평소처럼 Claude에게"Codex에게 물어봐"라고 시키면 됩니다.

MCP Server · Populars

MCP Server · New

    raine

    consult-llm

    MCP server for consulting powerful reasoning models in Claude Code

    Community raine
    sipyourdrink-ltd

    bernstein

    Deterministic orchestrator for 30+ CLI AI coding agents. Git worktree isolation, HMAC audit trail, MCP server mode.

    Community sipyourdrink-ltd
    wxtsky

    byob

    Bring Your Own Browser — let your AI agent use the Chrome you already have open

    Community wxtsky
    punkpeye

    FastMCP

    A TypeScript framework for building MCP servers.

    Community punkpeye
    can4hou6joeng4

    boss-agent-cli

    AI-agent-first CLI for BOSS 直聘 — 职位搜索、福利筛选、招聘者工作流、MCP 工具与 AI 简历优化

    Community can4hou6joeng4