simple-dynamsoft-mcp 6.2.0 → 6.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.js CHANGED
@@ -31,9 +31,15 @@ const {
31
31
  LATEST_VERSIONS,
32
32
  LATEST_MAJOR,
33
33
  discoverDwtSamples,
34
+ discoverDcvMobileSamples,
35
+ discoverDcvServerSamples,
36
+ discoverDcvWebSamples,
34
37
  findCodeFilesInSample,
35
38
  getMobileSamplePath,
36
39
  getDbrServerSamplePath,
40
+ getDcvMobileSamplePath,
41
+ getDcvServerSamplePath,
42
+ getDcvWebSamplePath,
37
43
  getDwtSamplePath,
38
44
  getDdvSamplePath,
39
45
  readCodeFile,
@@ -72,7 +78,7 @@ const {
72
78
  const server = new McpServer({
73
79
  name: "simple-dynamsoft-mcp",
74
80
  version: pkg.version,
75
- description: "MCP server for latest major versions of Dynamsoft SDKs: Barcode Reader (Mobile/Server/Web), Dynamic Web TWAIN, and Document Viewer"
81
+ description: "MCP server for latest major versions of Dynamsoft SDKs: Capture Vision, Barcode Reader, Dynamic Web TWAIN, and Document Viewer. Includes guidance for choosing DBR vs DCV by scenario."
76
82
  });
77
83
 
78
84
  function formatScoreLabel(entry) {
@@ -93,7 +99,7 @@ server.registerTool(
93
99
  "get_index",
94
100
  {
95
101
  title: "Get Index",
96
- description: "Get a compact index of products, editions, versions, and available samples/docs.",
102
+ description: "Get a compact index of products, editions, versions, samples/docs, plus DBR-vs-DCV selection guidance.",
97
103
  inputSchema: {}
98
104
  },
99
105
  async () => ({
@@ -109,12 +115,12 @@ server.registerTool(
109
115
  "search",
110
116
  {
111
117
  title: "Search",
112
- description: "Semantic (RAG) search across docs and samples with fuzzy fallback; returns resource links for lazy loading.",
118
+ 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.",
113
119
  inputSchema: {
114
120
  query: z.string().describe("Keywords to search across docs and samples."),
115
- product: z.string().optional().describe("Product: dbr, dwt, ddv"),
116
- edition: z.string().optional().describe("Edition: mobile, web, server/desktop"),
117
- 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"),
121
+ product: z.string().optional().describe("Product: dcv, dbr, dwt, ddv"),
122
+ edition: z.string().optional().describe("Edition: core, mobile, web, server/desktop"),
123
+ 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"),
118
124
  version: z.string().optional().describe("Version constraint (major or full version)"),
119
125
  type: z.enum(["doc", "sample", "index", "policy", "any"]).optional(),
120
126
  limit: z.number().int().min(1).max(10).optional().describe("Max results (default 5)")
@@ -209,11 +215,11 @@ server.registerTool(
209
215
  "list_samples",
210
216
  {
211
217
  title: "List Samples",
212
- description: "List available sample IDs and URIs for a given scope.",
218
+ description: "List available sample IDs and URIs for a given scope. Use DCV scope for MRZ/VIN/document normalization scenarios.",
213
219
  inputSchema: {
214
- product: z.string().optional().describe("Product: dbr, dwt, ddv"),
215
- edition: z.string().optional().describe("Edition: mobile, web, server/desktop"),
216
- 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"),
220
+ product: z.string().optional().describe("Product: dcv, dbr, dwt, ddv"),
221
+ edition: z.string().optional().describe("Edition: core, mobile, web, server/desktop"),
222
+ 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"),
217
223
  limit: z.number().int().min(1).max(200).optional().describe("Max results (default 50)")
218
224
  }
219
225
  },
@@ -291,9 +297,9 @@ server.registerTool(
291
297
  description: "Resolve a sample_id (or sample URI) to matching sample URIs.",
292
298
  inputSchema: {
293
299
  sample_id: z.string().describe("Sample identifier or sample:// URI"),
294
- product: z.string().optional().describe("Product: dbr, dwt, ddv"),
295
- edition: z.string().optional().describe("Edition: mobile, web, server/desktop"),
296
- 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"),
300
+ product: z.string().optional().describe("Product: dcv, dbr, dwt, ddv"),
301
+ edition: z.string().optional().describe("Edition: core, mobile, web, server/desktop"),
302
+ 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"),
297
303
  limit: z.number().int().min(1).max(10).optional().describe("Max results (default 5)")
298
304
  }
299
305
  },
@@ -517,9 +523,9 @@ server.registerTool(
517
523
  title: "Resolve Version",
518
524
  description: "Resolve a concrete latest-major version for a product/edition/platform.",
519
525
  inputSchema: {
520
- product: z.string().describe("Product: dbr, dwt, or ddv"),
521
- edition: z.string().optional().describe("Edition: mobile, web, server/desktop"),
522
- 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"),
526
+ product: z.string().describe("Product: dcv, dbr, dwt, or ddv"),
527
+ edition: z.string().optional().describe("Edition: core, mobile, web, server/desktop"),
528
+ 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"),
523
529
  constraint: z.string().optional().describe("Version constraint, e.g., latest, 11.x, 10"),
524
530
  feature: z.string().optional().describe("Optional feature hint")
525
531
  }
@@ -529,10 +535,10 @@ server.registerTool(
529
535
  const normalizedPlatform = normalizePlatform(platform);
530
536
  const normalizedEdition = normalizeEdition(edition, normalizedPlatform, normalizedProduct);
531
537
 
532
- if (!["dbr", "dwt", "ddv"].includes(normalizedProduct)) {
538
+ if (!["dcv", "dbr", "dwt", "ddv"].includes(normalizedProduct)) {
533
539
  return {
534
540
  isError: true,
535
- content: [{ type: "text", text: `Unknown product "${product}". Use dbr, dwt, or ddv.` }]
541
+ content: [{ type: "text", text: `Unknown product "${product}". Use dcv, dbr, dwt, or ddv.` }]
536
542
  };
537
543
  }
538
544
 
@@ -548,6 +554,41 @@ server.registerTool(
548
554
  return { isError: true, content: [{ type: "text", text: policy.message }] };
549
555
  }
550
556
 
557
+ if (normalizedProduct === "dcv") {
558
+ if (!normalizedEdition) {
559
+ const lines = [
560
+ "# DCV Version Resolution",
561
+ `- Latest major: v${LATEST_MAJOR.dcv}`,
562
+ `- Core: ${LATEST_VERSIONS.dcv.core}`,
563
+ `- Web: ${LATEST_VERSIONS.dcv.web}`,
564
+ `- Mobile: ${LATEST_VERSIONS.dcv.mobile}`,
565
+ `- Server/Desktop: ${LATEST_VERSIONS.dcv.server}`,
566
+ "",
567
+ "Specify edition/platform to resolve a single version."
568
+ ];
569
+ return { content: [{ type: "text", text: lines.join("\n") }] };
570
+ }
571
+
572
+ const resolved = LATEST_VERSIONS.dcv[normalizedEdition];
573
+ if (!resolved) {
574
+ return {
575
+ isError: true,
576
+ content: [{ type: "text", text: `Edition "${normalizedEdition}" is not hosted by this MCP server.` }]
577
+ };
578
+ }
579
+
580
+ const displayPlatform = normalizedPlatform === "web" ? "js" : normalizedPlatform;
581
+ const lines = [
582
+ "# DCV Version Resolution",
583
+ `- Edition: ${normalizedEdition}`,
584
+ displayPlatform ? `- Platform: ${displayPlatform}` : "",
585
+ `- Latest major: v${LATEST_MAJOR.dcv}`,
586
+ `- Resolved version: ${resolved}`
587
+ ].filter(Boolean);
588
+
589
+ return { content: [{ type: "text", text: lines.join("\n") }] };
590
+ }
591
+
551
592
  if (normalizedProduct === "dbr") {
552
593
  if (!normalizedEdition) {
553
594
  const lines = [
@@ -609,15 +650,15 @@ server.registerTool(
609
650
  "get_quickstart",
610
651
  {
611
652
  title: "Get Quickstart",
612
- description: "Opinionated quickstart for a target product/edition/platform.",
653
+ description: "Opinionated quickstart for a target product/edition/platform. DCV supports MRZ/VIN/document-normalization/driver-license workflows.",
613
654
  inputSchema: {
614
- product: z.string().describe("Product: dbr, dwt, or ddv"),
615
- edition: z.string().optional().describe("Edition: mobile, web, server/desktop"),
616
- 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"),
655
+ product: z.string().describe("Product: dcv, dbr, dwt, or ddv"),
656
+ edition: z.string().optional().describe("Edition: core, mobile, web, server/desktop"),
657
+ 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"),
617
658
  language: z.string().optional().describe("Language hint: kotlin, java, swift, js, ts, python, cpp, csharp, react, vue, angular"),
618
659
  version: z.string().optional().describe("Version constraint"),
619
660
  api_level: z.string().optional().describe("API level: high-level or low-level (mobile only)"),
620
- scenario: z.string().optional().describe("Scenario: camera, image, single, multiple, react, etc.")
661
+ scenario: z.string().optional().describe("Scenario: camera, image, single, multiple, MRZ, VIN, document scan/normalization, driver license, react, etc.")
621
662
  }
622
663
  },
623
664
  async ({ product, edition, platform, language, version, api_level, scenario }) => {
@@ -636,6 +677,212 @@ server.registerTool(
636
677
  return { isError: true, content: [{ type: "text", text: policy.message }] };
637
678
  }
638
679
 
680
+ if (normalizedProduct === "dcv") {
681
+ const scenarioLower = `${scenario || ""} ${language || ""}`.toLowerCase();
682
+ const effectiveEdition = normalizedEdition || (normalizedPlatform ? normalizeEdition("", normalizedPlatform, "dcv") : "server");
683
+
684
+ function selectDcvServerSample(platformHint, hint) {
685
+ const platformName = normalizePlatform(platformHint) || "python";
686
+ if (platformName === "python") {
687
+ if (hint.includes("mrz")) return "mrz_scanner";
688
+ if (hint.includes("vin")) return "vin_scanner";
689
+ if (hint.includes("driver") || hint.includes("license")) return "driver_license_scanner";
690
+ if (hint.includes("gs1")) return "gs1_ai_scanner";
691
+ return "document_scanner";
692
+ }
693
+ if (platformName === "nodejs") {
694
+ if (hint.includes("lambda")) return "lambda";
695
+ if (hint.includes("pdf")) return "pdf-advanced";
696
+ if (hint.includes("koa")) return "koa";
697
+ return "express";
698
+ }
699
+ if (hint.includes("mrz")) return "MRZScanner";
700
+ if (hint.includes("vin")) return "VINScanner";
701
+ if (hint.includes("driver") || hint.includes("license")) return "DriverLicenseScanner";
702
+ if (hint.includes("gs1")) return "GS1AIScanner";
703
+ return "DocumentScanner";
704
+ }
705
+
706
+ function selectMobileSample(sampleNames, hint) {
707
+ const lowerToName = new Map(sampleNames.map((name) => [String(name).toLowerCase(), name]));
708
+ const candidates = hint.includes("mrz")
709
+ ? ["scanmrz", "mrzscanner"]
710
+ : hint.includes("vin")
711
+ ? ["scanvin", "vinscanner"]
712
+ : (hint.includes("driver") || hint.includes("license"))
713
+ ? ["driverlicensescanner"]
714
+ : ["scandocument", "documentscanner"];
715
+ for (const candidate of candidates) {
716
+ if (lowerToName.has(candidate)) return lowerToName.get(candidate);
717
+ }
718
+ return sampleNames[0] || "";
719
+ }
720
+
721
+ function readBestSampleContent(samplePath) {
722
+ if (!samplePath || !existsSync(samplePath)) return { text: "", fence: "text" };
723
+ const sampleStat = statSync(samplePath);
724
+ if (sampleStat.isFile()) {
725
+ return {
726
+ text: readCodeFile(samplePath),
727
+ fence: extname(samplePath).replace(".", "") || "text"
728
+ };
729
+ }
730
+ const readmePath = join(samplePath, "README.md");
731
+ if (existsSync(readmePath)) return { text: readCodeFile(readmePath), fence: "markdown" };
732
+
733
+ const codeFiles = findCodeFilesInSample(samplePath);
734
+ if (codeFiles.length > 0) {
735
+ const preferredNames = ["index.html", "index.js", "index.ts", "main.dart", "App.tsx", "MainActivity.kt", "MainActivity.java"];
736
+ const preferred = codeFiles.find((file) => preferredNames.includes(file.filename)) || codeFiles[0];
737
+ return {
738
+ text: readCodeFile(preferred.path),
739
+ fence: preferred.extension ? preferred.extension.replace(".", "") : "text"
740
+ };
741
+ }
742
+
743
+ return { text: "Sample found, but no code files detected.", fence: "text" };
744
+ }
745
+
746
+ function formatInstallLines(installation) {
747
+ if (!installation || typeof installation !== "object") return [];
748
+ const lines = [];
749
+ for (const value of Object.values(installation)) {
750
+ if (typeof value === "string" && value.trim()) lines.push(value);
751
+ }
752
+ return lines;
753
+ }
754
+
755
+ if (effectiveEdition === "server") {
756
+ const sdkEntry = registry.sdks["dcv-server"];
757
+ const targetPlatform = normalizePlatform(normalizedPlatform || sdkEntry.default_platform || "python");
758
+ const sampleName = selectDcvServerSample(targetPlatform, scenarioLower);
759
+ const samplePath = getDcvServerSamplePath(targetPlatform, sampleName);
760
+
761
+ if (!samplePath || !existsSync(samplePath)) {
762
+ return { isError: true, content: [{ type: "text", text: `Sample not found: ${sampleName}.` }] };
763
+ }
764
+
765
+ const { text: sampleContent, fence } = readBestSampleContent(samplePath);
766
+ const installLines = formatInstallLines(sdkEntry.platforms?.[targetPlatform]?.installation);
767
+
768
+ return {
769
+ content: [{
770
+ type: "text",
771
+ text: [
772
+ `# Quick Start: DCV Server (${targetPlatform})`,
773
+ "",
774
+ `**SDK Version:** ${sdkEntry.version}`,
775
+ `**Trial License:** \`${registry.trial_license}\``,
776
+ "",
777
+ installLines.length ? "## Install" : "",
778
+ installLines.length ? "```bash" : "",
779
+ ...installLines,
780
+ installLines.length ? "```" : "",
781
+ installLines.length ? "" : "",
782
+ `## ${sampleName}`,
783
+ "```" + fence,
784
+ sampleContent,
785
+ "```",
786
+ "",
787
+ `Docs: ${sdkEntry.platforms?.[targetPlatform]?.docs?.["user-guide"] || "N/A"}`
788
+ ].filter(Boolean).join("\n")
789
+ }]
790
+ };
791
+ }
792
+
793
+ if (effectiveEdition === "web") {
794
+ const sdkEntry = registry.sdks["dcv-web"];
795
+ const available = discoverDcvWebSamples();
796
+ const sampleName = scenarioLower.includes("vin") ? "VINScanner" : (available[0] || "VINScanner");
797
+ const samplePath = getDcvWebSamplePath(sampleName);
798
+ if (!samplePath || !existsSync(samplePath)) {
799
+ return { isError: true, content: [{ type: "text", text: `Sample not found: ${sampleName}.` }] };
800
+ }
801
+ const { text: sampleContent, fence } = readBestSampleContent(samplePath);
802
+ const installLines = formatInstallLines(sdkEntry.platforms?.web?.installation);
803
+
804
+ return {
805
+ content: [{
806
+ type: "text",
807
+ text: [
808
+ "# Quick Start: DCV Web",
809
+ "",
810
+ `**SDK Version:** ${sdkEntry.version}`,
811
+ `**Trial License:** \`${registry.trial_license}\``,
812
+ "",
813
+ installLines.length ? "## Install" : "",
814
+ installLines.length ? "```bash" : "",
815
+ ...installLines,
816
+ installLines.length ? "```" : "",
817
+ installLines.length ? "" : "",
818
+ `## ${sampleName}`,
819
+ "```" + fence,
820
+ sampleContent,
821
+ "```",
822
+ "",
823
+ `Docs: ${sdkEntry.platforms?.web?.docs?.["user-guide"] || "N/A"}`
824
+ ].filter(Boolean).join("\n")
825
+ }]
826
+ };
827
+ }
828
+
829
+ if (effectiveEdition === "mobile") {
830
+ const sdkEntry = registry.sdks["dcv-mobile"];
831
+ const targetPlatform = normalizePlatform(normalizedPlatform || sdkEntry.default_platform || "android");
832
+ const sampleNames = discoverDcvMobileSamples(targetPlatform);
833
+ const sampleName = selectMobileSample(sampleNames, scenarioLower);
834
+ const samplePath = getDcvMobileSamplePath(targetPlatform, sampleName);
835
+
836
+ if (!samplePath || !existsSync(samplePath)) {
837
+ return { isError: true, content: [{ type: "text", text: `Sample not found: ${sampleName || "N/A"}.` }] };
838
+ }
839
+
840
+ const { text: sampleContent, fence } = readBestSampleContent(samplePath);
841
+ const installLines = formatInstallLines(sdkEntry.platforms?.[targetPlatform]?.installation);
842
+
843
+ return {
844
+ content: [{
845
+ type: "text",
846
+ text: [
847
+ `# Quick Start: DCV Mobile (${targetPlatform})`,
848
+ "",
849
+ `**SDK Version:** ${sdkEntry.version}`,
850
+ `**Trial License:** \`${registry.trial_license}\``,
851
+ "",
852
+ installLines.length ? "## Install" : "",
853
+ installLines.length ? "```bash" : "",
854
+ ...installLines,
855
+ installLines.length ? "```" : "",
856
+ installLines.length ? "" : "",
857
+ `## ${sampleName}`,
858
+ "```" + fence,
859
+ sampleContent,
860
+ "```",
861
+ "",
862
+ `Docs: ${sdkEntry.platforms?.[targetPlatform]?.docs?.["user-guide"] || "N/A"}`
863
+ ].filter(Boolean).join("\n")
864
+ }]
865
+ };
866
+ }
867
+
868
+ if (effectiveEdition === "core") {
869
+ const sdkEntry = registry.sdks["dcv-core"];
870
+ return {
871
+ content: [{
872
+ type: "text",
873
+ text: [
874
+ "# Quick Start: DCV Core",
875
+ "",
876
+ `**SDK Version:** ${sdkEntry.version}`,
877
+ "",
878
+ "DCV core docs aggregate architecture, parameters, and cross-product workflows.",
879
+ `Docs: ${sdkEntry.platforms?.core?.docs?.introduction || "https://www.dynamsoft.com/capture-vision/docs/core/"}`
880
+ ].join("\n")
881
+ }]
882
+ };
883
+ }
884
+ }
885
+
639
886
  if (normalizedProduct === "dbr" && normalizedEdition === "server") {
640
887
  const sdkEntry = registry.sdks["dbr-server"];
641
888
  const scenarioLower = (scenario || "").toLowerCase();
@@ -946,7 +1193,7 @@ server.registerTool(
946
1193
  title: "Generate Project",
947
1194
  description: "Generate a project structure from a sample and return files inline (no zip/download).",
948
1195
  inputSchema: {
949
- product: z.string().describe("Product: dbr, dwt, or ddv"),
1196
+ product: z.string().describe("Product: dcv, dbr, dwt, or ddv"),
950
1197
  edition: z.string().optional().describe("Edition: mobile, web, server/desktop"),
951
1198
  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"),
952
1199
  version: z.string().optional().describe("Version constraint"),
@@ -1018,6 +1265,12 @@ server.registerTool(
1018
1265
  samplePath = getWebSamplePath(sampleInfo.category, sampleInfo.sampleName);
1019
1266
  } else if (sampleInfo.product === "dbr" && (sampleInfo.edition === "python" || sampleInfo.edition === "server")) {
1020
1267
  samplePath = getDbrServerSamplePath(sampleInfo.platform, sampleInfo.sampleName);
1268
+ } else if (sampleInfo.product === "dcv" && sampleInfo.edition === "mobile") {
1269
+ samplePath = getDcvMobileSamplePath(sampleInfo.platform, sampleInfo.sampleName);
1270
+ } else if (sampleInfo.product === "dcv" && sampleInfo.edition === "server") {
1271
+ samplePath = getDcvServerSamplePath(sampleInfo.platform, sampleInfo.sampleName);
1272
+ } else if (sampleInfo.product === "dcv" && sampleInfo.edition === "web") {
1273
+ samplePath = getDcvWebSamplePath(sampleInfo.sampleName);
1021
1274
  } else if (sampleInfo.product === "dwt") {
1022
1275
  samplePath = getDwtSamplePath(sampleInfo.category, sampleInfo.sampleName);
1023
1276
  } else if (sampleInfo.product === "ddv") {
@@ -1045,6 +1298,21 @@ server.registerTool(
1045
1298
  const altLevel = level === "high-level" ? "low-level" : "high-level";
1046
1299
  const alternatePath = getMobileSamplePath(targetPlatform, altLevel, sampleName);
1047
1300
  samplePath = existsSync(primaryPath) ? primaryPath : (existsSync(alternatePath) ? alternatePath : null);
1301
+ } else if (normalizedProduct === "dcv" && normalizedEdition === "mobile") {
1302
+ const platformCandidates = normalizedPlatform
1303
+ ? [normalizedPlatform]
1304
+ : ["android", "ios", "react-native", "flutter", "maui", "spm"];
1305
+ for (const platformCandidate of platformCandidates) {
1306
+ const candidate = getDcvMobileSamplePath(platformCandidate, sampleName);
1307
+ if (candidate && existsSync(candidate)) {
1308
+ samplePath = candidate;
1309
+ break;
1310
+ }
1311
+ }
1312
+ } else if (normalizedProduct === "dcv" && normalizedEdition === "web") {
1313
+ samplePath = getDcvWebSamplePath(sampleName);
1314
+ } else if (normalizedProduct === "dcv" && normalizedEdition === "server") {
1315
+ samplePath = getDcvServerSamplePath(normalizedPlatform || "python", sampleName);
1048
1316
  } else if (normalizedProduct === "dbr" && normalizedEdition === "web") {
1049
1317
  samplePath = getWebSamplePath(undefined, sampleName);
1050
1318
  } else if (normalizedProduct === "dbr" && normalizedEdition === "server") {
@@ -1212,7 +1480,7 @@ server.server.setRequestHandler(ListResourcesRequestSchema, async () => {
1212
1480
 
1213
1481
  server.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
1214
1482
  const parsed = parseResourceUri(request.params.uri);
1215
- if (parsed && ["dbr", "dwt", "ddv"].includes(parsed.product)) {
1483
+ if (parsed && ["dcv", "dbr", "dwt", "ddv"].includes(parsed.product)) {
1216
1484
  const policy = ensureLatestMajor({
1217
1485
  product: parsed.product,
1218
1486
  version: parsed.version,
@@ -1,4 +1,17 @@
1
1
  const sdkAliases = {
2
+ // DCV
3
+ "dcv": "dcv-mobile",
4
+ "dcv-mobile": "dcv-mobile",
5
+ "dcv-server": "dcv-server",
6
+ "dcv-web": "dcv-web",
7
+ "capture vision": "dcv-mobile",
8
+ "capture-vision": "dcv-mobile",
9
+ "dynamsoft capture vision": "dcv-mobile",
10
+ "mrz scanner": "dcv-mobile",
11
+ "vin scanner": "dcv-mobile",
12
+ "driver license scanner": "dcv-mobile",
13
+ "document normalization": "dcv-mobile",
14
+ "document normalizer": "dcv-mobile",
2
15
  // DDV
3
16
  "ddv": "ddv",
4
17
  "document-viewer": "ddv",
@@ -31,10 +44,7 @@ const sdkAliases = {
31
44
  "web twain": "dwt",
32
45
  "webtwain": "dwt",
33
46
  "dynamic web twain": "dwt",
34
- "document scanner": "dwt",
35
- "document scanning": "dwt",
36
- "twain": "dwt",
37
- "scanner": "dwt"
47
+ "twain": "dwt"
38
48
  };
39
49
 
40
50
  const platformAliases = {
@@ -54,6 +64,9 @@ const platformAliases = {
54
64
  maui: "maui",
55
65
  "dotnet maui": "maui",
56
66
  ".net maui": "maui",
67
+ spm: "spm",
68
+ "swift package manager": "spm",
69
+ "swiftpm": "spm",
57
70
  // Desktop/Server
58
71
  python: "python",
59
72
  py: "python",
@@ -147,6 +160,29 @@ const sampleAliases = {
147
160
  "upload": "upload"
148
161
  };
149
162
 
163
+ const dcvFeatureTerms = [
164
+ "capture vision",
165
+ "mrz",
166
+ "machine readable zone",
167
+ "vin",
168
+ "vehicle identification",
169
+ "driver license",
170
+ "drivers license",
171
+ "id card",
172
+ "passport",
173
+ "document normalization",
174
+ "document normalizer",
175
+ "document scanner",
176
+ "document scanning",
177
+ "document scan",
178
+ "auto capture",
179
+ "auto-capture",
180
+ "cropping",
181
+ "crop"
182
+ ];
183
+
184
+ const dwtFeatureTerms = ["dwt", "web twain", "webtwain", "twain", "wia", "ica", "sane"];
185
+
150
186
  let webFrameworkPlatformsGetter = null;
151
187
 
152
188
  function getWebFrameworkPlatformsInternal() {
@@ -198,6 +234,23 @@ function normalizeSampleName(name) {
198
234
  function normalizeProduct(product) {
199
235
  if (!product) return "";
200
236
  const normalized = product.trim().toLowerCase();
237
+ if (
238
+ [
239
+ "dcv",
240
+ "capture vision",
241
+ "capture-vision",
242
+ "dynamsoft capture vision",
243
+ "dynamsoft capture vision sdk",
244
+ "capture vision bundle",
245
+ "mrz scanner",
246
+ "vin scanner",
247
+ "driver license scanner",
248
+ "document scanner",
249
+ "document normalization"
250
+ ].includes(normalized)
251
+ ) {
252
+ return "dcv";
253
+ }
201
254
  if (["ddv", "document viewer", "document-viewer", "dynamsoft document viewer", "doc viewer", "pdf viewer"].includes(normalized)) {
202
255
  return "ddv";
203
256
  }
@@ -215,7 +268,8 @@ function normalizeEdition(edition, platform, product) {
215
268
  const normalizedPlatform = normalizePlatform(platform);
216
269
 
217
270
  if (!edition) {
218
- if (["android", "ios", "maui", "react-native", "flutter"].includes(normalizedPlatform)) return "mobile";
271
+ if (product === "dcv" && normalizedPlatform === "core") return "core";
272
+ if (["android", "ios", "maui", "react-native", "flutter", "spm"].includes(normalizedPlatform)) return "mobile";
219
273
  if (isWebPlatform(normalizedPlatform)) return "web";
220
274
  if (isServerPlatform(normalizedPlatform)) return "server";
221
275
  return "";
@@ -223,8 +277,9 @@ function normalizeEdition(edition, platform, product) {
223
277
 
224
278
  const normalized = edition.trim().toLowerCase();
225
279
  const compact = normalized.replace(/\s+/g, "");
226
- if (["mobile", "android", "ios", "maui", "react-native", "react native", "flutter"].includes(normalized)) return "mobile";
280
+ if (["mobile", "android", "ios", "maui", "react-native", "react native", "flutter", "spm"].includes(normalized)) return "mobile";
227
281
  if (["web", "javascript", "js", "typescript", "ts"].includes(normalized)) return "web";
282
+ if (normalized === "core") return "core";
228
283
  if (["server", "desktop", "server/desktop", "server-desktop", "serverdesktop"].includes(normalized) || compact === "serverdesktop") return "server";
229
284
  if (["python", "py", "java", "c++", "cpp", "dotnet", ".net", "c#", "csharp", "node", "nodejs", "node.js"].includes(normalized)) return "server";
230
285
  return normalized;
@@ -245,11 +300,15 @@ function isWebPlatform(platform) {
245
300
  function inferProductFromQuery(query) {
246
301
  if (!query) return "";
247
302
  const normalized = query.toLowerCase();
303
+ const isDwtQuery = dwtFeatureTerms.some((term) => normalized.includes(term));
304
+ if (isDwtQuery) return "dwt";
305
+ const isDcvQuery = dcvFeatureTerms.some((term) => normalized.includes(term));
306
+ if (isDcvQuery) return "dcv";
248
307
  if (normalized.includes("ddv") || normalized.includes("document viewer") || normalized.includes("pdf viewer") || normalized.includes("edit viewer")) {
249
308
  return "ddv";
250
309
  }
251
- if (normalized.includes("dwt") || normalized.includes("web twain") || normalized.includes("webtwain")) {
252
- return "dwt";
310
+ if (normalized.includes("dcv") || normalized.includes("capture vision") || normalized.includes("capture-vision")) {
311
+ return "dcv";
253
312
  }
254
313
  if (normalized.includes("dbr") || normalized.includes("barcode reader") || normalized.includes("barcode")) {
255
314
  return "dbr";