# Tripitaka MCP

> MCP server for the full Pāli Canon — search, cite, compare translations. Offered as Dhamma Dāna.

- name: io.github.dhamma-seeker/tripitaka-mcp
- version: 1.3.0
- connection class: R0 (autonomous)
- trust: 89/100
- quality: 89/100 (good)
- owner: not claimed
- descriptor: https://api.protogrid.dev/v1/servers/io.github.dhamma-seeker%2Ftripitaka-mcp
- tools: https://api.protogrid.dev/v1/servers/io.github.dhamma-seeker%2Ftripitaka-mcp/tools

**An agent can connect right now, no human step.** Remote endpoint, no authentication, reachable on the last probe.

## Connection (mcpServers)

```json
{
  "mcpServers": {
    "tripitaka-mcp": {
      "type": "http",
      "url": "https://mcp.tripitaka-mcp.com/mcp"
    }
  }
}
```


## Trust

- hygiene: 100
- liveness: 98
- freshness: 65
- provenance: 90
- drivers: +repository +namespace-matches-repo +website +reachable +uptime-94% ~updated-161d-ago
- Derived from observable signals (official registry feed and our own credential-free probes); no code audit performed.

## Quality 89/100 (good)

### protocol: 88 (weight 25)

- pass `protocol.modern`: supports 2026-07-28 (server/discover)
- pass `protocol.stateless`: answers without a session
- pass `protocol.transport`: streamable HTTP
- warn `protocol.list_ttl`: tools/list declares ttlMs 0, so clients re-fetch it on every use

### authorization: n/a (weight 15)

- n/a `auth.prm`, `auth.as_metadata`, `auth.cimd`: no remote uses OAuth
- n/a `auth.secret_in_url`: no templated URL

### tool hygiene: 86 (weight 30)

- pass `tools.descriptions`: every tool has a description
- warn `tools.description_length`: 12 tools have descriptions over 1,024 characters, which crowds the context
- pass `tools.schemas`: every tool has an object input schema
- pass `tools.annotations`: 14 of 14 tools declare readOnlyHint or destructiveHint
- pass `tools.directory_hints`: every tool declares readOnlyHint, destructiveHint, idempotentHint and openWorldHint
- warn `tools.token_cost`: about 9,298 tokens to load every tool
- pass `tools.api_dump`: tools are not a one-to-one API dump

### stability: 100 (weight 15)

- pass `stability.changes`: no tool changes in 30 days
- pass `stability.rug_pull`: no tool changed its meaning under the same name

### dependencies: n/a (weight 15)

- n/a `deps.known_vulns`, `deps.mcp_sdk_version`, `deps.resolvable`: no npm or PyPI package

- tools: 14, about 9,298 tokens to load them all
- tool set hash: 883e4a018cc403ece7cd89e469440da9
- badge: [![protogrid quality](https://protogrid.dev/badge/io.github.dhamma-seeker/tripitaka-mcp.svg)](https://protogrid.dev/servers/io.github.dhamma-seeker/tripitaka-mcp)
- Checks run on what our credential-free, read-only probes observe; tools are never called and no code audit is performed.

### Tool changes

_No tool definition changes recorded._

## Remotes

- https://mcp.tripitaka-mcp.com/mcp (streamable-http, auth none, reachable true, uptime30d 0.9380531)
- https://mcp.tripitaka-mcp.com/sse (sse, auth none, reachable true, uptime30d 0.8888889)

## Packages

_none_

## Tools (14)

- `compare_translations`: Compare every available translation for a single segment.

💡 **Use this tool when:**
- The user asks about the meaning/translation of a single Pāli line
  and wants to see multiple translators side-by-side.
- Checking how different translators interpret the same line —
  technical terms like `dukkha`, `anattā`, `nibbāna` carry nuance
  that varies across translations.
- Academic work that needs to quote multiple translations.

🔍 **vs `get_sutta`:** this tool targets a **single segment** (line
level); `get_sutta` returns the **whole sutta**. To compare a whole
sutta you'd call `compare_translations` for each segment.

📋 **segment_id format:** `<sutta_id>:<paragraph>.<line>`, e.g.
`mn1:171.4` (Mūlapariyāyasutta paragraph 171 line 4 — "Nandī
dukkhassa mūlaṁ"). Find segment_ids via `get_sutta` or search results.

⚠️ **Current state:** the `translation` table is mostly empty (the DB
only loads default Pāli + English from bilara). `total_editions` is
usually 0; `text_pali` and `text_english` are always populated. Thai
editions will be added later.
- `define_from_suttas`: Find how the **suttas and Vinaya define a Pāli term in their own words**.

The canon defines its own terms with fixed formulas — "Katamañca …
dukkhaṁ?" (what is X?) … "ayaṁ vuccati … dukkhaṁ" (this is called X),
"X adhivacana" (X is a designation for …), or the Vinaya "X nāma". This
tool locates those definitional passages and returns them **cited**, so
the assistant can present the doctrinal essence straight from the source.

🧭 **This tool vs `get_word_definition`:**
- **`define_from_suttas`** → the *doctrinal* definition, how the term is
  defined **inside the canon**. Use for "how do the suttas define X",
  "what is the canonical definition of X", "define X from the suttas".
  Returns a few precise segments, not a lexicon essay.
- **`get_word_definition`** → the *lexical* definition from dictionaries
  (Payutto / PTS / DPPN). Use for etymology and word meaning.
They complement each other — offer both when the user wants the full
picture (dictionary sense + how the Buddha defined it).

📖 **How to present the result:**
Results are ranked; the top one is usually the canonical definition.
**Quote the Pāli (and English where present) verbatim** and render each
`cross_reference.tripitaka_mcp_reader.segment_url` as clickable markdown
so the user can verify. Do NOT paraphrase into your own definition — the
point is the canon's own words. Each result is tagged `kind`
(direct / simile) and `detail` (descriptive / enumerative); a
*descriptive* definition characterises the term, an *enumerative* one
lists its types — prefer the descriptive when explaining the essence.

⚠️ A result tagged `context: true` **does not contain the term in its own
line**. The canon's stock similes attach to a formula rather than to a
word: the four jhāna similes (bath powder, deep lake, lotus pond, white
cloth) never say *jhāna*, they illustrate the `vivicceva kāmehi …` formula
that opens the paragraph. Such rows are found through that paragraph, so
**say so when quoting one** — present it as the simile the passage uses,
not as a line that defines the term.
- `get_reference`: Build a proper citation string for a sutta.

💡 **Use this tool when:**
- The user wants a citation for academic work, an article, or a reference.
- You need to know the canonical location of a sutta (pitaka / nikāya).
- You want a ready-to-use formatted citation string.

🔗 vs `get_sutta`: this tool returns metadata + citation only, no
segments. Pair it with `get_sutta` when you want both the content
and the citation.
- `get_sutta`: Fetch a sutta's content — OR its table of contents (`mode="outline"`).

⚡ **Decide which mode BEFORE calling — don't fetch the whole sutta and
parse it yourself:**
- The user wants the **structure / outline / table of contents**, or asks
  **"how many sections/parts"** / "what's in it" → call
  `get_sutta(sutta_id, mode="outline")`. It returns the section list
  (titles + segment counts + ids), NOT the full text — cheap and exact.
- The user wants the **context around a search hit** → `around="<segment_id>"`
  (search tools hand you the id, e.g. `dn22:18.1`) + optional `window`.
- The user wants a **specific part** you already located → `segment_range="A..B"`
  or `offset`+`limit`.
- Only fetch the **whole** sutta (no mode/selector) when the user actually
  wants to read/quote a SHORT sutta in full. Long ones (DN, long
  Vinaya/Abhidhamma; > ~400 segments — e.g. `dn16` is 1,664) should almost
  always start with `mode="outline"`; pulling the entire text wastes the
  context window.

Uses standard SuttaCentral IDs, e.g.:
- `mn1` = Majjhima Nikāya sutta 1 (Mūlapariyāyasutta, 334 segments)
- `dn22` = Dīgha Nikāya sutta 22 (Mahāsatipaṭṭhānasutta, 454 segments)
- `dn16` = Dīgha Nikāya sutta 16 (Mahāparinibbānasutta — the longest
  sutta in the canon, 1,664 segments)
- `sn56.11` = Saṃyutta 56.11 (Dhammacakkappavattana)
- `mn62` = Majjhima Nikāya 62 (Mahārāhulovāda — advice to Rāhula)
- `dhp1-20` = Dhammapada verses 1-20 (KN uses range format)
- `mil3.1.1` = Milindapañha 3.1.1 (paracanonical, 3–4 level id)

💡 **Hints for the AI client:**
- **Quote `text_pali` / `text_english` directly from the returned
  segments** — do not rely on training memory. The system is
  verifiable; AI recall is often wrong.
- Short segments numbered `:0.n` are **headers**, not the teaching
  itself — actual content starts around `:1.1`. They run collection →
  book → chapter → sutta, so the **last** one is the sutta's own name
  (`sn35.245:0.3` = Kiṁsukopamasutta, while `:0.2` is its chapter).
  How many there are varies: DN/MN stop at `:0.2`, Iti reaches `:0.4`.
- Segments ending in "...niṭṭhitaṁ" (e.g. `mn1:194.10` =
  "Mūlapariyāyasuttaṁ niṭṭhitaṁ paṭhamaṁ") are **colophons**
  marking the close of the sutta.
- Segments containing `…pe…` (peyyāla) are **abbreviated repetitions**
  — not missing data. Pāli texts use this convention for repeated
  stock phrases.
- **Citing this sutta? Link the reader — a citation the user cannot open
  is a claim they cannot check.** The response's
  `cross_reference.tripitaka_mcp_reader` (`is_primary: true`) renders
  SuttaCentral's `bilara-data` verbatim (the same canonical Pāli and
  bilara English this response carries), so it shows the user the very
  text your answer rests on — verification, not self-promotion. Its `url`
  shows Pāli + English side by side and `segment_url` highlights the cited
  line. Render it as clickable markdown for EVERY sutta you name. It is
  the only verify link to give.
- **Cite at the segment level.** Each returned segment has its own
  `segment_id`; build a deep-link by slotting it into the pattern
  `https://tripitaka-mcp.com/read/<sutta_id>#<segment_id>`. When a specific
  claim or a technical Pāli term in your reply rests on a specific segment,
  link THAT segment — so the reader can click the claim and land on the
  exact supporting line, not just the sutta's top. e.g. the first-jhāna
  factors are in `sn45.8:10.2`, the fourth-jhāna in `sn45.8:10.5`.

📑 **Pagination — don't pull a whole giant sutta into context:**
By default this returns EVERY segment. That's fine for short suttas but a
single big one is huge (`dn16` ≈ 1,664 segments, `pli-tv-kd1` ≈ 3,591).
Use one of these instead when the sutta is long (rule of thumb: > ~400
segments) or when you only need part of it:
- `mode="outline"` — a table of contents only (section keys + titles +
  counts + `first_segment_id`/`last_segment_id` + `offset`), **no segment
  text**. Cheap way to see the structure, then fetch one section.
- `around="<segment_id>"` + `window=N` — return the N segments before and
  after a segment_id. **Ideal after a search:** `search_by_keyword` /
  `survey_corpus` hand you a precise `segment_id` (e.g. `dn22:18.1`); pass
  it here to read its context without downloading the whole sutta.
- `segment_range="<startId>..<endId>"` — inclusive slice between two
  segment_ids (use the `..` separator; omit the end id to go to the end).
  Pairs with `mode="outline"` (use a section's first/last id).
- `offset` (0-based) + `limit` — ordinal paging. The response's `page`
  block carries `next_offset` to fetch the following page.
Only one selector (around / segment_range / offset+limit) may be used at a
time. Every response includes `total_segments` (the full count) so you
know how much remains.
⚠️ **A window can hand you half a sentence or half a verse.** The corpus
is segmented line by line, not sentence by sentence, so the first and last
segments of any `around` / `segment_range` / `offset`+`limit` slice may
each be a fragment. `get_sutta("dhp1-20", offset=4, limit=1)` returns one
segment, `dhp1:1` = "Manopubbaṅgamā dhammā," — half of the opening line
of the Dhammapada, which goes on in `dhp1:2` ("manoseṭṭhā manomayā;").
Before quoting anything from the edge of a window, read its neighbours:
widen `window` / `limit`, or fetch `around=<segment_id>` for the passage
you actually mean to cite. `verify_quote` joins up to four consecutive
segments for this same reason.

✅ **Coverage (v1.1+):** all three pitakas at parity with SuttaCentral
`bilara-data`:
- Sutta Piṭaka (DN/MN/SN/AN/KN): Pāli + bilara EN (5,791 sections) —
  Sujato for DN/MN/SN/AN and most of KN; a few KN books have other
  bilara translators
- Vinaya Piṭaka: Pāli + Brahmali EN — SC codes e.g.
  `pli-tv-bu-vb-pj1` (Bhikkhu Pārājika 1), `pli-tv-bi-vb-pj1`
  (Bhikkhunī), `pli-tv-kd1` (Mahāvagga), `pli-tv-pvr10` (Parivāra),
  `pli-tv-bu-pm` (Bhikkhu Pātimokkha)
- Abhidhamma Piṭaka: 7 books (ds, vb, dt, pp, kv, ya, patthana) —
  Pāli only (bilara has no English translator for any Abhidhamma book)
- `get_word_definition`: Look up the dictionary meaning of a Pāli word, with sutta context.

Serves as a Pāli Dictionary Bridge — pairs the "definition" with the
"context where the Buddha actually used the word".

📖 **About the dictionary sources:**
This tool draws from multiple primary dictionaries, including
"พจนานุกรมพุทธศาสน์ ฉบับประมวลศัพท์" (Buddhist Dictionary —
Concept-Glossary edition) by Somdet Phra Buddhaghosacariya (P. A.
Payutto). The Thai-language entries are **original scholarly works**
(not translations), so they are **always available** even when
ENABLED_LANGUAGES has Thai disabled. The AI client should translate
Thai entries into the user's language if needed.

🔀 **Two kinds of thing in one payload.** `definitions[]` come from the
dictionaries, not the canon, so they carry **no `segment_id`**;
`appears_in_context[]` is corpus text and does carry one. `related_words[]`
are headwords holding your query as a substring, so opposites appear —
`dhamma` returns `adhamma`. Context examples are drawn at random: two
identical calls return different ones.
- `list_editions`: List the translation editions available, with coverage stats.

💡 **Use this tool when:**
- Before calling `compare_translations` or `get_sutta(edition=...)`,
  so you know which edition values are valid and worth comparing.
- The user asks which editions are loaded in the DB.

🔍 **Filtering:** Filtered by the server's `TRIPITAKA_ENABLED_LANGUAGES`
— when Thai is disabled the list is empty. Only enabled languages
are returned.

⚠️ **Current state:** the DB mostly holds Pāli (default from
SuttaCentral bilara) and English (bilara's translators). Thai editions
(`dhiranandi`, `jayasaro`, `mbu`, `royal`) aren't indexed yet — the
list returns empty until they're loaded.
- `list_structure`: Show the structure of all three pitakas with coverage statistics.

💡 **Use this tool when:**
- The user asks for an overview of the Tipiṭaka (what's in it / which
  collections).
- You need to check coverage before promising a search will find
  something — `segment_count > 0` is the active-loaded signal.
- Verifying scope when compiling an artifact.

📊 **Current state (v1.1+, at parity with SuttaCentral bilara-data):**
- **Sutta Piṭaka** complete: DN 37, MN 155, SN 1,829, AN 1,419, KN
  2,351 sections (~284,702 segments) — Pāli + bilara EN (Sujato for
  DN/MN/SN/AN and most of KN; a few KN books have other bilara
  translators)
- **Vinaya Piṭaka** complete: Bhikkhu Vibhaṅga 222, Bhikkhunī Vibhaṅga
  127, Khandhaka 22, Parivāra 51 + Pātimokkha 2 (~71,557 segments) —
  Pāli + Brahmali EN
- **Abhidhamma Piṭaka** complete: 7 books (ds, vb, dt, pp, kv, ya,
  patthana) ~88,414 segments — Pāli only (bilara has no English for
  any Abhidhamma book)
- **Total ~444,673 segments** in the DB

⚠️ **Known quirks:**
- The schema carries duplicate legacy + SC-modern codes side by side:
  - Vinaya: `vin-v/vin-m/vin-c/vin-p` (legacy, segment_count = 0)
    alongside `pli-tv-bu-vb/pli-tv-bi-vb/pli-tv-kd/pli-tv-pvr`
    (active, populated).
  - Abhidhamma: `ym/pt` (legacy = 0) alongside `ya/patthana` (active).
- **Use the `active` flag** — each nikaya carries `active: true/false`
  (true ⇔ `segment_count > 0`). Pick `active` nikayas; the others are
  metadata placeholders from an older migration.

🌐 **Languages:** Returns Pāli + Thai + English labels regardless of
enabled set (these are metadata, not segment text). Text content
follows ENABLED_LANGUAGES. Thai translations aren't loaded yet.
- `open_sutta_viewer`: Open an interactive sutta viewer inside the chat — Pāli + English,
plus an optional third row in the user's own language translated BY YOU.

Renders each segment as: Pāli on top (canonical), the bilara-data
English below it (verification anchor), and — when you supply
`translations` — your translation in the user's language, clearly
badged as AI-generated. Prefer this over dumping raw segments when the
user wants to *read* a sutta.

- `sutta_id` — standard SuttaCentral id, e.g. `sn56.11`, `mn10`, `dn22`.
- `around` — a segment_id (e.g. `dn22:18.1`, from a search hit) to centre
  on; that segment is highlighted and scrolled into view. Use this after
  a search so the reader lands on the exact cited line.
- `offset` — 0-based segment index for paging long suttas (use
  `next_offset` from the previous result). Do NOT combine with `around`.
- `window` — segments before/after `around` to include (default 12).

🌐 **Translating for the user (important):** when the conversation
language is neither English nor Pāli, you SHOULD translate the displayed
segments and pass them via `translations` so the user reads in their own
language while still seeing the originals:
1. Fetch the segments first (`get_sutta` with the same selector) so you
   have the exact Pāli + English text. (Already called this tool without
   translations? The result contains the segments — translate them and
   call this tool AGAIN with the same selector plus `translations` to
   upgrade the view.) Your translation must travel through the
   `translations` parameter to appear in the viewer — writing it as a
   normal chat message leaves the viewer bilingual and looks broken; the
   tool always accepts `translations`, so never report it as missing.
2. Translate **from the Pāli as the source, using the English as a
   semantic guide** — never relay-translate from English alone. Preserve
   untranslatable doctrinal terms (dukkha, jhāna, taṇhā…) as loanwords
   with a brief gloss instead of forcing equivalents.
3. Call this tool with `translations=[{segment_id, text}, ...]` covering
   ONLY the segments being displayed (never a whole long sutta),
   `translation_language` (BCP-47, e.g. "th", "es"), and
   `translation_disclaimer` — one short line IN THE USER'S LANGUAGE
   saying the translation is AI-generated in this conversation and
   should be checked against the Pāli/English above.
Translations are conversation-ephemeral: nothing is stored server-side;
the canon stays Pāli + English only. Translations whose segment_id is
not in the displayed window are dropped (reported in
`translations_dropped`).

Without `around`, shows the sutta from the top (capped for long suttas).
- `parse_pali_word`: Strip Pāli inflectional suffixes to find the root form (basic stem).

💡 **Use this tool when:**
- You find an inflected Pāli word (e.g. `dukkhassa`, `bhikkhūnaṁ`) and
  `get_word_definition` doesn't find it directly — Pāli inflects nouns
  across 7 cases × 2 numbers, ~16 forms per root.
- You want to see possible stems before another `get_word_definition`
  lookup.

🔄 **Recommended workflow:**
`parse_pali_word(inflected_form)` → get `possible_stems[]` →
call `get_word_definition(stem)` per stem until you find a definition.

⚠️ **Limitations:**
- Rule-based first-pass — strips common suffixes (case endings, vowel
  shortening). Not a full morphological analyzer.
- Compound words (samāsa) are NOT split — `dukkhanirodha` won't be
  broken into `dukkha` + `nirodha`.
- Sandhi (sound junctions) like `tena ahaṁ → tenāhaṁ` aren't reversed.
- Returns **possible** stems — verify each via `get_word_definition`.

⚠️ **`possible_stems` are rule output, not findings.** They come from the
suffix table applied to your input — nothing was looked up, so no field
carries a `segment_id`, and some candidates are not Pāli words at all. Treat
each as a spelling to try in `get_word_definition`, never as an attested
form.
- `search_by_keyword`: Keyword search across the Pāli Tipiṭaka (trigram word-similarity).

Searches the configured enabled language(s) on the server. Filterable
by pitaka and translation edition.

💡 **Hints for the AI client:**
The system's canonical reference is Romanised Pāli (from SuttaCentral).
If the user asks in a disabled or unsupported language, translate the
keyword to **Romanised Pāli (preferred) or English** before calling this
tool — e.g. "suffering" → "dukkha", "mindfulness of breathing" →
"ānāpānassati". See the server instructions for the enabled language set.

✅ **Diacritics do not matter.** `anapanassati` and `ānāpānassati` return
the same thing; so do `nibbana` and `nibbāna`. Write the macrons if you
know them, guess without them if you don't — neither costs you results.

⚠️ **A common Pāli noun is a poor query.** `samudda` (sea) matches ~700
segments and the top of that list is mostly section headings, not the
passage that teaches anything. Two things to do instead:
- Search the **rarest distinctive noun** in the passage, not its most
  obvious one. For the simile of the blind turtle, `turtle`/`kacchapa`
  gets there; `ocean`/`samudda` does not, in either language.
- **Prefer English, or raise the limit.** `turtle` returns SN 56.47 and
  SN 56.48 inside the default window; `kacchapa` matches them too but
  ranks them past 30, so you need `limit=50` to see them.
- A word inside a **compound** may be out of reach entirely: `samudda`
  scores 0.50 against `mahāsamudde` (compounded *and* inflected), under
  the 0.6 cutoff, so SN 56.47 is not ranked low — it is excluded. Trying
  more spellings will not recover it; search a different word instead.

🔍 **Pick the right search tool for the question shape:**
- **Term lookup (exact word appearances)** — e.g. "occurrences of
  `ānāpānassati`": this tool is best (trigram nails the exact word).
- **Concept search ("discourses about X")** — e.g. "discourses about
  mindfulness of breathing": **use `search_hybrid` instead.** Canonical
  Pāli has two quirks that hurt keyword search for concepts:
    • Section headings (`Ānāpānapabba`) often use a different word than
      the teaching body, which uses verb forms (`assasati`, `passasati`,
      `dīghaṁ`, `rassaṁ`). E.g. DN22's Ānāpānapabba has 16 segments but
      the word `ānāpāna` appears in only 2 (header + footer) — the
      actual teaching segments won't match.
    • Stock phrases (e.g. `So satova assasati, satova passasati`)
      recur in 10+ suttas, so a keyword query ranks broadly and won't
      pinpoint the canonical reference.
- **General keyword survey** — set `limit≥30` and filter client-side,
  or call multiple related forms (root verb + noun + compound).

📊 **Reading the two scores.** `word_similarity` is the primary sort key: it
scores your query against the best-matching span inside the line, 0 to 1,
and a row appears at all only if it clears the 0.6 cutoff. `similarity`
scores it against the **whole line**, so it falls as the line grows longer.
Both measure resemblance of letters — neither measures doctrinal importance.
- `search_hybrid`: Hybrid search — combines keyword + semantic search via RRF.

Uses Reciprocal Rank Fusion (RRF) to merge exact-word results with
meaning-based results. **This is the recommended tool for "discourses
about X" / concept queries**, because the semantic side catches suttas
that discuss a concept using different vocabulary (e.g. some
mindfulness-of-breathing suttas use `assasati/passasati/dīghaṁ`
instead of `ānāpānassati`).

⚠️ **Send the question and nothing else. Do not pad the query.**
The whole string becomes one vector, so every word you add moves it.
Appending your own candidate terms — synonyms, Pāli equivalents, a
keyword list — searches for the blend, not for the question.

Measured on `Buddha flies in the sky`: asking it plainly put the right
passage at **rank 1** (3 relevant suttas). Appending three guessed Pāli
terms (`buddha, agga, sagga`) pushed it down to **rank 5** and left
only 1 — because `agga` (supreme) and `sagga` (heaven) drag the vector
toward their own meanings.

Have candidate terms worth searching? Give them their own
`search_by_keyword` call and merge the two result lists. One tool asks
what a passage means, the other asks where a word occurs; combined into
a single string they cancel out.

Rewriting the question to sound more canonical does not help either —
the same query phrased as `rose into the air and flew like a bird`
scored **zero** relevant hits.

💡 **Hints for the AI client:**
- English queries usually work best (e.g. `mindfulness of breathing`)
  because the embedding model is multilingual but EN-primary.
- Thai stop-word handling is weak. If a Thai query underperforms, the
  AI client should translate to Pāli/English first (see server
  instructions).
- The default `limit=5` is often too small for a topic survey — use
  `limit=15-20` (max 20) for good coverage.
- `language` only chooses what comes back; it does not change what
  matches or how results are ranked.
- Looking for a concrete thing rather than a concept (an animal, an
  object, a place)? `search_by_keyword` over `language="english"` is
  often better. The translations are segment-aligned to the Pāli, so
  one search for `turtle` finds every turtle passage whatever the Pāli
  underneath says (`kacchapa`, `kumma`, `maṇḍūkakacchapa`), and each
  hit still carries its segment id.
- Ranking is by similarity, NOT canonical importance — locus
  classicus suttas (e.g. MN118, DN22) may rank below smaller suttas
  that happen to use the exact vocabulary. Treat results as a
  starting point, then call `get_sutta` for the canonical references.
- `search_semantic`: Semantic search — match by meaning, not exact words.

Uses vector similarity (cosine distance) over `text_pali` embedded with
a multilingual MiniLM model.

🤔 **In most cases you should use `search_hybrid` instead** — it
combines this semantic search with keyword search and ranks better.
Use this tool only when you need:
- Pure semantic results (no keyword influence)
- Fine-grained `threshold` tuning (hybrid uses RRF which is harder
  to tune)
- To debug what semantic alone picks up vs keyword

⚠️ Known limitations:
- The index is **Pāli only** (English/Thai queries pass through the
  multilingual embedding but the model isn't tuned on Pāli)
- English queries usually embed better than Thai (model is EN-primary)
- For specific Pāli terms (`appamāda`, `dukkha`), exact match is
  better — use `search_by_keyword` instead
- Pāli stock phrases recur in many suttas → similarity scores
  cluster; read the top 10, don't trust rank 1 alone

📐 **Reading `distance`.** It is cosine distance between your query's
embedding and the segment's, so smaller is closer, and a row is returned
only at or under `threshold` (default 0.7) — an empty result means nothing
cleared that cut. Ordering is proximity of meaning in one model's space, not
doctrinal importance.
- `survey_corpus`: Exhaustively survey the WHOLE Tipiṭaka for a term — guaranteed complete.

Use this (not `search_by_keyword`) when the question is about **coverage or
counting** rather than "show me the best passages":
- "How many times does Kusinārā appear in the canon?"
- "Every place ānāpānassati is mentioned — don't miss any"
- "Which pitakas/how many suttas mention this term?"

Unlike `search_by_keyword` (ranked, capped at 50, no total), this returns an
**exact count**, a **per-pitaka breakdown**, the **distinct surface forms**
that matched (so you can audit and discard over-matches), and a paginated
enumeration. The `lexical` result carries `complete: true` — a hard
guarantee that nothing was dropped for the chosen `match_scope`.

Two layers, two different promises:
- **lexical** — the word and its forms. Deterministic + EXHAUSTIVE.
- **semantic** (`mode="thorough"`, hosted only) — passages teaching the same
  concept with DIFFERENT vocabulary (e.g. ānāpānassati via
  `assasati`/`passasati`). Approximate, **NOT exhaustive** — it never claims
  completeness, it only boosts recall.
- `verify_quote`: Verify, check, or confirm a Buddha quote — does the canon really say it?

Fact-check a quotation attributed to the Buddha. Catches fake, misquoted,
misattributed and misremembered passages, and finds the true reading.

Paste a line that has been quoted or half-remembered — Pāli or English —
and this says whether the canon really contains it, cites where, and if
not, shows the closest thing that is actually there.

🧭 **When to use this:**
- A quote is attributed to the Buddha and you are not certain it is real.
  A fabricated line that *sounds* canonical is the hardest error to catch
  by reading, because it reads correctly. Check it instead of trusting it.
- Someone recalls a passage imperfectly, or a chanted form has drifted
  from the written one. The tool shows the received text beside theirs.
- **Before repeating any Pāli you did not get from these tools**, verify
  it. This is cheap and it is the difference between citing and guessing.

⚠️ **Do not present an unverified passage as canonical.** If the verdict
is `not_found`, say plainly that the line is not in the canon rather than
quoting it with a hedge — a hedged fabrication still spreads.

⚖️ **Verdict thresholds.** `exact` = `contained` **or** `similarity` ≥
0.995; `close` = `coverage` ≥ 0.85 **and** `similarity` ≥ 0.55; else
`not_found`. `exact` means letters matched once folded, not that a human
confirmed the reading. Matches also carry `coverage` (your letters present,
in order), `contained` (present whole) and `spans` (segments joined; >1 →
`segment_id` is the first only).
