Claude Code

Claude Code is the one client that consumes Chamade's real-time push natively — DMs, inbound calls, and call state changes arrive in your agent's context the moment they happen, no polling loop to write.

Connect recommended

Two opt-ins, both needed for push: ?stateful on the MCP URL (so Chamade issues a persistent session) and a launch flag on Claude Code (so it consumes the push channel). Get an agent key from dashboard → Agents first.

1. Add Chamade to .mcp.json (in your project, or ~/.claude.json globally) with ?stateful on the URL:

json
{ "mcpServers": { "chamade": { "type": "http", "url": "https://mcp.dev.chamade.io/mcp/?stateful", "headers": { "Authorization": "Bearer chmd_your_key_here" } } } }

Key presence alone is enough — ?stateful, ?stateful=1, and ?stateful=yes all work the same. For the bearer vs OAuth options and the stdio shim, see the MCP Server reference.

2. Launch Claude Code with the channel flag — required every launch:

bash
# Launch Claude Code with chamade channels loaded claude --dangerously-load-development-channels server:chamade # Resume the current conversation after a restart claude --dangerously-load-development-channels server:chamade --continue # Unattended / auto-approve permissions claude --dangerously-skip-permissions --dangerously-load-development-channels server:chamade
Required every launch

The --dangerously-load-development-channels server:chamade flag is required every time you start Claude Code. It's a command-line argument, not a settings.json entry. Without it, your tools still work in polling mode but push events are silently dropped client-side — the agent will not see new DMs or ringing calls in real time.

The server name after server: must match the key in your .mcp.json. If you have multiple Chamade entries (chamade, chamade-http, etc.), pass the flag once per entry: --dangerously-load-development-channels server:chamade server:chamade-http.

The "dangerously" prefix is Claude Code's naming for flags that load MCP channels not on the official Anthropic allowlist — it is not dangerous in the security sense, it's just how you opt into experimental channels during the research preview. When Chamade is added to the allowlist, the flag will become optional.

Stateless is the default

Without ?stateful, the URL https://mcp.dev.chamade.io/mcp/ serves stateless per-request transport: no session ID, no push channel, transparent to chamade restarts. That's the right default for every client that doesn't need push. Add ?stateful only when you want real-time events via notifications/claude/channel — which Claude Code does.

For the @chamade/mcp-server@4 stdio shim, pass --stateful in args (it auto-appends ?stateful) plus the same launch flag.

What gets pushed

Once connected, events arrive on the open session as notifications/claude/channel messages and Claude Code surfaces them straight into the agent's context:

No polling needed

Do not call chamade_call_status or chamade_inbox in a loop in push mode. Events arrive on their own. You can still call those tools manually to catch up after a reconnect.

How it works

  1. Claude Code opens an MCP session with ?stateful in the URL and sends initialize.
  2. Chamade's hybrid session manager sees the query param, routes to a persistent session, issues an Mcp-Session-Id response header, and declares experimental.claude/channel: {} in server capabilities.
  3. With the launch flag, Claude Code opens a GET /mcp/ SSE stream using that session ID. That GET is the "I want server-initiated messages" handshake — it triggers chamade to spawn a per-session push bridge subscribed to your inbox hub.
  4. When any event is published to the hub (DM webhook, Maquisard bridge, SIP trunk, …), the bridge forwards it to the live SSE connection as a notifications/claude/channel notification.
  5. Claude Code surfaces the event to the agent, which reacts using the normal MCP tools (chamade_call_say, chamade_dm_chat, …).
  6. When the SSE stream closes, the bridge releases its hub subscription automatically — an idle probe catches stale connections within ~25 s even with no events flowing.
Stateful sessions die on chamade restart

A chamade redeploy wipes the persistent sessions. Per the MCP spec a client SHOULD re-initialize on HTTP 404; Claude Code doesn't always do that today, so you may see a brief "MCP disconnected" blip until you relaunch. The stateless default doesn't have this issue — use ?stateful only when you need push.

Voice call workflow

Hosted STT/TTS required for this exact flow

The example below pushes call_transcript events and uses chamade_call_say — both require hosted STT/TTS (BYOK). In BYO audio mode, transcripts come from your own STT and the agent speaks via your own TTS; neither touches the MCP channel. DM, call-state, and inbound-call push work identically in both modes.

YouJoin this Teams meeting: https://teams.microsoft.com/l/meetup-join/...
Toolchamade_call_join → call_id: "abc123", state: "connecting"
ChamadeTranscript: [Alice] OK let's start the sprint review.
ChamadeTranscript: [Bob] I finished the auth migration yesterday.
AgentAlice started the sprint review. Bob reports the auth migration is done. Let me take notes.
Toolchamade_call_say → "Got it, I'm noting that the auth migration is complete."

Messaging workflow

ChamadeNew message from Alice on Discord: "Can you check the test results?"
AgentAlice is asking about test results. Let me check and reply.
Toolchamade_dm_typing → OK
Toolchamade_dm_chat → "All 42 tests passing. The auth migration looks good."

Inbound calls

ChamadeIncoming call: SIP +33 1 23 45 67 89, state: "ringing"
AgentIncoming phone call. Let me answer.
Toolchamade_call_accept → state: "active"

Push vs polling

Clients without push support aren't stuck — they ignore the push notifications and fall back to polling chamade_inbox / chamade_call_list. See other MCP clients for the ready-to-paste polling loop. Nothing breaks; you trade real-time latency for a tighter control loop.

Polling (other clients)Push (Claude Code)
DM messagesPoll chamade_inbox (long-poll up to 55 s)Pushed automatically
Incoming callsPoll chamade_call_listPushed automatically
Transcripts (hosted STT)Poll chamade_call_statusPushed automatically
LatencyDepends on poll intervalReal-time (<1 s)
ClientsAny MCP clientClaude Code (and any client opting into experimental.claude/channel)

Using Codex instead? Codex can't consume this channel, so Chamade ships an autonomous agent that wakes it on each event — see Codex agent.