Skip to content

The Python SDK, completely

The MCP server lets an AI assistant use Lemma. This page is for when you want to use Lemma directly in your own Python code — in a script, a Jupyter notebook, or an automated test. It does the same checks, just called straight from Python instead of through an AI.

Everything here reads the same cards and runs the same checker as the MCP server — there’s no second, different copy of the logic.

flowchart TD
  subgraph SDK["the artano-lemma library"]
    CA[browse the cards]
    VA[validate a card]
    EN[check a formula]
    TL[the eight tool functions]
    CL[talk to a running server]
  end
  Cards[(cards)] --> CA
  CA --> VA --> EN
  classDef c fill:#1e3a5f,stroke:#4a90d9,color:#fff;
  class EN c;

Install it

In a terminal:

Terminal window
pip install artano-lemma

(If you’re working inside the project’s own code, use pip install -e ./sdk-py instead, which installs your local copy.)

If you want the engine to prove limit and conservation claims rather than record them, add the optional extra:

Terminal window
pip install "artano-lemma[symbolic]"

The lemma command

Installing the library also gives you a small command you can type in the terminal, called lemma, with seven sub-commands:

Type thisAnd it…
lemma verify <card> …checks a finished result against what the card declares
lemma crosscheck <card|file>checks a proposed card, from the corpus or a draft
lemma listlists the cards
lemma show arrhenius-rate-lawprints one card in full
lemma pathsshows where the cards and template live on disk
lemma authors <card>shows who a card credits
lemma serveruns Lemma as a tool server (the Python version)

Try lemma list first — it’s the quickest way to see what’s in the library.

Browse the cards from code

The most common thing: load the cards into your program and look around. Each line below is explained in the comment after it:

from artano_lemma import load_cards, find_card, filter_cards, domains
cards = load_cards() # load every card
print(len(cards), "cards across", len(domains(cards)), "subjects")
# -> 40 cards across 22 subjects
chem = filter_cards(domain_prefix="chemistry", cards=cards) # only chemistry
one = find_card("arrhenius-rate-law", cards) # one specific card
print(one.name, "·", one.formulaTeX) # its title and formula

load_cards() hands you the cards as Python objects you can read. filter_cards narrows by kind or subject; find_card grabs one by its id (or returns nothing if it doesn’t exist). This whole snippet is the runnable file examples/browse_cards.py.

Check that a card is well-formed

This is the same “does it fit the template?” check from the Cards page, but in Python:

from artano_lemma import is_valid_card_payload, parse_card, CardValidationError
is_valid_card_payload(my_card) # -> True or False
try:
parse_card(my_card) # turns it into an object, or complains
except CardValidationError as e:
for problem in e.issues: # each problem, in plain text
print(problem.path, problem.message)

A broken card lists every problem (e.g. 'formulaTeX' is a required property). Runnable version: examples/validate_card.py.

Check a formula (the cross-checker)

This runs the same verification the engine page describes:

from artano_lemma import load_cards, parse_card, run_hypothesis_checks
corpus = load_cards()
card = parse_card({
"kind": "hypothesis", "id": "candidate-ke", "version": "0.1.0",
"name": "Kinetic energy", "proposal": "E = half m v squared",
"proposedFormulaTeX": "E", "origin": "llm", "references": ["..."],
"checks": {"dimensional": {
"lhsLabel": "E", "lhsDims": {"M":1,"L":2,"T":-2},
"rhsLabel": "half m v^2", "rhsDims": {"M":1,"L":2,"T":-2},
"expr": "(1/2)*m*v**2", "symbols": {"m":{"M":1}, "v":{"L":1,"T":-1}},
}},
})
result = run_hypothesis_checks(card, corpus=corpus)
print(result.overall.severity) # -> NONE (i.e. green / looks correct)

You can also use just the units-working-out part on its own:

from artano_lemma import derive_dims, stringify_dims
print(stringify_dims(derive_dims("(1/2)*m*v**2", {"m":{"M":1}, "v":{"L":1,"T":-1}})))
# -> L^2·T^-2·M (i.e. energy)

The tool functions

The corpus and verification tools the AI assistant uses are also ordinary Python functions — handy if you want their nicely-formatted text directly:

from artano_lemma import (
cards_list, cards_get, ops_get, # browse
hypothesis_crosscheck, # check a proposed card
usce_check, series_check, # check a finished run
convergence_check, agreement_check,
)
cards_list(corpus=corpus) # the catalogue, as text
cards_get("arrhenius-rate-law", corpus=corpus) # one card, as text
ops_get("slurm-marenostrum5-gpp-compute", corpus=corpus) # a template, as text
hypothesis_crosscheck(id="free-fall-with-linear-drag") # a verdict on a proposal
usce_check(id="ideal-gas-law", output={"gasConstant_J_per_molK": 8.3145})
series_check(id="density-of-states", series={"epsilon": [-1, 0, 1], "g": [0.0, 1.2, 0.4]})
convergence_check(id="runge-kutta-4", refinement=[[0.1, 1e-7], [0.05, 6.25e-9]])
agreement_check(id="cross-method-reproducibility",
outputs={"a": {"latticeConstant_A": 5.470},
"b": {"latticeConstant_A": 5.475}})

These are the same eight tools the Python MCP server (lemma serve) exposes, and they render byte-identical text to the Node server’s. That matters because the two servers are drop-in alternatives — you pick one by changing a single command in your assistant’s config, and a verdict that read differently depending on which you installed would make that choice visible when it should be invisible.

Runnable version: examples/use_mcp_tools.py. The search tool, rag_lookup, is the one exception — it needs Postgres and an embedding model, so it lives only in the Node MCP server.

Talk to a running server from Python

If you’d rather drive a running Lemma server from Python (instead of doing the work in-process), the library can act as a client too:

from artano_lemma import connect_lemma_stdio

What’s ready, and what isn’t

Browsing cards, the lemma command, validating cards, the formula cross-checker, the tool functions, and the client are all ready today. So is USCE’s validation-envelope check — run_usce_checks(output, card) range-checks a finished output against the card’s declared bounds — and the cross-method comparison, run_agreement_checks(outputs, card), which asks whether two independent methods agree rather than whether one run is in range.

Two things worth knowing that are not on by default:

  • run_usce_checks(output, card, require_checks=True) turns “nothing was checked” into a finding instead of a clean pass. Useful in CI.
  • run_hypothesis_checks(card, corpus=cards, symbolic=True) actually proves limit and conservation claims instead of recording them. Needs pip install "artano-lemma[symbolic]", and it is Python-only — see symbolic verification.

USCE’s deeper time-series checks (causality, asymptotic decay) are on the roadmap.

Next