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.
- package/.env.example +35 -9
- package/README.md +156 -497
- package/package.json +13 -7
- package/scripts/prebuild-rag-index.mjs +1 -1
- package/scripts/run-gemini-tests.mjs +1 -1
- package/scripts/sync-submodules.mjs +1 -1
- package/scripts/verify-doc-resources.mjs +79 -0
- package/src/data/bootstrap.js +475 -0
- package/src/data/download-utils.js +99 -0
- package/src/data/hydration-mode.js +15 -0
- package/src/data/hydration-policy.js +39 -0
- package/src/data/repo-map.js +149 -0
- package/src/{data-root.js → data/root.js} +1 -1
- package/src/{submodule-sync.js → data/submodule-sync.js} +1 -1
- package/src/index.js +49 -1499
- package/src/observability/logging.js +51 -0
- package/src/rag/config.js +96 -0
- package/src/rag/index.js +266 -0
- package/src/rag/lexical-provider.js +170 -0
- package/src/rag/logger.js +46 -0
- package/src/rag/profile-config.js +48 -0
- package/src/rag/providers.js +585 -0
- package/src/rag/search-utils.js +166 -0
- package/src/rag/vector-cache.js +323 -0
- package/src/server/create-server.js +168 -0
- package/src/server/helpers/server-helpers.js +33 -0
- package/src/{resource-index → server/resource-index}/paths.js +2 -2
- package/src/{resource-index → server/resource-index}/samples.js +9 -1
- package/src/{resource-index.js → server/resource-index.js} +158 -93
- package/src/server/resources/register-resources.js +56 -0
- package/src/server/runtime-config.js +66 -0
- package/src/server/tools/register-index-tools.js +130 -0
- package/src/server/tools/register-project-tools.js +305 -0
- package/src/server/tools/register-quickstart-tools.js +572 -0
- package/src/server/tools/register-sample-tools.js +333 -0
- package/src/server/tools/register-version-tools.js +136 -0
- package/src/server/transports/http.js +84 -0
- package/src/server/transports/stdio.js +12 -0
- package/src/data-bootstrap.js +0 -255
- package/src/rag.js +0 -1203
- /package/src/{gemini-retry.js → rag/gemini-retry.js} +0 -0
- /package/src/{normalizers.js → server/normalizers.js} +0 -0
- /package/src/{resource-index → server/resource-index}/builders.js +0 -0
- /package/src/{resource-index → server/resource-index}/config.js +0 -0
- /package/src/{resource-index → server/resource-index}/docs-loader.js +0 -0
- /package/src/{resource-index → server/resource-index}/uri.js +0 -0
- /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}`);
|