Skip to main content
Glama
README.md
<div align="center">

# islamqa-org-mcp

**Give Claude, Codex, and any other MCP client access to ~98,000 sourced fatwas.**

An [MCP](https://modelcontextprotocol.io) server for [IslamQA.org](https://islamqa.org),
which aggregates answers from 47 independent darul ifta institutions across the
four Sunni madhahib.

[![CI](https://github.com/RealYusufIsmail/islamqa-org-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/RealYusufIsmail/islamqa-org-mcp/actions/workflows/ci.yml)
[![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
[![MCP](https://img.shields.io/badge/MCP-server-orange.svg)](https://modelcontextprotocol.io)

[Website](https://realyusufismail.github.io/islamqa-org-mcp/) ·
[Install](#install) · [ChatGPT](#chatgpt) · [Tools](#tools) · [How it works](#how-it-works) · [Etiquette](#etiquette-adab)

</div>

---

## Why

Ask a language model a fiqh question and it will answer from a blur of training
data, with no source, no madhhab, and no way to check it. That is the wrong
shape for this kind of question.

This server changes the default. Your assistant searches a real archive of
scholarly answers, and quotes one with the mufti, the madhhab, the institution,
and a URL you can open. When the schools differ, you see that they differ
instead of getting one position presented as *the* Islamic ruling.

```
You:  Can I wipe over ordinary cotton socks in wudu?

Claude: [searches IslamQA.org]

        The Hanafi position (Askimam.org, Mufti Ebrahim Desai) is that masah
        is only valid on socks with the characteristics of leather khuffayn —
        thick enough that water does not seep through, able to stand upright
        unaided, and durable over three to four miles of walking. Ordinary
        cotton socks do not qualify.
        https://islamqa.org/hanafi/askimam/127638/...

        Bin Bayyah (Maliki) notes the Hanbali school permits wiping over cloth
        socks, while the Maliki position restricts it to leather.
        https://islamqa.org/maliki/binbayyah/29891/...
```

## Install

No clone, no manual setup. Pick whichever runner you already have.

**With [uv](https://docs.astral.sh/uv/)** (recommended — it's what actually runs
the server):

```bash
uvx --from git+https://github.com/RealYusufIsmail/islamqa-org-mcp islamqa-mcp
```

**With npx**, if Node is what you reach for:

```bash
npx -y islamqa-mcp
```

The npm package is a thin wrapper — it hands off to `uv`, which fetches and
runs the Python server in an isolated cached environment. You still need `uv`
installed; the wrapper tells you how if it's missing.

<details>
<summary>Installing uv</summary>

```bash
curl -LsSf https://astral.sh/uv/install.sh | sh    # macOS / Linux
brew install uv                                    # Homebrew
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"   # Windows
```
</details>

### Claude Code

```bash
claude mcp add islamqa -- uvx --from git+https://github.com/RealYusufIsmail/islamqa-org-mcp islamqa-mcp
```

### Claude Desktop

Add to `claude_desktop_config.json` — on macOS,
`~/Library/Application Support/Claude/claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "islamqa": {
      "command": "uvx",
      "args": [
        "--from",
        "git+https://github.com/RealYusufIsmail/islamqa-org-mcp",
        "islamqa-mcp"
      ]
    }
  }
}
```

### Codex

Add to `~/.codex/config.toml`:

```toml
[mcp_servers.islamqa]
command = "uvx"
args = ["--from", "git+https://github.com/RealYusufIsmail/islamqa-org-mcp", "islamqa-mcp"]
```

Restart the client. The first search builds a local index (about a minute, ~22
requests); everything after that is instant.

<details>
<summary>From a local clone instead</summary>

```bash
git clone https://github.com/RealYusufIsmail/islamqa-org-mcp.git
cd islamqa-org-mcp && uv sync
```

Then point the client at it with
`uv --directory /absolute/path/to/islamqa-org-mcp run islamqa-mcp`.
</details>

### ChatGPT

ChatGPT is different from the clients above: it can't launch a local process,
so it needs the server **hosted at a public HTTPS URL**. Two things follow.

**1. Run it over HTTP instead of stdio.**

```bash
islamqa-mcp --transport streamable-http --host 0.0.0.0 --port 8000
```

The endpoint ChatGPT wants is then `https://your-host/mcp`.

**2. It exposes `search` and `fetch` in this mode.** ChatGPT rejects any
connector that doesn't have tools by exactly those names unless you've turned on
Developer Mode, and Deep Research only ever calls those two. So when running
over HTTP the server adds them as adapters over `search_fatwas` / `get_fatwa`,
in [OpenAI's required shape](https://developers.openai.com/api/docs/mcp) —
`search` returns `{id, title, url}` per result, `fetch` returns
`{id, title, text, url, metadata}`. Every result carries a real islamqa.org
URL, so ChatGPT renders proper citations.

They are **not** registered for stdio, deliberately: Claude and Codex get the
richer `search_fatwas`, and adding a weaker alias would just invite them to use
the worse one.

#### Trying it without deploying

Run it locally and point a tunnel at it — this is what the **Tunnel** toggle in
ChatGPT's connector dialog is for:

```bash
islamqa-mcp --transport streamable-http --port 8000
cloudflared tunnel --url http://localhost:8000     # or: ngrok http 8000
```

Then in ChatGPT: **Settings → Apps → Advanced → Developer mode**, add a
connector pointing at `https://<your-tunnel>/mcp`, authentication **None**.

#### Hosting it properly

A [Dockerfile](Dockerfile) is included and works on Render, Railway, Fly.io or
Cloud Run:

```bash
docker build -t islamqa-mcp .
docker run -p 8000:8000 islamqa-mcp
```

It builds the URL index **at image build time**, which matters: the index costs
~22 requests to islamqa.org, and baking it into the image means that happens
once per release rather than on every cold start of every replica. Without
that, an autoscaling free tier would re-crawl the sitemap all day. It also makes
the first user search instant.

> **Before you expose it publicly:** a connector with no authentication is open
> to anyone who finds the URL, and every request they make is a request against
> islamqa.org under your server's name. Put it behind OAuth or an auth proxy if
> it isn't just for you, and keep the rate limit where it is.

## The skill

An MCP server gives the model tools. It doesn't tell it how to use them well —
and for fiqh, *how* matters more than *what*.

[`skills/islamqa-fatwa`](skills/islamqa-fatwa/SKILL.md) is a
[Claude skill](https://docs.claude.com/en/docs/agents-and-tools/agent-skills/overview)
that ships alongside the server. It makes the assistant:

- **Ask which madhhab you follow** before answering — or present all four
  positions side by side if you don't follow one
- **Never issue a ruling it didn't retrieve.** No invented hadith, no
  half-remembered "the Hanafi view is…", no fabricated Arabic citations. If the
  archive has nothing, it says so instead of filling the gap
- **Use the tradition's actual categories** rather than flattening everything to
  halal/haram — including the Hanafi seven-fold scheme, so *makrūh taḥrīmī*
  isn't quietly downgraded to "disliked" or upgraded to "haram"
- **Say when coverage is thin.** The archive is heavily Hanafi; if there's no
  Maliki answer, it reports that rather than inferring one
- **Refer on** for divorce, inheritance, custody and other matters that need a
  person rather than a search index

Install it globally:

```bash
git clone https://github.com/RealYusufIsmail/islamqa-org-mcp.git /tmp/islamqa-mcp
cp -r /tmp/islamqa-mcp/skills/islamqa-fatwa ~/.claude/skills/
```

It activates on its own when a question turns out to be a ruling question.

## Tools

| Tool | What it does |
|---|---|
| `search_fatwas` | Full-text search across the archive. Filter by `madhhab` or `source`. |
| `get_fatwa` | One answer in full: question, answer, Arabic citations, issuing institution. |
| `list_sources` | All 47 darul ifta sites with per-site answer counts. |
| `browse_fatwas` | Most recent answers, optionally filtered. |
| `index_status` | Index size, cache size, last rebuild. |
| `rebuild_index` | Refresh the index from the sitemap. Rarely needed. |

Over HTTP, `search` and `fetch` are added for ChatGPT compatibility — see
[ChatGPT](#chatgpt).

Every answer is returned with its attribution and a note reminding the model to
cite the URL and to treat the ruling as one mufti's position, not a universal one.

## Command line

The same operations without an MCP client:

```bash
uv run islamqa search "wiping over socks" --madhhab hanafi
uv run islamqa get https://islamqa.org/hanafi/askimam/127638/can-i-wipe-make-masah-over-the-new-socks/
uv run islamqa sources
uv run islamqa status
```

## How it works

IslamQA.org runs WordPress with the REST API disabled, so there is no JSON
endpoint to call. What it does publish is a complete sitemap and
server-rendered pages — the same public pages any reader or search engine
crawler sees.

The server works in two layers:

**The index.** One pass over the sitemap (~22 requests) records every answer
URL along with its ID, madhhab, issuing source and a title derived from the
slug. That is ~98,000 answers for a few megabytes and about a minute, and it
means search is local and instant from then on.

**The cache.** Reading an answer fetches and parses that one page, then stores
it. Search runs on an SQLite FTS5 index over both layers, so an answer is
findable by title immediately and by its full text once anyone has read it —
the archive gets more searchable the more you use it.

```
sitemap ──► index (~98k URLs + titles) ──┐
                                         ├──► FTS5 (BM25) ──► search_fatwas
answer page ──► parse ──► cache (full) ──┘                    get_fatwa
```

Because answers were imported from 47 different sites over many years, pages
come in three template shapes, and all three are handled: the modern
`.ai-question` / `.ai-answer-content` wrappers; bare paragraphs with explicit
`Question:` / `Answer:` labels; and bare paragraphs with no labels at all. Stray
`Q:` prefixes and trailing "Original Source Link" text are stripped, and Arabic
endnotes citing classical texts are split into a separate `citations` field, so
an answer reads cleanly without losing the evidence behind it.

### Does islamqa.org have an API?

**No public JSON API.** The site runs WordPress, but the REST API is switched
off site-wide. Probed August 2026:

| Endpoint | Result |
|---|---|
| `/wp-json/` | `401` — `{"code":"rest_disabled"}` |
| `/wp-json/wp/v2/posts` | `401` — `rest_disabled` |
| `/wp-json/wp/v2/search` | `401` — `rest_disabled` |
| `/wp-json/elasticpress/v1/search` | `401` — `rest_disabled` |
| `/graphql`, `/api/` | `404` |

The on-site search box is rendered client-side by ElasticPress, so its results
aren't in the HTML either — which is why this server builds its own index
rather than proxying site search.

**What is machine-readable and open:**

| Endpoint | Format | Notes |
|---|---|---|
| `/sitemap_index.xml` | XML | 22 child sitemaps |
| `/sitemap-posts.xml?page=N` | XML | 5,000 answer URLs per page, with `lastmod` |
| `/feed/` | RSS 2.0 | Latest answers site-wide |
| `/category/{madhhab}/{source}/feed/` | RSS 2.0 | Latest per institution |
| Answer pages | HTML + JSON-LD | `NewsArticle` schema carries publish/modify dates |
| `/robots.txt` | — | Served empty: nothing disallowed |

This server uses the sitemap and the answer pages. The RSS feeds are a
lightweight option if you only want new answers and don't need search — they
need no index and no scraping:

```bash
curl -s https://islamqa.org/hanafi/askimam/feed/
```

Answer URLs are structured, so you can address any answer directly:

```
https://islamqa.org/{madhhab}/{source}/{post_id}/{slug}/
                     hanafi   askimam  127638   can-i-wipe-make-masah-over-the-new-socks
```

If islamqa.org ever enables its REST API, this server should switch to it —
[open an issue](https://github.com/RealYusufIsmail/islamqa-org-mcp/issues) if
you notice it come back.

### Configuration

All optional, set as environment variables:

| Variable | Default | Purpose |
|---|---|---|
| `ISLAMQA_DATA_DIR` | `~/.cache/islamqa-mcp` | Where the index and cache live |
| `ISLAMQA_REQUEST_DELAY` | `1.0` | Seconds between requests |
| `ISLAMQA_CACHE_TTL_DAYS` | `90` | How long a cached answer stays fresh |
| `ISLAMQA_INDEX_TTL_DAYS` | `30` | When to rebuild the index |
| `ISLAMQA_USER_AGENT` | identifies this tool | Sent with every request |

## Etiquette (adab)

This reads a free service run on donations, so it is built to be a good guest:

- **One request per second**, serialised globally — concurrent tool calls
  cannot fan out into a burst.
- **Caches aggressively.** Fatwas are effectively immutable once published, so
  a page is fetched once and reused for 90 days.
- **Fetches only what is asked for.** The bulk pass reads the sitemap, not
  100,000 answer pages. There is no crawler here.
- **Identifies itself** with a real User-Agent pointing back to this repo.
- **Locked to one host.** Every URL is checked against an allowlist before a
  socket opens, so no argument from a model can turn this into an open proxy.
- **Attributes everything.** The issuing institution, the mufti where named,
  and a link to the darul ifta's own copy travel with every answer.

If you maintain islamqa.org and want anything changed here — the rate limit,
the User-Agent, or the tool's existence — please
[open an issue](https://github.com/RealYusufIsmail/islamqa-org-mcp/issues) and
it will be addressed.

> **On using this.** These are answers from qualified muftis, but a fatwa is
> given to a particular person in a particular context. A search result is not
> a ruling on your situation, and a language model relaying one is not a
> scholar. For anything consequential, ask a qualified person directly.

## Development

```bash
uv sync --extra dev
uv run pytest          # 81 tests, no network — runs against saved fixtures
uv run ruff check .
uv run mypy src/islamqa_mcp
```

The parser tests run against trimmed copies of real pages. If islamqa.org
changes its template they fail loudly, which is deliberate: a silent parse
regression would quietly feed empty answers to the model.

## Licence

MIT — see [LICENSE](LICENSE).

The licence covers this software only. The fatwas belong to the scholars and
institutions that issued them. This tool reads public pages and links back; it
does not redistribute the archive.

TDQS

A4.4/5.0

Scored across 6 tools

Disambiguation5/5

Each tool serves a unique, clearly defined purpose: search for fatwas, browse recent ones, fetch a specific fatwa, list sources, check index status, and rebuild the index. There is no overlap or ambiguity between them.

Naming Consistency5/5

All six tools follow the verb_noun pattern in snake_case (search_fatwas, list_sources, get_fatwa, browse_fatwas, index_status, rebuild_index), with consistent verb choices that clearly indicate the action taken.

Tool Count5/5

Six tools is well within the ideal range for a domain-specific server. Each tool is necessary and none are redundant; the count is neither too sparse nor too heavy for the server's stated purpose of accessing IslamQA fatwas.

Completeness5/5

The tool surface covers the full workflow: searching, browsing, retrieving individual fatwas, understanding available sources, and maintaining the local index. There are no obvious missing operations for a read-only scholarly answer API.

Maintenance

ActivityMaintained
ResponsivenessNo issues