simple-dynamsoft-mcp 6.4.0 → 7.0.1
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 +29 -7
- package/README.md +157 -506
- package/data/metadata/data-manifest.json +2 -2
- package/package.json +12 -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 +1 -1
- 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} +117 -92
- 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 -1245
- /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;
|
|
@@ -555,6 +579,7 @@ export {
|
|
|
555
579
|
buildVersionPolicyText,
|
|
556
580
|
buildIndexData,
|
|
557
581
|
buildResourceIndex,
|
|
582
|
+
refreshResourceIndex,
|
|
558
583
|
editionMatches,
|
|
559
584
|
platformMatches,
|
|
560
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
|
+
}
|