Skip to content

Commit 6e96fa6

Browse files
committed
Release v1.6.0: search both AI summaries, return one
The dataset now splits bibo:shortDescription's two @language literals into descriptionAI (fr) + descriptionAI_en rather than pipe-joining them (fmadore/IWAC-Hugging-Face#9). Both are added to SUBSET_FIELDS for articles, documents and audiovisual. The English column is `searchable: true` with `views: []` — searched, never returned. Those two halves are each load-bearing: - Without `searchable`, the English summaries ship to the Hub but stay out of TEXT_COLS/FAST_TEXT_COLS, so an English query matches nothing they contain. That is strictly worse for anglophone discovery than the pipe-joined column it replaced, which at least had the English text inside a searched field. - Without `views: []`, every response carries both summaries. They say the same thing about the same item, so that roughly doubles the ~125 tokens/row an abstract costs to tell the reader nothing new. French is the one returned: source language, and the only one present on every row (the 51 non-FR/EN articles keep an untagged French summary with no English counterpart). So a keyword hit can now come from a language the item is not written in. The tool descriptions, the MCP instructions string and the research skill all say so — an English keyword matching a French article is a discovery route, not evidence the term is attested in the source. Guards: a unit test asserting the English column reaches both TEXT_COLS and FAST_TEXT_COLS wherever the French one exists, another asserting no view ever projects it, and a fixture where "pilgrimage" appears only in descriptionAI_en so a hit can only have arrived through the English column. Fixtures carry the column with English text that is deliberately not a translation. Tool definitions grow 32 tokens (0.2%), within budget.
1 parent 00eb2ed commit 6e96fa6

16 files changed

Lines changed: 186 additions & 41 deletions

.agents/skills/iwac-mcp/references/biases-and-limitations.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ The 4,697 index entries (persons, organizations, places, events, subjects) repre
110110
## 8. Search Limitations
111111

112112
- **Substring matching only** — no wildcards, fuzzy matching, or Boolean operators. One term per call; run variants as separate searches.
113-
- **Keyword scope varies by tool:** articles = title + OCR + AI abstract; publications = title + subject + table of contents + OCR; references = title + abstract. Keyword does NOT search the spatial field — use `subject` for curated tags.
113+
- **Keyword scope varies by tool:** articles = title + OCR + AI abstracts (**both languages**); publications = title + subject + table of contents + OCR; references = title + abstract. Keyword does NOT search the spatial field — use `subject` for curated tags.
114+
- **An English keyword can match a French article through its AI abstract.** Articles and documents carry two AI summaries, French and English, and keyword search matches both — so `keyword: "pilgrimage"` returns French-language articles about the hajj even though that word appears nowhere in the source. This is deliberate: it is the only English-language entry point into a francophone corpus. But it means a keyword hit does **not** imply the term is attested in the item itself. For what the sources actually say, read the OCR; for counting how often a word appears *in the press*, AI abstracts are not evidence. Only one summary (the French one) is returned in responses.
114115
- **Accents and case are folded** (server ≥ 0.6.0): `pelerinage`, `Bénin`, `These` all match. Spelling differences still matter.
115116
- **Country filters take exact names** (Benin, Burkina Faso, Côte d'Ivoire, Niger, Togo; Nigeria only in references/index/audiovisual). Partial names ("Burkina") are invalid and return an `{error, valid_values}` response — pick a valid value and retry; never read the error as a finding.
116117
- **Keyword search terms must be French for primary-source subsets** — formulate keyword terms in French for press, publications, documents, and index searches. Academic references are multilingual: search titles/abstracts with French and English concept terms when relevant, while keeping metadata/filter values in French. Semantic embedding queries may be in any language.

.agents/skills/iwac-mcp/references/capabilities-overview.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ Because reports are date-stamped and location-specific, the collection also supp
3636

3737
## Ways into the data (recommend 2-3, matched to the question)
3838

39-
- **Keyword search** (French concept terms for press/publications; French and English title/abstract terms for scholarship) across titles, full OCR text and AI abstracts.
39+
- **Keyword search** (French concept terms for press/publications; French and English title/abstract terms for scholarship) across titles, full OCR text and AI abstracts. Articles and documents carry a French **and** an English AI abstract and both are searched, so an English term can reach a French item through its summary — responses still return only the French one.
4040
- **Curated themes** — 214 subject tags (Hadj, Laïcité, Ramadan, Éducation islamique…) for precise, pre-classified slices.
4141
- **People, organisations, places** — find the canonical name among the 4,700 index records, then pull every article that mentions it.
4242
- **Conceptual (semantic) search** in any language, when keywords would miss paraphrases — "Islamic education reform" also surfaces madrasa-modernisation pieces.

.agents/skills/iwac-mcp/references/research-domains.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ Server matching is **accent- and case-insensitive** (v0.6.0+), so `pelerinage` a
172172
- **Country filters take exact names:** Benin, Burkina Faso, Côte d'Ivoire, Niger, Togo (+ Nigeria for references/index/audiovisual). Partial names ("Burkina") are invalid and return an error listing the valid values.
173173
- **Start broad, then narrow:** begin with a general term (e.g., "madrasa"), then add country or date filters.
174174
- **Check the index first:** use `search_index` to find the canonical form of a person/organization name, then search articles with that exact form.
175-
- **Prefer `subject` over `keyword`** for known thematic categories: `keyword` searches title + OCR + AI abstract; `subject` searches the curated tags (which may use different terminology than the OCR text). Discover tags via `list_subjects`.
175+
- **Prefer `subject` over `keyword`** for known thematic categories: `keyword` searches title + OCR + AI abstracts (French and English); `subject` searches the curated tags (which may use different terminology than the OCR text). Discover tags via `list_subjects`.
176176
- **Islamic publications vs. mainstream press:** `search_publications` covers Islamic community media (Islam Info, An-Nasr Vendredi, …). Most items are entire issues — navigate by series (`list_periodicals`), subject (87% tagged), country and year; keyword also matches tables of contents (17 of 25 series have them, returned as `matching_toc_entries`); use `get_publication_fulltext` keyword excerpts to read inside an issue.
177177
- **State press vs. private press:** pre-1991 articles come almost entirely from state/single-party organs, and some outlets changed names across regimes (Ehuzu → La Nation; Togo-Presse ↔ La Nouvelle Marche; L'Observateur → L'Observateur Paalga) — search all name variants when following one outlet across time (see biases-and-limitations.md §6).
178178
- **Temporal filtering:** articles take `YYYY-MM-DD` or `YYYY`; publications/references take years.

.agents/skills/iwac-mcp/references/tools-by-phase.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Cross-subset search for skill-less clients and quick discovery.
99
- `query` (required): one concept, name, or short phrase. Tokens are AND-ed across each subset's searchable fields; use French concepts for primary-source discovery, and French/English terms for references. A query whose every word is under 2 characters is **refused** (it would otherwise return an empty result indistinguishable from a real absence).
1010
- `limit` (default 20, max 50)
1111
- Returns `results` with namespaced ids (`articles:28576`, `references:11045`, `images:12237`), `title`, `url`, `category`, plus a `ranking` note and `deep_scan`. There is no numeric relevance score; for precise filters use the granular `search_*` tools.
12-
- **Two passes.** Pass 1 matches curated metadata only (titles, subjects, AI abstracts, tables of contents) and answers in ~0.2 s; if that fills fewer than `limit` results, pass 2 also scans the full OCR (~2.5 s) and `deep_scan: true` says so. A common term therefore never pays for the OCR scan, and a rare one still finds everything — but treat `search` as discovery, not a census: for exhaustive counts use `search_*` or `get_temporal_distribution`.
12+
- **Two passes.** Pass 1 matches curated metadata only (titles, subjects, AI abstracts in both languages, tables of contents) and answers in ~0.2 s; if that fills fewer than `limit` results, pass 2 also scans the full OCR (~2.5 s) and `deep_scan: true` says so. A common term therefore never pays for the OCR scan, and a rare one still finds everything — but treat `search` as discovery, not a census: for exhaustive counts use `search_*` or `get_temporal_distribution`.
1313

1414
### fetch
1515
Fetch one item returned by `search`.
@@ -58,7 +58,7 @@ The 25 Islamic periodical/series titles in the publications subset, with issue c
5858

5959
### search_articles
6060
Primary search tool for the 12,287 newspaper articles.
61-
- `keyword` (optional): substring match on **title + OCR + AI abstract** (does NOT search subject/spatial — use the `subject` parameter for curated tags)
61+
- `keyword` (optional): substring match on **title + OCR + AI abstracts, French and English** (does NOT search subject/spatial — use the `subject` parameter for curated tags). An English term can therefore match a French article via its English summary
6262
- `country` (optional): exact name — Benin | Burkina Faso | Côte d'Ivoire | Niger | Togo
6363
- `newspaper` (optional): substring match
6464
- `subject` (optional): substring match on the pipe-separated curated tags

CITATION.cff

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ identifiers:
2626
value: 10.5281/zenodo.21805837
2727
description: "Concept DOI for all versions of the software."
2828
license: MIT
29-
version: 1.5.1
30-
date-released: "2026-08-05"
29+
version: 1.6.0
30+
date-released: "2026-08-07"
3131
keywords:
3232
- Model Context Protocol
3333
- MCP server

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,15 +193,15 @@ Machine-readable metadata lives in [CITATION.cff](CITATION.cff) — GitHub's
193193
**Cite this repository** button (sidebar) renders it as APA or BibTeX with the
194194
current version filled in. In text:
195195

196-
> Madore, F. (2026). *IWAC MCP Server* (Version 1.5.1) [Computer software].
196+
> Madore, F. (2026). *IWAC MCP Server* (Version 1.6.0) [Computer software].
197197
> Zenodo. https://doi.org/10.5281/zenodo.21805837
198198
199199
```bibtex
200200
@software{madore_iwac_mcp_server,
201201
author = {Madore, Frédérick},
202202
title = {{IWAC MCP Server}},
203203
year = {2026},
204-
version = {1.5.1},
204+
version = {1.6.0},
205205
publisher = {Zenodo},
206206
doi = {10.5281/zenodo.21805837},
207207
url = {https://github.com/fmadore/iwac-mcp-server},

mcpb/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"manifest_version": "0.3",
33
"name": "iwac-mcp-server",
44
"display_name": "Islam West Africa Collection (IWAC)",
5-
"version": "1.5.1",
5+
"version": "1.6.0",
66
"description": "Read-only access to the Islam West Africa Collection via Hugging Face datasets.",
77
"long_description": "Search and analyse the Islam West Africa Collection (IWAC): ~12,000 newspaper articles, 1,500 Islamic publications (searchable by keyword, subject, series, and table of contents), 4,700 index entries, plus audiovisual recordings, fieldwork photographs, archival documents, and academic references. Includes AI sentiment analysis (polarity/centrality/subjectivity, scored independently by gemini-3-flash-preview, gpt-5-mini and ministral-14b-2512), per-article AI abstracts, and per-year coverage timelines. All matching is accent- and case-insensitive.\n\n**No API key is required for the core tools** (cross-subset search/fetch, keyword search, filtering, statistics, timelines, item details). Three optional semantic-search tools use Gemini embeddings — over all articles, the publication series with tables of contents, and the photographs (cross-modal: describe what an image shows) — and need a free Google/Gemini API key; these are disabled by default.\n\nOn first use the server downloads ~250 MB of parquet data from Hugging Face into a local cache.",
88
"author": {

mcpb/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mcpb/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "iwac-mcp-server-bundle",
3-
"version": "1.5.1",
3+
"version": "1.6.0",
44
"description": "Node MCP server for the Islam West Africa Collection, packaged as a Claude Desktop extension (.mcpb).",
55
"private": true,
66
"type": "module",

mcpb/scripts/make-fixtures.mjs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,29 @@ const IWAC = "https://islam.zmo.de/s/afrique_ouest/item/";
2222
/** Subsets that carry an `OCR` column, and therefore the per-row public flag. */
2323
const OCR_SUBSETS = ["articles", "publications", "documents", "audiovisual"];
2424

25+
/**
26+
* Subsets carrying the AI summary. Since the summariser went bilingual the
27+
* dataset stores the two `@language` literals as `descriptionAI` (fr) and
28+
* `descriptionAI_en` rather than pipe-joining them, so the fixture must hold
29+
* both — an English-only term has to be findable, which is the whole point of
30+
* tagging the English column `searchable`.
31+
*/
32+
const DESC_AI_SUBSETS = ["articles", "documents", "audiovisual"];
33+
34+
/**
35+
* English summaries, keyed by `o:id`. Deliberately NOT translations of the
36+
* French: each uses vocabulary ("pilgrimage", "fasting") that appears nowhere
37+
* in the French text or the OCR, so a test matching one proves the English
38+
* column is really in the search surface rather than riding on a shared token.
39+
* Audiovisual is absent on purpose — 0/47 filled in the real subset.
40+
*/
41+
const DESC_AI_EN = {
42+
101: "Report on the pilgrimage of Beninese faithful to Mecca.",
43+
102: "The fasting month as lived in the capital's mosques.",
44+
103: "Celebrations marking the end of the fasting month in Abidjan.",
45+
501: "Annual report of a Burkinabè Muslim students' association.",
46+
};
47+
2548
/**
2649
* Subsets the pipeline writes Hijri date columns to
2750
* (post-processing/calculate_hijri_dates.py). `references` is deliberately
@@ -277,6 +300,19 @@ async function main() {
277300
await conn.run(`ALTER TABLE ${table} ADD COLUMN "OCR_is_public" BOOLEAN`);
278301
await conn.run(`UPDATE ${table} SET "OCR_is_public" = length(trim(coalesce("OCR", ''))) > 0`);
279302
}
303+
// The English half of the bilingual AI summary. Empty string, not NULL,
304+
// for rows without one — the real parquet stores empty strings here, and a
305+
// NULL would let a `COUNT()` claim coverage the subset does not have.
306+
if (DESC_AI_SUBSETS.includes(subset)) {
307+
await conn.run(`ALTER TABLE ${table} ADD COLUMN "descriptionAI_en" VARCHAR`);
308+
await conn.run(`UPDATE ${table} SET "descriptionAI_en" = ''`);
309+
for (const [oid, text] of Object.entries(DESC_AI_EN)) {
310+
await conn.run(
311+
`UPDATE ${table} SET "descriptionAI_en" = '${text.replace(/'/g, "''")}'
312+
WHERE "o:id" = '${oid}'`,
313+
);
314+
}
315+
}
280316
// Hijri columns, keyed off pub_date exactly as the pipeline does. Rows whose
281317
// date is not a complete YYYY-MM-DD (year-only, or a `1981-04/1981-06`
282318
// range) are left NULL — an imprecise date has no lunar day, and that

0 commit comments

Comments
 (0)