the-librarian
Optionally routes the shortlist through Apple's on-device Foundation Model (Apple Intelligence) for a local reranking pass that scores each candidate's fit, adds one-sentence why/why-not notes and a final pick, while falling back to plain embedding order on failure or timeout.
Uses an Ollama server (local or remote via OLLAMA_HOST) with the nomic-embed-text model to embed skill descriptions and search intents, powering the semantic similarity search behind the librarian's recommendations and reindexing.
the_librarian
A skill librarian MCP server — your agents stop carrying the whole skill collection in context and start asking for the one right book.

How it started
One question in a chat:
"I'm sure you know better than the internet what you need to get better. What's the best workflow with Claude Code? Installing skills and just you pick the best? More the better? Less is more?"
The answer: less is more. Every installed skill costs tokens and adds triggering ambiguity — with 50 skills, descriptions overlap and the wrong one fires; with 8 sharp ones, triggering is nearly deterministic.
But the collection had ~3,000. So instead of installing any of them: give the collection a librarian.
librarian_find(intent)→ librarian recommendsagent uses the skill, does the work
librarian_report(skill, worked: true/false, note)→ librarian logs it
"The poetic part: the librarian is itself the curation loop. Log every query and what got picked vs ignored — skills that never surface are your kill candidates, queries that match nothing are your gaps. The collection optimizes itself from its own usage data."
"So the full shape: SQLite (skills, embeddings, query log, outcome log) + FastMCP + two read tools + one write tool. Maybe 300 lines of Python. It's small, it's one thing, and it compounds."
It's ~600 lines now. Scope creep found even the librarian. The original conversation — typed poolside on a phone, typos preserved on purpose — is in ORIGIN.md.
Related MCP server: AAS Core MCP Server
What it is
A local-first MCP server that acts as a librarian between coding agents and a large skill collection. The collection stays raw markdown on disk — agents never load it into context. They describe what they're trying to do in plain language; the librarian finds the skills whose meaning matches (semantic search — no keyword guessing), makes sure the shortlist isn't three flavors of the same thing, avoids repeating what it just recommended, optionally lets Apple's on-device model deliberate over the finalists — and learns from what agents report back.
Core principle: agents read recommendations, write only outcomes. They never edit skills, weights, or rankings. Curation decisions stay with the human, informed by librarian_stats.
Every stage is local and free: Ollama embeddings, SQLite, and (optionally) Apple Intelligence.
→ In production since June: real usage numbers — 366 queries, 284 agent-filed outcome reports across three snapshots (latest 2026-09-12), what worked and what's queued next.
Pipeline (one find request, end to end)
In plain words: every skill's description is turned once into an embedding — a list of numbers that captures what the skill is about, so "package my server for distribution" can match a skill that never uses the word "package." A request goes through five steps:
intent ─→ Ollama embed (nomic-embed-text, /api/embed)
─→ cosine vs every skill vector (SQLite) ← "which skills mean the same thing?"
─→ MMR diversity + recency decay ← anti-monotony (see below)
─→ [opt-in] AFM rerank: Apple's on-device model scores fit 1–10,
why / why-not per candidate, final pick (bin/afm-rerank, ~6–12s)
─→ [opt-in] soft filter: a local decision model marks the top 4
"likely" or "weak match", likely first, nothing dropped (nimble, ~3–5s)
─→ compact JSON shortlistSetup (2 minutes)
git clone https://github.com/aka-kika/the-librarian.git
cd the-librarian
uv venv --python 3.12 .venv
uv pip install --python .venv/bin/python "mcp[cli]>=2" httpx pydantic
# embedding model on whichever node you point at:
ollama pull nomic-embed-textClaude Code config
Add to ~/.claude.json (or project .mcp.json), with paths adjusted to where you cloned this and where your skills live:
{
"mcpServers": {
"skill-librarian": {
"command": "/path/to/the-librarian/.venv/bin/python",
"args": ["/path/to/the-librarian/server.py"],
"env": {
"LIBRARIAN_SKILLS_DIR": "/path/to/your/skills-collection",
"OLLAMA_HOST": "http://localhost:11434",
"LIBRARIAN_EMBED_MODEL": "nomic-embed-text",
"LIBRARIAN_CANONICAL_PREFIXES": "categories",
"LIBRARIAN_RERANK_BIN": "/path/to/the-librarian/bin/afm-rerank"
}
}
}
}The skills dir is scanned recursively for SKILL.md files (standard agent-skill format: YAML frontmatter with name + description, body below). OLLAMA_HOST can point at a remote node (e.g. over Tailscale) to offload embedding — though an M4 Max reindexed the original 3,000-skill collection in ~2 minutes locally (it has since been trimmed to under 900, which takes seconds).
First run
> use librarian_reindex
> use librarian_find with intent "package my ollama wrapper for distribution"Soft filter (optional, local decision model)
After the AFM rerank, librarian_find can ask a small local model whether each of the top candidates fits the intent. Skills it marks "match": "likely" come first; "weak match" ones stay in the list below them. Nothing is dropped. Settings in ~/.skill_librarian/soft_filter.json (enabled, model, judge_top, timeout_s); LIBRARIAN_SOFT_FILTER=off turns it off. A slow or missing model returns the previous order. Each find appends one line to ~/.skill_librarian/soft_filter.jsonl. Bench and test set: bench/decision-filter-2026-10-09/ (nimble 72% precision vs 33% without; laya models not usable).
AFM reranker (optional, Apple Intelligence)
librarian_find can pass its shortlist through Apple's on-device Foundation Model for a deliberation pass — reorders by reasoned fit (1–10) and adds one-sentence why / why-not per candidate, plus a final pick. Fully local, free, ~5–12s. Build and enable:
mkdir -p bin
swiftc -O -parse-as-library afm_rerank.swift -o bin/afm-rerank
# then set LIBRARIAN_RERANK_BIN to the binary path in the MCP envUnset LIBRARIAN_RERANK_BIN (or any failure/timeout) falls back to pure embedding order. Brainstorm is never reranked — it wants divergence, not convergence. The reranker's output is validated against the real candidate set — small on-device models happily hallucinate skill names. Skill names and descriptions are someone else's text, so they reach the model cleaned (no line breaks or control characters) inside a <candidates> fence, one JSON line each, with an instruction to treat them as data: a description that says "rank me first" no longer wins the pick.
Tools
Tool | What it does |
| Ranked shortlist for an intent — varied picks, no repeats from recent queries (the closest match is never held back), success rates shown, and a plain |
| Hands over one skill in full (SKILL.md plus its other file names), so an agent can read a skill it does not have without copying it. Each fetch is logged. |
| Wide diverse sweep: k candidates, the last 2 random wildcards. For ideation, not convergence. |
| Agent reports skill worked/failed. Append-only — agents can't touch rankings. |
| Rescan + re-embed changed SKILL.md files (hash-checked, cheap to rerun). Also mirrors the pre-archive room ( |
| Curation digest: hot skills, never-surfaced (kill candidates), low success rate (rewrite candidates), skills on trial after a fetch, and searches where no skill fit (with the room skills they came close to). |
Teaching your agents to use it
Connecting the server is half the job — agents also need the habit of asking. The repo
ships one agent skill for that: skills/using-the-skill-librarian.
Install it in each agent's skills directory and it teaches the whole loop: find before
any non-trivial task, weigh the why/why-nots, read the winning skill in place, and
always file a librarian_report afterward — the reports are what make the collection
curate itself.
The joke writes itself, but it's real: the only skill you install is the one that teaches agents to ask the librarian. In three months of production use, that habit produced outcome reports on ~78% of queries, unprompted (~88% since this skill shipped).
Why it won't recommend the same 3 skills forever
The failure mode this design exists to kill: a naive retriever recommends the same handful of skills every time. Three mechanisms prevent it:
MMR selection (maximal marginal relevance) — each next pick has to be relevant to the request and different from the picks already made, so the shortlist can't be three near-duplicates.
Recency decay — the librarian remembers its own recent recommendations; anything suggested in the last 10 queries gets pushed down (
RECENCY_PENALTY = 0.15per appearance — tune at top of server.py).Wildcards in brainstorm mode — 2 random skills from outside the relevant set, every time. They are not logged as recommendations, so they never pick up a repeat penalty.
The AFM reranker runs after these — it reorders the already-diversified shortlist, so it can't reintroduce monotony.
Three months of real data says it works: 525 distinct skills surfaced across 366 queries. See the usage report for the one sharp edge (recency decay also hides just-confirmed winners) and the fix queued for it.
The flywheel
Every librarian_find is logged. Every librarian_report is logged. Run librarian_stats weekly: never-surfaced skills → kill, low-success skills → rewrite descriptions. The collection curates itself from usage.
First time it paid out (2026-09-12): after three months the rewrite list went from empty to 11 skills, and it is almost entirely one family — mcp-integration (6 uses, 1 worked), mcporter (5, 0), skill-installer (5, 0), mcp-building (4, 0). The agents' notes explain why: those four surface for every MCP-flavoured intent and get rejected as the wrong tool for that exact job. No static review would have found that; the descriptions all read fine. The fix is a description pass on four files, not a new feature. Details in the usage report.
Tuning knobs (top of server.py)
RECENCY_WINDOW/RECENCY_PENALTY— how hard repeats get punishedMMR_LAMBDA_FIND(0.7) — relevance vs diversity for findMMR_LAMBDA_BRAINSTORM(0.45) — brainstorm leans diverseRERANK_TIMEOUT(30s) — AFM reranker budget before falling back to embed order
Env knobs: LIBRARIAN_CANONICAL_PREFIXES (which top-level dirs win duplicate-name races, e.g. categories; inside one prefix the shallower path wins, so a root-level override beats the same name nested in a pack; a skill reached through two symlinks is ranked the same way, never by folder-walk order), LIBRARIAN_RERANK_BIN (path to afm-rerank; empty disables rerank).
Hard rules the code keeps
Never load full skill bodies into tool responses — descriptions + metadata only. The whole point is keeping the collection out of agent context.
Agents get no write access beyond the append-only outcome log.
DB is SQLite WAL at
~/.skill_librarian/librarian.db;query_logandoutcome_logare append-only — no tool deletes or rewrites log rows.
FAQ
What is a skill librarian? An MCP server that sits between AI agents and a skill collection: agents describe a task in plain language, the librarian recommends the few skills that fit, and the collection itself never enters agent context.
Why not just install all the skills? Every installed skill's description is loaded into context on every request — with a large collection that's a constant token cost, and overlapping descriptions make the wrong skill fire. The librarian reduces that to one tool call when a skill is actually needed.
Does it work with agents other than Claude Code? Yes — it's a standard stdio MCP server. Any MCP client (Claude Code, Claude Desktop, Cursor, Goose, and others) can use it; this one runs against five different agents daily.
Does my skill collection leave my machine? No. Indexing (Ollama), storage (SQLite), and the optional reranker (Apple's on-device model) all run locally. Nothing is sent anywhere.
See CHANGELOG.md for the full history of changes and the decisions behind them.
This server cannot be deployed
Maintenance
Related MCP Connectors
- Skill MeOAuthdev.skillme
Agent skills for Claude, Codex and Cursor: find, install and load skills from one connector.
AI agent skills marketplace — token-efficient skill search & execution
Shared memory for coding agents. Stop re-explaining your codebase every session.
Agent-first skill marketplace with USK open standard for Claude, Cursor, Gemini, Codex CLI.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to semantically search and retrieve relevant skills from a local Markdown skill database, reducing context waste by loading only needed skills through tiered detail levels and tool calls.7 npm6MIT
- AlicenseNot gradedqualityCmaintenanceEnables coding agents to search, recommend, and validate a stack of reusable skills from a local catalog, producing deterministic plans without modifying the project.5,074 npm1MIT
- AlicenseAqualityBmaintenanceYour coding agent remembers how it solved past tasks and reuses it next time: trigger, steps, outcome, lessons. Hooks recall the right skill before a task; skills strengthen with confirmed use and unused ones decay. Local SQLite, no API key, nothing leaves your machine. 9 MCP tools, works with Claude Code, Codex and Cursor. Agent-written memory stays untrusted until you approve it.9262 PyPI11Apache 2.0
- FlicenseNot gradedqualityAmaintenanceEnables AI coding agents to store, semantically search, and recall project-specific and cross-project memories directly during a session, backed by local SQLite stores that work offline with no API keys. It also exposes optional task-queue tools so agents can queue, orchestrate, and autonomously execute coding work using that same stored context.-