@cookiecrumbs-eu/mcp
v0.7.1
Published
CookieCrumbs MCP server — let an AI coding agent read and configure your consent banner, sites, scans, services, alerts, webhooks and templates through audited two-step writes
Maintainers
Readme
@cookiecrumbs-eu/mcp
The CookieCrumbs Model Context Protocol server. It gives a coding agent read access to your consent banner, your hosted scans and your compliance issues — and, when the token carries a write scope, two-step writes that land in the audit trail as via MCP.
stdio only. Node 22+.
claude mcp add cookiecrumbs -- npx -y @cookiecrumbs-eu/mcp// .cursor/mcp.json
{
"mcpServers": {
"cookiecrumbs": {
"command": "npx",
"args": ["-y", "@cookiecrumbs-eu/mcp"],
"env": { "COOKIECRUMBS_TOKEN": "cc_live_…" }
}
}
}The token
The server reads the token from, in order:
COOKIECRUMBS_TOKEN;- the credentials file the CLI writes (
%APPDATA%\cookiecrumbs\credentials.jsonon Windows,~/.config/cookiecrumbs/credentials.jsonelsewhere) — one credential store, shared withnpx cookiecrumbs.
If neither exists the server still starts, and the first tool call answers with the two ways to fix it.
It does not run a device flow: a stdio server has no terminal to print a user code on and no browser
to open. Run npx cookiecrumbs login once, or create a machine token in the dashboard under
Developers → MCP, then restart the server.
Machine tokens can be restricted to one site and one environment. The server picks that up from
GET /v1/me and every tool then defaults to that site.
| Variable | Meaning |
|---|---|
| COOKIECRUMBS_TOKEN | cc_live_… bearer token |
| COOKIECRUMBS_API | gateway base (default: the hosted gateway) |
| MCP_CLIENT | overrides the client name in X-CookieCrumbs-Client: mcp/<client> |
| COOKIECRUMBS_CREDENTIALS | path to the CLI credentials file |
Tools, and the scopes they need
A tool is advertised only when the token carries one of its scopes. Hand an agent a read-only token and the write tools are not in its tool list at all — there is nothing for it to be tempted by.
| Tool | Kind | Scope |
|---|---|---|
| list_sites | read | sites:read |
| get_site | read | sites:read |
| get_banner | read | banner:read |
| get_compliance_status | read | banner:read |
| scan_site | read | scans:run |
| get_scan | read | scans:read |
| list_findings | read | scans:read |
| explain_classification | read | scans:read |
| check_first_layer | read | banner:read |
| rules_reference | read | — |
| get_declaration | read | banner:read |
| list_versions | read | banner:read |
| logs_summary | read | analytics:read |
| logs_export | read | logs:export |
| classify_tracker | write, two-step | banner:write |
| update_banner | write, two-step | banner:write |
| push_config | write, two-step | banner:publish or banner:write |
| rollback_version | write, two-step | banner:publish |
The whole site (src/tools-config.ts)
Everything the dashboard can change, an agent can change too — and nothing more (team, billing and tokens stay in the dashboard). Same rules: scope-gated, two-step, audited.
| Tool | Kind | Scope |
|---|---|---|
| create_site | write, two-step | sites:write |
| update_site — name, retention, settings | write, two-step | sites:write |
| list_domains | read | sites:read |
| verify_domain — re-issues the DNS TXT / meta token | write, two-step | sites:write |
| list_scans · get_scan_diff · get_scan_schedule · get_install_status | read | scans:read |
| set_scan_schedule — cadence, pages, states, URLs, patterns, robots, pause | write, two-step | scans:run |
| check_install — queue an install check, optionally wait | read | scans:run |
| list_alerts | read | scans:read |
| acknowledge_alert · resolve_alert | write, two-step | scans:run |
| list_services | read | banner:read |
| add_service · update_service · delete_service (destructive) | write, two-step | banner:write |
| suppress_issue (reason required) · unsuppress_issue | write, two-step | banner:write |
| promote_version — preview → production, diff first | write, two-step | banner:publish |
| list_templates · get_template | read | banner:read |
| apply_template · save_template · delete_template (destructive) | write, two-step | banner:write |
| list_webhooks · list_alert_channels · get_usage · list_export_destinations | read | sites:read |
| create_webhook (secret shown once) · update_webhook · delete_webhook (destructive) | write, two-step | sites:write |
| create_alert_channel · update_alert_channel · delete_alert_channel (destructive) | write, two-step | sites:write |
| list_export_schedules | read | logs:export |
| create_export_schedule · update_export_schedule · delete_export_schedule (destructive) | write, two-step | logs:export |
Site, service, template, webhook and channel arguments accept an id or a name (a domain for
sites, a URL for webhooks); the tool says what it matched. Deletes carry destructiveHint so an
agent host can ask first.
The table lives in src/matrix.ts. src/tools.ts and src/tools-config.ts build
their tool definitions from it and the dashboard renders it, so the three can never disagree.
Resources
cookiecrumbs://sites/<id>/config— the draft configuration with its legal lintcookiecrumbs://sites/<id>/declaration— the current cookie declaration as Markdowncookiecrumbs://sites/<id>/issues— open compliance issues with their rules
<id> accepts a site id, a primary domain or a site name.
The rules the server keeps
Every write is two-step. The first call — confirm: false, which is the default — returns a
unified diff of what would change (plus the legal lint where a banner config is involved), and
changes nothing. Only confirm: true writes. A confirmed push_config also requires a note, which
goes into the version and into the audit trail. Plan limits answer with the feature they need (402),
and a value the plan clamped (a daily scan cadence on a monthly plan) is reported in the answer,
never silently applied.
Nothing subject-level leaves the API. logs_export returns an export id and the signed download
URL. It never streams consent records into a conversation. logs_summary reads daily roll-ups only.
Classifications are quoted, not guessed. explain_classification answers with the
tracker_patterns row that matched, the tracker_db entry behind it — including its source and
its licence — and the regime rule that follows from the category. If the finding is unclassified it
says exactly that, and offers nothing else.
Citations are real or absent. The rules reference (@cookiecrumbs/config/rules) carries verbatim
wording only where it was verified against the primary source; everything else carries a summary in our
own words and quote: null. Nothing the server returns is a legal assessment, and every citation block
says so.
Every request is attributable. X-CookieCrumbs-Client: mcp/<client> goes out on every call — the
client name comes from MCP_CLIENT or from the MCP initialize params — so the gateway records
channel = 'mcp' and the audit row reads via MCP · <token name>.
Example
> why is _fbp classified as Marketing on e2e.cookiecrumbs.test?
# http_cookie `_fbp` on e2e.cookiecrumbs.test (first party)
## Category: marketing
Decided by: service (classification_source `service`, confidence 1)
## The tracker database row this came from
provider: Meta Pixel (facebook.com)
source: cookiecrumbs
licence: CC0
Consent Mode: ad_storage, ad_user_data, ad_personalization
## The pattern that matched
tracker_patterns row …: kind=cookie_name, pattern=`_fbp`, match_type=exact
## The regime rule applied
Under an opt-in regime … nothing in category "marketing" may be stored or read before
the visitor gives consent …
Citations:
• Directive 2002/58/EC (ePrivacy Directive), Article 5(3) — “Member States shall ensure that …”
• EDPB, Cookie Banner Taskforce report (para. 24) — “… the legal basis for the placement/reading
of cookies pursuant to Article 5 (3) cannot be the legitimate interests of the controller.”
This is not a legal assessment.Known gaps
classify_trackerwithconfirm: truecallsPATCH /v1/services/:id. Where a gateway does not expose that route yet, the dry run still works and the confirmed call answers with what to do instead of a raw error.logs_summarycallsGET /v1/sites/:id/analytics/dailyand needs theanalytics:readscope. Where the route or the scope is not deployed, the tool is simply not advertised, or answers that nothing was read.
Development
npm run build -w @cookiecrumbs-eu/mcp # tsc → dist/mcp/src/index.js (the bin)
npm test -w @cookiecrumbs-eu/mcp # tool registration by scope, diff rendering, error mapping