From cardinal
Connect Claude Code to Cardinal — runs the device-code flow to enable telemetry to the Outcomes Dashboard AND the unified Cardinal MCP tools, in one consent.
How this skill is triggered — by the user, by Claude, or both
Slash command
/cardinal:connectThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Wires Claude Code up to a Cardinal workspace. **Enables both sides at
Wires Claude Code up to a Cardinal workspace. Enables both sides at once by default:
env block in
~/.claude/settings.json.cardinal MCP server appears in this Claude
Code session, exposing whichever tools the org has integrations
configured for. The plugin declares the MCP server natively via
plugins/cardinal/.mcp.json with ${CARDINAL_MCP_URL} and
${CARDINAL_MCP_API_KEY} substitution; the connect script just sets
those env vars in the same settings.json env block. No
~/.claude.json write.Both are minted in one browser-approved consent via the maestro
device-code flow (cardinal-mcp-aggregator.md R5b). The MCP URL is a
single durable endpoint per org (https://<host>/api/orgs/<uuid>/mcp)
whose aggregator fans out to whatever integrations are configured —
adding / removing integrations on the Cardinal side never requires
re-running this command.
You MUST run cardinal-connect in the background. The script
blocks for up to 10 minutes waiting for the user to approve in their
browser; the Bash tool's stdout is buffered until the call returns, so
if you don't background it the user never sees the verification URL.
Invoke via the Bash tool with run_in_background: true:
cardinal-connect
Then surface the URL via the pending side-channel file:
~/.claude/cardinal-pending.json — the script writes it within
1–2 seconds of starting. Read up to 5 times with 1-second gaps.{
"verification_uri": "https://app.cardinalhq.io/connect?code=ABCD-EFGH",
"user_code": "ABCD-EFGH",
"expires_in": 600,
"written_at": "2026-06-05T05:40:59Z",
"plugin_version": "0.3.1"
}
verification_uri to the user prominently — wrap it in
a code fence (a real markdown link is fine too) and say something
like "Open this in your browser, log in if needed, pick your org,
and click Approve. I'm watching for it." Do NOT block on it
yourself; the background bash call is doing that.The pending file is deleted automatically when cardinal-connect
exits — success, denied, expired, or error.
/api/auth/device/code to start the flow.~/.claude/cardinal-pending.json
(this is what step 1 above reads)./api/auth/device/token until approval lands (or the user
denies / the 10-minute TTL expires).~/.claude/settings.json — OTel env keys + the two
CARDINAL_MCP_* env vars (atomic; preserves any unrelated env
keys).~/.claude/cardinal.json — non-secret state + key ids for
/cardinal:status and /cardinal:disconnect.~/.claude.json already had a v0.2-era mcpServers.cardinal
entry, or legacy per-driver cardinal-* entries, prunes them
(with a backup) so the plugin-declared cardinal server doesn't
collide with stale user-config copies.~/.claude/cardinal-pending.json on exit.--telemetry-only — request only the ingest scope. The two
CARDINAL_MCP_* env vars are NOT written; the plugin's .mcp.json
is still loaded by Claude Code but with the env vars unset the
cardinal server entry resolves to empty and silently doesn't
connect.--rotate — proceed even when state shows we're already connected.
Mints fresh keys; the previous ones stay alive until their TTL or
until /cardinal:disconnect revokes them.--host <url> — Cardinal host (default https://app.cardinalhq.io).--no-tool-details — opt out of OTel tool-details capture.--skip-legacy-cleanup — don't prune the v0.2 mcpServers.cardinal
entry or cardinal-* entries from ~/.claude.json. Default behavior
is to prune them.--deployment-env <name> — override the derived
deployment.environment label.--dry-run — run the device-code flow, print what would be written.The plugin's plugins/cardinal/.mcp.json:
{
"cardinal": {
"type": "http",
"url": "${CARDINAL_MCP_URL}",
"headers": { "X-CardinalHQ-API-Key": "${CARDINAL_MCP_API_KEY}" }
}
}
Claude Code reads ~/.claude/settings.json env at process start and
substitutes ${VAR} references in plugin-declared .mcp.json files at
MCP server connect time. So setting CARDINAL_MCP_URL and
CARDINAL_MCP_API_KEY in the env block is all that's needed to bring
the server online — no ~/.claude.json ownership required.
--no-tool-detailsTool-details capture is on by default because without it the
Outcomes Dashboard can't derive repo or service from per-step
events — every session shows as repo=unknown and service=unknown.
Bash command lines and file paths may contain PII some orgs' privacy
policies forbid. If the user's org has such a policy, suggest
--no-tool-details.
Tell the user:
~/.claude/settings.json env has been updated; any v0.2-era
~/.claude.json cardinal entries were pruned.settings.json env is read at process start, and Claude
Code substitutes the env vars into the plugin's .mcp.json when it
loads the MCP servers./cardinal:status from the new session to verify both sides.Surface the script's stderr verbatim and don't claim success. Common cases:
Cardinal is already connected as ... — exit 2 from the
already-connected guard. Re-run with --rotate to overwrite.Consent request expired before approval — the 10-minute TTL
elapsed; re-run.Request was denied in the browser — the user clicked Deny.settings.json is not valid JSON — the script refuses to write into
an unparseable file. Tell the user to fix or back up the file.ingest reachability failed / MCP reachability failed — the
newly-minted keys don't authenticate at the endpoint. Usually means a
maestro misconfig (org has no active lakerunner integration for the
ingest side, gateway not running for the MCP side).npx claudepluginhub cardinalhq/cardinal-claude-plugin --plugin cardinalCreates, edits, and verifies skills using a test-driven development approach with pressure scenarios and subagents.