wingman
Click on "Deploy 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., "@wingmanlist my active Codex sessions"
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.
Wingman
Your coding agent's wingman — a TypeScript MCP bridge + pair CLI so Grok Bot, Cursor, Muse Code (and other MCP hosts) can talk to live Codex and Claude Code sessions on your machine while you still see the session.
Dual-provider: Codex (app-server) + Claude Code (Agent SDK).
See ROADMAP.md for positioning, backlog, and non-goals.
Why Wingman?
Cloud assistants are great copilots — until they need to touch the session you're already in. Wingman sits on your machine, pairs with a one-command CLI, and exposes a small MCP surface so a remote host can list threads, read transcripts, send messages, and interrupt turns — without hijacking your TTY or pretending to be the agent UI.
Think of it as a radio link between the bot in the cloud and the agent on your desk. You're still flying; Wingman just rides shotgun.
Related MCP server: Rail Connector MCP
Quickstart
Option 1: npx (recommended)
# Run directly without install — try it now!
npx wingman-mcp
# Or install globally for repeated use
npm install -g wingman-mcp
wingman-pairSet CODEX_MOCK=1 or CLAUDE_MOCK=1 for mock mode (no real agent required).
Option 2: Clone (for contributors)
git clone https://github.com/juangurdian/wingman.git
cd wingman
npm install
npm run pairAfter pairing
Pair —
wingman-pairgenerates a bearer token, writes~/.wingman/config.json, and starts MCP on127.0.0.1:3847/mcp.Tunnel — remote hosts cannot reach localhost. Pick a tunnel option (ranked by durability):
# Option 1: Tailscale Serve (stable, loopback to your network) tailscale serve --bg 3847 # Option 2: Tailscale Funnel (stable, public URL) tailscale funnel 3847 # Option 3: Cloudflare named tunnel (stable, custom domain) # Run 'wingman-tunnel' for one-time setup instructions cloudflared tunnel run wingman # Option 4: Cloudflare quick tunnel (ephemeral, for demos) cloudflared tunnel --url http://127.0.0.1:3847For detailed setup help, run
wingman-tunnel(ornpm run tunnel).Add MCP server (Grok Bot / Cursor
AddMcpServer):Field
Value
name
wingmanurl
https://YOUR-TUNNEL-HOST/mcpAuthorization
Bearer <token printed by pair>
Then ask the host to call list_sessions → read_transcript / send_message / interrupt.
Host-specific guides
Different MCP hosts have different configuration methods. See the detailed guides:
Host | Support | Guide |
Grok Bot | ✅ Full | |
Cursor IDE | ✅ Full | |
Muse Code | ✅ Full | |
Claude Desktop | ⚠️ Limited | |
Other clients | Varies |
Note: Wingman is a remote HTTP MCP server. Hosts that support HTTP + bearer auth work natively. Stdio-only hosts (like Claude Desktop) require a bridge — see the Claude Desktop guide for workarounds.
Real provider modes
Codex — Requires codex on PATH. Wingman speaks Codex app-server JSON-RPC (codex app-server over stdio).
npm run pair # unset CODEX_MOCKClaude Code — Uses the @anthropic-ai/claude-agent-sdk. Sessions are created and resumed through the SDK.
npm run pair # unset CLAUDE_MOCKDocs: Codex App Server · Claude Agent SDK
Can / Can't
Can
Pair in mock mode with no agent install (
CODEX_MOCK=1orCLAUDE_MOCK=1)Bridge Grok Bot ↔ local Codex threads via app-server once tunneled
Bridge Grok Bot ↔ local Claude Code sessions via Agent SDK once tunneled
Discover existing Claude sessions via SDK's
listSessions()(sessions you created viaclaudeCLI or Claude Code IDE)Resume discovered sessions by ID — the SDK reads session state from
~/.claude/projects/Send messages to sessions asynchronously (returns
acceptedquickly; turn runs in background)Check session status (idle vs running) via
get_sessionorlist_sessionsBearer-protect the MCP HTTP endpoint
Create / list / read / message / interrupt sessions (real or mock) for both providers
Keep you in the loop — the session stays visible on your machine
Can't (scope / v1 limits)
Reach the bridge from a remote host without a tunnel (binds loopback only)
Type into an open TTY — SDK resume ≠ injecting keystrokes into a Claude/Codex terminal you're watching; Wingman calls SDK APIs that operate on session state files, not terminal processes
Hijack arbitrary Claude / Codex TTYs you already have open elsewhere — Wingman manages sessions via SDK, not by attaching to interactive shells
Interrupt a discovered session reliably unless Wingman started the current turn (no active query handle)
Auto-approve sandbox prompts (approvals still belong to the local agent client)
Use legacy
codex mcp-server(removed / not used here)
Architecture
flowchart LR
subgraph cloud [Cloud]
Grok[Grok Bot]
Cursor[Cursor IDE]
Muse[Muse Code]
end
subgraph user [User machine]
Tunnel[cloudflared / Tailscale]
Bridge[Wingman MCP\n127.0.0.1:PORT]
Codex[Codex app-server\nJSON-RPC stdio]
Claude[Claude Agent SDK\ndiscovery + resume]
end
Grok -->|HTTPS + Bearer| Tunnel
Cursor -->|HTTPS + Bearer| Tunnel
Muse -->|HTTPS + Bearer| Tunnel
Tunnel --> Bridge
Bridge --> Codex
Bridge --> ClaudeMCP tools
Tool | Args | Notes |
|
| Lists sessions for one or both providers (includes |
|
| Get detailed session info including status ( |
|
| Recent messages (newest at end) |
|
| Codex: |
|
| Codex: |
|
| Codex: |
|
| Wait for turn to complete/fail/timeout; returns status + message snippet |
|
| Add guidance to in-flight turn (Codex only; Claude returns unsupported) |
|
| List pending approvals (Codex); Claude returns empty array |
|
| Resolve approval (Codex); Claude returns unsupported error |
|
| Set session name/tags for easier discovery (Wingman-owned sessions) |
|
| Export transcript as |
create_session behavior
For Claude sessions, create_session returns immediately:
Without prompt: Returns
{ sessionId, status: "created" }— session is registered but no turn is runningWith prompt: Returns
{ sessionId, status: "accepted", turnId }— the initial prompt turn runs in the background, preventing MCP HTTP timeouts
For Codex sessions, create_session calls thread/start and returns { sessionId, status: "created" | "accepted", model? }.
Codex model override
By default, Wingman does not override the Codex model — it uses whatever is configured in ~/.codex/config.toml or Codex's built-in defaults. Override only when you intentionally need a different model for Wingman-created sessions:
Via environment variable:
export WINGMAN_CODEX_MODEL="your-preferred-model"
npx wingman-mcpVia create_session argument (per-session):
{ "provider": "codex", "model": "your-preferred-model", "prompt": "Hello" }Priority order: create_session.model > WINGMAN_CODEX_MODEL env > user's ~/.codex/config.toml > Codex defaults
Note: Some models may require API access rather than a ChatGPT subscription. If session creation fails due to model access, either ensure you have the required access level, or let Codex pick its default by not setting a model override. Run wingman-doctor to check for models that may require API access.
send_message behavior
For Claude sessions, send_message returns immediately with { status: "accepted", turnId } while the SDK query runs in the background. Use get_session or read_transcript to observe progress. This prevents MCP HTTP timeouts during long model turns.
For Codex sessions, send_message blocks until turn/start returns (typically fast), then returns { status: "completed" | "inProgress" }.
interrupt behavior
For Claude sessions, interrupt works reliably when Wingman owns the active turn (i.e., you called send_message through Wingman for the current turn). Returns { status: "interrupted", turnId } on success, or { status: "no_active_turn" } if the session is idle.
Limitation: Interrupting discovered sessions or sessions where the turn was started outside Wingman (e.g., via Claude CLI directly) may not work — Wingman has no active query handle to abort. In these cases, use the Claude CLI directly: Ctrl+C in the terminal or claude interrupt.
For Codex sessions, interrupt requires a known active turnId tracked from turn/started notifications.
Session tags/names
Sessions can have human-friendly names and tags for easier discovery and filtering:
create_session: Pass name and/or tags when creating a session:
create_session({ provider: "claude", cwd: "/project", name: "Auth Refactor", tags: ["refactor", "auth"] })set_session_meta: Update name/tags on existing Wingman-owned sessions:
set_session_meta({ provider: "claude", session_id: "sess_123", name: "Updated Name", tags: ["new", "tags"] })Sessions returned by list_sessions and get_session include name and tags fields. For discovered Claude sessions, the legacy tag field is also converted to tags.
Note: In real mode, set_session_meta only works for Wingman-owned sessions (sessions created via Wingman's create_session). Codex real mode does not support custom metadata.
wait_turn (both providers)
wait_turn provides long-poll waiting instead of busy-polling read_transcript. Works for both Codex and Claude sessions.
For Claude sessions, wait_turn polls activeTurns until the turn completes, times out, or the session becomes idle. This is essential for real Claude cold starts which can take 1-3+ minutes — use wait_turn to avoid MCP HTTP timeouts.
Example Claude workflow:
1. create_session(prompt: "Build a web scraper") → { status: "accepted", turnId: "turn_1" }
2. wait_turn(timeout_ms: 180000) → { status: "completed", latestMessage: "I've created..." }For Codex sessions, wait_turn returns when the turn completes, fails, is interrupted, times out, or when approvals are pending.
Example response:
{ "sessionId": "thr_123", "turnId": "turn_456", "status": "completed", "latestMessage": "Done!" }steer/approvals (Codex-specific, Claude soft stubs)
steer: Add mid-turn guidance without starting a new turn. Uses Codex's turn/steer API.
Codex: Useful for follow-up instructions while Codex is working.
Claude: Returns
{ accepted: false, error: "Claude does not support mid-turn steering..." }. Useinterrupt()+send_message()instead.
list_approvals + resolve_approval: Programmatic approval handling for sandbox commands, file changes, or network access.
Codex: Full support via JSON-RPC requests.
Claude:
list_approvalsreturns empty array;resolve_approvalreturns unsupported error. Approvals must be handled in the local Claude CLI directly.
Example Codex approval flow:
1. send_message("sudo apt update") → { status: "inProgress" }
2. list_approvals() → { approvals: [{ id: "appr_1", kind: "command", command: "sudo apt update" }] }
3. resolve_approval("appr_1", "accept") → { resolved: true }
4. wait_turn() → { status: "completed" }Note: In mock mode (CODEX_MOCK=1), commands containing sudo or rm -rf trigger simulated approval requests for testing.
Environment variables
General
Variable | Default | Description |
| (generated) | Bearer token for MCP auth |
|
| MCP server port |
|
| MCP server bind address |
|
| Default timeout for |
|
| Default poll interval for |
|
| Set to |
Codex
Variable | Default | Description |
| unset | Set to |
|
| Path to Codex CLI binary |
|
| Args passed to Codex binary |
|
| JSON-RPC timeout |
| unset | Override Codex model for Wingman sessions (optional; defaults to Codex config/defaults) |
Claude
Variable | Default | Description |
| unset | Set to |
|
| Set to |
| (all) | Colon-separated directories to search for sessions |
|
| Timeout for Claude SDK send operations |
The Claude provider uses the bundled Agent SDK binary automatically. No separate claude CLI install is required unless you override pathToClaudeCodeExecutable in code.
Multi-Host
Variable | Default | Description |
| hostname | Machine identifier for multi-host setups |
| hostname | Human-friendly host name for display |
See docs/hosts/multi-host.md for running Wingman on multiple machines.
Session management
Wingman-managed sessions
Wingman tracks sessions it creates in a local registry (~/.wingman/claude-sessions.json for Claude). This ensures:
Isolation — Only sessions started through Wingman's
create_sessionare visible to MCP clients by defaultNo TTY hijack — We don't scan for or attach to Claude/Codex processes you started elsewhere
Resumable — Sessions can be resumed by ID across Wingman restarts
Claude session discovery
Wingman can also discover existing Claude Code sessions via the Agent SDK's listSessions(). This allows list_sessions to find sessions the user created via claude CLI or Claude Code IDE — not only sessions created through Wingman.
How discovery works:
list_sessionsmerges Wingman's registry with sessions discovered via SDKIf the same session ID exists in both, the Wingman registry entry takes precedence
read_transcript,send_message, andinterruptwork for discovered sessions by resuming viaquery({ options: { resume: sessionId } })When you interact with a discovered session, it's auto-registered in Wingman's registry
What discovery is NOT: This is not TTY hijacking. Wingman does not attach to terminal processes, scrape windows, or take over interactive sessions. Discovery reads session files on disk via official SDK APIs.
Set CLAUDE_DISCOVER=0 to disable discovery and only show Wingman-created sessions.
SessionSummary fields
Sessions returned by list_sessions include:
Field | Type | Description |
|
| Origin of the session |
|
| Human-friendly session name |
|
| User-set tags for categorization/filtering |
|
| Git branch at end of session (discovered) |
|
| Legacy single tag (discovered sessions only) |
Claude session storage
Claude sessions are stored by the Agent SDK in ~/.claude/projects/<project-key>/<session-id>.jsonl. Wingman's registry maps session IDs to their working directories so listSessions() and readTranscript() can locate them.
Scripts & CLI
When installed globally (npm i -g wingman-mcp) or via npx:
Command | Purpose |
| Generate token, save config, start MCP, print pair instructions |
| Check environment: Node version, config, port, Claude SDK / Codex binary |
| Detect tunnel tools, print ranked setup commands, persist tunnel state |
For local development:
Script | Purpose |
| Same as |
| Same as |
| Same as |
| Start MCP only (needs existing config/token) |
| Compile TypeScript → |
| Vitest unit tests (mocked providers) |
Config
~/.wingman/config.json (new installs). If you still have a legacy ~/.session-bridge/config.json, Wingman will read it as a fallback.
{
"token": "...",
"host": "127.0.0.1",
"port": 3847,
"mcpPath": "/mcp",
"createdAt": "...",
"mock": true
}Health Endpoint
Wingman exposes a /healthz endpoint for monitoring and tunnel health checks.
Default behavior: Requires bearer authentication (same token as /mcp).
Auth-free mode: Set WINGMAN_HEALTHZ_AUTH_FREE=1 to allow unauthenticated access to /healthz. This is useful for:
Cloudflare Tunnel health checks
Load balancer probes
Uptime monitors that can't send auth headers
# Enable auth-free healthz (document the security tradeoff)
WINGMAN_HEALTHZ_AUTH_FREE=1 npm run pairSecurity tradeoff: An auth-free /healthz reveals that Wingman is running but exposes no secrets, session data, or MCP functionality. The /mcp endpoint always requires bearer auth.
Example response:
{ "ok": true, "service": "wingman" }Doctor / Health Check
Run npm run doctor (or wingman-pair --doctor) to verify your environment:
npm run doctorChecks:
Node.js version (20+ required)
Config file (
~/.wingman/config.json)Port availability (3847 by default)
Claude Agent SDK availability (unless
CLAUDE_MOCK=1)Codex binary on PATH (unless
CODEX_MOCK=1)
The doctor prints clear next steps if any check fails.
Agent skill
See skills/pair-coding-sessions/SKILL.md for setup / pair / operate steps aligned with this CLI.
Security
See SECURITY.md for security policy, bearer token handling, and best practices.
Community
Open-source. MVP — Codex and Claude providers work (mock + real); APIs may shift before 1.0.
Issues and PRs welcome — see CONTRIBUTING.md. Ideas for tunnel helpers, provider improvements, and other MCP-host guides are especially useful.
If Wingman helped you pair a session, star the repo or open an issue with what you tried. Good wingmen share the checklist.
Develop
git clone https://github.com/juangurdian/wingman.git
cd wingman
npm install
npm run doctor # check environment
npm test # run tests
npm run build # compile TypeScript
CODEX_MOCK=1 npm run pair # or CLAUDE_MOCK=1Node 20+. Success criteria: install, test, build succeed; mock pair prints URL + token.
License
MIT © Juan Gurdian and contributors
This server cannot be deployed
Maintenance
Related MCP Connectors
Real-time chat for AI agents. Claude Code, Cursor, Cline and Codex join channels over MCP.
Enable secure connectivity between Sentry issues and debugging data, and LLM clients, using a Model Context Protocol (MCP) server.
Connect AI agents to Replynodes over the Model Context Protocol.
Remote MCP server for The Colony — a social network for AI agents (posts, DMs, search, marketplace).
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables MCP clients to spawn and control Codex CLI and Claude Code sessions on the host machine, with session management and filesystem access.4MIT
- AlicenseAqualityBmaintenanceLocal MCP bridge that lets Codex operate local Claude Code sessions, including listing, starting, resuming, forking, prompting, and stopping conversations via the Remote Control CLI.14MIT
- FlicenseNot gradedqualityCmaintenanceEnables MCP-compatible hosts such as OpenCode to drive the Codex CLI through codex app-server over stdio, exposing tools to run prompts, inspect status, list threads, and interrupt running turns.-
- AlicenseNot gradedqualityBmaintenanceEnables MCP clients and external AI supervisors to oversee and steer native Codex sessions through a thin local stdio bridge. It exposes eleven codex_* supervisory tools for tasks such as listing threads, starting turns, observing progress, steering, responding to approvals, interrupting, checkpointing, and rolling over work.MIT