Skip to content

Quickstart

Zero infrastructure. Zero credentials. From pip install to a working dependency graph in under a minute.

pip install model-ledger
from model_ledger import Ledger, DataNode

ledger = Ledger()  # in-memory; swap for Ledger.from_sqlite("inv.db") to persist

ledger.add([
    DataNode("raw_txns",      platform="warehouse", outputs=["transactions"]),
    DataNode("feature_build", platform="etl",       inputs=["transactions"],  outputs=["features"]),
    DataNode("fraud_model",   platform="ml",         inputs=["features"],      outputs=["risk_scores"]),
    DataNode("review_queue",  platform="alerting",   inputs=["risk_scores"]),
])
ledger.connect()                 # ports match → edges appear

print(ledger.trace("review_queue"))
# ['raw_txns', 'feature_build', 'fraud_model', 'review_queue']

print(ledger.upstream("fraud_model"))
# ['raw_txns', 'feature_build']

That's the whole idea: declare nodes, the graph connects itself. Next, give a node an identity and a history → Register a model.

pip install "model-ledger[mcp]"

# Register the server with Claude Code (one time)
claude mcp add model-ledger -- model-ledger mcp --demo

Then just ask:

You: what models are in my inventory?

Claude: 7 models across 5 platforms. fraud_scoring was retrained and deployed this week. Want me to dig into anything?

You: if we deprecate customer_features, what breaks?

Claude: 3 models consume it directly, 2 more transitively.

The --demo flag loads a sample inventory so you can explore before connecting your own data. See the Agent guide for the full tool surface.

pip install "model-ledger[rest-api]"
model-ledger serve --demo --port 8000

Open http://localhost:8000/docs for live, auto-generated OpenAPI docs, or:

curl "localhost:8000/query?limit=5"
curl "localhost:8000/trace/fraud_scoring?direction=upstream"
curl "localhost:8000/overview"

Register a model

A DataNode gives you the graph. register() gives a model an identity and starts its history — the two things a regulator asks for.

from model_ledger import Ledger
ledger = Ledger.from_sqlite("./inventory.db")

ledger.register(
    name="fraud_scoring",
    owner="risk-team",
    model_type="ml_model",
    tier="high",
    purpose="Real-time card fraud detection",
)

# Record an event — any payload you like, no schema to maintain
ledger.record("fraud_scoring", event="retrained", actor="ml-pipeline",
              payload={"accuracy": 0.94, "features_added": ["velocity_24h"]})

for snap in ledger.history("fraud_scoring"):
    print(snap.timestamp, snap.event_type)
# ... registered
# ... retrained

Every call appends an immutable Snapshot. Nothing is overwritten — that's what makes the inventory auditable.

Choose where it lives

Storage is a one-line decision and never changes your code:

from model_ledger import Ledger
from model_ledger.backends.json_files import JsonFileLedgerBackend

Ledger()                                       # in-memory — tests & demos
Ledger.from_sqlite("./inventory.db")           # zero-infra, single file
Ledger(JsonFileLedgerBackend("./inventory"))   # git-friendly JSON files
Ledger.from_snowflake(conn, schema="DB.MODEL_LEDGER")  # production

More on backends

Where to next

  •  ConceptsDataNode, Snapshot, Composite. The whole model in three ideas.
  •  Agent guide — the 8 MCP tools and a worked multi-tool transcript.
  •  Recipes — copy-paste solutions to real tasks.
  •  API reference — generated from source, never out of date.