Skip to main content
Glama
woodpecker-mcp

woodpecker-mcp

by si-essence
README.md
# woodpecker-mcp

MCP connector for a self-hosted [Woodpecker CI](https://github.com/woodpecker-ci/woodpecker) v3.20.1 REST API. It speaks official `/api` JSON over stdio and over stateless Streamable HTTP on localhost.

Woodpecker is Apache-2.0 software (`LICENSE` in the v3.20.1 source tree). This connector is MIT. It does not copy Woodpecker source. It is not affiliated with woodpecker-ci/woodpecker.

`spec/api-inventory.json` is a compact extraction of every gin `/api` route registered in v3.20.1 `server/router/api.go`. It records the release version, Apache-2.0, file sha256s, and `routeTreeSha256`. `scripts/gen-operations.mjs` maps that inventory onto tool actions or exclusions and writes `src/operations.ts` and `woodpecker.routes.json`. A route that is neither mapped nor excluded fails generation. Do not edit `src/operations.ts` by hand. Default generation and `npm run ci` read only the committed inventory.

`WOODPECKER_URL` and `WOODPECKER_TOKEN` (or `WOODPECKER_TOKEN_FILE`) are required. There is no default origin. The connector does not start Woodpecker. Each request sends `Authorization: Bearer`. The token is never placed in the URL.

## Setup

```bash
npm install
npm run ci
npm start
npm run start:http
```

`npm start` speaks stdio. `npm run start:http` serves Streamable HTTP at `http://127.0.0.1:3000/mcp` and `GET /health`. HTTP mode has no authentication of its own. Keep it on localhost.

```json
{
  "mcpServers": {
    "woodpecker": {
      "command": "node",
      "args": ["dist/index.js"],
      "env": {
        "WOODPECKER_URL": "http://127.0.0.1:8800",
        "WOODPECKER_TOKEN": "...",
        "WOODPECKER_ALLOW_WRITES": "false"
      }
    }
  }
}
```

The token is a Woodpecker personal access token from User settings → CLI and API. The connector never prints it.

HTTP mode has no authentication of its own. Keep it on localhost. Do not put secrets in a Docker image.

```bash
docker build -t woodpecker-mcp .
docker run -p 127.0.0.1:3000:3000 \
  -e WOODPECKER_URL=http://127.0.0.1:8800 \
  -e WOODPECKER_TOKEN=... \
  woodpecker-mcp
```

Regenerate the registry from the committed inventory.

```bash
npm run gen:operations
node scripts/gen-operations.mjs --check
```

Refresh the inventory from an official v3.20.1 tree. This is the only command that reads route source. Keep that tree outside this MIT connector (for example `/workspace/tools/woodpecker/upstream/source-root`).

```bash
node scripts/gen-operations.mjs --source-root /path/to/woodpecker --write-inventory
node scripts/gen-operations.mjs --source-root /path/to/woodpecker --check
```

`WOODPECKER_SOURCE_ROOT` is accepted by a write run. `--check` ignores that variable unless you also pass `--source-root`.

`scripts/live-check.mjs` talks to a real server over stdio. Set `WOODPECKER_URL`. Set `WOODPECKER_TOKEN` or `WOODPECKER_TOKEN_FILE` to a chmod 600 file outside this repo. The script unsets `DATABASE_URL`. It prints pass or fail lines only, and it redacts the token and live secret values. It does not run inside `npm run ci`. A missing token is a failure.

Isolated local runtime scripts live under `/workspace/tools/woodpecker` (`start.sh`, `stop.sh`). They keep sqlite and secrets under `/tmp/woodpecker-mcp`.

## Config

| Variable                   | Meaning                                                                                                               |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `WOODPECKER_URL`           | Required. Origin only, `http` or `https`, no userinfo, query, hash, or path. Example `http://127.0.0.1:8800`.         |
| `WOODPECKER_TOKEN`         | Required unless `WOODPECKER_TOKEN_FILE` is set. Sent as `Authorization: Bearer`. Redacted in logs, errors, and results. |
| `WOODPECKER_TOKEN_FILE`    | Optional. chmod 600 file whose trimmed contents are the token. Used when `WOODPECKER_TOKEN` is omitted.               |
| `WOODPECKER_ALLOW_WRITES`  | Must be the literal `true` before a confirmed write is sent.                                                          |
| `WOODPECKER_PER_MINUTE`    | Local request budget. Default 30. Cache hits do not spend it. Live-check sets 600.                                    |
| `WOODPECKER_TIMEOUT_MS`    | Upstream timeout. Default 20000.                                                                                      |
| `WOODPECKER_CACHE_TTL_SEC` | Read cache TTL. Default 60. Writes clear the cache.                                                                   |
| `WOODPECKER_CONTACT`       | Contact token in the User-Agent. Must not contain the API token.                                                      |
| `MCP_TRANSPORT`            | `stdio` or `http`. `--http` overrides.                                                                                |
| `PORT`                     | HTTP port. Default 3000.                                                                                              |

Invalid values are reported by field name and issue code. The rejected value is not included.

Calls go to `/api/...` paths from the operations registry. `Accept` is `application/json`. JSON writes send `Content-Type: application/json`. Redirects are refused so the token is not forwarded. There are no automatic retries, including HTTP 429. The token is never placed in the URL, query, or User-Agent.

## Safety

Reads run without a write flag. A write is sent only when `confirm` is the boolean `true` and `WOODPECKER_ALLOW_WRITES=true`. That gate covers every mutation in the registry: activate, update, repair, chown, delete repo, trigger, cancel, restart, approve, decline, delete pipeline, delete logs, cron create/update/delete/run, and secret create/update/delete at repo, org, and global scope. Omit `confirm`, or pass boolean `false`, and the tool returns a dry-run preview of method, path, query, and body. It sends nothing. `blocked` is false on that preview, whether or not writes are enabled. `confirm: true` with writes disabled returns the same preview with `blocked` true and the reason `WOODPECKER_ALLOW_WRITES is not true. The write was not sent.` The string `"true"` is rejected.

Omitted fields are dropped. `enabled: false` and an empty `branch: ""` on cron update are sent when you set them. A field that is not valid for the selected action is rejected before any request is sent. Unknown fields are rejected.

HTTP 401 is unauthorized. HTTP 403 is forbidden. HTTP 400 and 422 are invalid input. HTTP 404 is not found. HTTP 409 is conflict. HTTP 429 includes Retry-After when the header is present. Other failures are server, network, or timeout. Response bodies are capped at 1,000,000 bytes.

Every non-empty string or number under a sensitive key on that request is a secret, at any nesting depth and inside arrays or objects below that key, including 1-character values and values longer than 64 characters. Sensitive keys include `token`, `password`, `authorization`, `secret` *values* under the JSON key `value`, `apiKey`/`apikey`, `session`, `cookie`, `credential`, `passphrase`, `privateKey`, and similar names. The path parameter `secret` is the secret's **name** and is not treated as a credential. The configured token (minimum 8 characters) and any URL userinfo found in the request are included too. Secrets are collected once per request and are not kept for the next call.

Sensitive-key values become `[redacted]` in objects. Substring masking of free text (raw, percent-encoded any-case hex, and JSON-escaped forms) runs only for secrets of length 8 or more. If the request carries a shorter credential, upstream free-text error bodies and log detail are replaced with `upstream detail omitted because this request carried a short credential`. Object keys and this connector's preview envelope (`method`, `path`, `summary`, `executed`, and the other write-gate fields) are not rewritten. That rule covers MCP success results, MCP error text, write previews, HTTP and network errors, and every logger line.

Secret `value` is sent on the HTTP create/update body and is stripped from previews, results, errors, and logs.

## Tools

Six tools. Each takes `action` plus the fields for that action. Fields for a different action are rejected.

| Tool                   | Actions                                                                                                                                                                                          | Routes |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------ |
| `woodpecker_system`    | version, health, me, feed, queue_info, pipeline_queue                                                                                                                                            | GET `/api/version`, GET `/api/healthz`, GET `/api/user`, GET `/api/user/feed`, GET `/api/queue/info`, GET `/api/queue` |
| `woodpecker_repos`     | list, list_all, lookup, get, activate, update, repair, chown, delete, branches, pull_requests, permissions, workflows                                                                            | GET `/api/user/repos`, GET `/api/repos`, GET `/api/repos/lookup/{repo_full_name}`, GET/PATCH/DELETE `/api/repos/{repo_id}`, POST `/api/repos` (`forge_remote_id` query), POST repair/chown, GET branches, pull-requests, permissions, workflows |
| `woodpecker_pipelines` | list, get, create, cancel, restart, approve, decline, delete, config, metadata                                                                                                                   | GET/POST `/api/repos/{repo_id}/pipelines`, GET/DELETE `/api/repos/{repo_id}/pipelines/{pipeline_number}`, POST restart/cancel/approve/decline, GET config and metadata |
| `woodpecker_logs`      | get, delete, delete_pipeline                                                                                                                                                                     | GET/DELETE `/api/repos/{repo_id}/logs/{pipeline_number}/{step_id}`, DELETE `/api/repos/{repo_id}/logs/{pipeline_number}` |
| `woodpecker_cron`      | list, get, create, update, delete, run                                                                                                                                                           | GET/POST `/api/repos/{repo_id}/cron`, GET/PATCH/DELETE `/api/repos/{repo_id}/cron/{cron}`, POST run |
| `woodpecker_secrets`   | list, get, create, update, delete, org_list, org_get, org_create, org_update, org_delete, global_list, global_get, global_create, global_update, global_delete                                   | Repo `/api/repos/{repo_id}/secrets`, org `/api/orgs/{org_id}/secrets`, global `/api/secrets` |

`health` calls GET `/api/healthz`. v3.20.1 answers HTTP 204; this connector returns `{ ok: true, status: 204 }`. The connector's own Streamable HTTP `GET /health` is separate and returns the plain text `ok`. `lookup` needs `repo_full_name` as `owner/name`. `activate` needs `forge_remote_id` (the forge's repository id). `create` pipeline needs `branch`. Secret create needs `name`, `value`, and `events`. Step logs decode Woodpecker's base64 `data` into `text`.

53 of 123 expanded v3.20.1 `/api` routes are mapped. 70 are excluded. The registry has 53 actions and 26 writes. See `woodpecker.routes.json` for every mapping and exclusion reason.

## Coverage and exclusions

In scope: current user, version/health, queue info, repository list/lookup/get/activate/update/repair/chown/delete, branches, pull requests, permissions, workflows, pipeline list/get/trigger/cancel/restart/approve/decline/delete/config/metadata, step logs read and delete, cron CRUD and run, repo/org/global secret CRUD.

Excluded with a stored reason, grouped:

- Admin users: `/api/users`, `/api/users/{login}`.
- Forge administration: `/api/forges`, `/api/forges/{forge_id}`.
- Agents: `/api/agents`, org agents. Agent tokens join the cluster.
- Registries: repo/org/global registry credentials.
- Organization directory and org settings outside secret CRUD.
- Queue pause/resume, log-level, and other instance admin controls.
- Repo move/rename, bulk repair-all, binary log download.
- User token mint/delete (`/api/user/token`) so this connector cannot mint a new PAT.

There is no arbitrary HTTP method/path passthrough.

## Source provenance

Official latest stable release checked via the GitHub releases API: **v3.20.1**, released 29 Oct 2026. Linux amd64 binaries:

- `https://github.com/woodpecker-ci/woodpecker/releases/download/v3.20.1/woodpecker-server_linux_amd64.tar.gz` sha256 `82dd50755bb198943521fa30e788ddd20de8179126d5abf9cb5de43e506e45a2`
- `https://github.com/woodpecker-ci/woodpecker/releases/download/v3.20.1/woodpecker-agent_linux_amd64.tar.gz` sha256 `cb8cf9028cde5a8eaf8612e9aec59a3e8636c1b2ab8d339cfafb770ebba5e3d8`

Checksums from the release `checksums.txt`. Inventory from `server/router/api.go` plus `server/router/router.go` and `cmd/server/openapi/docs.go` at tag `v3.20.1`.

Pinned inventory hashes:

| File                         | sha256                                                             |
| ---------------------------- | ------------------------------------------------------------------ |
| `server/router/api.go`       | `a0ceb23e3786b40dac78e8da3cec8094793be3fefbf4e9e86a229d404511506e` |
| `server/router/router.go`    | `7a19e72290b7e97434cfc011ec81a3e7b4f3cdd631701d5b50daa0234fab7095` |
| `cmd/server/openapi/docs.go` | `67e4dc8475022c9d1f86f190e23f585be1d9027669d08d0f2c8103e9fce61919` |
| route tree                   | `2cb9ca460a98fca26d57af082c4c15f9120ef491fb59af68c2213f8084f43a81` |

## Other MCP servers

Read 11 Nov 2026. Pipeline trigger/cancel/restart/approve, cron, and secrets already exist in the strongest rivals. Those API operations are not unique here. Admin user/forge/agent coverage in [ni-c/woodpecker-ci-mcp](https://github.com/ni-c/woodpecker-ci-mcp) is broader; this connector excludes those on purpose.

| Server | Writes | Confirmation | Read-only switch | Notes |
| ------ | ------ | ------------ | ---------------- | ----- |
| [ni-c/woodpecker-ci-mcp](https://github.com/ni-c/woodpecker-ci-mcp) (~71 tools, MIT, TS, active 5 Nov 2026) | Full API including admin users, forges, agents, registries, queue pause, log level | Yes: MCP elicitation via `guarded()` in [`src/guard.ts`](https://raw.githubusercontent.com/ni-c/woodpecker-ci-mcp/main/src/guard.ts) lines 48–80, with two-call `confirm_token` fallback. `WOODPECKER_READ_ONLY` hides write tools ([`src/config.ts`](https://raw.githubusercontent.com/ni-c/woodpecker-ci-mcp/main/src/config.ts) lines 167–169, 19–20). | `WOODPECKER_READ_ONLY` | Strongest rival. `create_secret` writes on the first call ([`src/tools/secrets.ts`](https://raw.githubusercontent.com/ni-c/woodpecker-ci-mcp/main/src/tools/secrets.ts) 154–167, no `guarded()`). `trigger_pipeline`, `restart_pipeline`, `cancel_pipeline`, `decline_pipeline` also write immediately ([`src/tools/pipelines.ts`](https://raw.githubusercontent.com/ni-c/woodpecker-ci-mcp/main/src/tools/pipelines.ts) 287–300, 338–350, 374–381, 467–477). `approve_pipeline` and `delete_pipeline` are guarded (418–444, 505–525). `update_secret` is guarded when rotating `value` (276–311). |
| [KalvadTech/woodpecker-mcp](https://github.com/KalvadTech/woodpecker-mcp) (~55 tools, MIT, Python, active 9 Nov 2026) | Cron/secret CRUD, trigger, plus resources | README documents `MCP_READ_ONLY=true` hiding write tools. No `confirm:true` two-key gate in the README. | `MCP_READ_ONLY` | Per-request Bearer on HTTP. |
| [rtuszik/woodpecker-mcp](https://github.com/rtuszik/woodpecker-mcp) (~30 tools, Apache-2.0, Python) | Five pipeline writes | README: `WOODPECKER_MCP_READ_ONLY` removes the five write tools. No per-call confirm. | `WOODPECKER_MCP_READ_ONLY` | No repo activate/secret/cron writes. |
| [denysvitali/woodpecker-ci-mcp](https://github.com/denysvitali/woodpecker-ci-mcp) (8 tools, Go) | start/stop/approve | README lists the tools; no confirm gate described. | none described | |
| [j04n-f/woodpecker-mcp](https://github.com/j04n-f/woodpecker-mcp) (read-only, MIT, TS, last push Sep 2025) | none | n/a | read-only | Repos, pipelines, config, step logs. |

Honest differences that still hold after those reads:

- Every mutation, including trigger, cancel, restart, approve, cron run, repair, activate, log delete, and secret create/update, needs boolean `confirm: true` **and** `WOODPECKER_ALLOW_WRITES=true`.
- Omitted or false `confirm` always returns a method/path/query/body preview with zero HTTP writes. Secret `value` in that preview is `[redacted]`.
- ni-c already has elicitation plus `confirm_token` on a subset of writes, and `WOODPECKER_READ_ONLY`. It does not apply that guard to `create_secret` or `trigger_pipeline`. This connector's two-key gate is uniform; it is not a claim that ni-c lacks confirmation.
- Request-scoped redaction includes secret values supplied on create/update, percent-encoded any-case forms, and JSON-escaped forms. Nothing accumulates across requests.
- Route coverage is checked by the generator against a committed independent inventory. Public CI is offline.
- Six action-field toolsets instead of one tool per route.
- No automatic retries. Redirects are refused.

If a later rival adds the same two-key gate and dry-run preview on every write, that would no longer be unique.

## Limitations

- Admin user, forge, agent, registry, and queue pause/resume stay out of the tool list. Use Woodpecker's own UI or ni-c if you need those.
- Global secret actions require an admin token. A non-admin token gets HTTP 403 from Woodpecker.
- `GET /api/healthz` is empty 204. The connector wraps that as `{ ok: true, status: 204 }`.
- Live bad tokens from v3.20.1 are recorded in `LIVE-CHECK.md`. Unit tests still simulate 401, 422, and 429 with one fetch.
- The connector never starts or stops the user's Woodpecker. Isolated live scripts under `/workspace/tools/woodpecker` are throwaway only.
- A short credential (under 8 characters) that is spelled the same as an object key or a word in this connector's own preview envelope still appears there as that key or word. Values that contain it are redacted, and upstream free text is replaced. Use credentials of 8 or more characters.

## Live check

See `LIVE-CHECK.md` for the checks that actually ran, including first-start notes. `npm run live-check` is not part of `npm run ci`.

`npm run ci` typechecks, lints, tests, and builds. `node scripts/gen-operations.mjs --check` reports 53 mapped routes, 70 excluded, 53 tool actions, and 123 spec routes.

Run `unset DATABASE_URL`, set `WOODPECKER_URL`, and set `WOODPECKER_TOKEN` or `WOODPECKER_TOKEN_FILE`. Then run `npm run live-check`. The script never prints the token.

## License

MIT. Woodpecker itself is Apache-2.0. This connector only talks to the published REST API.

TDQS

B3.4/5.0

Scored across 6 tools

Disambiguation4/5

Each tool is scoped to a distinct Woodpecker resource (system, repos, pipelines, logs, cron, secrets), making most selections clear. Minor overlap exists around log/pipeline deletion and queue-related system actions versus pipeline listing, but descriptions provide enough guidance.

Naming Consistency5/5

All tool names follow a consistent woodpecker_<resource> snake_case convention. Actions within each tool use a predictable verb-style pattern such as list, get, create, update, delete.

Tool Count5/5

Six resource-grouped tools fit the scope well; each groups a coherent set of related operations rather than proliferating one tool per action. The count is appropriate for a CI server MCP surface.

Completeness4/5

Core CI lifecycle coverage is strong: repositories, pipelines, logs, cron, secrets, and system health are all represented. Some peripheral Woodpecker areas such as agents, registries, webhooks, or broader user/org administration appear absent but are not central to most workflows.

Maintenance

ActivityMaintained
ResponsivenessNo recent community issues