Install
$ agentstack add mcp-maxmedawar-tandem Open-source listing, not yet scanned by AgentStack. Follow the source repository for install instructions.
Security review
⚠ Flagged1 finding(s); flagged for manual review. · v0.1.0 How review works →
- • Prompt-injection patterns
- • Secret / credential exfiltration
- • Dangerous shell & filesystem operations
- • Untrusted network calls
- • Known-malicious package signatures
- high Pipes remote content directly into a shell (remote code execution).
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Run and manage Claude Code sessions from your Claude.ai chat, and it talks back on its own.
=22.6">
An MCP bridge that lets a chat AI (Claude.ai, ChatGPT) spawn and drive real Claude Code sessions on your own machine.
What it is
tandem is a bridge between a chatbot and your computer: the chat AI connects over a URL you control and drives real, interactive Claude Code sessions running locally — opening them, sending work, and reading back results — while you watch or type alongside in your own terminal. It can also run two sessions autonomously: a manager that plans and reviews hands work to a worker that builds, relaying back and forth with no human in the loop. Everything runs on your machine; the only thing that leaves it is the tunnel you started yourself.
Quick start
Set up with Claude Code (easy, automated - recommended)
Paste the prompt below into a new Claude Code session — it does the whole install for you and pauses with simple instructions whenever you need to click something in your own account.
You are setting up "tandem" on this machine for me. tandem is an MCP bridge (public repo: https://github.com/Maxmedawar/tandem) that lets my chat AI drive Claude Code sessions here over a persistent Tailscale Funnel URL. Do the whole setup yourself and only stop when you genuinely need me to click something in my own account.
- Run every network or tailscale command wrapped in `timeout 40` so nothing ever hangs forever. If a command times out, treat it as a failure, tell me plainly, and move to the matching STOP block below.
- Do the install steps silently. Do not explain what you are about to do or narrate progress. Only talk to me when you hit a STOP block or when you are finished.
- You CANNOT click inside my browser or my Tailscale account. When a step needs that, use a STOP block: print the exact simple instructions, then wait for me before doing anything else. Never guess past a STOP. When I reply: if I say "continue" or "done", re-check the step worked and move on; if I describe a problem or say I am stuck, help me fix it in plain language, then continue once it is sorted.
- Keep STOP instructions plain and non-technical: numbered, short, no jargon. Give me the exact link to click when there is one.
- Never print my token anywhere except the single final MCP URL at the very end.
1. PREREQS. Check for: Node 22.6+, tmux, the `claude` CLI, and `tailscale`. Install whatever is missing using the system package manager (on macOS use Homebrew; Tailscale on macOS is `brew install --cask tailscale-app`, the standalone app - the Mac App Store version does not work). If a tool cannot be auto-installed, use a STOP block telling me how to install it.
2. TAILSCALE LOGIN. Run `timeout 40 tailscale status`. If it says logged out / needs login / not running, do this STOP block:
PAUSE. One quick thing:
1. Open the Tailscale app on this computer.
2. Click "Log in" and sign in (any account, it is free).
3. Wait until it says Connected.
Then come back here and type: continue
3. GET THE CODE. If a `tandem` folder is not already here, run `git clone https://github.com/Maxmedawar/tandem.git`. cd into it.
4. FREE THE PORT. Run `lsof -ti:8787 | xargs kill -9 2>/dev/null` to clear any old bridge, ignore errors.
5. TURN ON THE FUNNEL (this is where the two one-time account switches live). Run `timeout 40 tailscale funnel --bg 8787` and read the result:
- If it succeeds, continue to step 6.
- If the output mentions Funnel is not enabled / not permitted / a node attribute, it will include a link. Do this STOP block, pasting the real link it gave:
PAUSE. One quick switch to flip (takes 30 seconds):
1. Open this link:
2. Click the button to turn Funnel on.
3. Come back here and type: continue
- If the output mentions HTTPS is not enabled / certificates, do this STOP block:
PAUSE. One quick switch to flip (takes 30 seconds):
1. Open this link: https://login.tailscale.com/admin/dns
2. Find "HTTPS Certificates" and click Enable.
3. Come back here and type: continue
After I type continue, run the same funnel command again. Repeat until it succeeds (I may need to flip both switches, one at a time).
6. START THE BRIDGE + PRINT THE URL. Run `TANDEM_SETUP_MODE=tailscale ./setup.sh`. It installs deps, reuses or makes the token, starts the bridge, and prints the MCP URL. If it asks anything interactively, pick tailscale.
7. VERIFY. Get my funnel hostname from `tailscale status --json` (the DNSName), then run `timeout 40 curl -s -o /dev/null -w "%{http_code}" https:///health`. It must print 200. If it does not, tell me plainly what failed and stop.
When /health returns 200, print exactly this and nothing else after it:
tandem is live. Here is your connector:
URL: /mcp>
To connect it in Claude.ai:
1. In the left sidebar, click your name / "Customize", then click "Connectors".
2. At the top of the Connectors panel, click the three dots (...) and choose "Add custom connector".
3. Give it a name: Tandem
4. Paste the URL above into the URL field (the second box). Click enter.
5. That is it - you are set.
Then install the "Claude for Chrome" extension and sign in, and in any chat say: start tandem
(If Claude.ai ever asks you to "sign in" to the connector instead of just adding it, the URL is wrong - re-paste the full one above, including the part after the last slash.)
(You need Claude Code installed first: npm i -g @anthropic-ai/claude-code, which needs Node 22.6+. Then run claude. The same prompt lives in [SETUP-PROMPT.md](SETUP-PROMPT.md).)
Set up yourself (not recommended)
- Install the prereqs:
- Node 22.6+
- tmux
- Claude Code CLI (
claude) - Tailscale:
- macOS:
brew install --cask tailscale-app(the standalone GUI app; the Mac App Store version does not support Funnel) - Linux:
curl -fsSL https://tailscale.com/install.sh | sh
- Get tandem:
git clone https://github.com/Maxmedawar/tandem.git && cd tandem && ./setup.sh - Pick Tailscale when the script asks which mode (it's the default — just press Enter). You get a permanent URL you set up once and never touch again.
- Sign into Tailscale when prompted — open the Tailscale app and log in (one time, free account).
- Copy the MCP URL the script prints (
https://..ts.net//mcp). - Paste it into your chat app: Claude.ai or ChatGPT → Settings → Connectors → Add custom connector.
- Install the Claude Chrome extension ("Claude for Chrome" from the Chrome Web Store) and sign in — this lets sessions report back to your chat.
- Say "start tandem" in your chat, and you're off.
Full walkthrough + troubleshooting: see [SETUP.md](SETUP.md).
How it works
tandem runs a small MCP server on your machine and exposes it one of three ways:
- Tailscale Funnel (recommended): a persistent public URL —
https://..ts.net— that never changes across restarts. Free on every Tailscale plan, real HTTPS, no interstitial page. One-time login, then set-and-forget. - Cloudflare quick tunnel: a free, anonymous
https://.trycloudflare.comURL — no account at all, but the URL changes every run. - Local stdio (desktop): no network, no tunnel — for Claude Desktop / ChatGPT desktop.
A chat AI connects to that URL and can open a real, interactive claude session, talk to it, and watch it work, while you sit at the same terminal.
Sessions are real interactive Claude Code TUIs running inside tmux (ccm-), driven by keystroke injection and screen scraping against the genuine claude CLI. You can tmux attach -t ccm- to watch or type alongside the AI. Everything runs locally; the only thing that leaves your machine is the tunnel you started yourself.
Capabilities
- Drive a live Claude Code session — open, send turns to, and read back from an interactive
claudesession running locally in tmux. - Shared live session — you and the chat AI both interact with the same tmux session;
tmux attachlets you watch or type alongside, and reads are incremental (cursor-based) so neither side blocks the other. - Turn-completion detection —
send_to_sessionholds open until the turn finishes (detected via Claude Code's "esc to interrupt" marker plus screen-stability); if a turn runs long it returnsstatus:"running"and you pollread_sessionuntilidle:true. - Autonomous lead/worker relay — two interactive sessions message each other with no human in the loop: a lead strategist hands one step at a time to a worker and reviews results, relaying until the lead emits
RELAY_DONEor a turn cap is hit. Steer it live withinject. - Persistent manager (park-and-wait) — the lead doesn't die when a task finishes: it parks (idle, alive, keeping its on-disk memory) and waits for the next task you
enqueue, running each under a fresh per-task budget, until you stop it, it sits idle past a timeout, or it escalates that it's stuck.
What this can't do (honest limits)
- It cannot wake the claude.ai chat tab. A remote MCP connector is
request/response; the stateless Streamable-HTTP transport here holds no standing server→client channel. tandem can emit a completion signal (events.log + optional webhook) and ping a device (your phone, via ntfy), but it cannot make the claude.ai chat send an unprompted reply. See Completion events / waking the client below.
- The ntfy push reaches a device, not the chat. It tells you the work is
done so you can return to the chat — it does not resume the conversation.
- It is not a hosted/multi-tenant service. Each user runs their own local
bridge and their own tunnel; there is no shared server.
- Automating Claude is on you. tandem drives the official
claudeCLI through
automated and sometimes unattended means; depending on how you auth it, this may fall under Anthropic's Consumer Terms restrictions on automated access. Run it on your own account at your own risk; an Anthropic API key is the cleanly-permitted path for autonomous use.
Tools
Six tools:
open_session— spawn an interactive session in an allowlisted dir. Skip-permissions (autonomous) by default; optionalmodel/effortset the session model and thinking effort (session-scoped, viaclaude --model/--effort).list_sessions— list live + recent sessions.send_to_session— send a prompt and wait (bounded byTANDEM_WAIT_MS) for the turn; returns the report, orstatus:"running"to call again. Omittextfor poll mode (fetch new output sincecursorwithout sending) — this replaces the oldread_session. Accepts slash commands verbatim (see below) and optional per-turnmodel/effortoverrides.interrupt_session— Ctrl-C the current turn, keep the session.close_session— kill the session.relay— one tool withaction: start | read | enqueue | inject | stopfor the autonomous lead/worker relay (replaces the oldstart_relay/read_relay/inject_to_relay/stop_relay). The lead is a persistent manager: when a task finishes it parks and waits;enqueuehands it the next task (see Persistent manager below).
Consolidated from 10 → 6; no capability was removed (the underlying routes are unchanged and still reachable).
Autonomy & control
Skip-permissions by default. Spawned sessions launch with --dangerously-skip-permissions so autonomous turns don't stall on allow-prompts. Disable per host with TANDEM_SKIP_PERMISSIONS=0. This is contained: it only suppresses Claude Code's in-session tool prompts. The cwd allowlist is enforced before every spawn (in open_session/relay and again inside the engine's spawn), and the pane is created in the already-validated cwd — so skipping prompts can never widen which directories are reachable. (On a host that has never accepted bypass mode and lacks skipDangerousModePermissionPrompt, Claude Code shows a one-time acceptance dialog; the engine auto-accepts it on warmup.)
Model & effort. Set them per session at open time, or override per turn on send:
| Param | Accepted values | |---|---| | model | alias default / opus / sonnet / haiku, or a full claude-* id (e.g. claude-opus-4-8) | | effort | low / medium / high / xhigh / max |
open_session{ model?, effort? }→ session-scopedclaude --model/--effortflags (no global side effect).send_to_session{ model?, effort? }→ applied to that turn via in-session/model//effortcontrols (these also persist as Claude Code's saved default for new sessions — prefer open-time for strictly session-scoped control).- Unsupported values are rejected with a clear 400, never silently ignored.
Slash-command passthrough. Any slash command sent as send_to_session's text reaches the TUI verbatim and executes — the autocomplete's exact match resolves on the submit. Examples:
send_to_session { "name": "s1", "text": "/status" } // session/model/account status
send_to_session { "name": "s1", "text": "/mcp" } // MCP server status
send_to_session { "name": "s1", "text": "/model opus" } // switch model
send_to_session { "name": "s1", "text": "/goal ship the parser" } // custom command
Completion events / waking the client
tandem emits a completion event the moment a turn or relay finishes — you don't have to keep polling to learn that work is done. Detection reuses the engine's proven idle/done logic (the "esc to interrupt" marker + screen-stability for turns; RELAY_DONE / cap for relays).
What is emitted — a JSON object:
{ "ts": "…", "type": "session", "status": "done", "id": "", "cursor": 12345, "summary": "…", "reason": "…", "handoff": "CC check — session \"…\" finished (done).\nSummary: …\nCommit: …\nFiles changed: …\nNext: …" }
The handoff field is a chat-ready, copy-pasteable plain-text block (also used as the phone notification body — see Phone notifications below).
Where it goes (the EMIT side, which this repo implements):
~/.tandem/events.log— one JSON line is appended per completion. Durable;tail -fit or have any local process watch it.TANDEM_DONE_WEBHOOK— if set, the same JSON isPOSTed to that URL (fire-and-forget, no deps). Point it at any local listener, notifier, or automation.
Phone notifications (ntfy)
For a real buzz on your phone when a session finishes, tandem can push to ntfy (free, no account) on top of the event emit above. It's off until you set a topic. Three steps:
- Install the ntfy app (iOS App Store / Google Play), or use the web app.
- Subscribe to a topic in the app — pick a long, hard-to-guess name (anyone
who knows the topic can read it), e.g. tandem-9f3a2c-done.
- Set
TANDEM_NTFY_TOPICin your.envto that exact topic (and optionally
TANDEM_NTFY_SERVER if you self-host ntfy; default https://ntfy.sh).
Now each completion sends a notification titled tandem: done whose body is a chat-ready handoff block — the same plain-text block written to events.log and POSTed to the webhook. On the phone it looks like:
🔔 tandem: my-session done
CC check — session "my-session" finished (done).
Summary: added the handoff block to completion events
Commit: 880f6e0
Files changed: 3
Next: Review the session output and decide the next step.
The notification carries a Click action pointing at https://claude.ai, so tapping it opens claude.ai on your phone. There you paste check (or paste the handoff block itself) and the chat Claude immediately picks up — it knows what finished, the commit, how many files changed, and what to do next, without you re-typing any context. The POST is fire-and-forget; if ntfy is unreachable the failure is logged t
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Maxmedawar
- Source: Maxmedawar/tandem
- License: MIT
- Homepage: https://eFreedom.com
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.