pip install corpora-py # or: uv add corpora-pyAUTH_REQUIRED=false corpora-api # http://127.0.0.1:8000 β leave it runningThe examples below run in a second shell and pipe through
jq (brew install jq).
Auth is on by default and fails closed (401 without a Supabase JWT).
AUTH_REQUIRED=false is for local use β see Settings.
flowchart LR
S["π EPUB Β· PDF Β· HTML<br/>XML Β· TEI Β· text"] -->|POST /convert| C["βοΈ conversion job"]
C --> A["π¦ .corpus archive<br/>every word a graph node"]
A --> M["π€ /mcp<br/>AI reads and searches it"]
A --> R["π /refs<br/>citable passages"]
A --> H["βοΈ /storage<br/>publish to the Hub"]
curl -sF file=@book.epub -F source_format=epub -F name='My Book' \
localhost:8000/convert | tee job.json
# β {"job_id": "1ec2121b-β¦", "status_url": "/convert/1ec2121b-β¦", "ws_url": "β¦/ws"}
JOB=$(jq -r .job_id job.json) # every command below uses itsource_format: epub Β· pdf Β· html Β· xml Β· tei Β· tei_zip Β· plain Β· tf_zip
curl -s localhost:8000/convert/$JOB # {"status": "succeeded", β¦}
curl -sOJ localhost:8000/convert/$JOB/downloadLong jobs push the same status over a WebSocket: ws://β¦/convert/$JOB/ws.
curl -s localhost:8000/convert/$JOB/sections # the table of contents
curl -s "localhost:8000/convert/$JOB/content?limit=1" # passages + tokens
curl -s localhost:8000/convert/$JOB/manifest # title, authors, versionAdd to claude_desktop_config.json:
{
"mcpServers": {
"corpora": {
"command": "cf-mcp",
"args": ["--corpus", "/Users/you/.exegia/datasets/BHSA", "--name", "BHSA"]
}
}
}Then ask it things. A good tool order for an agent:
describe_corpus() β list_features() β search(β¦, "count") β search(β¦, "results") β get_passages(β¦)
curl "localhost:8000/refs/resolve?ref=bhsa@2021/Deut:4:2!clause1"One grammar for every corpus β corpus@version/Section:Section!typeN:
| Reference | Means |
|---|---|
bhsa@2021/Deut:4:2 |
Deuteronomy 4:2 |
bhsa/Deut:4:2!clause1 |
its 1st clause |
mobydick@1.0/Moby-Dick:3!word12 |
12th word of chapter 3 |
POST /refs turns a node into one. Full grammar: skills/tf-reference-id.
curl -sX POST localhost:8000/storage \
-H 'content-type: application/json' -d "{\"job_id\": \"$JOB\"}" # publish
curl -s localhost:8000/storage # what's published
curl -s localhost:8000/storage/my-book.corpus/content # read itNeeds HF_STORAGE_REPO + HF_TOKEN.
| Path | What it does |
|---|---|
/mcp |
MCP server β 30 tools (26 read-only, 15 in a standalone cf-mcp) |
/convert |
Upload β job β .corpus; read, annotate and version the result |
/storage |
Publish, list, read and edit archives on the Hub |
/refs |
Reference β node, plus labels, pills and share URLs |
/validate |
Confirm a dataset round-trips .tf β .cfm β mmap |
/ingest |
Docling β Context Fabric v1 graph.json (extra: corpora-py[docling]) |
/ai |
β³ stub β every route answers 501 (#214) |
/health Β· /capabilities |
Liveness, and what this deployment permits |
Interactive docs while the server runs: http://127.0.0.1:8000/docs
All 30 MCP tools
| Group | Tools | In cf-mcp |
|---|---|---|
| Discovery | list_corpora describe_corpus list_features describe_feature get_text_formats |
β |
| Search | search search_continue search_csv search_syntax_guide |
β |
| Read | get_passages get_node_features |
β |
| Validate | validate_corpus |
β |
| References | reference_create reference_resolve reference_shortcode |
β |
| Hub storage | storage_list_corpora storage_corpus_info storage_download_corpus storage_upload_corpus* storage_delete_corpus* |
β |
| Corpus detail | corpus_sections corpus_index corpus_content corpus_node_get corpus_manifest_get corpus_manifest_update* corpus_node_annotate* |
β |
| Corpus refs | corpus_reference_create corpus_reference_resolve corpus_reference_shortcode |
β |
* Write tools β not registered at all when HF_READ_ONLY=true.
| Variable | Default | Purpose |
|---|---|---|
AUTH_REQUIRED |
true |
Require a Supabase JWT everywhere but /health, /capabilities, /, docs |
PROJECT_REF |
β | Supabase project whose JWKS verifies those tokens |
HF_STORAGE_REPO Β· HF_TOKEN |
β | The Hub repo behind /storage |
HF_READ_ONLY |
false |
Refuse every Hub write β 403 on REST, write tools unregistered |
JOB_STORE |
memory |
supabase shares job state across instances |
Public demo = AUTH_REQUIRED=false and HF_READ_ONLY=true. Set both, or
anonymous visitors can write to your Hub.
from admin.converters import CONVERTERS, convert_to_corpus
from admin.parsers import SourceFormat
tf_dir = CONVERTERS[SourceFormat.EPUB]("book.epub", "out/book.tf")
convert_to_corpus(tf_dir, "book.corpus", name="My Book", language_code="en")from corpora_mcp.corpus import corpus_manager
name = corpus_manager.load("~/.exegia/datasets/BHSA", name="BHSA")
api = corpus_manager.get_api(name) # Text-Fabric api: api.F, api.T, api.Spip install corpora-py ships all of it β corpora_mcp, admin and common
are bundled in that one wheel; there is no separate corpora-mcp on PyPI.
make docker-build-corpora # or: docker build -f dockerfiles/Dockerfile -t corpora-py .
docker run -p 8000:8000 -v ~/.exegia/datasets:/data/datasets:ro corpora-pyImages are also published to ghcr.io/exegia/corpora-py (login required).
MCP-only image: dockerfiles/Dockerfile.client Β· Compose:
docker compose -f dockerfiles/docker-compose.yml up corpora
| π₯οΈ Desktop / web app | example/ β live demo |
| β¨οΈ Terminal CLI | exegia/corpora-cli β brew tap exegia/corpora-cli https://github.com/exegia/corpora-cli && brew install corpora |
| π§ Conversion internals | packages/admin/README.md |
| π Data model spec | Context Fabric v1 |
| π οΈ Contributing / dev setup | CLAUDE.md Β· make help Β· .github/WORKFLOW.md |
