Skip to main content
Glama
DanZai233

image-provenance-mcp

by DanZai233

image-provenance-mcp

tests

Read the signature before guessing from pixels.

An MCP server for one question: is this image AI-made, edited, or real? It answers in two tiers, in the only order that makes sense:

  1. C2PA Content Credentials — signed, machine-verifiable provenance. When the file carries a manifest, that is the answer and we stop.

  2. Pixel heuristics — noise, ELA, spectral decay, colour, EXIF. Used only when tier 1 has nothing to say, and always labelled as a guess.

No API keys, no cloud calls, no telemetry. Tier 1 needs nothing but the Python standard library plus the openssl binary you already have.

Why the order matters

Running this on a ChatGPT (gpt-image) PNG:

signal

says

noise uniformity score 8.27

"natural-looking"

no camera EXIF, square-ish crop

"suspicious"

C2PA manifest

trainedAlgorithmicMedia, created by ChatGPT

The heuristics hedged both ways and would have produced a confident-sounding POSSIBLY AI-GENERATED. The manifest produced a fact. Same pipeline, opposite direction on a WeChat re-upload: the messenger strips EXIF, the heuristics fire META_NO_CAMERA plus uniform ELA, and you get an AI accusation built entirely from metadata that was removed in transit.

Heuristics describe artifacts; C2PA describes authorship. Do not let the first impersonate the second.

Related MCP server: c2pa-mcp

Tools

tool

tier

what it returns

image_provenance

1 → 2

the whole pipeline, tier 1 first; stubs out tier 2 if a manifest exists

c2pa_inspect

1

signed provenance only — faster, and works with zero pixel deps

full_forensic_report

2

all heuristic analysers plus a combined verdict

noise_map, error_level_analysis, frequency_analysis, spectral_decay_analysis, color_analysis, metadata_check

2

the individual analysers

Tier-2 tools are vendored from surewht/image-forensics-mcp (MIT — see NOTICE.md). They need the [pixel] extra; without it they simply are not registered and the C2PA tools keep working.

Verdicts (tier 1)

verdict

meaning

AI_GENERATED

the signer declares digitalSourceType=trainedAlgorithmicMedia — a fact about the file

EDITED

signed action history contains edited / opened / filtered / repackaged

CAPTURED

only capture-side actions were signed

INVALID_HASH

manifest present but pixels no longer match the signed hash — modified after signing

NO_C2PA

nothing signed in this file — says nothing about authenticity

Install

git clone https://github.com/DanZai233/image-provenance-mcp
cd image-provenance-mcp
pip install -e '.[pixel]'      # omit [pixel] for C2PA only

No network to a package index? Skip packaging entirely and run from the tree:

PYTHONPATH=src python -m provenance_mcp photo.jpg
PYTHONPATH=src python -m provenance_mcp --mcp        # stdio MCP server

Tier 1 needs only the standard library and openssl, so that works on a bare interpreter.

CLI

image-provenance photo.jpg              # tier 1, then tier 2 if needed
image-provenance photo.jpg --mode c2pa  # signature only
image-provenance photo.jpg --mode pixels
image-provenance photo.jpg --json       # parsed manifest as JSON
image-provenance --mcp                  # stdio MCP server

Exit codes: 0 a manifest was found, 1 no manifest, 2 usage error.

Wire it into an MCP client

Generic stdio config:

{
  "mcpServers": {
    "image-provenance": {
      "command": "image-provenance",
      "args": ["--mcp"]
    }
  }
}

DeepSeek Harness (~/.dsh/profiles/web/cordis.patch.yml):

- insert:
    - id: mcp-image-provenance
      name: '@deepseek-ai/dsh-mcp-client'
      config:
        serverName: image_provenance
        transport: stdio
        command: image-provenance
        args: ['--mcp']
        toolCallTimeoutMs: 180000

Check the wiring without any SDK — the probe speaks raw JSON-RPC over stdio:

python examples/probe_mcp.py image-provenance --mcp

Agent skill: forensic triage

The CLI answers "what does this file prove". The hard part is everything around it: deciding what to run first, which findings to trust, and how far the evidence can be pushed before the answer becomes a lie. That reasoning is packaged as a skill your agent can load.

skills/image-forensic-triage/
├── SKILL.md                          # the protocol: tier 1 → tier 2 → human layer → grade
├── references/
│   ├── evidence-grading.md           # A/B/C/D, and what each grade may NOT be used for
│   ├── false-positives.md            # seven failures observed in real files, with the numbers
│   └── worked-examples.md            # three cases end to end, including a wrong first answer
└── scripts/triage.py                 # batch ledger → markdown + json

Give it to an agent (Claude Code, DSH and anything else that reads ~/.dsh/skills or a repo skills/ folder) either in place or by symlink:

ln -s "$PWD/skills/image-forensic-triage" ~/.dsh/skills/image-forensic-triage

Then a plain question gets a graded answer instead of a guess:

python skills/image-forensic-triage/scripts/triage.py inbox/*.jpg
| file | size | dims | C2PA | notes | grade |
|---|---|---|---|---|---|
| `hmi.jpg` | 100 KB | 540×618 | none | annotation #ba1807×4630; EXIF stripped | C (vector annotation present — not the camera original) |
| `doc.jpg` | 461 KB | 1773×2364 | none | EXIF stripped; block-noise outlier ×40.49 | C (no EXIF; cannot exclude re-encoding) |
| `signed.png` | 1852 KB | 841×1870 | AI_GENERATED (OpenAI Media Service API) | block-noise outlier ×28.28 | A (signed, pixel hash verified) |

The script is the deterministic part; SKILL.md is what keeps an agent from reporting the middle row as "authentic" and the first row as "AI-generated".

What is actually verified

The test suite builds its own fixtures byte by byte (tests/fixture_builders.py); no photograph is committed. That proves the reader handles the structure, not that any particular vendor's output is genuine — for that, run it on a file you already trust.

test

result

synthetic manifest, bound to its pixels

AI_GENERATED from digitalSourceType, hash: ok, method carrier-removed

tamper: flip one byte inside IDAT, keep the manifest

INVALID_HASH; every strategy that can run produces the same wrong digest, so the mismatch is the content's fault, not the verifier's

PNG without a manifest

NO_C2PA, no crash

truncated file

NO_C2PA, no crash

real-world check (maintainer, out of tree)

a ChatGPT/gpt-image PNG read as AI_GENERATED with generator OpenAI Media Service API (c2pa-rs 0.79.2) and chain CN=OpenAI Media Service ← Trufo C2PA Claim Signing CA

27 unit tests

python -m unittest discover -s tests

Limitations (read these)

  • No cryptographic signature verification. This tool checks the sha256 hard binding and prints the COSE x5chain; it does not verify the signature, check revocation, or validate against a trust anchor. So INVALID_HASH is trustworthy (pure hashing), while "this chain is genuine" is currently only "this chain looks right". For the full check use c2patool or c2pa-python.

  • C2PA presence is not C2PA ubiquity. Most cameras, every messenger, every screenshot pipeline and every CDN re-encode strips manifests. NO_C2PA is the common case, not a red flag.

  • JPEG APP11 multi-segment reassembly follows the spec but has no end-to-end test with a real signed JPEG; only PNG manifests were validated here.

  • Old XMP-embedded c2pa fields (early Adobe style) are not parsed; only JUMBF.

  • Tier-2 thresholds are upstream's and were tuned on a small sample. Treat every tier-2 verdict as a hint.

  • Tier 2 needs numpy, scipy, pillow.

License

MIT for this project. Vendored tier-2 code is MIT, © 2026 surewht — see NOTICE.md and LICENSES-image-forensics-mcp.txt.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Enables reading and verifying C2PA Content Credentials from images and videos through both embedded manifests and invisible watermarks. Supports direct filesystem access, URL checking, and provides structured provenance data including creator identity, AI generation info, and content authenticity.
    2
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Verifies C2PA Content Credentials for local files or URLs and returns an LLM-ready verdict on trust, AI generation, and provenance.
    4
    11 npm
    2
    Apache 2.0
  • A
    license
    A
    quality
    D
    maintenance
    Provides MCP-compatible AI agents with a verify_image tool to check a photo's capture time and provenance using C2PA, EXIF, XMP metadata, and pixel forensics, returning a verdict and confidence score.
    1
    16 npm
    2
    MIT