simple-dynamsoft-mcp 7.3.54 → 7.4.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.
@@ -1,7 +1,13 @@
1
1
  import { existsSync, statSync } from "node:fs";
2
2
  import { extname, join } from "node:path";
3
3
  import { z } from "zod";
4
- import { buildUnknownPublicProductResponse, isKnownPublicOffering } from "../public-offerings.js";
4
+ import {
5
+ buildUnknownPublicProductResponse,
6
+ isKnownPublicOffering,
7
+ API_LEVEL_NOTE,
8
+ DBR_ONLY_EDITIONS_NOTE,
9
+ WEB_ONLY_OMIT_NOTE
10
+ } from "../public-offerings.js";
5
11
  import { buildUnsupportedPublicScopeResponse } from "./public-routing.js";
6
12
 
7
13
  export function registerQuickstartTools({
@@ -25,8 +31,54 @@ export function registerQuickstartTools({
25
31
  getDdvSamplePath,
26
32
  readCodeFile,
27
33
  getMainCodeFile,
28
- getWebSamplePath
34
+ getWebSamplePath,
35
+ getMrzWebSamplePath,
36
+ getMdsWebSamplePath,
37
+ getSampleSuggestions,
38
+ getSampleIdFromUri,
39
+ formatScopeLabel
29
40
  }) {
41
+ // Actionable miss response: instead of a bare "Sample not found", surface
42
+ // related samples the agent can fetch next (mirrors get_sample_files). (#151)
43
+ async function buildQuickstartMissResponse({ reason, product, edition, platform, scenario }) {
44
+ const lines = [reason];
45
+ let suggestions = [];
46
+ if (typeof getSampleSuggestions === "function") {
47
+ try {
48
+ suggestions = await getSampleSuggestions({
49
+ query: [scenario, product, edition, platform].filter(Boolean).join(" "),
50
+ product,
51
+ edition,
52
+ platform,
53
+ limit: 5
54
+ });
55
+ } catch {
56
+ suggestions = [];
57
+ }
58
+ }
59
+ const content = [{
60
+ type: "text",
61
+ text: suggestions.length
62
+ ? `${reason}\nRelated samples you can fetch with get_sample_files or open with get_quickstart:`
63
+ : `${reason}\nCall get_index to see valid product/edition/platform combinations, or search for a scenario.`
64
+ }];
65
+ for (const entry of suggestions) {
66
+ const versionLabel = entry.version ? `v${entry.version}` : "n/a";
67
+ const scopeLabel = typeof formatScopeLabel === "function" ? formatScopeLabel(entry) : `${entry.product}/${entry.edition}`;
68
+ const sampleId = entry.type === "sample" && typeof getSampleIdFromUri === "function" ? getSampleIdFromUri(entry.uri) : "";
69
+ const sampleHint = sampleId ? ` | sample_id: ${sampleId}` : "";
70
+ content.push({
71
+ type: "resource_link",
72
+ uri: entry.uri,
73
+ name: entry.title,
74
+ description: `${entry.type.toUpperCase()} | ${scopeLabel} | ${versionLabel} - ${entry.summary}${sampleHint}`,
75
+ mimeType: entry.mimeType,
76
+ annotations: { audience: ["assistant"], priority: 0.6 }
77
+ });
78
+ }
79
+ return { isError: true, content };
80
+ }
81
+
30
82
  function getPublicWebQuickstartLinks(product) {
31
83
  if (product === "mrz") {
32
84
  return {
@@ -94,11 +146,11 @@ export function registerQuickstartTools({
94
146
  "",
95
147
  "PARAMETERS:",
96
148
  "- product (required): dbr, dwt, ddv, mrz, or mds.",
97
- "- edition: core, mobile, web, or server. Inferred from platform if omitted.",
98
- "- platform: android, ios, js, python, cpp, java, dotnet, nodejs, react, vue, angular, flutter, react-native, maui, etc.",
149
+ `- edition: core, mobile, web, or server. ${DBR_ONLY_EDITIONS_NOTE} Inferred from platform if omitted.`,
150
+ `- platform: only DBR spans multiple platforms (android, ios, js, python, cpp, java, dotnet, nodejs, react, vue, angular, flutter, react-native, maui, etc.). ${WEB_ONLY_OMIT_NOTE}`,
99
151
  "- language: kotlin, java, swift, js, ts, python, cpp, csharp, react, vue, angular. Helps select the best sample variant.",
100
152
  "- version: Version constraint. Latest major is used by default.",
101
- "- api_level: 'high-level' or 'low-level' (mobile only). Controls API abstraction level in generated code.",
153
+ `- api_level: ${API_LEVEL_NOTE}`,
102
154
  "- scenario: MRZ, document scan, camera, image, single, multiple, react, vue, angular, etc. DBR web defaults to foundational guidance; MRZ and MDS return public workflow guidance where available.",
103
155
  "",
104
156
  "RETURNS: A formatted text block with SDK version, trial license key, install commands, and sample code. Ready to copy-paste.",
@@ -107,11 +159,11 @@ export function registerQuickstartTools({
107
159
  ].join("\n"),
108
160
  inputSchema: {
109
161
  product: z.string().trim().min(1, "Product is required.").describe("Product: dbr, dwt, ddv, mrz, mds"),
110
- edition: z.string().optional().describe("Edition: core, mobile, web, server/desktop"),
111
- 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"),
162
+ edition: z.string().optional().describe(`Edition: core, mobile, web, server/desktop. ${DBR_ONLY_EDITIONS_NOTE}`),
163
+ platform: z.string().optional().describe(`Platform (DBR only spans multiple): 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. ${WEB_ONLY_OMIT_NOTE}`),
112
164
  language: z.string().optional().describe("Language hint: kotlin, java, swift, js, ts, python, cpp, csharp, react, vue, angular"),
113
165
  version: z.string().optional().describe("Version constraint"),
114
- api_level: z.string().optional().describe("API level: high-level or low-level (mobile only)"),
166
+ api_level: z.string().optional().describe(`API level: ${API_LEVEL_NOTE}`),
115
167
  scenario: z.string().optional().describe("Scenario: camera, image, single, multiple, MRZ, document scan/normalization, driver license, react, etc.")
116
168
  },
117
169
  annotations: {
@@ -281,7 +333,44 @@ export function registerQuickstartTools({
281
333
  if (effectiveEdition === "web") {
282
334
  const sdkEntry = registry.sdks["dcv-web"];
283
335
  if (isPublicDcvProduct) {
336
+ // Serve the hydrated hello-world sample inline instead of link-only.
337
+ // The dedicated web sample repos ship runnable starters (#136).
284
338
  const publicLinks = getPublicWebQuickstartLinks(normalizedProduct);
339
+ const getDedicated = normalizedProduct === "mrz" ? getMrzWebSamplePath : getMdsWebSamplePath;
340
+ let dedicatedSample = "hello-world";
341
+ if (normalizedProduct === "mds" && (scenarioLower.includes("pdf"))) dedicatedSample = "scanning-to-pdf";
342
+ else if (scenarioLower.includes("image") || scenarioLower.includes("file")) {
343
+ dedicatedSample = normalizedProduct === "mrz" ? "use-file-input" : "image-file-scanning";
344
+ }
345
+ const dedicatedPath = typeof getDedicated === "function" ? getDedicated("root", dedicatedSample) : "";
346
+ // Guard against a directory result (the resolver can return a dir when a
347
+ // sample lacks a single html entry): readCodeFile would throw EISDIR.
348
+ if (dedicatedPath && existsSync(dedicatedPath) && statSync(dedicatedPath).isFile()) {
349
+ const dedicatedContent = readCodeFile(dedicatedPath);
350
+ const mrzFields = normalizedProduct === "mrz"
351
+ ? "\n**Result fields** (`result.data`): firstName, lastName, documentNumber, dateOfExpiry, dateOfBirth, nationality, sex, issuingState.\n"
352
+ : "";
353
+ return {
354
+ content: [{
355
+ type: "text",
356
+ text: [
357
+ `# Quick Start: ${publicProductLabel} Web`,
358
+ "",
359
+ `**SDK Version:** ${sdkEntry.version}`,
360
+ `**Trial License:** \`${registry.trial_license}\``,
361
+ mrzFields,
362
+ `## Sample: ${dedicatedSample}.html`,
363
+ "```html",
364
+ dedicatedContent,
365
+ "```",
366
+ "",
367
+ `Docs: ${publicLinks.docsUrl}`,
368
+ `More samples: ${publicLinks.samplesUrl}`
369
+ ].filter(Boolean).join("\n")
370
+ }]
371
+ };
372
+ }
373
+ // Fall back to reference links only if the sample is not hydrated.
285
374
  return buildPublicReferenceQuickstart({
286
375
  product: normalizedProduct,
287
376
  edition: effectiveEdition,
@@ -386,36 +475,80 @@ export function registerQuickstartTools({
386
475
  if (normalizedProduct === "dbr" && normalizedEdition === "server") {
387
476
  const sdkEntry = registry.sdks["dbr-server"];
388
477
  const scenarioLower = (scenario || "").toLowerCase();
389
- const sampleName = scenarioLower.includes("video") ? "video_decoding" : "read_an_image";
390
- const samplePath = getDbrServerSamplePath("python", sampleName);
478
+ const targetPlatform = normalizePlatform(normalizedPlatform) || "python";
479
+
480
+ // Per-platform "hello world" sample names (repos differ in convention).
481
+ const SERVER_DEFAULT_SAMPLE = {
482
+ python: scenarioLower.includes("video") ? "video_decoding" : "read_an_image",
483
+ nodejs: "express",
484
+ java: scenarioLower.includes("video") ? "VideoDecoding" : "HelloWorld",
485
+ cpp: scenarioLower.includes("video") ? "VideoDecoding" : "HelloWorld",
486
+ dotnet: "HelloWorld"
487
+ };
488
+ const sampleName = SERVER_DEFAULT_SAMPLE[targetPlatform];
489
+ if (!sampleName) {
490
+ return await buildQuickstartMissResponse({
491
+ reason: `DBR server quickstart is not available for platform "${targetPlatform}". Supported: python, nodejs, java, cpp, dotnet.`,
492
+ product: "dbr", edition: "server", platform: targetPlatform, scenario
493
+ });
494
+ }
391
495
 
392
- if (!existsSync(samplePath)) {
393
- return { isError: true, content: [{ type: "text", text: `Sample not found: ${sampleName}.` }] };
496
+ const samplePath = getDbrServerSamplePath(targetPlatform, sampleName);
497
+ if (!samplePath || !existsSync(samplePath)) {
498
+ return await buildQuickstartMissResponse({
499
+ reason: `DBR server sample "${sampleName}" not found for ${targetPlatform}.`,
500
+ product: "dbr", edition: "server", platform: targetPlatform, scenario
501
+ });
394
502
  }
395
503
 
396
- const content = readCodeFile(samplePath);
504
+ // java/cpp/dotnet/nodejs samples are project directories; python is a file.
505
+ let contentFileName = sampleName;
506
+ let contentPath = samplePath;
507
+ if (statSync(samplePath).isDirectory()) {
508
+ const codeFiles = findCodeFilesInSample(samplePath);
509
+ const main = codeFiles.find((f) => /^(main|program|hello|read|index|app|server)/i.test(f.filename)) || codeFiles[0];
510
+ if (!main) {
511
+ return await buildQuickstartMissResponse({
512
+ reason: `Found the ${sampleName} project for ${targetPlatform} but no primary source file inside it.`,
513
+ product: "dbr", edition: "server", platform: targetPlatform, scenario
514
+ });
515
+ }
516
+ contentPath = main.path;
517
+ contentFileName = main.relativePath || main.filename;
518
+ }
519
+ const content = readCodeFile(contentPath);
520
+ const platformLabel = { python: "Python", nodejs: "Node.js", java: "Java", cpp: "C++", dotnet: ".NET" }[targetPlatform] || targetPlatform;
521
+ const installLines = [];
522
+ const installObj = sdkEntry.platforms?.[targetPlatform]?.installation;
523
+ if (installObj && typeof installObj === "object") {
524
+ for (const value of Object.values(installObj)) {
525
+ if (typeof value === "string" && value.trim()) installLines.push(value);
526
+ }
527
+ }
528
+ const fence = { python: "python", nodejs: "javascript", java: "java", cpp: "cpp", dotnet: "csharp" }[targetPlatform] || "text";
397
529
 
398
530
  return {
399
531
  content: [{
400
532
  type: "text",
401
533
  text: [
402
- "# Quick Start: DBR Server (Python)",
534
+ `# Quick Start: DBR Server (${platformLabel})`,
403
535
  "",
404
536
  `**SDK Version:** ${sdkEntry.version}`,
405
537
  `**Trial License:** \`${registry.trial_license}\``,
406
538
  "",
407
- "## Install",
408
- "```bash",
409
- sdkEntry.platforms.python.installation.pip,
410
- "```",
411
- "",
412
- `## ${sampleName}.py`,
413
- "```python",
539
+ installLines.length ? "## Install" : "",
540
+ installLines.length ? "```bash" : "",
541
+ ...installLines,
542
+ installLines.length ? "```" : "",
543
+ installLines.length ? "" : "",
544
+ `## ${sampleName} — ${contentFileName}`,
545
+ "```" + fence,
414
546
  content,
415
547
  "```",
416
548
  "",
417
- `Docs: ${sdkEntry.platforms.python.docs["user-guide"]}`
418
- ].join("\n")
549
+ `Full project files: get_sample_files product=dbr edition=server platform=${targetPlatform} sample_id=${sampleName}`,
550
+ `Docs: ${sdkEntry.platforms?.[targetPlatform]?.docs?.["user-guide"] || "N/A"}`
551
+ ].filter(Boolean).join("\n")
419
552
  }]
420
553
  };
421
554
  }
@@ -423,14 +556,106 @@ export function registerQuickstartTools({
423
556
  if (normalizedProduct === "dbr" && normalizedEdition === "web") {
424
557
  const sdkEntry = registry.sdks["dbr-web"];
425
558
  const scenarioLower = (scenario || "").toLowerCase();
426
- const sampleName = scenarioLower.includes("hello") ? "hello-world" : "read-an-image";
427
- const samplePath = getWebSamplePath("root", sampleName);
559
+
560
+ // Framework-specific starter: if the caller named a JS framework that ships
561
+ // a project under frameworks/, serve runnable foundational code inline AND
562
+ // point to the full framework project via get_sample_files. Framework
563
+ // samples are multi-file Vite/CLI projects, so a single inlined file is not
564
+ // standalone-runnable — the fetch pointer is how the agent gets the rest
565
+ // (issue #134).
566
+ const DBR_WEB_FRAMEWORKS = new Set(["react", "angular", "vue", "next", "nuxt", "svelte", "electron", "capacitor", "blazor", "es6", "native-ts", "pwa", "requirejs", "webview"]);
567
+ if (normalizedPlatform && DBR_WEB_FRAMEWORKS.has(normalizedPlatform)) {
568
+ const foundationalPath = getWebSamplePath("basics", "scan-a-single-barcode")
569
+ || getWebSamplePath("basics", "hello-world");
570
+ const foundationalCode = foundationalPath && existsSync(foundationalPath) ? readCodeFile(foundationalPath) : "";
571
+ return {
572
+ content: [{
573
+ type: "text",
574
+ text: [
575
+ `# Quick Start: DBR Web (${normalizedPlatform})`,
576
+ "",
577
+ `**SDK Version:** ${sdkEntry.version}`,
578
+ `**Trial License:** \`${registry.trial_license}\``,
579
+ `**Starter profile:** ${normalizedPlatform} framework project available`,
580
+ "",
581
+ "## Install (npm)",
582
+ "```bash",
583
+ sdkEntry.platforms.web.installation.npm,
584
+ "```",
585
+ "",
586
+ `## Full ${normalizedPlatform} project`,
587
+ `A ready-to-run ${normalizedPlatform} sample ships with this MCP. Fetch all its files with:`,
588
+ "```",
589
+ `get_sample_files product=dbr edition=web platform=${normalizedPlatform} sample_id=frameworks/${normalizedPlatform}`,
590
+ "```",
591
+ foundationalCode ? "" : null,
592
+ foundationalCode ? "## Foundational reference (vanilla JS — the same CaptureVisionRouter flow the framework sample wraps)" : null,
593
+ foundationalCode ? "```html" : null,
594
+ foundationalCode || null,
595
+ foundationalCode ? "```" : null,
596
+ "",
597
+ `Docs: ${sdkEntry.platforms.web.docs["user-guide"]}`
598
+ ].filter((line) => line !== null).join("\n")
599
+ }]
600
+ };
601
+ }
602
+
603
+ // Map the scenario to a foundational sample under basics/. Web has no
604
+ // high-level vs low-level API split (api_level is a mobile-only concept),
605
+ // so it is intentionally not consulted here.
606
+ let sampleName;
607
+ // The camera scenario fallback below substitutes a differently-named
608
+ // scenarios/* sample, so it is only appropriate for the generic
609
+ // camera/single/default request — not for requests that named a
610
+ // specific starter (hello) or a different intent (image/file, which is
611
+ // camera-based nowhere in scenarios/*). Those hard-error honestly when
612
+ // their sample is absent.
613
+ let allowCameraFallback = false;
614
+ if (scenarioLower.includes("hello")) {
615
+ sampleName = "hello-world";
616
+ } else if (scenarioLower.includes("image") || scenarioLower.includes("file")) {
617
+ sampleName = "read-an-image";
618
+ } else {
619
+ sampleName = "scan-a-single-barcode"; // camera / single / default
620
+ allowCameraFallback = true;
621
+ }
622
+
623
+ // Foundational samples now live under basics/ upstream; getWebSamplePath
624
+ // falls back to the repo root when basics/ is absent, so this stays
625
+ // compatible with older sample sets that kept them at the root.
626
+ let samplePath = getWebSamplePath("basics", sampleName);
627
+ let fallbackSample = null;
628
+
629
+ // Graceful degradation: a deployment may serve an older sample data set
630
+ // that predates the foundational samples. Rather than hard-erroring,
631
+ // fall back to a reliably-present camera scenario sample — but only when
632
+ // the request is camera-intent (see allowCameraFallback above).
633
+ if ((!samplePath || !existsSync(samplePath)) && allowCameraFallback) {
634
+ for (const candidate of ["scan-common-1D-and-2D", "scan-qr-code"]) {
635
+ const candidatePath = getWebSamplePath("scenarios", candidate);
636
+ if (candidatePath && existsSync(candidatePath)) {
637
+ samplePath = candidatePath;
638
+ fallbackSample = candidate;
639
+ break;
640
+ }
641
+ }
642
+ }
428
643
 
429
644
  if (!samplePath || !existsSync(samplePath)) {
430
- return { isError: true, content: [{ type: "text", text: `Sample not found: ${sampleName}.` }] };
645
+ return await buildQuickstartMissResponse({
646
+ reason: `DBR web sample "${sampleName}" not found in the served sample set.`,
647
+ product: "dbr", edition: "web", platform: normalizedPlatform, scenario
648
+ });
431
649
  }
432
650
 
433
651
  const content = readCodeFile(samplePath);
652
+ const displaySample = fallbackSample || sampleName;
653
+ const sampleHeading = fallbackSample
654
+ ? `## Sample: ${displaySample} (camera scenario fallback)`
655
+ : `## Foundational sample: ${displaySample}`;
656
+ const fallbackNoteLines = fallbackSample
657
+ ? [`> Note: \`${sampleName}\` was not available in the served sample set; showing the \`${fallbackSample}\` camera scenario instead.`, ""]
658
+ : [];
434
659
 
435
660
  return {
436
661
  content: [{
@@ -443,7 +668,9 @@ export function registerQuickstartTools({
443
668
  "**Starter profile:** Foundational-first",
444
669
  "",
445
670
  "Use the foundational web flow first so capture and decoding remain explicit and easier to adapt.",
671
+ "api_level does not apply to DBR web (it is a mobile-only distinction).",
446
672
  "",
673
+ ...fallbackNoteLines,
447
674
  "## Option 1: CDN",
448
675
  "```html",
449
676
  `<script src="${sdkEntry.platforms.web.installation.cdn}"></script>`,
@@ -454,7 +681,7 @@ export function registerQuickstartTools({
454
681
  sdkEntry.platforms.web.installation.npm,
455
682
  "```",
456
683
  "",
457
- `## Foundational sample: ${sampleName}.html`,
684
+ sampleHeading,
458
685
  "```html",
459
686
  content,
460
687
  "```",
@@ -471,24 +698,73 @@ export function registerQuickstartTools({
471
698
  const level = normalizeApiLevel(api_level || scenario);
472
699
  const scenarioLower = (scenario || "").toLowerCase();
473
700
 
474
- let sampleName = "ScanSingleBarcode";
475
- if (scenarioLower.includes("multiple") || scenarioLower.includes("batch")) sampleName = "ScanMultipleBarcodes";
476
- else if (scenarioLower.includes("image") || scenarioLower.includes("file")) sampleName = "DecodeFromAnImage";
701
+ const isCrossPlatform = ["flutter", "react-native", "maui"].includes(targetPlatform);
702
+ const wantsImage = scenarioLower.includes("image") || scenarioLower.includes("file");
703
+ const wantsDriverLicense = scenarioLower.includes("driver") || scenarioLower.includes("license") || scenarioLower.includes("licence");
477
704
 
478
- if (level === "low-level") {
479
- if (sampleName === "ScanSingleBarcode" || sampleName === "ScanMultipleBarcodes") {
480
- sampleName = "DecodeWithCameraEnhancer";
705
+ let sampleName;
706
+ if (isCrossPlatform) {
707
+ // Cross-platform repos use different sample names than Android/iOS. There
708
+ // is no still-image decode sample in these editions (#132, #159).
709
+ if (wantsImage) {
710
+ return {
711
+ content: [{
712
+ type: "text",
713
+ text: [
714
+ `# DBR ${targetPlatform}: still-image decoding`,
715
+ "",
716
+ `The ${targetPlatform} edition ships camera-stream barcode scanning, not a still-image/gallery decode sample.`,
717
+ "",
718
+ "Nearest options:",
719
+ "- Use the camera-based scanner below (ScanBarcodes_ReadyToUseComponent).",
720
+ "- For decoding a picked/gallery image, use the native Android/iOS `DecodeFromAnImage` sample (get_quickstart product=dbr edition=mobile platform=android scenario=image) and bridge it, or call the foundational API on a captured frame.",
721
+ "",
722
+ `Docs: ${sdkEntry.platforms[targetPlatform]?.docs?.[level]?.["user-guide"] || sdkEntry.platforms[targetPlatform]?.docs?.["high-level"]?.["user-guide"] || "N/A"}`
723
+ ].join("\n")
724
+ }]
725
+ };
726
+ }
727
+ sampleName = wantsDriverLicense
728
+ ? "ScanDriversLicense"
729
+ : (level === "low-level" ? "ScanBarcodes_FoundationalAPI" : "ScanBarcodes_ReadyToUseComponent");
730
+ } else {
731
+ sampleName = "ScanSingleBarcode";
732
+ if (scenarioLower.includes("multiple") || scenarioLower.includes("batch")) sampleName = "ScanMultipleBarcodes";
733
+ else if (wantsImage) sampleName = "DecodeFromAnImage";
734
+
735
+ if (level === "low-level") {
736
+ if (sampleName === "ScanSingleBarcode" || sampleName === "ScanMultipleBarcodes") {
737
+ sampleName = "DecodeWithCameraEnhancer";
738
+ }
481
739
  }
482
740
  }
483
741
 
484
- const samplePath = getMobileSamplePath(targetPlatform, level, sampleName);
742
+ // Prefer a Kotlin variant when the caller asked for Kotlin (#135).
743
+ const wantsKotlin = String(language || "").toLowerCase() === "kotlin";
744
+ let samplePath = "";
745
+ let servedVariant = sampleName;
746
+ if (wantsKotlin && targetPlatform === "android") {
747
+ const ktPath = getMobileSamplePath(targetPlatform, level, `${sampleName}Kt`);
748
+ if (existsSync(ktPath)) {
749
+ samplePath = ktPath;
750
+ servedVariant = `${sampleName}Kt`;
751
+ }
752
+ }
753
+ if (!samplePath) samplePath = getMobileSamplePath(targetPlatform, level, sampleName);
754
+
485
755
  if (!existsSync(samplePath)) {
486
- return { isError: true, content: [{ type: "text", text: `Sample not found: ${sampleName}.` }] };
756
+ return await buildQuickstartMissResponse({
757
+ reason: `DBR mobile sample "${sampleName}" not found for ${targetPlatform}.`,
758
+ product: "dbr", edition: "mobile", platform: targetPlatform, scenario
759
+ });
487
760
  }
488
761
 
489
762
  const mainFile = getMainCodeFile(targetPlatform, samplePath);
490
763
  if (!mainFile) {
491
- return { isError: true, content: [{ type: "text", text: "Could not find main code file." }] };
764
+ return await buildQuickstartMissResponse({
765
+ reason: `Found the ${servedVariant} sample for ${targetPlatform} but could not locate its main code file.`,
766
+ product: "dbr", edition: "mobile", platform: targetPlatform, scenario
767
+ });
492
768
  }
493
769
 
494
770
  const content = readCodeFile(mainFile.path);
@@ -521,7 +797,7 @@ dependencies {
521
797
  \`\`\`xml
522
798
  <uses-permission android:name="android.permission.CAMERA" />
523
799
  \`\`\``;
524
- } else {
800
+ } else if (targetPlatform === "ios") {
525
801
  deps = `
526
802
  ## Dependencies
527
803
 
@@ -540,13 +816,41 @@ end
540
816
  <key>NSCameraUsageDescription</key>
541
817
  <string>Camera access for barcode scanning</string>
542
818
  \`\`\``;
819
+ } else if (targetPlatform === "flutter") {
820
+ deps = `
821
+ ## Dependencies
822
+
823
+ **pubspec.yaml**
824
+ \`\`\`yaml
825
+ dependencies:
826
+ dynamsoft_barcode_reader_bundle: ^${sdkEntry.version}
827
+ \`\`\`
828
+ Then run \`flutter pub get\`. Add camera usage permissions to AndroidManifest.xml and Info.plist.`;
829
+ } else if (targetPlatform === "react-native") {
830
+ deps = `
831
+ ## Dependencies
832
+
833
+ \`\`\`bash
834
+ npm install dynamsoft-capture-vision-react-native
835
+ \`\`\`
836
+ Add camera permissions to AndroidManifest.xml and Info.plist, then rebuild the app.`;
837
+ } else if (targetPlatform === "maui") {
838
+ deps = `
839
+ ## Dependencies
840
+
841
+ \`\`\`xml
842
+ <PackageReference Include="Dynamsoft.BarcodeReaderBundle.Maui" Version="${sdkEntry.version}" />
843
+ \`\`\`
844
+ Add camera permissions in Platforms/Android/AndroidManifest.xml and Platforms/iOS/Info.plist.`;
543
845
  }
544
846
 
545
847
  const output = [
546
848
  "# Quick Start: DBR Mobile",
547
849
  "",
850
+ `**Platform:** ${targetPlatform}`,
548
851
  `**SDK Version:** ${sdkEntry.version}`,
549
852
  `**API Level:** ${level}`,
853
+ servedVariant !== sampleName ? `**Variant:** ${servedVariant} (Kotlin)` : "",
550
854
  `**Trial License:** \`${registry.trial_license}\``,
551
855
  "",
552
856
  deps,
@@ -556,8 +860,9 @@ end
556
860
  content,
557
861
  "```",
558
862
  "",
559
- `Docs: ${sdkEntry.platforms[targetPlatform]?.docs[level]?.["user-guide"] || "N/A"}`
560
- ];
863
+ `Full project files: get_sample_files product=dbr edition=mobile platform=${targetPlatform} sample_id=${servedVariant}`,
864
+ `Docs: ${sdkEntry.platforms[targetPlatform]?.docs?.[level]?.["user-guide"] || "N/A"}`
865
+ ].filter(Boolean);
561
866
 
562
867
  return { content: [{ type: "text", text: output.join("\n") }] };
563
868
  }
@@ -681,7 +986,19 @@ end
681
986
 
682
987
  return {
683
988
  isError: true,
684
- content: [{ type: "text", text: "Unsupported product/edition for quickstart." }]
989
+ content: [{
990
+ type: "text",
991
+ text: [
992
+ `Unsupported product/edition for quickstart: product="${normalizedProduct || product}", edition="${normalizedEdition || edition || "(inferred)"}".`,
993
+ "Supported combinations:",
994
+ "- dbr: web (js + react/angular/vue/next/...), mobile (android/ios/flutter/react-native/maui), server (python/nodejs/java/cpp/dotnet)",
995
+ "- dwt: web",
996
+ "- ddv: web",
997
+ "- mrz: web",
998
+ "- mds: web",
999
+ "Call get_index to see valid product/edition/platform combinations."
1000
+ ].join("\n")
1001
+ }]
685
1002
  };
686
1003
  }
687
1004
  );
@@ -1,5 +1,10 @@
1
1
  import { z } from "zod";
2
- import { buildUnknownPublicProductResponse, isKnownPublicOffering } from "../public-offerings.js";
2
+ import {
3
+ buildUnknownPublicProductResponse,
4
+ isKnownPublicOffering,
5
+ DBR_ONLY_EDITIONS_NOTE,
6
+ WEB_ONLY_OMIT_NOTE
7
+ } from "../public-offerings.js";
3
8
  import { buildUnsupportedPublicScopeResponse } from "./public-routing.js";
4
9
 
5
10
  export function registerSampleTools({
@@ -34,8 +39,8 @@ export function registerSampleTools({
34
39
  "",
35
40
  "PARAMETERS:",
36
41
  "- product: dbr, dwt, ddv, mrz, or mds. Omit to list across all public offerings.",
37
- "- edition: core, mobile, web, or server. Omit to list across all editions.",
38
- "- platform: android, ios, js, python, cpp, java, dotnet, nodejs, react, vue, angular, flutter, react-native, maui, etc.",
42
+ `- edition: core, mobile, web, or server. ${DBR_ONLY_EDITIONS_NOTE} Omit to list across all editions.`,
43
+ `- platform: only DBR spans multiple platforms (android, ios, js, python, cpp, java, dotnet, nodejs, react, vue, angular, flutter, react-native, maui, etc.). ${WEB_ONLY_OMIT_NOTE}`,
39
44
  "- limit: 1-200 (default 50). Max number of results.",
40
45
  "",
41
46
  "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.",
@@ -46,8 +51,8 @@ export function registerSampleTools({
46
51
  ].join("\n"),
47
52
  inputSchema: {
48
53
  product: z.string().optional().describe("Product: dbr, dwt, ddv, mrz, mds"),
49
- edition: z.string().optional().describe("Edition: core, mobile, web, server/desktop"),
50
- 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"),
54
+ edition: z.string().optional().describe(`Edition: core, mobile, web, server/desktop. ${DBR_ONLY_EDITIONS_NOTE}`),
55
+ platform: z.string().optional().describe(`Platform (DBR only spans multiple): 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. ${WEB_ONLY_OMIT_NOTE}`),
51
56
  limit: z.number().int().min(1).max(200).optional().describe("Max results (default 50)")
52
57
  },
53
58
  annotations: {
@@ -1,5 +1,10 @@
1
1
  import { z } from "zod";
2
- import { buildUnknownPublicProductResponse, isKnownPublicOffering } from "../public-offerings.js";
2
+ import {
3
+ buildUnknownPublicProductResponse,
4
+ isKnownPublicOffering,
5
+ DBR_ONLY_EDITIONS_NOTE,
6
+ WEB_ONLY_OMIT_NOTE
7
+ } from "../public-offerings.js";
3
8
 
4
9
  export function registerVersionTools({
5
10
  server,
@@ -64,8 +69,8 @@ export function registerVersionTools({
64
69
  "",
65
70
  "PARAMETERS:",
66
71
  "- product (required): dbr, dwt, ddv, mrz, or mds.",
67
- "- edition: core, mobile, web, or server. Omit to see all editions for the product.",
68
- "- platform: android, ios, js, python, cpp, java, dotnet, nodejs, etc. Helps narrow edition when ambiguous.",
72
+ `- edition: core, mobile, web, or server. ${DBR_ONLY_EDITIONS_NOTE} Omit to see all editions for the product.`,
73
+ `- platform: only DBR spans multiple platforms (android, ios, js, python, cpp, java, dotnet, nodejs, etc.). ${WEB_ONLY_OMIT_NOTE} Helps narrow edition when ambiguous.`,
69
74
  "- 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.",
70
75
  "- feature: Optional feature hint for version policy checks.",
71
76
  "",
@@ -77,8 +82,8 @@ export function registerVersionTools({
77
82
  ].join("\n"),
78
83
  inputSchema: {
79
84
  product: z.string().trim().min(1, "Product is required.").describe("Product: dbr, dwt, ddv, mrz, mds"),
80
- edition: z.string().optional().describe("Edition: core, mobile, web, server/desktop"),
81
- 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"),
85
+ edition: z.string().optional().describe(`Edition: core, mobile, web, server/desktop. ${DBR_ONLY_EDITIONS_NOTE}`),
86
+ platform: z.string().optional().describe(`Platform (DBR only spans multiple): 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. ${WEB_ONLY_OMIT_NOTE}`),
82
87
  constraint: z.string().optional().describe("Version constraint, e.g., latest, 11.x, 10"),
83
88
  feature: z.string().optional().describe("Optional feature hint")
84
89
  },