{
  "markdown": "# mcp-docs-ask\n\n[![CI](https://github.com/alyiox/mcp-docs-ask/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/alyiox/mcp-docs-ask/actions/workflows/ci.yml)\n[![PyPI](https://img.shields.io/pypi/v/mcp-docs-ask.svg)](https://pypi.org/project/mcp-docs-ask/)\n[![Python 3.13+](https://img.shields.io/badge/python-3.13%2B-blue.svg)](https://www.python.org/downloads/)\n[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n\n<!-- mcp-name: io.github.alyiox/mcp-docs-ask -->\n\nLocal RAG MCP for documentation. Point `source` at any markdown repository\n(local path or git URL).\n\nThe server does **retrieval only** (no answer LLM). `ask_docs` returns grounded\npassages and citations; the MCP host (Cursor / Claude) synthesizes the answer.\n\n## Features\n\n- `ask_docs` retrieval with configurable path-based layer filters\n- `list_docs` discovery for configured docs collections and layer filters\n- `reindex` rebuilds the local vector index; for git URL sources it also fetches updates\n\n## Requirements\n\n- Python 3.13+\n- [`uv`](https://docs.astral.sh/uv/)\n- `git` on PATH (only if `source` is a git URL)\n- Git credentials on the machine when `source` is a **private** git URL\n  (`gh auth login`, HTTPS credential helper, or SSH). No tokens in config.\n- First run downloads the embedding model weights once (sentence-transformers)\n\n## Quick start\n\n```bash\ngit clone git@github.com:alyiox/mcp-docs-ask.git\ncd mcp-docs-ask\nuv sync\nmkdir -p ~/.config/mcp-docs-ask\ncp config.example.json ~/.config/mcp-docs-ask/config.json\n# Prefer a local checkout while developing:\n#   set docs.<id>.source to your docs repo path\nnpx -y @modelcontextprotocol/inspector uv run mcp-docs-ask\n```\n\n## Configuration\n\nConfig path: `~/.config/mcp-docs-ask/config.json`\n\n> **Windows:** `%USERPROFILE%\\.config\\mcp-docs-ask\\config.json`\n\n```json\n{\n  \"docs\": {\n    \"product\": {\n      \"source\": \"https://github.com/example/docs.git\",\n      \"desc\": \"Product guides and API reference\",\n      \"ref\": \"main\",\n      \"include\": [\"**/*.md\"],\n      \"exclude\": [\"archive/**\"],\n      \"layers\": {\n        \"guides\": {\n          \"desc\": \"How-to and onboarding guides\",\n          \"include\": [\"docs/guides/**\"]\n        },\n        \"api\": {\n          \"desc\": \"HTTP API reference\",\n          \"include\": [\"docs/api/**\"]\n        }\n      },\n      \"embedding_model\": \"sentence-transformers/all-MiniLM-L6-v2\"\n    },\n    \"team-notes\": {\n      \"source\": \"/path/to/docs\",\n      \"desc\": \"Internal team notes (local path; ref unused)\",\n      \"include\": [\"**/*.md\"],\n      \"exclude\": [\"archive/**\"]\n    }\n  },\n  \"default\": {\n    \"docs\": \"product\",\n    \"embedding_model\": \"sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2\",\n    \"top_k\": 8,\n    \"chunk_max_chars\": 1500\n  }\n}\n```\n\n`product` is a git URL (`ref` applies). `team-notes` is a filesystem path (`ref` unused).\nOptional `desc` on each docs collection and layer helps agents pick the right target.\n\n`embedding_model`, `top_k`, and `chunk_max_chars` resolve as:\n`docs.<id>.X` → `default.X` → built-in. Omit per-docs keys to inherit.\n\n**Embedding model recommendation**\n\nAny Hugging Face id loadable by `sentence-transformers` works. Pick by language mix:\n\n| Docs / queries | Recommended `embedding_model` |\n|---|---|\n| **English-only** (built-in when omitted) | `sentence-transformers/all-MiniLM-L6-v2` |\n| **Chinese-only** | `BAAI/bge-small-zh-v1.5` |\n| **Multilingual** (~50 langs) | `sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2` |\n\nChanging `embedding_model` requires a `reindex` (the on-disk index stores the model name).\n\n| Field | Description |\n|---|---|\n| `docs.<id>.source` | Docs **repo root**: local path or git URL |\n| `docs.<id>.desc` | Short description for discovery (`list_docs`) |\n| `docs.<id>.ref` | Branch / tag / SHA for git URL sources only (default `main`; ignored for local paths) |\n| `docs.<id>.include` | Globs relative to repo root (default `**/*.md`) |\n| `docs.<id>.exclude` | Globs to skip |\n| `docs.<id>.layers.<name>.include` | Path globs for that layer (first match wins) |\n| `docs.<id>.layers.<name>.desc` | Short layer description for discovery |\n| `docs.<id>.embedding_model` | Optional override (see recommendation above) |\n| `docs.<id>.top_k` | Optional override for default retrieval count |\n| `docs.<id>.chunk_max_chars` | Optional override for max body chars per heading chunk |\n| `default.docs` | Default docs collection id |\n| `default.embedding_model` | Default sentence-transformers model id |\n| `default.top_k` | Default retrieval count |\n| `default.chunk_max_chars` | Default max body chars per heading chunk |\n\n**Layers** partition indexed files by path glob. First match wins. Names are\ncase-insensitive; `all` is reserved (cannot be configured as a layer name).\n\n| `ask_docs` `layer` | Meaning |\n|---|---|\n| `all` (default) | Every indexed chunk (named layers and paths outside them) |\n| `<named>` | Only chunks whose path matched that named layer’s `include` globs |\n\nPaths that match no named-layer glob are still indexed and only appear under\n`layer=all`. Omit `layers` (or set `\"layers\": {}`) for flat repos — use\n`layer=all`.\n\nCache layout:\n\n- Repos (git URL): `~/.cache/mcp-docs-ask/repos/<docs-id>/`\n- Indexes: `~/.cache/mcp-docs-ask/indexes/<docs-id>/`\n\n## Tools\n\n| Tool | Description |\n|---|---|\n| `list_docs` | List configured docs collections, layer filters, and index state |\n| `ask_docs` | Retrieve grounded passages + citations (`layer`: `all` or a named layer) |\n| `reindex` | Sync git source (if URL) and rebuild the vector index |\n\n`list_docs` returns a `default` block with the same keys as the config `default`\nblock (`docs`, `embedding_model`, `top_k`, `chunk_max_chars`), plus a `docs` list\nwhere each entry carries its resolved values, a `default` flag, and an `index`\nblock (`null` when the collection has never been indexed). Valid `layer` values\nare `all` plus the named layer ids — see **Layers** above.\n\n### Index block\n\n`list_docs` and `reindex` return the same `index` keys: `origin`, `root`, `rev`,\n`files`, `chunks`, `layers`.\n\n`origin` mirrors the configured source: `file` for a filesystem path, `git` for\na URL the server clones into `~/.cache/mcp-docs-ask/repos/<docs-id>/` and\nfetches on `reindex`. `root` is where the files actually are — `null` only when\na built index outlived its source directory. `rev` is the checkout HEAD when\nthere is one, so a `file` source that is itself a git clone still reports one;\nits working tree may hold uncommitted edits, so `rev` labels the checkout, not\nthe exact indexed content.\n\n`ask_docs` carries only the two answer-scoped keys, `root` and `rev`: the\ncheckout that produced the passages, and the revision they came from.\n\n### Reading a full source file\n\n`citations[].path` is repo-relative and stable; `answer_context` holds the\npassage text once, keyed by the `[n]` markers that match `citations[].n`. To read\na whole source file, join `index.root` from the same `ask_docs` response with a\ncitation path:\n\n```\n/home/you/docs-repo  +  product/features/budget.md\n```\n\nTake `root` from the response that produced the citations rather than an earlier\n`reindex` — `ask_docs` rebuilds a stale index itself, so its `rev` is the one\nthat matches the passages in hand.\n\n## MCP host examples\n\nThe examples below launch the server with `uvx`, which installs the package on first\nuse. Run it once in a terminal beforehand so your host does not block on that install:\n\n```bash\n$ uvx mcp-docs-ask\nInstalled 84 packages in 275ms\n```\n\nThe server then starts on stdio and waits for input — press Ctrl-C once you see the\ninstall line. Embedding model weights are fetched separately, on the first `ask_docs`\nor `reindex` call.\n\n> **Linux (including WSL, containers, and CI):** the PyPI `torch` wheel for Linux is\n> the CUDA build. It pulls ~15 `nvidia-*` packages whether or not the machine has an\n> NVIDIA GPU — about 2.7 GB of wheels and ~4 GB on disk. Windows and macOS resolve to\n> a CPU-only wheel (~1 GB) and never download CUDA. Pre-warming matters most here:\n> expect the first `uvx` run to take minutes, not milliseconds.\n\n### Cursor\n\nAdd to `.cursor/mcp.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"docs-ask\": {\n      \"command\": \"uvx\",\n      \"args\": [\"mcp-docs-ask\"]\n    }\n  }\n}\n```\n\n### Claude Code\n\nAdd to your Claude Code MCP config:\n\n```json\n{\n  \"mcpServers\": {\n    \"docs-ask\": {\n      \"command\": \"uvx\",\n      \"args\": [\"mcp-docs-ask\"]\n    }\n  }\n}\n```\n\n### Codex\n\n```toml\n[mcp_servers.docs-ask]\ncommand = \"uvx\"\nargs = [\"mcp-docs-ask\"]\n```\n\n### OpenCode\n\n```json\n{\n  \"$schema\": \"https://opencode.ai/config.json\",\n  \"mcp\": {\n    \"docs-ask\": {\n      \"type\": \"local\",\n      \"enabled\": true,\n      \"command\": [\"uvx\", \"mcp-docs-ask\"]\n    }\n  }\n}\n```\n\n### GitHub Copilot\n\n```json\n{\n  \"inputs\": [],\n  \"servers\": {\n    \"docs-ask\": {\n      \"type\": \"stdio\",\n      \"command\": \"uvx\",\n      \"args\": [\"mcp-docs-ask\"]\n    }\n  }\n}\n```\n\n## Development\n\n```bash\nuv sync\nuv run ruff check src/ tests/\nuv run ruff format --check src/ tests/\nuv run pyright\nuv run pytest\n```\n\n## Notes\n\n- **Local path:** `ask_docs` rebuilds the index automatically when file mtimes/sizes\n  change (fingerprint check). You do not need `reindex` after editing local docs.\n- **Git URL:** `ask_docs` never fetches. Call `reindex` to `git fetch` the configured\n  `ref` and rebuild.\n- Changing `embedding_model` invalidates the on-disk index (rebuild on next use /\n  `reindex`).\n",
  "bytes": 9400,
  "sha": "9ab261a1ebc8b92f0cc01892eb46af22370431bf27dc7c19f5bfacb57386e2ed",
  "repo_slug": "alyiox/mcp-docs-ask",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_alyiox_mcp_docs_ask_a6a9b4e3/readme"
}