react-docs-mcp 1.0.6 → 1.2.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/README.md +20 -0
- package/dist/config.d.ts +47 -15
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +25 -31
- package/dist/config.js.map +1 -1
- package/dist/docsManager.d.ts +11 -1
- package/dist/docsManager.d.ts.map +1 -1
- package/dist/docsManager.js +55 -5
- package/dist/docsManager.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -266
- package/dist/index.js.map +1 -1
- package/dist/markdownParser.d.ts +9 -0
- package/dist/markdownParser.d.ts.map +1 -1
- package/dist/markdownParser.js +14 -8
- package/dist/markdownParser.js.map +1 -1
- package/dist/presets/reactDocs.d.ts +7 -0
- package/dist/presets/reactDocs.d.ts.map +1 -0
- package/dist/presets/reactDocs.js +44 -0
- package/dist/presets/reactDocs.js.map +1 -0
- package/dist/presets/reactNativeDocs.d.ts +21 -0
- package/dist/presets/reactNativeDocs.d.ts.map +1 -0
- package/dist/presets/reactNativeDocs.js +51 -0
- package/dist/presets/reactNativeDocs.js.map +1 -0
- package/dist/presets/searchDefaults.d.ts +9 -0
- package/dist/presets/searchDefaults.d.ts.map +1 -0
- package/dist/presets/searchDefaults.js +16 -0
- package/dist/presets/searchDefaults.js.map +1 -0
- package/dist/searchEngine.d.ts +6 -4
- package/dist/searchEngine.d.ts.map +1 -1
- package/dist/searchEngine.js +25 -10
- package/dist/searchEngine.js.map +1 -1
- package/dist/server.d.ts +7 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +283 -0
- package/dist/server.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
AI-powered semantic search over React documentation for Claude, Cursor, and other MCP clients.
|
|
8
8
|
|
|
9
|
+
> **📱 Also in this repo: [react-native-docs-mcp](https://www.npmjs.com/package/react-native-docs-mcp)** — the same engine, but for the official React Native docs (reactnative.dev). Source lives in [`packages/react-native-docs-mcp`](./packages/react-native-docs-mcp). Both packages share one search engine, so improvements land in both.
|
|
10
|
+
|
|
9
11
|
<p align="center">
|
|
10
12
|
<img src="./demo.gif" width="100%" alt="React Docs MCP Demo">
|
|
11
13
|
</p>
|
|
@@ -54,6 +56,8 @@ Edit: `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
|
|
|
54
56
|
|
|
55
57
|
## Features
|
|
56
58
|
|
|
59
|
+
- **🔑 No API Key**: Unlike hosted docs services (Context7, GitMCP), everything runs on your machine — no account, no key, no rate limits
|
|
60
|
+
- **🔌 Works Offline**: Clones the official react.dev docs repo once, then searches locally — no network calls at query time
|
|
57
61
|
- **🔍 Semantic Search**: AI-powered search using embeddings for conceptual matches
|
|
58
62
|
- **⚡ Fast Results**: In-memory vector search with hybrid keyword+semantic ranking
|
|
59
63
|
- **📦 Zero Config**: Works with `npx` - no installation needed
|
|
@@ -125,6 +129,7 @@ Get a specific documentation page.
|
|
|
125
129
|
**Parameters**:
|
|
126
130
|
|
|
127
131
|
- `path` (required): Document path (e.g., "learn/hooks/useState")
|
|
132
|
+
- `full` (optional): Return the full raw page instead of the ~1500 char summary (default: false)
|
|
128
133
|
|
|
129
134
|
**Example**:
|
|
130
135
|
|
|
@@ -132,6 +137,13 @@ Get a specific documentation page.
|
|
|
132
137
|
Get the useState documentation
|
|
133
138
|
```
|
|
134
139
|
|
|
140
|
+
**Why `full`?** The default ~1500 char summary is enough for most API references, but it can cut off partway through longer pages — migration guides, upgrade walkthroughs, or anything with many sequential steps. If the summary seems to end mid-thought or you need every step of a guide, ask for the full page:
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
Get the full content of the React 19 upgrade guide, not just the summary
|
|
144
|
+
```
|
|
145
|
+
which calls `get_doc` with `{ "path": "learn/upgrading-to-react-19", "full": true }` and returns the complete raw page instead of the truncated summary.
|
|
146
|
+
|
|
135
147
|
#### `list_sections`
|
|
136
148
|
|
|
137
149
|
List all available documentation sections.
|
|
@@ -146,6 +158,14 @@ What sections are available?
|
|
|
146
158
|
|
|
147
159
|
Pull latest documentation from the Git repository.
|
|
148
160
|
|
|
161
|
+
### CLI
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
npx react-docs-mcp --version # print the installed package version and exit
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
(Version pinning via `--docs-version` is a [react-native-docs-mcp](./packages/react-native-docs-mcp) feature — react.dev has no versioned docs.)
|
|
168
|
+
|
|
149
169
|
**Example**:
|
|
150
170
|
|
|
151
171
|
```
|
package/dist/config.d.ts
CHANGED
|
@@ -1,28 +1,60 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* config.ts
|
|
3
|
-
* Centralize configuration constants
|
|
3
|
+
* Centralize configuration constants, driven by a swappable preset
|
|
4
4
|
*/
|
|
5
|
-
|
|
5
|
+
export interface SearchConfig {
|
|
6
|
+
defaultLimit: number;
|
|
7
|
+
maxLimit: number;
|
|
8
|
+
minScore: number;
|
|
9
|
+
semanticSearchEnabled: boolean;
|
|
10
|
+
semanticMinSimilarity: number;
|
|
11
|
+
hybridKeywordWeight: number;
|
|
12
|
+
hybridSemanticWeight: number;
|
|
13
|
+
}
|
|
14
|
+
export interface DocUrlConfig {
|
|
15
|
+
base: string;
|
|
16
|
+
useFrontmatterId: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface SectionResourceOverride {
|
|
19
|
+
name: string;
|
|
20
|
+
description: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Static values that fully describe a docs source (react.dev, react-native-website, etc.)
|
|
24
|
+
*/
|
|
25
|
+
export interface DocsMcpPreset {
|
|
26
|
+
cacheDirName: string;
|
|
27
|
+
repoFolderName: string;
|
|
6
28
|
repo: {
|
|
7
29
|
url: string;
|
|
8
|
-
localPath: string;
|
|
9
30
|
contentPath: string;
|
|
10
31
|
};
|
|
11
|
-
search:
|
|
12
|
-
defaultLimit: number;
|
|
13
|
-
maxLimit: number;
|
|
14
|
-
minScore: number;
|
|
15
|
-
semanticSearchEnabled: boolean;
|
|
16
|
-
semanticMinSimilarity: number;
|
|
17
|
-
hybridKeywordWeight: number;
|
|
18
|
-
hybridSemanticWeight: number;
|
|
19
|
-
};
|
|
32
|
+
search: SearchConfig;
|
|
20
33
|
server: {
|
|
21
34
|
name: string;
|
|
22
35
|
version: string;
|
|
23
36
|
};
|
|
24
|
-
sections: readonly [
|
|
37
|
+
sections: readonly string[];
|
|
38
|
+
resourceUriScheme: string;
|
|
39
|
+
docsLabel: string;
|
|
40
|
+
searchToolName: string;
|
|
41
|
+
searchToolDescription: string;
|
|
42
|
+
pathExample: string;
|
|
43
|
+
docUrl: DocUrlConfig;
|
|
44
|
+
sectionResourceOverrides?: Partial<Record<string, SectionResourceOverride>>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Resolved runtime config: preset values with repo.localPath derived from the
|
|
48
|
+
* cache dir. Derived from DocsMcpPreset so new preset fields flow through.
|
|
49
|
+
*/
|
|
50
|
+
export type DocsMcpConfig = Omit<DocsMcpPreset, 'cacheDirName' | 'repoFolderName' | 'repo'> & {
|
|
51
|
+
repo: {
|
|
52
|
+
url: string;
|
|
53
|
+
contentPath: string;
|
|
54
|
+
localPath: string;
|
|
55
|
+
};
|
|
25
56
|
};
|
|
26
|
-
|
|
27
|
-
export
|
|
57
|
+
declare let activeConfig: DocsMcpConfig;
|
|
58
|
+
export declare function configure(preset: DocsMcpPreset): void;
|
|
59
|
+
export { activeConfig as default };
|
|
28
60
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAuBH,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,qBAAqB,EAAE,OAAO,CAAC;IAC/B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IAGb,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE;QACJ,GAAG,EAAE,MAAM,CAAC;QACZ,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,YAAY,CAAC;IACrB,wBAAwB,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC,CAAC;CAC7E;AAED;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,EAAE,cAAc,GAAG,gBAAgB,GAAG,MAAM,CAAC,GAAG;IAC5F,IAAI,EAAE;QACJ,GAAG,EAAE,MAAM,CAAC;QACZ,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH,CAAC;AAiBF,QAAA,IAAI,YAAY,EAAE,aAAwC,CAAC;AAE3D,wBAAgB,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAErD;AAGD,OAAO,EAAE,YAAY,IAAI,OAAO,EAAE,CAAC"}
|
package/dist/config.js
CHANGED
|
@@ -1,48 +1,42 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* config.ts
|
|
3
|
-
* Centralize configuration constants
|
|
3
|
+
* Centralize configuration constants, driven by a swappable preset
|
|
4
4
|
*/
|
|
5
5
|
import { homedir } from 'os';
|
|
6
6
|
import { join } from 'path';
|
|
7
|
+
import { reactDocsPreset } from './presets/reactDocs.js';
|
|
7
8
|
// Determine cache directory based on OS
|
|
8
|
-
const getCacheDir = () => {
|
|
9
|
+
const getCacheDir = (cacheDirName) => {
|
|
9
10
|
const platform = process.platform;
|
|
10
11
|
const home = homedir();
|
|
11
12
|
if (platform === 'darwin' || platform === 'linux') {
|
|
12
13
|
// macOS and Linux: use ~/.cache
|
|
13
|
-
return join(home, '.cache',
|
|
14
|
+
return join(home, '.cache', cacheDirName);
|
|
14
15
|
}
|
|
15
16
|
else if (platform === 'win32') {
|
|
16
17
|
// Windows: use %LOCALAPPDATA%
|
|
17
|
-
return join(process.env.LOCALAPPDATA || join(home, 'AppData', 'Local'),
|
|
18
|
+
return join(process.env.LOCALAPPDATA || join(home, 'AppData', 'Local'), cacheDirName);
|
|
18
19
|
}
|
|
19
20
|
// Fallback for other platforms
|
|
20
|
-
return join(home,
|
|
21
|
+
return join(home, `.${cacheDirName}`);
|
|
21
22
|
};
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
name: 'react-docs-mcp',
|
|
42
|
-
version: '1.0.0',
|
|
43
|
-
},
|
|
44
|
-
// Content sections in the repo
|
|
45
|
-
sections: ['learn', 'reference', 'blog', 'community'],
|
|
46
|
-
};
|
|
47
|
-
export default CONFIG;
|
|
23
|
+
function resolve(preset) {
|
|
24
|
+
const { cacheDirName, repoFolderName, repo, ...rest } = preset;
|
|
25
|
+
return {
|
|
26
|
+
...rest,
|
|
27
|
+
repo: {
|
|
28
|
+
url: repo.url,
|
|
29
|
+
contentPath: repo.contentPath,
|
|
30
|
+
localPath: join(getCacheDir(cacheDirName), repoFolderName),
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
// Module-level active config; swappable via configure() before the server starts.
|
|
35
|
+
// Seeded from the react.dev preset (single source of truth — no duplicate defaults).
|
|
36
|
+
let activeConfig = resolve(reactDocsPreset);
|
|
37
|
+
export function configure(preset) {
|
|
38
|
+
activeConfig = resolve(preset);
|
|
39
|
+
}
|
|
40
|
+
// Live binding: consumers importing the default export see updates made by configure().
|
|
41
|
+
export { activeConfig as default };
|
|
48
42
|
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,wCAAwC;AACxC,MAAM,WAAW,GAAG,CAAC,YAAoB,EAAU,EAAE;IACnD,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IAEvB,IAAI,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QAClD,gCAAgC;QAChC,OAAO,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;IAC5C,CAAC;SAAM,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QAChC,8BAA8B;QAC9B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,YAAY,CAAC,CAAC;IACxF,CAAC;IAED,+BAA+B;IAC/B,OAAO,IAAI,CAAC,IAAI,EAAE,IAAI,YAAY,EAAE,CAAC,CAAC;AACxC,CAAC,CAAC;AA6DF,SAAS,OAAO,CAAC,MAAqB;IACpC,MAAM,EAAE,YAAY,EAAE,cAAc,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC;IAE/D,OAAO;QACL,GAAG,IAAI;QACP,IAAI,EAAE;YACJ,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,SAAS,EAAE,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,EAAE,cAAc,CAAC;SAC3D;KACF,CAAC;AACJ,CAAC;AAED,kFAAkF;AAClF,qFAAqF;AACrF,IAAI,YAAY,GAAkB,OAAO,CAAC,eAAe,CAAC,CAAC;AAE3D,MAAM,UAAU,SAAS,CAAC,MAAqB;IAC7C,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AACjC,CAAC;AAED,wFAAwF;AACxF,OAAO,EAAE,YAAY,IAAI,OAAO,EAAE,CAAC"}
|
package/dist/docsManager.d.ts
CHANGED
|
@@ -19,7 +19,10 @@ export declare class DocsManager {
|
|
|
19
19
|
*/
|
|
20
20
|
private checkRepoExists;
|
|
21
21
|
/**
|
|
22
|
-
* Clone the repository
|
|
22
|
+
* Clone the repository.
|
|
23
|
+
* Clones into a temp directory and renames into place so that two
|
|
24
|
+
* processes starting on a fresh machine (e.g. two MCP servers sharing one
|
|
25
|
+
* cache) don't corrupt each other; the loser discards its redundant copy.
|
|
23
26
|
*/
|
|
24
27
|
private cloneRepo;
|
|
25
28
|
/**
|
|
@@ -42,6 +45,13 @@ export declare class DocsManager {
|
|
|
42
45
|
* @returns Array of file paths relative to content root
|
|
43
46
|
*/
|
|
44
47
|
getAllDocs(): Promise<string[]>;
|
|
48
|
+
/**
|
|
49
|
+
* Filter a list of section names down to those whose directory actually
|
|
50
|
+
* exists under the content root. Lets the server avoid advertising
|
|
51
|
+
* sections that are empty in the checked-out docs (e.g. a versioned
|
|
52
|
+
* snapshot that predates a section).
|
|
53
|
+
*/
|
|
54
|
+
getExistingSections(sections: readonly string[]): Promise<string[]>;
|
|
45
55
|
/**
|
|
46
56
|
* Read file content
|
|
47
57
|
* @param relativePath - Path relative to content root
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"docsManager.d.ts","sourceRoot":"","sources":["../src/docsManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,qBAAa,WAAW;IACtB,OAAO,CAAC,GAAG,CAAY;IACvB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,SAAS,CAAoC;;IAQrD;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"docsManager.d.ts","sourceRoot":"","sources":["../src/docsManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,qBAAa,WAAW;IACtB,OAAO,CAAC,GAAG,CAAY;IACvB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,SAAS,CAAoC;;IAQrD;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAuBjC;;OAEG;YACW,eAAe;IAS7B;;;;;OAKG;YACW,SAAS;IAiCvB;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,UAAU,CAAC;IAsBtC;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;IAgCpC;;;;OAIG;IACG,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IA6B1D;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAiBrC;;;;;OAKG;IACG,mBAAmB,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAezE;;;;OAIG;IACG,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAYpD;;;OAGG;IACG,SAAS,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAUxD"}
|
package/dist/docsManager.js
CHANGED
|
@@ -24,13 +24,22 @@ export class DocsManager {
|
|
|
24
24
|
async initialize() {
|
|
25
25
|
const repoExists = await this.checkRepoExists();
|
|
26
26
|
if (!repoExists) {
|
|
27
|
-
console.log('Cloning
|
|
27
|
+
console.log('Cloning documentation repository...');
|
|
28
28
|
await this.cloneRepo();
|
|
29
29
|
console.log('Repository cloned successfully');
|
|
30
30
|
}
|
|
31
31
|
else {
|
|
32
32
|
console.log('Repository already exists');
|
|
33
33
|
}
|
|
34
|
+
// Fail loudly instead of serving an empty index when the configured
|
|
35
|
+
// content path is missing (e.g. an invalid --docs-version value)
|
|
36
|
+
try {
|
|
37
|
+
await fs.access(this.contentPath);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
throw new Error(`Docs content path not found: ${this.contentPath}. ` +
|
|
41
|
+
'The cloned repository has no such folder — if you passed --docs-version, check that this docs version exists.');
|
|
42
|
+
}
|
|
34
43
|
}
|
|
35
44
|
/**
|
|
36
45
|
* Check if repository exists locally
|
|
@@ -45,17 +54,38 @@ export class DocsManager {
|
|
|
45
54
|
}
|
|
46
55
|
}
|
|
47
56
|
/**
|
|
48
|
-
* Clone the repository
|
|
57
|
+
* Clone the repository.
|
|
58
|
+
* Clones into a temp directory and renames into place so that two
|
|
59
|
+
* processes starting on a fresh machine (e.g. two MCP servers sharing one
|
|
60
|
+
* cache) don't corrupt each other; the loser discards its redundant copy.
|
|
49
61
|
*/
|
|
50
62
|
async cloneRepo() {
|
|
63
|
+
const tempPath = `${this.repoPath}.cloning-${process.pid}`;
|
|
51
64
|
try {
|
|
52
65
|
// Ensure parent directory exists
|
|
53
66
|
await fs.mkdir(path.dirname(this.repoPath), { recursive: true });
|
|
54
|
-
await this.git.clone(CONFIG.repo.url,
|
|
67
|
+
await this.git.clone(CONFIG.repo.url, tempPath, {
|
|
55
68
|
'--depth': 1, // Shallow clone for faster download
|
|
56
69
|
});
|
|
70
|
+
try {
|
|
71
|
+
await fs.rename(tempPath, this.repoPath);
|
|
72
|
+
}
|
|
73
|
+
catch (renameError) {
|
|
74
|
+
if (await this.checkRepoExists()) {
|
|
75
|
+
// Another process finished the clone first — ours is redundant
|
|
76
|
+
await fs.rm(tempPath, { recursive: true, force: true });
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
throw renameError;
|
|
80
|
+
}
|
|
57
81
|
}
|
|
58
82
|
catch (error) {
|
|
83
|
+
try {
|
|
84
|
+
await fs.rm(tempPath, { recursive: true, force: true });
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// best-effort cleanup of the temp clone
|
|
88
|
+
}
|
|
59
89
|
throw new Error(`Failed to clone repository: ${error instanceof Error ? error.message : String(error)}`);
|
|
60
90
|
}
|
|
61
91
|
}
|
|
@@ -128,9 +158,10 @@ export class DocsManager {
|
|
|
128
158
|
// Section doesn't exist
|
|
129
159
|
return [];
|
|
130
160
|
}
|
|
131
|
-
const files = await fg('**/*.md', {
|
|
161
|
+
const files = await fg(['**/*.md', '**/*.mdx'], {
|
|
132
162
|
cwd: sectionPath,
|
|
133
163
|
absolute: false,
|
|
164
|
+
ignore: ['**/_*.md', '**/_*.mdx'],
|
|
134
165
|
});
|
|
135
166
|
// Convert to paths relative to content root
|
|
136
167
|
const relativePaths = files.map(file => `${section}/${file}`);
|
|
@@ -146,13 +177,32 @@ export class DocsManager {
|
|
|
146
177
|
if (this.fileCache.has(cacheKey)) {
|
|
147
178
|
return this.fileCache.get(cacheKey);
|
|
148
179
|
}
|
|
149
|
-
const files = await fg('**/*.md', {
|
|
180
|
+
const files = await fg(['**/*.md', '**/*.mdx'], {
|
|
150
181
|
cwd: this.contentPath,
|
|
151
182
|
absolute: false,
|
|
183
|
+
ignore: ['**/_*.md', '**/_*.mdx'],
|
|
152
184
|
});
|
|
153
185
|
this.fileCache.set(cacheKey, files);
|
|
154
186
|
return files;
|
|
155
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Filter a list of section names down to those whose directory actually
|
|
190
|
+
* exists under the content root. Lets the server avoid advertising
|
|
191
|
+
* sections that are empty in the checked-out docs (e.g. a versioned
|
|
192
|
+
* snapshot that predates a section).
|
|
193
|
+
*/
|
|
194
|
+
async getExistingSections(sections) {
|
|
195
|
+
const checks = await Promise.all(sections.map(async (section) => {
|
|
196
|
+
try {
|
|
197
|
+
await fs.access(path.join(this.contentPath, section));
|
|
198
|
+
return section;
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
}));
|
|
204
|
+
return checks.filter((section) => section !== null);
|
|
205
|
+
}
|
|
156
206
|
/**
|
|
157
207
|
* Read file content
|
|
158
208
|
* @param relativePath - Path relative to content root
|
package/dist/docsManager.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"docsManager.js","sourceRoot":"","sources":["../src/docsManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAa,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,WAAW,CAAC;AAC3B,OAAO,MAAM,MAAM,aAAa,CAAC;AAGjC,MAAM,OAAO,WAAW;IACd,GAAG,CAAY;IACf,QAAQ,CAAS;IACjB,WAAW,CAAS;IACpB,SAAS,GAA0B,IAAI,GAAG,EAAE,CAAC;IAErD;QACE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACpD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACrE,IAAI,CAAC,GAAG,GAAG,SAAS,EAAE,CAAC;IACzB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,UAAU;QACd,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;QAEhD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"docsManager.js","sourceRoot":"","sources":["../src/docsManager.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;GAIG"}
|