ChronoVerify

Use ChronoVerify with LangChain and LangGraph

Load ChronoVerify as native LangChain tools with langchain-mcp-adapters pointed at the published MCP server. Your agent gets verify_image and get_signed_report with no glue code.

When your agent should call ChronoVerify

Call it before you trust or act on any user-submitted or sourced image. Strong triggers:

Branch on the verdict and confidence it returns; treat anything below your bar as needing human review.

Install

pip install langchain-mcp-adapters

Load the ChronoVerify tools

Point a MultiServerMCPClient at the published MCP server and you get verify_image and get_signed_report as native LangChain tools, with no wrapper to write.

from langchain_mcp_adapters.client import MultiServerMCPClient

client = MultiServerMCPClient({
    "chronoverify": {
        "command": "npx",
        "args": ["-y", "chronoverify-mcp"],
        "transport": "stdio",
        # "env": {"CHRONOVERIFY_API_KEY": "cv_live_..."},  # optional
    },
})
tools = await client.get_tools()

On Windows, an npx stdio server sometimes needs "command": "npx.cmd" or to be wrapped via cmd /c.

Raw HTTP, no MCP

If you cannot run the npx stdio server (for example a hosted backend with no Node), wrap a direct call to the API as a StructuredTool. The MCP path above is the recommended default.

import base64, io, requests
from langchain_core.tools import StructuredTool

def _verify(url: str | None = None, image_base64: str | None = None) -> dict:
    headers = {}  # add "Authorization": "Bearer cv_live_..." for metered use
    if url:
        r = requests.post("https://chronoverify.com/v1/verify", data={"url": url}, headers=headers)
    else:
        blob = io.BytesIO(base64.b64decode(image_base64))
        r = requests.post("https://chronoverify.com/v1/verify", files={"file": blob}, headers=headers)
    r.raise_for_status()
    return r.json()

verify_image = StructuredTool.from_function(
    func=_verify,
    name="verify_image",
    description=("Verify a photo's capture time and provenance: C2PA validation, "
                 "EXIF/XMP, pixel forensics. Provenance-first, not a deepfake detector."),
)

What comes back

The response is one JSON object, the same in the browser and the API. The verdict is one of provenance_confirmed, consistent, inconclusive, metadata_anomaly, or manipulation_indicated.

{
  "schema_version": "v1",
  "verdict": "consistent",
  "confidence": 61,
  "headline": "Metadata is internally consistent. No manipulation signals fired.",
  "capture_time": {
    "value": "2026-05-18T14:32:10",
    "source": "exif",
    "consistent": null
  },
  "capture_device": {
    "make": "Canon",
    "model": "EOS R6",
    "software": "Firmware 1.8.1"
  },
  "c2pa": {
    "present": false,
    "validated": null,
    "validation_state": null,
    "signer": null
  },
  "integrity": {
    "sha256": "1313339a...",
    "sha512": "93a81e4a...",
    "format": "JPEG"
  }
}

Full field reference, including the C2PA validation state and signer, is on the method and API page and in /openapi.json.

What a verdict tells you

ChronoVerify returns the photo's capture time, the capture device, the validated provenance state, and the file hashes, as a typed verdict with a confidence. It is not a deepfake or AI-generation detector, and a verdict is investigative triage, not proof: a clean result means a file's saved data is internally consistent, not that the scene it shows is real. Never use a verdict as the sole basis for an automated decision about a person.

Common questions

Do I need to write a custom tool?

No. langchain-mcp-adapters turns the published MCP server into LangChain tools automatically.

Is a key required?

Optional. Add an env block with CHRONOVERIFY_API_KEY for metered use, or omit it for the free public path.

Is ChronoVerify C2PA conformant?

Yes. ChronoVerify is a C2PA Conformant Validator on the C2PA Conforming Products List, record 019f8a20-6452-7a43-b11b-59d0b0e4a84a, covering validation of JPEG, PNG, WebP and AVIF under C2PA specification 2.2. The list is public, so you can check the record yourself. It covers validation rather than generation: ChronoVerify reads and validates Content Credentials, it does not sign them.

See what it returns on one of your own photos.

Try the free verifier