_ registry / mcp streamable-http · checked 2h ago

qurangallery-shamela

https://qurangallery.com

Registry code: 21ada720e9b1b210

api record

Quran Gallery's classical Islamic library: 8,594 works by 3,188 authors from Maktabah al-Shamela — Quran, hadith, tafsir, fiqh, aqeedah and the Arabic language sciences. Every passage is cited to its printed edition, volume and page, with the edition data the library records and a shamela.ws link, and the main text (matn) is kept separate from the editor's footnotes. No account, sign-in or API key is needed.

Search classical Islamic texts and cite what you find. Every passage carries a real book, printed edition, volume/page and verbatim Arabic — quote those rather than recalling a reference.…

endpoint
https://qurangallery.com/mcp/shamela
protocol
streamable-http ·2025-06-18
authentication
none observed
public key
none — nobody has proven they own this listing · is it yours? claim it
karma
0 · newcomer
_ is it live, free and safe measured by this hub
Is qurangallery-shamela live?
Yes — it answered the hub's last check (checked 2h ago). It answered 100% of checks over the last 30 days.
Is qurangallery-shamela free to use?
Yes — the hub reached it with no key and no payment.
What tools does qurangallery-shamela have?
39 tools: search_library, search, fetch, shamela_search_pages, shamela_search_books, shamela_search_authors, shamela_get_toc, shamela_get_book, ….
Is qurangallery-shamela safe to connect?
The hub found no text in its card or tool descriptions aimed at the agent reading them. It measures what the server answers, not its code — grant it only the access its tools need.
reachable
live
uptime, 30 days
100%

90 days 100%· all time 100%

latency
274ms

last good check

priced tools
0

of 39 tools

_ answered our checks, 90 days 1 checks · signed record
  • unknown → live
_ usage and payments 30 days

Calls placed through this hub's router, from its own receipts. Every caller and every payer counts the same; the chain total is counted from three payers.

accounts
0

through this hub

calls served
0

successful

paid through this hub
0 USDC

what callers paid

_ what it can do 39 tools
1 open 38 never probed 1 of 39 classified

Price is per tool, not per server. An agent whose handshake is open can hold tools that demand a key or a payment, and one figure for the whole agent sends callers into a wall.

  • shamela_brief open 2h ago

    بيان قدرات الخادم ومزالق البحث Capability handshake + operating brief for programmatic callers. Call this FIRST, before sending any argument you are not certain this build accepts. Returns `capabilities[]` — the arguments and response fields this server supports — plus `server_version`, `limits`, and `traps[]`. Every tool input here is strict, so sending an unknown argument (e.g. `shape`, `response_format:'none'`) to a server that predates it is a HARD validation error that fails the whole call, not a silently ignored field; and `tools/list` cannot help, because it proves a tool exists without saying which arguments it takes. Gate each new argument on its capability string. `traps[]` is the operating half: each entry pairs a way a search result misleads with the exact RESPONSE FIELD that proves it fired (`relaxed_min_match`, `zero_hit_terms`, `paging_ceiling`, `coverage.sample_is_complete`, `scope_resolution.dropped`, `madhab_source`, `probes[].qualifiers`, …), so a caller checks the envelope instead of trusting prose. `brief_hash` covers every other field: cache the brief and re-fetch only when it changes. Pure text and constants — no library access, so it answers even when the Shamela install is missing. Examples: shamela_brief({}), shamela_brief({response_format:'json'}).

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "properties": {
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 17 lines
  • search_library unknown never probed

    Search a Maktabah al-Shamela library of 8,594 classical works and return passages with the book, author, printed edition page and the verbatim Arabic. Each passage also carries its edition, editor and publisher when the library records them, and a sourceUrl on shamela.ws. Runs an exact-phrase pass first and falls back to a token search, reporting which one answered. A scope is REQUIRED: unscoped ranking in this corpus is by term frequency, which surfaces a poetry anthology above a hadith collection.

    mcp-tool

    {
      "type": "object",
      "required": [
        "query",
        "scope"
      ],
      "properties": {
        "limit": {
          "type": "integer",
          "default": 5,
          "maximum": 10,
          "minimum": 1,
          "description": "Maximum passages to return (max 10)."
        },
        "query": {
          "type": "string",
          "description": "Arabic search text. A quoted wording works best; a topic also works."
        },
        "scope": {
          "enum": [
            "hadith-primary",
            "tafsir-canonical",
            "fiqh-hanafi",
            "fiqh-maliki",
            "fiqh-shafii",
            "fiqh-hanbali",
            "fiqh-comparative",
            "aqeedah",
            "hadith-sciences",
            "tafsir",
            "seerah",
            "lugha"
          ],
          "type": "string",
          "description": "hadith-primary — The eight primary hadith collections, edition-pinned; tafsir-canonical — Canonical tafsir — Ibn Kathir, al-Tabari, Ibn al-Jawzi first; fiqh-hanafi — Hanafi fiqh (+ usul, comparative); fiqh-maliki — Maliki fiqh (+ usul, comparative); fiqh-shafii — Shafi'i fiqh (+ usul, comparative); fiqh-hanbali — Hanbali fiqh (+ usul, comparative); fiqh-comparative — All four schools plus comparative and fatwa collections; aqeedah — Creed and sects; hadith-sciences — Hadith commentary, takhrij and the sciences of hadith; tafsir — The whole tafsir shelf and Quranic sciences; seerah — Prophetic biography, history and biographical dictionaries; lugha — Arabic language, lexicons, grammar and adab"
        }
      },
      "additionalProperties": false
    }
    arguments 39 lines
  • search unknown never probed

    Search all 8,594 works of the classical Islamic library and return up to 10 matching pages as {id, title, url}. Pass an id to `fetch` to read that page. Query in ARABIC. A title ending in [footnote match] matched only an editor's footnote, not the main text; [token match] means the exact phrase was not found and the words occur separately. This search is unscoped and ranks by term frequency; for a hadith, tafsir or fiqh question, search_library with a scope ranks the standard sources first.

    mcp-tool

    {
      "type": "object",
      "required": [
        "query"
      ],
      "properties": {
        "query": {
          "type": "string",
          "description": "Arabic search text — a quoted wording or a topic."
        }
      },
      "additionalProperties": false
    }
    arguments 13 lines
  • fetch unknown never probed

    Read one library page by the id `search` returned ("{bookId}:{pageId}"). Returns the main text and, under a separate label, the footnotes — the editor's notes, not the author's words — with book, author, printed volume/page, the edition data the library records, and the shamela.ws link.

    mcp-tool

    {
      "type": "object",
      "required": [
        "id"
      ],
      "properties": {
        "id": {
          "type": "string",
          "description": "A document id from `search`, e.g. \"117359:1796\"."
        }
      },
      "additionalProperties": false
    }
    arguments 13 lines
  • shamela_search_pages unknown never probed

    بحث في صفحات الكتب Search the body (matn) and footnotes (الحواشي) of every Shamela page the user has downloaded locally. AND-combines tokens; each token can match in any of the search_in fields. Default scope is the full downloaded library; pass `scope` (book_ids/author_ids/category_ids/period_*/downloaded_only/madhab/book_type/citable_only) to narrow. `scope.madhab` ('hanafi'|'maliki'|'shafii'|'hanbali') answers a school-specific fiqh question — it uses Shamela's school categories AND the main author's school, so it reaches مسائل فقهية and الفقه العام where no school is filed; books whose school is unknown are excluded, so leave it unset for comparative questions. `scope.book_type` excludes what is not an authored book (transcript/thesis/journal/electronic/manuscript), and `scope.citable_only` keeps only books whose page numbers match a real printed edition — set it whenever the answer will quote a page as a citation. `options` controls morphology (Arabic root expansion via AlKhalil — NOTE Arabic roots are ambiguous and the analyzer keeps every reading, so «كتب» also searches تبب/توب/وتب, i.e. perdition and repentance; the response returns `morphology_expanded_to` naming exactly what was searched, and results are a SUPERSET of the word you asked for), wildcards (`*`/`?` per token, cannot combine with morphology), and search_in subset (body/foot). Returns total_hits + paginated results with book name, author, printed-page label, and a snippet with <mark>...</mark> around matches; coverage rolls up by category/century/book/author — EXACT (every matching page counted) whenever `coverage.sample_is_complete` is true, and otherwise a top-scoring sample of `coverage.sampled_hits` of `total_hits`, whose shares are then indicative rather than a census. by_category/by_century cover the whole sample; by_book/by_author are the largest `coverage.top_n` only, so they do not sum to it. Plus `edition_groups` whenever hits landed in two or more catalog entries of one work (several editions of the same book) — read those as one source, not several. preserve_diacritics/_hamza/_digits return OPTION_NOT_SUPPORTED here — use `shamela_search_exact` for those. Use `shamela_search_titles` for chapter title search instead. Examples: shamela_search_pages({query:'الكلام'}), shamela_search_pages({query:'استصناع', scope:{category_ids:[17]}}), shamela_search_pages({query:'كلم', options:{morphology:true}}).

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "query"
      ],
      "properties": {
        "limit": {
          "type": "integer",
          "default": 20,
          "maximum": 100,
          "minimum": 1,
          "description": "Maximum number of results to return (1-100, default 20)."
        },
        "query": {
          "type": "string",
          "minLength": 1,
          "description": "Arabic search phrase. Multiple words are AND-combined; each can match in body or footnotes. If a word does not exist in the library under the spelling you used, the spelling the books DO use is searched instead and reported in `respelled_terms` — say so when you quote the result, because the source writes the word differently."
        },
        "scope": {
          "type": "object",
          "properties": {
            "madhab": {
              "type": "array",
              "items": {
                "enum": [
                  "hanafi",
                  "maliki",
                  "shafii",
                  "hanbali"
                ],
                "type": "string"
              },
              "description": "Restrict to books of these Sunni schools. master.db has NO madhab field, so this is resolved two ways: a book filed under a school-specific category (الفقه الحنفي/المالكي/الشافعي/الحنبلي) counts directly, and otherwise the MAIN AUTHOR's school is used, inferred from the categories that author's own books are filed under. That second path is what reaches مسائل فقهية and الفقه العام, where Shamela records no school — it raises madhab coverage of the fiqh shelves from 27% to 43%. Books whose school cannot be established are EXCLUDED by this filter, so do not read absence as 'no school said this'; for comparative questions leave madhab unset and scope by category instead."
            },
            "book_ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "description": "Restrict to these book IDs. Use `shamela_resolve` or `shamela_list_downloaded_books` to find IDs. Combine with other scope keys for intersection."
            },
            "book_type": {
              "type": "array",
              "items": {
                "enum": [
                  "book",
                  "journal",
                  "manuscript",
                  "thesis",
                  "electronic",
                  "transcript"
                ],
                "type": "string"
              },
              "description": "Restrict to these kinds of entry (master.db.book.book_type — the same filter row Shamela's own 'اختيار كتاب' window shows). 'transcript' (تفريغات) is a transcribed lecture series, not an authored book, and 'thesis'/'electronic' are likewise not printed works — excluding them sharpens a scholarly search. Of 8,594 entries, 7,334 are 'book'; 1,260 are not."
            },
            "period_to": {
              "type": "integer",
              "maximum": 2000,
              "minimum": 1,
              "description": "Hijri year, inclusive upper bound. Pair with period_from."
            },
            "author_ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "description": "Restrict to books by these authors. Includes co-authored books. Use `shamela_resolve(type='author')` to find IDs."
            },
            "period_from": {
              "type": "integer",
              "maximum": 2000,
              "minimum": 1,
              "description": "Hijri year, inclusive lower bound. Matches book.book_date (composition year) OR author.death_number (death year). Pair with period_to."
            },
            "category_ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "description": "Restrict to books in these categories. Categories are flat in master.db (no parent_id, no transitive subtree expansion). Use `shamela_list_categories` to find IDs."
            },
            "citable_only": {
              "type": "boolean",
              "default": false,
              "description": "Keep only books whose page numbering matches a real printed edition (master.db.book.printed == 1 — Shamela's «يوافق المطبوع»). The other 1,417 entries are paginated by Shamela automatically, so their page numbers are NOT a citable reference to any published edition. Set this true whenever the answer will quote a page number as a citation."
            },
            "downloaded_only": {
              "type": "boolean",
              "default": false,
              "description": "Restrict to books actually downloaded on this user's machine (master.db.book.major_ondisk > 0). Critical for honest research scoping when using `shamela_search_pages` — only downloaded books have searchable content."
            }
          },
          "description": "Restrict the search to specific books, authors, categories, or a Hijri date range. Use shamela_list_categories / shamela_resolve to find IDs.",
          "additionalProperties": false
        },
        "shape": {
          "enum": [
            "full",
            "brief",
            "index"
          ],
          "type": "string",
          "default": "full",
          "description": "How much of each hit to return. 'full' (default) is every field on every row, including the snippets — ask for this when the answer will QUOTE the page. 'brief' keeps book_id/page_id, printed_page, match_quality and matched_in_section and hoists book metadata into a top-level `books` dictionary keyed by book_id, but carries NO snippet: it answers which pages these are, how well each matched and how to cite them, for a fraction of the characters, so far more rows survive the budget. 'index' returns book_id/page_id only, for choosing what to read before paying to read it. Requires the 'shape.full_brief_index' capability (see shamela_brief)."
        },
        "offset": {
          "type": "integer",
          "default": 0,
          "minimum": 0,
          "description": "Number of results to skip for pagination. Use with `limit` to page through large result sets. The response includes `has_more` and `next_offset` to drive paging."
        },
        "options": {
          "type": "object",
          "properties": {
            "sort": {
              "enum": [
                "relevance",
                "chronological"
              ],
              "type": "string",
              "default": "relevance",
              "description": "Result order. 'relevance' (default) ranks by how well the page matches — the right answer to 'find me X'. 'chronological' orders by the author's death year, then book, then page, so the EARLIEST sources come first — the right answer to 'who says this first' or 'how does this idea travel through the tradition'. Undated books sort last."
            },
            "search_in": {
              "type": "array",
              "items": {
                "enum": [
                  "body",
                  "foot"
                ],
                "type": "string"
              },
              "default": [
                "body",
                "foot"
              ],
              "description": "Which page sections to search. 'body' = matn (المتن), 'foot' = footnotes (الحواشي). Default is both. Reader comments (التعليقات) are NOT searchable — Shamela keeps them in database/user/data.db, outside the Lucene index, so passing 'comment' returns OPTION_NOT_SUPPORTED rather than silently matching nothing. To search chapter titles use the separate `shamela_search_titles` tool."
            },
            "wildcards": {
              "type": "boolean",
              "default": false,
              "description": "Interpret `*` (zero-or-more characters) and `?` (exactly-one character) within tokens. Examples: `صيا*` matches صيام/صيامهم, `ص?م` matches صام/صوم/صمم. Leading wildcards (`*ابن`) are slow. Cannot combine with `morphology`. With this OFF, `*` and `?` are treated as ordinary punctuation and split the word, so a stray one no longer zeroes the search. CAVEAT on prefixes: the index folds some words as a WHOLE — «مائة» is stored as «ميه», «داوود» as «داود» — and a wildcard stem is folded without the letters the wildcard replaced, so `مائ*` becomes `ماي*` and reaches مائع/مائدة but NOT مائة. When a prefix search returns less than you expect for a word like these, search the full word without a wildcard instead."
            },
            "morphology": {
              "type": "boolean",
              "default": false,
              "description": "Search Arabic root forms via the AlKhalil morphological analyzer. Useful when looking for all word forms (يكتب، كاتب، مكتوب…) of a root (كتب). Cannot be combined with `wildcards`."
            },
            "preserve_hamza": {
              "type": "boolean",
              "default": false,
              "description": "Returns OPTION_NOT_SUPPORTED here — the index folds ٱآأإ→ا before storing. Use `shamela_search_exact({preserve:{preserve_hamza:true}})` to distinguish «أحمد» from «احمد»."
            },
            "preserve_digits": {
              "type": "boolean",
              "default": false,
              "description": "Returns OPTION_NOT_SUPPORTED here — the index unifies Arabic-Indic and Western digits. Use `shamela_search_exact({preserve:{preserve_digits:true}})` to distinguish ٢٥٥ from 255."
            },
            "relax_when_empty": {
              "type": "boolean",
              "default": true,
              "description": "When the AND of every term matches no page, retry requiring only the largest number of your terms that some page does carry, and report it in `relaxed_min_match`. Only ever fires on a result that would otherwise be empty, and never drops below half your terms. Set false to keep a strict all-terms-or-nothing search."
            },
            "preserve_diacritics": {
              "type": "boolean",
              "default": false,
              "description": "Returns OPTION_NOT_SUPPORTED here — the index itself stores no diacritics, so this search cannot honor them. Use `shamela_search_exact({preserve:{preserve_diacritics:true}})`, which re-checks each candidate against the book's raw text."
            }
          },
          "description": "Search options: morphology, wildcards, sort, search_in (body/foot), relax_when_empty. Defaults to body+foot, no toggles. preserve_* redirect to shamela_search_exact.",
          "additionalProperties": false
        },
        "exclude_pages": {
          "type": "array",
          "items": {
            "type": "object",
            "required": [
              "book_id",
              "page_id"
            ],
            "properties": {
              "book_id": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "page_id": {
                "type": "integer",
                "minimum": 0
              }
            },
            "additionalProperties": false
          },
          "description": "Exact pages to keep out — the pages a previous round already returned. Excluding these rather than whole books is what lets a follow-up search find MORE evidence in a book that has already contributed some."
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        },
        "exclude_book_ids": {
          "type": "array",
          "items": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "description": "Book ids to keep out of the results entirely."
        },
        "exclude_author_ids": {
          "type": "array",
          "items": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "description": "Exclude every book by these authors."
        },
        "exclude_category_ids": {
          "type": "array",
          "items": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "description": "Exclude every book on these shelves."
        },
        "exclude_work_group_ids": {
          "type": "array",
          "items": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "description": "Exclude every catalogue entry of these works — all printings at once. This is the one to use after a round returned «صحيح البخاري - ط السلطانية»: excluding that book_id alone leaves the other seven entries of group 1681 free to return the same text again as if it were a second source."
        }
      },
      "additionalProperties": false
    }
    arguments 244 lines
  • shamela_search_books unknown never probed

    بحث في فهرس الكتب Search Shamela's catalog of ~8,500 books by name, author, bibliography text, OR the editorial description of what the book is about (1,729 entries carry one, averaging ~1,500 characters). That last field is what finds a book by SUBJECT rather than title: «الرجز» matches 0 titles and 2 descriptions. Each hit reports `matched_in`: 'bibliography' (title/author/publication line) or 'description'. Pre-built index — works even before any books are downloaded. scope.book_ids is not accepted (the catalog IS the universe); use scope.author_ids, category_ids, period_*, downloaded_only. Returns paginated results with book name, author, category, book_date, downloaded flag, and a snippet showing the matched text. Examples: shamela_search_books({query:'الأصول'}), shamela_search_books({query:'فقه', scope:{category_ids:[17], downloaded_only:true}}).

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "query"
      ],
      "properties": {
        "limit": {
          "type": "integer",
          "default": 20,
          "maximum": 100,
          "minimum": 1,
          "description": "Maximum number of results to return (1-100, default 20)."
        },
        "query": {
          "type": "string",
          "minLength": 1,
          "description": "Arabic search phrase matched against the book name + author + bibliography concatenation."
        },
        "scope": {
          "type": "object",
          "properties": {
            "period_to": {
              "type": "integer",
              "maximum": 2000,
              "minimum": 1,
              "description": "Hijri year, inclusive upper bound. Pair with period_from."
            },
            "author_ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "description": "Restrict to books by these authors. Includes co-authored books. Use `shamela_resolve(type='author')` to find IDs."
            },
            "period_from": {
              "type": "integer",
              "maximum": 2000,
              "minimum": 1,
              "description": "Hijri year, inclusive lower bound. Matches book.book_date (composition year) OR author.death_number (death year). Pair with period_to."
            },
            "category_ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "description": "Restrict to books in these categories. Categories are flat in master.db (no parent_id, no transitive subtree expansion). Use `shamela_list_categories` to find IDs."
            },
            "downloaded_only": {
              "type": "boolean",
              "default": false,
              "description": "Restrict to books actually downloaded on this user's machine (master.db.book.major_ondisk > 0). Critical for honest research scoping when using `shamela_search_pages` — only downloaded books have searchable content."
            }
          },
          "description": "Optional: restrict to specific authors, categories, periods, or downloaded-only.",
          "additionalProperties": false
        },
        "offset": {
          "type": "integer",
          "default": 0,
          "minimum": 0,
          "description": "Number of results to skip for pagination. Use with `limit` to page through large result sets. The response includes `has_more` and `next_offset` to drive paging."
        },
        "options": {
          "type": "object",
          "properties": {
            "sort": {
              "enum": [
                "relevance",
                "chronological"
              ],
              "type": "string",
              "default": "relevance",
              "description": "Result order. 'relevance' (default) ranks by how well the page matches — the right answer to 'find me X'. 'chronological' orders by the author's death year, then book, then page, so the EARLIEST sources come first — the right answer to 'who says this first' or 'how does this idea travel through the tradition'. Undated books sort last."
            },
            "search_in": {
              "type": "array",
              "items": {
                "enum": [
                  "body",
                  "foot"
                ],
                "type": "string"
              },
              "default": [
                "body",
                "foot"
              ],
              "description": "Which page sections to search. 'body' = matn (المتن), 'foot' = footnotes (الحواشي). Default is both. Reader comments (التعليقات) are NOT searchable — Shamela keeps them in database/user/data.db, outside the Lucene index, so passing 'comment' returns OPTION_NOT_SUPPORTED rather than silently matching nothing. To search chapter titles use the separate `shamela_search_titles` tool."
            },
            "wildcards": {
              "type": "boolean",
              "default": false,
              "description": "Interpret `*` (zero-or-more characters) and `?` (exactly-one character) within tokens. Examples: `صيا*` matches صيام/صيامهم, `ص?م` matches صام/صوم/صمم. Leading wildcards (`*ابن`) are slow. Cannot combine with `morphology`. With this OFF, `*` and `?` are treated as ordinary punctuation and split the word, so a stray one no longer zeroes the search. CAVEAT on prefixes: the index folds some words as a WHOLE — «مائة» is stored as «ميه», «داوود» as «داود» — and a wildcard stem is folded without the letters the wildcard replaced, so `مائ*` becomes `ماي*` and reaches مائع/مائدة but NOT مائة. When a prefix search returns less than you expect for a word like these, search the full word without a wildcard instead."
            },
            "morphology": {
              "type": "boolean",
              "default": false,
              "description": "Search Arabic root forms via the AlKhalil morphological analyzer. Useful when looking for all word forms (يكتب، كاتب، مكتوب…) of a root (كتب). Cannot be combined with `wildcards`."
            },
            "preserve_hamza": {
              "type": "boolean",
              "default": false,
              "description": "Returns OPTION_NOT_SUPPORTED here — the index folds ٱآأإ→ا before storing. Use `shamela_search_exact({preserve:{preserve_hamza:true}})` to distinguish «أحمد» from «احمد»."
            },
            "preserve_digits": {
              "type": "boolean",
              "default": false,
              "description": "Returns OPTION_NOT_SUPPORTED here — the index unifies Arabic-Indic and Western digits. Use `shamela_search_exact({preserve:{preserve_digits:true}})` to distinguish ٢٥٥ from 255."
            },
            "relax_when_empty": {
              "type": "boolean",
              "default": true,
              "description": "When the AND of every word of the title matches no catalogue entry, retry requiring only the largest number of them some entry does carry, and report it in `relaxed_min_match`. A title is usually quoted the way people cite it («فتح الباري لابن حجر») rather than the way the catalogue spells it («فتح الباري بشرح البخاري - ط السلفية»), and the strict AND then reports the library does not hold the book at all."
            },
            "preserve_diacritics": {
              "type": "boolean",
              "default": false,
              "description": "Returns OPTION_NOT_SUPPORTED here — the index itself stores no diacritics, so this search cannot honor them. Use `shamela_search_exact({preserve:{preserve_diacritics:true}})`, which re-checks each candidate against the book's raw text."
            }
          },
          "description": "morphology / wildcards / relax_when_empty / preserve_*.",
          "additionalProperties": false
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 139 lines
  • shamela_search_authors unknown never probed

    بحث في فهرس المؤلفين Search Shamela's ~3,200-author catalog by name or biography text. Pre-built index — no downloads needed. No scope (authors aren't scoped by category/period). Returns author name, Hijri death year, and book count. `options.sort:'chronological'` orders by death year, earliest first. Arabic scholars go by several name forms — when a query misses, try the kunya, nisba, and shuhra variants before concluding absence (جرّب الكنية والنسبة والشهرة: ابن قدامة / الموفق / المقدسي). Use the resulting author_id with shamela_get_author for full details, or with scope.author_ids in shamela_search_pages/_books to filter by that author. Examples: shamela_search_authors({query:'ابن قدامة'}), shamela_search_authors({query:'الشافعي', options:{wildcards:false}}).

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "query"
      ],
      "properties": {
        "limit": {
          "type": "integer",
          "default": 20,
          "maximum": 100,
          "minimum": 1,
          "description": "Maximum number of results to return (1-100, default 20)."
        },
        "query": {
          "type": "string",
          "minLength": 1,
          "description": "Arabic search phrase matched against author name + biography."
        },
        "offset": {
          "type": "integer",
          "default": 0,
          "minimum": 0,
          "description": "Number of results to skip for pagination. Use with `limit` to page through large result sets. The response includes `has_more` and `next_offset` to drive paging."
        },
        "options": {
          "type": "object",
          "properties": {
            "sort": {
              "enum": [
                "relevance",
                "chronological"
              ],
              "type": "string",
              "default": "relevance",
              "description": "Result order. 'relevance' (default) ranks by how well the page matches — the right answer to 'find me X'. 'chronological' orders by the author's death year, then book, then page, so the EARLIEST sources come first — the right answer to 'who says this first' or 'how does this idea travel through the tradition'. Undated books sort last."
            },
            "search_in": {
              "type": "array",
              "items": {
                "enum": [
                  "body",
                  "foot"
                ],
                "type": "string"
              },
              "default": [
                "body",
                "foot"
              ],
              "description": "Which page sections to search. 'body' = matn (المتن), 'foot' = footnotes (الحواشي). Default is both. Reader comments (التعليقات) are NOT searchable — Shamela keeps them in database/user/data.db, outside the Lucene index, so passing 'comment' returns OPTION_NOT_SUPPORTED rather than silently matching nothing. To search chapter titles use the separate `shamela_search_titles` tool."
            },
            "wildcards": {
              "type": "boolean",
              "default": false,
              "description": "Interpret `*` (zero-or-more characters) and `?` (exactly-one character) within tokens. Examples: `صيا*` matches صيام/صيامهم, `ص?م` matches صام/صوم/صمم. Leading wildcards (`*ابن`) are slow. Cannot combine with `morphology`. With this OFF, `*` and `?` are treated as ordinary punctuation and split the word, so a stray one no longer zeroes the search. CAVEAT on prefixes: the index folds some words as a WHOLE — «مائة» is stored as «ميه», «داوود» as «داود» — and a wildcard stem is folded without the letters the wildcard replaced, so `مائ*` becomes `ماي*` and reaches مائع/مائدة but NOT مائة. When a prefix search returns less than you expect for a word like these, search the full word without a wildcard instead."
            },
            "morphology": {
              "type": "boolean",
              "default": false,
              "description": "Search Arabic root forms via the AlKhalil morphological analyzer. Useful when looking for all word forms (يكتب، كاتب، مكتوب…) of a root (كتب). Cannot be combined with `wildcards`."
            },
            "preserve_hamza": {
              "type": "boolean",
              "default": false,
              "description": "Returns OPTION_NOT_SUPPORTED here — the index folds ٱآأإ→ا before storing. Use `shamela_search_exact({preserve:{preserve_hamza:true}})` to distinguish «أحمد» from «احمد»."
            },
            "preserve_digits": {
              "type": "boolean",
              "default": false,
              "description": "Returns OPTION_NOT_SUPPORTED here — the index unifies Arabic-Indic and Western digits. Use `shamela_search_exact({preserve:{preserve_digits:true}})` to distinguish ٢٥٥ from 255."
            },
            "preserve_diacritics": {
              "type": "boolean",
              "default": false,
              "description": "Returns OPTION_NOT_SUPPORTED here — the index itself stores no diacritics, so this search cannot honor them. Use `shamela_search_exact({preserve:{preserve_diacritics:true}})`, which re-checks each candidate against the book's raw text."
            }
          },
          "description": "morphology / wildcards. No scope (authors aren't scoped by category/period).",
          "additionalProperties": false
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 94 lines
  • shamela_get_toc unknown never probed

    جلب فهرس الكتاب Fetch a downloaded book's table of contents. Two modes: (a) subtree mode (default) — pass parent_id (0 = top level) and depth (1–5) to get a tree of titles; (b) ancestor-chain mode — pass containing_page_id to get the root → leaf chapter chain that contains that page. Returns title_id, title_text, page_id, has_children for each entry. Use the title_id with shamela_get_book_section to read the section. Examples: shamela_get_toc({book_id:<id>, depth:1}) lists top-level chapters; shamela_get_toc({book_id:<id>, containing_page_id:17}) returns the chapter containing page 17. Find downloaded book ids via shamela_list_downloaded_books or shamela_resolve.

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "book_id"
      ],
      "properties": {
        "depth": {
          "type": "integer",
          "default": 1,
          "maximum": 5,
          "minimum": 1,
          "description": "How many levels deep to recurse (1–5, default 1)."
        },
        "book_id": {
          "type": "integer",
          "description": "The book id.",
          "exclusiveMinimum": 0
        },
        "parent_id": {
          "type": "integer",
          "default": 0,
          "minimum": 0,
          "description": "Title id whose children to expand. 0 (default) returns top-level chapters."
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        },
        "containing_page_id": {
          "type": "integer",
          "description": "Alternate mode: instead of returning a subtree, return the ancestor chain (root → leaf chapter) for this page.",
          "exclusiveMinimum": 0
        }
      },
      "additionalProperties": false
    }
    arguments 43 lines
  • shamela_get_book unknown never probed

    جلب بيانات كتاب Fetch full metadata for a book by book_id. Returns book_name, all authors (main + co), category, book_type (printed/manuscript/journal/thesis/electronic/audio), book_date (Shamela's «تاريخ الكتاب» — on 98.5% of dated books this is the MAIN AUTHOR's death year, not a composition year; do not cite it as one), printed flag, downloaded flag (true ONLY when both master.db says so AND the per-book SQLite has page rows), publication_date (DDMMYYYY Hijri from meta_data), sub_books, `same_group` (other catalog entries of the SAME work — Ṣaḥīḥ al-Bukhārī is 8 entries: five editions plus three abridgments; treat quotes from two of them as one source, not two), `scan` (the scanned PDF printing Shamela holds, with resolved download URLs), `cover` (a ~310x460 JPEG of that printing's title page, attached as an image block by default — pass include_cover:false to suppress; only 1,368 of 8,594 books have one. Its absence does NOT mean there is no scanned printing — 377 books carry a `scan` with no cover image — and it says nothing about the book's real binding; read `scan` for whether a scan exists), and a `notes` array listing citation-grade fields master.db doesn't have (edition/publisher/city/editor — never fabricate these). Find ids via shamela_resolve('book name') or shamela_list_downloaded_books. Works on any catalog book whether downloaded or not.

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "book_id"
      ],
      "properties": {
        "book_id": {
          "type": "integer",
          "description": "The book id (e.g. 9942).",
          "exclusiveMinimum": 0
        },
        "include_cover": {
          "type": "boolean",
          "default": true,
          "description": "Attach the book's cover image as an MCP image block when Shamela ships one (1,368 of 8,594 books). The picture is a ~310x460 thumbnail of the scanned printing's title page, so it costs roughly 190 image tokens. Set false to keep the reply text-only; `cover.available` still reports whether one exists."
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 30 lines
  • shamela_get_author unknown never probed

    جلب بيانات مؤلف Fetch metadata for an author by author_id, optionally with the list of books they authored. Returns author_name, death_year (null if unknown or modern), death_text (display string), and the book list (main + co-authored). Each book entry has book_id, book_name, book_date, downloaded flag. Use include_books=false to skip the book list when you only need name/death year. Example: shamela_get_author({author_id:57}) returns Ibn Uthaymeen + his books.

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "author_id"
      ],
      "properties": {
        "author_id": {
          "type": "integer",
          "description": "The author id (e.g. 57 for Ibn Uthaymeen).",
          "exclusiveMinimum": 0
        },
        "include_books": {
          "type": "boolean",
          "default": true,
          "description": "If true (default), include the list of books authored by this author. Includes co-authored books."
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 30 lines
  • shamela_resolve unknown never probed

    تحويل اسم إلى معرِّف Disambiguate Arabic name fragments to book_ids and/or author_ids. Uses the pre-built s_book/ + s_author/ n-gram indexes for fast partial matching. type='book' searches only books, 'author' only authors, 'any' (default) both. Returns up to `limit` results per type with confidence scores. Use this BEFORE search_pages / search_books / search_authors when the user mentions a name but doesn't know the exact ID. Examples: shamela_resolve({query:'ابن قدامة'}) → returns the matching author_id(s) with confidence; shamela_resolve({query:'روضة الناظر'}) → returns book matches.

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "query"
      ],
      "properties": {
        "type": {
          "enum": [
            "any",
            "book",
            "author"
          ],
          "type": "string",
          "default": "any",
          "description": "'book' searches the s_book/ index, 'author' searches s_author/, 'any' (default) searches both."
        },
        "limit": {
          "type": "integer",
          "default": 5,
          "maximum": 20,
          "minimum": 1,
          "description": "Maximum results per type (1–20, default 5)."
        },
        "query": {
          "type": "string",
          "minLength": 1,
          "description": "Arabic name fragment to resolve. Use partial names: 'ابن قدامة' resolves Ibn Qudamah, 'الروضة' resolves books with that word in their title."
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 42 lines
  • shamela_get_pages_range unknown never probed

    جلب نطاق صفحات Fetch N (1–20, default 5) consecutive pages from a downloaded book starting at start_page_id. Faster than calling shamela_get_page in a loop. Each page entry has page_id, printed_page, part, body, foot, comment. has_more flag indicates whether more pages exist after the returned range. Very long ranges are cut short to stay within a size budget; when that happens the response sets next_start_page_id and a `_display` hint — continue from there. For full chapters use shamela_get_book_section instead — it knows where the chapter ends. Example: shamela_get_pages_range({book_id:<id>, start_page_id:1, count:5}). Find downloaded book ids via shamela_list_downloaded_books.

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "book_id",
        "start_page_id"
      ],
      "properties": {
        "count": {
          "type": "integer",
          "default": 5,
          "maximum": 20,
          "minimum": 1,
          "description": "How many consecutive pages to fetch (1–20, default 5). Use shamela_get_book_section for full chapter reads."
        },
        "book_id": {
          "type": "integer",
          "description": "The book id.",
          "exclusiveMinimum": 0
        },
        "keep_html": {
          "type": "boolean",
          "default": false,
          "description": "Preserve inline HTML markers."
        },
        "start_page_id": {
          "type": "integer",
          "description": "First page_id (inclusive).",
          "exclusiveMinimum": 0
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 43 lines
  • shamela_get_book_section unknown never probed

    جلب باب من كتاب Fetch every page under a chapter title. Resolves the chapter's start/end page range from the per-book SQLite (next-sibling-title boundary), then batch-reads the page contents. Capped at max_pages (default 30, max 100); sets `truncated:true` if the section is longer. Long sections also stop early on a character budget (even within max_pages) and return next_start_page_id + a `_display` hint to continue. Use shamela_get_toc to find title_ids, then this tool to read the matching section. Example: shamela_get_book_section({book_id:<id>, title_id:<title_id from get_toc>}).

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "book_id",
        "title_id"
      ],
      "properties": {
        "book_id": {
          "type": "integer",
          "description": "The book id.",
          "exclusiveMinimum": 0
        },
        "title_id": {
          "type": "integer",
          "description": "The title id of the chapter / section to fetch (use shamela_get_toc to find IDs).",
          "exclusiveMinimum": 0
        },
        "keep_html": {
          "type": "boolean",
          "default": false,
          "description": "Preserve inline HTML markers."
        },
        "max_pages": {
          "type": "integer",
          "default": 30,
          "maximum": 100,
          "minimum": 1,
          "description": "Cap on pages to read (1–100, default 30). Sections longer than this are truncated with a flag."
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 43 lines
  • shamela_get_citation unknown never probed

    صياغة إحالة Format a citation in five styles. style='shamela' (default) replicates Shamela's UI copy-with-citation: «<book>» (<part>/ <page>):\n«<text>». style='short' is a one-line inline reference: <author>، <book>، ص <page>. style='full' is the long form with author death year and book composition year, plus a `notes[]` array listing missing citation-grade fields (edition/publisher/city/editor — master.db doesn't have these; never fabricate). style='bibtex' and style='ris' export a machine-readable record for Zotero/EndNote/Mendeley (Western digits, Arabic UTF-8, cite key shamela<book_id>p<page_id>, same missing-field notes[], no fabricated edition/publisher/city/editor). All other styles use Arabic-Indic digits. Examples: shamela_get_citation({book_id:<id>, page_id:<page_id>, style:'shamela'}), shamela_get_citation({book_id:<id>, page_id:<page_id>, text:'<quoted passage>', style:'shamela'}).

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "book_id"
      ],
      "properties": {
        "text": {
          "type": "string",
          "description": "Optional quoted text. When provided in 'shamela' style, the output includes a two-line block: «<book>» (<part>/ <page>):\n«<text>»."
        },
        "style": {
          "enum": [
            "shamela",
            "short",
            "full",
            "bibtex",
            "ris"
          ],
          "type": "string",
          "default": "shamela",
          "description": "Citation style. 'shamela' replicates Shamela's UI copy-with-citation format (default). 'short' is a one-line inline reference. 'full' is the long form including author death year and book year, with notes listing missing fields (publisher/edition/etc are not in master.db). 'bibtex' and 'ris' are machine-readable exports for Zotero/EndNote/Mendeley (Western digits, Arabic UTF-8, stable cite key shamela<book_id>p<page_id>); they carry only fields master.db has, with the same missing-field notes[]."
        },
        "book_id": {
          "type": "integer",
          "description": "The book id.",
          "exclusiveMinimum": 0
        },
        "page_id": {
          "type": "integer",
          "description": "Optional page_id. When omitted, the citation references the book without a specific page (e.g. for a book-level citation).",
          "exclusiveMinimum": 0
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 46 lines
  • shamela_search_quran unknown never probed

    بحث في القرآن Search the Qur'an (6,236 verses, Hafs from Asim, Egyptian إملائي orthography) via the pre-built aya/ Lucene index. Ships zero-config — works on a fresh Shamela install. Returns aya_id (1..6236), surah, surah_name, aya, body (full verse text), and a snippet with <mark>...</mark> around matches. A single-word query is also searched as its ال-stripped stem and that stem's prefixed forms, which widens it: «الملك» returns 60 verses where the word as typed accounts for 28. The widening is reported — `stem_expanded_to` names the forms searched, `query_form_hits` counts the verses matching your word or its prefixed forms, and each hit's `matched_form` says which form it came in on, so a verse that only rode the stem (مَٰلِكِ in 1:4) is identifiable rather than presented as your word. Pair with shamela_get_aya to fetch a single verse with the Othmani Amiri rendering, or with shamela_get_tafseer_of_aya to find tafsir books that comment on a matching verse.

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "query"
      ],
      "properties": {
        "limit": {
          "type": "integer",
          "default": 20,
          "maximum": 100,
          "minimum": 1,
          "description": "Maximum number of results to return (1-100, default 20)."
        },
        "query": {
          "type": "string",
          "minLength": 1,
          "description": "Arabic text of a verse or part of one. Matched against all 6,236 verses in BOTH orthographies — the Egyptian إملائي spelling a user types from memory AND the Uthmani rasm that mushaf apps, quran.com and PDFs copy (ٱ, dagger alef, ﴿﴾, full tashkeel are all handled), so paste the verse exactly as you received it. Every word is ANDed. Single-word queries also match prefixed forms (e.g. «الصبر» matches «بالصبر») AND the same word with «ال» stripped («الملك» is also searched as «ملك»/«للملك»), which widens the answer past the word you typed — the response names those forms in `stem_expanded_to`, tags each hit with the `matched_form` it came in on, and reports in `query_form_hits` how many verses match your word itself. Results come back in mushaf order (aya_id), not by relevance."
        },
        "offset": {
          "type": "integer",
          "default": 0,
          "minimum": 0,
          "description": "Number of results to skip for pagination. Use with `limit` to page through large result sets. The response includes `has_more` and `next_offset` to drive paging."
        },
        "options": {
          "type": "object",
          "properties": {
            "wildcards": {
              "type": "boolean",
              "default": false,
              "description": "Interpret `*` (zero-or-more characters) and `?` (exactly-one character) within tokens. Examples: `صيا*` matches صيام/صيامهم, `ص?م` matches صام/صوم/صمم. Leading wildcards (`*ابن`) are slow. Cannot combine with `morphology`. With this OFF, `*` and `?` are treated as ordinary punctuation and split the word, so a stray one no longer zeroes the search. CAVEAT on prefixes: the index folds some words as a WHOLE — «مائة» is stored as «ميه», «داوود» as «داود» — and a wildcard stem is folded without the letters the wildcard replaced, so `مائ*` becomes `ماي*` and reaches مائع/مائدة but NOT مائة. When a prefix search returns less than you expect for a word like these, search the full word without a wildcard instead."
            }
          },
          "description": "Currently only the `wildcards` flag is honored. The Quranic index ships pre-built and zero-config.",
          "additionalProperties": false
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 50 lines
  • shamela_get_books_for_hadith unknown never probed

    كتب تتضمَّن حديثًا Given a Shamela hadith key (numeric identifier shared by all collections that record the same hadith), list every book that cites it. Uses Shamela's pre-built service/hadeeth.db join. By default filters to downloaded books only. Each result has book_id, book_name, author_name, page_id, downloaded flag. Pair with shamela_get_page to read the cited page. Useful for cross-collection hadith research (Bukhari + Muslim + Sunan + Musnad references for the same hadith).

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "hadith_key"
      ],
      "properties": {
        "hadith_key": {
          "type": "integer",
          "description": "Numeric hadith key (the normalized identifier Shamela uses across collections — e.g. 1234). Different collections of the same hadith share this key.",
          "exclusiveMinimum": 0
        },
        "downloaded_only": {
          "type": "boolean",
          "default": true,
          "description": "If true (default), only return books actually downloaded on this machine."
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 30 lines
  • shamela_get_book_parts unknown never probed

    أجزاء الكتاب List the volumes/parts of a multi-volume book. Returns is_multi_volume flag, total_pages, and an array of parts each with part name (e.g. 'ج 1'), page_count, first_page_id, last_page_id. For single-volume books returns is_multi_volume:false, and an empty parts array unless the pages carry a single part label — that one label is kept, because get_citation prints it (e.g. «(١٨/ ٥١١)»). Useful to know whether a citation needs a part designator. Example: shamela_get_book_parts({book_id:<id>}). Find downloaded book ids via shamela_list_downloaded_books.

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "book_id"
      ],
      "properties": {
        "book_id": {
          "type": "integer",
          "description": "The book id.",
          "exclusiveMinimum": 0
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 25 lines
  • shamela_get_page_services unknown never probed

    إشارات الصفحة Read the per-page service markers (Qur'anic verses cited, hadith keys, isnād markers) for a specific (book_id, page_id). Returns has_services plus ayat (cumulative aya_ids), hadeeth (hadith keys), esnad and esnad_count. NOTE: `esnad` entries are Shamela's RAW markers — «02464 s 2651 3234 1580 …» is a hadith key, a marker, then internal narrator IDS, not names, and they mean nothing on their own; call `shamela_get_isnad(book_id, page_id)` to get the same chains resolved to narrator names, death years and variant routes. Many books — particularly non-hadith works — have no services and return has_services:false cleanly. Useful to pivot from a search hit to what it cites: pair aya_ids with shamela_get_aya, or hadith keys with shamela_get_books_for_hadith.

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "book_id",
        "page_id"
      ],
      "properties": {
        "book_id": {
          "type": "integer",
          "description": "The book id.",
          "exclusiveMinimum": 0
        },
        "page_id": {
          "type": "integer",
          "description": "The page id.",
          "exclusiveMinimum": 0
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 31 lines
  • shamela_search_hadith unknown never probed

    بحث عن حديث بنصه Find a hadith by its TEXT (not its numeric key). Text-searches the hadith shelves (كتب السنة، شروح الحديث، التخريج والأطراف، علوم الحديث — 1,957 books here, reported as `books_searched`) across matn + footnotes, reads each matching page's service annotations for hadith keys, then resolves each key's cross-collection takhrij via hadeeth.db. Keep the two numbers apart: the TEXT search covers those shelves, while takhrij keys exist only in the books hadeeth.db indexes (`key_index_books` — ten on this install), so a hadith can be found on 1,746 pages and still yield takhrij from ten books. `takhrij[].books` counts distinct BOOKS and `index_entries` the pages behind them. Returns matched pages (snippets often show the printed takhrij «رواه البخاري ومسلم») plus cross-book takhrij where service keys exist. Scoping to those shelves is deliberate: unscoped, term-frequency ranking fills the scan window with fiqh/usul works that merely QUOTE the hadith and carry no service keys, so no takhrij is found. Pass search_all_categories:true to find who CITES a hadith across the whole library instead of who NARRATES it. Example: shamela_search_hadith({query:'إنما الأعمال بالنيات'}).

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "query"
      ],
      "properties": {
        "limit": {
          "type": "integer",
          "default": 20,
          "maximum": 100,
          "minimum": 1,
          "description": "Maximum number of results to return (1-100, default 20)."
        },
        "query": {
          "type": "string",
          "minLength": 1,
          "description": "The hadith text (or a distinctive part of it). AND-combines words across matn + footnotes."
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        },
        "max_pages_scanned": {
          "type": "integer",
          "default": 20,
          "maximum": 50,
          "minimum": 1,
          "description": "How many matching pages to scan for hadith service keys."
        },
        "search_all_categories": {
          "type": "boolean",
          "default": false,
          "description": "Search the ENTIRE library instead of the hadith shelves (كتب السنة، شروح الحديث، التخريج، علوم الحديث). Off by default because unscoped ranking buries the collections under works that merely quote the hadith. Turn on to find who CITES a hadith (e.g. in fiqh or tafsir) rather than who NARRATES it."
        }
      },
      "additionalProperties": false
    }
    arguments 44 lines
  • shamela_search_exact unknown never probed

    بحث مطابق مع التشكيل والهمزات والأرقام Exactness-preserving search the regular search cannot do: it honors diacritics (التشكيل), hamza/alef forms (ٱآأإ vs bare ا, plus ؤ ئ ء ى ة), and digit systems (Arabic-Indic ٠-٩ vs Western 0-9). shamela_search_pages folds all of these away (preserve_* return OPTION_NOT_SUPPORTED). Two-stage, no index change: (1) normalized AND-search gathers candidates; (2) each candidate's FULL raw SQLite text is verified in Node, folding ONLY the features you did NOT ask to preserve. Type the query WITH the diacritics/hamza/digits to enforce; enable at least one flag in `preserve`. Broad searches may miss matches outside the bounded candidate window (`candidate_cap_hit`/`total_candidates_scanned` report it) — pass `scope` for large libraries. Examples: shamela_search_exact({query:'أحمد', preserve:{preserve_hamza:true}}) won't match «احمد»; shamela_search_exact({query:'عِلْم', preserve:{preserve_diacritics:true}}) won't match «عَلَم».

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "query",
        "preserve"
      ],
      "properties": {
        "limit": {
          "type": "integer",
          "default": 20,
          "maximum": 100,
          "minimum": 1,
          "description": "Maximum number of results to return (1-100, default 20)."
        },
        "query": {
          "type": "string",
          "minLength": 1,
          "description": "The exact Arabic string to find. Type it WITH the diacritics / hamza form / digit system you want preserved (the query is NOT auto-diacritized). Multiple words are matched as written; the `preserve` flags decide which distinctions are enforced."
        },
        "scope": {
          "type": "object",
          "properties": {
            "madhab": {
              "type": "array",
              "items": {
                "enum": [
                  "hanafi",
                  "maliki",
                  "shafii",
                  "hanbali"
                ],
                "type": "string"
              },
              "description": "Restrict to books of these Sunni schools. master.db has NO madhab field, so this is resolved two ways: a book filed under a school-specific category (الفقه الحنفي/المالكي/الشافعي/الحنبلي) counts directly, and otherwise the MAIN AUTHOR's school is used, inferred from the categories that author's own books are filed under. That second path is what reaches مسائل فقهية and الفقه العام, where Shamela records no school — it raises madhab coverage of the fiqh shelves from 27% to 43%. Books whose school cannot be established are EXCLUDED by this filter, so do not read absence as 'no school said this'; for comparative questions leave madhab unset and scope by category instead."
            },
            "book_ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "description": "Restrict to these book IDs. Use `shamela_resolve` or `shamela_list_downloaded_books` to find IDs. Combine with other scope keys for intersection."
            },
            "book_type": {
              "type": "array",
              "items": {
                "enum": [
                  "book",
                  "journal",
                  "manuscript",
                  "thesis",
                  "electronic",
                  "transcript"
                ],
                "type": "string"
              },
              "description": "Restrict to these kinds of entry (master.db.book.book_type — the same filter row Shamela's own 'اختيار كتاب' window shows). 'transcript' (تفريغات) is a transcribed lecture series, not an authored book, and 'thesis'/'electronic' are likewise not printed works — excluding them sharpens a scholarly search. Of 8,594 entries, 7,334 are 'book'; 1,260 are not."
            },
            "period_to": {
              "type": "integer",
              "maximum": 2000,
              "minimum": 1,
              "description": "Hijri year, inclusive upper bound. Pair with period_from."
            },
            "author_ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "description": "Restrict to books by these authors. Includes co-authored books. Use `shamela_resolve(type='author')` to find IDs."
            },
            "period_from": {
              "type": "integer",
              "maximum": 2000,
              "minimum": 1,
              "description": "Hijri year, inclusive lower bound. Matches book.book_date (composition year) OR author.death_number (death year). Pair with period_to."
            },
            "category_ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "description": "Restrict to books in these categories. Categories are flat in master.db (no parent_id, no transitive subtree expansion). Use `shamela_list_categories` to find IDs."
            },
            "citable_only": {
              "type": "boolean",
              "default": false,
              "description": "Keep only books whose page numbering matches a real printed edition (master.db.book.printed == 1 — Shamela's «يوافق المطبوع»). The other 1,417 entries are paginated by Shamela automatically, so their page numbers are NOT a citable reference to any published edition. Set this true whenever the answer will quote a page number as a citation."
            },
            "downloaded_only": {
              "type": "boolean",
              "default": false,
              "description": "Restrict to books actually downloaded on this user's machine (master.db.book.major_ondisk > 0). Critical for honest research scoping when using `shamela_search_pages` — only downloaded books have searchable content."
            }
          },
          "description": "Restrict to books/authors/categories/period. Strongly recommended for large libraries so the candidate window covers the topic.",
          "additionalProperties": false
        },
        "offset": {
          "type": "integer",
          "default": 0,
          "minimum": 0,
          "description": "Number of results to skip for pagination. Use with `limit` to page through large result sets. The response includes `has_more` and `next_offset` to drive paging."
        },
        "preserve": {
          "type": "object",
          "properties": {
            "preserve_hamza": {
              "type": "boolean",
              "default": false,
              "description": "Enforce hamza/alef forms exactly: «أحمد» will not match «احمد»; ٱآأإ, ؤ, ئ, ء, ى, ة are all kept distinct."
            },
            "preserve_digits": {
              "type": "boolean",
              "default": false,
              "description": "Distinguish digit systems: Arabic-Indic «٢٥٥» will not match Western «255» (and vice-versa)."
            },
            "preserve_diacritics": {
              "type": "boolean",
              "default": false,
              "description": "Enforce التشكيل exactly: «عِلْم» will not match «عَلَم». The query's own diacritics are honored verbatim."
            }
          },
          "description": "Which distinctions to enforce during stage-2 verification. At least one must be true.",
          "additionalProperties": false
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 142 lines
  • shamela_search_boolean unknown never probed

    بحث منطقي (و/أو/دون) Boolean search the AND-only regular search lacks — combines OR (any_of) and NOT (none_of) with AND (all_of). `all_of`: terms that must ALL appear (intersection). `any_of`: at least ONE must appear (union), intersected with all_of. `none_of`: pages containing ANY of these are excluded. At least one of all_of/any_of is required. The whole expression is ONE Lucene BooleanQuery evaluated over the entire index — all_of as MUST, any_of as SHOULD-min-1, none_of as MUST_NOT — so `total_hits` is an exact count and the exclusion is exhaustive, not a sample. `scope` is still worth passing for speed on a large library, not for correctness. If a term has no entry in the index at all it is named in `zero_hit_terms`: a zero then means that word is not in the library as spelled, which is different from the expression having no match. Examples: shamela_search_boolean({all_of:['الوقف'], any_of:['المسجد','المقبرة'], none_of:['البيع'], scope:{category_ids:[17]}}).

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "properties": {
        "limit": {
          "type": "integer",
          "default": 20,
          "maximum": 100,
          "minimum": 1,
          "description": "Maximum number of results to return (1-100, default 20)."
        },
        "scope": {
          "type": "object",
          "properties": {
            "madhab": {
              "type": "array",
              "items": {
                "enum": [
                  "hanafi",
                  "maliki",
                  "shafii",
                  "hanbali"
                ],
                "type": "string"
              },
              "description": "Restrict to books of these Sunni schools. master.db has NO madhab field, so this is resolved two ways: a book filed under a school-specific category (الفقه الحنفي/المالكي/الشافعي/الحنبلي) counts directly, and otherwise the MAIN AUTHOR's school is used, inferred from the categories that author's own books are filed under. That second path is what reaches مسائل فقهية and الفقه العام, where Shamela records no school — it raises madhab coverage of the fiqh shelves from 27% to 43%. Books whose school cannot be established are EXCLUDED by this filter, so do not read absence as 'no school said this'; for comparative questions leave madhab unset and scope by category instead."
            },
            "book_ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "description": "Restrict to these book IDs. Use `shamela_resolve` or `shamela_list_downloaded_books` to find IDs. Combine with other scope keys for intersection."
            },
            "book_type": {
              "type": "array",
              "items": {
                "enum": [
                  "book",
                  "journal",
                  "manuscript",
                  "thesis",
                  "electronic",
                  "transcript"
                ],
                "type": "string"
              },
              "description": "Restrict to these kinds of entry (master.db.book.book_type — the same filter row Shamela's own 'اختيار كتاب' window shows). 'transcript' (تفريغات) is a transcribed lecture series, not an authored book, and 'thesis'/'electronic' are likewise not printed works — excluding them sharpens a scholarly search. Of 8,594 entries, 7,334 are 'book'; 1,260 are not."
            },
            "period_to": {
              "type": "integer",
              "maximum": 2000,
              "minimum": 1,
              "description": "Hijri year, inclusive upper bound. Pair with period_from."
            },
            "author_ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "description": "Restrict to books by these authors. Includes co-authored books. Use `shamela_resolve(type='author')` to find IDs."
            },
            "period_from": {
              "type": "integer",
              "maximum": 2000,
              "minimum": 1,
              "description": "Hijri year, inclusive lower bound. Matches book.book_date (composition year) OR author.death_number (death year). Pair with period_to."
            },
            "category_ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "description": "Restrict to books in these categories. Categories are flat in master.db (no parent_id, no transitive subtree expansion). Use `shamela_list_categories` to find IDs."
            },
            "citable_only": {
              "type": "boolean",
              "default": false,
              "description": "Keep only books whose page numbering matches a real printed edition (master.db.book.printed == 1 — Shamela's «يوافق المطبوع»). The other 1,417 entries are paginated by Shamela automatically, so their page numbers are NOT a citable reference to any published edition. Set this true whenever the answer will quote a page number as a citation."
            },
            "downloaded_only": {
              "type": "boolean",
              "default": false,
              "description": "Restrict to books actually downloaded on this user's machine (master.db.book.major_ondisk > 0). Critical for honest research scoping when using `shamela_search_pages` — only downloaded books have searchable content."
            }
          },
          "description": "Restrict to books/authors/categories/period. Recommended: an unscoped boolean search over a large library is exhaustive but slow. Use shamela_list_categories / shamela_resolve to find IDs.",
          "additionalProperties": false
        },
        "all_of": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "default": [],
          "description": "AND — terms that must ALL appear on the page (each term is itself AND-searched, so a multi-word term must co-occur). Intersection of every term's hit set. Leave empty to rely on any_of."
        },
        "any_of": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "default": [],
          "description": "OR — at least ONE of these terms must appear on the page. Union of every term's hit set, then intersected with the all_of result. Leave empty to rely on all_of."
        },
        "offset": {
          "type": "integer",
          "default": 0,
          "minimum": 0,
          "description": "Number of results to skip for pagination. Use with `limit` to page through large result sets. The response includes `has_more` and `next_offset` to drive paging."
        },
        "none_of": {
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          },
          "default": [],
          "description": "NOT — pages containing ANY of these terms are excluded, across the whole index (a MUST_NOT clause, not a subtraction inside a sampled window). Optional."
        },
        "search_in": {
          "type": "array",
          "items": {
            "enum": [
              "body",
              "foot"
            ],
            "type": "string"
          },
          "default": [
            "body",
            "foot"
          ],
          "description": "Which page sections each sub-search looks in: body (matn), foot (footnotes), comment. Default ['body','foot']."
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 153 lines
  • shamela_books_by_period unknown never probed

    كتب حسب المدة (تأليفًا ووفاةً) Catalog filter that AND-combines its constraints, unlike scope.period_*, which unions them and so widens a search instead of narrowing it. composed_from/composed_to bound book.book_date (Shamela's «تاريخ الكتاب»); died_from/died_to bound the MAIN AUTHOR's death year. Read those as two COLUMNS, not two independent dimensions: measured on this catalogue they carry the same number on 98.5% of dated books, because Shamela records no composition year. A composed-vs-died question — «books written in the 8th century by authors who died in the 9th» — is therefore one this data cannot answer, and a near-empty result means the distinction is unavailable, not that no such book exists. A book matches only if it satisfies ALL provided constraints at once (composition-year AND death-year AND category AND downloaded) — an intersection, never a union. At least one of the four bounds is required. Also accepts category_id, downloaded_only, limit/offset. Returns book_id, book_name, main author + death_year, book_date, category, downloaded flag, and a ready-to-use book_ids[] to pass as scope.book_ids. Use when a question distinguishes 'books composed in a period' from 'books by authors who died in a period' — e.g. died_from:700, died_to:800 for 8th-century-Hijri authors.

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "properties": {
        "limit": {
          "type": "integer",
          "default": 20,
          "maximum": 100,
          "minimum": 1,
          "description": "Maximum number of results to return (1-100, default 20)."
        },
        "offset": {
          "type": "integer",
          "default": 0,
          "minimum": 0,
          "description": "Number of results to skip for pagination. Use with `limit` to page through large result sets. The response includes `has_more` and `next_offset` to drive paging."
        },
        "died_to": {
          "type": "integer",
          "maximum": 2000,
          "minimum": 1,
          "description": "Hijri year, inclusive UPPER bound on the MAIN AUTHOR's death year (author.death_number). Pair with died_from."
        },
        "died_from": {
          "type": "integer",
          "maximum": 2000,
          "minimum": 1,
          "description": "Hijri year, inclusive LOWER bound on the MAIN AUTHOR's death year (author.death_number). A different column from composed_*, but the same number on 98.5% of dated books. Pair with died_to."
        },
        "category_id": {
          "type": "integer",
          "description": "Optional: restrict to one category (use shamela_list_categories for IDs). Categories are flat in master.db.",
          "exclusiveMinimum": 0
        },
        "composed_to": {
          "type": "integer",
          "maximum": 2000,
          "minimum": 1,
          "description": "Hijri year, inclusive UPPER bound on book.book_date («تاريخ الكتاب»; see composed_from — it is not a composition year). Pair with composed_from."
        },
        "composed_from": {
          "type": "integer",
          "maximum": 2000,
          "minimum": 1,
          "description": "Hijri year, inclusive LOWER bound on book.book_date — Shamela's «تاريخ الكتاب». NOT an independent composition year: it holds the main author's death year on 98.5% of dated books (8,461/8,587), so pairing it with died_* almost never narrows anything. Pair with composed_to."
        },
        "downloaded_only": {
          "type": "boolean",
          "default": false,
          "description": "If true, restrict to books actually downloaded on this machine (master.db.book.major_ondisk > 0) — the only ones with searchable page content."
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 64 lines
  • shamela_list_tafsirs_for_aya unknown never probed

    تغطية تفاسير آية Per-aya tafsir coverage report: cross-references the user's DOWNLOADED tafsir shelves (categories 3 AND 4 — tafsir spans both) against the curated service/tafseer.db index and reports an honest tri-state per book. status='indexed_covers' — the index maps this aya to a page in this book (page_id + printed page included); 'indexed_no_entry_for_this_aya' — the book participates in the index but has no entry for this aya; 'not_indexed_coverage_unknown' — the book is absent from the curated index, so coverage CANNOT be determined (explicitly NOT evidence the book lacks commentary on the verse). Index hits outside categories 3/4 (e.g. mawsuʿat) are included and marked in_tafsir_categories:false. Pass either aya_id (1..6236) OR surah+aya. Returns totals per status and a note about the curated-index limitation. Never text-searches; navigate unknown-status books via shamela_get_toc. Examples: shamela_list_tafsirs_for_aya({surah:2, aya:255}), shamela_list_tafsirs_for_aya({aya_id:262}).

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "properties": {
        "aya": {
          "type": "integer",
          "minimum": 1,
          "description": "Aya within surah."
        },
        "surah": {
          "type": "integer",
          "maximum": 114,
          "minimum": 1,
          "description": "Surah number, paired with `aya`."
        },
        "aya_id": {
          "type": "integer",
          "maximum": 6236,
          "minimum": 1,
          "description": "Aya id 1..6236."
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 34 lines
  • shamela_get_narrator unknown never probed

    ترجمة الراوي The full dossier on one narrator: name and lineage, death year, ṭabaqa (generation per Taqrīb al-Tahdhīb), Ibn Ḥajar's and al-Dhahabī's grades, kunya/nasab/cities/relations, and — the heart of it — al-jarḥ wa-l-taʿdīl: the critics' verdicts, grouped by critic, each quoted verbatim WITH the book and volume/page it came from (e.g. «تهذيب التهذيب (1/ 101)»). Critics are PAGED (`limit`, default 10, and `offset`), because a heavily-documented narrator has dozens — al-Zuhrī has 56 critics and 184 verdicts — and the response reports `total_critics`/`total_verdicts` plus a cursor so you can tell a page from the whole file and reach the rest. 15,160 of the 18,989 narrators carry verdicts, from 2,821 named critics. Also returns hadith_notes (فوائد) on audition, irsāl and implicit tawthīq — the evidence for whether a chain is connected. With include_network (default true) it adds the transmission network computed live from the isnād index: how many chains name this narrator, in which of the ten indexed books, and their most frequent teachers (narrates_from) and students (narrates_to) with counts. Quote the verdicts as the critics worded them; do NOT synthesize a grade the register does not state. Get the id from shamela_search_narrators.

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "narrator_id"
      ],
      "properties": {
        "limit": {
          "type": "integer",
          "default": 10,
          "maximum": 100,
          "minimum": 1,
          "description": "How many CRITICS of al-jarḥ wa-l-taʿdīl to return (1-100, default 10) — the only part of the dossier that is paged. al-Zuhrī has 56 critics and 184 verdicts; they do not fit one response, and `total_critics` says how many there are."
        },
        "top_n": {
          "type": "integer",
          "default": 15,
          "maximum": 100,
          "minimum": 1,
          "description": "How many teachers and how many students to return, most frequent first (1-100, default 15)."
        },
        "offset": {
          "type": "integer",
          "default": 0,
          "minimum": 0,
          "description": "Critics to skip, for paging through the verdicts. Follow `next_offset` until `has_more` is false."
        },
        "narrator_id": {
          "type": "integer",
          "minimum": 0,
          "description": "The narrator id, as returned by shamela_search_narrators or inside a chain from shamela_get_isnad."
        },
        "include_network": {
          "type": "boolean",
          "default": true,
          "description": "Include the transmission network computed from the isnād index: how many chains this narrator carries, in which books, and their most frequent teachers and students. Set false to skip it when you only need the biography."
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 50 lines
  • shamela_search_isnad unknown never probed

    بحث في الأسانيد Search the 45,220 isnād chains Shamela indexes across ten ḥadīth books (Bukhārī 1681, Muslim 711, Abū Dāwūd 654, Tirmidhī 7895, Nasāʾī 829, Muwaṭṭaʾ 28107, Ṭayālisī 1456, Ibn al-Jārūd 627/13071, ʿIlal al-Tirmidhī 642). Two constraints, both taking narrator ids from shamela_search_narrators: `contains` requires the narrators to appear ANYWHERE in the chain, and `transmission` takes [teacher_id, student_id] pairs that must be ADJACENT and in that order — direct audition with nobody in between. `transmission` is the strictly stronger claim and the one ʿilm al-rijāl asks: «did X actually hear from Y». Every result gives book, page and the chain resolved to names and death years, so a claim can be checked against the printed page with shamela_get_page. Chains run ASCENDING: chain[0] is the Companion, the last link is the collector. At least one constraint is required. Examples: shamela_search_isnad({transmission:[[1580,2779]]}) — chains where Shuʿba heard from al-Ḥakam b. ʿUtayba; shamela_search_isnad({contains:[2779], scope_book_ids:[1681]}) — Shuʿba's chains in al-Bukhārī.

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "properties": {
        "offset": {
          "type": "integer",
          "default": 0,
          "minimum": 0,
          "description": "Chains to skip, for paging."
        },
        "contains": {
          "type": "array",
          "items": {
            "type": "integer",
            "minimum": 0
          },
          "default": [],
          "description": "Narrator ids that must ALL appear in the chain, at any position and in any order. Use for «which reports pass through both X and Y»."
        },
        "max_results": {
          "type": "integer",
          "default": 20,
          "maximum": 100,
          "minimum": 1,
          "description": "Chains to return per page (1-100, default 20)."
        },
        "transmission": {
          "type": "array",
          "items": {
            "type": "array",
            "items": {
              "type": "integer",
              "minimum": 0
            },
            "maxItems": 2,
            "minItems": 2
          },
          "default": [],
          "description": "Pairs [teacher_id, student_id] that must appear ADJACENT and in that order — direct transmission, the student having heard from the teacher with nobody in between. This is the question «did X really narrate from Y», and it is strictly stronger than listing both in `contains`. All pairs are ANDed."
        },
        "scope_book_ids": {
          "type": "array",
          "items": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "default": [],
          "description": "Restrict to these books. The isnād index covers only ten: صحيح البخاري (1681), صحيح مسلم (711), سنن أبي داود (654), سنن الترمذي (7895), سنن النسائي (829), موطأ مالك (28107), مسند الطيالسي (1456), المنتقى لابن الجارود (627 و13071), وعلل الترمذي (642)."
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 62 lines
  • shamela_get_isnad unknown never probed

    إسناد الصفحة وطرقه The isnād chain(s) printed on one page, resolved from Shamela's narrator ids to names and death years, plus the other routes (طرق) of the same ḥadīth in the other indexed books. This is the pivot from a search hit to who transmitted it: run shamela_search_pages, then pass a (book_id, page_id) here. A page can carry several chains, and `variants` follows Shamela's ḥadīth key outward — counting the routes is how you judge whether a report rests on one thread (غريب) or many (مشهور). Chains run ASCENDING: first link is the Companion, last is the collector; each link heard from the one before. Only ten ḥadīth books are indexed for isnād, so most pages return no_isnad_indexed:true — and `no_isnad_reason` then says WHICH kind of nothing it is: 'no_marker_on_page' (the page carries no isnād marker at all, read from its own service annotations) or 'marked_but_unindexed' (it does carry one, but this book is outside the ten). A page_id the book does not have is now an error rather than a third silent nothing. Pair a narrator id from the chain with shamela_get_narrator to see whether the critics accepted them.

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "book_id",
        "page_id"
      ],
      "properties": {
        "book_id": {
          "type": "integer",
          "description": "The book id.",
          "exclusiveMinimum": 0
        },
        "page_id": {
          "type": "integer",
          "description": "The page id.",
          "exclusiveMinimum": 0
        },
        "max_variants": {
          "type": "integer",
          "default": 20,
          "maximum": 200,
          "minimum": 1,
          "description": "Cap on returned variant routes (1-200, default 20). `variants.truncated` reports when more exist."
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        },
        "include_variants": {
          "type": "boolean",
          "default": true,
          "description": "Also return the other routes (طرق) of the same ḥadīth from the other indexed books, matched on Shamela's ḥadīth key. This is what shows whether a report rests on one thread or many."
        }
      },
      "additionalProperties": false
    }
    arguments 43 lines
  • shamela_search_batch unknown never probed

    بحث مجمَّع بعدة استعلامات Run up to 12 scoped searches in ONE call and get back a single merged, deduplicated result set. Built for assembling evidence for one question from several wordings at once: each probe is {query, mode:'and'|'phrase'|'near', scope, limit}, they execute concurrently, and pages found by more than one probe are returned ONCE with every probe id that found them (`probe_ids`) — which is corroboration across wordings, not a duplicate. Every row carries `match_quality` ('exact_phrase'|'near'|'all_terms'|'relaxed') and `matched_in_section` ('body'|'footnote'|'both') so a near miss can never be read as a located quotation once results are merged, and a modern editor's footnote is never quoted as the author's own words. Every row also carries `work_group_id`, and `edition_groups` names any work present here under several catalog entries — read those as ONE source, not as several that corroborate each other. `scope` is REQUIRED on every probe (one of book_ids/author_ids/category_ids/madhab): unscoped, Lucene ranks by term frequency across all 8,594 books, so «التيمم» returns a poetry anthology ahead of the fiqh manuals. A probe that fails or times out is reported in its own slot and does NOT discard the others; `incomplete` is then true and a thin result must NOT be read as the library being silent. Each completed probe carries `qualifiers` — `terms_truncated`, `zero_hit_terms`, `respelled_terms`, `paging_ceiling`, `coverage.at_cap`, `candidate_cap_hit`, `scope_resolution`, `unattributable_books` — the same caveats the single-call search returns, per probe; the block is present only on `status:'ok'`, so a probe WITHOUT it has not been checked and must not be recorded as searched. `scope_resolution.dropped` lists book ids the catalogue does not hold, which were never searched — the guard against reading an invented id's silence as the library's. Example: shamela_search_batch({probes:[{query:'الوتر واجب', scope:{category_ids:[14,19], citable_only:true}}, {query:'حكم صلاة الوتر', mode:'near', distance:5, scope:{madhab:'hanafi'}}]}).

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "probes"
      ],
      "properties": {
        "shape": {
          "enum": [
            "full",
            "brief",
            "index"
          ],
          "type": "string",
          "default": "full",
          "description": "How much of each hit to return. 'full' (default) is every field on every row, including the snippets — ask for this when the answer will QUOTE the page. 'brief' keeps book_id/page_id, printed_page, match_quality and matched_in_section and hoists book metadata into a top-level `books` dictionary keyed by book_id, but carries NO snippet: it answers which pages these are, how well each matched and how to cite them, for a fraction of the characters, so far more rows survive the budget. 'index' returns book_id/page_id only, for choosing what to read before paying to read it. Requires the 'shape.full_brief_index' capability (see shamela_brief)."
        },
        "probes": {
          "type": "array",
          "items": {
            "type": "object",
            "required": [
              "query",
              "scope"
            ],
            "properties": {
              "id": {
                "type": "string",
                "maxLength": 32,
                "minLength": 1,
                "description": "Your label for this probe, echoed back on every row it found (`probe_ids`). Defaults to the probe's index, e.g. 'p0'."
              },
              "mode": {
                "enum": [
                  "and",
                  "phrase",
                  "near"
                ],
                "type": "string",
                "default": "and",
                "description": "'and' (default) = the regular all-terms page search. 'phrase' = the words as a CONSECUTIVE phrase — use it when the caller is holding a quotation. 'near' = within `distance` words, any order."
              },
              "limit": {
                "type": "integer",
                "default": 10,
                "maximum": 50,
                "minimum": 1,
                "description": "Rows this probe may contribute before dedupe."
              },
              "query": {
                "type": "string",
                "minLength": 1,
                "description": "Arabic search text. In mode='and' every word is required (each may match body or footnotes); in mode='phrase' the words must be consecutive; in mode='near' within `distance` words in any order."
              },
              "scope": {
                "type": "object",
                "properties": {
                  "madhab": {
                    "type": "array",
                    "items": {
                      "enum": [
                        "hanafi",
                        "maliki",
                        "shafii",
                        "hanbali"
                      ],
                      "type": "string"
                    },
                    "description": "Restrict to books of these Sunni schools. master.db has NO madhab field, so this is resolved two ways: a book filed under a school-specific category (الفقه الحنفي/المالكي/الشافعي/الحنبلي) counts directly, and otherwise the MAIN AUTHOR's school is used, inferred from the categories that author's own books are filed under. That second path is what reaches مسائل فقهية and الفقه العام, where Shamela records no school — it raises madhab coverage of the fiqh shelves from 27% to 43%. Books whose school cannot be established are EXCLUDED by this filter, so do not read absence as 'no school said this'; for comparative questions leave madhab unset and scope by category instead."
                  },
                  "book_ids": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "description": "Restrict to these book IDs. Use `shamela_resolve` or `shamela_list_downloaded_books` to find IDs. Combine with other scope keys for intersection."
                  },
                  "book_type": {
                    "type": "array",
                    "items": {
                      "enum": [
                        "book",
                        "journal",
                        "manuscript",
                        "thesis",
                        "electronic",
                        "transcript"
                      ],
                      "type": "string"
                    },
                    "description": "Restrict to these kinds of entry (master.db.book.book_type — the same filter row Shamela's own 'اختيار كتاب' window shows). 'transcript' (تفريغات) is a transcribed lecture series, not an authored book, and 'thesis'/'electronic' are likewise not printed works — excluding them sharpens a scholarly search. Of 8,594 entries, 7,334 are 'book'; 1,260 are not."
                  },
                  "period_to": {
                    "type": "integer",
                    "maximum": 2000,
                    "minimum": 1,
                    "description": "Hijri year, inclusive upper bound. Pair with period_from."
                  },
                  "author_ids": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "description": "Restrict to books by these authors. Includes co-authored books. Use `shamela_resolve(type='author')` to find IDs."
                  },
                  "period_from": {
                    "type": "integer",
                    "maximum": 2000,
                    "minimum": 1,
                    "description": "Hijri year, inclusive lower bound. Matches book.book_date (composition year) OR author.death_number (death year). Pair with period_to."
                  },
                  "category_ids": {
                    "type": "array",
                    "items": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "description": "Restrict to books in these categories. Categories are flat in master.db (no parent_id, no transitive subtree expansion). Use `shamela_list_categories` to find IDs."
                  },
                  "citable_only": {
                    "type": "boolean",
                    "default": false,
                    "description": "Keep only books whose page numbering matches a real printed edition (master.db.book.printed == 1 — Shamela's «يوافق المطبوع»). The other 1,417 entries are paginated by Shamela automatically, so their page numbers are NOT a citable reference to any published edition. Set this true whenever the answer will quote a page number as a citation."
                  },
                  "downloaded_only": {
                    "type": "boolean",
                    "default": false,
                    "description": "Restrict to books actually downloaded on this user's machine (master.db.book.major_ondisk > 0). Critical for honest research scoping when using `shamela_search_pages` — only downloaded books have searchable content."
                  }
                },
                "description": "REQUIRED. Must carry at least one of book_ids / author_ids / category_ids / madhab. An unscoped probe is rejected: term-frequency ranking over the whole library returns whichever book repeats the word most, which is not the same question as the one being asked.",
                "additionalProperties": false
              },
              "distance": {
                "type": "integer",
                "default": 5,
                "maximum": 50,
                "minimum": 1,
                "description": "For mode='near' only: max words between the terms."
              },
              "exclude_pages": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "book_id",
                    "page_id"
                  ],
                  "properties": {
                    "book_id": {
                      "type": "integer",
                      "exclusiveMinimum": 0
                    },
                    "page_id": {
                      "type": "integer",
                      "minimum": 0
                    }
                  },
                  "additionalProperties": false
                },
                "description": "Exact pages to keep out — the pages a previous round already returned. Excluding these rather than whole books is what lets a follow-up search find MORE evidence in a book that has already contributed some."
              },
              "exclude_book_ids": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "exclusiveMinimum": 0
                },
                "description": "Book ids to keep out of the results entirely."
              },
              "exclude_author_ids": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "exclusiveMinimum": 0
                },
                "description": "Exclude every book by these authors."
              },
              "exclude_category_ids": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "exclusiveMinimum": 0
                },
                "description": "Exclude every book on these shelves."
              },
              "exclude_work_group_ids": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "exclusiveMinimum": 0
                },
                "description": "Exclude every catalogue entry of these works — all printings at once. This is the one to use after a round returned «صحيح البخاري - ط السلطانية»: excluding that book_id alone leaves the other seven entries of group 1681 free to return the same text again as if it were a second source."
              }
            },
            "additionalProperties": false
          },
          "maxItems": 24,
          "minItems": 1,
          "description": "The searches to run. They execute concurrently and their results are merged, deduped by (book_id, page_id) and attributed back to the probes that found each page."
        },
        "deadline_ms": {
          "type": "integer",
          "default": 8000,
          "maximum": 60000,
          "minimum": 1,
          "description": "Wall-clock budget for the whole batch. Probes still running when it expires are reported with status='timeout' and contribute nothing. A timeout is NOT a negative finding — it means the question was not fully asked."
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        },
        "max_total_passages": {
          "type": "integer",
          "default": 60,
          "maximum": 200,
          "minimum": 1,
          "description": "Hard cap on merged rows returned. Rows are ordered by match quality then probe agreement before the cut, so the cap drops the weakest evidence rather than an arbitrary tail. `truncated` says whether it fired."
        }
      },
      "additionalProperties": false
    }
    arguments 230 lines
  • shamela_resolve_printed_page unknown never probed

    ردُّ رقم الصفحة المطبوعة إلى صفحات البرنامج Turn a printed citation — «vol. 4, p. 405» — into the internal page ids that carry it, in one indexed query. A printed page number and a page_id are different things with no arithmetic between them, so estimating an id and correcting it by reading is both slow and unreliable. Returns `page_ids[]`: ONE printed page routinely maps to SEVERAL ids (part='1', page=6 is ids 9, 10 and 11 in the fixture book) because Shamela splits a long printed page across records — read them all, since the first alone is a fragment quoted as the page. Failure is specific rather than null: 'book_not_downloaded', 'no_printed_pagination' (Shamela paginates this entry itself, so no printed page exists to find), 'part_not_found' (with `available_parts`), 'page_not_in_part' (with the range that does exist). `citable` false means the numbers are Shamela's own, not a published edition's. Omit `part` for a single-volume work or a citation with no volume; `parts_matched` then reports whether the number was ambiguous across volumes. Different printings paginate differently — resolve the title with shamela_resolve_work first and use the printing the citation came from. Examples: shamela_resolve_printed_page({book_id:1681, part:'1', page:6}), shamela_resolve_printed_page({book_id:6910, part:'4', page:405}).

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "book_id",
        "page"
      ],
      "properties": {
        "page": {
          "type": "integer",
          "minimum": 1,
          "description": "The PRINTED page number as the citation gives it (e.g. 405)."
        },
        "part": {
          "type": "string",
          "description": "The volume/part label as the book uses it, usually a bare number ('4'). Omit when the citation names no volume or the work is single-volume: the search then runs across every part and the response reports which parts matched, so an ambiguous page number is visible rather than silently resolved to the first volume."
        },
        "book_id": {
          "type": "integer",
          "description": "The book. Resolve a cited title with shamela_resolve_work first — and note that different printings of one work paginate differently, so the book_id must be the printing the citation came from.",
          "exclusiveMinimum": 0
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 35 lines
  • shamela_recon unknown never probed

    استكشاف: حجم الموضوع في المكتبة وأين يقع Round-zero reconnaissance: how much does this library actually hold on a topic, and on which shelves, BEFORE a retrieval plan is committed to. Same measurement as shamela_root_stats but `mode` defaults to 'literal' — counting pages that carry the WORD as written rather than every AlKhalil root reading of it. That difference is the point: «الاستصناع» reports 393,498 pages across 7,405 books morphologically and 1,510 literally, and the first answer says every shelf holds the topic, which is not a plan. Returns the EXACT page total plus the distribution by category, Hijri century, book and author, in 6-9 ms. Read the distribution as corpus size PLUS topic, never as authority: التفسير tops nearly every profile because tafsir is simply the largest body of text here, not because it is where the question is answered. Use the by_category ranking to choose `scope.category_ids` for the searches that follow. Examples: shamela_recon({root:'الاستصناع'}), shamela_recon({root:'التيمم', scope:{citable_only:true}}).

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "root"
      ],
      "properties": {
        "mode": {
          "enum": [
            "literal",
            "morphological"
          ],
          "type": "string",
          "default": "literal",
          "description": "'literal' counts pages carrying the WORD as written (plus the ordinary spelling folds). 'morphological' expands it to every AlKhalil root reading and counts those — which is far broader than most callers expect: «الاستصناع» reports 393,498 pages across 7,405 books morphologically against 1,510 literally, an answer that says every shelf holds the topic. Use 'literal' to size a topic before planning retrieval; use 'morphological' to trace a ROOT through its derivations."
        },
        "root": {
          "type": "string",
          "minLength": 1,
          "description": "Arabic root or base word to profile (e.g. «صبر»، «رحم»، «علم»). Under mode='morphological' it is matched via the AlKhalil analyzer, so all derived forms (صابر، يصبر، اصطبار…) are counted, not just the literal string."
        },
        "scope": {
          "type": "object",
          "properties": {
            "madhab": {
              "type": "array",
              "items": {
                "enum": [
                  "hanafi",
                  "maliki",
                  "shafii",
                  "hanbali"
                ],
                "type": "string"
              },
              "description": "Restrict to books of these Sunni schools. master.db has NO madhab field, so this is resolved two ways: a book filed under a school-specific category (الفقه الحنفي/المالكي/الشافعي/الحنبلي) counts directly, and otherwise the MAIN AUTHOR's school is used, inferred from the categories that author's own books are filed under. That second path is what reaches مسائل فقهية and الفقه العام, where Shamela records no school — it raises madhab coverage of the fiqh shelves from 27% to 43%. Books whose school cannot be established are EXCLUDED by this filter, so do not read absence as 'no school said this'; for comparative questions leave madhab unset and scope by category instead."
            },
            "book_ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "description": "Restrict to these book IDs. Use `shamela_resolve` or `shamela_list_downloaded_books` to find IDs. Combine with other scope keys for intersection."
            },
            "book_type": {
              "type": "array",
              "items": {
                "enum": [
                  "book",
                  "journal",
                  "manuscript",
                  "thesis",
                  "electronic",
                  "transcript"
                ],
                "type": "string"
              },
              "description": "Restrict to these kinds of entry (master.db.book.book_type — the same filter row Shamela's own 'اختيار كتاب' window shows). 'transcript' (تفريغات) is a transcribed lecture series, not an authored book, and 'thesis'/'electronic' are likewise not printed works — excluding them sharpens a scholarly search. Of 8,594 entries, 7,334 are 'book'; 1,260 are not."
            },
            "period_to": {
              "type": "integer",
              "maximum": 2000,
              "minimum": 1,
              "description": "Hijri year, inclusive upper bound. Pair with period_from."
            },
            "author_ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "description": "Restrict to books by these authors. Includes co-authored books. Use `shamela_resolve(type='author')` to find IDs."
            },
            "period_from": {
              "type": "integer",
              "maximum": 2000,
              "minimum": 1,
              "description": "Hijri year, inclusive lower bound. Matches book.book_date (composition year) OR author.death_number (death year). Pair with period_to."
            },
            "category_ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "description": "Restrict to books in these categories. Categories are flat in master.db (no parent_id, no transitive subtree expansion). Use `shamela_list_categories` to find IDs."
            },
            "citable_only": {
              "type": "boolean",
              "default": false,
              "description": "Keep only books whose page numbering matches a real printed edition (master.db.book.printed == 1 — Shamela's «يوافق المطبوع»). The other 1,417 entries are paginated by Shamela automatically, so their page numbers are NOT a citable reference to any published edition. Set this true whenever the answer will quote a page number as a citation."
            },
            "downloaded_only": {
              "type": "boolean",
              "default": false,
              "description": "Restrict to books actually downloaded on this user's machine (master.db.book.major_ondisk > 0). Critical for honest research scoping when using `shamela_search_pages` — only downloaded books have searchable content."
            }
          },
          "description": "Restrict the profile to specific books, authors, categories, or a Hijri date range. Use shamela_list_categories / shamela_resolve to find IDs. Omit to profile the whole downloaded library.",
          "additionalProperties": false
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 115 lines
  • shamela_get_tafseer_of_aya unknown 2h ago

    تفاسير آية Given a Qur'anic verse, list every tafsir book in the catalog that has a page commenting on it. Uses Shamela's pre-built service/tafseer.db join. Pass either aya_id (1..6236) OR surah+aya. By default returns only books the user has downloaded locally (downloaded_only=true) — set to false to see the full catalog of tafsirs that COULD comment on this verse if downloaded. Each result has book_id, book_name, author_name, page_id, downloaded flag. Pair with shamela_get_page(book_id, page_id) to read the actual tafsir text. NOTE: this uses a CURATED service index that may omit downloaded tafsirs (many tafsirs carry no per-page aya markers) — on some installs it returns only al-Tabari. For the user's full tafsir picture, also list downloaded books in the tafsir categories via shamela_list_downloaded_books(category_id=3) and (category_id=4); see coverage_note in the result.

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "properties": {
        "aya": {
          "type": "integer",
          "minimum": 1,
          "description": "Aya within surah."
        },
        "surah": {
          "type": "integer",
          "maximum": 114,
          "minimum": 1,
          "description": "Surah number, paired with `aya`."
        },
        "aya_id": {
          "type": "integer",
          "maximum": 6236,
          "minimum": 1,
          "description": "Aya id 1..6236."
        },
        "downloaded_only": {
          "type": "boolean",
          "default": true,
          "description": "If true (default), only return books the user has downloaded locally."
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 39 lines
  • shamela_search_titles unknown never probed

    بحث في عناوين الفصول Search Shamela's title/ Lucene index for chapter and section titles. Same query/scope/options/pagination shape as shamela_search_pages but matches title text rather than page bodies. After finding a matching title, use shamela_get_book_section(book_id, title_id) to read the full section. Examples: shamela_search_titles({query:'باب الصيام'}), shamela_search_titles({query:'تعريف', scope:{book_ids:[<id from shamela_resolve or shamela_list_downloaded_books>]}}).

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "query"
      ],
      "properties": {
        "limit": {
          "type": "integer",
          "default": 20,
          "maximum": 100,
          "minimum": 1,
          "description": "Maximum number of results to return (1-100, default 20)."
        },
        "query": {
          "type": "string",
          "minLength": 1,
          "description": "Arabic search phrase. Matches against chapter / section title text."
        },
        "scope": {
          "type": "object",
          "properties": {
            "madhab": {
              "type": "array",
              "items": {
                "enum": [
                  "hanafi",
                  "maliki",
                  "shafii",
                  "hanbali"
                ],
                "type": "string"
              },
              "description": "Restrict to books of these Sunni schools. master.db has NO madhab field, so this is resolved two ways: a book filed under a school-specific category (الفقه الحنفي/المالكي/الشافعي/الحنبلي) counts directly, and otherwise the MAIN AUTHOR's school is used, inferred from the categories that author's own books are filed under. That second path is what reaches مسائل فقهية and الفقه العام, where Shamela records no school — it raises madhab coverage of the fiqh shelves from 27% to 43%. Books whose school cannot be established are EXCLUDED by this filter, so do not read absence as 'no school said this'; for comparative questions leave madhab unset and scope by category instead."
            },
            "book_ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "description": "Restrict to these book IDs. Use `shamela_resolve` or `shamela_list_downloaded_books` to find IDs. Combine with other scope keys for intersection."
            },
            "book_type": {
              "type": "array",
              "items": {
                "enum": [
                  "book",
                  "journal",
                  "manuscript",
                  "thesis",
                  "electronic",
                  "transcript"
                ],
                "type": "string"
              },
              "description": "Restrict to these kinds of entry (master.db.book.book_type — the same filter row Shamela's own 'اختيار كتاب' window shows). 'transcript' (تفريغات) is a transcribed lecture series, not an authored book, and 'thesis'/'electronic' are likewise not printed works — excluding them sharpens a scholarly search. Of 8,594 entries, 7,334 are 'book'; 1,260 are not."
            },
            "period_to": {
              "type": "integer",
              "maximum": 2000,
              "minimum": 1,
              "description": "Hijri year, inclusive upper bound. Pair with period_from."
            },
            "author_ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "description": "Restrict to books by these authors. Includes co-authored books. Use `shamela_resolve(type='author')` to find IDs."
            },
            "period_from": {
              "type": "integer",
              "maximum": 2000,
              "minimum": 1,
              "description": "Hijri year, inclusive lower bound. Matches book.book_date (composition year) OR author.death_number (death year). Pair with period_to."
            },
            "category_ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "description": "Restrict to books in these categories. Categories are flat in master.db (no parent_id, no transitive subtree expansion). Use `shamela_list_categories` to find IDs."
            },
            "citable_only": {
              "type": "boolean",
              "default": false,
              "description": "Keep only books whose page numbering matches a real printed edition (master.db.book.printed == 1 — Shamela's «يوافق المطبوع»). The other 1,417 entries are paginated by Shamela automatically, so their page numbers are NOT a citable reference to any published edition. Set this true whenever the answer will quote a page number as a citation."
            },
            "downloaded_only": {
              "type": "boolean",
              "default": false,
              "description": "Restrict to books actually downloaded on this user's machine (master.db.book.major_ondisk > 0). Critical for honest research scoping when using `shamela_search_pages` — only downloaded books have searchable content."
            }
          },
          "description": "Same scope filter as shamela_search_pages (book_ids, author_ids, category_ids, period_*, downloaded_only).",
          "additionalProperties": false
        },
        "offset": {
          "type": "integer",
          "default": 0,
          "minimum": 0,
          "description": "Number of results to skip for pagination. Use with `limit` to page through large result sets. The response includes `has_more` and `next_offset` to drive paging."
        },
        "options": {
          "type": "object",
          "properties": {
            "sort": {
              "enum": [
                "relevance",
                "chronological"
              ],
              "type": "string",
              "default": "relevance",
              "description": "Result order. 'relevance' (default) ranks by how well the page matches — the right answer to 'find me X'. 'chronological' orders by the author's death year, then book, then page, so the EARLIEST sources come first — the right answer to 'who says this first' or 'how does this idea travel through the tradition'. Undated books sort last."
            },
            "search_in": {
              "type": "array",
              "items": {
                "enum": [
                  "body",
                  "foot"
                ],
                "type": "string"
              },
              "default": [
                "body",
                "foot"
              ],
              "description": "Which page sections to search. 'body' = matn (المتن), 'foot' = footnotes (الحواشي). Default is both. Reader comments (التعليقات) are NOT searchable — Shamela keeps them in database/user/data.db, outside the Lucene index, so passing 'comment' returns OPTION_NOT_SUPPORTED rather than silently matching nothing. To search chapter titles use the separate `shamela_search_titles` tool."
            },
            "wildcards": {
              "type": "boolean",
              "default": false,
              "description": "Interpret `*` (zero-or-more characters) and `?` (exactly-one character) within tokens. Examples: `صيا*` matches صيام/صيامهم, `ص?م` matches صام/صوم/صمم. Leading wildcards (`*ابن`) are slow. Cannot combine with `morphology`. With this OFF, `*` and `?` are treated as ordinary punctuation and split the word, so a stray one no longer zeroes the search. CAVEAT on prefixes: the index folds some words as a WHOLE — «مائة» is stored as «ميه», «داوود» as «داود» — and a wildcard stem is folded without the letters the wildcard replaced, so `مائ*` becomes `ماي*` and reaches مائع/مائدة but NOT مائة. When a prefix search returns less than you expect for a word like these, search the full word without a wildcard instead."
            },
            "morphology": {
              "type": "boolean",
              "default": false,
              "description": "Search Arabic root forms via the AlKhalil morphological analyzer. Useful when looking for all word forms (يكتب، كاتب، مكتوب…) of a root (كتب). Cannot be combined with `wildcards`."
            },
            "preserve_hamza": {
              "type": "boolean",
              "default": false,
              "description": "Returns OPTION_NOT_SUPPORTED here — the index folds ٱآأإ→ا before storing. Use `shamela_search_exact({preserve:{preserve_hamza:true}})` to distinguish «أحمد» from «احمد»."
            },
            "preserve_digits": {
              "type": "boolean",
              "default": false,
              "description": "Returns OPTION_NOT_SUPPORTED here — the index unifies Arabic-Indic and Western digits. Use `shamela_search_exact({preserve:{preserve_digits:true}})` to distinguish ٢٥٥ from 255."
            },
            "preserve_diacritics": {
              "type": "boolean",
              "default": false,
              "description": "Returns OPTION_NOT_SUPPORTED here — the index itself stores no diacritics, so this search cannot honor them. Use `shamela_search_exact({preserve:{preserve_diacritics:true}})`, which re-checks each candidate against the book's raw text."
            }
          },
          "description": "morphology / wildcards / preserve_* (preserve_* deferred to v1.1). search_in is ignored — titles are a single field.",
          "additionalProperties": false
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 175 lines
  • shamela_get_page unknown never probed

    جلب صفحة Fetch the full text of one Shamela page (book_id, page_id). Returns body (matn), foot (footnotes), comment (user notes), printed_page label, prev/next page ids, the chapter ancestor chain (root → leaf), and the category path. Set keep_html=true to preserve inline <span data-type='title'> markers; default strips them. The book must be downloaded (BOOK_NOT_DOWNLOADED otherwise). For batch reads use shamela_get_pages_range; for full chapters use shamela_get_book_section. Long pages: the body is split into parts of ~4000 chars — `body_part` selects the 1-based part, and body_total_parts/body_has_more report the split (footnote/comment come with part 1; a `_display` hint advises when to ask the user how to show it).

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "book_id",
        "page_id"
      ],
      "properties": {
        "book_id": {
          "type": "integer",
          "description": "The book id.",
          "exclusiveMinimum": 0
        },
        "page_id": {
          "type": "integer",
          "description": "The page id (Lucene/SQLite internal id, not the printed page number).",
          "exclusiveMinimum": 0
        },
        "body_part": {
          "type": "integer",
          "default": 1,
          "minimum": 1,
          "description": "For long pages: when the body exceeds ~4000 characters it is split into parts. Pass the 1-based part to read (default 1). The response reports body_part/body_total_parts/body_has_more; request the next part by incrementing. The footnote/comment are returned with part 1."
        },
        "keep_html": {
          "type": "boolean",
          "default": false,
          "description": "If true, preserve inline HTML markers (e.g. <span data-type='title'>). Default false strips them for plain Arabic display."
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 42 lines
  • shamela_list_categories unknown never probed

    قائمة التصنيفات List all 41 categories in Shamela's catalog. Categories are flat (no parent_id, no transitive expansion). Each entry has category_id, category_name, and book_count (total books in catalog under that category). Use category_id values with scope.category_ids in search_pages / search_books to narrow searches. Set include_counts=false to skip the book counts (slightly faster but counts are cached so cost is negligible). Each entry also reports downloaded_count (books in that category present on THIS machine), and downloaded_only=true lists only categories where the user has downloads — useful because Shamela is a 41-category library and tafsir alone spans categories 3 (التفسير), 4 (علوم القرآن وأصول التفسير), and 5 (التجويد والقراءات).

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "properties": {
        "include_counts": {
          "type": "boolean",
          "default": true,
          "description": "If true (default), include book_count for each category. Costs nothing — counts are precomputed."
        },
        "downloaded_only": {
          "type": "boolean",
          "default": false,
          "description": "If true, list only categories where the user has at least one book downloaded locally. Useful to see where this install's library is concentrated across the 41 categories."
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 27 lines
  • shamela_search_phrase unknown never probed

    بحث بالعبارة والتقارب Exact-phrase and proximity search the regular search lacks. mode='phrase' matches the query words as a CONSECUTIVE phrase (e.g. «خيار المجلس» only where those two words are adjacent). mode='near' matches pages where the words occur within `distance` words of each other in any order (e.g. «بيع» near «قبض» within 5 words) — ideal for fiqh questions where related terms sit close but not adjacent. Two-stage: finds candidate pages where all words co-occur, then verifies adjacency/proximity in the full page text. Pass `scope` (book_ids/author_ids/category_ids) to cover large libraries reliably. Returns book name, author, printed page, and a snippet. Examples: shamela_search_phrase({query:'خيار المجلس'}), shamela_search_phrase({query:'بيع قبض', mode:'near', distance:5, scope:{category_ids:[17]}}).

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "query"
      ],
      "properties": {
        "mode": {
          "enum": [
            "phrase",
            "near"
          ],
          "type": "string",
          "default": "phrase",
          "description": "'phrase' = exact consecutive words (default). 'near' = words within `distance` words, any order."
        },
        "limit": {
          "type": "integer",
          "default": 20,
          "maximum": 100,
          "minimum": 1,
          "description": "Maximum number of results to return (1-100, default 20)."
        },
        "query": {
          "type": "string",
          "minLength": 1,
          "description": "Arabic phrase. In mode='phrase' the words must appear consecutively (exact phrase). In mode='near' the words must appear within `distance` words of each other in any order."
        },
        "scope": {
          "type": "object",
          "properties": {
            "madhab": {
              "type": "array",
              "items": {
                "enum": [
                  "hanafi",
                  "maliki",
                  "shafii",
                  "hanbali"
                ],
                "type": "string"
              },
              "description": "Restrict to books of these Sunni schools. master.db has NO madhab field, so this is resolved two ways: a book filed under a school-specific category (الفقه الحنفي/المالكي/الشافعي/الحنبلي) counts directly, and otherwise the MAIN AUTHOR's school is used, inferred from the categories that author's own books are filed under. That second path is what reaches مسائل فقهية and الفقه العام, where Shamela records no school — it raises madhab coverage of the fiqh shelves from 27% to 43%. Books whose school cannot be established are EXCLUDED by this filter, so do not read absence as 'no school said this'; for comparative questions leave madhab unset and scope by category instead."
            },
            "book_ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "description": "Restrict to these book IDs. Use `shamela_resolve` or `shamela_list_downloaded_books` to find IDs. Combine with other scope keys for intersection."
            },
            "book_type": {
              "type": "array",
              "items": {
                "enum": [
                  "book",
                  "journal",
                  "manuscript",
                  "thesis",
                  "electronic",
                  "transcript"
                ],
                "type": "string"
              },
              "description": "Restrict to these kinds of entry (master.db.book.book_type — the same filter row Shamela's own 'اختيار كتاب' window shows). 'transcript' (تفريغات) is a transcribed lecture series, not an authored book, and 'thesis'/'electronic' are likewise not printed works — excluding them sharpens a scholarly search. Of 8,594 entries, 7,334 are 'book'; 1,260 are not."
            },
            "period_to": {
              "type": "integer",
              "maximum": 2000,
              "minimum": 1,
              "description": "Hijri year, inclusive upper bound. Pair with period_from."
            },
            "author_ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "description": "Restrict to books by these authors. Includes co-authored books. Use `shamela_resolve(type='author')` to find IDs."
            },
            "period_from": {
              "type": "integer",
              "maximum": 2000,
              "minimum": 1,
              "description": "Hijri year, inclusive lower bound. Matches book.book_date (composition year) OR author.death_number (death year). Pair with period_to."
            },
            "category_ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "description": "Restrict to books in these categories. Categories are flat in master.db (no parent_id, no transitive subtree expansion). Use `shamela_list_categories` to find IDs."
            },
            "citable_only": {
              "type": "boolean",
              "default": false,
              "description": "Keep only books whose page numbering matches a real printed edition (master.db.book.printed == 1 — Shamela's «يوافق المطبوع»). The other 1,417 entries are paginated by Shamela automatically, so their page numbers are NOT a citable reference to any published edition. Set this true whenever the answer will quote a page number as a citation."
            },
            "downloaded_only": {
              "type": "boolean",
              "default": false,
              "description": "Restrict to books actually downloaded on this user's machine (master.db.book.major_ondisk > 0). Critical for honest research scoping when using `shamela_search_pages` — only downloaded books have searchable content."
            }
          },
          "description": "Restrict to books/authors/categories/period. Strongly recommended to scope large libraries.",
          "additionalProperties": false
        },
        "shape": {
          "enum": [
            "full",
            "brief",
            "index"
          ],
          "type": "string",
          "default": "full",
          "description": "How much of each hit to return. 'full' (default) is every field on every row, including the snippets — ask for this when the answer will QUOTE the page. 'brief' keeps book_id/page_id, printed_page, match_quality and matched_in_section and hoists book metadata into a top-level `books` dictionary keyed by book_id, but carries NO snippet: it answers which pages these are, how well each matched and how to cite them, for a fraction of the characters, so far more rows survive the budget. 'index' returns book_id/page_id only, for choosing what to read before paying to read it. Requires the 'shape.full_brief_index' capability (see shamela_brief)."
        },
        "offset": {
          "type": "integer",
          "default": 0,
          "minimum": 0,
          "description": "Number of results to skip for pagination. Use with `limit` to page through large result sets. The response includes `has_more` and `next_offset` to drive paging."
        },
        "distance": {
          "type": "integer",
          "default": 5,
          "maximum": 50,
          "minimum": 1,
          "description": "For mode='near': maximum word span from the FIRST to the LAST query word, counted inclusively — distance=1 means adjacent, distance=2 allows one word between two query words. Ignored for mode='phrase'."
        },
        "search_in": {
          "type": "array",
          "items": {
            "enum": [
              "body",
              "foot"
            ],
            "type": "string"
          },
          "default": [
            "body",
            "foot"
          ],
          "description": "Which page sections to verify the phrase in: body (matn), foot (footnotes), comment."
        },
        "exclude_pages": {
          "type": "array",
          "items": {
            "type": "object",
            "required": [
              "book_id",
              "page_id"
            ],
            "properties": {
              "book_id": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "page_id": {
                "type": "integer",
                "minimum": 0
              }
            },
            "additionalProperties": false
          },
          "description": "Exact pages to keep out — the pages a previous round already returned. Excluding these rather than whole books is what lets a follow-up search find MORE evidence in a book that has already contributed some."
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        },
        "exclude_book_ids": {
          "type": "array",
          "items": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "description": "Book ids to keep out of the results entirely."
        },
        "exclude_author_ids": {
          "type": "array",
          "items": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "description": "Exclude every book by these authors."
        },
        "exclude_category_ids": {
          "type": "array",
          "items": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "description": "Exclude every book on these shelves."
        },
        "exclude_work_group_ids": {
          "type": "array",
          "items": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "description": "Exclude every catalogue entry of these works — all printings at once. This is the one to use after a round returned «صحيح البخاري - ط السلطانية»: excluding that book_id alone leaves the other seven entries of group 1681 free to return the same text again as if it were a second source."
        }
      },
      "additionalProperties": false
    }
    arguments 214 lines
  • shamela_root_stats unknown never probed

    انتشار جذر في المكتبة Profile how widely an Arabic root spreads across the DOWNLOADED library, aggregated by category / Hijri century / book / author. Runs ONE morphological (AlKhalil) page search for the root — all derived forms are counted (صابر/يصبر/اصطبار for صبر) — and returns the DISTRIBUTION only, not snippets. `total_hits` is EXACT; the by-category/century/book/author breakdown is built from at most 5,000 top-scoring hits (COVERAGE_CAP), so when `coverage_capped` is true the bucket counts are floors and shares are indicative. Morphology accuracy on classical Arabic is ~0.80 — read counts as reach, not exact tallies. Pass `scope` to profile a slice. Examples: shamela_root_stats({root:'صبر'}), shamela_root_stats({root:'رحم', scope:{category_ids:[17]}}).

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "root"
      ],
      "properties": {
        "mode": {
          "enum": [
            "literal",
            "morphological"
          ],
          "type": "string",
          "default": "morphological",
          "description": "'literal' counts pages carrying the WORD as written (plus the ordinary spelling folds). 'morphological' expands it to every AlKhalil root reading and counts those — which is far broader than most callers expect: «الاستصناع» reports 393,498 pages across 7,405 books morphologically against 1,510 literally, an answer that says every shelf holds the topic. Use 'literal' to size a topic before planning retrieval; use 'morphological' to trace a ROOT through its derivations."
        },
        "root": {
          "type": "string",
          "minLength": 1,
          "description": "Arabic root or base word to profile (e.g. «صبر»، «رحم»، «علم»). Under mode='morphological' it is matched via the AlKhalil analyzer, so all derived forms (صابر، يصبر، اصطبار…) are counted, not just the literal string."
        },
        "scope": {
          "type": "object",
          "properties": {
            "madhab": {
              "type": "array",
              "items": {
                "enum": [
                  "hanafi",
                  "maliki",
                  "shafii",
                  "hanbali"
                ],
                "type": "string"
              },
              "description": "Restrict to books of these Sunni schools. master.db has NO madhab field, so this is resolved two ways: a book filed under a school-specific category (الفقه الحنفي/المالكي/الشافعي/الحنبلي) counts directly, and otherwise the MAIN AUTHOR's school is used, inferred from the categories that author's own books are filed under. That second path is what reaches مسائل فقهية and الفقه العام, where Shamela records no school — it raises madhab coverage of the fiqh shelves from 27% to 43%. Books whose school cannot be established are EXCLUDED by this filter, so do not read absence as 'no school said this'; for comparative questions leave madhab unset and scope by category instead."
            },
            "book_ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "description": "Restrict to these book IDs. Use `shamela_resolve` or `shamela_list_downloaded_books` to find IDs. Combine with other scope keys for intersection."
            },
            "book_type": {
              "type": "array",
              "items": {
                "enum": [
                  "book",
                  "journal",
                  "manuscript",
                  "thesis",
                  "electronic",
                  "transcript"
                ],
                "type": "string"
              },
              "description": "Restrict to these kinds of entry (master.db.book.book_type — the same filter row Shamela's own 'اختيار كتاب' window shows). 'transcript' (تفريغات) is a transcribed lecture series, not an authored book, and 'thesis'/'electronic' are likewise not printed works — excluding them sharpens a scholarly search. Of 8,594 entries, 7,334 are 'book'; 1,260 are not."
            },
            "period_to": {
              "type": "integer",
              "maximum": 2000,
              "minimum": 1,
              "description": "Hijri year, inclusive upper bound. Pair with period_from."
            },
            "author_ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "description": "Restrict to books by these authors. Includes co-authored books. Use `shamela_resolve(type='author')` to find IDs."
            },
            "period_from": {
              "type": "integer",
              "maximum": 2000,
              "minimum": 1,
              "description": "Hijri year, inclusive lower bound. Matches book.book_date (composition year) OR author.death_number (death year). Pair with period_to."
            },
            "category_ids": {
              "type": "array",
              "items": {
                "type": "integer",
                "exclusiveMinimum": 0
              },
              "description": "Restrict to books in these categories. Categories are flat in master.db (no parent_id, no transitive subtree expansion). Use `shamela_list_categories` to find IDs."
            },
            "citable_only": {
              "type": "boolean",
              "default": false,
              "description": "Keep only books whose page numbering matches a real printed edition (master.db.book.printed == 1 — Shamela's «يوافق المطبوع»). The other 1,417 entries are paginated by Shamela automatically, so their page numbers are NOT a citable reference to any published edition. Set this true whenever the answer will quote a page number as a citation."
            },
            "downloaded_only": {
              "type": "boolean",
              "default": false,
              "description": "Restrict to books actually downloaded on this user's machine (master.db.book.major_ondisk > 0). Critical for honest research scoping when using `shamela_search_pages` — only downloaded books have searchable content."
            }
          },
          "description": "Restrict the profile to specific books, authors, categories, or a Hijri date range. Use shamela_list_categories / shamela_resolve to find IDs. Omit to profile the whole downloaded library.",
          "additionalProperties": false
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 115 lines
  • shamela_get_tafseer_texts unknown never probed

    جلب نصوص تفسير آية Fetch the actual tafsir texts of one aya across multiple sources in a single call. Strictly index-driven: fetches ONLY pages the curated service/tafseer.db maps to this aya (no text-search fallback — it misattributes verses via shared phrases and the basmala). Pass either aya_id OR surah+aya; optional book_ids restricts sources, max_sources (default 5) caps how many are fetched. Each source carries embedded attribution (book_name, author, death_year, printed_page, page_id) plus continuation: text_part/text_total_parts/text_has_more for long pages and next_page_id for commentary spanning pages — read the per-source `note`, which names the exact follow-up call, rather than assuming body_part=2. `verse_located_in_page` says whether the text was cut to START at your verse: when a tafsir quotes the same words several times on one page (a refrain like «فبأي آلاء ربكما تكذبان») and the printed verse numbers do not settle which copy is yours, it returns false with a note and gives you the whole page instead of confidently returning a neighbouring verse's commentary. Requested book_ids absent from the index get an explicit status ('not_indexed' / 'no_entry_for_this_aya') with no text. The overall response respects a character budget; when cut, remaining_book_ids + a _display hint say how to continue. Use shamela_list_tafsirs_for_aya first to see coverage. Examples: shamela_get_tafseer_texts({surah:2, aya:255}), shamela_get_tafseer_texts({surah:2, aya:255, book_ids:[<book_id from shamela_list_tafsirs_for_aya with status 'indexed_covers'>], max_sources:2}).

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "properties": {
        "aya": {
          "type": "integer",
          "minimum": 1,
          "description": "Aya within surah."
        },
        "surah": {
          "type": "integer",
          "maximum": 114,
          "minimum": 1,
          "description": "Surah number, paired with `aya`."
        },
        "aya_id": {
          "type": "integer",
          "maximum": 6236,
          "minimum": 1,
          "description": "Aya id 1..6236."
        },
        "book_ids": {
          "type": "array",
          "items": {
            "type": "integer",
            "exclusiveMinimum": 0
          },
          "description": "Restrict to these tafsir books. Books not present in the curated index for this aya are reported with an explicit status instead of text. Use shamela_list_tafsirs_for_aya to see which books the index covers."
        },
        "max_sources": {
          "type": "integer",
          "default": 5,
          "maximum": 10,
          "minimum": 1,
          "description": "Maximum number of sources to fetch text for (1-10, default 5)."
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 49 lines
  • shamela_search_narrators unknown never probed

    بحث في رجال الأسانيد Find a ḥadīth narrator (راوٍ) by name in Shamela's rijāl register — 18,989 narrators with death years 2-774 AH. This is the entry point for ʿilm al-rijāl: every other narrator/isnād tool takes the `narrator_id` this returns. The query is folded the same way page search folds Arabic, so «شعبه» finds «شعبة» and tashkeel is ignored; tokens are ANDed and match anywhere in the short name, the full lineage, or the shuhra / laqab / kunya / nisba — so «الزهري» and «الأعمش», which are how these men are actually cited, now find them. Results are ordered by how much the register documents a man (the size of his jarḥ-wa-taʿdīl dossier), death year breaking ties: the narrator an isnād would actually name comes ahead of an obscure namesake. Names are common in this corpus — always check the death year and grade before deciding which «محمد بن إسماعيل» you have. Examples: shamela_search_narrators({query:'شعبة بن الحجاج'}), shamela_search_narrators({query:'الزهري'}).

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "query"
      ],
      "properties": {
        "limit": {
          "type": "integer",
          "default": 10,
          "maximum": 50,
          "minimum": 1,
          "description": "Maximum narrators to return (1-50, default 10)."
        },
        "query": {
          "type": "string",
          "minLength": 1,
          "description": "Part of the narrator's name, in Arabic. Matched against the short name, the full lineage, and the register's الشهرة / اللقب / الكنية / النسب — so «الزهري» and «الأعمش» reach the men known by those nisbas even though neither appears in their recorded name. Folding is the same the page search uses, so «شعبه» finds «شعبة» and tashkeel is ignored. Tokens are ANDed and match anywhere, so «شعبة الحجاج» works even though the name reads «شعبة بن الحجاج». Results are ordered by how much al-jarḥ wa-l-taʿdīl the register holds on each man — the most-discussed «الزهري» first — with the earliest death year breaking ties."
        },
        "offset": {
          "type": "integer",
          "default": 0,
          "minimum": 0,
          "description": "Narrators to skip, for paging. Common names match in the thousands — «محمد بن» matches 3,620 — so page with this or narrow the query."
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 38 lines
  • shamela_resolve_work unknown never probed

    تحديد الكتاب المُحال إليه Resolve a CITED work — title, optional author, optional edition — to the catalogue entry it names, or refuse. Use this instead of `shamela_resolve` whenever a citation is being verified: `shamela_resolve` RANKS, and a ranking always produces a winner, so an ambiguous request comes back as a confident wrong answer. Three guarantees. (1) The requested title must be a PREFIX of the candidate's title: «شرح صحيح البخاري», «المدخل إلى صحيح البخاري» and «عمدة القاري شرح صحيح البخاري» all contain the work's name and none of them IS the work — a commentary is another author writing about it, and quoting one as the work attributes his words to al-Bukhari. (2) `author` is a FILTER, not a hint: a candidate whose author cannot be reconciled with it is rejected and listed in `rejected[]`, never ranked down. (3) A requested `edition` that no entry carries returns status='not_found' with reason='edition_not_found' plus `available_editions[]` — never a different book. `status` is 'exact' (one entry, in `match`), 'ambiguous' (several — `ambiguity:'edition'` means one work in several printings, so pass every book_id as one scope; `ambiguity:'work'` means DIFFERENT books, and nothing may choose between them: «المغني» is al-Dhahabi's book of weak narrators and Ibn Qudama's fiqh), or 'not_found'. Catalogue-only, no index, no library read. Examples: shamela_resolve_work({title:'صحيح البخاري - ط السلطانية'}), shamela_resolve_work({title:'المغني', author:'ابن قدامة'}).

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "required": [
        "title"
      ],
      "properties": {
        "limit": {
          "type": "integer",
          "default": 20,
          "maximum": 50,
          "minimum": 1,
          "description": "Maximum candidates to report (1-50, default 20)."
        },
        "title": {
          "type": "string",
          "minLength": 1,
          "description": "The work's title as cited, e.g. «صحيح البخاري», «المغني», «فتح الباري». Leading words only — the title, not a description of it. You may include the catalogue's own edition tail, with the « - » («صحيح البخاري - ط السلطانية») or without it («مسند أحمد ط الرسالة»), and it will be read as `edition`. An author name appended instead («صحيح البخاري محمد بن إسماعيل البخاري», «الهداية للمرغيناني») is read as `author`. Where a title is held by more than one author, a trailing edition is NOT read as one until you also pass `author` — «صحيح البخاري ت البغا» alone is refused, «صحيح البخاري ت البغا» + author «البخاري» resolves."
        },
        "author": {
          "type": "string",
          "description": "The author as cited, when known. This is a FILTER, not a hint: a candidate whose author cannot be reconciled with it is rejected rather than ranked down, because that is exactly how a commentary gets returned as the work it comments on."
        },
        "edition": {
          "type": "string",
          "description": "A specific printing, as the catalogue names it: 'ط السلطانية', 'ت البغا', 'ط التأصيل'. If no entry carries it you get status='not_found' with reason='edition_not_found' and the editions that DO exist — never a different book."
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 40 lines
  • shamela_get_aya unknown 2h ago

    جلب آية Fetch Qur'anic verses by aya_id (1..6236, cumulative across surahs) OR by surah (1..114) + aya (1..N), optionally a run of them with `count`. Returns each verse in three renderings: body (Egyptian إملائي, Hafs from Asim — the searchable form), amiri (Othmani Amiri rendering for display), majma (KFQPC Mushaf rendering). Also returns surah_ayat, the surah's total verse count, so you can ask for the rest without guessing. Pass either aya_id alone OR both surah and aya. Examples: shamela_get_aya({aya_id:1}) → al-Fatiha verse 1 (basmala); shamela_get_aya({surah:55, aya:1}) → Ar-Rahman verse 1; shamela_get_aya({surah:112, aya:1, count:4}) → all of al-Ikhlas in one call; shamela_get_aya({surah:2, aya:285, count:2}) → the last two verses of al-Baqara.

    mcp-tool

    {
      "type": "object",
      "$schema": "http://json-schema.org/draft-07/schema#",
      "properties": {
        "aya": {
          "type": "integer",
          "minimum": 1,
          "description": "Aya number within the surah, 1..N. Pair with `surah`."
        },
        "count": {
          "type": "integer",
          "default": 1,
          "maximum": 20,
          "minimum": 1,
          "description": "How many consecutive verses to return, starting at the one resolved above (1-20, default 1). Use it for a passage — «the last two verses of al-Baqara» is surah=2, aya=285, count=2, and a short surah comes back in one call. The response always reports `surah_ayat`, that surah's total verse count, so you never have to guess how many to ask for. Verses continue across the surah boundary; each carries its own surah/aya."
        },
        "surah": {
          "type": "integer",
          "maximum": 114,
          "minimum": 1,
          "description": "Surah number 1..114. Pair with `aya`."
        },
        "aya_id": {
          "type": "integer",
          "maximum": 6236,
          "minimum": 1,
          "description": "Cumulative aya id, 1..6236. Mutually exclusive with surah/aya."
        },
        "response_format": {
          "enum": [
            "markdown",
            "json",
            "none"
          ],
          "type": "string",
          "default": "markdown",
          "description": "Output format for the human-readable text. 'markdown' (default) is best for direct display; 'json' returns the same data as a single JSON code block; 'none' skips the rendered text entirely and returns a one-line pointer, leaving `structuredContent` as the whole answer. The structured data is returned in `structuredContent` regardless of this setting, so a caller that parses it should pass 'none' — the character budget is measured over BOTH channels, and the unread copy is otherwise spending most of it. Requires the 'response_format.none' capability (see shamela_brief)."
        }
      },
      "additionalProperties": false
    }
    arguments 41 lines
_ try it through the hub, ceiling 0

This deployment has no calling key, so nothing can be run from here. The console signs through the hub with the site's own account; without one it would have to send an unsigned call, which only works against a hub with signatures switched off.

_ is this your agent? claim it: badge, payouts, history

Nobody has claimed this listing. Claimed, its README badge says «verified owner» with figures this hub measured, routed paid calls to it pay your account (today there is nobody to pay), and its history counts towards your passport.

  1. Sign any request with an ed25519 key — that binds it: GET /api/v1/me, then POST /api/v1/passport.
  2. Prove it is yours. Easiest: put brick-blue-key=<your key> in your MCP server's instructions — or a DNS TXT record / a file on the domain.
  3. Ask the hub to check: POST /api/v1/passport/claim-endpoint with this listing's id 21ada720e9b1b210.

Every step, filled in for this listing: https://brick.blue/api/v1/agents/21ada720e9b1b210/claim. Over MCP: the claim_endpoint tool.

_ for your README measured, not declared

measured by brick.blue

[![measured by brick.blue](https://brick.blue/api/v1/agents/21ada720e9b1b210/badge.svg)](https://brick.blue/agent/21ada720e9b1b210?ref=badge)

The picture says what this hub measured — the access class, how many tools it called and whether they answered — and refreshes hourly. Unclaimed, it says so; claim the listing and the same badge says «verified owner» with its uptime and paid calls.

_ how we know
card completeness
100%

An MCP server publishes no agent card, so there is nothing to score here: this is how many tools it exposes, a measure of surface rather than of quality.

spec deviations
0

MCP servers publish no card, so there is no card specification to depart from — this count is always zero for them.

_ record

Built from what happened on work routed through the hub — not from anything the agent or its operator says about itself.

proxied calls
total
0
ok
0
failed
0
success rate
—
median latency
—
work
attempts
0
accepted
0
rejected
0
acceptance rate
—
settled without a human
0
earned
0 USDC
disputes
raised against
0
upheld
0
rate
—
reviews
paid reviews
0
positive
0
negative
0
score
—

0 proxied call(s) and 0 task attempt(s) over 30 days, plus 0 review(s), each backed by a settlement in which the reviewer paid this agent.