Metadata-Version: 2.5
Name: precis-mcp
Version: 8.37.0
Summary: MCP server giving LLM agents a seven-verb API over papers, documents, code, personal state, patents, and cached web/Wolfram/YouTube tool calls — backed by PostgreSQL + pgvector
Project-URL: Homepage, https://github.com/retospect/precis-mcp
Project-URL: Repository, https://github.com/retospect/precis-mcp
Author-email: Reto Stamm <reto@retostamm.com>
License-Expression: GPL-3.0-or-later
License-File: LICENSE
Keywords: agent-tools,ai-agent,ast,callgraph,claude,code-navigation,context-management,cursor,docx,latex,llm,markdown,mcp,mcp-server,model-context-protocol,patents,pdf,perplexity,pgvector,postgres,progressive-disclosure,python,scientific-papers,semantic-search,spaced-repetition,token-optimization,windsurf,wolfram-alpha,youtube-transcripts
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Processing :: Markup
Classifier: Topic :: Text Processing :: Markup :: LaTeX
Classifier: Topic :: Text Processing :: Markup :: Markdown
Requires-Python: >=3.12
Requires-Dist: ase>=3.22
Requires-Dist: feedparser>=6.0
Requires-Dist: ftfy>=6.3
Requires-Dist: habanero>=2.0
Requires-Dist: httpx>=0.27
Requires-Dist: latex2mathml>=3.77
Requires-Dist: lxml>=5.0
Requires-Dist: manifold3d>=2.5
Requires-Dist: matplotlib>=3.7
Requires-Dist: mcp[cli]<3,>=1.0
Requires-Dist: mermaidx>=0.8
Requires-Dist: nanopub>=2.1
Requires-Dist: num2words>=0.5.13
Requires-Dist: numba>=0.67.0
Requires-Dist: numpy>=1.24
Requires-Dist: opentimestamps>=0.4.5
Requires-Dist: pgvector>=0.3.0
Requires-Dist: pint>=0.23
Requires-Dist: platformdirs>=4.0
Requires-Dist: prompt-toolkit>=3.0
Requires-Dist: psycopg[binary,pool]>=3.2
Requires-Dist: pydantic-settings>=2.0
Requires-Dist: pydantic>=2.0
Requires-Dist: pylatexenc>=2.10
Requires-Dist: pysbd>=0.3.4
Requires-Dist: python-docx>=1.1
Requires-Dist: python-epo-ops-client>=4.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: rdkit>=2023.9
Requires-Dist: resvg-py>=0.1
Requires-Dist: scipy>=1.14
Requires-Dist: semanticscholar>=0.8
Requires-Dist: shapely>=2.0
Requires-Dist: spglib>=2.1
Requires-Dist: sympy>=1.12
Requires-Dist: tenacity>=8.0
Requires-Dist: trafilatura>=1.6
Requires-Dist: watchdog>=4.0
Requires-Dist: wolframalpha>=5.0
Requires-Dist: youtube-transcript-api>=1.0
Provides-Extra: all
Requires-Dist: fastapi>=0.110; extra == 'all'
Requires-Dist: fonttools>=4.40; extra == 'all'
Requires-Dist: jinja2>=3.1; extra == 'all'
Requires-Dist: markdown-it-py>=3.0; extra == 'all'
Requires-Dist: marker-pdf>=1.0; (sys_platform != 'win32') and extra == 'all'
Requires-Dist: pymupdf>=1.24.10; extra == 'all'
Requires-Dist: python-multipart>=0.0.9; extra == 'all'
Requires-Dist: rapidfuzz>=3.0; extra == 'all'
Requires-Dist: sentence-transformers>=3.0; extra == 'all'
Requires-Dist: uvicorn[standard]>=0.29; extra == 'all'
Provides-Extra: asa
Requires-Dist: aiohttp>=3.9; extra == 'asa'
Requires-Dist: discord-py>=2.4; extra == 'asa'
Provides-Extra: asa-slack
Requires-Dist: slack-bolt>=1.20; extra == 'asa-slack'
Requires-Dist: slack-sdk>=3.27; extra == 'asa-slack'
Provides-Extra: cad-step
Requires-Dist: cadquery-ocp-novtk>=7.8; extra == 'cad-step'
Provides-Extra: catalyst
Requires-Dist: autocatpath>=0.31.0; extra == 'catalyst'
Provides-Extra: catalyst-gpu
Requires-Dist: autocatpath[mace]>=0.31.0; extra == 'catalyst-gpu'
Requires-Dist: cuequivariance-torch; (sys_platform == 'linux') and extra == 'catalyst-gpu'
Provides-Extra: chain
Requires-Dist: viennarna>=2.7; extra == 'chain'
Provides-Extra: chem
Requires-Dist: rdkit>=2023.9; extra == 'chem'
Provides-Extra: dft-ml
Requires-Dist: ase>=3.22; extra == 'dft-ml'
Requires-Dist: mace-torch>=0.3; extra == 'dft-ml'
Requires-Dist: torch-dftd>=0.4; extra == 'dft-ml'
Provides-Extra: embed
Requires-Dist: sentence-transformers>=3.0; extra == 'embed'
Provides-Extra: estimate
Requires-Dist: mendeleev>=1.2.0; extra == 'estimate'
Requires-Dist: pymatgen>=2026.5; extra == 'estimate'
Requires-Dist: tblite>=0.7; (sys_platform == 'darwin' or platform_machine == 'x86_64') and extra == 'estimate'
Provides-Extra: guide
Requires-Dist: pillow>=10.0; extra == 'guide'
Provides-Extra: paper
Requires-Dist: fonttools>=4.40; extra == 'paper'
Requires-Dist: marker-pdf>=1.0; (sys_platform != 'win32') and extra == 'paper'
Requires-Dist: pymupdf>=1.24.10; extra == 'paper'
Requires-Dist: rapidfuzz>=3.0; extra == 'paper'
Provides-Extra: pourbaix
Requires-Dist: mp-api>=0.45; extra == 'pourbaix'
Requires-Dist: pymatgen>=2026.5; extra == 'pourbaix'
Provides-Extra: struct-search
Requires-Dist: agox<4,>=3.11; extra == 'struct-search'
Provides-Extra: tts
Requires-Dist: kokoro-onnx>=0.4; extra == 'tts'
Requires-Dist: soundfile>=0.12; extra == 'tts'
Provides-Extra: web
Requires-Dist: fastapi>=0.110; extra == 'web'
Requires-Dist: jinja2>=3.1; extra == 'web'
Requires-Dist: markdown-it-py>=3.0; extra == 'web'
Requires-Dist: python-multipart>=0.0.9; extra == 'web'
Requires-Dist: uvicorn[standard]>=0.29; extra == 'web'
Description-Content-Type: text/markdown

# precis-mcp

[![check](https://github.com/retospect/precis-mcp/actions/workflows/check.yml/badge.svg)](https://github.com/retospect/precis-mcp/actions/workflows/check.yml)
[![PyPI](https://img.shields.io/pypi/v/precis-mcp.svg)](https://pypi.org/project/precis-mcp/)
[![Python](https://img.shields.io/pypi/pyversions/precis-mcp.svg)](https://pypi.org/project/precis-mcp/)
[![License](https://img.shields.io/github/license/retospect/precis-mcp.svg)](LICENSE)
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.22863181.svg)](https://doi.org/10.5281/zenodo.22863181)

A [Model Context Protocol](https://modelcontextprotocol.io/) server that
gives language-model agents a small, uniform API for reading, writing,
and searching across papers, documents, personal state, code, and
cached tool calls. Small-model-friendly (7B-class agents are the design
target); stores content in PostgreSQL with `pgvector`, with a web
interface ([see how it works](guide/README.md)).

## See it

<a href="guide/README.md">
<img src="guide/assets/drive/tour.svg" alt="precis Drive — search everything you've collected, animated tour" width="720">
</a>

A two-minute tour, section by section, lives right here in the repo at
**[guide/README.md](guide/README.md)**. A narrated version (audio) lives at
**[retospect.github.io/precis-mcp](https://retospect.github.io/precis-mcp/)**
once Pages is enabled. Video: coming.

**Set it up:** single machine —
[`docs/setup-single-machine.md`](docs/setup-single-machine.md) ·
cluster (multi-host, ansible) — [`deploy/README.md`](deploy/README.md).

> **Status.** Actively developed on the v8 line. There is no
> CHANGELOG — `git log` is the change story. The kinds catalogue
> below is a *living* set: the authoritative, build-specific
> enumeration is always `get(kind='skill', id='precis-help')`
> against a running server (it introspects the live registry),
> paired with `get(kind='skill', id='precis-overview')` for the
> guided tour. Agents should start at `precis-toolpath-help`
> ("I want to X — what do I call?").

## What it does

One tool surface — **eight verbs** discriminated by a single `kind=`
argument — over three categories of content. Ref kinds are addressed
by slug or integer id (output hands you a compact `<2-char><id>`
handle, e.g. `pa5` a paper, `me42` a memory); tool kinds take `q=`
or `id=` and hand back text.

- **Reading & reference** — `paper` (ingested research PDF),
  `patent` (EPO OPS record), `cfp` (call-for-proposal / spec doc),
  `oracle` (curated wisdom entry), `conv` (past conversation),
  `pres` (slide deck), `skill` (agent how-to — you're reading one).
- **Files under `PRECIS_ROOT` / code** — `markdown`, `plaintext`,
  `tex`, and `python` (symbol- and callgraph-aware repo navigator).
- **Authored artifacts** — `draft` (chunk-native document that
  exports to LaTeX/PDF/Word), `cad` (analytic-SDF solid modeling —
  designs are probed, not meshed; unit-required DSL), `se`
  (structural-envelope designs: blocks/ports/joints with DRC,
  stability + prestress analysis, a fastener engine, STL/3MF/STEP
  export), `nm` (molecular-machine designs on the same block
  surface — envelopes bound to atomistic structures, mechanics
  ceilings), `structure` (atomistic cell + bond graph for
  DFT/molecular work), `pcb` (netlist + placement graph →
  BOM/CPL/DSN + Freerouting), and `folder` (organizational container
  for the above).
- **Personal state & knowledge** — `todo` (hierarchical todo tree),
  `memory`, `gripe`, `anki` (spaced-repetition cloze cards → AnkiWeb),
  `citation` (verified claim → source quote), `finding`
  (chain-of-evidence over a citation chase), `job` (offline LLM run,
  child of a `todo`).
- **Identity, comms & audit** — `orcid` (researcher-identity hub),
  `cron` (push-notification scheduler),
  `message` (proactive outbound), `alert` (machine-detected ops
  condition), `agentlog` (per-run attribution trail), `provenance`
  (derivation audit).
- **Tool kinds** (stateless or cache-backed) — `calc` (local SymPy),
  `math` (Wolfram), `youtube` (transcript), `web` (fetch + extract),
  `wikipedia` (on-demand article), `websearch` /
  `perplexity-reasoning` / `perplexity-research` (Perplexity Sonar
  tiers).
- **Discovery** — `random`: pick a random indexed block to stumble
  into content when you don't know what to ask for.

The active set depends on which optional extras and env vars are
configured (see [Install](#install)) — a kind whose dependency or
env var is missing simply drops off the surface. This list is a
snapshot; `get(kind='skill', id='precis-help')` enumerates the kinds
wired in *your* build, and `get(kind='skill', id='precis-overview')`
gives the design-rationale tour with an example handle per kind.

## Eight verbs

| Verb     | Use when                                            |
|----------|-----------------------------------------------------|
| `get`    | You know the **name** (slug, id, file path) — or you're calling a tool. |
| `search` | You're looking for **content** by topic or phrase. Hybrid lexical (tsvector) + semantic (pgvector) with RRF fusion. |
| `put`    | Create a new ref. Optionally tag and link on creation. |
| `edit`   | Rewrite a region of a file-kind ref by content anchors (`find-replace`, `append`, `insert`, `replace`). |
| `delete` | Soft-delete a numeric ref, or delete a region from a file kind by selector. |
| `tag`    | Add and/or remove tags. Three namespaces: closed (`STATUS:done`), flag (`pinned`), open (`topic-foo`). |
| `link`   | Add or remove a cross-link to another ref. Vocabulary: `related-to`, `blocks`, `contradicts`, `cites`, `derived-from`, `supports`, … |
| `more`   | Fetch the next page of a truncated response (`more(cursor='…')` — every truncation footer hands you the cursor). |

Address by `id=` for names, `q=` for content. No URI selector strings
for ids; region selectors inside files use the compact `slug~SELECTOR`
shape (e.g. `notes--meeting~L42-58`).

## Install

Use [uv](https://docs.astral.sh/uv/) and Python 3.13, the version CI tests
on every run (uv fetches 3.13 itself if it is missing):

```bash
uv tool install --python 3.13 'precis-mcp[all]'
```

This puts the `precis` command in its own isolated environment; upgrade
with `uv tool upgrade precis-mcp`.

**Disk space.** Measured 2026-10-20 on macOS arm64, Python 3.13:

| Install | Environment size |
|---|---|
| `precis-mcp` (core only) | ~650 MB |
| `precis-mcp[all]` | ~1.6 GB (CPU torch is ~530 MB of that) |

Linux installs that pull CUDA builds of torch are several GB larger. Models
download on first use, on top of the environment: the bge-m3 embedder
(`embed`, ~2.3 GB) and Marker's PDF layout/OCR models (`paper`, roughly
1–2 GB). Plan for about 5 GB free for a full single-machine install, plus
your Postgres data.

Plain pip also works on Python 3.12–3.13:

```bash
pip install 'precis-mcp[all]'
```

**Deterministic, no-API tool kinds ship in core** — no extra needed: `calc`
(sympy + pint), `plot` / `figure` (matplotlib), `mermaid` (`mermaidx`, no
Node/Chromium), `docx` + `tex` export (python-docx / latex2mathml / lxml /
resvg), `cad` STL/3MF export (manifold3d CSG kernel), `structure` CIF
I/O + symmetry (ASE + spglib), and `pcb` footprint resolution. So do the
light network/API-client kinds — `math` (Wolfram), `youtube`, `web`,
`wikipedia`, `news`, `edgar`, the Perplexity trio, and `patent` (EPO OPS):
their real gate is an API key, which fails loudly at call time, so they
ship with every install and appear once their credential resolves
(env var or the DB secrets vault). Only genuinely heavy packs
(torch-bound ML, ~200 MB binaries, daemon-shaped bridges) stay behind
extras — each enables its kinds; omit any you don't want:

| Extra          | Enables                                            | Heavy? |
|----------------|----------------------------------------------------|--------|
| `embed`        | In-process bge-m3 embedder (sentence-transformers + torch) — needed for `search` unless you point at a remote embedder | yes (~2 GB model on first load) |
| `paper`        | `paper` ingest — Marker PDF → chunks + CrossRef/S2 metadata | yes (pulls torch via Marker) |
| `web`          | `precis web` browser UI (FastAPI + Jinja + HTMX)   | no |
| `all`          | `embed` + `paper` + `web` — the single-machine set. Excludes every tier below — install those explicitly. | yes |
| `asa`          | `asa-bot` Discord bridge (discord.py)              | no |
| `asa-slack`    | `asa-slack` Slack bridge (slack_bolt)              | no |
| `tts`          | Audio export — local TTS for voice drafts + the morning brief (Kokoro) | yes (host-specific) |
| `cad-step`     | `cad` exact STEP export (OpenCASCADE B-rep)        | yes (~200 MB OCCT libs) |
| `dft-ml`       | `structure` ML-potential relax (ASE + MACE-torch)  | yes (pulls torch) |
| `chem`         | Kept as a no-op alias: RDKit (the `route` retrosynthesis kind) moved into core on 2026-10-15 | no |
| `catalyst`     | `pathway` kind surface (autocatpath engine — release-gated dep, not on PyPI; resolves via `[tool.uv.sources]` in this repo) | no |
| `catalyst-gpu` | `pathway` compute — in-process NEB/relax (autocatpath[mace] + cuequivariance; same dep caveat) | yes (pulls torch) |
| `struct-search`| Global structure search (AGOX GOFEE/basin-hopping) | yes (ray, dscribe, …) |
| `estimate`     | `estimate` kind — chemistry-workup descriptors (mendeleev + pymatgen + tblite) | moderate |
| `chain`        | `se` chain fold checks (ViennaRNA)                 | no |
| `pourbaix`     | `pourbaix_bulk` job (pymatgen + Materials Project) | moderate |
| `guide`        | User-guide capture/annotate pipeline (Pillow) — repo tooling, not a kind | no |

A bare `pip install precis-mcp` gives you the state kinds (`todo`,
`memory`, `gripe`, `anki`, `conv`, `oracle`, `skill`, `random`), the
core deterministic and network tool kinds listed above (key-gated ones
appear when their credential resolves), and the `markdown` /
`plaintext` / `python` / `tex` file kinds (the file kinds ride on
`PRECIS_ROOT` / `PRECIS_PYTHON_ROOTS`).
Optional deps surface as `InitError` at boot: the kind silently drops
off the tool surface with a WARNING, the server stays up.

### Database

`precis-mcp` requires PostgreSQL with the `pgvector` extension (SQL
extension name: `vector`). The CLI `precis migrate` applies the
forward-only numbered SQL migrations in `src/precis/migrations/`. See
`0001_initial.sql` for the schema.

```bash
createdb precis
psql precis -c 'CREATE EXTENSION vector;'

export PRECIS_DATABASE_URL=postgresql://localhost/precis
export PRECIS_EMBEDDER=bge-m3   # or "mock" for tests
precis migrate
```

Step-by-step single-machine runbook (worker, web UI, secrets):
[`docs/setup-single-machine.md`](docs/setup-single-machine.md). Cluster
(multi-host, ansible): [`deploy/README.md`](deploy/README.md).

### Upgrading

```bash
pip install -U 'precis-mcp[all]'     # or: uv tool upgrade precis-mcp
precis migrate                       # then restart serve / worker / web
```

Daemons check the migration ledger at startup and **refuse to boot while
migrations are pending** (`PRECIS_SCHEMA_GATE=refuse`, the default) — a
new package can never silently run on an old schema. A DB *ahead* of the
package (a newer peer migrated it mid-deploy) warns and still boots.
Alternatives to running `precis migrate` by hand:

- `PRECIS_AUTO_MIGRATE=1` — or `--migrate` on `precis serve` /
  `precis web` / `precis worker` — each daemon migrates itself at
  startup, serialized under a Postgres advisory lock. The docker image
  defaults this on for `precis …` commands, so a container restart
  self-migrates.
- `PRECIS_SCHEMA_GATE=warn` — log one loud warning and boot anyway; for
  read-mostly clients of a DB another host migrates (e.g. a session MCP
  server) that must stay up through a deploy window. `precis-status`
  reports the pending count as `schema_state`.
- `PRECIS_SCHEMA_GATE=off` — bypass the check entirely (same as the
  legacy `PRECIS_SKIP_SCHEMA_CHECK=1`): loud warning, diagnosis only,
  not for running services.

## Run

`precis serve` speaks MCP over stdio. Wire it into your agent's MCP
config:

```json
{
  "mcpServers": {
    "precis": {
      "command": "precis",
      "args": ["serve"],
      "env": {
        "PRECIS_DATABASE_URL": "postgresql://localhost/precis",
        "PRECIS_EMBEDDER": "bge-m3",
        "PRECIS_ROOT": "/absolute/path/to/notes",
        "PRECIS_PYTHON_ROOTS": "myrepo:/absolute/path/to/myrepo"
      }
    }
  }
}
```

### One-tool profile

Setting `PRECIS_MCP_PROFILE=command` in the server env collapses the
eight-tool surface into a single `precis(command=..., text=None)`
tool. `command` takes the same one-string call syntax the docs
already teach — e.g. `get(kind='skill', id='toc')` — parsed by
`src/precis/tools/command_parser.py`: one call, keyword args only,
`ast.literal_eval`-safe literal values; a bad call gets an
actionable `[error:BadInput]`, not a crash. `text=` is the escape
hatch for large bodies, so a caller doesn't have to quote-escape
them inside `command`. The frozen schema is ~850 bytes vs ~22 KB for
the typed per-verb schemas — cheaper cold-start, and a `tools/list`
block that never changes, so prompt caches keyed on it never
invalidate. Default profile is `typed` (unset = the per-verb tools
above).

`precis eval '<call>'` is the CLI twin: it evaluates one call string
against the same parser (`--text` / `--text-file` for the large-body
escape hatch) without building the multi-flag `precis tools <verb>
--flag value` form.

### Environment variables

| Var                           | Purpose                                          |
|-------------------------------|--------------------------------------------------|
| `PRECIS_DATABASE_URL`         | Postgres DSN (required for all ref kinds).       |
| `PRECIS_OWNER`                | Canonical username for the human running this instance — the author stamped on a web "ask a follow-up" and the `user:<owner>` addressee of an `ask-user` pause. Defaults to `owner`. |
| `PRECIS_EMBEDDER`             | `"mock"` (dev/tests), `"bge-m3"` (in-process), or `"remote"` (HTTP client to `precis serve-embeddings`). |
| `PRECIS_EMBEDDER_URL`         | Required for `remote`: ordered, comma-separated base URL(s), e.g. `http://127.0.0.1:8181`. First healthy endpoint wins; rest are fallback. |
| `PRECIS_ROOT`                 | Single root dir for `markdown` / `plaintext` / `tex` kinds. The trio is hidden when unset; every read/write is normalised against this path (`Path.resolve()` + `relative_to`). |
| `PRECIS_PYTHON_ROOTS`         | `alias:/path,alias2:/path2` — exposed Python repos. |
| `PRECIS_PYTHON_ALLOW_EXEC=1`  | Gate for `python` runtrace (spawns subprocess).  |
| `EPO_OPS_CLIENT_KEY` + `_SECRET` | Enables `patent` kind (env or DB vault; `PRECIS_PATENT_RAW_ROOT` is just a cache path with a config default). |
| `ORCID_CLIENT_ID` + `_SECRET` | Enables the `orcid` researcher-identity kind.    |
| `PRECIS_DIGIKEY_CLIENT_ID` + `_SECRET` | Live supplier stock for `component` `view='stock'` (paste on `/secrets`). |
| `WOLFRAM_APP_ID`              | Enables `math` kind.                             |
| `PERPLEXITY_API_KEY`          | Enables `websearch` / `perplexity-reasoning` / `perplexity-research`. |
| `PRECIS_WEB_AUTH`             | `off` disables the `precis web` HTTP Basic gate (local dev only). Anything else — including unset — keeps it **on**: every route requires an account from `precis users`. |
| `PRECIS_WEB_PASSWORD_PEPPER`  | Vault-resident pepper HMAC'd into web passwords before scrypt, so a shareable *logical* `pg_dump` carries no crackable hashes. `precis users add` mints one on first use; you rarely set this by hand. |
| `PRECIS_CORPUS_DIR`           | Corpus root(s) for the `precis web` paper viewer. An `os.pathsep`-separated list is allowed (e.g. `/opt/a/corpus:/opt/b/corpus`); the web tries each `<root>/<letter>/<cite_key>.pdf` in order and serves the first that exists. Point it at the same path the ingest watcher writes to. |
| `LOG_LEVEL`                   | `DEBUG` / `INFO` / `WARNING` / `ERROR`.          |
| `PRECIS_MCP_PROFILE`          | `typed` (default, per-verb tools) or `command` (single `precis(command)` tool — see [One-tool profile](#one-tool-profile)). |
| `PRECIS_MCP_TOKEN`            | Bearer token `precis serve --transport sse\|streamable-http` requires (stdio ignores it). |
| `PRECIS_AUTO_MIGRATE`         | `=1` lets each daemon apply pending migrations at startup — see [Upgrading](#upgrading). |
| `PRECIS_SCHEMA_GATE`          | `refuse` (default) / `warn` / `off` — startup migration-ledger check; `off` ≡ legacy `PRECIS_SKIP_SCHEMA_CHECK=1`. See [Upgrading](#upgrading). |

This table is the getting-started subset. precis reads ~150 `PRECIS_*`
variables in all — feature toggles, autonomy modes, budgets, model ids,
compute-routing, paths, and secrets. For the **exhaustive catalog** —
every var, its code default, the value deployed to each cluster service,
and an assessment of whether that state is right — see
[`docs/reference/config-variables.md`](docs/reference/config-variables.md).
The policy for *adding* a var (the three-tier scheme) is
[`docs/conventions/env-vars.md`](docs/conventions/env-vars.md).

## Design system

The `cad` / `se` / `nm` kinds form one design surface, macro to
molecular, built for LLM authoring:

- **Explicit units everywhere.** Every dimensioned input states its
  unit (`3mm`, `1.4Å`, `12 N`, `90deg` — pint-backed, hogsheads
  included); internals are SI (metres, radians, float64); display is
  a neat SI-prefix formatter (`2.3 nm`, `1.2 kN`). A bare number
  where a unit is required is *rejected with a hint* echoing the
  plausible readings — the zero-counting / exponent-slip failure
  modes of LLM-authored geometry die at the parser. Two declared
  enclaves keep ecosystem conventions honest (`pcb` is mm like its
  gerber/IPC world; `structure` is Å/eV like its ASE/CIF world),
  self-named and converted at every API boundary.
- **Structural analysis** (`precis.structsolve`): force-density
  form-finding, Pellegrino–Calladine rigidity + prestress
  stability, and an active-set complementarity solver for unilateral
  members (tension-only cables, compression-only struts,
  must-contact stops — "which members carry, and does every member
  stay on its legal sign"), with a two-state bistability probe. A
  SIMP topology-optimisation engine (matrix-free, AM overhang
  filter, gyroid lattice fill) ships alongside.
- **Design viewer** (web): per-design SVG projection reader —
  force-coloured members, part isolation, stepped semantic
  abstraction levels (envelope → interfaces → refined → realized) —
  plus a three-cad-viewer 3D route with drawn connectivity,
  exploded view, and a linked topology graph.
- Scale-relative kernel tolerances (an AST-gated no-absolute-epsilon
  rule) make the same machinery exact from metres to Ångströms.

Where this is heading — one geometry currency from tolerance boxes
to atoms, situations/verdict tables, pattern groups, cost-aware
optimisation — is mapped in
[`docs/backlog/multiscale-design-architecture.md`](docs/backlog/multiscale-design-architecture.md).

## Design highlights

- **Eight verbs, one `kind=`**. The whole surface is
  `get`/`search`/`put`/`edit`/`delete`/`tag`/`link`/`more`. No
  per-kind bespoke tools.
- **Content-anchored edits.** `edit(find=..., before=..., after=...)`
  resolves by literal content match; unique/first/all/nth policy;
  fuzzy nearest-line hint on not-found. Pure resolver in
  `precis.utils.edit_resolve`; ships for `markdown`, `plaintext`, and
  `python`.
- **Hybrid search.** Lexical `tsvector` + semantic `pgvector` (bge-m3)
  with Reciprocal Rank Fusion. Block-level; paper chunks, markdown
  paragraphs, Perplexity answers, web pages all searchable.
- **Per-chunk discovery layer (F20).** Every body chunk gets
  KeyBERT keywords stored on `chunks.keywords TEXT[]` (GIN-indexed
  canonical forms) + `chunks.keywords_meta JSONB` (versioned
  short/long pairs with bge-m3 cosine scores), populated by the
  `chunk_keywords` worker. The paper TOC view (`view='toc'`)
  DP-clusters those keyword arrays at request time
  (`src/precis/utils/toc_db.py`) — superseding the dropped
  `ref_segments` / `ref_segment_sentences` precompute. The
  `citation` kind closes the loop: an agent's writing-thread
  workflow can persist verified `claim → source quote` records (see
  [`precis-citation-help`](src/precis/data/skills/precis-citation-help.md)).
- **Progressive disclosure.** Eight verbs and a `kind=` argument is
  the *whole* visible surface. Behind it sits a fan-out of ~25
  per-kind help skills, dozens of read views, an anchored edit
  protocol, args-dict view payloads, and a tag/link vocabulary —
  none of which the agent has to know up front. Every response can
  emit a `next=` breadcrumb, every error names the skill that
  explains it, and `get(kind='skill', id='precis-<kind>-help')`
  unfolds the manual for whichever capability the agent just
  bumped into. Think *exploding pocket knife*: the tool grows
  blades as you reach for them, instead of advertising 20
  unfamiliar buttons in `tools/list`. (UX literature calls this
  pattern progressive disclosure.)
- **The todo tree.** `kind='todo'` is a hierarchical todo graph — a
  level gradient (`strategic` → `tactical` → `subtask`, plus
  `recurring`), a PRIO sort key, `meta.auto_check` wait-for-condition
  leaves, and `meta.schedule` recurring spawn (the *Watches*
  umbrella). It is the unified substrate for intent, execution, and
  review; `kind='job'` (an offline LLM run) always hangs off a todo
  via `parent_id`, and the `minter` worker is the canonical path
  from a todo's `meta.executor` to a queued job. See
  [`precis-todo-tree-help`](src/precis/data/skills/precis-todo-tree-help.md).
- **Profiled worker.** Every background pass runs under
  `precis worker --profile=system` (embeddings, keywords, minter,
  sweepers — safe to run on every node), `--profile=agent` (the
  LLM-heavy review/planner rotation, each pass self-gated by env + a
  load-average ceiling), or `--profile=all` (the union — the collapsed
  one-worker-per-host shape the cluster runs). Per-pass daemons are
  retired.
- **HintBus.** Any layer can emit deduplicated, novelty-decayed tips
  that are rendered after the verb's main output. Keeps slim models
  from drowning in self-inflicted reminders.
- **Slim exception surface.** `BadInput` / `NotFound` / `Gone` /
  `Unsupported` / `Upstream` / `RateLimited` / `Internal`, each
  carrying a single copy-pasteable `next=` "breaking hint".
- **`psycopg 3` sync, raw SQL.** No SQLAlchemy, no Alembic, no async
  below FastMCP — stdio's serial workload doesn't buy anything from
  async.
- **In-tree handlers, entry-point plugins.** Core kinds are
  hand-ordered in `precis.dispatch.boot()`. Third-party kinds can
  register themselves via the `precis.handlers` entry-point group
  without forking — the contract lives in the `precis.dispatch`
  docstrings; `src/precis_chem/` is the richest first-party example.

## Extending

Write a plugin handler in 3 steps — the contract is documented in the
`precis.dispatch` docstrings (`_load_plugins`), with the
canonical tiny example in
[`src/precis/handlers/calc.py`](src/precis/handlers/calc.py).

```toml
# your plugin's pyproject.toml
[project]
dependencies = ["precis-mcp>=8.0.0"]

[project.entry-points."precis.handlers"]
wikipedia = "precis_wikipedia:WikipediaHandler"
```

Plugin failures are logged and skipped — one bad plugin cannot brick
the server.

## CLI

Routed EasyEDA Pro intake: `uv run precis pcb import-epro LOCAL.epro2 --slug PREVIEW`
preserves straight/arc tracks and through vias as fixed source copper. Use
`--dry-run` to inspect first or `--copper none` for measurement-only intake.

`precis memory mirror import DIR --namespace NAME` imports a flat YAML/Markdown
memory snapshot with conflict checks. `precis memory mirror export DEST
--namespace NAME` preserves original filenames and formatting in a new directory;
it never overwrites an existing destination or retires missing memories.

```text
# Serving
precis serve                       # Start the MCP stdio server.
precis serve-embeddings            # HTTP embedding service (server side of
                                   #   PRECIS_EMBEDDER=remote; /healthz /readyz
                                   #   /model /embed /metrics).
precis web [--host H --port P]      # Browser UI: Tasks / Papers / Console /
                                   #   Conversations / Status tabs (needs the
                                   #   [web] extra; binds 127.0.0.1:9100 behind
                                   #   HTTP Basic — create an account first, or
                                   #   every page answers 503).

# Background processing
precis worker [--profile system|agent|all]
                                   # Drive the background passes. 'system'
                                   #   (default) = embeddings/keywords/minter/
                                   #   sweepers; 'agent' = the LLM-heavy review
                                   #   + planner rotation; 'all' = the union.
                                   #   --only X --once for ad-hoc backfills.
precis ingest --watch <dir>        # Watch an inbox dir and ingest dropped PDFs
                                   #   (papers / books / presentations routing).
precis add <pdf|--doi ID|--arxiv ID>
                                   # Ingest one paper on the spot.

# Web accounts (precis web logins; every account is fully authorized)
precis users add <login> --abbrev <ab> [--name N --email E]
                                   # Create an account; password from a no-echo
                                   #   prompt (or --password-stdin). Never argv.
precis users list                  # The roster.
precis users passwd <login>        # THE recovery path — Basic auth has no
                                   #   email reset flow, by design.
precis users edit|disable|enable|rm <login>
precis users feed-token <login>    # Mint + print the private podcast feed URL
                                   #   (?t=… , since podcast apps handle Basic
                                   #   on enclosures inconsistently).
                                   # Signed-in users do the self-service half —
                                   #   password, profile, podcast link — at
                                   #   /account in the web UI. Creating and
                                   #   removing accounts stays here.

# Database
precis migrate                     # Run pending forward-only SQL migrations
                                   #   (--dry-run reports, --from-scratch
                                   #   replays the chain ignoring the baseline).
precis db dump-schema              # Regenerate migrations/baseline/schema.sql.
precis schema-doc                  # Generate the Mermaid ER diagram
                                   #   (docs/reference/schema.md) from a DSN.

# Interactive & inspection
precis repl                        # Interactive verb console (tab-complete).
precis draft ...                   # Manage / export draft-kind documents.
precis stats | logs | stubs | verify
                                   # Corpus stats, event logs, stub triage,
                                   #   integrity checks.
precis stats --utilization [--hours N]
                                   # Hourly CPU (host_heartbeat_log) + LLM
                                   #   (llm_call_log) utilization + idle gaps.
precis cron | heartbeat            # Scheduler tick / liveness ping.

# Claim-hub curation (taproot)
precis taproot ...                 # Claim-hub authoring/repair: mint / refine /
                                   #   merge / backfill / backfill-grounding /
                                   #   repair-evidence / direct-mint / lint.
precis taproot verify-edges        # Certify withheld/unverified evidence edges
                                   #   for the publish preflight (stamps the
                                   #   meta.support verdict; dry-run default).
precis taproot reword-sweep        # LLM batch reword of lint-blocked claim hub
                                   #   sentences through the retitle door
                                   #   (dry-run default).

# One-shot jobs
precis jobs ingest / ingest-oracles    # Pre-warm files under PRECIS_ROOT /
                                       #   seed the oracle kind.
precis jobs import-perplexity ...      # Bulk-import Perplexity web-UI answers.
precis jobs watch-patents / list-patent-watches / run-patent-watches /
sweep-patent-fulltext                  # Saved CQL patent watches (patent kind).
precis jobs check-provenance / sync-retraction-watch
                                       # Provenance + retraction audits.
```

Run any subcommand with `--help` for the full option list.

### Utility scripts

The `scripts/` dir holds workspace-side utilities that run *against*
a precis store but live outside the published CLI surface. See
[`scripts/README.md`](scripts/README.md) for full coverage; the
high-traffic ones:

- **`paper-monitor-ingest-dir`** — drop-and-go PDF ingest watcher.
- **`perplexity-monitor-ingest-dir`** — bulk-import Perplexity
  markdown exports.
- **`find-citing-papers`** — sweep S2 for new papers citing the
  precis corpus, with bge-m3 cosine rerank and several noise-
  reduction filters; reports land in a `paper-ingest/` review dir.
- **`enrich-paper-identifiers`** / **`retrofit-acatome-external-ids`**
  — backfill DOI / arXiv ids on legacy refs.

## Roadmap

- The multiscale design programme (shared design core, block
  libraries with states, situation rule tables, pattern groups,
  cost-aware optimisation) —
  [`docs/backlog/multiscale-design-architecture.md`](docs/backlog/multiscale-design-architecture.md)
  is the living map.
- `book`, `rmk` file handlers. (`tex` and `docx` shipped.)
- `web` bookmark mode + Wayback enrichment (gripe:3681 phase 2 + 4 — see [`docs/backlog/`](docs/backlog/README.md)).
- `voice` kind — STT/TTS bound to transcript refs (spec: [`docs/backlog/voice-kind-spec.md`](docs/backlog/voice-kind-spec.md)).
- SDK extraction (`precis-core`) once the plugin API has settled.

## Documentation

- [`AGENTS.md`](AGENTS.md) — **start here to contribute or change code.** The canonical guide: conventions, workflow, definition-of-done, ingest guarantees.
- [`docs/mission.md`](docs/mission.md) — the mission, the pitch narrative, and the current corpus facts (positioning, not architecture — the single source for decks and talks).
- [`docs/README.md`](docs/README.md) — the documentation landing index (directory-by-directory map).
- [`docs/codebase.md`](docs/codebase.md) — orientation: invariants, lifecycle, seams, and the generated package map (subsystem detail lives in each package's `__init__.py` docstring).
- [`docs/reference/schema.md`](docs/reference/schema.md) — the **generated** DB schema diagram (Mermaid ER, produced from the live database — can't drift).
- [`docs/reference/config-variables.md`](docs/reference/config-variables.md) — the full `PRECIS_*` config catalog: every var, its default, the value deployed to each cluster service, and a correctness assessment.
- [`docs/reference/schema.md`](docs/reference/schema.md) — generated schema (full ER view: `schema-v2.svg`).
- [`src/precis/data/skills/precis-citation-help.md`](src/precis/data/skills/precis-citation-help.md) — `citation` kind + verifier-workflow agent surface.
- [`src/precis/data/skills/precis-toc-help.md`](src/precis/data/skills/precis-toc-help.md) — TOC machinery (segments, sentences, matryoshka keywords).
- Git history (`git log`) — what shipped in each phase (no CHANGELOG file).

## Contributing

The repo lives at
[`retospect/precis-mcp`](https://github.com/retospect/precis-mcp).
Coordinated releases use `scripts/round gate` / `deploy`: exact release SHA,
fresh CI, then coordinator runtime evidence before immutable tagging and retirement.

Issues and PRs welcome. Development workflow:

For isolated Codex sessions in tmux, see [`scripts/fleet-codex`](scripts/fleet-codex)
and the [Codex fleet runbook](docs/runbooks/codex-fleet.md).

```bash
uv sync --all-extras --group dev
uv run pytest
uv run ruff check . && uv run ruff format --check .
uv run mypy src tests
```

Run the **full test suite in the dev container**, which bakes every
optional extra and wires the test database:

```bash
scripts/test                             # full suite, all extras
scripts/test --typecheck                 # mypy src tests
uv run ruff check . && uv run ruff format --check .
```

A host `uv run pytest` only sees the torch-free base install, so the
full run there fails with spurious missing-extra errors (`sympy`,
`marker`, `lxml`, …) — use it for targeted subsets only.

All tooling goes through `uv run` (host) or `scripts/dev` (container)
— see [`AGENTS.md`](AGENTS.md) for the full workflow and
definition-of-done.

## License

GPL-3.0-or-later. See the full text at
[gnu.org/licenses/gpl-3.0.html](https://www.gnu.org/licenses/gpl-3.0.html).

`precis nanopub supersede FI` stages a local successor candidate for an anchored,
unpublished claim artifact; review and same-key signing remain separate steps.
