docusaurus-plugin-mcp-server 0.5.0 → 0.7.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/dist/adapters-entry.d.mts +1 -1
- package/dist/adapters-entry.d.ts +1 -1
- package/dist/adapters-entry.js +253 -121
- package/dist/adapters-entry.js.map +1 -1
- package/dist/adapters-entry.mjs +252 -120
- package/dist/adapters-entry.mjs.map +1 -1
- package/dist/cli/verify.js +250 -119
- package/dist/cli/verify.js.map +1 -1
- package/dist/cli/verify.mjs +250 -119
- package/dist/cli/verify.mjs.map +1 -1
- package/dist/{index-CzA4FjeE.d.mts → index-4g0ZZK3z.d.mts} +33 -0
- package/dist/{index-CzA4FjeE.d.ts → index-4g0ZZK3z.d.ts} +33 -0
- package/dist/index.d.mts +287 -7
- package/dist/index.d.ts +287 -7
- package/dist/index.js +382 -143
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +378 -143
- package/dist/index.mjs.map +1 -1
- package/dist/theme/index.d.mts +2 -1
- package/dist/theme/index.d.ts +2 -1
- package/dist/theme/index.js +313 -158
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +308 -158
- package/dist/theme/index.mjs.map +1 -1
- package/package.json +4 -1
package/dist/cli/verify.mjs
CHANGED
|
@@ -137,19 +137,126 @@ async function importSearchIndex(data) {
|
|
|
137
137
|
}
|
|
138
138
|
return index;
|
|
139
139
|
}
|
|
140
|
+
var FlexSearchProvider = class {
|
|
141
|
+
name = "flexsearch";
|
|
142
|
+
docs = null;
|
|
143
|
+
searchIndex = null;
|
|
144
|
+
ready = false;
|
|
145
|
+
async initialize(_context, initData) {
|
|
146
|
+
if (!initData) {
|
|
147
|
+
throw new Error("[FlexSearch] SearchProviderInitData required for FlexSearch provider");
|
|
148
|
+
}
|
|
149
|
+
if (initData.docs && initData.indexData) {
|
|
150
|
+
this.docs = initData.docs;
|
|
151
|
+
this.searchIndex = await importSearchIndex(initData.indexData);
|
|
152
|
+
this.ready = true;
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (initData.docsPath && initData.indexPath) {
|
|
156
|
+
if (await fs2.pathExists(initData.docsPath)) {
|
|
157
|
+
this.docs = await fs2.readJson(initData.docsPath);
|
|
158
|
+
} else {
|
|
159
|
+
throw new Error(`[FlexSearch] Docs file not found: ${initData.docsPath}`);
|
|
160
|
+
}
|
|
161
|
+
if (await fs2.pathExists(initData.indexPath)) {
|
|
162
|
+
const indexData = await fs2.readJson(initData.indexPath);
|
|
163
|
+
this.searchIndex = await importSearchIndex(indexData);
|
|
164
|
+
} else {
|
|
165
|
+
throw new Error(`[FlexSearch] Search index not found: ${initData.indexPath}`);
|
|
166
|
+
}
|
|
167
|
+
this.ready = true;
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
throw new Error(
|
|
171
|
+
"[FlexSearch] Invalid init data: must provide either file paths (docsPath, indexPath) or pre-loaded data (docs, indexData)"
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
isReady() {
|
|
175
|
+
return this.ready && this.docs !== null && this.searchIndex !== null;
|
|
176
|
+
}
|
|
177
|
+
async search(query, options) {
|
|
178
|
+
if (!this.isReady() || !this.docs || !this.searchIndex) {
|
|
179
|
+
throw new Error("[FlexSearch] Provider not initialized");
|
|
180
|
+
}
|
|
181
|
+
const limit = options?.limit ?? 5;
|
|
182
|
+
return searchIndex(this.searchIndex, this.docs, query, { limit });
|
|
183
|
+
}
|
|
184
|
+
async getDocument(route) {
|
|
185
|
+
if (!this.docs) {
|
|
186
|
+
throw new Error("[FlexSearch] Provider not initialized");
|
|
187
|
+
}
|
|
188
|
+
if (this.docs[route]) {
|
|
189
|
+
return this.docs[route];
|
|
190
|
+
}
|
|
191
|
+
const normalizedRoute = route.startsWith("/") ? route : `/${route}`;
|
|
192
|
+
const withoutSlash = route.startsWith("/") ? route.slice(1) : route;
|
|
193
|
+
return this.docs[normalizedRoute] ?? this.docs[withoutSlash] ?? null;
|
|
194
|
+
}
|
|
195
|
+
async healthCheck() {
|
|
196
|
+
if (!this.isReady()) {
|
|
197
|
+
return { healthy: false, message: "FlexSearch provider not initialized" };
|
|
198
|
+
}
|
|
199
|
+
const docCount = this.docs ? Object.keys(this.docs).length : 0;
|
|
200
|
+
return {
|
|
201
|
+
healthy: true,
|
|
202
|
+
message: `FlexSearch provider ready with ${docCount} documents`
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Get all loaded documents (for compatibility with existing server code)
|
|
207
|
+
*/
|
|
208
|
+
getDocs() {
|
|
209
|
+
return this.docs;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Get the FlexSearch index (for compatibility with existing server code)
|
|
213
|
+
*/
|
|
214
|
+
getSearchIndex() {
|
|
215
|
+
return this.searchIndex;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
140
218
|
|
|
141
|
-
// src/
|
|
142
|
-
function
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
219
|
+
// src/providers/loader.ts
|
|
220
|
+
async function loadSearchProvider(specifier) {
|
|
221
|
+
if (specifier === "flexsearch") {
|
|
222
|
+
return new FlexSearchProvider();
|
|
223
|
+
}
|
|
224
|
+
try {
|
|
225
|
+
const module = await import(specifier);
|
|
226
|
+
const ProviderClass = module.default;
|
|
227
|
+
if (typeof ProviderClass === "function") {
|
|
228
|
+
const instance = new ProviderClass();
|
|
229
|
+
if (!isSearchProvider(instance)) {
|
|
230
|
+
throw new Error(
|
|
231
|
+
`Invalid search provider module "${specifier}": does not implement SearchProvider interface`
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
return instance;
|
|
235
|
+
}
|
|
236
|
+
if (isSearchProvider(ProviderClass)) {
|
|
237
|
+
return ProviderClass;
|
|
238
|
+
}
|
|
239
|
+
throw new Error(
|
|
240
|
+
`Invalid search provider module "${specifier}": must export a default class or SearchProvider instance`
|
|
241
|
+
);
|
|
242
|
+
} catch (error) {
|
|
243
|
+
if (error instanceof Error && error.message.includes("Cannot find module")) {
|
|
244
|
+
throw new Error(
|
|
245
|
+
`Search provider module not found: "${specifier}". Check the path or package name.`
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
throw error;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function isSearchProvider(obj) {
|
|
252
|
+
if (!obj || typeof obj !== "object") {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
const provider = obj;
|
|
256
|
+
return typeof provider.name === "string" && typeof provider.initialize === "function" && typeof provider.isReady === "function" && typeof provider.search === "function";
|
|
152
257
|
}
|
|
258
|
+
|
|
259
|
+
// src/mcp/tools/docs-search.ts
|
|
153
260
|
function formatSearchResults(results, baseUrl) {
|
|
154
261
|
if (results.length === 0) {
|
|
155
262
|
return "No matching documents found.";
|
|
@@ -175,28 +282,6 @@ function formatSearchResults(results, baseUrl) {
|
|
|
175
282
|
}
|
|
176
283
|
|
|
177
284
|
// src/mcp/tools/docs-get-page.ts
|
|
178
|
-
function executeDocsGetPage(params, docs) {
|
|
179
|
-
const { route } = params;
|
|
180
|
-
if (!route || typeof route !== "string") {
|
|
181
|
-
throw new Error("Route parameter is required and must be a string");
|
|
182
|
-
}
|
|
183
|
-
let normalizedRoute = route.trim();
|
|
184
|
-
if (!normalizedRoute.startsWith("/")) {
|
|
185
|
-
normalizedRoute = "/" + normalizedRoute;
|
|
186
|
-
}
|
|
187
|
-
if (normalizedRoute.length > 1 && normalizedRoute.endsWith("/")) {
|
|
188
|
-
normalizedRoute = normalizedRoute.slice(0, -1);
|
|
189
|
-
}
|
|
190
|
-
const doc = docs[normalizedRoute];
|
|
191
|
-
if (!doc) {
|
|
192
|
-
const altRoute = normalizedRoute.slice(1);
|
|
193
|
-
if (docs[altRoute]) {
|
|
194
|
-
return docs[altRoute] ?? null;
|
|
195
|
-
}
|
|
196
|
-
return null;
|
|
197
|
-
}
|
|
198
|
-
return doc;
|
|
199
|
-
}
|
|
200
285
|
function formatPageContent(doc, baseUrl) {
|
|
201
286
|
if (!doc) {
|
|
202
287
|
return "Page not found. Please check the route path and try again.";
|
|
@@ -241,52 +326,6 @@ function extractSection(markdown, headingId, headings) {
|
|
|
241
326
|
}
|
|
242
327
|
|
|
243
328
|
// src/mcp/tools/docs-get-section.ts
|
|
244
|
-
function executeDocsGetSection(params, docs) {
|
|
245
|
-
const { route, headingId } = params;
|
|
246
|
-
if (!route || typeof route !== "string") {
|
|
247
|
-
throw new Error("Route parameter is required and must be a string");
|
|
248
|
-
}
|
|
249
|
-
if (!headingId || typeof headingId !== "string") {
|
|
250
|
-
throw new Error("HeadingId parameter is required and must be a string");
|
|
251
|
-
}
|
|
252
|
-
let normalizedRoute = route.trim();
|
|
253
|
-
if (!normalizedRoute.startsWith("/")) {
|
|
254
|
-
normalizedRoute = "/" + normalizedRoute;
|
|
255
|
-
}
|
|
256
|
-
if (normalizedRoute.length > 1 && normalizedRoute.endsWith("/")) {
|
|
257
|
-
normalizedRoute = normalizedRoute.slice(0, -1);
|
|
258
|
-
}
|
|
259
|
-
const doc = docs[normalizedRoute];
|
|
260
|
-
if (!doc) {
|
|
261
|
-
return {
|
|
262
|
-
content: null,
|
|
263
|
-
doc: null,
|
|
264
|
-
headingText: null,
|
|
265
|
-
availableHeadings: []
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
const availableHeadings = doc.headings.map((h) => ({
|
|
269
|
-
id: h.id,
|
|
270
|
-
text: h.text,
|
|
271
|
-
level: h.level
|
|
272
|
-
}));
|
|
273
|
-
const heading = doc.headings.find((h) => h.id === headingId.trim());
|
|
274
|
-
if (!heading) {
|
|
275
|
-
return {
|
|
276
|
-
content: null,
|
|
277
|
-
doc,
|
|
278
|
-
headingText: null,
|
|
279
|
-
availableHeadings
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
const content = extractSection(doc.markdown, headingId.trim(), doc.headings);
|
|
283
|
-
return {
|
|
284
|
-
content,
|
|
285
|
-
doc,
|
|
286
|
-
headingText: heading.text,
|
|
287
|
-
availableHeadings
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
329
|
function formatSectionContent(result, headingId, baseUrl) {
|
|
291
330
|
if (!result.doc) {
|
|
292
331
|
return "Page not found. Please check the route path and try again.";
|
|
@@ -323,8 +362,7 @@ function isDataConfig(config) {
|
|
|
323
362
|
}
|
|
324
363
|
var McpDocsServer = class {
|
|
325
364
|
config;
|
|
326
|
-
|
|
327
|
-
searchIndex = null;
|
|
365
|
+
searchProvider = null;
|
|
328
366
|
mcpServer;
|
|
329
367
|
initialized = false;
|
|
330
368
|
constructor(config) {
|
|
@@ -357,18 +395,26 @@ var McpDocsServer = class {
|
|
|
357
395
|
},
|
|
358
396
|
async ({ query, limit }) => {
|
|
359
397
|
await this.initialize();
|
|
360
|
-
if (!this.
|
|
398
|
+
if (!this.searchProvider || !this.searchProvider.isReady()) {
|
|
361
399
|
return {
|
|
362
400
|
content: [{ type: "text", text: "Server not initialized. Please try again." }],
|
|
363
401
|
isError: true
|
|
364
402
|
};
|
|
365
403
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
404
|
+
try {
|
|
405
|
+
const results = await this.searchProvider.search(query, { limit });
|
|
406
|
+
return {
|
|
407
|
+
content: [
|
|
408
|
+
{ type: "text", text: formatSearchResults(results, this.config.baseUrl) }
|
|
409
|
+
]
|
|
410
|
+
};
|
|
411
|
+
} catch (error) {
|
|
412
|
+
console.error("[MCP] Search error:", error);
|
|
413
|
+
return {
|
|
414
|
+
content: [{ type: "text", text: `Search error: ${String(error)}` }],
|
|
415
|
+
isError: true
|
|
416
|
+
};
|
|
417
|
+
}
|
|
372
418
|
}
|
|
373
419
|
);
|
|
374
420
|
this.mcpServer.registerTool(
|
|
@@ -381,16 +427,24 @@ var McpDocsServer = class {
|
|
|
381
427
|
},
|
|
382
428
|
async ({ route }) => {
|
|
383
429
|
await this.initialize();
|
|
384
|
-
if (!this.
|
|
430
|
+
if (!this.searchProvider || !this.searchProvider.isReady()) {
|
|
385
431
|
return {
|
|
386
432
|
content: [{ type: "text", text: "Server not initialized. Please try again." }],
|
|
387
433
|
isError: true
|
|
388
434
|
};
|
|
389
435
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
436
|
+
try {
|
|
437
|
+
const doc = await this.getDocument(route);
|
|
438
|
+
return {
|
|
439
|
+
content: [{ type: "text", text: formatPageContent(doc, this.config.baseUrl) }]
|
|
440
|
+
};
|
|
441
|
+
} catch (error) {
|
|
442
|
+
console.error("[MCP] Get page error:", error);
|
|
443
|
+
return {
|
|
444
|
+
content: [{ type: "text", text: `Error getting page: ${String(error)}` }],
|
|
445
|
+
isError: true
|
|
446
|
+
};
|
|
447
|
+
}
|
|
394
448
|
}
|
|
395
449
|
);
|
|
396
450
|
this.mcpServer.registerTool(
|
|
@@ -406,26 +460,95 @@ var McpDocsServer = class {
|
|
|
406
460
|
},
|
|
407
461
|
async ({ route, headingId }) => {
|
|
408
462
|
await this.initialize();
|
|
409
|
-
if (!this.
|
|
463
|
+
if (!this.searchProvider || !this.searchProvider.isReady()) {
|
|
410
464
|
return {
|
|
411
465
|
content: [{ type: "text", text: "Server not initialized. Please try again." }],
|
|
412
466
|
isError: true
|
|
413
467
|
};
|
|
414
468
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
{
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
469
|
+
try {
|
|
470
|
+
const doc = await this.getDocument(route);
|
|
471
|
+
if (!doc) {
|
|
472
|
+
return {
|
|
473
|
+
content: [
|
|
474
|
+
{
|
|
475
|
+
type: "text",
|
|
476
|
+
text: formatSectionContent(
|
|
477
|
+
{ content: null, doc: null, headingText: null, availableHeadings: [] },
|
|
478
|
+
headingId,
|
|
479
|
+
this.config.baseUrl
|
|
480
|
+
)
|
|
481
|
+
}
|
|
482
|
+
]
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
const availableHeadings = doc.headings.map((h) => ({
|
|
486
|
+
id: h.id,
|
|
487
|
+
text: h.text,
|
|
488
|
+
level: h.level
|
|
489
|
+
}));
|
|
490
|
+
const heading = doc.headings.find((h) => h.id === headingId.trim());
|
|
491
|
+
if (!heading) {
|
|
492
|
+
return {
|
|
493
|
+
content: [
|
|
494
|
+
{
|
|
495
|
+
type: "text",
|
|
496
|
+
text: formatSectionContent(
|
|
497
|
+
{ content: null, doc, headingText: null, availableHeadings },
|
|
498
|
+
headingId,
|
|
499
|
+
this.config.baseUrl
|
|
500
|
+
)
|
|
501
|
+
}
|
|
502
|
+
]
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
const sectionContent = extractSection(doc.markdown, headingId.trim(), doc.headings);
|
|
506
|
+
return {
|
|
507
|
+
content: [
|
|
508
|
+
{
|
|
509
|
+
type: "text",
|
|
510
|
+
text: formatSectionContent(
|
|
511
|
+
{ content: sectionContent, doc, headingText: heading.text, availableHeadings },
|
|
512
|
+
headingId,
|
|
513
|
+
this.config.baseUrl
|
|
514
|
+
)
|
|
515
|
+
}
|
|
516
|
+
]
|
|
517
|
+
};
|
|
518
|
+
} catch (error) {
|
|
519
|
+
console.error("[MCP] Get section error:", error);
|
|
520
|
+
return {
|
|
521
|
+
content: [{ type: "text", text: `Error getting section: ${String(error)}` }],
|
|
522
|
+
isError: true
|
|
523
|
+
};
|
|
524
|
+
}
|
|
424
525
|
}
|
|
425
526
|
);
|
|
426
527
|
}
|
|
427
528
|
/**
|
|
428
|
-
*
|
|
529
|
+
* Get a document by route using the search provider
|
|
530
|
+
*/
|
|
531
|
+
async getDocument(route) {
|
|
532
|
+
if (!this.searchProvider) {
|
|
533
|
+
return null;
|
|
534
|
+
}
|
|
535
|
+
if (this.searchProvider.getDocument) {
|
|
536
|
+
return this.searchProvider.getDocument(route);
|
|
537
|
+
}
|
|
538
|
+
if (this.searchProvider instanceof FlexSearchProvider) {
|
|
539
|
+
const docs = this.searchProvider.getDocs();
|
|
540
|
+
if (!docs) return null;
|
|
541
|
+
if (docs[route]) {
|
|
542
|
+
return docs[route];
|
|
543
|
+
}
|
|
544
|
+
const normalizedRoute = route.startsWith("/") ? route : `/${route}`;
|
|
545
|
+
const withoutSlash = route.startsWith("/") ? route.slice(1) : route;
|
|
546
|
+
return docs[normalizedRoute] ?? docs[withoutSlash] ?? null;
|
|
547
|
+
}
|
|
548
|
+
return null;
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Load docs and search index using the configured search provider
|
|
429
552
|
*
|
|
430
553
|
* For file-based config: reads from disk
|
|
431
554
|
* For data config: uses pre-loaded data directly
|
|
@@ -435,24 +558,26 @@ var McpDocsServer = class {
|
|
|
435
558
|
return;
|
|
436
559
|
}
|
|
437
560
|
try {
|
|
561
|
+
const searchSpecifier = this.config.search ?? "flexsearch";
|
|
562
|
+
this.searchProvider = await loadSearchProvider(searchSpecifier);
|
|
563
|
+
const providerContext = {
|
|
564
|
+
baseUrl: this.config.baseUrl ?? "",
|
|
565
|
+
serverName: this.config.name,
|
|
566
|
+
serverVersion: this.config.version ?? "1.0.0",
|
|
567
|
+
outputDir: ""
|
|
568
|
+
// Not relevant for runtime
|
|
569
|
+
};
|
|
570
|
+
const initData = {};
|
|
438
571
|
if (isDataConfig(this.config)) {
|
|
439
|
-
|
|
440
|
-
|
|
572
|
+
initData.docs = this.config.docs;
|
|
573
|
+
initData.indexData = this.config.searchIndexData;
|
|
441
574
|
} else if (isFileConfig(this.config)) {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
} else {
|
|
445
|
-
throw new Error(`Docs file not found: ${this.config.docsPath}`);
|
|
446
|
-
}
|
|
447
|
-
if (await fs2.pathExists(this.config.indexPath)) {
|
|
448
|
-
const indexData = await fs2.readJson(this.config.indexPath);
|
|
449
|
-
this.searchIndex = await importSearchIndex(indexData);
|
|
450
|
-
} else {
|
|
451
|
-
throw new Error(`Search index not found: ${this.config.indexPath}`);
|
|
452
|
-
}
|
|
575
|
+
initData.docsPath = this.config.docsPath;
|
|
576
|
+
initData.indexPath = this.config.indexPath;
|
|
453
577
|
} else {
|
|
454
578
|
throw new Error("Invalid server config: must provide either file paths or pre-loaded data");
|
|
455
579
|
}
|
|
580
|
+
await this.searchProvider.initialize(providerContext, initData);
|
|
456
581
|
this.initialized = true;
|
|
457
582
|
} catch (error) {
|
|
458
583
|
console.error("[MCP] Failed to initialize:", error);
|
|
@@ -513,12 +638,18 @@ var McpDocsServer = class {
|
|
|
513
638
|
* Useful for health checks and debugging
|
|
514
639
|
*/
|
|
515
640
|
async getStatus() {
|
|
641
|
+
let docCount = 0;
|
|
642
|
+
if (this.searchProvider instanceof FlexSearchProvider) {
|
|
643
|
+
const docs = this.searchProvider.getDocs();
|
|
644
|
+
docCount = docs ? Object.keys(docs).length : 0;
|
|
645
|
+
}
|
|
516
646
|
return {
|
|
517
647
|
name: this.config.name,
|
|
518
648
|
version: this.config.version ?? "1.0.0",
|
|
519
649
|
initialized: this.initialized,
|
|
520
|
-
docCount
|
|
521
|
-
baseUrl: this.config.baseUrl
|
|
650
|
+
docCount,
|
|
651
|
+
baseUrl: this.config.baseUrl,
|
|
652
|
+
searchProvider: this.searchProvider?.name
|
|
522
653
|
};
|
|
523
654
|
}
|
|
524
655
|
/**
|