Skip to main content
Glama
README.md
# arabic-dict-mcp

[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/downloads/)
[![MCP](https://img.shields.io/badge/protocol-MCP-black.svg)](https://modelcontextprotocol.io)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](#license)

> An [MCP](https://modelcontextprotocol.io) server that gives AI agents grounded Arabic dictionary lookup — so replies about words, roots, and meanings come from real dictionary data instead of the model's parametric memory.

Built for Arabic learners who are tired of AI assistants confidently inventing meanings, roots, and derivations.

---

## Why

Ask any LLM "what's the root of _مكتوب_ and its siblings?" and you'll get an answer that _sounds_ right — sometimes it is, often it isn't. This server exposes three tools the model can call to look words up in real dictionaries. When wired into Claude Code, Claude Desktop, or any MCP-compatible client, the model grounds its answer in the returned data instead of guessing.

## Tools

| Tool | What it does |
|---|---|
| `lookup_root(root)` | All dictionary entries under a root. Accepts any form: `كتب`, `ك-ت-ب`, `كَتَبَ`. |
| `lookup_word(word)` | Matches an inflected surface form or lemma. Returns each match's root so the agent can pivot to `lookup_root` for siblings. |
| `search_meaning(query, lang="en"\|"ar")` | Full-text search over meanings for "what's the root for _X_?" style questions. |

**Diacritics.** Input is tashkeel-insensitive — `كَاتِبٌ`, `كاتب`, and `كَاتِب` all match. Output preserves full vocalization from the source (including final case marks), because the vowelled form is what disambiguates the lemma.

## Data sources

Downloaded at build time by `scripts/build_dataset.py`; not vendored in the repo.

| Source | Coverage | License |
|---|---|---|
| [linuxscout/arramooz](https://github.com/linuxscout/arramooz) | ~40k MSA verb & noun entries — root, POS, wazn, forms, Arabic definitions | GPL |
| [aliozdenisik/quran-arabic-roots-lane-lexicon](https://github.com/aliozdenisik/quran-arabic-roots-lane-lexicon) | 1,651 Quranic roots with English (Lane) glosses | GPLv3 |

Total: **5,610 roots · 42,351 entries** in a unified SQLite index with FTS5.

### Optional: Hans Wehr (personal use)

The classic Hans Wehr Arabic–English Dictionary has full entries with principal parts (past + present + maṣdar) inline — e.g. `kataba u (katb, كتبة kitba, كتابة kitāba) to write, …`. Passing `--hanswehr` to the builder adds ~25k Hans Wehr entries.

```bash
.venv/bin/python scripts/build_dataset.py --hanswehr
```

The underlying dictionary is **copyrighted** (Otto Harrassowitz / Spoken Language Services). The transcription downloaded lives in [GibreelAbdullah/HansWehrDictionary](https://github.com/GibreelAbdullah/HansWehrDictionary) and ships **without a license file**. Use of `--hanswehr` is at your own discretion for **personal use only** — this repo does not ship the data, does not commit it, and does not encourage redistributing the resulting `data/arabic.db`.

If you already have a copy locally, point the flag at it:

```bash
.venv/bin/python scripts/build_dataset.py --hanswehr /path/to/hanswehr.sqlite
```

## Install

```bash
git clone https://github.com/arnizamani/arabic-dict-mcp.git
cd arabic-dict-mcp
uv venv .venv
uv pip install --python .venv/bin/python -e .
.venv/bin/python scripts/build_dataset.py    # ~40 MB download → data/arabic.db
```

## Use — Claude Code / Desktop (stdio)

```bash
claude mcp add arabic-dict \
  -- /absolute/path/arabic-dict-mcp/.venv/bin/python -m arabic_dict_mcp.main
```

Or add to `~/.claude/mcp.json` / `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "arabic-dict": {
      "command": "/absolute/path/arabic-dict-mcp/.venv/bin/python",
      "args": ["-m", "arabic_dict_mcp.main"]
    }
  }
}
```

Then ask Claude something like _"What's the root of مكتوب and what other words come from it?"_ — the model will call `lookup_word` → `lookup_root` and reply with grounded data.

### From a Windows client, server in WSL

Point `command` at `wsl.exe` and give the interpreter its Linux path:

```json
{
  "mcpServers": {
    "arabic-dict": {
      "command": "wsl.exe",
      "args": [
        "-e",
        "/mnt/c/path/to/arabic-dict-mcp/.venv/bin/python",
        "-m",
        "arabic_dict_mcp.main"
      ]
    }
  }
}
```

If the client reports `ClosedResourceError`, the pipe to the server died rather than a tool failing — usually the WSL VM cold-starting past the client's startup timeout. Run any `wsl.exe echo hi` first to warm the VM, then retry.

## Debug — MCP Inspector

The server is a silent stdio daemon: it prints nothing on startup and waits for newline-delimited JSON-RPC on stdin. To poke at it interactively, use the [MCP Inspector](https://github.com/modelcontextprotocol/inspector) rather than running it bare.

```bash
npx @modelcontextprotocol/inspector .venv/bin/python -m arabic_dict_mcp.main
```

Open the URL it prints — it carries a pre-filled session token, and the bare `http://localhost:6274` will fail auth. Then **Connect** → **Tools** → **List Tools**.

Against a git worktree (where `data/` is gitignored and absent), point at the source and a real database:

```bash
PYTHONPATH=$PWD/src \
ARABIC_MCP_DB=/path/to/arabic-dict-mcp/data/arabic.db \
npx @modelcontextprotocol/inspector \
  /path/to/arabic-dict-mcp/.venv/bin/python -m arabic_dict_mcp.main
```

From Windows, mirror whatever the client uses so the same `wsl.exe` hop is exercised:

```powershell
npx @modelcontextprotocol/inspector wsl.exe -e /mnt/c/path/to/arabic-dict-mcp/.venv/bin/python -m arabic_dict_mcp.main
```

For HTTP mode, start the server yourself, then launch a bare `npx @modelcontextprotocol/inspector` and connect to `http://127.0.0.1:8000/mcp` with transport **Streamable HTTP**.

### Driving stdio by hand

No inspector needed — pipe the handshake in, but keep stdin open. On EOF the server shuts down, so a plain heredoc answers `initialize` and then exits before later replies arrive; the trailing `sleep` is what makes them show up.

```bash
{ printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"t","version":"1"}}}' \
  '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
  '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"lookup_root","arguments":{"root":"كتب"}}}'
  sleep 5
} | .venv/bin/python -m arabic_dict_mcp.main
```

Order matters: `initialize`, then the `initialized` notification, then real calls. Note that stdout is protocol-only — any stray `print()` in server code corrupts the framing and the client drops the connection, so logs must go to stderr.

## Use — HTTP (remote)

```bash
.venv/bin/python -m arabic_dict_mcp.main --http --port 8000
```

By default, HTTP is unauthenticated — fine for `localhost` testing. For anything on the public network, set a bearer token first:

```bash
export MCP_AUTH_TOKEN=$(openssl rand -hex 24)
.venv/bin/python -m arabic_dict_mcp.main --http --host 0.0.0.0 --port 8000
```

When `MCP_AUTH_TOKEN` is set, clients must send `Authorization: Bearer $MCP_AUTH_TOKEN`.

## Test

```bash
PYTHONPATH=src .venv/bin/python -m pytest tests/
```

## Layout

```
src/arabic_dict_mcp/
  normalize.py   diacritic + letter-variant normalization (pyarabic)
  db.py          SQLite queries: lookup_root / lookup_word / search_meaning
  server.py      MCPServer with the three tools registered
  main.py        --http | stdio entrypoint
scripts/
  build_dataset.py   downloads sources → unified data/arabic.db with FTS5
tests/
  test_normalize.py
```

## Known limits

- **English meanings** only cover the ~1,651 Quranic roots. Other MSA vocabulary returns terse Arabic glosses from arramooz only.
- **arramooz definitions** are short dictionary glosses, not full lexicon entries. A future version could bring in the full Lane's Lexicon (Perseus XML) for classical coverage.
- **License asymmetry**: this repository's code is MIT, but the downloaded dictionary data is GPL/GPLv3. Redistributing the built `data/arabic.db` triggers GPL; the code alone doesn't.

## Contributing

Issues and pull requests are welcome. Ideas that would help:

- Full Lane's Lexicon ingestion beyond Quranic roots
- Additional MSA/dialect dictionaries with permissive licenses
- Root-similarity or fuzzy matching for typo tolerance

## License

MIT — see [LICENSE](LICENSE) for details. Note the data-license asymmetry above.

TDQS

A4.7/5.0

Scored across 3 tools

Disambiguation5/5

Each tool targets a distinct access path: root lookup, surface word lookup, and meaning search. The descriptions explicitly differentiate when to use each, so there is no ambiguity.

Naming Consistency5/5

All tools follow a clear verb_noun pattern: lookup_root, lookup_word, search_meaning. The pattern is consistent and predictable.

Tool Count5/5

Three tools is well-scoped for a dictionary server. Each tool covers a necessary and non-overlapping function, and the count is within the typical 3-15 range.

Completeness5/5

The tool set covers the full spectrum of dictionary queries: root lookup, word lookup, and semantic search. This provides comprehensive coverage for the domain with no obvious dead ends.

Maintenance

ActivitySlowing
ResponsivenessNo issues