Skip to main content
Glama
README.md
# skillsearch-mcp

Semantic skill-library search as an MCP server. Given a natural-language
query, it finds the most relevant skills (or any Markdown knowledge files)
in your local skill directories — works out of the box with **Claude Code**,
**Codex**, Cursor, and any MCP client.

Built on a small, local-first stack:

- **Embeddings**: `bge-small-zh-v1.5` via Ollama (works for Chinese + English)
- **Storage**: SQLite (WAL) — vector blobs + keyword columns, no heavy vector DB
- **Ranking**: dual-channel Reciprocal Rank Fusion (cosine + substring keywords),
  so both semantic intent and exact terms like `ffmpeg` or `14234` are found

```
user query ──▶ embed (bge-small-zh) ──┐
              keyword grams (4/2-gram) ─┴─▶ RRF ──▶ top-k skills + paths
```

## Why

Claude Code and Codex already have filesystem access. What they lack is a
fast way to answer *"which of my 100+ skills/knowledge files matches this
task?"*. This server turns your skill library into a searchable index that
any MCP client can query in ~100ms (local Ollama).

## Requirements

- Python 3.10+
- [Ollama](https://ollama.com) running locally with the embedding model:

```bash
ollama pull quentinz/bge-small-zh-v1.5
```

## Install

```bash
pip install skillsearch-mcp        # from PyPI (once published)
# or from source
git clone https://github.com/<you>/skillsearch-mcp.git
cd skillsearch-mcp && pip install -e .
```

## Configuration

Environment variables:

| Variable | Default | Description |
| --- | --- | --- |
| `SKILLSEARCH_SKILLS_DIR` | `~/skills` | Skill/knowledge root(s). Multiple dirs separated by `;` (Windows) or `:` (Unix). Scanned recursively for `SKILL.md` files. |
| `SKILLSEARCH_DB` | `~/.cache/skillsearch/vector_index.db` | Where the index lives. Rebuilds automatically when skills change (mtime-based). |
| `OLLAMA_URL` | `http://127.0.0.1:11434` | Ollama endpoint. |
| `EMBED_MODEL` | `quentinz/bge-small-zh-v1.5` | Embedding model. Any Ollama embedding model works, but index must be rebuilt after changing it (server does this automatically via meta check). |

## Client setup

### Claude Code

```bash
claude mcp add skillsearch -- python -m skillsearch_mcp
```

or in `.mcp.json`:

```json
{
  "mcpServers": {
    "skillsearch": {
      "command": "python",
      "args": ["-m", "skillsearch_mcp"],
      "env": { "SKILLSEARCH_SKILLS_DIR": "/path/to/skills" }
    }
  }
}
```

### Codex

```bash
codex mcp add skillsearch -- python -m skillsearch_mcp
```

or `~/.codex/config.toml`:

```toml
[mcp_servers.skillsearch]
command = "python"
args = ["-m", "skillsearch_mcp"]
env = { SKILLSEARCH_SKILLS_DIR = "/path/to/skills" }
```

## Tools

### `skill_search(query, k=5)`

Semantic search. Returns top-k matching skills with name, score, description,
and absolute path. k is capped at 10.

### `skill_list()`

List every indexed skill (name + description + path). Useful for exploration
or for clients that want a full inventory.

## Skill file format

Any directory containing `SKILL.md` files is indexed. A minimal example:

```markdown
---
name: ffmpeg-video-batch-processing
description: ffmpeg video batch processing — watermark, compress, rename
---

# ffmpeg video batch processing
...full content...
```

`name` and `description` from the frontmatter are used for search. Files
under `.archive`, `.git`, `references`, `assets`, `templates`,
`node_modules` are skipped.

## How indexing works

- On first query the server scans `SKILLSEARCH_SKILLS_DIR`, embeds the first
  512 chars of each skill, and stores vectors in SQLite (WAL).
- On subsequent queries it only re-indexes files whose mtime changed
  (cheap `update_index` pass), and prunes deleted ones.
- Query path: embed the query with the retrieval prefix, cosine-search the
  vector table, run 4-gram/2-gram substring scoring over name/description/
  full_text, then fuse both rankings with RRF.

## License

MIT