Real-time CI/CD notifications delivered straight into your Claude Code session. When a CI workflow or pipeline fails, Claude sees it immediately and can investigate the failure, check logs, and suggest fixes — without you having to context-switch.
Supports GitHub Actions, GitLab CI, and Gitea Actions.
Forge (GitHub/GitLab/Gitea)
│
webhook POST
│
▼
smee.io (relay) ← auto-provisioned on first run
│
▼
localhost:{port} ← random port, no conflicts
│
▼
┌───────────────────┐
│ Channel Plugin │ ← validates signature, deduplicates, filters
│ (MCP server) │
└─────────┬─────────┘
│
▼
┌───────────────────┐
│ Claude Code │ ← receives structured notification
│ (session) │
└───────────────────┘
NOTE: Claude Channels is still a new feature so config is a bit complicated.
LLM agents: See INSTALL.md for step-by-step installation instructions designed for AI agents to follow programmatically.
The plugin auto-generates a webhook secret and provisions a smee.io relay on first run. You just need to:
From the project you want to monitor:
cd /path/to/your-project
claude mcp add-json --scope project ci '{"command":"npx","args":["-y","ci-channel"]}'This adds ci-channel to your project's .mcp.json. npx downloads and runs it on first invocation — no clone or local install required. Each project gets its own dedicated smee channel and webhook — no cross-talk between projects.
Installing from source instead? See Development Setup at the bottom of this doc.
claude --dangerously-load-development-channels server:ciOn first run, the plugin:
- Generates a
WEBHOOK_SECRETand provisions a smee.io relay channel - Saves auto-provisioned state to
<project-root>/.claude/channels/ci/state.json(per-project, persists across restarts) - Sends a channel notification to Claude with the webhook URL and secret:
CI channel ready. Configure your forge webhook:
URL: https://smee.io/abc123
Secret: a1b2c3d4...
Events: Workflow runs (GitHub/Gitea) or Pipeline events (GitLab)
Copy the URL and secret from the notification and configure your forge's webhook. For GitHub, you can use the gh CLI:
gh api repos/OWNER/REPO/hooks --method POST --input - <<'EOF'
{
"config": {
"url": "https://smee.io/YOUR_CHANNEL_URL",
"content_type": "json",
"secret": "YOUR_WEBHOOK_SECRET"
},
"events": ["workflow_run"],
"active": true
}
EOFOr configure it manually in the GitHub UI (see Per-Forge Setup Guides below).
That's it. No .env file to create manually, no browser visit to smee.io.
Setup (run from your project directory):
claude mcp add-json --scope project ci '{"command":"npx","args":["-y","ci-channel"]}'
claude --dangerously-load-development-channels server:ciNo --forge flag needed — GitHub is the default.
Webhook configuration — Use the gh CLI or the GitHub UI:
gh api repos/OWNER/REPO/hooks --method POST --input - <<'EOF'
{
"config": {
"url": "SMEE_URL_FROM_NOTIFICATION",
"content_type": "json",
"secret": "SECRET_FROM_NOTIFICATION"
},
"events": ["workflow_run"],
"active": true
}
EOFOr manually: Settings > Webhooks > Add webhook
| Field | Value |
|---|---|
| Payload URL | The smee.io URL from the notification |
| Content type | application/json |
| Secret | The secret from the notification |
| Events | Select "Workflow runs" only |
.env file (<project-root>/.claude/channels/ci/.env): auto-generated on first run. If configuring manually:
WEBHOOK_SECRET=your-webhook-secretOptional CLI: Install gh CLI for startup reconciliation and failed job enrichment.
Reference: GitHub Webhooks docs, workflow_run event
Setup (run from your project directory):
claude mcp add-json --scope project ci '{"command":"npx","args":["-y","ci-channel","--forge","gitlab","--repos","group/project"]}'
claude --dangerously-load-development-channels server:ciFor nested namespaces, use the exact path_with_namespace value: --repos "group/subgroup/project".
Webhook configuration — Go to your GitLab project: Settings > Webhooks > Add new webhook
| Field | Value |
|---|---|
| URL | The smee.io URL from the notification |
| Secret token | The secret from the notification |
| Trigger | Check Pipeline events only |
.env file (<project-root>/.claude/channels/ci/.env): auto-generated on first run. If configuring manually:
WEBHOOK_SECRET=your-gitlab-secret-tokenOptional CLI: Install glab CLI for startup reconciliation and failed job enrichment.
Reference: GitLab Webhooks docs, Pipeline events
Setup (run from your project directory):
claude mcp add-json --scope project ci '{"command":"npx","args":["-y","ci-channel","--forge","gitea","--gitea-url","https://your-gitea-instance.com","--repos","owner/repo"]}'
claude --dangerously-load-development-channels server:ciSecrets — Add to <project-root>/.claude/channels/ci/.env:
GITEA_TOKEN=your-gitea-api-tokenWebhook configuration — Go to your Gitea repo: Settings > Webhooks > Add Webhook > Gitea
| Field | Value |
|---|---|
| Target URL | The smee.io URL from the notification |
| Secret | The secret from the notification |
| Events | Select "Workflow runs" |
.env file (<project-root>/.claude/channels/ci/.env):
WEBHOOK_SECRET=your-webhook-secret
GITEA_TOKEN=your-gitea-api-tokenNote: --gitea-url is required for startup reconciliation and job enrichment (uses the Gitea API directly). GITEA_TOKEN enables authenticated API access.
Reference: Gitea Webhooks docs
If you use Codev for AI-assisted development, you can add ci-channel to your architect session so CI notifications arrive while you work.
Step 1: Register the MCP server in your Codev project:
cd /path/to/your-codev-project
claude mcp add-json --scope project ci '{"command":"npx","args":["-y","ci-channel"]}'Step 2: Update .codev/config.json to add the channel flag to the architect command:
{
"shell": {
"architect": "claude --dangerously-skip-permissions --dangerously-load-development-channels server:ci",
"builder": "claude --dangerously-skip-permissions",
"shell": "bash"
}
}The architect session will now receive CI notifications in real-time. Builders don't need the channel — they focus on implementation.
Step 3: Configure the webhook for your repo (see Quick Start step 4).
Filtering: Project scope naturally isolates each project, but if you want to further limit notifications to specific repos, add --repos to the MCP server args:
claude mcp add-json --scope project ci '{"command":"npx","args":["-y","ci-channel","--repos","your-org/your-repo"]}'Configuration uses CLI args (passed in the args array when registering the MCP server) for structural settings, and <project-root>/.claude/channels/ci/.env for secrets. Auto-provisioned state (generated secret, smee URL) is persisted to <project-root>/.claude/channels/ci/state.json. Each project is fully isolated.
Project root detection: ci-channel walks up from process.cwd() looking for .mcp.json or .git/ to find the project root. Falls back to the legacy global path ~/.claude/channels/ci/ if neither is found (for backward compatibility).
Precedence: CLI args > env vars > .env file > state.json.
| Arg | Default | Description |
|---|---|---|
--forge |
github |
Forge type: github, gitlab, or gitea |
--repos |
— | Comma-separated repo/project allowlist |
--workflow-filter |
— | Comma-separated workflow names to monitor |
--reconcile-branches |
ci,develop |
Branches to check for recent failures on startup |
--port |
0 (random) |
HTTP server port (0 = OS-assigned) |
--gitea-url |
— | Gitea instance base URL (required for Gitea reconciliation) |
--smee-url |
— | smee.io channel URL (auto-provisioned if not set) |
| Variable | Required | Description |
|---|---|---|
WEBHOOK_SECRET |
No (auto-generated) | Shared secret for webhook signature validation |
GITEA_TOKEN |
No | Gitea API token for authenticated access |
All CLI args also accept env vars for backward compatibility:
| Env var | Maps to | Notes |
|---|---|---|
FORGE |
--forge |
CLI arg takes precedence |
REPOS |
--repos |
CLI arg takes precedence |
GITHUB_REPOS |
--repos |
Legacy alias, lowest precedence |
PORT |
--port |
CLI arg takes precedence |
SMEE_URL |
--smee-url |
CLI arg takes precedence |
WORKFLOW_FILTER |
--workflow-filter |
CLI arg takes precedence |
RECONCILE_BRANCHES |
--reconcile-branches |
CLI arg takes precedence |
GITEA_URL |
--gitea-url |
CLI arg takes precedence |
claude mcp add-json --scope project ci '{"command":"npx","args":["-y","ci-channel","--repos","myorg/api,myorg/frontend","--workflow-filter","CI,Deploy to Production","--reconcile-branches","main,develop"]}'
claude --dangerously-load-development-channels server:ciBy default, the plugin auto-provisions a smee.io channel on first run and persists it to state.json, so the same URL is reused across restarts. You only configure your forge webhook once.
To use a manually provisioned channel instead (e.g., for shared team use), include --smee-url in the args:
claude mcp add-json --scope project ci '{"command":"npx","args":["-y","ci-channel","--smee-url","https://smee.io/your-channel"]}'When the plugin starts, it checks configured branches for recent CI failures that occurred while offline:
- GitHub:
gh run list(requires gh CLI) - GitLab:
glab ci list(requires glab CLI) - Gitea: Gitea API via
--gitea-url
After pushing the initial failure notification, the plugin asynchronously fetches the names of failed jobs. A follow-up notification with job details arrives shortly after — without blocking the initial alert.
Forges occasionally retry webhook delivery. The plugin tracks the last 100 delivery IDs to prevent duplicate notifications. For GitLab, a synthetic delivery ID (gitlab-{project_id}-{pipeline_id}-{status}) ensures different pipeline state transitions aren't suppressed.
- Repository allowlist (
--repos): Only receive notifications from repos you care about - Workflow filter (
--workflow-filter): Only monitor specific workflows
- Signature validation — Every webhook payload is verified:
- GitHub/Gitea: HMAC-SHA256 with timing-safe comparison
- GitLab: Token comparison with timing-safe comparison
- Localhost-only binding — The HTTP server binds to
127.0.0.1only - Repository allowlist — Optional
--reposrestricts which repos can push events - Prompt injection prevention — All user-controlled fields sanitized before inclusion in notifications
- Deduplication — Prevents replay of duplicate webhook deliveries
-
Verify the MCP server is connected:
claude mcp list
The
ciserver should showConnected. -
Check webhook deliveries are arriving:
gh api repos/OWNER/REPO/hooks --jq '.[0].id' # Use the hook ID: gh api repos/OWNER/REPO/hooks/HOOK_ID/deliveries --jq '.[:3] | .[] | {event, status_code, delivered_at}'
You should see
workflow_runevents withstatus_code: 200. -
Trigger a test failure: Push a commit that breaks a test. Within a minute, you should see a channel notification like:
<channel source="ci" workflow="CI" branch="main" run_url="..." conclusion="failure"> CI failure: CI on branch main — commit "break a test" by you </channel>
-
Check the plugin logs: The plugin logs to stderr. If running locally, stderr output shows webhook receipt, signature validation, and notification delivery.
The npm package is ci-channel, but inside Claude Code it's keyed as ci in your .mcp.json. The channel flag uses the key: --dangerously-load-development-channels server:ci. Don't use server:ci-channel — it won't match.
You forgot the channel flag. The MCP server connected, but without --dangerously-load-development-channels server:ci, Claude Code doesn't treat it as a channel.
Project-scoped MCP servers are dormant until you either (a) approve them via /mcp inside a Claude Code session, or (b) add the key to enabledMcpjsonServers in ~/.claude.json. If unsure, use --scope user instead.
- Check that the plugin sent a setup notification on startup (with URL and secret)
- Verify the webhook URL and secret match between your forge and the plugin
- Confirm the correct events are enabled (Workflow runs for GitHub/Gitea, Pipeline events for GitLab)
- Verify the MCP server is registered (
claude mcp list) and you launched with--dangerously-load-development-channels server:ci
The plugin defaults to port 0 (OS-assigned random port), so multiple sessions can run concurrently without port conflicts. Each session auto-provisions its own smee channel.
Startup reconciliation and job enrichment are best-effort:
- GitHub: Requires
ghCLI installed and authenticated - GitLab: Requires
glabCLI installed and authenticated - Gitea: Requires
--gitea-urlconfigured
If the CLI/API is unavailable, the plugin logs a warning and continues — live webhook notifications still work.
Most users should install via npx ci-channel (see Quick Start). This section is for contributors and people who want to run ci-channel from source.
git clone https://github.com/cluesmith/ci-channel.git
cd ci-channel
npm install
npm test # Run all tests (173 tests across 12 files)
npm run build # Build to dist/
npx tsx server.ts # Run from sourceTo register a source build in a project's .mcp.json:
claude mcp add-json --scope project ci '{"command":"npx","args":["tsx","server.ts"],"cwd":"/absolute/path/to/ci-channel"}'server.ts # MCP server entry point — HTTP, smee, bootstrap, reconciliation
lib/
forge.ts # Forge interface definition
forges/
github.ts # GitHub Actions forge implementation
gitlab.ts # GitLab CI forge implementation
gitea.ts # Gitea Actions forge implementation
config.ts # Configuration loader (CLI args + env vars + .env file)
bootstrap.ts # First-run auto-provisioning (secret, smee, notification)
handler.ts # Webhook handler pipeline (validate → dedup → filter → notify)
webhook.ts # WebhookEvent type, deduplication, filtering
notify.ts # Notification formatting and sanitization
reconcile.ts # Startup reconciliation orchestration
tests/
forges/
gitlab.test.ts # GitLab forge unit tests
gitea.test.ts # Gitea forge unit tests
webhook.test.ts # GitHub forge + shared webhook tests
notify.test.ts # Sanitization, formatting
config.test.ts # Config loading, CLI args, precedence
bootstrap.test.ts # Auto-provisioning with injected deps
reconcile.test.ts # Reconciliation, job enrichment
integration.test.ts # GitHub HTTP pipeline end-to-end
integration-gitlab.test.ts # GitLab HTTP pipeline end-to-end
integration-gitea.test.ts # Gitea HTTP pipeline end-to-end
stdio-lifecycle.test.ts # MCP stdio stability regression test
fixtures/ # Sample webhook payloads per forge
This is a Codev project and follows the Codev methodology for AI-assisted development. The preferred way to contribute is via PRs generated with Codev — three documents in addition to the code changes.
Each feature follows the three-document model:
- Specification (
codev/specs/) — What to build and why - Plan (
codev/plans/) — How to build it, in testable phases - Review (
codev/reviews/) — What was learned, deviations from plan
PRs that include Codev artifacts (spec, plan, review) are significantly easier to review and integrate.
The project's architectural knowledge lives in:
- Architecture — System design, data flow, security model, component reference
- Lessons Learned — Accumulated insights from reviews and production use
See CLAUDE.md for Claude Code-specific instructions and AGENTS.md for cross-tool AI agent compatibility (Cursor, GitHub Copilot, etc.).
MIT License. See LICENSE for details.