Skarp CRA
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Skarp CRAClassify our smart thermostat under the CRA"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Skarp CRA
An MCP server that helps software makers meet EU Cyber Resilience Act (Regulation 2024/2847) obligations from inside their coding agent: scope classification, the Article 13(2) risk assessment, Annex I requirements with versioned evidence, vulnerability and incident records with their Article 14 reporting clocks, and the Annex VII technical file.
Not a regulation-lookup tool. It does not quote the CRA at you; it helps you produce and maintain the artefacts that evidence compliance, in the order the regulation actually requires them.
A working aid, not a compliance determination. This server records what you have decided and evidenced, and shows what is missing. It cannot certify that a product is compliant, and it does not replace legal review or a notified body where the product class requires one.
Hosted service
Nothing to install. Add it to Claude, Codex or another MCP client as a connector:
https://cra.skarp.app/mcp/me/mcpYou are asked for an email address and sent a six-digit code — no password, no token to copy. Setup and the first three things to ask it · what each tool does, against the obligation it serves · pricing
One product is free, with unlimited colleagues, and free covers the part with a deadline: classification, the Article 13(2) assessment, the Annex I and Annex II checklists with evidence, daily SBOM scanning, the Article 14 reporting clocks and the Annex VII gap report. You can export everything you have recorded at any time, on any plan.
This repository is the source of that service — read it, self-host it, or send a patch. Everything below is for that.
Related MCP server: ai-economy-infrastructure
Compliance timeline
Date | What binds |
11 Sept 2026 | Reporting begins. Actively exploited vulnerabilities and severe incidents: 24h early warning, 72h full notification, 14 days final report (1 month for severe incidents), via ENISA's Single Reporting Platform to the relevant CSIRT. |
11 Dec 2027 | Full application: secure-by-design, vulnerability handling, technical documentation, conformity assessment, EU Declaration of Conformity, CE marking, ≥5-year support period. |
The clocks start when the manufacturer becomes aware — which is why deadline tracking, rather than document drafting, is this tool's centre of gravity.
The reporting duty arrives fifteen months before the obligation that makes it survivable. Article 71 applies the Regulation from 11 December 2027, "however, Article 14 shall apply from 11 September 2026" — so from this September a manufacturer owes a 24-hour notification, while Annex I Part II, which requires a vulnerability handling process, does not bind until the following December. Anyone who waits for the second date spends fifteen months answering statutory clocks with nothing behind them.
Part II is the eight requirements the process has to satisfy — a bill of
materials, remediation without delay, regular testing, public disclosure of
fixed vulnerabilities, a coordinated disclosure policy, a contact address for
reports, and secure, free distribution of updates. This tool operates the
first two and records the rest against them: record_sbom and the nightly scan
maintain the component picture, record_vulnerability → update_vulnerability
carries a flaw to a corrective measure with its clocks attached, and
set_submitter_profile holds the disclosure policy and contact address that
II(5) and II(6) ask for. Publishing advisories for fixed vulnerabilities —
II(4) — is not built yet and is tracked as an open issue rather than implied by
the list above.
Self-hosting
The hosted service is cra.skarp.app — see the top of this file. To run your own:
./scripts/dev_up.sh # venv, Postgres, migrations
.venv/bin/python scripts/demo.py # the whole product, narratedQUICKSTART.md covers connecting your own agent, including
scripts/dev_token.py for a local credential.
deploy/ has the Dockerfile, the compose example and the scripts a real
deployment needs.
The MCP endpoint is POST /mcp/me/mcp with Authorization: Bearer cra_…, or
/mcp/{product_id}/mcp for a token scoped to one product. Configure
DATABASE_URL for anything but local development — see Limitations.
Tools
47 tools. Grouped by the obligation they serve:
Scope —
classify_productagainst the Annex III/IV class lists,get_applicable_csirt,record_sbom,set_support_periodwith the Article 13(8) five-year floorRisk —
start_risk_assessment→propose_risks→decide_risk→confirm_risk_assessment, the Article 13(2) assessment everything else rests onRequirements — the Annex I checklist (22 requirements) and the Annex II user information (15 items), with
attach_evidencestoring artefacts by value against the release they evidenceDetection —
scan_advisoriesover your SBOM against OSV, CISA KEV and EPSS, plus a nightly sweep;confirm_advisory/dismiss_advisoryReporting —
record_vulnerability→update_vulnerability→report_incident→get_reporting_deadlines→record_report_submission, withdraft_reportrendering ENISA's own field layoutConformity —
record_build(free) records that a version exists;place_on_marketis the legal act that starts the Article 13(13) clock, and the two are separate on purpose. Thenassemble_technical_file,generate_declaration_of_conformity,sign_offYours to take —
export_productreturns everything held about a product, anddelete_productremoves one that was never placed on the market. Both free, and the first will stay that way: a paywall on getting your own data out would make every other promise here conditional on a subscription
The regulation itself is versioned data in
src/cra/regulation/ — Annex I Parts I and II, Annex
II, the Annex III/IV classes, the Annex VII slots and the Annex V declaration
fields — each entry carrying its own provenance.
Design decisions
Most of the design exists to prevent one failure: a technical file that looks considered and is not. These are the load-bearing decisions.
Requirement applicability
confirm_risk_assessment marks the requirements that accepted risks name as
applicable, and leaves everything else undetermined — which still reads as
a gap. It never writes not_applicable.
That flag reads as a considered decision, and "the model did not mention it" is not a justification. An AI-drafted assessment quietly dismissing two thirds of Annex I Part I is the exact failure worth designing against. Ruling a requirement out stays a deliberate act with reasoning attached, and a requirement named by a standing accepted risk cannot be ruled out at all.
Risk assessment workflow
The model invoking this connector is already sitting in the repository with the code, the dependency manifest and the deployment topology — the material a risk assessment is actually made from. A server-side model would see four free-text fields.
So propose_risks takes the agent's draft, records which model wrote it,
and determines nothing. Only decide_risk — a separately recorded act with a
mandatory rationale — moves a risk to accepted, and only accepted risks touch
the checklist. There is deliberately no path that accepts and applies in one
call.
Reporting clock anchoring
Article 14 counts from when the manufacturer became aware, so
record_vulnerability and update_vulnerability both take became_aware_at
and the cascade anchors on it.
They used to anchor on the moment of the call, which meant a team that learned of exploitation on Friday and logged it on Monday got a 24-hour clock starting Monday — reported as comfortably on time while roughly two days late. Omitting the anchor still falls back to now, because the tool cannot know, but it says so and points at the correction.
Its mirror: a clock that has not started shows no deadline. The final report's 14 days run from when a corrective measure becomes available, which has usually not happened when the incident is recorded. That obligation is materialised later, when the anchor arrives. Showing a fabricated due date would be worse than showing none.
Advisory matching
advisories.py is the only code here that can produce awareness rather than
record it — and awareness starts a 24-hour clock, on the strength of a
version-range match that may be wrong: vendored patches, unreachable code,
over-broad affected ranges.
So a scan writes candidate rows and stops. It never opens an incident. A person confirms, and the clocks anchor on when the tool notified them rather than when they got round to agreeing. A person dismisses with a VEX justification, which is itself Annex I Pt II(2) evidence of vulnerability handling rather than an absence of it.
Three ways to report an absence of knowledge as knowledge of absence, all
closed: an unreachable feed reports sources_ok: false rather than an empty
result; components with no version or an unsupported ecosystem are counted and
named; and absence from CISA KEV is never rendered as "not exploited", because
KEV is high-precision and lags real exploitation.
Scanning and legal awareness
A reasonable objection: Article 14's clocks run from awareness, scanning produces awareness, so not looking can seem like the safer option. It is not, on three separate grounds.
Not looking is itself a breach. Article 13(5): "manufacturers shall exercise due diligence when integrating components sourced from third parties so that those components do not compromise the cybersecurity of the product, including when integrating components of free and open-source software". Annex I Pt II(1) requires you to "identify and document vulnerabilities and components", including a bill of materials. The Regulation does not merely permit looking; it requires it. Not scanning trades one obligation for another and keeps both.
A scan does not make you aware in Article 14's sense. The duty is to notify "any actively exploited vulnerability contained in the product with digital elements that it becomes aware of". A feed match is not that: version ranges over-match, vendored patches are invisible to them, and the affected code may not be reachable in your build. It is a question about whether you are affected, and the answer is a determination only a person can make. That is the whole reason a scan writes candidates and stops — the design is not squeamishness about clocks, it is that a match and an awareness are different things.
And you will find out anyway, later and worse. A researcher emails you, a customer forwards a CISA bulletin, an upstream maintainer publishes. Awareness arrives on somebody else's schedule, the 24-hour clock starts then regardless, and you have no contemporaneous record of when you learned. The manufacturer who scanned and ruled a candidate out with a VEX justification has evidence of diligence dated before the incident; the one who never looked has no answer to "when did you know?".
EPSS scoring
EPSS scores how likely a CVE is to be exploited in the next 30 days — close enough to Article 3(41)'s potential to be effectively used to be genuinely useful, and close enough to look like an answer.
So: no threshold anywhere a user can see, because any cutoff is a compliance policy rather than a fact. A missing score is unscored, never low. Both probability and percentile are always shown, because 5% reads as negligible and can be the 92nd percentile. And EPSS can never be a dismissal on its own — the VEX justification is a statement about your product and stays required.
Both feeds are mirrored whole rather than queried per-CVE. Asking a scoring API about a customer's CVEs would disclose exactly what the mirror exists to avoid.
Derived state
Deadline status, risk-assessment staleness and evidence currency are all
computed on read. Persisting an overdue boolean flipped by a cron would let a
sweeper outage silently mark a user compliant.
Evidence currency is the subtlest: Annex I attaches to the product as placed
on the market, so evidence records which release it proves something about.
Three verdicts, and the distinction between the last two carries weight —
current, stale (covers only superseded releases; counts as a gap), and
unversioned (evidence predating the column; reported, not a gap). Calling
those stale would have turned every requirement in every account into a gap on
deploy, on the strength of something nobody had checked.
Evidence storage
attach_evidence demands the artefact itself plus a source_ref — a git SHA,
a CI run URL, a tool and version — and stores it by value with a SHA-256. A
link evidences nothing in ten years, which is how long the file is retained.
Audit trail
Under the CRA the technical file is retained ten years and the trail is what
evidences every change to it. So a failed audit write fails the tool call
(errors.AuditWriteFailed), and the state write and the audit insert commit in
one transaction. That is why the state blob lives in products.state JSONB
behind SELECT … FOR UPDATE rather than in a document store.
Reporting unknown results
When deadlines cannot be read — no database configured — get_compliance_status
returns open_obligations: null with an explicit unavailable note, never an
empty list. An agent must not be able to say "nothing is due" on the strength
of a missing connection string.
The same rule governs the paywall. A refusal names what the tool would have done and says the plan does not cover it; a free account that cannot reach the reporting tools has not been told its product is fine.
Regulation data and provenance
The catalogue was reconciled against the published text on 2026-08-06 (CELEX 32024R2847, via the Publications Office).
It found the summaries faithful and every Annex I Part I anchor wrong. Annex I Part I is (1), then (2)(a)-(m) under one chapeau; the catalogue had (1), (2), then (3)(a)-(l) — a point that does not exist, and every letter shifted by one.
Nothing in the checklist was missing or invented, because each summary described a real requirement. What was wrong was the citation attached to it — the part an auditor reads, and the part that lands in Annex VII(3). A technical file frozen before this cited provisions that are not in the regulation.
That the numbering survived that long is the argument for the discipline:
every tool had been returning source_verified: false to its caller the whole
time. provenance() reports the oldest verification date across the four
catalogue files, so a freshly transcribed annex cannot make an older one look
re-checked, and its caveat is never null in either state. Verification means
the anchors exist and the paraphrases are faithful. It does not make them the
text of the law, and Article 7(4) delegated acts can amend the annexes.
Limitations
Without a database the file backend is used, and it is not transactional. It cannot commit a state write and its audit row together, so a crash between them loses the record of who changed what.
CRA_STOREfollowsDATABASE_URLunless set explicitly, and the fallback logs a warning saying so — but the backend still exists and is still selectable.mcpis pinned<2. 2.0.0 removedFastMCPfor a restructuredmcpservermodule. Migrating is a scoped task nobody has done.A suppressed alert is never retried. Fixing a missing sender does not re-send what was suppressed while it was unset; the next escalation rung fires normally. Treat suppression rows as an incident, not a queue that drains itself.
There is no organisation object. A product has members; nothing sits above them, and plans hang off individual users.
Severe incidents are not detectable by any feed — no public source knows what is happening to your product in operation. The leverage there is
check_reporting_readinessbeforehand and fast capture during.
Development
.venv/bin/python -m pytest -q # most pass; the DB-backed ones skip
DATABASE_URL=postgresql+psycopg://… \
.venv/bin/python -m pytest -q # the skipped ones run tooDATABASE_URL alone is enough. It did not use to be: the dispatcher read
through store_backend, which defaulted to file regardless, so ~80 tests
failed on state the integration fixtures had seeded into Postgres — which read
as a broken suite rather than a missing second variable. The default now
follows DATABASE_URL, so the trap is gone rather than documented.
./scripts/dev_up.sh prints the URL to export.
The skips without a database are the _NEEDS_DB pattern, not a broken setup —
the count moves as tests are added, so it is not written down here.
The module docstrings carry the invariants above in more detail, the reasoning
behind them, and the things you can break silently. They are long on purpose:
risk.py, annex.py, advisories.py, releases.py and conformity.py each
open with why the domain works the way it does before any code runs.
Contributing
Welcome, and not expected — this works whether or not anyone sends a patch. CONTRIBUTING.md has the detail; two things specific to this project are worth knowing before you start:
A change to src/cra/regulation/ is a claim about the law. Cite the
provision, and do not set source_verified by inspection — fetch the text.
eur-lex.europa.eu sits behind a bot challenge that returns HTTP 202 with an
empty body, which reads as an empty document rather than a refusal; use
https://publications.europa.eu/resource/celex/32024R2847 instead.
New tools must be classified. Every tool belongs to _READ or _MUTATING,
and to exactly one of _REQUIRES or _FREE in
src/cra/agents/dispatch.py. A test sweeps the
registration tables, so a new tool fails the suite until someone decides which
side it is on. That is the point, not an obstacle.
Contributions are accepted under a permissive grant, not the AGPL. CLA.md is derived from the Apache ICLA and lets the project sublicense your work — including under a paid commercial licence, which is how this is funded. You keep copyright in what you write.
That asymmetry is deliberate and worth deciding about rather than clicking through: CONTRIBUTING.md explains why it exists and what the alternative would cost. If you would rather not, a bug report carries no agreement and is usually worth more than a patch here.
Security
See SECURITY.md. Please report vulnerabilities to security@skarp.app rather than opening a public issue.
Worth knowing before you test: accounts using the reporting tools store unreported vulnerability records, including ones marked actively exploited, before those are filed with a CSIRT. Anything crossing a product or account boundary is treated as the most severe class.
Licence
GNU Affero General Public License v3.0 only.
If you run a modified version of this as a network service, the AGPL requires you to offer its source to your users. That is the intended effect: self-host it, change it, and build on it freely — but a hosted fork stays open.
The regulation catalogue in src/cra/regulation/ is data transcribed from
Regulation (EU) 2024/2847. EU legislation is not subject to copyright; the
paraphrases and the structure around it are covered by the licence above.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Alicense-qualityBmaintenanceMCP server for compliance automation of AI agents, enabling EU AI Act compliance, verifiable credentials, and decentralized identity management with 47 tools across 9 modules.17Apache 2.0
- AlicenseAqualityCmaintenanceAn MCP server for AI economy infrastructure with built-in EU AI Act compliance, supporting risk management, transparency, and bias detection.10MIT
- AlicenseAqualityDmaintenanceMCP server for EU AI Act compliance, providing risk classification of AI features and Article 50 transparency notices.21Apache 2.0
- AlicenseAqualityAmaintenanceAn MCP server providing structured intelligence about the EU AI Act, including risk classification, deadline tracking, obligations, penalties, and article lookups.92603MIT
Related MCP Connectors
MEOK CRA Article 14 Reporter MCP — actively-exploited-vulnerability notification with 24h/72h/14d
Conformance checker for MCP servers. Free, no key, verdicts recomputable and re-measured daily.
MCP server providing access to the Scorecard API to evaluate and optimize LLM systems.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ozzynxe/cra'
If you have feedback or need assistance with the MCP directory API, please join our Discord server