An MCP server that verifies C2PA Content Credentials and returns an LLM-ready verdict.
Point any MCP client (Claude Desktop, Claude Code, Cursor, ...) at a local file or a URL and get back a plain-language answer: is this image/video/audio trusted, valid, tampered, or unsigned? Who signed it? Is it AI-generated? What's its edit history and provenance lineage?
Built by c2paviewer.com. Verification runs locally on the official C2PA Rust engine via @contentauth/c2pa-node. Files never leave your machine.
Read-only. This server verifies and inspects Content Credentials. It does not sign or create them.
No global install needed. Add it to your MCP client config and it runs via npx:
{
"mcpServers": {
"c2pa": {
"command": "npx",
"args": ["-y", "@c2paviewer/c2pa-mcp"]
}
}
}- Claude Desktop: Settings → Developer → Edit Config, add the block above.
- Claude Code:
claude mcp add c2pa -- npx -y @c2paviewer/c2pa-mcp - Cursor / others: add the same
mcpServersentry to the client's MCP config.
Requires Node.js 18+.
First run: the underlying engine (
@contentauth/c2pa-node) downloads a ~18MB native binary on first install. On a slow connection this can outlast an MCP client's startup timeout — if the very first launch seems to hang, let the download finish and relaunch; subsequent runs are instant.
| Tool | What it does |
|---|---|
verify_c2pa_file |
Verify a local image/video/audio/PDF by path. |
verify_c2pa_url |
Download a public https media URL and verify it (SSRF-guarded). |
scan_c2pa_directory |
Audit a folder: which files have credentials, their verdict, signer, AI status. |
c2pa_info |
Report engine version, supported media types, and trust-list status. |
Each verify tool returns a human-readable summary plus a structured digest:
Pass "includeRaw": true to also get the full raw manifest store.
To report a signer as trusted (not just cryptographically valid), the server checks the signing certificate against two trust lists, fetched live and cached (24h TTL) so decisions stay current without a release:
- the official C2PA Conformance Program list (going-forward signers), and
- the legacy CAI Interim Trust List (frozen Jan 2026, but still the only list that recognizes pre-conformance signers — Adobe, Leica, Truepic, Canon, Samsung, and most real-world content today).
Both are on by default; without the ITL, mainstream signed content would read as valid_untrusted.
If the trust lists can't be fetched, the server degrades loudly: verification still runs, but the verdict becomes valid_trust_unknown and trust.evaluated is false with a reason. It never silently treats an unknown signer as trusted, and never silently uses a stale snapshot.
Environment overrides:
| Variable | Default | Purpose |
|---|---|---|
C2PA_TRUST_LIST_URL |
conformance + ITL | Comma-separated PEM URLs. Replaces the defaults (conformance list + Interim Trust List). |
C2PA_TRUST_TTL_SECONDS |
86400 |
Cache lifetime for the fetched trust list. |
C2PA_MAX_FETCH_BYTES |
104857600 |
Max download size for verify_c2pa_url (100 MB). |
C2PA_MAX_FILE_BYTES |
524288000 |
Max size of a local file the file/scan tools will verify (500 MB). |
C2PA_ALLOWED_ROOTS |
(unset) | Comma/semicolon-separated absolute paths. When set, verify_c2pa_file and scan_c2pa_directory refuse any path outside these roots. |
- Local processing. Files are read and verified on your machine; nothing is uploaded.
- SSRF-guarded URL fetching.
verify_c2pa_urlallows only publichttps, pins the resolved IP against DNS rebinding, re-validates every redirect, sends no cookies or auth, and enforces a content-type allowlist plus size cap. - Local path safety. The file/scan tools reject remote
file://and UNC paths, resolve symlinks before the allowed-roots check, and return a generic error for inaccessible paths. - Set
C2PA_ALLOWED_ROOTSin any untrusted context. The file/scan tools expose the host filesystem by design, so a prompt-injected model could point them anywhere. Confining them to a specific directory is the single most important hardening setting. - Trust integrity. Trust lists are fetched over HTTPS (integrity rests on TLS and the upstream sources); the cache is per-user (
0700/0600, ownership-checked) and bound to the configured URL set.
- Experimental. Not legal evidence. C2PA tooling and trust infrastructure are still evolving. Do not rely on these verdicts for legal, compliance, or safety-critical decisions.
- Watermarks are reported as declared, not pixel-verified. A
synthidentry means the manifest declares a SynthID watermark; confirming the signal in the pixels requires the vendor's detector. - AI-generation reflects what the manifest declares via IPTC
digitalSourceType. Absence of an AI declaration is not proof the content is not AI-generated.
- Every verification fails with "Verification engine failed to load". The native
@contentauth/c2pa-nodebinary was interrupted or corrupted during install (common on slow/unreliable connections). Runc2pa_info— it reportsengine: FAILED: <reason>when this happens. Fix: reinstall the dependency (deletenode_modules/@contentauth/c2pa-nodeand reinstall, ornpm cache clean --forcethen re-run). Checksum/retry hardening of that download lives in the upstream@contentauth/c2pa-nodepostinstall script, which this project doesn't control. verify_c2pa_urlfails with "timed out after ...ms". The fetch exceededC2PA_FETCH_TIMEOUT_MS(default 30000). Raise it if you're verifying URLs from a slow host.
npm install
npm run build
npm test # builds, then runs unit + end-to-end tests (network needed for the trust list)Source code is dual-licensed under MIT or Apache-2.0, at your option.
The test images under test/fixtures/ are redistributed unmodified from c2pa-org/public-testfiles and are licensed separately under CC BY-SA 4.0.
Note: if you publish under an unscoped name instead of
@c2paviewer/c2pa-mcp, changenameinpackage.jsonand theargsin the install block above; nothing else depends on the package name.
{ "verdict": "invalid", // trusted | valid_untrusted | valid_trust_unknown | invalid | no_credentials "summary": "Content Credentials are INVALID: an integrity or signature check failed ...", "signer": { "name": "Example Signer", "trusted": false }, "aiGenerated": { "isAI": true, "tools": ["DALL-E"], "digitalSourceTypes": ["...trainedAlgorithmicMedia"] }, "provenance": [ { "depth": 0, "title": "This file", "relationship": "This file", "verdict": "invalid" } ], "edits": [ { "label": "Created", "agent": "Photoshop", "when": "...", "detail": "" } ], "watermarks": [ { "kind": "synthid", "assertionLabel": "...", "algorithm": "" } ], "issues": [ { "code": "assertion.dataHash.mismatch", "severity": "error", "explanation": "The media content was changed after it was signed. ..." } ], "trust": { "evaluated": true, "listSource": "https://.../C2PA-TRUST-LIST.pem" } }