Exact-version library docs from your lockfile — local, offline, no rate limits.
Runs on your machine from its package; the steps are below.
At most: only reads or looks up information.
Odel charges nothing per call. The publisher may charge for its own service.
Runs locally over stdio
This server isn't hosted — your MCP client launches it from a package registry. Use one of the commands below, or drop the config into your client (e.g. Claude Desktop).
Run
npx -y @sylphx/lockdocs
MCP client config
{
"mcpServers": {
"lockdocs": {
"command": "npx",
"args": [
"-y",
"@sylphx/lockdocs"
]
}
}
}Exact-version library docs from your lockfile — local, offline, no rate limits.
Reads your lockfile. Answers from the docs and type declarations of the exact version you installed.
npm · PyPI · crates.io · Go. MCP server + CLI in one Rust binary. No account, no API key. MIT.
npx -y @sylphx/lockdocs setup
Docs · Quickstart · Tools · Benchmarks · Compare · How it works
A real terminal: one question, two installs. pydantic 1.10.18 answers BaseModel.dict() (pydantic/main.py:427), pydantic 2.9.2 answers model_dump() (pydantic/main.py:352). It opens with Next.js 14 vs 15 cookies().
Ask for the exact signature of BaseModel.model_dump in a project that pins pydantic 2.9.2 (the run in the demo above):
$ lockdocs api BaseModel.model_dump --tokens 300
pydantic@2.9.2 · pypi · .venv/lib/python3.12/site-packages · pinned in uv.lock
### pydantic.main.BaseModel.model_dump (method) — pydantic@2.9.2 pydantic/main.py:352
def model_dump(self, *, mode: Literal['json', 'python'] | str = 'python', include: IncEx | None = None, …) -> dict[str, Any]
Usage docs: https://docs.pydantic.dev/2.9/concepts/serialization/#modelmodel_dump
Generate a dictionary representation of the model, optionally specifying which fields to include or exclude.
The same call in a pydantic 1 project answers that pydantic 1.10.18 has no model_dump, and shows the closest documentation instead.
npx -y @sylphx/lockdocs setup # add lockdocs to Claude Code, Codex, Cursor, VS Code, Claude Desktop, Windsurf, Gemini CLI
That's it. Using Claude Code only? claude mcp add lockdocs -- npx -y @sylphx/lockdocs mcp. setup detects the clients you have, writes their MCP config, and prints every change. Run it again and nothing changes. Then ask your agent something like "Use lockdocs: how do I reject unknown keys with the zod we use?"
From a terminal, inside any project:
npx -y @sylphx/lockdocs zod "reject unknown keys" # docs for the zod version in your lockfile
npx -y @sylphx/lockdocs api axum::Router::route # exact signature + doc comment
npx -y @sylphx/lockdocs resolve # every pinned version, and whether its docs are here
npx -y @sylphx/lockdocs fetch # once: add upstream docs at each version's git tag
Requires Node 18+. Confirm the install in your editor, open your project, and enable the server. Both links run npx -y @sylphx/lockdocs mcp locally; no API key is needed.
For VS Code from a terminal:
code --add-mcp '{"name":"lockdocs","command":"npx","args":["-y","@sylphx/lockdocs","mcp"]}'
Or add the server to .cursor/mcp.json:
{
"mcpServers": {
"lockdocs": {
"command": "npx",
"args": [
"-y",
"@sylphx/lockdocs",
"mcp"
]
}
}
}
For .vscode/mcp.json, use the VS Code servers format:
{
"servers": {
"lockdocs": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@sylphx/lockdocs",
"mcp"
]
}
}
}
{
"mcpServers": {
"lockdocs": { "command": "npx", "args": ["-y", "@sylphx/lockdocs", "mcp"] }
}
}
Claude Code: claude mcp add lockdocs -- npx -y @sylphx/lockdocs mcp
Claude Code plugin: /plugin marketplace add SylphxAI/lockdocs, then /plugin install lockdocs@lockdocs
Codex (~/.codex/config.toml):
[mcp_servers.lockdocs]
command = "npx"
args = ["-y", "@sylphx/lockdocs", "mcp"]
The server answers for the client's workspace root (or its working directory, or LOCKDOCS_ROOT). Every tool also takes root.
Your agent writes code against the library version it remembers, not the one you installed. So it calls .dict() on a pydantic 2 model, cookies() without await on Next.js 15, and /:id routes on axum 0.8. Hosted doc servers help, but they guess the version from the prompt, cap free usage, and cannot see your private packages.
lockdocs takes the version question off the table:
package-lock.json, pnpm-lock.yaml, yarn.lock, bun.lock, Cargo.lock, uv.lock, poetry.lock, Pipfile.lock, requirements*.txt and go.mod. No library IDs, no "use v14" in the prompt.docs/ folders, plus the API reference in the package itself: .d.ts declarations with JSDoc, Python docstrings and stubs, rustdoc comments, Go doc comments. If it is installed, it is documented, including your private and internal packages.node_modules, your virtualenv, ~/.cargo/registry and the Go module cache. The optional model downloads once (129 MB, kept as 32 MB). Use --offline (or LOCKDOCS_OFFLINE=1) to prohibit all downloads; LOCKDOCS_EMBED=0 disables only the model. No account or hosted query quota. First-use upstream requests reveal the public repository and version being fetched, not your question or project files.--no-fetch or LOCKDOCS_FETCH=0 opts out; cached docs still work offline. Failures are reported alongside local answers, never replaced with latest-version docs.model_validate instead").package@version path:line.Three tools, cheap enough to call before every unfamiliar API:
| Tool | Ask it | Returns |
|---|---|---|
resolve | "Which zod do we use?" | Pinned versions from every lockfile, direct vs transitive, and where their docs are (or what to install) |
docs | "How do I reject unknown keys?" (optionally package: "zod") | The most relevant README, changelog and API sections of the installed version, within budget, each cited zod@4.1.5 v4/classic/schemas.d.ts:453 |
api | z.object, tokio::spawn, BaseModel.model_dump, gin.Context.JSON | The exact signature and doc comment, overloads, members of a class/interface/struct/trait, and other matches |
| Ecosystem | Versions from | Docs read from | API reference |
|---|---|---|---|
| npm | package-lock.json, npm-shrinkwrap.json, pnpm-lock.yaml (v5-v9), yarn.lock (v1 and Berry), bun.lock; else package.json + node_modules | node_modules (including pnpm's .pnpm store and monorepo roots), Yarn PnP zip cache (.yarn/cache, global Berry cache) | .d.ts/.d.mts/.d.cts with JSDoc; @types/* when the package ships none; JSDoc'd JS otherwise |
| PyPI | uv.lock, poetry.lock, pdm.lock, Pipfile.lock, requirements*.txt; else the virtualenv | .venv, venv, $VIRTUAL_ENV, $CONDA_PREFIX, then the system interpreter | Docstrings and signatures from .py, .pyi stubs; README from the wheel's METADATA |
| crates.io | Cargo.lock | ~/.cargo/registry/src (or $CARGO_HOME), git dependencies from ~/.cargo/git/checkouts, vendor/ | Public items with rustdoc (///, //!), including items declared inside macros such as tokio's cfg_rt! |
| Go | go.mod (with replace) | $GOMODCACHE/~/go/pkg/mod, vendor/ | Exported funcs, methods, types, interface methods with doc comments; package docs |
Legacy copies bundled inside a package (zod/v3 inside zod 4, pydantic/v1 inside pydantic 2) rank below the current API.
The first docs or api query adds the selected dependencies' public release-tag docs once: it finds the GitHub repository in the package's own metadata and the git tag of your pinned version, resolves that tag to an immutable commit, and downloads only its docs folders (Markdown, MDX, reStructuredText, docs examples). It does not use ambient GitHub credentials or download major-version website docs by default. Previously opted-in docs-site caches are preserved and their provenance stays visible. lockdocs fetch remains available to prewarm docs and explicitly add major-version docs sites. Answers then cite next@15.1.0 upstream:docs/01-app/.../cookies.mdx:12. See Upstream docs and fetching.
Registry package downloads remain opt-in; default upstream enrichment uses installed package metadata (plus the one-time embedding model download). If a pinned package is not installed (a fresh clone, CI, a lockfile you are reviewing), it says so and tells you how to install it. Pass --fetch (or set LOCKDOCS_FETCH=1, or lockdocs setup --fetch) to let it download exactly that version from the registry (npm tarball, PyPI wheel or sdist, crates.io .crate, Go module proxy zip) into its cache. Fetched answers say fetched from registry.npmjs.org. You can also ask for a version you do not use: lockdocs npm:zod@4.1.5 "strict object" --fetch.
105 questions whose correct answer depends on the version, over 15 libraries (zod, Next.js, React Router, pydantic, axum, tokio, Tailwind CSS, ESLint, Prisma, React, Vite, Express, SQLAlchemy, Django, FastAPI), each asked in a real project with that version installed. An answer passes when it contains the version-correct API and none of the other version's. Same questions and grader against Context7's anonymous API, on a GitHub-hosted runner (run):
| correct | older majors | newer majors | tokio | held-out | median tokens | median latency | |
|---|---|---|---|---|---|---|---|
| lockdocs, default first use | 70/105 | 28/45 | 38/55 | 4/5 | 10/18 | 894 | 185 ms |
lockdocs + lockdocs fetch | 96/105 | 38/45 | 53/55 | 5/5 | 16/18 | 880 | 58 ms |
| Context7 (anonymous) | 77/105 | 19/45 | 53/55 | 5/5 | 15/18 | 908 | 2,583 ms |
lockdocs fetch (185 ms on first use), a fresh CLI process each time. Context7's hosted API took a median 2,583 ms in the earlier runs its reused answers come from.Method, questions, per-question results and scripts: benchmark page and bench/.
| lockdocs | Context7 | docs-mcp-server | Ref | |
|---|---|---|---|---|
| Where docs come from | Your installed packages | Hosted index of public repos and sites | Sites and repos you scrape into a local index | Hosted index |
| Which version | Exact, from your lockfile | Chosen by library ID or prompt; a few indexed versions per library | The version you scraped | Mostly latest |
| Per-library setup | None | None | Scrape each library (and version) | None |
| Private / internal packages | Yes, if installed | Paid plans | Yes, if you scrape them | Private GitHub repos and PDFs you connect (plan limits) |
| API reference from types and doc comments | .d.ts, docstrings, rustdoc, Go doc | Doc snippets | Doc pages | Doc pages |
| Works offline | Yes | No | After scraping | No |
| Limits | None | Anonymous ratelimit-limit: 200 (observed); free key 1,000 calls/month | None | 200 free credits once, then from $19/month |
| Account / API key | No | Optional | Optional (embeddings) | Yes |
| License | MIT | MIT client, hosted service | MIT | Proprietary |
Context7 knows many sites and guides that never ship inside a package, so for a question whose answer lives only on a docs website, it can do better. lockdocs is for the other case: when the version matters, when you are offline or rate-limited, and when the package is yours. They combine well.
(ecosystem, name, version) triples, marking direct dependencies..d.ts, Python, Rust and Go sources with tree-sitter into symbols with signatures, doc comments and qualified paths (z.object, tokio::task::spawn, pydantic.main.BaseModel.model_dump).Typical costs on a GitHub-hosted runner: indexing every declaration in Next.js 15 (7,700 symbols) takes about 0.4 s, zod 4 about 0.05 s, once per version; a query then takes 5-20 ms.
lockdocs <package> [question] Docs for the version this project pins (no question: overview)
lockdocs resolve [filter] Pinned versions and where their docs are
lockdocs docs <question> Search all direct dependencies (--pkg to focus)
lockdocs api <symbol> Exact signature + doc comment
lockdocs fetch [package...] Once: upstream docs at each version's tag, missing packages, the model
lockdocs index [package] Build indexes ahead of time
lockdocs cache [clean] Show or delete the cache
lockdocs upgrade <pkg> <ver> Pro: API changes from the pinned version to <ver>, with your call sites
lockdocs licence status|activate <token> Show or activate a Pro licence (free to run)
lockdocs setup Configure MCP clients (--client a,b --dry-run --remove --fetch)
lockdocs mcp MCP server on stdio
Options: -C/--root <dir>, --pkg <package>, --tokens <n>, --fetch, --no-fetch, --offline, --json
Prebuilt binaries for macOS (arm64, x64), Linux glibc (x64, arm64) and Windows x64 ship through npm; each GitHub release has them too. From source: cargo install --git https://github.com/SylphxAI/lockdocs lockdocs.
Everything above is free, under MIT. The one paid feature is the upgrade report (lockdocs upgrade next 15.0.0, or upgrade_to in the docs tool): which APIs you call were removed, renamed or deprecated between your pinned version and the target, with your call sites. lockdocs Pro is US$120 per seat per year. The licence is checked offline: run lockdocs licence activate <token> (check it with lockdocs licence status, which warns during the last 30 days), or set LOCKDOCS_LICENCE_TOKEN in CI. Details: lockdocs Pro.
lockdocs reads files on your machine and answers over stdio. Network use: the embedding model once from huggingface.co (pinned revision, SHA-256 checked; LOCKDOCS_EMBED=0 or --offline skips it), and anonymous GitHub requests (git tag lookup and one codeload.github.com tarball, not the rate-limited REST API; a repository whose archive is too large or whose download fails is read file by file through the REST API instead) for public docs at the resolved release commit on first query. --fetch / lockdocs fetch also access public registries and major-version docs-site repositories; only these explicit fetches may use GITHUB_TOKEN / GH_TOKEN, and only on api.github.com (never on git, codeload or raw downloads; private repositories are not supported). Upstream requests disclose the repository, version and file paths, not your question, lockfile or project files. --no-fetch / LOCKDOCS_FETCH=0 disables query package/docs downloads; --offline / LOCKDOCS_OFFLINE=1 prohibits all downloads. The cache lives in your OS cache directory (LOCKDOCS_CACHE overrides it).
More from Sylphx: https://sylphx.com/open-source
Questions, a bug, or a team rollout? Email hi@sylphx.com or open an issue.
MIT licence. © Sylphx Limited, registered in England and Wales, company no. 16438428. Registered office: 128 City Road, London EC1V 2NX, United Kingdom. Email hi@sylphx.com. Privacy · Terms · sylphx.com