Skip to main content
Glama
README.md
# @ledgerproof/mcp-server

A [Model Context Protocol](https://modelcontextprotocol.io) server that lets any
MCP client (Claude Desktop, Claude Code, IDEs, agents) issue and verify
**EU AI Act Article 50 transparency receipts** through
[`@ledgerproof/sdk`](../typescript).

LedgerProof **implements the IETF SCITT architecture drafts**
(`draft-ietf-scitt-architecture-22`) with **COSE Receipts**
(`draft-ietf-cose-merkle-tree-proofs-18`), and additionally anchors a daily
Merkle root to **Bitcoin** (OP_RETURN). It is an early implementation of live
IETF Internet-Drafts — **not an RFC, not a standard, not certified, not "SCITT
compliant."** That precision is deliberate; please keep it in any derived copy.

> Issuing a receipt is a real, deliberate action: each receipt's daily Merkle
> root is anchored to Bitcoin, which **costs a Bitcoin transaction fee**. Do not
> wire agents to auto-issue on loose triggers. The bundled
> `ledgerproof_when_to_issue` prompt spells out when issuance is (and isn't)
> appropriate.

## Transports

This server speaks the two **current** MCP transports. The deprecated
HTTP+SSE transport is intentionally **not** used.

- **stdio** (default) — for local clients that launch the server as a subprocess.
- **Streamable HTTP** (`StreamableHTTPServerTransport`) — for networked clients.
  In this mode `ledgerproof_issue_receipt` **requires `precomputed_sha256` and
  rejects raw payloads**, so artifact bytes never cross the network.

## Tools

| Tool | Purpose |
|---|---|
| `ledgerproof_issue_receipt` | Produce an Article-50 record for AI-generated content and register it. Exactly one of `artifact` or `precomputed_sha256`, plus `aiSystemId`, `deployerName`, `contentCategory`, etc. Mirrors `publishAiArticle50`. Returns `{sequence, entry_hash, verify_url, anchor_status:"pending"}`. |
| `ledgerproof_verify_receipt` | Verify by `{sequence}` (API lookup) **or** `{transparent_statement}` (SCITT bundle, trust-minimized §7 check → `{issuerSignatureValid, inclusionProofValid, receiptSignatureValid, bitcoinConfirmed, valid}`). |
| `ledgerproof_check_anchor` | Poll a `{sequence}` for Bitcoin anchor status (`pending` → `anchored` with `txid`, block, Merkle proof). |
| `ledgerproof_hash_artifact` | SHA-256 a `{payload}` locally so raw content never leaves the machine. |

### Prompt

- `ledgerproof_when_to_issue` — scoped guidance: issue **only** when deliberately
  "producing an Article-50 transparency record for AI-generated content," and a
  reminder that anchoring costs a Bitcoin tx fee, so do not auto-fire on loose
  triggers.

## Honest behavior notes / gaps

- **Anchoring is asynchronous.** Issuance always returns
  `anchor_status: "pending"`. The server never reports a fabricated "anchored"
  status — `ledgerproof_check_anchor` reports `anchored` only when the API
  returns a real Bitcoin `txid`. Poll until then.
- **Raw payloads stay local even on stdio.** The SDK hashes the artifact locally
  and only the `artifact_hash` (never the bytes) is sent to the LedgerProof API.
  Streamable HTTP additionally keeps the bytes off the *MCP* hop by requiring
  `precomputed_sha256`.
- **SCITT verification is guarded.** `verifyTransparentStatement` lives in the
  SDK's `scitt/` module, which is being built in parallel and **may not be
  exported yet**. If it's absent, `ledgerproof_verify_receipt` (Transparent
  Statement mode) returns a clear "not available in the installed SDK" error
  instead of failing the build. Sequence-mode verification works today.
- **`precomputed_sha256` issuance depends on an SDK feature.** The base
  `publishAiArticle50` always hashes raw bytes and exposes no precomputed-hash
  entrypoint. The server passes the hash through optimistically (forward-compat);
  if the installed SDK can't accept it, the tool returns a precise error rather
  than registering a record with the wrong `artifact_hash`. Until the SDK adds a
  precomputed-hash publish path, issue from raw `artifact` over **stdio**.

## Configuration (environment variables)

| Variable | Required | Default | Notes |
|---|---|---|---|
| `LEDGERPROOF_PUBLISHER_ID` | yes | — | Legal-entity id (LEI/EUID/VAT/DID). Not an email. |
| `LEDGERPROOF_DEPLOYER_COUNTRY` | yes | — | ISO country code, e.g. `DE`. |
| `LEDGERPROOF_API_KEY` | yes | — | LedgerProof API key. |
| `LEDGERPROOF_API_BASE` | no | `https://api-eu.ledgerproofhq.io` | API base URL (`api-eu.ledgerproofhq.io`). |
| `LEDGERPROOF_SIGNING_KEY_HEX` | no | ephemeral | Ed25519 signing seed (hex). **Never logged.** |
| `LEDGERPROOF_KEY_PATH` | no | — | Path to a file containing the hex seed (alt to `_HEX`). |
| `LEDGERPROOF_KEY_ID` | no | `default` | Key id registered with the service. |

HTTP-only (optional): `PORT`/`LEDGERPROOF_MCP_PORT` (default `3000`),
`LEDGERPROOF_MCP_HOST` (default `127.0.0.1`),
`LEDGERPROOF_MCP_TRANSPORT=http` (same as `--http`).

> The Ed25519 signing key is never logged, never returned by any tool, and never
> serialized. Only its presence (`provided` vs `ephemeral`) is printed at boot.

## Install / build

```bash
npm install
npm run build      # tsc → dist/
npm run lint       # tsc --noEmit
```

## Run

```bash
# stdio (default)
npx @ledgerproof/mcp-server

# Streamable HTTP on http://127.0.0.1:3000/mcp
npx @ledgerproof/mcp-server --http --port 3000
```

## MCP client config

### stdio

```json
{
  "mcpServers": {
    "ledgerproof": {
      "command": "npx",
      "args": ["-y", "@ledgerproof/mcp-server"],
      "env": {
        "LEDGERPROOF_PUBLISHER_ID": "LEI:5493001KJTIIGC8Y1R12",
        "LEDGERPROOF_DEPLOYER_COUNTRY": "DE",
        "LEDGERPROOF_API_KEY": "sk_live_...",
        "LEDGERPROOF_API_BASE": "https://api-eu.ledgerproofhq.io",
        "LEDGERPROOF_SIGNING_KEY_HEX": "<32-byte-hex-ed25519-seed>",
        "LEDGERPROOF_KEY_ID": "default"
      }
    }
  }
}
```

### Streamable HTTP

Start the server (`npx @ledgerproof/mcp-server --http --port 3000`), then point
the client at the URL:

```json
{
  "mcpServers": {
    "ledgerproof": {
      "type": "streamable-http",
      "url": "http://127.0.0.1:3000/mcp"
    }
  }
}
```

Over Streamable HTTP, call `ledgerproof_hash_artifact` first and pass the result
to `ledgerproof_issue_receipt` as `precomputed_sha256` — raw payloads are
rejected on this transport by design.

## License

Apache-2.0

TDQS

A4.7/5.0

Scored across 4 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: hashing content, issuing a receipt, checking Bitcoin anchoring, and verifying a receipt. There is no overlap in functionality.

Naming Consistency5/5

All tool names follow the consistent pattern 'ledgerproof_verb_noun' using snake_case. Verbs and nouns are clear and match the tool's action (hash_artifact, issue_receipt, check_anchor, verify_receipt).

Tool Count5/5

Four tools is well-scoped for the domain of managing AI-generated content receipts. Each tool covers a necessary step in the workflow without unnecessary extras.

Completeness5/5

The tool set covers the full lifecycle: hash content (hash_artifact), issue receipt (issue_receipt), check anchoring status (check_anchor), and verify receipt (verify_receipt). No obvious gaps for the intended purpose.

Maintenance

ActivityStale
ResponsivenessNo issues