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/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var path2 = require('path');
5
+ var path = require('path');
6
6
  var fs3 = require('fs-extra');
7
7
  var pMap = require('p-map');
8
8
  var unified = require('unified');
@@ -20,7 +20,7 @@ var zod = require('zod');
20
20
 
21
21
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
22
22
 
23
- var path2__default = /*#__PURE__*/_interopDefault(path2);
23
+ var path__default = /*#__PURE__*/_interopDefault(path);
24
24
  var fs3__default = /*#__PURE__*/_interopDefault(fs3);
25
25
  var pMap__default = /*#__PURE__*/_interopDefault(pMap);
26
26
  var rehypeParse__default = /*#__PURE__*/_interopDefault(rehypeParse);
@@ -49,7 +49,10 @@ var DEFAULT_OPTIONS = {
49
49
  name: "docs-mcp-server",
50
50
  version: "1.0.0"
51
51
  },
52
- excludeRoutes: ["/404*", "/search*"]
52
+ excludeRoutes: ["/404*", "/search*"],
53
+ indexers: void 0,
54
+ // Default: ['flexsearch'] applied at runtime
55
+ search: "flexsearch"
53
56
  };
54
57
  function filterRoutes(routes, excludePatterns) {
55
58
  return routes.filter((route) => {
@@ -65,15 +68,15 @@ async function discoverHtmlFiles(outDir) {
65
68
  async function scanDirectory(dir) {
66
69
  const entries = await fs3__default.default.readdir(dir, { withFileTypes: true });
67
70
  for (const entry of entries) {
68
- const fullPath = path2__default.default.join(dir, entry.name);
71
+ const fullPath = path__default.default.join(dir, entry.name);
69
72
  if (entry.isDirectory()) {
70
73
  if (["assets", "img", "static"].includes(entry.name)) {
71
74
  continue;
72
75
  }
73
76
  await scanDirectory(fullPath);
74
77
  } else if (entry.name === "index.html") {
75
- const relativePath = path2__default.default.relative(outDir, fullPath);
76
- let routePath = "/" + path2__default.default.dirname(relativePath).replace(/\\/g, "/");
78
+ const relativePath = path__default.default.relative(outDir, fullPath);
79
+ let routePath = "/" + path__default.default.dirname(relativePath).replace(/\\/g, "/");
77
80
  if (routePath === "/.") {
78
81
  routePath = "/";
79
82
  }
@@ -504,6 +507,201 @@ async function importSearchIndex(data) {
504
507
  return index;
505
508
  }
506
509
 
510
+ // src/providers/indexers/flexsearch-indexer.ts
511
+ var FlexSearchIndexer = class {
512
+ name = "flexsearch";
513
+ docsIndex = {};
514
+ exportedIndex = null;
515
+ docCount = 0;
516
+ /**
517
+ * FlexSearch indexer always runs by default.
518
+ * It respects the indexers configuration - if not included, it won't run.
519
+ */
520
+ shouldRun() {
521
+ return true;
522
+ }
523
+ async initialize(_context) {
524
+ this.docsIndex = {};
525
+ this.exportedIndex = null;
526
+ this.docCount = 0;
527
+ }
528
+ async indexDocuments(docs) {
529
+ this.docCount = docs.length;
530
+ for (const doc of docs) {
531
+ this.docsIndex[doc.route] = doc;
532
+ }
533
+ console.log("[FlexSearch] Building search index...");
534
+ const searchIndex2 = buildSearchIndex(docs);
535
+ this.exportedIndex = await exportSearchIndex(searchIndex2);
536
+ console.log(`[FlexSearch] Indexed ${this.docCount} documents`);
537
+ }
538
+ async finalize() {
539
+ const artifacts = /* @__PURE__ */ new Map();
540
+ artifacts.set("docs.json", this.docsIndex);
541
+ artifacts.set("search-index.json", this.exportedIndex);
542
+ return artifacts;
543
+ }
544
+ async getManifestData() {
545
+ return {
546
+ searchEngine: "flexsearch"
547
+ };
548
+ }
549
+ };
550
+ var FlexSearchProvider = class {
551
+ name = "flexsearch";
552
+ docs = null;
553
+ searchIndex = null;
554
+ ready = false;
555
+ async initialize(_context, initData) {
556
+ if (!initData) {
557
+ throw new Error("[FlexSearch] SearchProviderInitData required for FlexSearch provider");
558
+ }
559
+ if (initData.docs && initData.indexData) {
560
+ this.docs = initData.docs;
561
+ this.searchIndex = await importSearchIndex(initData.indexData);
562
+ this.ready = true;
563
+ return;
564
+ }
565
+ if (initData.docsPath && initData.indexPath) {
566
+ if (await fs3__default.default.pathExists(initData.docsPath)) {
567
+ this.docs = await fs3__default.default.readJson(initData.docsPath);
568
+ } else {
569
+ throw new Error(`[FlexSearch] Docs file not found: ${initData.docsPath}`);
570
+ }
571
+ if (await fs3__default.default.pathExists(initData.indexPath)) {
572
+ const indexData = await fs3__default.default.readJson(initData.indexPath);
573
+ this.searchIndex = await importSearchIndex(indexData);
574
+ } else {
575
+ throw new Error(`[FlexSearch] Search index not found: ${initData.indexPath}`);
576
+ }
577
+ this.ready = true;
578
+ return;
579
+ }
580
+ throw new Error(
581
+ "[FlexSearch] Invalid init data: must provide either file paths (docsPath, indexPath) or pre-loaded data (docs, indexData)"
582
+ );
583
+ }
584
+ isReady() {
585
+ return this.ready && this.docs !== null && this.searchIndex !== null;
586
+ }
587
+ async search(query, options) {
588
+ if (!this.isReady() || !this.docs || !this.searchIndex) {
589
+ throw new Error("[FlexSearch] Provider not initialized");
590
+ }
591
+ const limit = options?.limit ?? 5;
592
+ return searchIndex(this.searchIndex, this.docs, query, { limit });
593
+ }
594
+ async getDocument(route) {
595
+ if (!this.docs) {
596
+ throw new Error("[FlexSearch] Provider not initialized");
597
+ }
598
+ if (this.docs[route]) {
599
+ return this.docs[route];
600
+ }
601
+ const normalizedRoute = route.startsWith("/") ? route : `/${route}`;
602
+ const withoutSlash = route.startsWith("/") ? route.slice(1) : route;
603
+ return this.docs[normalizedRoute] ?? this.docs[withoutSlash] ?? null;
604
+ }
605
+ async healthCheck() {
606
+ if (!this.isReady()) {
607
+ return { healthy: false, message: "FlexSearch provider not initialized" };
608
+ }
609
+ const docCount = this.docs ? Object.keys(this.docs).length : 0;
610
+ return {
611
+ healthy: true,
612
+ message: `FlexSearch provider ready with ${docCount} documents`
613
+ };
614
+ }
615
+ /**
616
+ * Get all loaded documents (for compatibility with existing server code)
617
+ */
618
+ getDocs() {
619
+ return this.docs;
620
+ }
621
+ /**
622
+ * Get the FlexSearch index (for compatibility with existing server code)
623
+ */
624
+ getSearchIndex() {
625
+ return this.searchIndex;
626
+ }
627
+ };
628
+
629
+ // src/providers/loader.ts
630
+ async function loadIndexer(specifier) {
631
+ if (specifier === "flexsearch") {
632
+ return new FlexSearchIndexer();
633
+ }
634
+ try {
635
+ const module = await import(specifier);
636
+ const IndexerClass = module.default;
637
+ if (typeof IndexerClass === "function") {
638
+ const instance = new IndexerClass();
639
+ if (!isContentIndexer(instance)) {
640
+ throw new Error(
641
+ `Invalid indexer module "${specifier}": does not implement ContentIndexer interface`
642
+ );
643
+ }
644
+ return instance;
645
+ }
646
+ if (isContentIndexer(IndexerClass)) {
647
+ return IndexerClass;
648
+ }
649
+ throw new Error(
650
+ `Invalid indexer module "${specifier}": must export a default class or ContentIndexer instance`
651
+ );
652
+ } catch (error) {
653
+ if (error instanceof Error && error.message.includes("Cannot find module")) {
654
+ throw new Error(`Indexer module not found: "${specifier}". Check the path or package name.`);
655
+ }
656
+ throw error;
657
+ }
658
+ }
659
+ async function loadSearchProvider(specifier) {
660
+ if (specifier === "flexsearch") {
661
+ return new FlexSearchProvider();
662
+ }
663
+ try {
664
+ const module = await import(specifier);
665
+ const ProviderClass = module.default;
666
+ if (typeof ProviderClass === "function") {
667
+ const instance = new ProviderClass();
668
+ if (!isSearchProvider(instance)) {
669
+ throw new Error(
670
+ `Invalid search provider module "${specifier}": does not implement SearchProvider interface`
671
+ );
672
+ }
673
+ return instance;
674
+ }
675
+ if (isSearchProvider(ProviderClass)) {
676
+ return ProviderClass;
677
+ }
678
+ throw new Error(
679
+ `Invalid search provider module "${specifier}": must export a default class or SearchProvider instance`
680
+ );
681
+ } catch (error) {
682
+ if (error instanceof Error && error.message.includes("Cannot find module")) {
683
+ throw new Error(
684
+ `Search provider module not found: "${specifier}". Check the path or package name.`
685
+ );
686
+ }
687
+ throw error;
688
+ }
689
+ }
690
+ function isContentIndexer(obj) {
691
+ if (!obj || typeof obj !== "object") {
692
+ return false;
693
+ }
694
+ const indexer = obj;
695
+ return typeof indexer.name === "string" && typeof indexer.initialize === "function" && typeof indexer.indexDocuments === "function" && typeof indexer.finalize === "function";
696
+ }
697
+ function isSearchProvider(obj) {
698
+ if (!obj || typeof obj !== "object") {
699
+ return false;
700
+ }
701
+ const provider = obj;
702
+ return typeof provider.name === "string" && typeof provider.initialize === "function" && typeof provider.isReady === "function" && typeof provider.search === "function";
703
+ }
704
+
507
705
  // src/plugin/docusaurus-plugin.ts
508
706
  function resolveOptions(options) {
509
707
  return {
@@ -560,6 +758,10 @@ function mcpServerPlugin(context, options) {
560
758
  async postBuild({ outDir }) {
561
759
  console.log("[MCP] Starting MCP artifact generation...");
562
760
  const startTime = Date.now();
761
+ if (resolvedOptions.indexers === false) {
762
+ console.log("[MCP] Indexing disabled, skipping artifact generation");
763
+ return;
764
+ }
563
765
  const routes = await collectRoutes(outDir, resolvedOptions.excludeRoutes);
564
766
  console.log(`[MCP] Found ${routes.length} routes to process`);
565
767
  if (routes.length === 0) {
@@ -584,27 +786,47 @@ function mcpServerPlugin(context, options) {
584
786
  console.warn("[MCP] No valid documents to index");
585
787
  return;
586
788
  }
587
- const docsIndex = {};
588
- for (const doc of validDocs) {
589
- docsIndex[doc.route] = doc;
590
- }
591
- console.log("[MCP] Building search index...");
592
- const searchIndex2 = buildSearchIndex(validDocs);
593
- const exportedIndex = await exportSearchIndex(searchIndex2);
594
- const manifest = {
595
- version: resolvedOptions.server.version,
596
- buildTime: (/* @__PURE__ */ new Date()).toISOString(),
597
- docCount: validDocs.length,
789
+ const mcpOutputDir = path__default.default.join(outDir, resolvedOptions.outputDir);
790
+ const providerContext = {
791
+ baseUrl: context.siteConfig.url,
598
792
  serverName: resolvedOptions.server.name,
599
- baseUrl: context.siteConfig.url
793
+ serverVersion: resolvedOptions.server.version,
794
+ outputDir: mcpOutputDir
600
795
  };
601
- const mcpOutputDir = path2__default.default.join(outDir, resolvedOptions.outputDir);
796
+ const indexerSpecs = resolvedOptions.indexers ?? ["flexsearch"];
602
797
  await fs3__default.default.ensureDir(mcpOutputDir);
603
- await Promise.all([
604
- fs3__default.default.writeJson(path2__default.default.join(mcpOutputDir, "docs.json"), docsIndex, { spaces: 0 }),
605
- fs3__default.default.writeJson(path2__default.default.join(mcpOutputDir, "search-index.json"), exportedIndex, { spaces: 0 }),
606
- fs3__default.default.writeJson(path2__default.default.join(mcpOutputDir, "manifest.json"), manifest, { spaces: 2 })
607
- ]);
798
+ const indexerNames = [];
799
+ for (const indexerSpec of indexerSpecs) {
800
+ try {
801
+ const indexer = await loadIndexer(indexerSpec);
802
+ if (indexer.shouldRun && !indexer.shouldRun()) {
803
+ console.log(`[MCP] Skipping indexer: ${indexer.name}`);
804
+ continue;
805
+ }
806
+ console.log(`[MCP] Running indexer: ${indexer.name}`);
807
+ await indexer.initialize(providerContext);
808
+ await indexer.indexDocuments(validDocs);
809
+ const artifacts = await indexer.finalize();
810
+ for (const [filename, content] of artifacts) {
811
+ await fs3__default.default.writeJson(path__default.default.join(mcpOutputDir, filename), content, { spaces: 0 });
812
+ }
813
+ indexerNames.push(indexer.name);
814
+ } catch (error) {
815
+ console.error(`[MCP] Error running indexer "${indexerSpec}":`, error);
816
+ throw error;
817
+ }
818
+ }
819
+ if (indexerNames.length > 0) {
820
+ const manifest = {
821
+ version: resolvedOptions.server.version,
822
+ buildTime: (/* @__PURE__ */ new Date()).toISOString(),
823
+ docCount: validDocs.length,
824
+ serverName: resolvedOptions.server.name,
825
+ baseUrl: context.siteConfig.url,
826
+ indexers: indexerNames
827
+ };
828
+ await fs3__default.default.writeJson(path__default.default.join(mcpOutputDir, "manifest.json"), manifest, { spaces: 2 });
829
+ }
608
830
  const elapsed = Date.now() - startTime;
609
831
  console.log(`[MCP] Artifacts written to ${mcpOutputDir}`);
610
832
  console.log(`[MCP] Generation complete in ${elapsed}ms`);
@@ -632,17 +854,6 @@ var docsSearchTool = {
632
854
  required: ["query"]
633
855
  }
634
856
  };
635
- function executeDocsSearch(params, index, docs) {
636
- const { query, limit = 5 } = params;
637
- if (!query || typeof query !== "string" || query.trim().length === 0) {
638
- throw new Error("Query parameter is required and must be a non-empty string");
639
- }
640
- const effectiveLimit = Math.min(Math.max(1, limit), 20);
641
- const results = searchIndex(index, docs, query.trim(), {
642
- limit: effectiveLimit
643
- });
644
- return results;
645
- }
646
857
  function formatSearchResults(results, baseUrl) {
647
858
  if (results.length === 0) {
648
859
  return "No matching documents found.";
@@ -682,28 +893,6 @@ var docsGetPageTool = {
682
893
  required: ["route"]
683
894
  }
684
895
  };
685
- function executeDocsGetPage(params, docs) {
686
- const { route } = params;
687
- if (!route || typeof route !== "string") {
688
- throw new Error("Route parameter is required and must be a string");
689
- }
690
- let normalizedRoute = route.trim();
691
- if (!normalizedRoute.startsWith("/")) {
692
- normalizedRoute = "/" + normalizedRoute;
693
- }
694
- if (normalizedRoute.length > 1 && normalizedRoute.endsWith("/")) {
695
- normalizedRoute = normalizedRoute.slice(0, -1);
696
- }
697
- const doc = docs[normalizedRoute];
698
- if (!doc) {
699
- const altRoute = normalizedRoute.slice(1);
700
- if (docs[altRoute]) {
701
- return docs[altRoute] ?? null;
702
- }
703
- return null;
704
- }
705
- return doc;
706
- }
707
896
  function formatPageContent(doc, baseUrl) {
708
897
  if (!doc) {
709
898
  return "Page not found. Please check the route path and try again.";
@@ -757,52 +946,6 @@ var docsGetSectionTool = {
757
946
  required: ["route", "headingId"]
758
947
  }
759
948
  };
760
- function executeDocsGetSection(params, docs) {
761
- const { route, headingId } = params;
762
- if (!route || typeof route !== "string") {
763
- throw new Error("Route parameter is required and must be a string");
764
- }
765
- if (!headingId || typeof headingId !== "string") {
766
- throw new Error("HeadingId parameter is required and must be a string");
767
- }
768
- let normalizedRoute = route.trim();
769
- if (!normalizedRoute.startsWith("/")) {
770
- normalizedRoute = "/" + normalizedRoute;
771
- }
772
- if (normalizedRoute.length > 1 && normalizedRoute.endsWith("/")) {
773
- normalizedRoute = normalizedRoute.slice(0, -1);
774
- }
775
- const doc = docs[normalizedRoute];
776
- if (!doc) {
777
- return {
778
- content: null,
779
- doc: null,
780
- headingText: null,
781
- availableHeadings: []
782
- };
783
- }
784
- const availableHeadings = doc.headings.map((h) => ({
785
- id: h.id,
786
- text: h.text,
787
- level: h.level
788
- }));
789
- const heading = doc.headings.find((h) => h.id === headingId.trim());
790
- if (!heading) {
791
- return {
792
- content: null,
793
- doc,
794
- headingText: null,
795
- availableHeadings
796
- };
797
- }
798
- const content = extractSection(doc.markdown, headingId.trim(), doc.headings);
799
- return {
800
- content,
801
- doc,
802
- headingText: heading.text,
803
- availableHeadings
804
- };
805
- }
806
949
  function formatSectionContent(result, headingId, baseUrl) {
807
950
  if (!result.doc) {
808
951
  return "Page not found. Please check the route path and try again.";
@@ -839,8 +982,7 @@ function isDataConfig(config) {
839
982
  }
840
983
  var McpDocsServer = class {
841
984
  config;
842
- docs = null;
843
- searchIndex = null;
985
+ searchProvider = null;
844
986
  mcpServer;
845
987
  initialized = false;
846
988
  constructor(config) {
@@ -873,18 +1015,26 @@ var McpDocsServer = class {
873
1015
  },
874
1016
  async ({ query, limit }) => {
875
1017
  await this.initialize();
876
- if (!this.docs || !this.searchIndex) {
1018
+ if (!this.searchProvider || !this.searchProvider.isReady()) {
877
1019
  return {
878
1020
  content: [{ type: "text", text: "Server not initialized. Please try again." }],
879
1021
  isError: true
880
1022
  };
881
1023
  }
882
- const results = executeDocsSearch({ query, limit }, this.searchIndex, this.docs);
883
- return {
884
- content: [
885
- { type: "text", text: formatSearchResults(results, this.config.baseUrl) }
886
- ]
887
- };
1024
+ try {
1025
+ const results = await this.searchProvider.search(query, { limit });
1026
+ return {
1027
+ content: [
1028
+ { type: "text", text: formatSearchResults(results, this.config.baseUrl) }
1029
+ ]
1030
+ };
1031
+ } catch (error) {
1032
+ console.error("[MCP] Search error:", error);
1033
+ return {
1034
+ content: [{ type: "text", text: `Search error: ${String(error)}` }],
1035
+ isError: true
1036
+ };
1037
+ }
888
1038
  }
889
1039
  );
890
1040
  this.mcpServer.registerTool(
@@ -897,16 +1047,24 @@ var McpDocsServer = class {
897
1047
  },
898
1048
  async ({ route }) => {
899
1049
  await this.initialize();
900
- if (!this.docs) {
1050
+ if (!this.searchProvider || !this.searchProvider.isReady()) {
901
1051
  return {
902
1052
  content: [{ type: "text", text: "Server not initialized. Please try again." }],
903
1053
  isError: true
904
1054
  };
905
1055
  }
906
- const doc = executeDocsGetPage({ route }, this.docs);
907
- return {
908
- content: [{ type: "text", text: formatPageContent(doc, this.config.baseUrl) }]
909
- };
1056
+ try {
1057
+ const doc = await this.getDocument(route);
1058
+ return {
1059
+ content: [{ type: "text", text: formatPageContent(doc, this.config.baseUrl) }]
1060
+ };
1061
+ } catch (error) {
1062
+ console.error("[MCP] Get page error:", error);
1063
+ return {
1064
+ content: [{ type: "text", text: `Error getting page: ${String(error)}` }],
1065
+ isError: true
1066
+ };
1067
+ }
910
1068
  }
911
1069
  );
912
1070
  this.mcpServer.registerTool(
@@ -922,26 +1080,95 @@ var McpDocsServer = class {
922
1080
  },
923
1081
  async ({ route, headingId }) => {
924
1082
  await this.initialize();
925
- if (!this.docs) {
1083
+ if (!this.searchProvider || !this.searchProvider.isReady()) {
926
1084
  return {
927
1085
  content: [{ type: "text", text: "Server not initialized. Please try again." }],
928
1086
  isError: true
929
1087
  };
930
1088
  }
931
- const result = executeDocsGetSection({ route, headingId }, this.docs);
932
- return {
933
- content: [
934
- {
935
- type: "text",
936
- text: formatSectionContent(result, headingId, this.config.baseUrl)
937
- }
938
- ]
939
- };
1089
+ try {
1090
+ const doc = await this.getDocument(route);
1091
+ if (!doc) {
1092
+ return {
1093
+ content: [
1094
+ {
1095
+ type: "text",
1096
+ text: formatSectionContent(
1097
+ { content: null, doc: null, headingText: null, availableHeadings: [] },
1098
+ headingId,
1099
+ this.config.baseUrl
1100
+ )
1101
+ }
1102
+ ]
1103
+ };
1104
+ }
1105
+ const availableHeadings = doc.headings.map((h) => ({
1106
+ id: h.id,
1107
+ text: h.text,
1108
+ level: h.level
1109
+ }));
1110
+ const heading = doc.headings.find((h) => h.id === headingId.trim());
1111
+ if (!heading) {
1112
+ return {
1113
+ content: [
1114
+ {
1115
+ type: "text",
1116
+ text: formatSectionContent(
1117
+ { content: null, doc, headingText: null, availableHeadings },
1118
+ headingId,
1119
+ this.config.baseUrl
1120
+ )
1121
+ }
1122
+ ]
1123
+ };
1124
+ }
1125
+ const sectionContent = extractSection(doc.markdown, headingId.trim(), doc.headings);
1126
+ return {
1127
+ content: [
1128
+ {
1129
+ type: "text",
1130
+ text: formatSectionContent(
1131
+ { content: sectionContent, doc, headingText: heading.text, availableHeadings },
1132
+ headingId,
1133
+ this.config.baseUrl
1134
+ )
1135
+ }
1136
+ ]
1137
+ };
1138
+ } catch (error) {
1139
+ console.error("[MCP] Get section error:", error);
1140
+ return {
1141
+ content: [{ type: "text", text: `Error getting section: ${String(error)}` }],
1142
+ isError: true
1143
+ };
1144
+ }
940
1145
  }
941
1146
  );
942
1147
  }
943
1148
  /**
944
- * Load docs and search index
1149
+ * Get a document by route using the search provider
1150
+ */
1151
+ async getDocument(route) {
1152
+ if (!this.searchProvider) {
1153
+ return null;
1154
+ }
1155
+ if (this.searchProvider.getDocument) {
1156
+ return this.searchProvider.getDocument(route);
1157
+ }
1158
+ if (this.searchProvider instanceof FlexSearchProvider) {
1159
+ const docs = this.searchProvider.getDocs();
1160
+ if (!docs) return null;
1161
+ if (docs[route]) {
1162
+ return docs[route];
1163
+ }
1164
+ const normalizedRoute = route.startsWith("/") ? route : `/${route}`;
1165
+ const withoutSlash = route.startsWith("/") ? route.slice(1) : route;
1166
+ return docs[normalizedRoute] ?? docs[withoutSlash] ?? null;
1167
+ }
1168
+ return null;
1169
+ }
1170
+ /**
1171
+ * Load docs and search index using the configured search provider
945
1172
  *
946
1173
  * For file-based config: reads from disk
947
1174
  * For data config: uses pre-loaded data directly
@@ -951,24 +1178,26 @@ var McpDocsServer = class {
951
1178
  return;
952
1179
  }
953
1180
  try {
1181
+ const searchSpecifier = this.config.search ?? "flexsearch";
1182
+ this.searchProvider = await loadSearchProvider(searchSpecifier);
1183
+ const providerContext = {
1184
+ baseUrl: this.config.baseUrl ?? "",
1185
+ serverName: this.config.name,
1186
+ serverVersion: this.config.version ?? "1.0.0",
1187
+ outputDir: ""
1188
+ // Not relevant for runtime
1189
+ };
1190
+ const initData = {};
954
1191
  if (isDataConfig(this.config)) {
955
- this.docs = this.config.docs;
956
- this.searchIndex = await importSearchIndex(this.config.searchIndexData);
1192
+ initData.docs = this.config.docs;
1193
+ initData.indexData = this.config.searchIndexData;
957
1194
  } else if (isFileConfig(this.config)) {
958
- if (await fs3__default.default.pathExists(this.config.docsPath)) {
959
- this.docs = await fs3__default.default.readJson(this.config.docsPath);
960
- } else {
961
- throw new Error(`Docs file not found: ${this.config.docsPath}`);
962
- }
963
- if (await fs3__default.default.pathExists(this.config.indexPath)) {
964
- const indexData = await fs3__default.default.readJson(this.config.indexPath);
965
- this.searchIndex = await importSearchIndex(indexData);
966
- } else {
967
- throw new Error(`Search index not found: ${this.config.indexPath}`);
968
- }
1195
+ initData.docsPath = this.config.docsPath;
1196
+ initData.indexPath = this.config.indexPath;
969
1197
  } else {
970
1198
  throw new Error("Invalid server config: must provide either file paths or pre-loaded data");
971
1199
  }
1200
+ await this.searchProvider.initialize(providerContext, initData);
972
1201
  this.initialized = true;
973
1202
  } catch (error) {
974
1203
  console.error("[MCP] Failed to initialize:", error);
@@ -1029,12 +1258,18 @@ var McpDocsServer = class {
1029
1258
  * Useful for health checks and debugging
1030
1259
  */
1031
1260
  async getStatus() {
1261
+ let docCount = 0;
1262
+ if (this.searchProvider instanceof FlexSearchProvider) {
1263
+ const docs = this.searchProvider.getDocs();
1264
+ docCount = docs ? Object.keys(docs).length : 0;
1265
+ }
1032
1266
  return {
1033
1267
  name: this.config.name,
1034
1268
  version: this.config.version ?? "1.0.0",
1035
1269
  initialized: this.initialized,
1036
- docCount: this.docs ? Object.keys(this.docs).length : 0,
1037
- baseUrl: this.config.baseUrl
1270
+ docCount,
1271
+ baseUrl: this.config.baseUrl,
1272
+ searchProvider: this.searchProvider?.name
1038
1273
  };
1039
1274
  }
1040
1275
  /**
@@ -1048,6 +1283,8 @@ var McpDocsServer = class {
1048
1283
  };
1049
1284
 
1050
1285
  exports.DEFAULT_OPTIONS = DEFAULT_OPTIONS;
1286
+ exports.FlexSearchIndexer = FlexSearchIndexer;
1287
+ exports.FlexSearchProvider = FlexSearchProvider;
1051
1288
  exports.McpDocsServer = McpDocsServer;
1052
1289
  exports.buildSearchIndex = buildSearchIndex;
1053
1290
  exports.collectRoutes = collectRoutes;
@@ -1062,6 +1299,8 @@ exports.extractHeadingsFromMarkdown = extractHeadingsFromMarkdown;
1062
1299
  exports.extractSection = extractSection;
1063
1300
  exports.htmlToMarkdown = htmlToMarkdown;
1064
1301
  exports.importSearchIndex = importSearchIndex;
1302
+ exports.loadIndexer = loadIndexer;
1303
+ exports.loadSearchProvider = loadSearchProvider;
1065
1304
  exports.mcpServerPlugin = mcpServerPlugin;
1066
1305
  exports.parseHtml = parseHtml;
1067
1306
  exports.parseHtmlFile = parseHtmlFile;