Tools
The four Qaf MCP tools, search, fetch, lookup_catalog and get_quran_verses, with every input, limit, default and output field.
The Qaf MCP server has four tools. They only read the library: none of them changes anything or reads your account. Each result comes back twice, as structured content and as the same JSON in a text block, so apps that read either one get the full result.
Every tool takes locale, en (the default) or ar, which picks the language of book, author and surah names. Passage and verse text stays Arabic.
search
Searches the library with hybrid semantic and keyword retrieval, then reranks the matches for relevance. Returns passages with their book, author, printed pages and a link to the page. API equivalent: POST /v1/search.
| Input | Type | Limits and default | Description |
|---|---|---|---|
query |
string | Required. 1–2,000 characters after trimming. | What to search for, in Arabic or English. |
filters |
object | Optional. Set at least one field. | Restricts results by the passage’s book and author, not by what the passage mentions. |
filters.authorIds |
integer[] | 1–20 IDs | Author IDs from lookup_catalog or a passage’s author.id. |
filters.bookIds |
string[] | 1–20 UUIDs | Book IDs from lookup_catalog or a passage’s book.id. |
filters.deathYearAH |
object | At least one of gt, gte, lt, lte, as integers |
The author’s death year in AH. All bounds must hold, and together they must allow at least one year. Leaves out authors with no recorded death year. |
filters.genres |
string[] | 1–20 slugs | Genre slugs. A genre describes a book, not a topic. |
filterOperator |
"AND" or "OR" |
Default "AND" |
How the filter fields combine: AND requires every one, OR accepts any. The IDs within one list always combine with OR. |
limit |
integer | 1–15, default 5 | Passages to return. |
excludeIds |
string[] | Up to 200 UUIDs | Passages to skip. Pass the IDs you already have to get further passages for the same query. |
locale |
"en" or "ar" |
Default "en" |
Language of book and author names. |
Output: { "results": [passage, …] }, best match first. Each passage has the fields described in The Qaf library.
Errors the app can correct: an author or book ID that isn’t in the catalog, or filters that no book can match, such as a genre with a death-year range none of its authors fall in. The tool explains the problem so the app can fix its call.
fetch
Reads a passage by ID, with up to radius neighbouring passages on each side, in reading order. Neighbours carry their own IDs, so reading can continue from either end. API equivalent: GET /v1/passages/{id}.
| Input | Type | Limits and default | Description |
|---|---|---|---|
id |
string | Required. A UUID. | A passage ID from search or fetch. |
radius |
integer | 0–5, default 0 | Neighbouring passages to include on each side. |
locale |
"en" or "ar" |
Default "en" |
Language of book and author names. |
Output: the passage’s fields, plus before and after: arrays of passages in reading order. Neighbours come from the same edition, so a passage near the start or end of a book has fewer.
Errors: an ID with no passage: No passage has ID <id>; use an ID returned by search or fetch.
lookup_catalog
Resolves an author or book name to catalog IDs for search filters. Returns up to five candidates, each with a match score. API equivalent: GET /v1/catalog/lookup.
| Input | Type | Limits and default | Description |
|---|---|---|---|
type |
"author" or "book" |
Required | Whether the name is an author or a book. |
query |
string | Required. 1–200 characters after trimming. | The name to resolve, in Arabic or English. |
locale |
"en" or "ar" |
Default "en" |
Language of the candidate labels. |
Output: { "candidates": [candidate, …] }. Each candidate has a type:
| Field | Author | Book | Description |
|---|---|---|---|
id |
integer | UUID | The ID that filters.authorIds or filters.bookIds takes. |
label |
yes | yes | The author’s name or the book’s title, in locale. |
authorId, authorLabel |
yes | The book’s author. | |
deathYearAH |
yes | yes | The author’s death year in AH, or null when unknown. |
deathInexactLabel |
yes | yes | A label for an approximate or unknown death year, or null. |
categoryId |
yes | The book’s genre ID. | |
matchScore |
yes | yes | How closely the name matches the query, from 0 to 1. |
When several candidates are close, the app should choose by the question’s context or ask you which one you mean.
get_quran_verses
Gets the Uthmani Arabic text of a verse, or of up to 50 consecutive verses in one surah, with the surah name, juz and place of revelation. The text comes from the Quran Foundation API. API equivalent: GET /v1/quran/verses.
| Input | Type | Limits and default | Description |
|---|---|---|---|
verseKey |
string | Required. 3–15 characters. | A surah and verse, like 2:255, or a range in one surah, like 2:255-257. At most 50 verses. |
locale |
"en" or "ar" |
Default "en" |
en adds an English translation and the English surah name; ar returns Arabic only. |
Output:
| Field | Description |
|---|---|
surahName |
The surah’s name in locale. |
surahNameArabic |
The surah’s Arabic name. |
surahNumber |
1–114. |
verseStart, verseEnd |
The verses returned. |
verseText |
The Arabic text of the verses, separated by spaces. |
translation |
The English translation for en; null for ar. |
juz |
The juz of the first verse, 1–30. |
revelation |
makki, madani or unknown. |
Errors: a key that names no real verse, or a range over 50 verses, fails validation.
Errors
A tool that fails returns an error result instead of output:
- Invalid input, such as a
limitof 20: the message names the field and the rule. - Input the app can correct, such as an unknown author ID: the message explains what to change.
- Anything else, such as a call that runs past 30 seconds:
Qaf could not complete this request. Try again shortly.