simple-dynamsoft-mcp 7.0.1 → 7.1.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/README.md +17 -7
- package/data/metadata/data-manifest.json +4 -4
- package/data/metadata/dynamsoft_sdks.json +1 -1
- package/package.json +1 -1
- package/src/server/create-server.js +5 -8
- package/src/server/resources/register-resources.js +37 -29
- package/src/server/tools/register-index-tools.js +179 -10
- package/src/server/tools/register-project-tools.js +39 -7
- package/src/server/tools/register-quickstart-tools.js +33 -2
- package/src/server/tools/register-sample-tools.js +32 -239
- package/src/server/tools/register-version-tools.js +32 -2
package/README.md
CHANGED
|
@@ -223,13 +223,23 @@ Location:
|
|
|
223
223
|
|
|
224
224
|
The server exposes this minimal tool surface:
|
|
225
225
|
|
|
226
|
-
- `get_index`
|
|
227
|
-
- `search`
|
|
228
|
-
- `list_samples`
|
|
229
|
-
- `
|
|
230
|
-
- `
|
|
231
|
-
- `
|
|
232
|
-
|
|
226
|
+
- `get_index` -- compact product/version/sample index with selection guidance
|
|
227
|
+
- `search` -- semantic search across docs and samples (also accepts exact sample IDs)
|
|
228
|
+
- `list_samples` -- browse available samples for a product/edition/platform
|
|
229
|
+
- `resolve_version` -- resolve current version for a product/edition/platform
|
|
230
|
+
- `get_quickstart` -- opinionated quickstart: picks a sample by scenario, returns code + install instructions
|
|
231
|
+
- `get_sample_files` -- get full project files for a known sample (discovered via list_samples or search)
|
|
232
|
+
|
|
233
|
+
## Companion: Dynamsoft SDK Skills
|
|
234
|
+
|
|
235
|
+
For AI agents that support skills (Claude Code, OpenCode, Codex), install [dynamsoft-sdk-skills](https://github.com/user/dynamsoft-sdk-skills) for guided integration workflows:
|
|
236
|
+
|
|
237
|
+
npx dynamsoft-sdk-skills install --all
|
|
238
|
+
|
|
239
|
+
- **Skills** provide integration patterns, gotchas, and decision trees (loaded into agent context)
|
|
240
|
+
- **MCP Server** provides runtime tools: version resolution, doc search, sample browsing, and retrieval of full sample project files
|
|
241
|
+
|
|
242
|
+
Both work independently, but together the skills guide agents to use MCP tools at the right moments.
|
|
233
243
|
|
|
234
244
|
## Quick Troubleshooting
|
|
235
245
|
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"branch": "main",
|
|
9
9
|
"owner": "dynamsoft-docs",
|
|
10
10
|
"repo": "barcode-reader-docs-js",
|
|
11
|
-
"commit": "
|
|
12
|
-
"archiveUrl": "https://codeload.github.com/dynamsoft-docs/barcode-reader-docs-js/zip/
|
|
11
|
+
"commit": "acab784e29ff035b355dcd6d6ebe284fcb34eef3",
|
|
12
|
+
"archiveUrl": "https://codeload.github.com/dynamsoft-docs/barcode-reader-docs-js/zip/acab784e29ff035b355dcd6d6ebe284fcb34eef3"
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"name": "data/documentation/barcode-reader-docs-mobile",
|
|
@@ -88,8 +88,8 @@
|
|
|
88
88
|
"branch": "master",
|
|
89
89
|
"owner": "dynamsoft-docs",
|
|
90
90
|
"repo": "web-twain-docs",
|
|
91
|
-
"commit": "
|
|
92
|
-
"archiveUrl": "https://codeload.github.com/dynamsoft-docs/web-twain-docs/zip/
|
|
91
|
+
"commit": "275eefb2a97512e91cfca139ac58f8a8b6317f8a",
|
|
92
|
+
"archiveUrl": "https://codeload.github.com/dynamsoft-docs/web-twain-docs/zip/275eefb2a97512e91cfca139ac58f8a8b6317f8a"
|
|
93
93
|
},
|
|
94
94
|
{
|
|
95
95
|
"name": "data/samples/dynamic-web-twain",
|
|
@@ -455,7 +455,7 @@
|
|
|
455
455
|
"dwt": {
|
|
456
456
|
"name": "Dynamic Web TWAIN",
|
|
457
457
|
"description": "Document scanning SDK for web applications. Supports TWAIN, WIA, ICA, SANE scanners.",
|
|
458
|
-
"version": "19.3",
|
|
458
|
+
"version": "19.3.2",
|
|
459
459
|
"default_platform": "web",
|
|
460
460
|
"snippet_path": "dynamic-web-twain",
|
|
461
461
|
"api_docs": "web-twain-api-docs.json",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "simple-dynamsoft-mcp",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.1.1",
|
|
4
4
|
"description": "MCP server for Dynamsoft SDKs - Capture Vision, Barcode Reader (Mobile/Python/Web), Dynamic Web TWAIN, and Document Viewer. Provides documentation, code snippets, and API guidance.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -75,7 +75,10 @@ registerIndexTools({
|
|
|
75
75
|
buildIndexData,
|
|
76
76
|
getSampleIdFromUri,
|
|
77
77
|
formatScopeLabel,
|
|
78
|
-
searchResources
|
|
78
|
+
searchResources,
|
|
79
|
+
normalizeSampleName,
|
|
80
|
+
getSampleEntries,
|
|
81
|
+
getSampleSuggestions
|
|
79
82
|
});
|
|
80
83
|
|
|
81
84
|
registerSampleTools({
|
|
@@ -85,16 +88,10 @@ registerSampleTools({
|
|
|
85
88
|
normalizeProduct,
|
|
86
89
|
normalizePlatform,
|
|
87
90
|
normalizeEdition,
|
|
88
|
-
normalizeSampleName,
|
|
89
|
-
parseSampleUri,
|
|
90
|
-
resourceIndex,
|
|
91
91
|
getSampleEntries,
|
|
92
92
|
getSampleIdFromUri,
|
|
93
93
|
getDisplayEdition,
|
|
94
|
-
getDisplayPlatform
|
|
95
|
-
formatScopeLabel,
|
|
96
|
-
searchResources,
|
|
97
|
-
getSampleSuggestions
|
|
94
|
+
getDisplayPlatform
|
|
98
95
|
});
|
|
99
96
|
|
|
100
97
|
registerVersionTools({
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ListResourcesRequestSchema,
|
|
3
|
-
ReadResourceRequestSchema,
|
|
4
|
-
SubscribeRequestSchema,
|
|
5
|
-
UnsubscribeRequestSchema
|
|
6
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
1
|
+
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
7
2
|
|
|
8
3
|
export function registerResourceHandlers({
|
|
9
4
|
server,
|
|
@@ -12,25 +7,27 @@ export function registerResourceHandlers({
|
|
|
12
7
|
ensureLatestMajor,
|
|
13
8
|
readResourceContent
|
|
14
9
|
}) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
10
|
+
for (const entry of getPinnedResources()) {
|
|
11
|
+
server.registerResource(
|
|
12
|
+
entry.title,
|
|
13
|
+
entry.uri,
|
|
14
|
+
{
|
|
15
|
+
description: entry.summary,
|
|
16
|
+
mimeType: entry.mimeType
|
|
17
|
+
},
|
|
18
|
+
async (uri) => {
|
|
19
|
+
const resource = await readResourceContent(uri.toString());
|
|
20
|
+
if (!resource) {
|
|
21
|
+
throw new Error(`Resource not found: ${uri}`);
|
|
22
|
+
}
|
|
23
|
+
return { contents: [resource] };
|
|
24
|
+
}
|
|
25
|
+
);
|
|
26
|
+
}
|
|
31
27
|
|
|
32
|
-
|
|
33
|
-
const
|
|
28
|
+
async function templateReadHandler(uri) {
|
|
29
|
+
const uriStr = uri.toString();
|
|
30
|
+
const parsed = parseResourceUri(uriStr);
|
|
34
31
|
if (parsed && ["dcv", "dbr", "dwt", "ddv"].includes(parsed.product)) {
|
|
35
32
|
const policy = ensureLatestMajor({
|
|
36
33
|
product: parsed.product,
|
|
@@ -43,14 +40,25 @@ export function registerResourceHandlers({
|
|
|
43
40
|
}
|
|
44
41
|
}
|
|
45
42
|
|
|
46
|
-
const resource = await readResourceContent(
|
|
43
|
+
const resource = await readResourceContent(uriStr);
|
|
47
44
|
if (!resource) {
|
|
48
|
-
throw new Error(`Resource not found: ${
|
|
45
|
+
throw new Error(`Resource not found: ${uriStr}`);
|
|
49
46
|
}
|
|
50
47
|
|
|
51
48
|
return { contents: [resource] };
|
|
52
|
-
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
server.registerResource(
|
|
52
|
+
"doc-resource",
|
|
53
|
+
new ResourceTemplate("doc://{product}/{edition}/{platform}/{version}/{+slug}", {}),
|
|
54
|
+
{ description: "Dynamsoft documentation resource" },
|
|
55
|
+
templateReadHandler
|
|
56
|
+
);
|
|
53
57
|
|
|
54
|
-
server.
|
|
55
|
-
|
|
58
|
+
server.registerResource(
|
|
59
|
+
"sample-resource",
|
|
60
|
+
new ResourceTemplate("sample://{product}/{edition}/{platform}/{version}/{+rest}", {}),
|
|
61
|
+
{ description: "Dynamsoft sample code resource" },
|
|
62
|
+
templateReadHandler
|
|
63
|
+
);
|
|
56
64
|
}
|
|
@@ -11,14 +11,44 @@ export function registerIndexTools({
|
|
|
11
11
|
buildIndexData,
|
|
12
12
|
getSampleIdFromUri,
|
|
13
13
|
formatScopeLabel,
|
|
14
|
-
searchResources
|
|
14
|
+
searchResources,
|
|
15
|
+
normalizeSampleName,
|
|
16
|
+
getSampleEntries,
|
|
17
|
+
getSampleSuggestions
|
|
15
18
|
}) {
|
|
16
19
|
server.registerTool(
|
|
17
20
|
"get_index",
|
|
18
21
|
{
|
|
19
22
|
title: "Get Index",
|
|
20
|
-
description:
|
|
21
|
-
|
|
23
|
+
description: [
|
|
24
|
+
"Get a compact index of all Dynamsoft products, editions, platforms, versions, and available docs/samples.",
|
|
25
|
+
"",
|
|
26
|
+
"WHEN TO USE:",
|
|
27
|
+
"- As the first call in any conversation to discover what is available.",
|
|
28
|
+
"- To determine valid product/edition/platform combinations before calling other tools.",
|
|
29
|
+
"- To get DBR-vs-DCV selection guidance (DBR for barcode-only; DCV for MRZ, VIN, document normalization, driver license).",
|
|
30
|
+
"",
|
|
31
|
+
"WHEN NOT TO USE:",
|
|
32
|
+
"- Do not call get_index repeatedly; the index is static within a session.",
|
|
33
|
+
"- If you already know the product/edition/platform, skip directly to search or get_quickstart.",
|
|
34
|
+
"",
|
|
35
|
+
"RETURNS: A JSON object with top-level keys: productSelection and products. productSelection contains guidance for choosing between products (for example, DBR vs DCV), and products contains per-product entries (dcv, dbr, dwt, ddv) with editions, platforms, latest versions, and counts of available docs and samples.",
|
|
36
|
+
"",
|
|
37
|
+
"PARAMETERS: None.",
|
|
38
|
+
"",
|
|
39
|
+
"EXAMPLE WORKFLOW:",
|
|
40
|
+
"1. Call get_index to discover available products.",
|
|
41
|
+
"2. Use the returned product/edition/platform values in search, list_samples, or get_quickstart.",
|
|
42
|
+
"",
|
|
43
|
+
"RELATED TOOLS: search (find specific resources), list_samples (browse samples), resolve_version (get exact version numbers)."
|
|
44
|
+
].join("\n"),
|
|
45
|
+
inputSchema: {},
|
|
46
|
+
annotations: {
|
|
47
|
+
readOnlyHint: true,
|
|
48
|
+
destructiveHint: false,
|
|
49
|
+
idempotentHint: true,
|
|
50
|
+
openWorldHint: false
|
|
51
|
+
}
|
|
22
52
|
},
|
|
23
53
|
async () => ({
|
|
24
54
|
content: [{ type: "text", text: JSON.stringify(buildIndexData(), null, 2) }]
|
|
@@ -29,21 +59,50 @@ export function registerIndexTools({
|
|
|
29
59
|
"search",
|
|
30
60
|
{
|
|
31
61
|
title: "Search",
|
|
32
|
-
description:
|
|
62
|
+
description: [
|
|
63
|
+
"Search across documentation and samples using semantic (RAG) search with fuzzy fallback.",
|
|
64
|
+
"",
|
|
65
|
+
"WHEN TO USE:",
|
|
66
|
+
"- To find docs or samples by keyword, topic, or exact sample ID.",
|
|
67
|
+
"- To look up specific scenarios: MRZ scanning, VIN reading, barcode decoding, document normalization, etc.",
|
|
68
|
+
"- When you have a natural-language question about a Dynamsoft SDK.",
|
|
69
|
+
"- For sample lookup by exact ID (e.g. query='hello-world', type='sample').",
|
|
70
|
+
"",
|
|
71
|
+
"WHEN NOT TO USE:",
|
|
72
|
+
"- To browse all samples in a scope, use list_samples instead.",
|
|
73
|
+
"- To get starter code quickly, use get_quickstart instead.",
|
|
74
|
+
"- If you do not know valid products/editions, call get_index first.",
|
|
75
|
+
"",
|
|
76
|
+
"PARAMETERS:",
|
|
77
|
+
"- query (required): Keywords or exact sample ID. Examples: 'barcode scanning from camera', 'MRZ passport reader', 'hello-world'.",
|
|
78
|
+
"- product: dcv, dbr, dwt, or ddv. Prefer DCV for MRZ/VIN/document-normalization/driver-license; DBR for barcode-only.",
|
|
79
|
+
"- edition: core, mobile, web, or server.",
|
|
80
|
+
"- platform: android, ios, js, python, cpp, java, dotnet, nodejs, react, vue, angular, flutter, react-native, maui, etc.",
|
|
81
|
+
"- version: Version constraint (e.g. '10', '11.x'). Only latest major is served by default.",
|
|
82
|
+
"- type: 'doc', 'sample', 'index', 'policy', or 'any' (default). Use 'sample' to restrict to sample results.",
|
|
83
|
+
"- limit: 1-10 (default 5). Max number of results.",
|
|
84
|
+
"",
|
|
85
|
+
"RETURNS: An MCP response whose content array includes a leading text summary item followed by zero or more resource_link items with URIs. Use resources/read to fetch full content of doc:// URIs. Use get_sample_files to fetch full project files for sample:// URIs.",
|
|
86
|
+
"",
|
|
87
|
+
"RELATED TOOLS: get_index (discover products first), list_samples (browse all samples), get_sample_files (retrieve full sample project files), resources/read (read a doc resource)."
|
|
88
|
+
].join("\n"),
|
|
33
89
|
inputSchema: {
|
|
34
|
-
query: z.string().describe("Keywords to search across docs and samples."),
|
|
90
|
+
query: z.string().trim().min(1, "Query is required.").describe("Keywords to search across docs and samples."),
|
|
35
91
|
product: z.string().optional().describe("Product: dcv, dbr, dwt, ddv"),
|
|
36
92
|
edition: z.string().optional().describe("Edition: core, mobile, web, server/desktop"),
|
|
37
93
|
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
94
|
version: z.string().optional().describe("Version constraint (major or full version)"),
|
|
39
95
|
type: z.enum(["doc", "sample", "index", "policy", "any"]).optional(),
|
|
40
96
|
limit: z.number().int().min(1).max(10).optional().describe("Max results (default 5)")
|
|
97
|
+
},
|
|
98
|
+
annotations: {
|
|
99
|
+
readOnlyHint: true,
|
|
100
|
+
destructiveHint: false,
|
|
101
|
+
idempotentHint: true,
|
|
102
|
+
openWorldHint: false
|
|
41
103
|
}
|
|
42
104
|
},
|
|
43
105
|
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
106
|
const normalizedProduct = normalizeProduct(product);
|
|
48
107
|
const normalizedPlatform = normalizePlatform(platform);
|
|
49
108
|
const normalizedEdition = normalizeEdition(edition, normalizedPlatform, normalizedProduct);
|
|
@@ -68,16 +127,126 @@ export function registerIndexTools({
|
|
|
68
127
|
}
|
|
69
128
|
|
|
70
129
|
const maxResults = Math.min(limit || 5, 10);
|
|
130
|
+
|
|
131
|
+
// Exact sample-ID fast path (when searching for samples)
|
|
132
|
+
const effectiveType = type || "any";
|
|
133
|
+
if (effectiveType === "sample" || effectiveType === "any") {
|
|
134
|
+
const normalizedQuery = normalizeSampleName(query);
|
|
135
|
+
const scopedSamples = getSampleEntries({
|
|
136
|
+
product: normalizedProduct,
|
|
137
|
+
edition: normalizedEdition,
|
|
138
|
+
platform: normalizedPlatform
|
|
139
|
+
});
|
|
140
|
+
const exactMatches = scopedSamples.filter((entry) => {
|
|
141
|
+
const entryId = getSampleIdFromUri(entry.uri);
|
|
142
|
+
return entryId && entryId.toLowerCase() === normalizedQuery.toLowerCase();
|
|
143
|
+
});
|
|
144
|
+
if (exactMatches.length > 0) {
|
|
145
|
+
const selected = exactMatches.slice(0, maxResults);
|
|
146
|
+
const content = [
|
|
147
|
+
{
|
|
148
|
+
type: "text",
|
|
149
|
+
text: `Found ${selected.length} exact match(es) for "${query}". Read the links you need with resources/read.`
|
|
150
|
+
}
|
|
151
|
+
];
|
|
152
|
+
|
|
153
|
+
for (const entry of selected) {
|
|
154
|
+
const versionLabel = entry.version ? `v${entry.version}` : "n/a";
|
|
155
|
+
const scopeLabel = formatScopeLabel(entry);
|
|
156
|
+
const sampleId = getSampleIdFromUri(entry.uri);
|
|
157
|
+
const sampleHint = sampleId ? ` | sample_id: ${sampleId}` : "";
|
|
158
|
+
const scoreLabel = formatScoreLabel(entry);
|
|
159
|
+
content.push({
|
|
160
|
+
type: "resource_link",
|
|
161
|
+
uri: entry.uri,
|
|
162
|
+
name: entry.title,
|
|
163
|
+
description: `${entry.type.toUpperCase()} | ${scopeLabel} | ${versionLabel}${scoreLabel} - ${entry.summary}${sampleHint}`,
|
|
164
|
+
mimeType: entry.mimeType,
|
|
165
|
+
annotations: {
|
|
166
|
+
audience: ["assistant"],
|
|
167
|
+
priority: 0.8
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const plainLines = selected.map((entry, index) => {
|
|
173
|
+
const sampleId = getSampleIdFromUri(entry.uri);
|
|
174
|
+
const action = "get_sample_files resource_uri";
|
|
175
|
+
const sampleNote = sampleId ? ` sample_id=${sampleId}` : "";
|
|
176
|
+
const scoreNote = formatScoreNote(entry);
|
|
177
|
+
return `- ${index + 1}. ${entry.uri}${sampleNote}${scoreNote} (${action})`;
|
|
178
|
+
});
|
|
179
|
+
content.push({
|
|
180
|
+
type: "text",
|
|
181
|
+
text: ["Plain URIs (copy/paste):", ...plainLines].join("\n")
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
return { content };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
71
188
|
const topResults = await searchResources({
|
|
72
189
|
query,
|
|
73
190
|
product: normalizedProduct,
|
|
74
191
|
edition: normalizedEdition,
|
|
75
192
|
platform: normalizedPlatform,
|
|
76
|
-
type:
|
|
193
|
+
type: effectiveType,
|
|
77
194
|
limit: maxResults
|
|
78
195
|
});
|
|
79
196
|
|
|
80
197
|
if (topResults.length === 0) {
|
|
198
|
+
// Only try sample suggestions when searching samples or any type
|
|
199
|
+
if (effectiveType === "sample" || effectiveType === "any") {
|
|
200
|
+
const suggestions = await getSampleSuggestions({
|
|
201
|
+
query,
|
|
202
|
+
product: normalizedProduct,
|
|
203
|
+
edition: normalizedEdition,
|
|
204
|
+
platform: normalizedPlatform,
|
|
205
|
+
limit: maxResults
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
if (suggestions.length > 0) {
|
|
209
|
+
const content = [
|
|
210
|
+
{
|
|
211
|
+
type: "text",
|
|
212
|
+
text: `No exact results for "${query}". Related samples:`
|
|
213
|
+
}
|
|
214
|
+
];
|
|
215
|
+
|
|
216
|
+
for (const entry of suggestions) {
|
|
217
|
+
const versionLabel = entry.version ? `v${entry.version}` : "n/a";
|
|
218
|
+
const scopeLabel = formatScopeLabel(entry);
|
|
219
|
+
const sampleId = entry.type === "sample" ? getSampleIdFromUri(entry.uri) : "";
|
|
220
|
+
const sampleHint = sampleId ? ` | sample_id: ${sampleId}` : "";
|
|
221
|
+
const scoreLabel = formatScoreLabel(entry);
|
|
222
|
+
content.push({
|
|
223
|
+
type: "resource_link",
|
|
224
|
+
uri: entry.uri,
|
|
225
|
+
name: entry.title,
|
|
226
|
+
description: `${entry.type.toUpperCase()} | ${scopeLabel} | ${versionLabel}${scoreLabel} - ${entry.summary}${sampleHint}`,
|
|
227
|
+
mimeType: entry.mimeType,
|
|
228
|
+
annotations: {
|
|
229
|
+
audience: ["assistant"],
|
|
230
|
+
priority: 0.6
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const plainLines = suggestions.map((entry, index) => {
|
|
236
|
+
const sampleId = entry.type === "sample" ? getSampleIdFromUri(entry.uri) : "";
|
|
237
|
+
const sampleNote = sampleId ? ` sample_id=${sampleId}` : "";
|
|
238
|
+
const scoreNote = formatScoreNote(entry);
|
|
239
|
+
return `- ${index + 1}. ${entry.uri}${sampleNote}${scoreNote}`;
|
|
240
|
+
});
|
|
241
|
+
content.push({
|
|
242
|
+
type: "text",
|
|
243
|
+
text: ["Plain URIs (copy/paste):", ...plainLines].join("\n")
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
return { content };
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
81
250
|
return {
|
|
82
251
|
content: [{
|
|
83
252
|
type: "text",
|
|
@@ -114,7 +283,7 @@ export function registerIndexTools({
|
|
|
114
283
|
|
|
115
284
|
const plainLines = topResults.map((entry, index) => {
|
|
116
285
|
const sampleId = entry.type === "sample" ? getSampleIdFromUri(entry.uri) : "";
|
|
117
|
-
const action = entry.type === "sample" ? "
|
|
286
|
+
const action = entry.type === "sample" ? "get_sample_files resource_uri" : "resources/read uri";
|
|
118
287
|
const sampleNote = sampleId ? ` sample_id=${sampleId}` : "";
|
|
119
288
|
const scoreNote = formatScoreNote(entry);
|
|
120
289
|
return `- ${index + 1}. ${entry.uri}${sampleNote}${scoreNote} (${action})`;
|
|
@@ -27,18 +27,50 @@ export function registerProjectTools({
|
|
|
27
27
|
getSampleSuggestions
|
|
28
28
|
}) {
|
|
29
29
|
server.registerTool(
|
|
30
|
-
"
|
|
30
|
+
"get_sample_files",
|
|
31
31
|
{
|
|
32
|
-
title: "
|
|
33
|
-
description:
|
|
32
|
+
title: "Get Sample Files",
|
|
33
|
+
description: [
|
|
34
|
+
"Get the full project files for a known sample and return them inline as text.",
|
|
35
|
+
"",
|
|
36
|
+
"WHEN TO USE:",
|
|
37
|
+
"- When you have a sample_id (from list_samples) or a sample:// resource_uri (from search) and need the complete source code.",
|
|
38
|
+
"- When the user wants to see or scaffold a full sample project (multiple files, build configs, manifests).",
|
|
39
|
+
"",
|
|
40
|
+
"WHEN NOT TO USE:",
|
|
41
|
+
"- If you do not have a sample_id or resource_uri yet, call list_samples or search first to discover one.",
|
|
42
|
+
"- For doc:// URIs, use resources/read instead (this tool only handles sample:// URIs).",
|
|
43
|
+
"- If the user just wants a quick code snippet, use get_quickstart instead.",
|
|
44
|
+
"",
|
|
45
|
+
"PARAMETERS:",
|
|
46
|
+
"- product (required): dcv, dbr, dwt, or ddv.",
|
|
47
|
+
"- edition: mobile, web, or server.",
|
|
48
|
+
"- platform: android, ios, js, python, cpp, java, dotnet, nodejs, react, vue, angular, flutter, react-native, maui, etc.",
|
|
49
|
+
"- version: Version constraint. Latest major is used by default.",
|
|
50
|
+
"- sample_id: Sample identifier as returned by list_samples (e.g. 'hello-world', 'ScanSingleBarcode'). Requires product/edition.",
|
|
51
|
+
"- resource_uri: A sample:// URI as returned by search (e.g. 'sample://dbr/mobile/android/10/high-level/ScanSingleBarcode'). Preferred over sample_id when available.",
|
|
52
|
+
"- api_level: 'high-level' or 'low-level' (DBR mobile only).",
|
|
53
|
+
"",
|
|
54
|
+
"RETURNS: A text block containing all project files inline, each under a heading with its relative path and wrapped in a fenced code block. Files larger than 50KB are excluded. No zip file is created.",
|
|
55
|
+
"",
|
|
56
|
+
"EXAMPLE: get_sample_files with resource_uri='sample://dbr/mobile/android/10/high-level/ScanSingleBarcode' returns all source files for the Android barcode scanning sample.",
|
|
57
|
+
"",
|
|
58
|
+
"RELATED TOOLS: list_samples (discover sample IDs), search (find samples by keyword), get_quickstart (quick single-file snippet)."
|
|
59
|
+
].join("\n"),
|
|
34
60
|
inputSchema: {
|
|
35
|
-
product: z.string().describe("Product: dcv, dbr, dwt, or ddv"),
|
|
61
|
+
product: z.string().trim().min(1, "Product is required.").describe("Product: dcv, dbr, dwt, or ddv"),
|
|
36
62
|
edition: z.string().optional().describe("Edition: mobile, web, server/desktop"),
|
|
37
63
|
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"),
|
|
38
64
|
version: z.string().optional().describe("Version constraint"),
|
|
39
65
|
sample_id: z.string().optional().describe("Sample identifier (name or path)"),
|
|
40
66
|
resource_uri: z.string().optional().describe("Resource URI returned by search"),
|
|
41
67
|
api_level: z.string().optional().describe("API level: high-level or low-level (mobile only)")
|
|
68
|
+
},
|
|
69
|
+
annotations: {
|
|
70
|
+
readOnlyHint: true,
|
|
71
|
+
destructiveHint: false,
|
|
72
|
+
idempotentHint: true,
|
|
73
|
+
openWorldHint: false
|
|
42
74
|
}
|
|
43
75
|
},
|
|
44
76
|
async ({ product, edition, platform, version, sample_id, resource_uri, api_level }) => {
|
|
@@ -284,10 +316,10 @@ export function registerProjectTools({
|
|
|
284
316
|
const validFiles = files.filter((f) => f.content.length < 50000);
|
|
285
317
|
|
|
286
318
|
const output = [
|
|
287
|
-
`#
|
|
319
|
+
`# Sample Files: ${sampleLabel}`,
|
|
288
320
|
"",
|
|
289
|
-
"
|
|
290
|
-
"Note:
|
|
321
|
+
"Below are the retrieved sample project files.",
|
|
322
|
+
"Note: Files are returned inline and no downloadable zip is created.",
|
|
291
323
|
""
|
|
292
324
|
];
|
|
293
325
|
|
|
@@ -29,15 +29,46 @@ export function registerQuickstartTools({
|
|
|
29
29
|
"get_quickstart",
|
|
30
30
|
{
|
|
31
31
|
title: "Get Quickstart",
|
|
32
|
-
description:
|
|
32
|
+
description: [
|
|
33
|
+
"Get an opinionated quickstart with installation instructions and working sample code for a target product/edition/platform.",
|
|
34
|
+
"",
|
|
35
|
+
"WHEN TO USE:",
|
|
36
|
+
"- When the user wants to get started quickly with a Dynamsoft SDK.",
|
|
37
|
+
"- To generate a ready-to-run code snippet with install commands, license key, and SDK version.",
|
|
38
|
+
"- For scenario-specific starters: pass scenario='MRZ' for passport reading, 'VIN' for vehicle identification, 'document scan' for document normalization, etc.",
|
|
39
|
+
"",
|
|
40
|
+
"WHEN NOT TO USE:",
|
|
41
|
+
"- If the user wants full project files (multiple source files, build configs), use get_sample_files instead.",
|
|
42
|
+
"- If the user wants to browse available samples first, use search or list_samples.",
|
|
43
|
+
"- If the user only needs version info, use resolve_version.",
|
|
44
|
+
"",
|
|
45
|
+
"PARAMETERS:",
|
|
46
|
+
"- product (required): dcv, dbr, dwt, or ddv.",
|
|
47
|
+
"- edition: core, mobile, web, or server. Inferred from platform if omitted.",
|
|
48
|
+
"- platform: android, ios, js, python, cpp, java, dotnet, nodejs, react, vue, angular, flutter, react-native, maui, etc.",
|
|
49
|
+
"- language: kotlin, java, swift, js, ts, python, cpp, csharp, react, vue, angular. Helps select the best sample variant.",
|
|
50
|
+
"- version: Version constraint. Latest major is used by default.",
|
|
51
|
+
"- api_level: 'high-level' or 'low-level' (mobile only). Controls API abstraction level in generated code.",
|
|
52
|
+
"- scenario: MRZ, VIN, document scan, driver license, camera, image, single, multiple, etc. DCV supports MRZ/VIN/document-normalization/driver-license workflows.",
|
|
53
|
+
"",
|
|
54
|
+
"RETURNS: A formatted text block with SDK version, trial license key, install commands, and sample code. Ready to copy-paste.",
|
|
55
|
+
"",
|
|
56
|
+
"RELATED TOOLS: search (find specific docs or samples), get_sample_files (get full multi-file project), resolve_version (version numbers only)."
|
|
57
|
+
].join("\n"),
|
|
33
58
|
inputSchema: {
|
|
34
|
-
product: z.string().describe("Product: dcv, dbr, dwt, or ddv"),
|
|
59
|
+
product: z.string().trim().min(1, "Product is required.").describe("Product: dcv, dbr, dwt, or ddv"),
|
|
35
60
|
edition: z.string().optional().describe("Edition: core, mobile, web, server/desktop"),
|
|
36
61
|
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"),
|
|
37
62
|
language: z.string().optional().describe("Language hint: kotlin, java, swift, js, ts, python, cpp, csharp, react, vue, angular"),
|
|
38
63
|
version: z.string().optional().describe("Version constraint"),
|
|
39
64
|
api_level: z.string().optional().describe("API level: high-level or low-level (mobile only)"),
|
|
40
65
|
scenario: z.string().optional().describe("Scenario: camera, image, single, multiple, MRZ, VIN, document scan/normalization, driver license, react, etc.")
|
|
66
|
+
},
|
|
67
|
+
annotations: {
|
|
68
|
+
readOnlyHint: true,
|
|
69
|
+
destructiveHint: false,
|
|
70
|
+
idempotentHint: true,
|
|
71
|
+
openWorldHint: false
|
|
41
72
|
}
|
|
42
73
|
},
|
|
43
74
|
async ({ product, edition, platform, language, version, api_level, scenario }) => {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { formatScoreLabel, formatScoreNote } from "../helpers/server-helpers.js";
|
|
3
2
|
|
|
4
3
|
export function registerSampleTools({
|
|
5
4
|
server,
|
|
@@ -8,27 +7,51 @@ export function registerSampleTools({
|
|
|
8
7
|
normalizeProduct,
|
|
9
8
|
normalizePlatform,
|
|
10
9
|
normalizeEdition,
|
|
11
|
-
normalizeSampleName,
|
|
12
|
-
parseSampleUri,
|
|
13
|
-
resourceIndex,
|
|
14
10
|
getSampleEntries,
|
|
15
11
|
getSampleIdFromUri,
|
|
16
12
|
getDisplayEdition,
|
|
17
|
-
getDisplayPlatform
|
|
18
|
-
formatScopeLabel,
|
|
19
|
-
searchResources,
|
|
20
|
-
getSampleSuggestions
|
|
13
|
+
getDisplayPlatform
|
|
21
14
|
}) {
|
|
22
15
|
server.registerTool(
|
|
23
16
|
"list_samples",
|
|
24
17
|
{
|
|
25
18
|
title: "List Samples",
|
|
26
|
-
description:
|
|
19
|
+
description: [
|
|
20
|
+
"List all available sample IDs and URIs for a given product/edition/platform scope.",
|
|
21
|
+
"",
|
|
22
|
+
"WHEN TO USE:",
|
|
23
|
+
"- To browse the full catalog of samples available for a product/edition/platform.",
|
|
24
|
+
"- To discover sample IDs before calling get_sample_files.",
|
|
25
|
+
"- When the user wants to see what samples exist without a specific keyword.",
|
|
26
|
+
"- Use DCV scope for MRZ, VIN, document normalization, and driver license scenarios.",
|
|
27
|
+
"",
|
|
28
|
+
"WHEN NOT TO USE:",
|
|
29
|
+
"- If you have a specific keyword or topic, use search instead (it ranks results by relevance).",
|
|
30
|
+
"- If you already have a sample ID or URI, go directly to get_sample_files.",
|
|
31
|
+
"",
|
|
32
|
+
"PARAMETERS:",
|
|
33
|
+
"- product: dcv, dbr, dwt, or ddv. Omit to list across all products.",
|
|
34
|
+
"- edition: core, mobile, web, or server. Omit to list across all editions.",
|
|
35
|
+
"- platform: android, ios, js, python, cpp, java, dotnet, nodejs, react, vue, angular, flutter, react-native, maui, etc.",
|
|
36
|
+
"- limit: 1-200 (default 50). Max number of results.",
|
|
37
|
+
"",
|
|
38
|
+
"RETURNS: A single text content item that starts with totals and plain URIs, then appends 'JSON:' followed by a JSON object with total count and sample entries. Each entry includes sample_id, uri (sample:// URI), product, edition, platform, version, title, and summary. Use sample_id or uri with get_sample_files to retrieve full project files.",
|
|
39
|
+
"",
|
|
40
|
+
"EXAMPLE: Call list_samples with product='dbr', edition='mobile', platform='android' to see all Android barcode reader samples.",
|
|
41
|
+
"",
|
|
42
|
+
"RELATED TOOLS: search (keyword-based discovery), get_sample_files (retrieve full project files for a sample), get_index (discover valid product/edition/platform combinations)."
|
|
43
|
+
].join("\n"),
|
|
27
44
|
inputSchema: {
|
|
28
45
|
product: z.string().optional().describe("Product: dcv, dbr, dwt, ddv"),
|
|
29
46
|
edition: z.string().optional().describe("Edition: core, mobile, web, server/desktop"),
|
|
30
47
|
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"),
|
|
31
48
|
limit: z.number().int().min(1).max(200).optional().describe("Max results (default 50)")
|
|
49
|
+
},
|
|
50
|
+
annotations: {
|
|
51
|
+
readOnlyHint: true,
|
|
52
|
+
destructiveHint: false,
|
|
53
|
+
idempotentHint: true,
|
|
54
|
+
openWorldHint: false
|
|
32
55
|
}
|
|
33
56
|
},
|
|
34
57
|
async ({ product, edition, platform, limit }) => {
|
|
@@ -100,234 +123,4 @@ export function registerSampleTools({
|
|
|
100
123
|
};
|
|
101
124
|
}
|
|
102
125
|
);
|
|
103
|
-
|
|
104
|
-
server.registerTool(
|
|
105
|
-
"resolve_sample",
|
|
106
|
-
{
|
|
107
|
-
title: "Resolve Sample",
|
|
108
|
-
description: "Resolve a sample_id (or sample URI) to matching sample URIs.",
|
|
109
|
-
inputSchema: {
|
|
110
|
-
sample_id: z.string().describe("Sample identifier or sample:// URI"),
|
|
111
|
-
product: z.string().optional().describe("Product: dcv, dbr, dwt, ddv"),
|
|
112
|
-
edition: z.string().optional().describe("Edition: core, mobile, web, server/desktop"),
|
|
113
|
-
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"),
|
|
114
|
-
limit: z.number().int().min(1).max(10).optional().describe("Max results (default 5)")
|
|
115
|
-
}
|
|
116
|
-
},
|
|
117
|
-
async ({ sample_id, product, edition, platform, limit }) => {
|
|
118
|
-
if (!sample_id || !sample_id.trim()) {
|
|
119
|
-
return { isError: true, content: [{ type: "text", text: "sample_id is required." }] };
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const normalizedProduct = normalizeProduct(product);
|
|
123
|
-
const normalizedPlatform = normalizePlatform(platform);
|
|
124
|
-
const normalizedEdition = normalizeEdition(edition, normalizedPlatform, normalizedProduct);
|
|
125
|
-
|
|
126
|
-
await ensureScopeHydrated({
|
|
127
|
-
product: normalizedProduct,
|
|
128
|
-
edition: normalizedEdition,
|
|
129
|
-
platform: normalizedPlatform,
|
|
130
|
-
type: "sample"
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
const policy = ensureLatestMajor({
|
|
134
|
-
product: normalizedProduct,
|
|
135
|
-
version: undefined,
|
|
136
|
-
query: sample_id,
|
|
137
|
-
edition: normalizedEdition,
|
|
138
|
-
platform: normalizedPlatform
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
if (!policy.ok) {
|
|
142
|
-
return { isError: true, content: [{ type: "text", text: policy.message }] };
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (sample_id.includes("://")) {
|
|
146
|
-
const parsed = parseSampleUri(sample_id);
|
|
147
|
-
if (!parsed) {
|
|
148
|
-
return {
|
|
149
|
-
isError: true,
|
|
150
|
-
content: [{
|
|
151
|
-
type: "text",
|
|
152
|
-
text: "sample_id looks like a URI but is not a valid sample:// URI. For doc:// URIs, use resources/read."
|
|
153
|
-
}]
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
const entry = resourceIndex.find((item) => item.uri === sample_id && item.type === "sample");
|
|
157
|
-
if (!entry) {
|
|
158
|
-
return {
|
|
159
|
-
isError: true,
|
|
160
|
-
content: [{
|
|
161
|
-
type: "text",
|
|
162
|
-
text: `Sample URI not found in index: ${sample_id}. Use list_samples or search.`
|
|
163
|
-
}]
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const payload = [{
|
|
168
|
-
sample_id: getSampleIdFromUri(entry.uri),
|
|
169
|
-
uri: entry.uri,
|
|
170
|
-
product: entry.product,
|
|
171
|
-
edition: getDisplayEdition(entry.edition),
|
|
172
|
-
platform: getDisplayPlatform(entry.platform),
|
|
173
|
-
version: entry.version,
|
|
174
|
-
title: entry.title,
|
|
175
|
-
summary: entry.summary
|
|
176
|
-
}];
|
|
177
|
-
|
|
178
|
-
const output = {
|
|
179
|
-
query: sample_id,
|
|
180
|
-
returned: payload.length,
|
|
181
|
-
samples: payload
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
return {
|
|
185
|
-
content: [{
|
|
186
|
-
type: "text",
|
|
187
|
-
text: [
|
|
188
|
-
`Found ${payload.length} match(es) for "${sample_id}".`,
|
|
189
|
-
"Plain URIs (copy/paste):",
|
|
190
|
-
`- 1. ${entry.uri} (sample_id: ${payload[0].sample_id})`,
|
|
191
|
-
"",
|
|
192
|
-
"JSON:",
|
|
193
|
-
JSON.stringify(output, null, 2)
|
|
194
|
-
].join("\n")
|
|
195
|
-
}, {
|
|
196
|
-
type: "resource_link",
|
|
197
|
-
uri: entry.uri,
|
|
198
|
-
name: entry.title,
|
|
199
|
-
description: `SAMPLE | ${formatScopeLabel(entry)} | v${entry.version}${formatScoreLabel(entry)} | sample_id: ${payload[0].sample_id}`,
|
|
200
|
-
mimeType: entry.mimeType,
|
|
201
|
-
annotations: {
|
|
202
|
-
audience: ["assistant"],
|
|
203
|
-
priority: 0.8
|
|
204
|
-
}
|
|
205
|
-
}]
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const sampleQuery = normalizeSampleName(sample_id);
|
|
210
|
-
const maxResults = Math.min(limit || 5, 10);
|
|
211
|
-
|
|
212
|
-
const scopedSamples = getSampleEntries({
|
|
213
|
-
product: normalizedProduct,
|
|
214
|
-
edition: normalizedEdition,
|
|
215
|
-
platform: normalizedPlatform
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
let matches = scopedSamples.filter((entry) => {
|
|
219
|
-
const entryId = getSampleIdFromUri(entry.uri);
|
|
220
|
-
return entryId && entryId.toLowerCase() === sampleQuery.toLowerCase();
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
if (matches.length === 0) {
|
|
224
|
-
matches = await searchResources({
|
|
225
|
-
query: sample_id,
|
|
226
|
-
product: normalizedProduct,
|
|
227
|
-
edition: normalizedEdition,
|
|
228
|
-
platform: normalizedPlatform,
|
|
229
|
-
type: "sample",
|
|
230
|
-
limit: maxResults
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const selected = matches.slice(0, maxResults);
|
|
235
|
-
if (selected.length === 0) {
|
|
236
|
-
const suggestions = await getSampleSuggestions({
|
|
237
|
-
query: sample_id,
|
|
238
|
-
product: normalizedProduct,
|
|
239
|
-
edition: normalizedEdition,
|
|
240
|
-
platform: normalizedPlatform,
|
|
241
|
-
limit: maxResults
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
const content = [{
|
|
245
|
-
type: "text",
|
|
246
|
-
text: suggestions.length
|
|
247
|
-
? `No exact sample match for "${sample_id}". Related samples:`
|
|
248
|
-
: `No samples found for "${sample_id}". Try list_samples or search.`
|
|
249
|
-
}];
|
|
250
|
-
|
|
251
|
-
for (const entry of suggestions) {
|
|
252
|
-
const sampleId = getSampleIdFromUri(entry.uri);
|
|
253
|
-
content.push({
|
|
254
|
-
type: "resource_link",
|
|
255
|
-
uri: entry.uri,
|
|
256
|
-
name: entry.title,
|
|
257
|
-
description: `${entry.type.toUpperCase()} | ${formatScopeLabel(entry)} | v${entry.version}${formatScoreLabel(entry)} | sample_id: ${sampleId || "n/a"}`,
|
|
258
|
-
mimeType: entry.mimeType,
|
|
259
|
-
annotations: {
|
|
260
|
-
audience: ["assistant"],
|
|
261
|
-
priority: 0.6
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
if (suggestions.length) {
|
|
267
|
-
const plainLines = suggestions.map((entry, index) => {
|
|
268
|
-
const sampleId = getSampleIdFromUri(entry.uri);
|
|
269
|
-
const sampleNote = sampleId ? ` (sample_id: ${sampleId})` : "";
|
|
270
|
-
const scoreNote = formatScoreNote(entry);
|
|
271
|
-
return `- ${index + 1}. ${entry.uri}${sampleNote}${scoreNote}`;
|
|
272
|
-
});
|
|
273
|
-
content.push({
|
|
274
|
-
type: "text",
|
|
275
|
-
text: ["Plain URIs (copy/paste):", ...plainLines].join("\n")
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
return { isError: true, content };
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
const payload = selected.map((entry) => ({
|
|
283
|
-
sample_id: getSampleIdFromUri(entry.uri),
|
|
284
|
-
uri: entry.uri,
|
|
285
|
-
product: entry.product,
|
|
286
|
-
edition: getDisplayEdition(entry.edition),
|
|
287
|
-
platform: getDisplayPlatform(entry.platform),
|
|
288
|
-
version: entry.version,
|
|
289
|
-
title: entry.title,
|
|
290
|
-
summary: entry.summary
|
|
291
|
-
}));
|
|
292
|
-
|
|
293
|
-
const lines = [
|
|
294
|
-
`Found ${selected.length} match(es) for "${sample_id}".`,
|
|
295
|
-
"Plain URIs (copy/paste):",
|
|
296
|
-
...selected.map((entry, index) => {
|
|
297
|
-
const sampleId = getSampleIdFromUri(entry.uri);
|
|
298
|
-
const sampleNote = sampleId ? ` (sample_id: ${sampleId})` : "";
|
|
299
|
-
const scoreNote = formatScoreNote(entry);
|
|
300
|
-
return `- ${index + 1}. ${entry.uri}${sampleNote}${scoreNote}`;
|
|
301
|
-
})
|
|
302
|
-
];
|
|
303
|
-
|
|
304
|
-
const output = {
|
|
305
|
-
query: sample_id,
|
|
306
|
-
returned: payload.length,
|
|
307
|
-
samples: payload
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
const content = [{
|
|
311
|
-
type: "text",
|
|
312
|
-
text: `${lines.join("\n")}\n\nJSON:\n${JSON.stringify(output, null, 2)}`
|
|
313
|
-
}];
|
|
314
|
-
|
|
315
|
-
for (const entry of selected) {
|
|
316
|
-
const sampleId = getSampleIdFromUri(entry.uri);
|
|
317
|
-
content.push({
|
|
318
|
-
type: "resource_link",
|
|
319
|
-
uri: entry.uri,
|
|
320
|
-
name: entry.title,
|
|
321
|
-
description: `${entry.type.toUpperCase()} | ${formatScopeLabel(entry)} | v${entry.version}${formatScoreLabel(entry)} | sample_id: ${sampleId || "n/a"}`,
|
|
322
|
-
mimeType: entry.mimeType,
|
|
323
|
-
annotations: {
|
|
324
|
-
audience: ["assistant"],
|
|
325
|
-
priority: 0.8
|
|
326
|
-
}
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
return { content };
|
|
331
|
-
}
|
|
332
|
-
);
|
|
333
126
|
}
|
|
@@ -13,13 +13,43 @@ export function registerVersionTools({
|
|
|
13
13
|
"resolve_version",
|
|
14
14
|
{
|
|
15
15
|
title: "Resolve Version",
|
|
16
|
-
description:
|
|
16
|
+
description: [
|
|
17
|
+
"Resolve a concrete latest-major version number for a Dynamsoft product/edition/platform.",
|
|
18
|
+
"",
|
|
19
|
+
"WHEN TO USE:",
|
|
20
|
+
"- To get the exact current version string (e.g. '10.4.2001') for use in package installation or dependency pinning.",
|
|
21
|
+
"- When the user asks 'what is the latest version of DBR?'.",
|
|
22
|
+
"- To verify version compatibility before generating project scaffolding.",
|
|
23
|
+
"",
|
|
24
|
+
"WHEN NOT TO USE:",
|
|
25
|
+
"- If you just need sample code, use get_quickstart (it already includes the correct version).",
|
|
26
|
+
"- For browsing docs or samples, use search or list_samples (they already scope to latest major).",
|
|
27
|
+
"",
|
|
28
|
+
"PARAMETERS:",
|
|
29
|
+
"- product (required): dcv, dbr, dwt, or ddv.",
|
|
30
|
+
"- edition: core, mobile, web, or server. Omit to see all editions for the product.",
|
|
31
|
+
"- platform: android, ios, js, python, cpp, java, dotnet, nodejs, etc. Helps narrow edition when ambiguous.",
|
|
32
|
+
"- constraint: Version constraint like 'latest', '11.x', '10'. Only latest major version is served; legacy versions (e.g. DBR v9) return an error with migration guidance.",
|
|
33
|
+
"- feature: Optional feature hint for version policy checks.",
|
|
34
|
+
"",
|
|
35
|
+
"RETURNS: A text block showing the resolved version. For DCV/DBR without an edition, returns all edition versions. For DWT/DDV, returns the single web version.",
|
|
36
|
+
"",
|
|
37
|
+
"EXAMPLE: resolve_version with product='dbr', edition='web' returns the latest DBR web SDK version string.",
|
|
38
|
+
"",
|
|
39
|
+
"RELATED TOOLS: get_quickstart (includes version in starter code), get_index (shows version overview)."
|
|
40
|
+
].join("\n"),
|
|
17
41
|
inputSchema: {
|
|
18
|
-
product: z.string().describe("Product: dcv, dbr, dwt, or ddv"),
|
|
42
|
+
product: z.string().trim().min(1, "Product is required.").describe("Product: dcv, dbr, dwt, or ddv"),
|
|
19
43
|
edition: z.string().optional().describe("Edition: core, mobile, web, server/desktop"),
|
|
20
44
|
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"),
|
|
21
45
|
constraint: z.string().optional().describe("Version constraint, e.g., latest, 11.x, 10"),
|
|
22
46
|
feature: z.string().optional().describe("Optional feature hint")
|
|
47
|
+
},
|
|
48
|
+
annotations: {
|
|
49
|
+
readOnlyHint: true,
|
|
50
|
+
destructiveHint: false,
|
|
51
|
+
idempotentHint: true,
|
|
52
|
+
openWorldHint: false
|
|
23
53
|
}
|
|
24
54
|
},
|
|
25
55
|
async ({ product, edition, platform, constraint, feature }) => {
|