Larger features — remote transport, auth, zero-config semantic search, skill portability — are tracked as GitHub issues. This file covers smaller items and project ops. Completed work lives in the git history (Track 1 plumbing shipped in v0.5.0; the data-alignment + token audit shipped in v0.6.0).
-
MCP App visualizations — Phases 0–3 of
docs/mcp-apps-roadmap.mdare implemented. One chart became twelve on a single sharedui://iwac/charts.html, and the shipped UI resource is smaller than the one chart it replaced (390.1 → 251.4 kb) because stubbing zod's non-English locales paid for the whole suite;server/index.jsfell 1665.4 → 1191.3 kb with it. Seven new tools (get_topic_distribution,get_field_distribution,get_cooccurrence,get_lexical_metrics,get_place_distribution,get_semantic_map,get_similar_items) plusmodelonget_sentiment_distribution. §7 of that document lists what is still open (PNG export, a wordcloud primitive, region drill-down) and §8 records where the plan turned out to be wrong. Shipped in v0.13.0. -
Publish to the official MCP Registry — automated since v0.9.0: the tag workflow generates
server.json(mcpb/scripts/make-server-json.mjs) and publishesio.github.fmadore/iwac-mcp-server(2.mcpbpackages + theislam.zmo.de/mcpremote) viamcp-publisherGitHub OIDC. Versions are immutable — fixing an entry means bumping and re-tagging. The Anthropic directory below is a separate, manual submission. -
Submit to the Anthropic extension directory Fill out the interest form: https://docs.google.com/forms/d/e/1FAIpQLScHtjkiCNjpqnWtFLIQStChXlvVcvX8NPXkMfjtYPDPymgang/viewform Prereqs: public GitHub repo ✓, MIT licence ✓, Node.js ✓, valid
manifest.jsonwithauthorpointing at the GitHub profile ✓. See also Anthropic's Software Directory Policy: https://support.claude.com/en/articles/13145358-anthropic-software-directory-policy -
Code-sign the
.mcpbwith a trusted code-signing cert (DigiCert / SSL.com, ~$100/yr) so Claude Desktop stops showing the generic "grants access to everything" warning. Self-signed certs don't help — Claude Desktop only trusts real CAs. Command:mcpb sign iwac-mcp-server.mcpb --cert prod.pem --key prod.key -
Zero-config semantic search with a free local model — see #5. Requires re-embedding the HF corpus with the chosen model (corpus + query must use the same model). Candidates:
Qwen3-Embedding-0.6B/4B,BGE-M3,EmbeddingGemma-300M, or the oldermultilingual-e5-small/paraphrase-multilingual-MiniLM-L12-v2. -
Remote transport / hosted deployments — shipped.
--httpselects the Streamable-HTTP transport inmain()(mcpb/src/index.ts), built oncreateMcpHandlerso it answers both protocol eras; a bearer token gates every/mcprequest (IWAC_MCP_BEARER_TOKEN, or a Docker secret located viaIWAC_MCP_TOKEN_FILE). HTTP mode refuses to start without a token rather than warning and running open — deliberately stricter than #3 asked for, because the endpoint is public. Live athttps://islam.zmo.de/mcp/. #1 is closed as delivered; #2 (Streamable-HTTP) was deleted from the tracker after it landed, which is why the "Depends on #2" line in #3 now dangles. #3 is now closed as delivered.- Upstream request limit confirmed on the IWAC-docker host. Rate
limiting was delegated to the front proxy by design (see the header comment
in
mcpb/src/http.ts), so nothing in this repo proves it exists — measured directly instead (2026-07-28): a burst of ~24 back-to-back requests succeeds, then every further request returns HTTP 429 until the bucket refills (~6 req/s sustained). Note the 429 body is raw proxy HTML, not a JSON-RPC error, so an MCP client surfaces it as an opaque transport failure — any probe or test script against the live endpoint must pace itself and retry on 429, or its results are silently polluted.
- Upstream request limit confirmed on the IWAC-docker host. Rate
limiting was delegated to the front proxy by design (see the header comment
in
-
Add
screenshots/showing a research query in Claude Desktop — the directory listing surfaces these. -
Drop the
@hono/node-serveroverride once the SDK catches up —mcpb/package.jsonforces@hono/node-serverto^2.0.12viaoverridesbecause the entire 1.x line carries GHSA-frvp-7c67-39w9 (Windowsserve-staticpath traversal) with no backport, while@modelcontextprotocol/node@2.0.0still declares^1.19.9. Running a transitive dep a major above what upstream asks for is safe here — the SDK imports exactly one symbol,getRequestListener, which v2 still exports, v2 wants Node ≥20 against this project's ≥24, and it peers onhono ^4— and the advisory was never reachable anyway (serveStaticappears nowhere in the built bundle;src/http.tsdrives Node's ownhttp.createServerrather than hono'sserve()). Once@modelcontextprotocol/nodewidens its own range, remove the override rather than bumping it, so the resolved version goes back to being upstream's problem. -
Migrate to MCP TypeScript SDK v2 / protocol 2026-07-28 — done 2026-07-29.
@modelcontextprotocol/server2.0.0 went stable 2026-07-27, four weeks earlier than the ~late-Aug estimate this entry carried; v1sdktopped out at 1.30.0 and never implements 2026-07-28.The codemod (
npx @modelcontextprotocol/codemod@latest v1-to-v2 .) did the package split and import rewrites, but it is a mechanical tool and left the server in the 2025 era: it renamesStreamableHTTPServerTransport→NodeStreamableHTTPServerTransportand keepsserver.connect(new StdioServerTransport()), and per the SDK's era matrix those are precisely the legacy-era entry points. Speaking 2026-07-28 required hand-rewriting both entries toserveStdio()/createMcpHandler(), which own era negotiation. Both default to serving 2025 clients too, so the server is dual-era.Also done by hand: wrapping every
inputSchema/outputSchema/argsSchemaraw shape inz.object()(the codemod only wraps shapes it converts from.tool(), and this server already usedregisterTool; raw shapes are converted by the SDK's bundled zod, which drops.describe()text), andcacheHintsfor the newCacheableResultfields.The flagged gap is closed:
instructionssurvives the loss of theinitializehandshake — the SDK carries it onserver/discover, byte-identical on both eras (asserted intest/fixture-server.test.mjs), and prompts still list. The remaining 2026-07-28 changes still need no action here, with one correction to the 2026-07-23 audit: it recorded "no resources", which stopped being true in v0.12.0 when theui://chart resource landed. The error-code change is emitted by the SDK either way, so nothing to write. -
Token budget in CI — the server's cost to a model is now measured rather than assumed.
npm run test:tokensgates two things on every PR: the always-on footprint (34 tool definitions + instructions = 14 091 tokens, checked against the committedtest/token-baseline.jsonat 5% drift and a 16k hard ceiling) and the worst-case response of every tool called at its maximum arguments against inflated fixtures (scripts/make-stress-fixtures.mjs), ceiling 20k — under the 25k cap Claude Code applies to a tool result. The same ceiling runs weekly insmoke-test.mjs, which is the only place the aggregate tools' real cardinality shows up. The gate earned itself on the first run:search_articles(limit=100, with_description=true)was 27 533 tokens, so a caller asking for 100 abstracts received nothing at all;with_descriptionnow caps the page at 25 with alimit_warningthat says why.Still open, in rough order of payoff:
outputSchemais 2 402 tokens, 19% of the footprint, and is attached to only 13 of 34 tools. It buysstructuredContentvalidation (asserted intest/fixture-server.test.mjs); worth deciding per tool rather than by habit, and worth re-checking once clients report whether they read it.- The 9 aggregate tools are 5 600 tokens, 44% of the footprint for tools a given session may never call. If MCP tool-search / progressive disclosure becomes reliable across clients, they are the obvious candidates to defer.
search_index(16 770) andsearch_references(15 867) sit closest to the response ceiling atlimit=100. Neither is wrong — the caller asked — but they are the two that a wider row would push over.
Governing rule: all AI enrichment is precomputed offline as HF columns and served as cheap column lookups. The MCP server never generates at request time. Whole-issue work is map-reduce (chunk → per-chunk extract → reduce) in monthly batches, so no single call is large. Verified fill rates that motivate this list (June 2026): references abstract 51% / subject 27%; publications TOC 325/1,501 (complete for 17/25 series; Islam Info, An-Nasr Vendredi and Islam Hebdo still have none), subject 87%, OCR 97% (median ~16k, max ~278k tokens/issue); audiovisual descriptionAI 0/45.
-
Precompute
hijri_year/hijri_month/hijri_day—post-processing/calculate_hijri_dates.pyin the pipeline repo convertspub_datewithhijridate(Umm al-Qura), the same converter as IwacVisualizations'generate_on_this_day.py, so the website's buckets and the MCP server's counts cannot drift. Precomputing rather than converting per consumer is load-bearing: measured on the livearticlessubset, ICU/Intldisagrees withhijridateon 75% of pre-2000 dates (2,365 of 3,152) and on none from 2000 on. Only 0.86% of articles change lunar month, so month-level aggregates are robust either way — day-level labels are not. Written for articles, publications, documents, audiovisual and images; deliberately not references (an academic imprint date has no meaningful lunar reading). Columns are allowlisted iniwac_common/public_columns.json. Server side:calendar=hijri+granularity=lunar_monthonget_temporal_distribution(with thelunarMCP App chart),hijri_month/hijri_yearfilters onsearch_articles/search_publications, and ahijri_datefield on article and publication rows. Every path degrades to a self-correctable error on a dataset revision that predates the columns. Shipped in v1.3.0.- Run the pipeline script and re-publish. The code is in place but
the columns do not exist on the Hub yet — until
calculate_hijri_dates.py --config <subset>runs against the private repo for each of the five subsets andpublish_public.pyprojects it, the live server answers the lunar tools with "no Hijri date columns in this dataset revision".
- Run the pipeline script and re-publish. The code is in place but
the columns do not exist on the Hub yet — until
- Auto-tag
subjectfrom title+abstract (only 27% tagged today), aligned to the indexSujetscontrolled vocabulary. Thesearch_references(subject=…)filter already exists — this raises its coverage from 27% toward the whole subset. - Backfill missing
abstract(51% present) from Crossref (DOI — 31% have one) and OpenAlex (title match). Fetch real abstracts; do not generate them from a bare title. - Compute
embedding_abstract(over title+abstract, same Gemini model / 768-dim as articles) → enables a newsemantic_search_referencestool. Tiny corpus (864 rows), large payoff: connects secondary scholarship to the same semantic surface as the articles.
- Extract
tableOfContentsfrom OCR — mostly done (June 2026): 325/1,501 issues now have a TOC +embedding_tableOfContents, covering 17 of the 25 series completely (avg TOC ~6.4k chars).semantic_search_publicationsand thematching_toc_entriespath now work for those series. Remaining: the three largest series — Islam Info (695 issues), An-Nasr Vendredi (318), Islam Hebdo (122) — plus 5 small series (~41 issues). - Per-issue
descriptionAI(2–4 sentences: themes, notable pieces). Publications have no summary surface at all today. - (stretch) Article-level publications index — explode extracted TOCs into one row per article (issue_id, page, title, author) as a small new table/subset, so users can search within periodicals without loading a full (up to ~278k-token) OCR blob.
- Populate
descriptionAI— the column exists but is empty for all 47 rows (length(trim(...)) = 0; a bareCOUNT()claims 47/47 because the parquet stores empty strings, not NULLs). Partly mitigated since July 2026: the subset gained anOCRtranscription column, which the server now serves as the item body (transcriptiononget_audiovisual,textonfetch) and searches — but only 4 of 47 rows have one, so AI descriptions remain the browsable surface this subset needs. - Transcribe the remaining 43 recordings — Hausa/Arabic audio; the 4 existing transcriptions prove the column and the server path work end to end.
- Populate
description— 2 of 30 photographs have a caption. Discovery currently leans on title/subject/place plus the multimodalembedding_image(semantic_search_images), which works without captions but cannot be quoted in a write-up.
semantic_search_references— new tool, needsembedding_abstract.semantic_search_publications— ✅ now useful for the 17 TOC-covered series; corpus-wide once Islam Info / An-Nasr Vendredi / Islam Hebdo TOCs land.search_publications— returns AI summaries oncedescriptionAIis populated (add adescriptionAIfield toSUBSET_FIELDS.publicationsinsrc/tools/_shared.ts, taggedsearchableand in thesummaryview).
-
Persona-based research framing — branch research on lens (Islamic scholar / historian / media studies), informed by local scholars & imams, as a framing axis alongside the Brief/Extended depth choice. See #6.
-
Enrich
research-domains.mdwith actual IWAC frequency data Uselist_subjects(214 subjects),list_persons, andlist_locationsto replace manually curated search terms with data-grounded suggestions including actual frequencies. -
Publish the
iwac-mcpresearch skill to the Claude Skills repository (it already ships as a standaloneiwac-mcp-skill.ziprelease asset on every tag — this item is about the Skills library specifically). Source of truth is.agents/skills/iwac-mcp/. Related but distinct from #4 (adapting the skill for non-Claude models). NB: the copy in the claude.ai Skills library (anthropic-skills:iwac-mcp) predates v0.5 — replace it with.agents/skills/iwac-mcp/or delete it.