contextqmd-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ContextQMD
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,176 @@
1
+ # contextqmd-mcp
2
+
3
+ MCP server for local-first, version-aware library documentation. Install, search, and retrieve docs for any library directly from your AI coding assistant.
4
+
5
+ ## What it does
6
+
7
+ ContextQMD is an alternative to context7 that keeps documentation local. It downloads doc packages from the [ContextQMD registry](https://github.com/darkamenosa/contextqmd-registry), indexes them with QMD (BM25 + vector + LLM reranking), and serves results through the Model Context Protocol.
8
+
9
+ Install flow is bundle-first:
10
+
11
+ 1. search the library catalog and choose a library/version
12
+ 2. fetch manifest
13
+ 3. download a `tar.gz` docs bundle when available
14
+ 4. verify SHA256 checksum and unpack into the local cache
15
+ 5. index with QMD
16
+ 6. search locally
17
+
18
+ If a compatible bundle is missing, the server falls back to `page-index` plus per-page fetches. Installs are atomic with rollback on failure.
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ npm install -g contextqmd-mcp
24
+ ```
25
+
26
+ Or run directly with npx:
27
+
28
+ ```bash
29
+ npx contextqmd-mcp
30
+ ```
31
+
32
+ ## MCP Configuration
33
+
34
+ Add to your MCP client configuration (e.g., Claude Desktop, Cursor, etc.):
35
+
36
+ ```json
37
+ {
38
+ "mcpServers": {
39
+ "contextqmd": {
40
+ "command": "npx",
41
+ "args": ["-y", "contextqmd-mcp"]
42
+ }
43
+ }
44
+ }
45
+ ```
46
+
47
+ Or if installed globally:
48
+
49
+ ```json
50
+ {
51
+ "mcpServers": {
52
+ "contextqmd": {
53
+ "command": "contextqmd-mcp"
54
+ }
55
+ }
56
+ }
57
+ ```
58
+
59
+ ### CLI Options
60
+
61
+ ```
62
+ --transport <type> Transport type: stdio (default) or http
63
+ --port <number> HTTP port (default: 3001)
64
+ --registry <url> Registry URL override
65
+ --token <token> API token
66
+ --cache-dir <path> Cache directory override
67
+ ```
68
+
69
+ ## Available Tools
70
+
71
+ | Tool | Description |
72
+ |------|-------------|
73
+ | `search_libraries` | Search the remote library catalog. Returns candidates with versions, aliases, source metadata, and local install status. |
74
+ | `install_docs` | Install a documentation package. Bundle-first with SHA256 verification; falls back to page API. Atomic with rollback. Idempotent — skips if already installed with same manifest checksum. |
75
+ | `update_docs` | Update installed docs to latest version or refresh when manifest checksum changes. Rolls back on failure. |
76
+ | `search_docs` | Search installed documentation locally. Returns page-level results with snippets, line anchors, and scores. Supports auto/fts/vector/hybrid modes. |
77
+ | `get_doc` | Read a bounded slice from a locally installed page by `doc_path` or `page_uid`. Supports sequential reads (`from_line`/`max_lines`) and context windows (`around_line`/`before`/`after`). |
78
+ | `list_installed_docs` | List all locally installed documentation packages with metadata. |
79
+ | `remove_docs` | Remove an installed documentation version or all versions for a library. Cleans up both cache and search index. |
80
+
81
+ ## Search Modes
82
+
83
+ `search_docs` supports four modes via the `mode` parameter:
84
+
85
+ - **auto** (default) — Smart routing based on query classification: short keyword queries use FTS, conceptual/how-to questions use vector, complex multi-aspect queries use hybrid.
86
+ - **fts** — BM25 full-text search (fast, keyword-based). Best for API names, function lookups, and code patterns.
87
+ - **vector** — Semantic vector search. Best for conceptual questions. Falls back to FTS on timeout.
88
+ - **hybrid** — Combined BM25 + vector with LLM reranking (best quality, slower). Falls back to FTS on timeout.
89
+
90
+ Cross-library searches always use FTS regardless of mode.
91
+
92
+ ## Progressive Retrieval
93
+
94
+ The server uses a progressive retrieval model — search returns small snippets with line anchors, then `get_doc` allows bounded expansion:
95
+
96
+ 1. Discover candidate libraries:
97
+ `search_libraries({ query: "react refs" })`
98
+ 2. Install the exact docs package:
99
+ `install_docs({ library: "facebook/react", version: "19.2.0" })`
100
+ 3. Search the local index:
101
+ `search_docs({ query: "how can i optimize refs", library: "facebook/react", version: "19.2.0" })`
102
+ 4. Read a bounded excerpt from the best result:
103
+ `get_doc({ library: "facebook/react", version: "19.2.0", doc_path: "reference/react/useRef.md", from_line: 40, max_lines: 30 })`
104
+
105
+ ### search_docs results
106
+
107
+ Each result includes: `doc_path`, `page_uid`, `title`, `content_md`, `score`, `snippet`, `line_start`, `line_end`, `search_mode`, and `url`.
108
+
109
+ `search_docs` is local-only. If the library is not installed, it returns a `NOT_INSTALLED` error instead of silently fetching from the network.
110
+
111
+ ### get_doc reading modes
112
+
113
+ - **Sequential**: `from_line` + `max_lines` (default: line 1, 60 lines)
114
+ - **Context window**: `around_line` + `before`/`after` (default: 30 before, 60 after)
115
+ - **Line numbers**: set `line_numbers: true` to get line-number-prefixed output
116
+
117
+ ## Upgrade Note
118
+
119
+ Older installed libraries may still have legacy `page_uid.md` paths in the local QMD index. The server lazily rebuilds those indexes on first search. If the rebuild is interrupted, rerun `update_docs` or reinstall the affected library version.
120
+
121
+ ## Configuration
122
+
123
+ Config file location: `~/.config/contextqmd/config.json`
124
+
125
+ ```json
126
+ {
127
+ "registry_url": "https://contextqmd.com",
128
+ "local_cache_dir": "~/.cache/contextqmd",
129
+ "default_install_mode": "slim",
130
+ "preferred_search_mode": "auto"
131
+ }
132
+ ```
133
+
134
+ Environment variables:
135
+ - `CONTEXTQMD_API_TOKEN` — API token for authenticated endpoints
136
+
137
+ ## Architecture
138
+
139
+ ```
140
+ src/
141
+ index.ts # CLI entry point, MCP server, 7 tool handlers
142
+ lib/
143
+ types.ts # TypeScript interfaces for the API contract
144
+ config.ts # Config loader (~/.config/contextqmd/config.json)
145
+ registry-client.ts # HTTP client for the ContextQMD registry API
146
+ local-cache.ts # Local filesystem cache manager (atomic installs, page layout)
147
+ doc-indexer.ts # QMD-backed search indexer (FTS, vector, hybrid, query classifier)
148
+ ```
149
+
150
+ Key design patterns:
151
+ - **Local-first**: All search is local-only. `search_docs` never touches the network.
152
+ - **Bundle-first installs**: Prefers `tar.gz` bundles; falls back to page-by-page API fetches.
153
+ - **Atomic installs**: Staged temp directories with backup/restore for safe upgrades.
154
+ - **Idempotent operations**: `install_docs` is a no-op when the same version/checksum is already installed.
155
+ - **Security**: Bundle extraction validates against path traversal, symlinks, and unsupported entry types.
156
+
157
+ ## Development
158
+
159
+ ```bash
160
+ npm install # install dependencies
161
+ npm run build # compile TypeScript to dist/
162
+ npm run dev # watch mode
163
+ npm run check # type-check without emitting
164
+ npm test # run tests (vitest)
165
+ npm run test:watch # watch mode tests
166
+ ```
167
+
168
+ Set `SKIP_INTEGRATION=1` to skip integration tests that require a running registry at localhost:3000.
169
+
170
+ ## Requirements
171
+
172
+ - Node.js >= 22.0.0
173
+
174
+ ## License
175
+
176
+ MIT
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { RegistryClient } from "./lib/registry-client.js";
4
+ import { LocalCache } from "./lib/local-cache.js";
5
+ import { DocIndexer, type SearchMode } from "./lib/doc-indexer.js";
6
+ export interface ServerDeps {
7
+ registryClient: RegistryClient;
8
+ cache: LocalCache;
9
+ indexer: DocIndexer;
10
+ }
11
+ type ToolResult = {
12
+ content: Array<{
13
+ type: "text";
14
+ text: string;
15
+ }>;
16
+ structuredContent?: Record<string, unknown>;
17
+ isError?: boolean;
18
+ };
19
+ type SearchDocsInput = {
20
+ query: string;
21
+ library?: string;
22
+ version?: string;
23
+ max_results?: number;
24
+ mode?: SearchMode;
25
+ };
26
+ type SearchLibrariesInput = {
27
+ query: string;
28
+ limit?: number;
29
+ };
30
+ type InstallDocsInput = {
31
+ library: string;
32
+ version?: string;
33
+ };
34
+ type GetDocInput = {
35
+ library: string;
36
+ version: string;
37
+ doc_path?: string;
38
+ page_uid?: string;
39
+ from_line?: number;
40
+ max_lines?: number;
41
+ around_line?: number;
42
+ before?: number;
43
+ after?: number;
44
+ line_numbers?: boolean;
45
+ };
46
+ export declare function handleSearchLibraries(deps: ServerDeps, input: SearchLibrariesInput): Promise<ToolResult>;
47
+ export declare function handleInstallDocs(deps: ServerDeps, input: InstallDocsInput): Promise<ToolResult>;
48
+ export declare function handleSearchDocs(deps: ServerDeps, input: SearchDocsInput): Promise<ToolResult>;
49
+ export declare function handleGetDoc(deps: ServerDeps, input: GetDocInput): Promise<ToolResult>;
50
+ export declare function handleListInstalledDocs(deps: ServerDeps): ToolResult;
51
+ export declare function handleUpdateDocs(deps: ServerDeps, input: {
52
+ library?: string;
53
+ }): Promise<ToolResult>;
54
+ export declare function handleRemoveDocs(deps: ServerDeps, input: {
55
+ library: string;
56
+ version?: string;
57
+ }): Promise<ToolResult>;
58
+ declare function createServer(deps: ServerDeps): McpServer;
59
+ export { createServer };
60
+ export declare function isCliEntrypoint(argvPath?: string, moduleUrl?: string): boolean;