simple-dynamsoft-mcp 6.3.0 → 7.0.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.
Files changed (47) hide show
  1. package/.env.example +35 -9
  2. package/README.md +156 -497
  3. package/package.json +13 -7
  4. package/scripts/prebuild-rag-index.mjs +1 -1
  5. package/scripts/run-gemini-tests.mjs +1 -1
  6. package/scripts/sync-submodules.mjs +1 -1
  7. package/scripts/verify-doc-resources.mjs +79 -0
  8. package/src/data/bootstrap.js +475 -0
  9. package/src/data/download-utils.js +99 -0
  10. package/src/data/hydration-mode.js +15 -0
  11. package/src/data/hydration-policy.js +39 -0
  12. package/src/data/repo-map.js +149 -0
  13. package/src/{data-root.js → data/root.js} +1 -1
  14. package/src/{submodule-sync.js → data/submodule-sync.js} +1 -1
  15. package/src/index.js +49 -1499
  16. package/src/observability/logging.js +51 -0
  17. package/src/rag/config.js +96 -0
  18. package/src/rag/index.js +266 -0
  19. package/src/rag/lexical-provider.js +170 -0
  20. package/src/rag/logger.js +46 -0
  21. package/src/rag/profile-config.js +48 -0
  22. package/src/rag/providers.js +585 -0
  23. package/src/rag/search-utils.js +166 -0
  24. package/src/rag/vector-cache.js +323 -0
  25. package/src/server/create-server.js +168 -0
  26. package/src/server/helpers/server-helpers.js +33 -0
  27. package/src/{resource-index → server/resource-index}/paths.js +2 -2
  28. package/src/{resource-index → server/resource-index}/samples.js +9 -1
  29. package/src/{resource-index.js → server/resource-index.js} +158 -93
  30. package/src/server/resources/register-resources.js +56 -0
  31. package/src/server/runtime-config.js +66 -0
  32. package/src/server/tools/register-index-tools.js +130 -0
  33. package/src/server/tools/register-project-tools.js +305 -0
  34. package/src/server/tools/register-quickstart-tools.js +572 -0
  35. package/src/server/tools/register-sample-tools.js +333 -0
  36. package/src/server/tools/register-version-tools.js +136 -0
  37. package/src/server/transports/http.js +84 -0
  38. package/src/server/transports/stdio.js +12 -0
  39. package/src/data-bootstrap.js +0 -255
  40. package/src/rag.js +0 -1203
  41. /package/src/{gemini-retry.js → rag/gemini-retry.js} +0 -0
  42. /package/src/{normalizers.js → server/normalizers.js} +0 -0
  43. /package/src/{resource-index → server/resource-index}/builders.js +0 -0
  44. /package/src/{resource-index → server/resource-index}/config.js +0 -0
  45. /package/src/{resource-index → server/resource-index}/docs-loader.js +0 -0
  46. /package/src/{resource-index → server/resource-index}/uri.js +0 -0
  47. /package/src/{resource-index → server/resource-index}/version-policy.js +0 -0
@@ -0,0 +1,99 @@
1
+ import { dirname, join } from "node:path";
2
+ import { existsSync, renameSync, rmSync } from "node:fs";
3
+
4
+ function sleepMs(delayMs) {
5
+ return new Promise((resolve) => setTimeout(resolve, delayMs));
6
+ }
7
+
8
+ function parseHttpStatus(message) {
9
+ const match = String(message || "").match(/HTTP\s+(\d{3})/i);
10
+ if (!match) return 0;
11
+ return Number.parseInt(match[1], 10);
12
+ }
13
+
14
+ function shouldRetryDownloadError(error) {
15
+ const message = String(error?.message || "");
16
+ const status = parseHttpStatus(message);
17
+ if (status === 408 || status === 429) return true;
18
+ if (status >= 500 && status <= 599) return true;
19
+
20
+ if (error?.name === "AbortError") return true;
21
+ if (/timed?\s*out/i.test(message)) return true;
22
+ if (/ECONNRESET|ECONNREFUSED|ENOTFOUND|EAI_AGAIN/i.test(message)) return true;
23
+ return false;
24
+ }
25
+
26
+ function getBackoffDelayMs(attempt, baseDelayMs, maxDelayMs) {
27
+ const exp = Math.max(0, attempt - 1);
28
+ const delay = baseDelayMs * (2 ** exp);
29
+ return Math.min(delay, maxDelayMs);
30
+ }
31
+
32
+ async function withRetry(operation, {
33
+ maxAttempts,
34
+ baseDelayMs,
35
+ maxDelayMs,
36
+ shouldRetry,
37
+ onRetry
38
+ }) {
39
+ let lastError = null;
40
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
41
+ try {
42
+ return await operation(attempt);
43
+ } catch (error) {
44
+ lastError = error;
45
+ const canRetry = attempt < maxAttempts && shouldRetry(error);
46
+ if (!canRetry) throw error;
47
+ const delayMs = getBackoffDelayMs(attempt, baseDelayMs, maxDelayMs);
48
+ if (typeof onRetry === "function") {
49
+ onRetry({ attempt, delayMs, error });
50
+ }
51
+ await sleepMs(delayMs);
52
+ }
53
+ }
54
+ throw lastError || new Error("Retry operation failed");
55
+ }
56
+
57
+ function replaceDirectoryWithRollback(targetPath, stagedPath) {
58
+ const parent = dirname(targetPath);
59
+ const backupPath = join(parent, `${targetPath.split("/").pop() || "repo"}.bak-${Date.now()}`);
60
+ let movedExisting = false;
61
+ try {
62
+ if (existsSync(targetPath)) {
63
+ renameSync(targetPath, backupPath);
64
+ movedExisting = true;
65
+ }
66
+
67
+ renameSync(stagedPath, targetPath);
68
+ if (movedExisting && existsSync(backupPath)) {
69
+ rmSync(backupPath, { recursive: true, force: true });
70
+ }
71
+ } catch (error) {
72
+ if (existsSync(targetPath)) {
73
+ rmSync(targetPath, { recursive: true, force: true });
74
+ }
75
+ if (movedExisting && existsSync(backupPath)) {
76
+ renameSync(backupPath, targetPath);
77
+ }
78
+ throw error;
79
+ }
80
+ }
81
+
82
+ function buildHydrationFailureMessage({ reason, scopeSummary }) {
83
+ const lines = [
84
+ `Lazy hydration failed: ${reason}`,
85
+ scopeSummary ? `Scope: ${scopeSummary}` : "",
86
+ "Try one of the following:",
87
+ "- retry the same request",
88
+ "- set MCP_DATA_HYDRATION_MODE=eager to prefetch repos at startup",
89
+ "- set MCP_DATA_DOWNLOAD_TIMEOUT_MS to a higher value in slow networks"
90
+ ].filter(Boolean);
91
+ return lines.join("\n");
92
+ }
93
+
94
+ export {
95
+ shouldRetryDownloadError,
96
+ withRetry,
97
+ replaceDirectoryWithRollback,
98
+ buildHydrationFailureMessage
99
+ };
@@ -0,0 +1,15 @@
1
+ function normalizeEnvValue(value) {
2
+ if (value === undefined || value === null) return "";
3
+ return String(value).trim().toLowerCase();
4
+ }
5
+
6
+ function resolveHydrationMode(env = process.env) {
7
+ const mode = normalizeEnvValue(env.MCP_DATA_HYDRATION_MODE);
8
+ if (!mode) return "lazy";
9
+ if (mode === "lazy" || mode === "eager") return mode;
10
+ return "eager";
11
+ }
12
+
13
+ export {
14
+ resolveHydrationMode
15
+ };
@@ -0,0 +1,39 @@
1
+ const VALID_TYPES = new Set(["any", "doc", "sample"]);
2
+
3
+ function normalizeValue(value) {
4
+ if (value === undefined || value === null) return "";
5
+ return String(value).trim().toLowerCase();
6
+ }
7
+
8
+ function normalizeHydrationScope(scope) {
9
+ if (!scope || typeof scope !== "object") return null;
10
+ const product = normalizeValue(scope.product);
11
+ if (!product) return null;
12
+
13
+ const edition = normalizeValue(scope.edition);
14
+ const platform = normalizeValue(scope.platform);
15
+ const requestedType = normalizeValue(scope.type);
16
+ const type = VALID_TYPES.has(requestedType) ? requestedType : "any";
17
+
18
+ return {
19
+ product,
20
+ edition,
21
+ platform,
22
+ type
23
+ };
24
+ }
25
+
26
+ function normalizeHydrationScopes(scopes = []) {
27
+ if (!Array.isArray(scopes)) return [];
28
+ const normalized = [];
29
+ for (const scope of scopes) {
30
+ const value = normalizeHydrationScope(scope);
31
+ if (value) normalized.push(value);
32
+ }
33
+ return normalized;
34
+ }
35
+
36
+ export {
37
+ normalizeHydrationScope,
38
+ normalizeHydrationScopes
39
+ };
@@ -0,0 +1,149 @@
1
+ import { normalizeHydrationScopes } from "./hydration-policy.js";
2
+
3
+ const REPO_MAP = {
4
+ dbr: {
5
+ docs: {
6
+ web: ["documentation/barcode-reader-docs-js"],
7
+ mobile: ["documentation/barcode-reader-docs-mobile"],
8
+ server: ["documentation/barcode-reader-docs-server"],
9
+ any: [
10
+ "documentation/barcode-reader-docs-js",
11
+ "documentation/barcode-reader-docs-mobile",
12
+ "documentation/barcode-reader-docs-server"
13
+ ]
14
+ },
15
+ samples: {
16
+ web: ["samples/dynamsoft-barcode-reader"],
17
+ mobile: [
18
+ "samples/dynamsoft-barcode-reader-mobile",
19
+ "samples/dynamsoft-barcode-reader-maui",
20
+ "samples/dynamsoft-barcode-reader-react-native",
21
+ "samples/dynamsoft-barcode-reader-flutter"
22
+ ],
23
+ server: [
24
+ "samples/dynamsoft-barcode-reader-python",
25
+ "samples/dynamsoft-barcode-reader-dotnet",
26
+ "samples/dynamsoft-barcode-reader-java",
27
+ "samples/dynamsoft-barcode-reader-c-cpp",
28
+ "samples/dynamsoft-capture-vision-nodejs"
29
+ ],
30
+ any: [
31
+ "samples/dynamsoft-barcode-reader",
32
+ "samples/dynamsoft-barcode-reader-mobile",
33
+ "samples/dynamsoft-barcode-reader-python",
34
+ "samples/dynamsoft-barcode-reader-dotnet",
35
+ "samples/dynamsoft-barcode-reader-java",
36
+ "samples/dynamsoft-barcode-reader-c-cpp",
37
+ "samples/dynamsoft-barcode-reader-maui",
38
+ "samples/dynamsoft-barcode-reader-react-native",
39
+ "samples/dynamsoft-barcode-reader-flutter",
40
+ "samples/dynamsoft-capture-vision-nodejs"
41
+ ]
42
+ }
43
+ },
44
+ dcv: {
45
+ docs: {
46
+ core: ["documentation/capture-vision-docs"],
47
+ web: ["documentation/capture-vision-docs-js"],
48
+ mobile: ["documentation/capture-vision-docs-mobile"],
49
+ server: ["documentation/capture-vision-docs-server"],
50
+ any: [
51
+ "documentation/capture-vision-docs",
52
+ "documentation/capture-vision-docs-js",
53
+ "documentation/capture-vision-docs-mobile",
54
+ "documentation/capture-vision-docs-server"
55
+ ]
56
+ },
57
+ samples: {
58
+ web: ["samples/dynamsoft-capture-vision-javascript"],
59
+ mobile: [
60
+ "samples/dynamsoft-capture-vision-mobile",
61
+ "samples/dynamsoft-capture-vision-maui",
62
+ "samples/dynamsoft-capture-vision-react-native",
63
+ "samples/dynamsoft-capture-vision-flutter",
64
+ "samples/dynamsoft-capture-vision-spm"
65
+ ],
66
+ server: [
67
+ "samples/dynamsoft-capture-vision-python",
68
+ "samples/dynamsoft-capture-vision-dotnet",
69
+ "samples/dynamsoft-capture-vision-java",
70
+ "samples/dynamsoft-capture-vision-c-cpp",
71
+ "samples/dynamsoft-capture-vision-nodejs"
72
+ ],
73
+ any: [
74
+ "samples/dynamsoft-capture-vision-javascript",
75
+ "samples/dynamsoft-capture-vision-mobile",
76
+ "samples/dynamsoft-capture-vision-python",
77
+ "samples/dynamsoft-capture-vision-dotnet",
78
+ "samples/dynamsoft-capture-vision-java",
79
+ "samples/dynamsoft-capture-vision-c-cpp",
80
+ "samples/dynamsoft-capture-vision-maui",
81
+ "samples/dynamsoft-capture-vision-react-native",
82
+ "samples/dynamsoft-capture-vision-flutter",
83
+ "samples/dynamsoft-capture-vision-nodejs",
84
+ "samples/dynamsoft-capture-vision-spm"
85
+ ]
86
+ }
87
+ },
88
+ dwt: {
89
+ docs: {
90
+ any: ["documentation/web-twain-docs"]
91
+ },
92
+ samples: {
93
+ any: ["samples/dynamic-web-twain"]
94
+ }
95
+ },
96
+ ddv: {
97
+ docs: {
98
+ any: ["documentation/document-viewer-docs"]
99
+ },
100
+ samples: {
101
+ any: ["samples/dynamsoft-document-viewer"]
102
+ }
103
+ }
104
+ };
105
+
106
+ function getMappedPaths(product, edition, sourceType) {
107
+ const productMap = REPO_MAP[product];
108
+ if (!productMap) return [];
109
+ const typeMap = productMap[sourceType];
110
+ if (!typeMap) return [];
111
+ if (edition && typeMap[edition]) return typeMap[edition];
112
+ return typeMap.any || [];
113
+ }
114
+
115
+ function resolveRepoPathsForScopes(scopes = [], manifest = null) {
116
+ const manifestPaths = new Set(
117
+ Array.isArray(manifest?.repos) ? manifest.repos.map((repo) => String(repo.path || "")) : []
118
+ );
119
+
120
+ const normalizedScopes = normalizeHydrationScopes(scopes);
121
+ if (normalizedScopes.length === 0) {
122
+ if (manifestPaths.size === 0) return [];
123
+ return Array.from(manifestPaths).sort((a, b) => a.localeCompare(b));
124
+ }
125
+
126
+ const resolved = new Set();
127
+ for (const scope of normalizedScopes) {
128
+ const includeDocs = scope.type === "any" || scope.type === "doc";
129
+ const includeSamples = scope.type === "any" || scope.type === "sample";
130
+
131
+ if (includeDocs) {
132
+ for (const path of getMappedPaths(scope.product, scope.edition, "docs")) {
133
+ resolved.add(path);
134
+ }
135
+ }
136
+ if (includeSamples) {
137
+ for (const path of getMappedPaths(scope.product, scope.edition, "samples")) {
138
+ resolved.add(path);
139
+ }
140
+ }
141
+ }
142
+
143
+ const filtered = Array.from(resolved).filter((path) => manifestPaths.size === 0 || manifestPaths.has(path));
144
+ return filtered.sort((a, b) => a.localeCompare(b));
145
+ }
146
+
147
+ export {
148
+ resolveRepoPathsForScopes
149
+ };
@@ -2,7 +2,7 @@ import { dirname, join, resolve } from "node:path";
2
2
  import { fileURLToPath } from "node:url";
3
3
 
4
4
  const __dirname = dirname(fileURLToPath(import.meta.url));
5
- const projectRoot = join(__dirname, "..");
5
+ const projectRoot = join(__dirname, "..", "..");
6
6
  const bundledDataRoot = join(projectRoot, "data");
7
7
 
8
8
  function getResolvedDataRoot() {
@@ -4,7 +4,7 @@ import { join, dirname } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
 
6
6
  const __dirname = dirname(fileURLToPath(import.meta.url));
7
- const projectRoot = join(__dirname, "..");
7
+ const projectRoot = join(__dirname, "..", "..");
8
8
 
9
9
  function logDataSync(message) {
10
10
  console.error(`[data-sync] ${message}`);