Skip to main content
Glama
README.md
# HISTOR

<!-- mcp-name: io.github.alexar76/histor -->

<!-- aicom-readme-badges -->
<p align="center">
  <a href="https://github.com/alexar76/histor/actions/workflows/ci.yml"><img src="https://github.com/alexar76/histor/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
  <a href="https://github.com/alexar76/histor/actions/workflows/pages.yml"><img src="https://github.com/alexar76/histor/actions/workflows/pages.yml/badge.svg" alt="Pages" /></a>
  <a href="https://histor.modelmarket.dev/"><img src="https://img.shields.io/website?url=https%3A%2F%2Fhistor.modelmarket.dev%2Fhealth&label=live&up_message=up&down_message=down" alt="Live service" /></a>
  <a href="https://histor.modelmarket.dev/log"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fhistor.modelmarket.dev%2Fapi%2Fv1%2Fbadges%2Flog" alt="Labels in the log (live)" /></a>
  <a href="https://histor.modelmarket.dev/servers"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fhistor.modelmarket.dev%2Fapi%2Fv1%2Fbadges%2Fpinned" alt="Tool sets pinned (live)" /></a>
  <a href="https://histor.modelmarket.dev/changes"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fhistor.modelmarket.dev%2Fapi%2Fv1%2Fbadges%2Fchanges" alt="Changes in the last 7 days (live)" /></a>
  <a href="https://histor.modelmarket.dev/log"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fhistor.modelmarket.dev%2Fapi%2Fv1%2Fbadges%2Fsth" alt="Age of the last signed tree head (live)" /></a>
  <a href="https://github.com/alexar76/histor/actions/workflows/pages.yml"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Falexar76.github.io%2Fhistor%2Fbadges%2Ftests.json" alt="Tests (measured by CI)" /></a>
  <a href="https://github.com/alexar76/histor/actions/workflows/pages.yml"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Falexar76.github.io%2Fhistor%2Fbadges%2Fcoverage.json" alt="Branch coverage (measured by CI)" /></a>
  <img src="https://img.shields.io/badge/python-%3E%3D3.11-3776AB" alt="Python >=3.11" />
  <img src="https://img.shields.io/badge/store-Postgres%20%C2%B7%20SQLite%20dev-336791" alt="Postgres in production, SQLite for development" />
  <img src="https://img.shields.io/badge/labels-MTL%2F1%20on%20AWR%2F2-8d83ff" alt="MTL/1 labels on AWR/2" />
  <img src="https://img.shields.io/badge/log-RFC%209162-5fe3ff" alt="RFC 9162 Merkle log" />
  <img src="https://img.shields.io/badge/docs-EN%20RU%20ES%20FR%20ZH-9c70ff" alt="Documentation in 5 languages" />
  <a href="https://github.com/alexar76/histor/blob/main/LICENSE"><img src="https://raw.githubusercontent.com/alexar76/histor/refs/heads/main/docs/badges/license.svg" alt="License: MIT" /></a>
</p>
<!-- /aicom-readme-badges -->

<!-- aicom-mirror-notice -->
> 🔄 Synced from the [AI-Factory monorepo](https://github.com/alexar76/aicom) (append-only history). **Pull requests are welcome** — merged PRs are imported back and re-synced here. [Issues](https://github.com/alexar76/histor/issues) · [Pull requests](https://github.com/alexar76/histor/pulls)

<p align="center">
  <strong>HISTOR</strong> (ἵστωρ, “the one who knows because they saw”) — a public transparency log of MCP tool definitions<br>
  What every remote MCP endpoint in the official registry advertised · when it changed · signed, appended, auditable
</p>

<p align="center">
  <a href="README.md"><b>English</b></a> ·
  <a href="README.ru.md">Русский</a> ·
  <a href="README.es.md">Español</a> ·
  <a href="README.fr.md">Français</a> ·
  <a href="README.zh.md">中文</a>
</p>

> [WARDEN quality cycle: MOMUS → AI-Factory → SKOPOS → shared node agent → deployment](https://github.com/alexar76/momus/blob/main/docs/quality-cycle.md).

**Live:** [histor.modelmarket.dev](https://histor.modelmarket.dev) ·
**Landing:** [alexar76.github.io/histor](https://alexar76.github.io/histor/) ·
**Capabilities:** `histor.check@v1` · `histor.server@v1` · `histor.changes@v1` ·
**Port:** `9490`

## The problem, in one paragraph

An MCP server can pass your review with harmless tool descriptions and change them later —
a new sentence in a description is all a prompt injection needs, and your client will pass it
to the model without asking you again. MCP has no content addressing for tool definitions, the
official registry verifies namespaces rather than contents, and a scanner on your laptop sees
only what the server shows your laptop. HISTOR is the missing public memory: it records what
each server advertised, signs it, and tells anyone who asks whether what they received is what
everyone else is seeing.

## What it does

```mermaid
flowchart LR
    R["Official MCP registry<br/>~21k servers"] --> C["Crawler<br/>server/discover + tools/list<br/>never a tool call"]
    C --> D["MTL/1 digest<br/>RFC 8785 + SHA-256"]
    D --> L["Four signed labels<br/>AWR/2 · did:key"]
    W["WARDEN sidecar<br/>published pattern set"] --> L
    L --> M["Merkle log<br/>RFC 9162"]
    M --> S["Signed tree head<br/>after every crawl"]
    M --> API["Desk · API · badge · Atom feed"]
    CL["Your client"] -- "tools it received" --> K["/api/v1/check"]
    K -- "signed: same · different · seen before" --> CL
    K -. "opt-in digest count" .-> M
```

- **Reads** every remote endpoint in the official registry: `server/discover`, then `tools/list` (legacy servers use initialization)
  drained across pages. No tool is called, nothing is installed or executed, private addresses
  are refused before a connection opens.
- **Also reads** a short list of popular remote servers the registry does not carry ([`histor/curated.json`](histor/curated.json), DeepWiki among them), under HISTOR's own namespace `urn:awr:mtl:1:registry:histor.modelmarket.dev/curated`, so no label claims a registry listing. When such a server publishes to the registry, the registry's listing replaces ours.
- **Runs npm and PyPI packages** — the servers people start locally with `npx` or `uvx` — in a [sandbox](docs/operations.md#package-sandbox): installed without running their code, started under gVisor with decoy credentials and no route out, read with discovery and `tools/list` (legacy initialization when needed), then up to 15 tools called once with canary arguments, inside the sandbox. gVisor's own trace says what the package did — programs started, names looked up, decoys opened — at install, at startup and when its tools were called. Completed observations are cached per published version; incomplete observations are retried; a new version that changes what its tools tell the model is a change in the log, like any endpoint's.
- **Digests** the tool set exactly as [MTL/1](https://github.com/alexar76/aicom/blob/main/awr/adoption/mcp-trust-label/PROFILE.md)
  defines it — name, description, input and output schema, UTF-16 code-unit order, RFC 8785.
- **Signs** four kinds of label, each a standalone AWR/2 `VerificationVerdict`: what was observed,
  what the WARDEN pattern set matched, whether the definitions changed since the previous label,
  and whether the name is on a threat list.
- **Appends** every label to an RFC 9162 Merkle log and signs a tree head after each crawl, so a
  consistency proof shows the history was only ever appended to.
- **Answers** `/check`: send the tools your client received; get back, signed, whether HISTOR
  observed the same set at that endpoint, an earlier one, or none.

Remote checks run independently of the daily registry/package cycle. The default target interval
is six hours; endpoints with at least two observed changes in the last seven days are checked
every 30 minutes. These are scheduling targets: host limits, failures and queue capacity can
make actual gaps longer. Intermediate states can still be missed. See
[remote polling operations](docs/remote-polling.md) for configuration and measured intervals.

Release changes and upgrade notes: [CHANGELOG.md](CHANGELOG.md).

**MCP 2026-07-28:** [modern and legacy compatibility](docs/mcp-compatibility.md).
**Local upgrade brief:** choose your dependencies and workflows, compare saved observations, and get a retest checklist.
From this source directory, run `python3 -m histor upgrade-brief --watchlist examples/upgrade-brief/watchlist.json --out-dir ../output/histor-upgrade-brief-example/en` (Python 3.11+).
Reports and checklists support `--lang ru|en|es|fr|zh` (default `en`).
See the [guide](docs/upgrade-brief.md) and [reproducible synthetic example](examples/upgrade-brief/README.md), available in five languages.

## What a label does not say

The profile forbids the words *safe, secure, audited, certified, approved* and *trusted*, and so
does this README. A pattern match is a reason to read a definition, not a finding. Three of the
four methods can never return `fail`; the fourth (continuity) fails only in the mechanical sense
that two digests differ. No source is read, no package resolved, no behaviour observed. A change
is shown as a date and a diff, never as an accusation. Details: [docs/labels.md](docs/labels.md).

**What a tool list costs.** A client resends every connected server's tool list with each model request. Over the 18,204 packages and 16,170 remote servers in the log, a list's median is about 1,300 tokens, the 90th percentile about 8,000, and 31 packages list over 100,000: [docs/tool-list-cost.md](docs/tool-list-cost.md). Each count is split into names, descriptions and input schemas; every change carries its cost diff (`/api/v1/changes/<id>/cost`), and `histor tool-cost check` runs the same count in a package's CI and fails a release that makes the list heavier: [docs/tool-list-cost.md](docs/tool-list-cost.md#where-the-tokens-are-and-what-a-release-changed).

**Watch the servers you use.** `https://histor.modelmarket.dev/feed.xml?watch=<id>,<id>` is a feed of the changes of up to 100 servers you follow; `/api/v1/changes?watch=…&after=<cursor>` is the same for a program, oldest first, resumable. Teams can have each change pushed, signed, to Slack or their own endpoint: [docs/watch.md](docs/watch.md). Registries, scanners and rating services: [docs/data-feed.md](docs/data-feed.md).

**Download the data.** Once a week the log's whole view is published as files at [histor.modelmarket.dev/datasets/](https://histor.modelmarket.dev/datasets/): every listed server, every tool set it showed, every recorded change and the WARDEN scans. The files are JSON Lines (gzip) with a manifest signed by the log's key; the compilation is CC BY 4.0. Each snapshot's README lists the fields and loads in pandas or DuckDB ([histor/dataset.py](histor/dataset.py)). Also on [Hugging Face](https://huggingface.co/datasets/alexar76/histor-mcp-tool-definitions); to cite a number, give the version DOI of the snapshot it was counted on (each archived snapshot has its own, listed on the datasets page); [doi:10.5281/zenodo.23512759](https://doi.org/10.5281/zenodo.23512759) stands for all of them and opens the newest.

**Public payer evidence.** The `histor payer-audit` command (since 0.4.1) checks Base USDC
receipts and exports sourced behavioural indications while retaining payment totals and unknown
cases. It needs no production database or wallet: [docs/payer-audit.md](docs/payer-audit.md).

## Install

```bash
pip install aimarket-histor        # the package is aimarket-histor — "histor" on PyPI is an unrelated project
histor audit https://histor.modelmarket.dev   # verify the public log's tree heads and proofs; needs no keys or database
uvx aimarket-histor                           # MCP server over stdio against the public log: histor_changes, histor_check, histor_server
```

Python 3.11 or newer. `histor audit` is the first command to run: it checks the live log the way a
stranger should, without trusting HISTOR ([docs/log.md](docs/log.md)).

In an MCP client (Claude Code, Cursor, …): `{ "mcpServers": { "histor": { "command": "uvx", "args": ["aimarket-histor"] } } }` —
three tools that ask the public log; nothing is crawled or stored locally. `aimarket-histor --local`
(or `histor mcp`) serves your own log from `HISTOR_DATA_DIR` instead. Listed in the official MCP registry as
`io.github.alexar76/histor`.

After connecting, ask: “Use `histor_server` with endpoint `npm:mongodb-mcp-server`.
Show the last observation, the number of tools, and the link to its HISTOR record.”
Substitute your own `npm:` or `pypi:` package, or
[look it up in the browser](https://histor.modelmarket.dev/servers?from=pypi-histor).

## Quick start

To run your own log from source:

```bash
git clone https://github.com/alexar76/histor && cd histor
npm ci --prefix scanner                     # the WARDEN sidecar (node >= 20)
uv sync --extra dev --project .
HISTOR_CRAWL_LIMIT=40 uv run --project . python -m histor crawl   # observe 40 endpoints
uv run --project . python -m histor serve   # desk + API on :9490 (SQLite under ./data)
```

For a local MCP client or Glama, run `python -m histor mcp`. It exposes
`histor_changes`, `histor_check` and `histor_server` over stdio using the local log.
It opens no HTTP port and starts no crawl; use the separate `crawl` command above to
populate a fresh log. See [Glama build settings](docs/GLAMA.md).

Production runs on Postgres, and `HISTOR_PROFILE=prod` refuses to start without it:

```bash
HISTOR_POSTGRES_PASSWORD=… HISTOR_OPERATOR_TOKEN=… HISTOR_PUBLIC_BASE=https://histor.example \
  docker compose -f docker-compose.yml -f docker-compose.postgres.yml up -d
```

Schema changes are numbered migrations (`python -m histor migrate up|status`), applied before the
service listens; see [docs/operations.md](docs/operations.md).

## Ask the log

```bash
# Is what my client received what HISTOR observed at that endpoint? Three lines that run as
# written: take a listed server, send its endpoint and tool list to /check, read the signed answer.
# With your own client, replace the second line by the endpoint and the tools/list result it received.
H=https://histor.modelmarket.dev/api/v1
S=$(curl -sS "$H/servers?limit=1" | jq -r '.servers[0].id')
curl -sS "$H/servers/$S" | jq '{endpoint: .server.endpoint, tools: .tools}' > received.json
curl -sS "$H/check" -H 'Content-Type: application/json' -d @received.json | jq '.verdict, .target.page'

# The signed tree head, and a proof that today's log extends yesterday's
curl -sS https://histor.modelmarket.dev/api/v1/log/sth
curl -sS "https://histor.modelmarket.dev/api/v1/log/proof/consistency?first=1000&second=1200"
```

Every endpoint is in [docs/api.md](docs/api.md). How to audit the log without trusting HISTOR is
in [docs/log.md](docs/log.md); the format itself (leaf input, tree, signed head, verification rule) is specified in [docs/spec/transparency-log.md](docs/spec/transparency-log.md) with normative vectors in [test-vectors/](test-vectors/). The internals are in [docs/architecture.md](docs/architecture.md).

## Tests

```bash
make test          # unit tests; set HISTOR_TEST_DATABASE_URL to run each storage test on Postgres too
make integration   # real sockets: a loopback registry and MCP servers, uvicorn, the real WARDEN sidecar
```

The tests and coverage badges above are measured by CI on every Pages deploy and read through
shields.io, and the log badges read the live service, so every number there is current.

## ERC-8004 identity

HISTOR is ERC-8004 agent [`96683` on Base](https://8004scan.io/agents/base/96683), registered on
2026-10-01 in the canonical IdentityRegistry `0x8004A169…a432` and owned by the AIMarket operator
wallet `0x1218ff36…Ad0a`. Its [registration file](https://modelmarket.dev/.well-known/erc-8004/histor.json)
lists the web page, the hosted `histor-check` endpoint and the `did:key` that signs every label, so a
label's signature can be tied back to the registered agent. Transactions and how to check them:
[ERC-8004 identities](https://github.com/alexar76/aicom/blob/main/docs/erc-8004-identities.md).

## Where it sits

| Component | Role |
|---|---|
| [WARDEN](https://github.com/alexar76/warden) | checks tool definitions at connect time, on the client |
| **HISTOR** | remembers what servers advertised, publicly |
| [THEMIS](https://github.com/alexar76/themis) | admits a capability at publish time on a Hub |
| [AIMarket Hub](https://modelmarket.dev) | lists `histor.check@v1` in the catalogue |
| [AWR](https://github.com/alexar76/aicom/tree/main/awr) | the signed document format every label uses |

## License

MIT — see [LICENSE](LICENSE). Part of the [AIMarket ecosystem](https://ecosystem.modelmarket.dev/).

TDQS

A3.5/5.0

Scored across 3 tools

Disambiguation4/5

Each tool targets a distinct aspect of MCP endpoint observation: a global recent-changes feed, a verification check against a supplied tools list, and a stored per-endpoint record. The check and server tools overlap somewhat in returning historical observation details, but the need to supply a digest versus retrieving a record distinguishes them.

Naming Consistency5/5

All three tools share the consistent 'histor_' prefix followed by a single noun/verb, with no mixing of styles (e.g., histor_changes, histor_check, histor_server). The pattern is predictable and readable.

Tool Count5/5

Three tools is well-scoped for a focused, read-only history/monitoring service, with each tool covering a distinct workflow (recent changes, verification, record lookup). No bloat or thinness.

Completeness4/5

The surface covers the core read-only operations: global change feed, endpoint record retrieval, and verification of a supplied tools list. Minor gaps exist—no direct per-endpoint change listing or endpoint/registry enumeration tool—but agents can work around these via the global changes feed or record links.

Maintenance

ActivityActive
ResponsivenessSyncing