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
|
@@ -19,6 +19,13 @@ let cachedDbrWebFrameworkPlatforms = null;
|
|
|
19
19
|
let cachedDdvWebFrameworkPlatforms = null;
|
|
20
20
|
let cachedDcvWebFrameworkPlatforms = null;
|
|
21
21
|
|
|
22
|
+
function resetSampleDiscoveryCaches() {
|
|
23
|
+
cachedWebFrameworkPlatforms = null;
|
|
24
|
+
cachedDbrWebFrameworkPlatforms = null;
|
|
25
|
+
cachedDdvWebFrameworkPlatforms = null;
|
|
26
|
+
cachedDcvWebFrameworkPlatforms = null;
|
|
27
|
+
}
|
|
28
|
+
|
|
22
29
|
function getCodeFileExtensions() {
|
|
23
30
|
return CODE_FILE_EXTENSIONS;
|
|
24
31
|
}
|
|
@@ -719,5 +726,6 @@ export {
|
|
|
719
726
|
getMainCodeFile,
|
|
720
727
|
getMimeTypeForExtension,
|
|
721
728
|
getDbrServerSampleContent,
|
|
722
|
-
getDcvServerSampleContent
|
|
729
|
+
getDcvServerSampleContent,
|
|
730
|
+
resetSampleDiscoveryCaches
|
|
723
731
|
};
|
|
@@ -53,7 +53,8 @@ import {
|
|
|
53
53
|
getMainCodeFile,
|
|
54
54
|
getMimeTypeForExtension,
|
|
55
55
|
getDbrServerSampleContent,
|
|
56
|
-
getDcvServerSampleContent
|
|
56
|
+
getDcvServerSampleContent,
|
|
57
|
+
resetSampleDiscoveryCaches
|
|
57
58
|
} from "./resource-index/samples.js";
|
|
58
59
|
import { parseResourceUri, parseSampleUri, getSampleIdFromUri } from "./resource-index/uri.js";
|
|
59
60
|
import {
|
|
@@ -126,95 +127,107 @@ function inferDcvServerPlatform(articlePath) {
|
|
|
126
127
|
return "server";
|
|
127
128
|
}
|
|
128
129
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
);
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
);
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
);
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
130
|
+
let dbrWebDocs = [];
|
|
131
|
+
let dbrMobileDocs = [];
|
|
132
|
+
let dbrServerDocs = [];
|
|
133
|
+
let dcvCoreDocs = [];
|
|
134
|
+
let dcvWebDocs = [];
|
|
135
|
+
let dcvMobileDocs = [];
|
|
136
|
+
let dcvServerDocs = [];
|
|
137
|
+
let dwtDocs = { articles: [] };
|
|
138
|
+
let ddvDocs = { articles: [] };
|
|
139
|
+
|
|
140
|
+
function loadDocumentationSets() {
|
|
141
|
+
dbrWebDocs = withEditionScope(
|
|
142
|
+
loadMarkdownDocs({
|
|
143
|
+
rootDir: DOC_ROOTS.dbrWeb,
|
|
144
|
+
urlBase: DOCS_CONFIG.dbrWeb.urlBase,
|
|
145
|
+
excludeDirs: DOCS_CONFIG.dbrWeb.excludeDirs,
|
|
146
|
+
excludeFiles: DOCS_CONFIG.dbrWeb.excludeFiles
|
|
147
|
+
}).articles,
|
|
148
|
+
"web",
|
|
149
|
+
() => "web"
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
dbrMobileDocs = withEditionScope(
|
|
153
|
+
loadMarkdownDocs({
|
|
154
|
+
rootDir: DOC_ROOTS.dbrMobile,
|
|
155
|
+
urlBase: DOCS_CONFIG.dbrMobile.urlBase,
|
|
156
|
+
excludeDirs: DOCS_CONFIG.dbrMobile.excludeDirs,
|
|
157
|
+
excludeFiles: DOCS_CONFIG.dbrMobile.excludeFiles
|
|
158
|
+
}).articles,
|
|
159
|
+
"mobile",
|
|
160
|
+
inferDbrMobilePlatform
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
dbrServerDocs = withEditionScope(
|
|
164
|
+
loadMarkdownDocs({
|
|
165
|
+
rootDir: DOC_ROOTS.dbrServer,
|
|
166
|
+
urlBase: DOCS_CONFIG.dbrServer.urlBase,
|
|
167
|
+
excludeDirs: DOCS_CONFIG.dbrServer.excludeDirs,
|
|
168
|
+
excludeFiles: DOCS_CONFIG.dbrServer.excludeFiles
|
|
169
|
+
}).articles,
|
|
170
|
+
"server",
|
|
171
|
+
inferDbrServerPlatform
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
dcvCoreDocs = withEditionScope(
|
|
175
|
+
loadMarkdownDocs({
|
|
176
|
+
rootDir: DOC_ROOTS.dcvCore,
|
|
177
|
+
urlBase: DOCS_CONFIG.dcvCore.urlBase,
|
|
178
|
+
excludeDirs: DOCS_CONFIG.dcvCore.excludeDirs,
|
|
179
|
+
excludeFiles: DOCS_CONFIG.dcvCore.excludeFiles
|
|
180
|
+
}).articles,
|
|
181
|
+
"core",
|
|
182
|
+
() => "core"
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
dcvWebDocs = withEditionScope(
|
|
186
|
+
loadMarkdownDocs({
|
|
187
|
+
rootDir: DOC_ROOTS.dcvWeb,
|
|
188
|
+
urlBase: DOCS_CONFIG.dcvWeb.urlBase,
|
|
189
|
+
excludeDirs: DOCS_CONFIG.dcvWeb.excludeDirs,
|
|
190
|
+
excludeFiles: DOCS_CONFIG.dcvWeb.excludeFiles
|
|
191
|
+
}).articles,
|
|
192
|
+
"web",
|
|
193
|
+
() => "web"
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
dcvMobileDocs = withEditionScope(
|
|
197
|
+
loadMarkdownDocs({
|
|
198
|
+
rootDir: DOC_ROOTS.dcvMobile,
|
|
199
|
+
urlBase: DOCS_CONFIG.dcvMobile.urlBase,
|
|
200
|
+
excludeDirs: DOCS_CONFIG.dcvMobile.excludeDirs,
|
|
201
|
+
excludeFiles: DOCS_CONFIG.dcvMobile.excludeFiles
|
|
202
|
+
}).articles,
|
|
203
|
+
"mobile",
|
|
204
|
+
inferDcvMobilePlatform
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
dcvServerDocs = withEditionScope(
|
|
208
|
+
loadMarkdownDocs({
|
|
209
|
+
rootDir: DOC_ROOTS.dcvServer,
|
|
210
|
+
urlBase: DOCS_CONFIG.dcvServer.urlBase,
|
|
211
|
+
excludeDirs: DOCS_CONFIG.dcvServer.excludeDirs,
|
|
212
|
+
excludeFiles: DOCS_CONFIG.dcvServer.excludeFiles
|
|
213
|
+
}).articles,
|
|
214
|
+
"server",
|
|
215
|
+
inferDcvServerPlatform
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
dwtDocs = loadMarkdownDocs({
|
|
219
|
+
rootDir: DOC_ROOTS.dwtArticles,
|
|
220
|
+
urlBase: DOCS_CONFIG.dwt.urlBase,
|
|
221
|
+
includeDirNames: DOCS_CONFIG.dwt.includeDirNames
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
ddvDocs = loadMarkdownDocs({
|
|
225
|
+
rootDir: DOC_ROOTS.ddv,
|
|
226
|
+
urlBase: DOCS_CONFIG.ddv.urlBase,
|
|
227
|
+
excludeDirs: DOCS_CONFIG.ddv.excludeDirs,
|
|
228
|
+
excludeFiles: DOCS_CONFIG.ddv.excludeFiles
|
|
229
|
+
});
|
|
230
|
+
}
|
|
218
231
|
|
|
219
232
|
const dbrServerSdk = registry.sdks["dbr-server"];
|
|
220
233
|
const dcvMobileSdk = registry.sdks["dcv-mobile"];
|
|
@@ -261,6 +274,7 @@ function buildVersionPolicyText() {
|
|
|
261
274
|
}
|
|
262
275
|
|
|
263
276
|
const resourceIndex = [];
|
|
277
|
+
const resourceIndexByUri = new Map();
|
|
264
278
|
|
|
265
279
|
function addResourceToIndex(entry) {
|
|
266
280
|
resourceIndex.push(entry);
|
|
@@ -340,9 +354,19 @@ function buildResourceIndex() {
|
|
|
340
354
|
});
|
|
341
355
|
}
|
|
342
356
|
|
|
343
|
-
|
|
357
|
+
function refreshResourceIndex() {
|
|
358
|
+
resetSampleDiscoveryCaches();
|
|
359
|
+
loadDocumentationSets();
|
|
360
|
+
resourceIndex.length = 0;
|
|
361
|
+
buildResourceIndex();
|
|
362
|
+
resourceIndexByUri.clear();
|
|
363
|
+
for (const entry of resourceIndex) {
|
|
364
|
+
resourceIndexByUri.set(entry.uri, entry);
|
|
365
|
+
}
|
|
366
|
+
return { resourceCount: resourceIndex.length };
|
|
367
|
+
}
|
|
344
368
|
|
|
345
|
-
|
|
369
|
+
refreshResourceIndex();
|
|
346
370
|
|
|
347
371
|
function editionMatches(normalizedEdition, entryEdition) {
|
|
348
372
|
if (!normalizedEdition) return true;
|
|
@@ -399,9 +423,49 @@ function getPinnedResources() {
|
|
|
399
423
|
return resourceIndex.filter((entry) => entry.pinned);
|
|
400
424
|
}
|
|
401
425
|
|
|
426
|
+
function safeDecodeURIComponent(value) {
|
|
427
|
+
try {
|
|
428
|
+
return decodeURIComponent(value);
|
|
429
|
+
} catch {
|
|
430
|
+
return value;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function buildResourceLookupCandidates(uri) {
|
|
435
|
+
const candidates = [];
|
|
436
|
+
if (typeof uri !== "string" || uri.length === 0) return candidates;
|
|
437
|
+
candidates.push(uri);
|
|
438
|
+
|
|
439
|
+
if (!uri.includes("://")) return candidates;
|
|
440
|
+
const [scheme, rest] = uri.split("://");
|
|
441
|
+
if (scheme !== "doc") return candidates;
|
|
442
|
+
|
|
443
|
+
const parts = String(rest || "").split("/").filter(Boolean);
|
|
444
|
+
if (parts.length < 5) return candidates;
|
|
445
|
+
|
|
446
|
+
const head = parts.slice(0, 4);
|
|
447
|
+
const slugRaw = parts.slice(4).join("/");
|
|
448
|
+
const decodedOnce = safeDecodeURIComponent(slugRaw);
|
|
449
|
+
const decodedTwice = safeDecodeURIComponent(decodedOnce);
|
|
450
|
+
|
|
451
|
+
for (const slug of [decodedOnce, decodedTwice]) {
|
|
452
|
+
const canonical = `${scheme}://${head.join("/")}/${encodeURIComponent(slug)}`;
|
|
453
|
+
if (!candidates.includes(canonical)) {
|
|
454
|
+
candidates.push(canonical);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return candidates;
|
|
459
|
+
}
|
|
460
|
+
|
|
402
461
|
async function readResourceContent(uri) {
|
|
403
|
-
|
|
462
|
+
let resource = null;
|
|
463
|
+
for (const candidate of buildResourceLookupCandidates(uri)) {
|
|
464
|
+
resource = resourceIndexByUri.get(candidate);
|
|
465
|
+
if (resource) break;
|
|
466
|
+
}
|
|
404
467
|
if (!resource) return null;
|
|
468
|
+
|
|
405
469
|
const content = await resource.loadContent();
|
|
406
470
|
return {
|
|
407
471
|
uri,
|
|
@@ -515,6 +579,7 @@ export {
|
|
|
515
579
|
buildVersionPolicyText,
|
|
516
580
|
buildIndexData,
|
|
517
581
|
buildResourceIndex,
|
|
582
|
+
refreshResourceIndex,
|
|
518
583
|
editionMatches,
|
|
519
584
|
platformMatches,
|
|
520
585
|
getDisplayEdition,
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ListResourcesRequestSchema,
|
|
3
|
+
ReadResourceRequestSchema,
|
|
4
|
+
SubscribeRequestSchema,
|
|
5
|
+
UnsubscribeRequestSchema
|
|
6
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
7
|
+
|
|
8
|
+
export function registerResourceHandlers({
|
|
9
|
+
server,
|
|
10
|
+
getPinnedResources,
|
|
11
|
+
parseResourceUri,
|
|
12
|
+
ensureLatestMajor,
|
|
13
|
+
readResourceContent
|
|
14
|
+
}) {
|
|
15
|
+
server.server.registerCapabilities({
|
|
16
|
+
resources: {
|
|
17
|
+
listChanged: false,
|
|
18
|
+
subscribe: true
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
server.server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
23
|
+
const resources = getPinnedResources().map((entry) => ({
|
|
24
|
+
uri: entry.uri,
|
|
25
|
+
name: entry.title,
|
|
26
|
+
description: entry.summary,
|
|
27
|
+
mimeType: entry.mimeType
|
|
28
|
+
}));
|
|
29
|
+
return { resources };
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
server.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
33
|
+
const parsed = parseResourceUri(request.params.uri);
|
|
34
|
+
if (parsed && ["dcv", "dbr", "dwt", "ddv"].includes(parsed.product)) {
|
|
35
|
+
const policy = ensureLatestMajor({
|
|
36
|
+
product: parsed.product,
|
|
37
|
+
version: parsed.version,
|
|
38
|
+
edition: parsed.edition,
|
|
39
|
+
platform: parsed.platform
|
|
40
|
+
});
|
|
41
|
+
if (!policy.ok) {
|
|
42
|
+
throw new Error(policy.message);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const resource = await readResourceContent(request.params.uri);
|
|
47
|
+
if (!resource) {
|
|
48
|
+
throw new Error(`Resource not found: ${request.params.uri}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return { contents: [resource] };
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
server.server.setRequestHandler(SubscribeRequestSchema, async () => ({}));
|
|
55
|
+
server.server.setRequestHandler(UnsubscribeRequestSchema, async () => ({}));
|
|
56
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const DEFAULT_TRANSPORT = "stdio";
|
|
2
|
+
const DEFAULT_HTTP_HOST = "127.0.0.1";
|
|
3
|
+
const DEFAULT_HTTP_PORT = 3333;
|
|
4
|
+
const SUPPORTED_TRANSPORTS = new Set(["stdio", "http"]);
|
|
5
|
+
|
|
6
|
+
const MCP_HTTP_PATH = "/mcp";
|
|
7
|
+
|
|
8
|
+
function parseCliArgs(argv) {
|
|
9
|
+
const parsed = {};
|
|
10
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
11
|
+
const token = argv[i];
|
|
12
|
+
if (!token.startsWith("--")) continue;
|
|
13
|
+
const option = token.slice(2);
|
|
14
|
+
if (!option) continue;
|
|
15
|
+
|
|
16
|
+
const equalsIndex = option.indexOf("=");
|
|
17
|
+
if (equalsIndex >= 0) {
|
|
18
|
+
const key = option.slice(0, equalsIndex);
|
|
19
|
+
const value = option.slice(equalsIndex + 1);
|
|
20
|
+
if (key) parsed[key] = value;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const next = argv[i + 1];
|
|
25
|
+
if (next && !next.startsWith("--")) {
|
|
26
|
+
parsed[option] = next;
|
|
27
|
+
i += 1;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
parsed[option] = "true";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return parsed;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function resolveRuntimeConfig(argv = process.argv.slice(2)) {
|
|
38
|
+
const parsed = parseCliArgs(argv);
|
|
39
|
+
const transport = String(parsed.transport || DEFAULT_TRANSPORT).toLowerCase();
|
|
40
|
+
|
|
41
|
+
if (!SUPPORTED_TRANSPORTS.has(transport)) {
|
|
42
|
+
throw new Error(`Invalid --transport "${transport}". Expected "stdio" or "http".`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (transport === "http") {
|
|
46
|
+
const host = String(parsed.host || DEFAULT_HTTP_HOST);
|
|
47
|
+
const portRaw = String(parsed.port || String(DEFAULT_HTTP_PORT));
|
|
48
|
+
const port = Number.parseInt(portRaw, 10);
|
|
49
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
50
|
+
throw new Error(`Invalid --port "${portRaw}". Expected an integer between 1 and 65535.`);
|
|
51
|
+
}
|
|
52
|
+
return { transport, host, port };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
transport,
|
|
57
|
+
host: DEFAULT_HTTP_HOST,
|
|
58
|
+
port: DEFAULT_HTTP_PORT
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export {
|
|
63
|
+
MCP_HTTP_PATH,
|
|
64
|
+
parseCliArgs,
|
|
65
|
+
resolveRuntimeConfig
|
|
66
|
+
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { formatScoreLabel, formatScoreNote } from "../helpers/server-helpers.js";
|
|
3
|
+
|
|
4
|
+
export function registerIndexTools({
|
|
5
|
+
server,
|
|
6
|
+
ensureScopeHydrated,
|
|
7
|
+
ensureLatestMajor,
|
|
8
|
+
normalizeProduct,
|
|
9
|
+
normalizePlatform,
|
|
10
|
+
normalizeEdition,
|
|
11
|
+
buildIndexData,
|
|
12
|
+
getSampleIdFromUri,
|
|
13
|
+
formatScopeLabel,
|
|
14
|
+
searchResources
|
|
15
|
+
}) {
|
|
16
|
+
server.registerTool(
|
|
17
|
+
"get_index",
|
|
18
|
+
{
|
|
19
|
+
title: "Get Index",
|
|
20
|
+
description: "Get a compact index of products, editions, versions, samples/docs, plus DBR-vs-DCV selection guidance.",
|
|
21
|
+
inputSchema: {}
|
|
22
|
+
},
|
|
23
|
+
async () => ({
|
|
24
|
+
content: [{ type: "text", text: JSON.stringify(buildIndexData(), null, 2) }]
|
|
25
|
+
})
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
server.registerTool(
|
|
29
|
+
"search",
|
|
30
|
+
{
|
|
31
|
+
title: "Search",
|
|
32
|
+
description: "Semantic (RAG) search across docs and samples with fuzzy fallback; returns resource links for lazy loading. Prefer DCV for MRZ/VIN/document-normalization/driver-license scenarios; DBR for barcode-only.",
|
|
33
|
+
inputSchema: {
|
|
34
|
+
query: z.string().describe("Keywords to search across docs and samples."),
|
|
35
|
+
product: z.string().optional().describe("Product: dcv, dbr, dwt, ddv"),
|
|
36
|
+
edition: z.string().optional().describe("Edition: core, mobile, web, server/desktop"),
|
|
37
|
+
platform: z.string().optional().describe("Platform: android, ios, maui, react-native, flutter, js, python, cpp, java, dotnet, nodejs, angular, blazor, capacitor, electron, es6, native-ts, next, nuxt, pwa, react, requirejs, svelte, vue, webview, spm, core"),
|
|
38
|
+
version: z.string().optional().describe("Version constraint (major or full version)"),
|
|
39
|
+
type: z.enum(["doc", "sample", "index", "policy", "any"]).optional(),
|
|
40
|
+
limit: z.number().int().min(1).max(10).optional().describe("Max results (default 5)")
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
async ({ query, product, edition, platform, version, type, limit }) => {
|
|
44
|
+
if (!query || !query.trim()) {
|
|
45
|
+
return { isError: true, content: [{ type: "text", text: "Query is required." }] };
|
|
46
|
+
}
|
|
47
|
+
const normalizedProduct = normalizeProduct(product);
|
|
48
|
+
const normalizedPlatform = normalizePlatform(platform);
|
|
49
|
+
const normalizedEdition = normalizeEdition(edition, normalizedPlatform, normalizedProduct);
|
|
50
|
+
|
|
51
|
+
await ensureScopeHydrated({
|
|
52
|
+
product: normalizedProduct,
|
|
53
|
+
edition: normalizedEdition,
|
|
54
|
+
platform: normalizedPlatform,
|
|
55
|
+
type: type || "any"
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const policy = ensureLatestMajor({
|
|
59
|
+
product: normalizedProduct,
|
|
60
|
+
version,
|
|
61
|
+
query,
|
|
62
|
+
edition: normalizedEdition,
|
|
63
|
+
platform: normalizedPlatform
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (!policy.ok) {
|
|
67
|
+
return { isError: true, content: [{ type: "text", text: policy.message }] };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const maxResults = Math.min(limit || 5, 10);
|
|
71
|
+
const topResults = await searchResources({
|
|
72
|
+
query,
|
|
73
|
+
product: normalizedProduct,
|
|
74
|
+
edition: normalizedEdition,
|
|
75
|
+
platform: normalizedPlatform,
|
|
76
|
+
type: type || "any",
|
|
77
|
+
limit: maxResults
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
if (topResults.length === 0) {
|
|
81
|
+
return {
|
|
82
|
+
content: [{
|
|
83
|
+
type: "text",
|
|
84
|
+
text: `No results for "${query}". Try get_index for available products or adjust filters.`
|
|
85
|
+
}]
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const content = [
|
|
90
|
+
{
|
|
91
|
+
type: "text",
|
|
92
|
+
text: `Found ${topResults.length} result(s) for "${query}". Read the links you need with resources/read.`
|
|
93
|
+
}
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
for (const entry of topResults) {
|
|
97
|
+
const versionLabel = entry.version ? `v${entry.version}` : "n/a";
|
|
98
|
+
const scopeLabel = formatScopeLabel(entry);
|
|
99
|
+
const sampleId = entry.type === "sample" ? getSampleIdFromUri(entry.uri) : "";
|
|
100
|
+
const sampleHint = sampleId ? ` | sample_id: ${sampleId}` : "";
|
|
101
|
+
const scoreLabel = formatScoreLabel(entry);
|
|
102
|
+
content.push({
|
|
103
|
+
type: "resource_link",
|
|
104
|
+
uri: entry.uri,
|
|
105
|
+
name: entry.title,
|
|
106
|
+
description: `${entry.type.toUpperCase()} | ${scopeLabel} | ${versionLabel}${scoreLabel} - ${entry.summary}${sampleHint}`,
|
|
107
|
+
mimeType: entry.mimeType,
|
|
108
|
+
annotations: {
|
|
109
|
+
audience: ["assistant"],
|
|
110
|
+
priority: 0.8
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const plainLines = topResults.map((entry, index) => {
|
|
116
|
+
const sampleId = entry.type === "sample" ? getSampleIdFromUri(entry.uri) : "";
|
|
117
|
+
const action = entry.type === "sample" ? "generate_project resource_uri" : "resources/read uri";
|
|
118
|
+
const sampleNote = sampleId ? ` sample_id=${sampleId}` : "";
|
|
119
|
+
const scoreNote = formatScoreNote(entry);
|
|
120
|
+
return `- ${index + 1}. ${entry.uri}${sampleNote}${scoreNote} (${action})`;
|
|
121
|
+
});
|
|
122
|
+
content.push({
|
|
123
|
+
type: "text",
|
|
124
|
+
text: ["Plain URIs (copy/paste):", ...plainLines].join("\n")
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
return { content };
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
}
|