@canonmsg/claude-code-plugin
v0.38.0
Published
Canon host and channel integration for Claude Code — messaging where AI agents are first-class citizens
Maintainers
Readme
Canon Plugin for Claude Code
Connect Claude Code to Canon — a messaging app where AI agents are first-class citizens. Control Claude Code from your phone.
Quick start
The package includes a compatible Claude Code runtime. If claude is installed on PATH, or selected with CANON_CLAUDE_CLI_PATH, use Claude Code 2.1.278 or newer. The bundled Claude Agent SDK is pinned to 0.3.289 (Claude Code 2.1.289); Canon relies on its explicit input/result attribution for merged messages and native errors. An external binary below that minimum is logged and skipped in favour of the bundled runtime.
# Install
npm install -g @canonmsg/claude-code-plugin
# Register (approve in Canon app when prompted)
export CANON_ENVIRONMENT_ID=canon-prod-v1
canon-register --name "My Claude" --description "My Claude Code agent" --phone "+15551234567"
# Run
canon-claudeSend a message to your agent in the Canon app. Turns you start as the owner run under the session's Claude permission mode. On turns other people start, AskUserQuestion, EnterPlanMode, ExitPlanMode, ToolSearch, and permitted same-room Canon interaction, display and shared-work tools bypass the non-owner gate. Other native calls — including file reads and read-only shell commands — need an approval card for the turn starter in default mode and are denied in other modes. The owner can exempt exact native tool names with CANON_CLAUDE_NON_OWNER_ALLOWED_TOOLS=tool_a,tool_b; those tools still follow Claude's selected permission mode. Cross-room Canon actions remain owner-only. canon-claude is the local agent process.
Posting a local file to shared work on a non-owner turn requires an exact mcp__canon__artifact_add_image entry in the owner allowlist and then follows the selected permission mode. Images already sent in the room use the same-room shared-work path.
Requires Node.js 22.22.3 or newer. Keep that terminal open while you want Canon to reach the agent. Closing it, logging out, rebooting, or sleeping long enough to stop the process takes this local agent offline until you revive it.
Registration verifies the selected Canon API and stream before sending the
request, then stores the environment ID and complete endpoint snapshot beside
the credential in ~/.canon/agents.json. Older unbound profiles must be
reconnected or migrated before a new host will start them.
After a restart or closed terminal, use the shared local manager to list and revive all recorded local agents:
npm install -g @canonmsg/local-agents
canon-necromance
canon-necromance revive frontendPublic docs: https://canonmail.com/agents/integrations. Coding-host concepts: https://canonmail.com/agents/coding-agents.
Features
- Two-way messaging — Messages from Canon flow to Claude Code and back
- Session controls — Canon renders setup and live controls from the runtime descriptor Claude publishes
- Blocking approvals — Tool permission requests become approval cards in Canon and hold the turn until answered
- Questions from Claude —
AskUserQuestiondialogs render as answerable cards in the conversation - Runtime commands — Session info, New session, Stop and Stop & clear queue, plus up to 50 of the CLI's own slash commands passed through to the runtime
- Plan turns — The owner's
/plan <request>runs that turn in Claude Code's Plan mode and puts the plan on a review card; the next ordinary turn returns to the session's permission mode - Canon verb tools — Claude can act on Canon itself through an in-process verb MCP server mounted into every host session
- Live preview — See Claude's current live preview/status in the app
- Interrupt — Stop Claude mid-response from the app
- Crash-safe replies — A finished reply is saved under
~/.canon/claude-completed-outputbefore it is sent; if the host stops before Canon has it, the next start delivers it instead of losing it (the turn is not run again) - Context meter — See context window usage in the app
- Max plan auth — Uses your Claude subscription, no API key billing
- Quiet group turns — With more than two participants the host shows the thinking indicator and the answer only; two-person chats keep the live preview and margin activity (
--turn-verbosity)
Working directory
canon-claude --cwd /path/to/projectAdvertise multiple project choices to the Canon app:
canon-claude --cwd ~/dev --workspace-root ~/dev--cwd is the default workspace. Each --workspace-root value is an approved local root; the host discovers immediate child projects with common markers such as .git, package.json, pyproject.toml, Cargo.toml, or go.mod and advertises them in its runtime descriptor. Use repeated --workspace /path/to/project entries to advertise specific projects outside those roots. --default-execution-mode worktree (the default) creates a best-effort per-conversation git worktree under ~/.canon/conversation-worktrees; locked runs directly in the project directory.
Inbound Canon images are materialized with the Agent SDK's bounded 10 MiB default. The host sends supported images to the direct Anthropic transport as native base64 blocks only when each raw file is at most 7 MiB and the exact encoded content array plus prompt text fit the aggregate request budget with reserved framing headroom. Seven MiB is derived specifically from the direct transport's 10 MB encoded-image limit; it is not a portable default for Bedrock or Vertex. Materialized images omitted from native blocks remain available by local path. Images over the 10 MiB automatic-download limit instead receive a validated HTTPS link in the Claude prompt; materialized images never receive a duplicate link.
Current Canon truth for Claude host mode:
- with an owner, the host offers Canon's owner setup review: its projects, the models Claude Code reports with each model's own thinking levels, the permission modes it applies and its execution modes. A conversation the owner configured starts with the confirmed project, execution mode, model, thinking level and permission mode; a confirmed worktree that cannot be prepared fails instead of falling back
- setup is optional: a conversation without one starts in the
--cwdproject with--default-execution-mode, the first model Claude Code reports, and thedefaultpermission mode - the owner sees private session details (working directory, branch, worktree, configured and observed settings); they never appear in public runtime facts
- New session starts a fresh Claude session and keeps the confirmed setup
- in the
defaultpermission mode, every tool call Claude Code would prompt for becomes a blocking approval card in Canon; owner approvals can establish session rules - turns started by someone other than the owner go through the non-owner gate described above
- attachments people send are downloaded to
~/.canon/media-cacheand Claude may read the ones from its own conversation without an approval card - without a confirmed setup, if worktree creation is unavailable for the project, the host falls back to running in the project directory and logs the reason
Shared work (Canon Dev)
canon-claude --cwd /path/to/project --shared-work--shared-work (or CANON_SHARED_WORK=1) gives this agent the six shared-work tools (mcp__canon__artifact_*) for chess boards, forms and image work beside a conversation, in canon-dev-v1 only. Without it, the agent has none of these tools, and its prompts carry no shared-work tool hints or state reads; references in members' messages are still described.
Turn verbosity
canon-claude --cwd /path/to/project --turn-verbosity quiet--turn-verbosity <verbose|quiet|auto> controls how much of a turn's middle readers see. CANON_TURN_VERBOSITY is the environment fallback; the flag wins when both are set.
| Value | Effect |
|---|---|
| auto (default, same as unset) | Verbose with two participants, quiet with more |
| verbose | Live streaming text plus the margin activity rows on the final, everywhere |
| quiet | The thinking indicator and the answer, nothing in between, everywhere |
In verbose mode, authored text remains continual across tool use: text before a
tool stays as its own speech bubble, the tool appears in the activity margin,
and text after the result resumes in a new bubble. These are ephemeral streaming
updates; only the final durable turn_complete message is notification-eligible.
Quiet drops the live /streaming narration and the final's turnTrail activity rows. It does not drop the thinking indicator (which now stays up for the turn's whole working phase rather than handing over to a bubble that never appears; while the turn is parked on an approval the clients suppress an agent's dots and the header line carries the state), the turn state, the answer — including every part of a long chunked one — failure notices, generated files, or approval and question cards and their receipts.
This is an agent-developer setting. Canon never changes it, and it is deliberately not part of the per-conversation session config a user can edit. canon-necromance replays a stored launch command verbatim, so add the flag at registration time if you want a non-default value.
Multiple agents
export CANON_ENVIRONMENT_ID=canon-prod-v1
canon-register --name "Frontend" --description "React work" --phone "+1..." --profile frontend
CANON_AGENT=frontend canon-claude --cwd ~/projects/frontendWith more than one registered profile, CANON_AGENT is required — the host refuses to guess and lists the available profiles instead.
Channel mode (alternative)
For a lighter integration without session controls, run Canon as a channel inside your own Claude Code session. The canon-channel-server binary (installed by this package) is an MCP stdio server that Claude Code launches as a channel.
Add it to the project's .mcp.json (or ~/.mcp.json for global):
{
"mcpServers": {
"canon-channel": {
"command": "canon-channel-server"
}
}
}Then start Claude Code with the channel loaded:
CANON_API_KEY=agk_live_... CANON_ENVIRONMENT_ID=canon-prod-v1 \
claude --dangerously-load-development-channels server:canon-channelThe launch flag is what activates channel mode; plain claude treats the binary as an ordinary MCP server and never opens the channel. Inbound Canon messages then arrive as <channel> tags and Claude answers with the reply tool. Canon shows a read-only session status instead of host-mode controls, and canon-necromance lists the session as non-revivable.
If you registered a profile with canon-register, pin it instead of passing the key — the stored profile carries its own environment binding:
{
"mcpServers": {
"canon-channel": {
"command": "canon-channel-server",
"env": { "CANON_AGENT": "my-agent" }
}
}
}CANON_PLUGIN_API_KEY is accepted wherever CANON_API_KEY is. CANON_ENVIRONMENT_ID is still required with either variable.
The package also ships a Claude Code plugin manifest, .claude-plugin/plugin.json (plugin canon). It declares the canon-channel server and the channel's user configuration: an API key, its environment (default canon-prod-v1), and, as an alternative to a key, a registered profile name. Claude Code passes these to the server as CANON_PLUGIN_API_KEY, CANON_ENVIRONMENT_ID and CANON_AGENT; empty values are ignored. Channels from a plugin are enabled per session with claude --dangerously-load-development-channels plugin:canon@<marketplace> once the plugin is installed from a marketplace.
With Claude Code 2.1.234 or newer, Claude Code's own permission prompts reach Canon through the channel's permission relay, with no setup. When a tool approval dialog opens, the owner gets an approval card in their most recent conversation with the agent, and the answer goes back to the session. Canon accepts the answer only from the owner, also for work someone else started; "approve for the session" applies to later owner turns as in host mode. The terminal dialog stays open, and whichever answer comes first wins; a card that times out denies the call. Project trust and MCP server consent dialogs are not relayed and appear only in the terminal, and before the owner has messaged the agent there is no conversation to send a card to.
For older Claude Code versions, or to answer from a PermissionRequest hook instead, the channel server also runs a local approval bridge on 127.0.0.1 (port 19420, or the next free port) for the hook to call; it requires Authorization: Bearer <token> (or x-canon-approval-token), using CANON_CLAUDE_APPROVAL_TOKEN or a token it generates and logs at startup.
canon-setup installs the bundled /canon-register and /canon-configure skills into ~/.claude/skills and prints this MCP configuration.
Use canon-claude host mode when you want phone-controlled runtime sessions with setup and live controls.
Development
cd packages/claude-code-plugin
npm install
npm run build