Back to the blog

Sellat MCP Server: Proof of Existence for AI Agents

Six months later

An AI agent writes the condition report for a flat, saves it as a PDF and sends it to the landlord. Nobody reads it closely; that is what the agent is for. Six months later the tenant disputes the deposit and claims the report was written after the keys were handed back.

How do you prove otherwise? With a log on the agent’s server, which anyone with access can edit. With the PDF’s modification date, which changes the moment you copy it. And with the word of whoever commissioned it.

Nothing a third party can check on their own.

This problem already existed when people made the documents. What changes with agents is the volume, and that there is no longer anyone in the middle who can vouch for anything. The agent has to be able to leave the proof itself, at the moment, without a human copying the file anywhere.

What MCP is

MCP (Model Context Protocol) is an open standard that lets an AI model use outside tools: read a file, query a database, call a service. Anthropic published it in late 2024. Today it is spoken by Claude, ChatGPT, Cursor, Gemini CLI and nearly any AI client you will come across.

Before, every AI application had to build its own integration with every service. With MCP the service publishes a server that describes what it can do, in plain language, and the model decides when to use it. It is an API written for a model to read. If you have written APIs for programmers, the difference is obvious straight away: here the documentation is part of the protocol, because whoever reads it is the one making the decisions.

If you don’t code: with the server connected, the agent can call Sellat on its own, in the middle of a task. You don’t copy or paste anything.

A tool that leaves a mark outside

Almost every MCP tool does things inside the system: it searches, calculates, writes somewhere. A timestamp leaves a mark outside, somewhere neither the agent nor we can touch afterwards.

The file never leaves the machine. The server computes the SHA-256 fingerprint locally and sends only that. Sellat batches that fingerprint with others in a Merkle tree and anchors the root on Polygon and, through OpenTimestamps, on Bitcoin. On request, the FNMT-RCM also issues a qualified timestamp (eIDAS, RFC 3161) over the same fingerprint.

What you are left with is proof that those exact bytes existed no later than the anchored date. And it can be checked against a public node without trusting Sellat, which is the point. Back to the flat report: if the agent stamped it when it generated it, the “written afterwards” claim collapses with a one-minute verification. Lying is still possible. What it no longer is, is cheap.

There is something else I find more interesting than stamping, though it gets less attention: the agent can also verify. Before working with a document it receives, it can check that it matches the evidence package that comes with it. An agent that works with proof instead of claims.

How to connect it

I published the server as open source (MIT): skanthemore/sellat-mcp. It runs on your computer. Sellat receives the fingerprint, the name and the size that appear on the certificate, and nothing else. Under the hood it uses sellat-cli and sellat-verify, the verifier that works offline. I have it running in my own GPT.

[Cristian: two sentences of your own on why you built it. What you got tired of doing by hand, or what you wanted to test. If there is nothing, delete this block.]

In Claude Desktop it is one click: download sellat.mcpb from the latest release and open it. It brings its own Node.js, there is nothing else to install. Paste your API key when it asks; you create one for free from the Sellat dashboard. Without a key you can find files, compute fingerprints and verify. Stamping needs an account.

In Claude Code, Cursor, Gemini CLI and the other clients you need Node.js 18 or later:

claude mcp add sellat -e SELLAT_API_TOKEN=sellat_… -- npx -y sellat-mcp

Or in the client’s MCP config file (.cursor/mcp.json, claude_desktop_config.json…):

{
  "mcpServers": {
    "sellat": {
      "command": "npx",
      "args": ["-y", "sellat-mcp"],
      "env": { "SELLAT_API_TOKEN": "sellat_…" }
    }
  }
}

From there, you just talk:

“Protect the contract I downloaded yesterday with Sellat.”

“Has the proof of my last photo reached Bitcoin yet?”

“Check this file against the evidence package in my Downloads.”

A file you drag into the chat goes to the model, not to the server: say where it is saved, or let the agent find it. These are the tools it has at its disposal:

ToolWhat it doesKey
sellat_findfinds a file by name in Downloads, Desktop, Documents and Pictures; names only, never contentsno
sellat_hashcomputes the SHA-256 fingerprint of a local fileno
sellat_stampprotects a file: hashes locally and creates the proof; qualified: true adds the FNMT sealyes
sellat_statusstate of a proof: Polygon anchor, Bitcoin, seal and linksyes
sellat_listthe account’s proofs, newest firstyes
sellat_sealFNMT-RCM qualified timestamp on an existing proof; spends one sealyes
sellat_accountplan, seals left and today’s proof budgetyes
sellat_downloadsaves the PDF certificate, the evidence package (ZIP) or the seal’s .tsr; never overwritesyes
sellat_verifychecks a file against a proof.json, an evidence ZIP or a proof id: recomputes the Merkle path locally and checks the anchor on a public Polygon nodeno

Some design decisions only make sense because the caller is an agent and not a person:

  • Stamping the same file twice returns the first proof (the earlier date is the stronger one), and a retried call never creates a second proof.
  • Nothing is ever paid through the MCP server. Protecting files is free within the account’s daily budget; the qualified seal spends a seal (new accounts come with a welcome one) and, when none are left, the tool returns the link to buy a pack on the website. The agent is instructed to request a seal only when you have asked for one.
  • The server tells the model the real timing: Polygon within minutes or at the end of a four-hour slot (00, 04, 08, 12, 16 and 20 h UTC) depending on the plan, Bitcoin some hours later, the qualified seal at once. So the agent does not promise a blockchain transaction in seconds.

What it proves and what it doesn’t

A Sellat stamp proves one thing: that those exact bytes existed no later than the anchor date. It does not prove who made them or who owns them. That comes from the signature, the context or the case file it is kept in, and it is worth being clear about it before someone argues the point.

The anchor on Polygon and Bitcoin is the technical layer: anyone can check it, in any country, with public tools. The qualified seal is the European legal layer: a qualified electronic timestamp issued by the FNMT-RCM, a qualified trust service provider under eIDAS. Sellat is not that provider; it requests the seal and keeps it next to the proof. The two dates are always shown separately. Requesting the qualified seal later does not backdate anything, and I don’t want anyone leaving with a different impression.

sellat_verify checks the file, the Merkle path and the anchor, and reads the block time straight from the chain. That is the only date it states as proven. The qualified seal’s .tsr can be verified with any RFC 3161 tool; openssl ts -verify will do.

[Cristian: something concrete that happened while building or testing it. A bug, a surprise, something you didn’t expect. One or two sentences. If there is nothing, delete this block.]

If your agent generates, captures or sends documents that someone will want to argue about one day, give it the tool to leave proof. It is one line of configuration.

Further reading: the server on GitHub · Sellat API documentation · modelcontextprotocol.io

Do you have a file to protect?

Sellat creates a verifiable proof of the exact version of your file at the moment you protect it.

Protect files