How Arabic search behaves in Shamela MCP
What the Shamela MCP search ignores and keeps: diacritics, hamza, ta marbuta, exact search, root expansion, wildcards, boolean queries, empty results.
On this page
An assistant that searches an Arabic library needs to know what the search treats as the same word. This page records the behaviour of the Quran Gallery server at https://qurangallery.com/mcp/shamela, each point checked against the running server on 2026-11-01.
The query must be Arabic
The library is Arabic. shamela_search_library and search reject a query that has no Arabic letters, and the error tells the assistant what to do:
The library is Arabic, so the query must contain Arabic words. Translate the search terms into Arabic and call again, for example «إنما الأعمال بالنيات» for “actions are judged by intentions”.
The result is marked isError, so the assistant reads the message and retries. A query that mixes Arabic with other words is accepted. The raw shamela_* tools pass the query through unchanged. shamela_search (match: 'phrase') answers an English query with BAD_ARG: Query has no searchable Arabic words., while shamela_search (match: 'words') searches the letters as given, which can only match Latin text inside the library and is almost never what you meant.
What the search ignores
The index stores words in a normalised form, and your query is normalised the same way, so these differences do not change the result.
| Difference | Example | Same result? |
|---|---|---|
| Diacritics (tashkil) | «إِنَّمَا الأَعْمَالُ بِالنِّيَّاتِ» and «إنما الأعمال بالنيات» | Yes |
| Hamza on alif | «انما الاعمال بالنيات» and «إنما الأعمال بالنيات» | Yes |
| Ta marbuta and ha | «رحمة الله» and «رحمه الله» | Yes: 37 hits in Sahih al-Bukhari (Sultaniyya) for both |
| Alif maqsura and ya | «النبى» and «النبي» | Yes: the same 4,707 pages in Sahih al-Bukhari (Sultaniyya) for both |
The proof for the first two rows is the hadith search: the vocalised phrase, the bare phrase and the bare-alif phrase all return the same passages from the eight primary collections (five passages, three of them exact-phrase matches; see Search the main text or the footnotes). The result of shamela_search (match: 'words') lists normalized_tokens, the form actually searched, so an assistant can see what its query became.
The consequence is that you cannot find a diacritised spelling by typing the diacritics into an ordinary search, and you cannot exclude one by omitting them. When the distinction matters, use the exact search below.
When the distinction matters: exact search
shamela_search (match: 'exact') runs in two stages. A normalised search gathers candidates, then each candidate’s raw page text is checked in the server against what you typed. You choose what must match exactly with a preserve object whose three flags are preserve_diacritics, preserve_hamza and preserve_digits; at least one must be true.
{
"name": "shamela_search",
"arguments": {
"match": "exact",
"query": "عِلْم",
"preserve": { "preserve_diacritics": true },
"scope": { "book_ids": [1681] },
"limit": 5
}
}Type the query with the diacritics you need. With limit 5 it scanned 40 candidates in that book, reached the candidate cap, and verified 24 pages whose raw text has «عِلْم» as written. At the default limit of 20 the cap is higher: it scanned 62 candidates, did not hit the cap, and verified 38. The cap scales with limit, which is why a negative needs candidate_cap_hit checked. With preserve_hamza, a search for «أحمد» in the same book scanned 40 candidates at limit 5, hit the candidate cap and verified 40 pages whose raw text has that spelling.
Two fields tell you how far to trust a negative: candidate_cap_hit says the candidate list was truncated, and total_verified says how many of the scanned candidates passed. Narrow the scope and run it again if the cap was hit.
The preserve_* options also exist on shamela_search (match: 'words'), but there they are declared and not supported: they return OPTION_NOT_SUPPORTED. Use shamela_search (match: 'exact') for them.
Root expansion
shamela_search (match: 'words') accepts options.morphology: true, which expands each word to the Arabic roots a morphological analyser finds for it. Arabic roots are ambiguous and the analyser keeps every reading, so a word is searched under all of them. For «كتب» the response lists exactly what was searched in morphology_expanded_to:
"كتب": ["كتب", "تبب", "توب", "وتب"]The extra readings include roots to do with perdition and repentance. In Sahih al-Bukhari (Sultaniyya), «كتب» matches 110 pages without root expansion and 714 with it. Treat root expansion results as a superset of the word you asked for, read the expansion list, and prefer a plain or phrase search when you want that word and no other.
Wildcards
Set options.wildcards: true and use * for any run of letters (including none) and ? for exactly one letter. In the same book, صيا* matches 52 pages. Wildcards cannot be combined with morphology: asking for both returns OPTION_CONFLICT.
One limit follows from the way words are stored. The index folds each word as a whole and some spellings change shape, for example «مائة» is stored as «ميه», so a wildcard that begins with the printed letters, such as مائ*, will miss it. Search the word itself when the spelling is irregular.
Boolean queries
shamela_search (match: 'boolean') takes all_of, any_of and none_of lists, with at least one of all_of or any_of. It runs as one query over the whole index, so total_hits is exact, and it returns zero_hit_terms for words that occur nowhere. For example, all_of: ["الوقف"], any_of: ["المسجد", "المقبرة"], none_of: ["البيع"] in category 17 ranged over 153 books and found 397 pages. See Search the main text or the footnotes for combining it with a layer filter.
When nothing matches
shamela_search (match: 'words') has options.relax_when_empty, on by default. If the full query finds nothing, it retries requiring fewer of the words, never fewer than half, and says so in the response with relaxed_min_match and relaxed_of_terms. The query «إنما الأعمال بالنيات زززكك», where the fourth word is made up, came back in Sahih al-Bukhari with relaxed_min_match: 3 of 4 and one hit. Set options.relax_when_empty to false when an empty answer is the answer you need; the same query then returns no hits.
shamela_search_library has the same idea in its own form: it tries the exact phrase first, then a word search, and reports which answered with relaxedToTokens. When it is true, the phrase itself was not found. An assistant should say so, not present the hit as a quotation of the phrase.
Ranking
Without a scope the ranking is by term frequency, so a commentary or a poetry anthology that repeats the words often can rank above the hadith collection. That is why shamela_search_library requires a scope and why the twelve ready-made scopes exist (Choosing a scope). The unscoped search tool keeps the default for deep research, with that trade-off.
Summary for an assistant
- Translate to Arabic before searching.
- Do not rely on diacritics or hamza to narrow an ordinary search.
- Use
shamela_search(match: 'exact') when a spelling must match. - Read
morphology_expanded_towhen using root expansion. - Check
relaxedToTokensandrelaxed_min_matchbefore calling a hit a quotation.