doctrove
Provides versioned documentation retrieval for the Express web framework, including catalog lookup, version listing, and extracting relevant documentation snippets for specific topics.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@doctroveWhat's the correct way to define route params in Express 5?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
doctrove — Versioned library documentation retrieval (agent toolset)
doctrove is a versioned documentation retrieval plugin for coding agents: it maintains a "library documentation catalog index", letting agents fetch accurate, versioned, traceable API documentation snippets on demand while writing code — instead of guessing API usage from training memory — and thereby avoiding "APIs that don't exist in the docs", "outdated signatures", and "fabricated parameters".
Zero runtime dependencies: uses only Node.js built-ins (
fetch,node:test), runs without installing any package;Standard MCP stdio server: any MCP-capable client (dsh, Claude Code, Codex, opencode, etc.) can connect;
Built for dsh: ships a dsh bundle (
cordis.patch.yml+ a self-built bridge plugin), one-step integration viadsh plugin add, tools automatically appear in the model's tool list (mcp__doctrove__*);Versioned: every entry carries multiple documentation volumes, supporting "latest stable / exact version / prefix version (
4→ 4.21.x)" selection;Scored, ranked results: entry retrieval and documentation snippets both carry 0–1 relevance scores and hit signals, so the model can verify "why it ranked first";
Offline-capable: ships with a built-in local demo index (
data/index.json), runs without networking or remote sources;Self-hostable remote index: the index is an open JSON format that can be hosted on any static hosting (a zero-dependency hosting script is included);
Smart caching: TTL + LRU in-memory cache, remote index and query results expire automatically per configuration,
--no-cachedisables it in one shot;Graceful degradation: when the remote index is unreachable, falls back to the local index automatically, results are tagged with
sourceso the agent can tell data provenance.
Quick start
Method A: connect directly from any MCP client
# Requires Node.js >= 18.17; no arguments means offline mode (built-in index)
node src/entry.jsExample config line using the official dsh bridge (also applies to Claude Code / Codex MCP config):
# dsh: insert into $DSH_HOME/profiles/<profile>/cordis.patch.yml
- insert:
- id: mcp-doctrove
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: doctrove
transport: stdio
command: node
args: ['/absolute/path/src/entry.js']Once connected, the model sees 3 tools: catalog_lookup, catalog_releases, doc_extract
(generic MCP clients see the bare names; in the dsh scenario they carry the mcp__doctrove__ prefix, see below).
Method B: install as a dsh plugin bundle (recommended)
This plugin is declared as a dsh bundle (the dsh.bundle field in package.json). Run the following in the plugin checkout directory:
dsh plugin --profile web add .On first use it automatically initializes the
webprofile and adds this package todsh.profile.bundles;The
doctrove/bridgeplugin defined incordis.patch.ymlspawns this MCP server inside the dsh process, and after the handshake registers all tools intoctx.tools, no manual config changes needed;Offline-capable: the built-in index is loaded by default; to use a remote index, configure
args: ['--index-url', ...]on the bridge line (see below);Uninstall:
dsh plugin --profile web remove doctrove.
Installing in DSH
dsh plugin --profile demo add github:JohnXu22786/docs-retrieverdemois a dsh profile: it is created automatically on first use, and the package is added todsh.profile.bundles;The
cordis.patch.ymlinside the package defines thedoctrove/bridgeplugin, which starts this MCP server within the dsh process and registers all tools intoctx.toolsafter the handshake — no manual configuration needed;Offline-capable out of the box: the built-in index is loaded by default; configure
args: ['--index-url', ...]on the bridge line to use a remote index (see below);Removal:
dsh plugin --profile demo remove doctroveAfter installing, restart dsh, then in a session you can simply say:
"Write an Express 5 endpoint with
:idroute params and a JSON response — look up the exact route-param syntax first"
The corresponding tool call chain: mcp__doctrove__catalog_lookup (confirm express) →
mcp__doctrove__doc_extract (id=express, focus=route parameters).
Note: dsh enables no MCP servers by default (every server command is trusted code executed outside the sandbox), and this plugin's bundle line is the "enable" action itself; only install plugins you trust.
Related MCP server: docs-cache-mcp
dsh integration notes (how the pluginized harness loads it)
dsh uses the Cordis plugin framework, and the composition unit is a bundle: an npm package + a patch layer. The loading chain is:
package.json(dsh.bundle.patch → ./cordis.patch.yml)
└─ one line in cordis.patch.yml: name: 'doctrove/bridge'
└─ src/bridge/plugin.js(Cordis plugin, inject: ['tools'])
├─ spawns src/entry.js with Node itself(MCP server subprocess, stdio)
├─ completes the initialize / tools/list handshake
└─ registers each tool as mcp__doctrove__<toolname> into ctx.toolsTool interface: the model-visible tool names are
mcp__<serverName>__<raw tool name>,serverNamedefaults todoctrove;Events/skills: this plugin registers no events or skills; it exposes capabilities only through the
ctx.toolstool interface (read-only tools, no side effects);Lifecycle: the handshake and registration happen during the plugin's
apply; on unload the subprocess is killed and all tools are deregistered automatically (registered viactx.effectcleanup, no leftovers after hot reload/unload);Two bridge options: the self-built bridge
doctrove/bridgebundled with this package (zero-dependency, works out of the box) and the official@deepseek-ai/dsh-mcp-clientconfig line (seeexamples/overlay-for-dsh.yml.example); tool naming and behavior are identical — pick either one, don't enable both;Environment variables: dsh filters credential-like variables from MCP subprocess environments; the self-built bridge's subprocess inherits the host environment, so
DOCTROVE_INDEX_URLand similar pass through, and can also be set explicitly withenv:on the bridge line.
Common dsh issues
Symptom | Fix |
Tools missing from the list | Check that the |
Want a remote index | Configure |
Want looser caching | Configure |
pnpm >=10 rejects git-installed prepare scripts | This plugin is pure JS with no build script, so it is not affected; install from checkout or tarball |
Tool list (3 tools, all read-only)
Tool | Purpose | Main parameters |
| Search the doc catalog by name/description, return candidates with scores and hit signals |
|
| List available and recommended versions of an entry |
|
| Extract doc snippets for an entry/version/focus (relevance-ranked) |
|
catalog_lookup
Search the doc catalog. When unsure of a library's canonical id, call this first, then use the returned id with doc_extract.
// request
{ "query": "express", "limit": 5 }
// response (structuredContent summary)
{
"results": [{
"id": "express", "name": "Express", "summary": "Minimal web framework for Node.js",
"score": 1.0, "matches": ["exact name match"],
"versions": ["5.1.0", "4.21.2"], "latest": "5.1.0", "source": "local:.../data/index.json"
}],
"total": 1, "sources": ["local:.../data/index.json"]
}catalog_releases
View an entry's version list and recommended version, useful for checking whether a target version is available (doc_extract supports the same version syntax).
{ "id": "express" }
// → { "id": "express", "name": "Express", "latest": "5.1.0",
// "versions": ["5.1.0", "4.21.2"], "source": "local:..." }doc_extract
Extract documentation. focus describes one concept at a time (e.g. "route parameters"); split cross-concept questions into multiple calls
to avoid diluted results; version defaults to the latest stable release.
{ "id": "express", "version": "5", "focus": "wildcard" }
// → {
// "id": "express", "name": "Express", "version": "5.1.0",
// "releaseKind": "prefix", "releaseNote": "prefix match 5.x → latest 5.x release",
// "sections": [{ "heading": "Wildcard routes", "score": 0.5, "matches": ["heading hit: 1 word"], ... }],
// "source": "local:..."
// }Errors are always structured isError results, with error.code taking one of: validation / not-found / version /
network / timeout / internal, and message carrying actionable hints (e.g. candidate versions when the requested one is unavailable).
Parameter-validation failures are likewise folded into isError (rather than the protocol-level -32602), so the model sees a structured error code in one call and can self-correct.
Scoring and ranking algorithm
Entry retrieval (catalog_lookup)
Score = signal-tier score + popularity fine-tuning, both capped at 1.0:
Signal | Base score | Notes |
Exact name match (case-insensitive) | 1.0 | name or id exactly equals the query |
Exact alias match | 0.95 | e.g. query |
Name prefix match | 0.90 | e.g. query |
Alias prefix match | 0.85 | |
Name token overlap | 0.60–0.83 | proportional to hit tokens; ceiling deliberately below the alias-prefix tier to keep tier order invariant |
Summary token overlap | 0.30–0.50 | when the name is completely unrelated |
Popularity fine-tuning =
(1 − raw) × min(0.1, log₁₀(popularity)/100), applied only within the headroom of the current signal tier, so "exact > alias > prefix > token overlap" can never be inverted by popularity;Tokenization: English by word, Chinese per character (space-less languages);
Ties are broken by popularity, descending (stable sort).
Doc snippet ranking (doc_extract focus)
Snippet score =
(2 × heading hit words + body hit words) / (2 × query words);Heading hits count double the body; zero-hit snippets are filtered out; truncated past
maxSections;Without
focus, snippets return in the index's original order.
Version selection (catalog_releases / doc_extract version)
latest / default → latest stable release (or latest prerelease when no stable exists);
exact version → unique match (a v/V prefix is tolerated; build metadata such as +build.2 does not participate in comparison);
prefix (5 / 5.1 / 5.1.x / 5.1.*) → latest release matching the prefix;
prerelease identifiers compare per semver rules (rc.10 > rc.9);
no match → version error with a candidate list attached.
Caching strategy
One TTL + LRU in-memory cache per process (default 256 entries, 600 s lifetime), caching: remote index fetches and query results; the local index itself is parsed once per process (static data);
TTL is configurable:
--cache-ttl <sec>(0–86400, 0 = disabled),--no-cacheis a shortcut for disabled;Failure cooldown (negative caching): after a remote index fetch fails, a 30-second cooldown kicks in during which the plugin falls back to local and does not repeat the network request (avoiding a timeout wait on every query while the source is down); after the cooldown it retries automatically and heals itself once the source recovers. The cooldown timing is independent of the cache TTL (a
--cache-ttlshorter than the cooldown does not cut it short); it does not apply under--no-cache/--cache-ttl 0(every failure then really retries);LRU evicts by access order; cache stats (hits/misses/evictions) are printed to stderr at exit with
--debug;Local-index cold start is free (synchronous read); after the first remote fetch, all queries hit the cache.
Offline mode and remote index
Offline mode (default)
Without --index-url the plugin is fully offline: it uses the built-in data/index.json (3 demo entries:
Express 5.1/4.21 dual versions, Zod 3.24/3.23, Day.js 1.11, including a version-difference demo).
The built-in index can be replaced with your own (--local-index <path>), see the format below.
Remote index
The index is an open JSON format hostable on any static HTTP service (GitHub Pages, object storage, intranet file servers all work):
index URL(--index-url / DOCTROVE_INDEX_URL,the URL of the directory containing index.json)
└─ <url>/index.json ← fetched by the plugin along this pathMinimal local hosting (zero dependencies, supports ETag conditional requests; by default listens on the local loopback only — change host yourself to expose on LAN):
node scripts/serve-index.mjs [dir] [port] # default ./data, port 8730
node src/entry.js --index-url http://localhost:8730Relationship between remote and local: remote first, local as fallback. When the remote fetch fails (offline/timeout/non-2xx/invalid format),
the plugin degrades to the local index and keeps serving; every entry and result carries a source tag so the model can judge data freshness.
Index format specification
{
"format": "doctrove-index@1", // required, versioned format identifier
"updatedAt": "2026-08-16T00:00:00.000Z",
"entries": [{
"id": "express", // required, canonical id (globally unique)
"name": "Express", // required, display name
"summary": "Minimal web framework for Node.js",
"aliases": ["expressjs"], // search aliases (array of strings)
"homepage": "https://expressjs.com",
"popularity": 1200, // popularity weight (scoring fine-tuning)
"versions": ["5.1.0", "4.21.2"], // required, available versions
"volumes": { // required, version → documentation volume
"5.1.0": {
"summary": "highlights of this version (optional)",
"sections": [{ // required, doc snippets (elements must be non-array objects)
"heading": "Route handlers", // snippet title (2x ranking weight)
"path": "https://expressjs.com/en/5x/api.html#app.METHOD", // provenance link (optional)
"body": "snippet body (may include code examples)"
}]
}
}
}]
}Validation rules: format must be doctrove-index@1; entries must be an array; id/name non-empty and id unique;
aliases must be an array of strings; every version in versions must have a matching volumes volume,
and a volume's sections must be a valid array of objects.
Invalid indexes are rejected (remote sources report network and degrade to local; local sources report config and exit).
Configuration reference
Precedence: command line > environment variables > config file > defaults.
Setting | CLI | Environment variable | Config file key | Default |
Remote index URL |
|
|
| none (offline) |
Local index path |
|
|
| built-in |
Cache lifetime (s) |
|
|
| 600 |
Remote timeout (ms) |
|
|
| 15000 |
Debug logging |
|
|
| false |
Config file |
|
| — | none |
The config file is JSON (example: examples/doctrove.config.example.json). All configuration is read-only:
the plugin performs no writes and persists no local state. Empty-string environment variables count as unset (defaults apply);
cacheTtl: 0 is a valid value (cache disabled).
Testing
node --test # 105 cases: scoring/versions/cache/config/JSON-RPC/engine/e2e/index hostingCoverage: scoring-ranking boundaries (tier order can never be inverted by popularity), version selection (latest/exact/prefix/prerelease/ build metadata), multi-source merge and degradation self-healing, failure cooldown, cache TTL/LRU, config precedence plus invalid values and empty strings, MCP protocol (uninitialized gate, version negotiation, error folding, conflicting messages), subprocess-level end-to-end (handshake + 3 tools + error paths + graceful exit), index hosting (ETag/304/traversal protection/symlink escape/malformed encodings).
Directory structure
src/
entry.js CLI entry: config → assembly → stdio MCP session
core/ config (layered config), errors (unified error model), version
vault/ttl.js TTL + LRU in-memory cache
catalog/ scoring (scoring/ranking), releases (version selection), store (catalog hub)
supply/provider.js data sources: LocalSource / RemoteSource + index validation
protocol/ jsonrpc / engine (MCP session engine) / transport (stdio line protocol)
tools/ registry (registry + parameter validation), definitions (3 tools)
bridge/ plugin.js (dsh Cordis plugin), client.js (MCP stdio client)
data/index.json built-in offline index (demo data, replaceable)
scripts/serve-index.mjs zero-dependency index hosting script
test/ 105 test casesLicense
MIT (see LICENSE).
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Alicense-qualityDmaintenanceAn MCP server that provides version-pinned, deterministic documentation sourced from DevDocs.io to AI assistants (Claude, RooCode, Cline, Copilot etc.) and also via offline mode. Not via Scraping! But using the supported downloading option from devdocs.3813MIT
- AlicenseAqualityBmaintenanceA local MCP server that fetches official library documentation (llms.txt-first), caches it to disk, and serves relevant sections to coding agents offline with deterministic retrieval.34MIT
- Alicense-qualityAmaintenanceMCP server that downloads and analyzes sources of Maven-published Kotlin/Java libraries, exposing structured API information, KDoc, and raw source to AI agents.8Apache 2.0
Related MCP Connectors
An MCP server that gives your AI access to the source code and docs of all public github repos
Augments MCP Server - A comprehensive framework documentation provider for Claude Code
MCP server for accessing curated awesome list documentation
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/JohnXu22786/docs-retriever'
If you have feedback or need assistance with the MCP directory API, please join our Discord server