Atlas / MCP servers / lodsb / prax

praxBLOCK

mcp/lodsb/prax

A self-hosted library for the papers and web pages one person reads: hybrid search, a graph whose links cite their sentence, and an MCP interface for agents

Verdict
BLOCK
Grade
F
Trust score
48 /100
Exposed tools
41 32r · 9w · 0d
Transport
—
License
MIT
Stars
3
01

Overview

From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.

prax is a self-hosted library for the papers, web pages, manuals and notes one person reads. Its search matches words and meaning at once, its graph records what the documents say about each other, and an AI agent can search and read it through the same interface its owner uses. Everything lives in one SQLite file and a folder of originals, on your own machines, with models you choose.

pip install -e ".[serve,work]" export PRAXDATADIR=~/prax-data prax serve # http://127.0.0.1:8000/ui/

prax add paper.pdf prax add https://example.org/article prax search feedback delay networks prax ask --answer why do FDNs colour the tail

An empty store works from the first command. A new document is archived as it arrives, and prax work --watch, run on the machine with the models, does the rest: its text, title, graph links and vectors. To keep the parts running, list them under run: in prax.yaml and run prax up --install, which starts them at login on Windows, Linux or macOS. Setting up the Zotero import and the browser extension is in docs/howto.md.

Why I built it

My reading was spread over a Zotero library, an external disk I called zoetrope, a NAS and a year of browser tabs. I could only find a paper again if I remembered where I had put it. I wanted one place that keeps the originals and reads them closely enough to answer "which of my papers says this, and where". An agent working with me should be able to search it without being handed whole PDFs. I built prax with Claude Code over a few weeks in 2026. I decided what it should do and measured whether it did, and the log of that work is in docs/log.md. It is shaped by

Read from source at commit 3c0e79fe33d8OBSERVED · 2026-10-11
02

Connect

Built from this server's own package name, version and transport as found in its source — not copied from anyone's documentation, so it cannot drift against a page we do not control. Replace the environment placeholders with a token scoped to the least it needs.

claude-code
claude mcp add prax --env ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} --env HF_TOKEN=${HF_TOKEN} --env PRAX_TOKEN=${PRAX_TOKEN} -- uvx prax
claude-desktop
{
  "mcpServers": {
    "prax": {
      "command": "uvx",
      "args": [
        "prax"
      ],
      "env": {
        "ANTHROPIC_API_KEY": "${ANTHROPIC_API_KEY}",
        "HF_TOKEN": "${HF_TOKEN}",
        "PRAX_TOKEN": "${PRAX_TOKEN}"
      }
    }
  }
}
03

Exposed tools (41)

32 read · 9 write · 0 destructive.

ToolRiskDescription
append_pagewriteAdd a section to an existing page as the agent, leaving what a
askreadThe context for answering a question from the library: one numbered
blogreadb
capture_urlreadFetch a web page or file by URL and keep it: a page is indexed at
capture_urlsreadFetch and keep several URLs in one call (at most 20), each as
changesreadWhat changed in a period (``since``/``until``: ``2026-09``, a day or
cited_but_missingwriteWhat a set of papers cites that the library does not hold, ranked
code-reviewwriteQuality review of prax code since a base commit: five dimensions, every finding re-checked
connectreadHow ``a`` is connected to ``b`` (two names: papers, people,
contextreadWhat places a document (by id) or a page (by slug) in the library,
datasheetreada part
documentsreadList documents, newest first, without their text: by ontology
essayreadan essay
figurereadLook at a figure: the picture itself, out of the document
getreadFetch one document by id (ids come from search results).
get_chunkreadFetch one chunk in full (ids come from search results).
get_pagereadA page of the library
healthreadWhether prax answers: the door
ingestreadIngest raw text as a new document (deduped by content hash).
ingest_filereadIngest a file from a path on this machine (the one running the MCP
instructionalreadtells how
linkwriteAdd a graph edge between two entities (created if missing).
manualreada manual
mathsreadA SymPy calculator for formulas, on a host that runs the maths pack.
opinionreadargues
page_imagereadLook at one page of a document
politicsreadpolicy
promotereadFlag a document for the expensive model
referencesreadA paper
request_readingreadAsk for a reading of a document the readers found nothing in, or
searchreadSearch the knowledge base. Returns compact snippets + ids.
set_domainswriteWhich ontology modules a document is read against (its domains, e.g.
set_titlewriteGive a document its title (a file name or an arXiv id where a
societyreadpeople
sociologyreadsociety
statusreadWhere documents are on their way to being read: ``indexed``,
sync_projectwriteSend a project
traversereadExpand the knowledge graph 1-2 hops from a named entity.
update_sectionwriteReplace the body under one heading of a page with ``text``, or add
whyreadWhat a derived edge follows from (an INFERRED edge whose producer is
write_pagewriteCreate a page (kind addendum, project or topic) or replace its text
04

Trust audit

BLOCKgrade F · trust 48/100 Do not install this without reading the findings. The audit found something that could harm you or your machine.

LayerWhat it checksResult
L0Provenance & inventoryPASS
L1Static analysis of the codeFAIL
L2Instruction surface (what it tells the agent)PASS
L3Class-specific surfacePASS
L4Behavioural (sandbox)SKIPPED

What the source does

Filesystem
declared (5 observation(s))
Network
declared (9 observation(s))
Shell
declared (5 observation(s))
Dependencies
pinned
Secrets in source
none-found

Findings (25)

HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
scripts/extension_bed.mjs:689
Promise.resolve().then(() => eval(arguments[0])).then((v) => done({ ok: true, v })).catch((e) => done({ ok: false, e: String(e && e.message || e) }));`, args: [expression] })
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHNetwork egress · net.tls_off · CWE-200, CWE-319
clients/send/prax_send.py:607
p.add_option("--insecure", action="store_true", help="accept any TLS certificate")
Why it matters. certificate verification is disabled
Fix. leave verification on
MEDIUMCode injection · code.dynamic_import · CWE-78, CWE-94, CWE-95
clients/cli/prax_cli/running.py:758
__import__(name)
MEDIUMCode injection · code.dynamic_import · CWE-78, CWE-94, CWE-95
src/prax/packs/__init__.py:118
return getattr(importlib.import_module(module), attr)
MEDIUMCode injection · code.dynamic_import · CWE-78, CWE-94, CWE-95
src/prax/parsers/base.py:50
importlib.import_module(self.package)
MEDIUMCode injection · code.dynamic_import · CWE-78, CWE-94, CWE-95
src/prax/steps/__init__.py:125
module = importlib.import_module(home if "." in home else f"prax.steps.{home}")
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
clients/cli/prax_cli/main.py:26
DEFAULT_DOOR = "http://127.0.0.1:8000"
MEDIUMObfuscation / stealth · obf.homoglyph · CWE-506, CWE-94
src/prax/packs/maths/runtime.py:725
r"^\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)\s*([GMkmuμnp]?)\s*[A-Za-zΩ]*\s*$"
LOWInventory / provenance · inv.binary · CWE-1104
tests/fixtures/video/bars.webm
bars.webm
Why it matters. a compiled or binary member cannot be reviewed from source
Fix. ship source, or explain the binary in the README
LOWInventory / provenance · inv.binary · CWE-1104
tests/fixtures/zotero/storage/HJR45JMH/.zotero-ft-cache
.zotero-ft-cache
Why it matters. a compiled or binary member cannot be reviewed from source
Fix. ship source, or explain the binary in the README
LOWInventory / provenance · inv.binary · CWE-1104
tests/fixtures/zotero/zotero.sqlite
zotero.sqlite
Why it matters. a compiled or binary member cannot be reviewed from source
Fix. ship source, or explain the binary in the README
LOWInventory / provenance · inv.hidden_file · CWE-1104
tests/fixtures/zotero/storage/432URMMU/.zotero-ft-cache
.zotero-ft-cache
Why it matters. hidden member outside the usual dotfiles
Fix. review its purpose
LOWInventory / provenance · inv.hidden_file · CWE-1104
tests/fixtures/zotero/storage/5QSJDXUQ/.zotero-ft-cache
.zotero-ft-cache
Why it matters. hidden member outside the usual dotfiles
Fix. review its purpose
LOWInventory / provenance · inv.hidden_file · CWE-1104
tests/fixtures/zotero/storage/AYY57KAK/.zotero-ft-cache
.zotero-ft-cache
Why it matters. hidden member outside the usual dotfiles
Fix. review its purpose
LOWInventory / provenance · inv.hidden_file · CWE-1104
tests/fixtures/zotero/storage/FBT2AP8S/.zotero-ft-cache
.zotero-ft-cache
Why it matters. hidden member outside the usual dotfiles
Fix. review its purpose
LOWInventory / provenance · inv.hidden_file · CWE-1104
tests/fixtures/zotero/storage/FCEK3EI9/.zotero-ft-cache
.zotero-ft-cache
Why it matters. hidden member outside the usual dotfiles
Fix. review its purpose
LOWCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
tests/eval/maths-hand-scores.yaml:1
# Hand scores of the maths eval (docs/symbolic-maths.md), R right, P partly,
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
LOWInsecure crypto · crypto.weak_hash · CWE-327, CWE-338
src/prax/store/documents/library.py:186
hashlib.sha1(" ".join(str(r[0]).split()).encode("utf-8")).hexdigest()
LOWInsecure crypto · crypto.weak_hash · CWE-327, CWE-338
src/prax/store/documents/library.py:195
hashlib.sha1(" ".join(t.split()).encode("utf-8")).hexdigest()
LOWInsecure crypto · crypto.weak_hash · CWE-327, CWE-338
src/prax/store/documents/reads.py:417
return hashlib.sha1(blob.encode("utf-8")).hexdigest()[:12]
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
src/prax/importers/mdwiki.py:143
if doc is None and "/" not in path:  # a topic's folder, "../../diffusion/"
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
tests/test_mdwiki_import.py:23
"PDF: [the paper](../../DDPM-2006.11239.pdf). See [noise](../noise.md),"
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
tests/test_mdwiki_import.py:24
" [attention](../../../attention/), [missing](../../gone.pdf),"
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
tests/test_paths.py:26
" [up](../../../outside.md) `pip install x` `*.md` and a sentence."
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
tests/ui/lib.test.js:5
const lib = require("../../src/prax/ui/lib.js");

Gates applied: no_behavioural_pass.

Audited 2026-10-11 · audit v0.4.1 · source sha 3c0e79fe33d8full audit observations/trust-audit/mcp-server/lodsb__prax.json · Report an issue / request a re-scan
05

Audit history

Every audit this server has had. A grade with a past is a grade somebody is still checking.

DateSourceVerdictGradeScoreChange
2026-10-113c0e79fe33d8BLOCKF48first audit
06

Questions

What is the prax MCP server?

A self-hosted library for the papers and web pages one person reads: hybrid search, a graph whose links cite their sentence, and an MCP interface for agents

What tools does prax expose?

41 in total: 32 read-only, 9 that write, and 0 that can delete or overwrite. Every one is listed on this page with its risk.

Is prax safe to connect to an agent?

No — not without reading the findings first. The audit graded it F (48/100) and found 2 critical or high issues in the source. Each one is listed on this page with the file and line it is on.

What credentials does prax need?

It reads ANTHROPIC_API_KEY, HF_TOKEN and PRAX_TOKEN from the environment. Give it a token scoped to the least it needs — an agent that can be talked into calling a tool can be talked into calling it with your credentials.

How current is this page?

The grade is for one exact copy of the source (3c0e79fe33d8), read on 2026-10-11. The repository is watched and re-audited when it changes.

Advertisement