simple-dynamsoft-mcp 2.1.0 → 2.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Xiao Ling
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -26,9 +26,11 @@ https://github.com/user-attachments/assets/cc1c5f4b-1461-4462-897a-75abc20d62a6
26
26
  | `get_sdk_info` | Get detailed SDK info for a specific platform |
27
27
  | `list_samples` | List mobile code samples |
28
28
  | `list_python_samples` | List Python SDK samples |
29
+ | `list_web_samples` | List web barcode reader samples |
29
30
  | `list_dwt_categories` | List Dynamic Web TWAIN sample categories |
30
31
  | `get_code_snippet` | Get mobile sample source code |
31
32
  | `get_python_sample` | Get Python sample code |
33
+ | `get_web_sample` | Get web barcode reader sample HTML/JS code |
32
34
  | `get_dwt_sample` | Get Dynamic Web TWAIN sample |
33
35
  | `get_quick_start` | Complete quick start guide with dependencies |
34
36
  | `get_gradle_config` | Android Gradle configuration |
@@ -85,8 +87,8 @@ Configuration:
85
87
 
86
88
  Global Location:
87
89
 
88
- - **macOS**: `~/.mcp.json`
89
- - **Windows**: `%USERPROFILE%\.mcp.json`
90
+ - **macOS**: `~/Library/Application Support/Code/User/mcp.json`
91
+ - **Windows**: `%APPDATA%\Code\User\mcp.json`
90
92
 
91
93
  ```json
92
94
  {
@@ -194,6 +196,12 @@ If you prefer running from source:
194
196
 
195
197
  **CDN:** `https://cdn.jsdelivr.net/npm/dynamsoft-barcode-reader-bundle@11.2.4000/dist/dbr.bundle.min.js`
196
198
 
199
+ **Samples:**
200
+ - **hello-world** - Basic barcode scanning from camera
201
+ - **read-an-image** - Decode from image files
202
+ - **frameworks/** - React, Vue, Angular, Next.js, PWA samples
203
+ - **scenarios/** - Multi-image reading, localize an item, driver license parsing
204
+
197
205
  ### Dynamic Web TWAIN (v19.3)
198
206
 
199
207
  **Installation:** `npm install dwt`
@@ -224,6 +232,12 @@ After connecting the MCP server, you can ask your AI assistant:
224
232
  - "Show me how to read barcodes from an image in Python"
225
233
  - "Get the Python sample for video decoding"
226
234
 
235
+ ### Web Barcode Reader
236
+ - "Create a web page that scans barcodes from a camera"
237
+ - "Show me the web barcode reader hello world sample"
238
+ - "Get the React sample for web barcode scanning"
239
+ - "How do I decode barcodes from an image in JavaScript?"
240
+
227
241
  ### Dynamic Web TWAIN
228
242
  - "Create a web page that scans documents from a TWAIN scanner"
229
243
  - "Show me how to save scanned documents as PDF"
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "simple-dynamsoft-mcp",
3
- "version": "2.1.0",
3
+ "version": "2.2.1",
4
4
  "description": "MCP server for Dynamsoft SDKs - Barcode Reader (Mobile/Python/Web) and Dynamic Web TWAIN. Provides documentation, code snippets, and API guidance.",
5
+ "license": "MIT",
5
6
  "repository": {
6
7
  "type": "git",
7
8
  "url": "https://github.com/yushulx/simple-dynamsoft-mcp.git"
@@ -17,7 +18,6 @@
17
18
  "src",
18
19
  "data",
19
20
  "code-snippet",
20
- "test",
21
21
  "README.md"
22
22
  ],
23
23
  "scripts": {
package/src/index.js CHANGED
@@ -216,6 +216,78 @@ function discoverPythonSamples() {
216
216
  return samples;
217
217
  }
218
218
 
219
+ function discoverWebSamples() {
220
+ const categories = {
221
+ "root": [],
222
+ "frameworks": [],
223
+ "scenarios": []
224
+ };
225
+ const webPath = join(codeSnippetRoot, "dynamsoft-barcode-reader", "web");
226
+
227
+ if (!existsSync(webPath)) return categories;
228
+
229
+ // Find HTML files in root
230
+ for (const entry of readdirSync(webPath, { withFileTypes: true })) {
231
+ if (entry.isFile() && entry.name.endsWith(".html")) {
232
+ categories["root"].push(entry.name.replace(".html", ""));
233
+ }
234
+ }
235
+
236
+ // Find samples in subdirectories
237
+ for (const subdir of ["frameworks", "scenarios"]) {
238
+ const subdirPath = join(webPath, subdir);
239
+ if (existsSync(subdirPath)) {
240
+ for (const entry of readdirSync(subdirPath, { withFileTypes: true })) {
241
+ if (entry.isDirectory()) {
242
+ categories[subdir].push(entry.name);
243
+ } else if (entry.isFile() && entry.name.endsWith(".html")) {
244
+ categories[subdir].push(entry.name.replace(".html", ""));
245
+ }
246
+ }
247
+ }
248
+ }
249
+
250
+ // Remove empty categories
251
+ for (const [key, value] of Object.entries(categories)) {
252
+ if (value.length === 0) delete categories[key];
253
+ }
254
+
255
+ return categories;
256
+ }
257
+
258
+ function getWebSamplePath(category, sampleName) {
259
+ const webPath = join(codeSnippetRoot, "dynamsoft-barcode-reader", "web");
260
+
261
+ if (category === "root" || !category) {
262
+ // Try root level
263
+ const htmlPath = join(webPath, `${sampleName}.html`);
264
+ if (existsSync(htmlPath)) return htmlPath;
265
+ } else {
266
+ // Try in subdirectory
267
+ const dirPath = join(webPath, category, sampleName);
268
+ if (existsSync(dirPath) && statSync(dirPath).isDirectory()) {
269
+ // Look for index.html or main html file
270
+ const indexPath = join(dirPath, "index.html");
271
+ if (existsSync(indexPath)) return indexPath;
272
+ // Look for any html file
273
+ for (const entry of readdirSync(dirPath, { withFileTypes: true })) {
274
+ if (entry.isFile() && entry.name.endsWith(".html")) {
275
+ return join(dirPath, entry.name);
276
+ }
277
+ }
278
+ }
279
+ // Try as html file directly
280
+ const htmlPath = join(webPath, category, `${sampleName}.html`);
281
+ if (existsSync(htmlPath)) return htmlPath;
282
+ }
283
+
284
+ // Fallback: search all
285
+ const rootPath = join(webPath, `${sampleName}.html`);
286
+ if (existsSync(rootPath)) return rootPath;
287
+
288
+ return null;
289
+ }
290
+
219
291
  function discoverDwtSamples() {
220
292
  const categories = {};
221
293
  const dwtPath = join(codeSnippetRoot, "dynamic-web-twain");
@@ -385,7 +457,9 @@ server.registerTool(
385
457
  lines.push("- `list_sdks` - List all SDKs");
386
458
  lines.push("- `get_sdk_info` - Get detailed SDK info for a platform");
387
459
  lines.push("- `list_samples` - List code samples (mobile)");
460
+ lines.push("- `list_web_samples` - List web barcode reader samples");
388
461
  lines.push("- `get_code_snippet` - Get actual source code");
462
+ lines.push("- `get_web_sample` - Get web barcode reader sample code");
389
463
  lines.push("- `get_quick_start` - Get complete working example");
390
464
  lines.push("- `get_gradle_config` - Get Android build config");
391
465
  lines.push("- `get_license_info` - Get license setup code");
@@ -581,6 +655,99 @@ server.registerTool(
581
655
  }
582
656
  );
583
657
 
658
+ // ============================================================================
659
+ // TOOL: list_web_samples
660
+ // ============================================================================
661
+
662
+ server.registerTool(
663
+ "list_web_samples",
664
+ {
665
+ title: "List Web Barcode Samples",
666
+ description: "List available JavaScript/Web barcode reader code samples",
667
+ inputSchema: {}
668
+ },
669
+ async () => {
670
+ const categories = discoverWebSamples();
671
+ const sdkEntry = registry.sdks["dbr-web"];
672
+
673
+ const lines = [
674
+ "# Web Barcode Reader Samples",
675
+ "",
676
+ `**SDK Version:** ${sdkEntry.version}`,
677
+ `**Install:** \`npm install dynamsoft-barcode-reader-bundle\``,
678
+ `**CDN:** \`${sdkEntry.platforms.web.installation.cdn}\``,
679
+ "",
680
+ "## Available Samples",
681
+ ""
682
+ ];
683
+
684
+ for (const [category, samples] of Object.entries(categories)) {
685
+ const categoryTitle = category === "root" ? "Basic Samples" : category.charAt(0).toUpperCase() + category.slice(1);
686
+ lines.push(`### ${categoryTitle}`);
687
+ lines.push(samples.map(s => `- ${s}`).join("\n"));
688
+ lines.push("");
689
+ }
690
+
691
+ lines.push("Use `get_web_sample` with sample_name to get code.");
692
+
693
+ return { content: [{ type: "text", text: lines.join("\n") }] };
694
+ }
695
+ );
696
+
697
+ // ============================================================================
698
+ // TOOL: get_web_sample
699
+ // ============================================================================
700
+
701
+ server.registerTool(
702
+ "get_web_sample",
703
+ {
704
+ title: "Get Web Barcode Sample",
705
+ description: "Get JavaScript/Web barcode reader sample code",
706
+ inputSchema: {
707
+ sample_name: z.string().describe("Sample name, e.g. hello-world, read-an-image"),
708
+ category: z.string().optional().describe("Category: root, frameworks, scenarios (optional)")
709
+ }
710
+ },
711
+ async ({ sample_name, category }) => {
712
+ const samplePath = getWebSamplePath(category, sample_name);
713
+
714
+ if (!samplePath) {
715
+ const categories = discoverWebSamples();
716
+ const allSamples = Object.entries(categories)
717
+ .map(([cat, samples]) => samples.map(s => `${cat}/${s}`))
718
+ .flat();
719
+ return {
720
+ content: [{
721
+ type: "text",
722
+ text: `Sample "${sample_name}" not found.\n\nAvailable samples:\n${allSamples.map(s => `- ${s}`).join("\n")}\n\nUse \`list_web_samples\` to see all available samples.`
723
+ }]
724
+ };
725
+ }
726
+
727
+ const content = readCodeFile(samplePath);
728
+ if (!content) {
729
+ return { content: [{ type: "text", text: `Could not read "${sample_name}".` }] };
730
+ }
731
+
732
+ const sdkEntry = registry.sdks["dbr-web"];
733
+
734
+ const output = [
735
+ `# Web Barcode Reader: ${sample_name}`,
736
+ "",
737
+ `**SDK Version:** ${sdkEntry.version}`,
738
+ `**Install:** \`npm install dynamsoft-barcode-reader-bundle\``,
739
+ `**CDN:** \`${sdkEntry.platforms.web.installation.cdn}\``,
740
+ `**Trial License:** \`${registry.trial_license}\``,
741
+ "",
742
+ "```html",
743
+ content,
744
+ "```"
745
+ ];
746
+
747
+ return { content: [{ type: "text", text: output.join("\n") }] };
748
+ }
749
+ );
750
+
584
751
  // ============================================================================
585
752
  // TOOL: list_dwt_categories
586
753
  // ============================================================================
@@ -1066,6 +1233,49 @@ server.registerTool(
1066
1233
  };
1067
1234
  }
1068
1235
 
1236
+ // Handle Web Barcode Reader SDK
1237
+ if (sdkId === "dbr-web") {
1238
+ const sdkEntry = registry.sdks["dbr-web"];
1239
+ const sampleName = use_case?.includes("image") ? "read-an-image" : "hello-world";
1240
+ const samplePath = getWebSamplePath("root", sampleName);
1241
+
1242
+ if (!samplePath || !existsSync(samplePath)) {
1243
+ return { content: [{ type: "text", text: `Sample not found. Use list_web_samples to see available.` }] };
1244
+ }
1245
+
1246
+ const content = readCodeFile(samplePath);
1247
+
1248
+ return {
1249
+ content: [{
1250
+ type: "text", text: [
1251
+ "# Quick Start: Web Barcode Reader",
1252
+ "",
1253
+ `**SDK Version:** ${sdkEntry.version}`,
1254
+ `**Trial License:** \`${registry.trial_license}\``,
1255
+ "",
1256
+ "## Option 1: CDN",
1257
+ "```html",
1258
+ `<script src="${sdkEntry.platforms.web.installation.cdn}"></script>`,
1259
+ "```",
1260
+ "",
1261
+ "## Option 2: NPM",
1262
+ "```bash",
1263
+ "npm install dynamsoft-barcode-reader-bundle",
1264
+ "```",
1265
+ "",
1266
+ `## Sample: ${sampleName}.html`,
1267
+ "```html",
1268
+ content,
1269
+ "```",
1270
+ "",
1271
+ "## Notes",
1272
+ "- Trial license requires network connection",
1273
+ `- User Guide: ${sdkEntry.platforms.web.docs["user-guide"]}`
1274
+ ].join("\n")
1275
+ }]
1276
+ };
1277
+ }
1278
+
1069
1279
  // Handle Mobile SDK (original logic)
1070
1280
  const level = normalizeApiLevel(api_level);
1071
1281
  const platformKey = platform || "android";
@@ -1566,6 +1776,29 @@ for (const sampleName of pythonSamples) {
1566
1776
  );
1567
1777
  }
1568
1778
 
1779
+ // Register Web barcode reader sample resources
1780
+ const webCategories = discoverWebSamples();
1781
+ for (const [category, samples] of Object.entries(webCategories)) {
1782
+ for (const sampleName of samples) {
1783
+ const resourceUri = `dynamsoft://samples/web/${category}/${sampleName}`;
1784
+ const resourceName = `web-${category}-${sampleName}`.toLowerCase().replace(/[^a-z0-9-]/g, "-");
1785
+ server.registerResource(
1786
+ resourceName,
1787
+ resourceUri,
1788
+ {
1789
+ title: `Web: ${sampleName}`,
1790
+ description: `Web barcode reader ${category}: ${sampleName}`,
1791
+ mimeType: "text/html"
1792
+ },
1793
+ async (uri) => {
1794
+ const samplePath = getWebSamplePath(category, sampleName);
1795
+ const content = samplePath && existsSync(samplePath) ? readCodeFile(samplePath) : "Sample not found";
1796
+ return { contents: [{ uri: uri.href, text: content, mimeType: "text/html" }] };
1797
+ }
1798
+ );
1799
+ }
1800
+ }
1801
+
1569
1802
  // Register DWT sample resources
1570
1803
  const dwtCategories = discoverDwtSamples();
1571
1804
  for (const [category, samples] of Object.entries(dwtCategories)) {
@@ -1,460 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Automated tests for Dynamsoft MCP Server
5
- * Run with: node test/server.test.js
6
- */
7
-
8
- import { spawn } from 'child_process';
9
- import { fileURLToPath } from 'url';
10
- import { dirname, join } from 'path';
11
-
12
- const __filename = fileURLToPath(import.meta.url);
13
- const __dirname = dirname(__filename);
14
- const serverPath = join(__dirname, '..', 'src', 'index.js');
15
-
16
- // Test counters
17
- let passed = 0;
18
- let failed = 0;
19
- const results = [];
20
-
21
- /**
22
- * Send a JSON-RPC request to the server and get the response
23
- */
24
- async function sendRequest(request) {
25
- return new Promise((resolve, reject) => {
26
- const proc = spawn('node', [serverPath], {
27
- stdio: ['pipe', 'pipe', 'pipe']
28
- });
29
-
30
- let stdout = '';
31
- let stderr = '';
32
-
33
- proc.stdout.on('data', (data) => {
34
- stdout += data.toString();
35
- });
36
-
37
- proc.stderr.on('data', (data) => {
38
- stderr += data.toString();
39
- });
40
-
41
- proc.on('close', (code) => {
42
- try {
43
- // Parse only the JSON-RPC response (last complete JSON object)
44
- const lines = stdout.trim().split('\n');
45
- const jsonLine = lines.find(line => {
46
- try {
47
- const parsed = JSON.parse(line);
48
- return parsed.jsonrpc === '2.0';
49
- } catch {
50
- return false;
51
- }
52
- });
53
-
54
- if (jsonLine) {
55
- resolve(JSON.parse(jsonLine));
56
- } else {
57
- reject(new Error(`No valid JSON-RPC response. stdout: ${stdout}, stderr: ${stderr}`));
58
- }
59
- } catch (e) {
60
- reject(new Error(`Failed to parse response: ${e.message}. stdout: ${stdout}`));
61
- }
62
- });
63
-
64
- proc.on('error', reject);
65
-
66
- // Send the request and close stdin
67
- proc.stdin.write(JSON.stringify(request) + '\n');
68
- proc.stdin.end();
69
- });
70
- }
71
-
72
- /**
73
- * Run a test case
74
- */
75
- async function test(name, fn) {
76
- try {
77
- await fn();
78
- passed++;
79
- results.push({ name, status: 'āœ… PASSED' });
80
- console.log(`āœ… ${name}`);
81
- } catch (error) {
82
- failed++;
83
- results.push({ name, status: 'āŒ FAILED', error: error.message });
84
- console.log(`āŒ ${name}`);
85
- console.log(` Error: ${error.message}`);
86
- }
87
- }
88
-
89
- /**
90
- * Assert helper
91
- */
92
- function assert(condition, message) {
93
- if (!condition) {
94
- throw new Error(message || 'Assertion failed');
95
- }
96
- }
97
-
98
- // ============================================
99
- // Test Cases
100
- // ============================================
101
-
102
- console.log('\n🧪 Dynamsoft MCP Server Test Suite\n');
103
- console.log('='.repeat(50));
104
-
105
- // Test 1: Server initialization
106
- await test('Server responds to initialize request', async () => {
107
- const response = await sendRequest({
108
- jsonrpc: '2.0',
109
- id: 1,
110
- method: 'initialize',
111
- params: {
112
- protocolVersion: '2024-11-05',
113
- capabilities: {},
114
- clientInfo: { name: 'test-client', version: '1.0.0' }
115
- }
116
- });
117
-
118
- assert(response.result, 'Should have result');
119
- assert(response.result.serverInfo, 'Should have serverInfo');
120
- assert(response.result.serverInfo.name === 'simple-dynamsoft-mcp', 'Server name should match');
121
- });
122
-
123
- // Test 2: List tools
124
- await test('tools/list returns all 16 tools', async () => {
125
- const response = await sendRequest({
126
- jsonrpc: '2.0',
127
- id: 1,
128
- method: 'tools/list'
129
- });
130
-
131
- assert(response.result, 'Should have result');
132
- assert(response.result.tools, 'Should have tools array');
133
- assert(response.result.tools.length === 16, `Expected 16 tools, got ${response.result.tools.length}`);
134
-
135
- const toolNames = response.result.tools.map(t => t.name);
136
- const expectedTools = [
137
- 'list_sdks', 'get_sdk_info', 'list_samples', 'list_python_samples',
138
- 'list_dwt_categories', 'get_code_snippet', 'get_python_sample',
139
- 'get_dwt_sample', 'get_quick_start', 'get_gradle_config',
140
- 'get_license_info', 'get_api_usage', 'search_samples', 'generate_project',
141
- 'search_dwt_docs', 'get_dwt_api_doc'
142
- ];
143
-
144
- for (const expected of expectedTools) {
145
- assert(toolNames.includes(expected), `Missing tool: ${expected}`);
146
- }
147
- });
148
-
149
- // Test 3: list_sdks tool
150
- await test('list_sdks returns SDK information', async () => {
151
- const response = await sendRequest({
152
- jsonrpc: '2.0',
153
- id: 1,
154
- method: 'tools/call',
155
- params: {
156
- name: 'list_sdks',
157
- arguments: {}
158
- }
159
- });
160
-
161
- assert(response.result, 'Should have result');
162
- assert(response.result.content, 'Should have content');
163
- assert(response.result.content.length > 0, 'Should have content items');
164
-
165
- const text = response.result.content[0].text;
166
- assert(text.includes('dbr-mobile'), 'Should include dbr-mobile');
167
- assert(text.includes('dbr-python'), 'Should include dbr-python');
168
- assert(text.includes('dbr-web'), 'Should include dbr-web');
169
- assert(text.includes('dwt'), 'Should include dwt');
170
- });
171
-
172
- // Test 4: get_sdk_info tool
173
- await test('get_sdk_info returns detailed SDK info', async () => {
174
- const response = await sendRequest({
175
- jsonrpc: '2.0',
176
- id: 1,
177
- method: 'tools/call',
178
- params: {
179
- name: 'get_sdk_info',
180
- arguments: { sdk_id: 'dbr-mobile' }
181
- }
182
- });
183
-
184
- assert(response.result, 'Should have result');
185
- assert(response.result.content, 'Should have content');
186
-
187
- const text = response.result.content[0].text;
188
- assert(text.includes('Android') || text.includes('android'), 'Should include Android');
189
- assert(text.includes('11.2.5000'), 'Should include version');
190
- });
191
-
192
- // Test 5: get_license_info tool (requires platform parameter)
193
- await test('get_license_info returns trial license', async () => {
194
- const response = await sendRequest({
195
- jsonrpc: '2.0',
196
- id: 1,
197
- method: 'tools/call',
198
- params: {
199
- name: 'get_license_info',
200
- arguments: { platform: 'android' }
201
- }
202
- });
203
-
204
- assert(response.result, 'Should have result');
205
- assert(!response.result.isError, 'Should not be an error');
206
-
207
- const text = response.result.content[0].text;
208
- assert(text.includes('DLS2') || text.includes('License'), 'Should include license info');
209
- });
210
-
211
- // Test 6: list_samples tool
212
- await test('list_samples returns mobile samples', async () => {
213
- const response = await sendRequest({
214
- jsonrpc: '2.0',
215
- id: 1,
216
- method: 'tools/call',
217
- params: {
218
- name: 'list_samples',
219
- arguments: { platform: 'android' }
220
- }
221
- });
222
-
223
- assert(response.result, 'Should have result');
224
-
225
- const text = response.result.content[0].text;
226
- assert(text.includes('android'), 'Should include android');
227
- });
228
-
229
- // Test 7: list_python_samples tool
230
- await test('list_python_samples returns Python samples', async () => {
231
- const response = await sendRequest({
232
- jsonrpc: '2.0',
233
- id: 1,
234
- method: 'tools/call',
235
- params: {
236
- name: 'list_python_samples',
237
- arguments: {}
238
- }
239
- });
240
-
241
- assert(response.result, 'Should have result');
242
- // Should return samples or indicate no local samples
243
- assert(response.result.content, 'Should have content');
244
- });
245
-
246
- // Test 8: list_dwt_categories tool
247
- await test('list_dwt_categories returns DWT categories', async () => {
248
- const response = await sendRequest({
249
- jsonrpc: '2.0',
250
- id: 1,
251
- method: 'tools/call',
252
- params: {
253
- name: 'list_dwt_categories',
254
- arguments: {}
255
- }
256
- });
257
-
258
- assert(response.result, 'Should have result');
259
- // Should return categories or indicate they exist
260
- assert(response.result.content, 'Should have content');
261
- });
262
-
263
- // Test 9: get_quick_start tool
264
- await test('get_quick_start returns quick start guide', async () => {
265
- const response = await sendRequest({
266
- jsonrpc: '2.0',
267
- id: 1,
268
- method: 'tools/call',
269
- params: {
270
- name: 'get_quick_start',
271
- arguments: { sdk_id: 'dbr-mobile', platform: 'android' }
272
- }
273
- });
274
-
275
- assert(response.result, 'Should have result');
276
-
277
- const text = response.result.content[0].text;
278
- assert(text.includes('Quick Start') || text.includes('Android'), 'Should include quick start info');
279
- });
280
-
281
- // Test 10: get_gradle_config tool
282
- await test('get_gradle_config returns Gradle configuration', async () => {
283
- const response = await sendRequest({
284
- jsonrpc: '2.0',
285
- id: 1,
286
- method: 'tools/call',
287
- params: {
288
- name: 'get_gradle_config',
289
- arguments: {}
290
- }
291
- });
292
-
293
- assert(response.result, 'Should have result');
294
-
295
- const text = response.result.content[0].text;
296
- assert(text.includes('gradle') || text.includes('Gradle') || text.includes('implementation'),
297
- 'Should include Gradle config');
298
- });
299
-
300
- // Test 11: get_api_usage tool
301
- await test('get_api_usage returns API usage info', async () => {
302
- const response = await sendRequest({
303
- jsonrpc: '2.0',
304
- id: 1,
305
- method: 'tools/call',
306
- params: {
307
- name: 'get_api_usage',
308
- arguments: { sdk_id: 'dbr-mobile', api_name: 'decode' }
309
- }
310
- });
311
-
312
- assert(response.result, 'Should have result');
313
- assert(response.result.content, 'Should have content');
314
- });
315
-
316
- // Test 12: search_samples tool
317
- await test('search_samples finds samples by keyword', async () => {
318
- const response = await sendRequest({
319
- jsonrpc: '2.0',
320
- id: 1,
321
- method: 'tools/call',
322
- params: {
323
- name: 'search_samples',
324
- arguments: { query: 'barcode' }
325
- }
326
- });
327
-
328
- assert(response.result, 'Should have result');
329
- assert(response.result.content, 'Should have content');
330
- });
331
-
332
- // Test 13: generate_project tool
333
- await test('generate_project returns project structure', async () => {
334
- const response = await sendRequest({
335
- jsonrpc: '2.0',
336
- id: 1,
337
- method: 'tools/call',
338
- params: {
339
- name: 'generate_project',
340
- arguments: { platform: 'android', sample_name: 'ScanSingleBarcode' }
341
- }
342
- });
343
-
344
- assert(response.result, 'Should have result');
345
- assert(response.result.content, 'Should have content');
346
- const text = response.result.content[0].text;
347
- assert(text.includes('# Project Generation:'), 'Should include project generation header');
348
- assert(text.includes('AndroidManifest.xml') || text.includes('build.gradle'), 'Should include project files');
349
- });
350
-
351
- // Test 14: search_dwt_docs tool
352
- await test('search_dwt_docs finds documentation articles', async () => {
353
- const response = await sendRequest({
354
- jsonrpc: '2.0',
355
- id: 1,
356
- method: 'tools/call',
357
- params: {
358
- name: 'search_dwt_docs',
359
- arguments: { query: 'PDF' }
360
- }
361
- });
362
-
363
- assert(response.result, 'Should have result');
364
- assert(response.result.content, 'Should have content');
365
- const text = response.result.content[0].text;
366
- assert(text.includes('DWT Documentation Search'), 'Should include search header');
367
- assert(text.includes('PDF') || text.includes('pdf'), 'Should find PDF-related articles');
368
- });
369
-
370
- // Test 15: get_dwt_api_doc tool
371
- await test('get_dwt_api_doc returns documentation article', async () => {
372
- const response = await sendRequest({
373
- jsonrpc: '2.0',
374
- id: 1,
375
- method: 'tools/call',
376
- params: {
377
- name: 'get_dwt_api_doc',
378
- arguments: { title: 'OCR' }
379
- }
380
- });
381
-
382
- assert(response.result, 'Should have result');
383
- assert(response.result.content, 'Should have content');
384
- const text = response.result.content[0].text;
385
- // Should return either the article or suggestions
386
- assert(text.includes('OCR') || text.includes('not found'), 'Should handle OCR query');
387
- });
388
-
389
- // Test 16: resources/list returns registered resources
390
- await test('resources/list returns registered resources', async () => {
391
- const response = await sendRequest({
392
- jsonrpc: '2.0',
393
- id: 1,
394
- method: 'resources/list'
395
- });
396
-
397
- assert(response.result, 'Should have result');
398
- assert(response.result.resources, 'Should have resources array');
399
- assert(response.result.resources.length > 0, 'Should have at least one resource');
400
-
401
- // Check for expected resource types
402
- const uris = response.result.resources.map(r => r.uri);
403
- assert(uris.some(u => u.includes('sdk-info')), 'Should have sdk-info resources');
404
- assert(uris.some(u => u.includes('docs/dwt')), 'Should have DWT doc resources');
405
- });
406
-
407
- // Test 17: Invalid tool call returns error
408
- await test('Invalid tool call returns proper error', async () => {
409
- const response = await sendRequest({
410
- jsonrpc: '2.0',
411
- id: 1,
412
- method: 'tools/call',
413
- params: {
414
- name: 'nonexistent_tool',
415
- arguments: {}
416
- }
417
- });
418
-
419
- assert(response.error || (response.result && response.result.isError),
420
- 'Should return error for invalid tool');
421
- });
422
-
423
- // Test 18: Tool with invalid arguments returns error
424
- await test('Tool with missing required arguments returns error', async () => {
425
- const response = await sendRequest({
426
- jsonrpc: '2.0',
427
- id: 1,
428
- method: 'tools/call',
429
- params: {
430
- name: 'get_license_info',
431
- arguments: {} // Missing required platform
432
- }
433
- });
434
-
435
- assert(response.result && response.result.isError,
436
- 'Should return error for missing required argument');
437
- });
438
-
439
- // ============================================
440
- // Test Summary
441
- // ============================================
442
-
443
- console.log('\n' + '='.repeat(50));
444
- console.log('\nšŸ“Š Test Summary\n');
445
- console.log(` Total: ${passed + failed}`);
446
- console.log(` Passed: ${passed} āœ…`);
447
- console.log(` Failed: ${failed} āŒ`);
448
- console.log(` Rate: ${((passed / (passed + failed)) * 100).toFixed(1)}%`);
449
-
450
- if (failed > 0) {
451
- console.log('\nāŒ Failed Tests:');
452
- results.filter(r => r.status.includes('FAILED')).forEach(r => {
453
- console.log(` - ${r.name}: ${r.error}`);
454
- });
455
- }
456
-
457
- console.log('\n' + '='.repeat(50));
458
-
459
- // Exit with appropriate code
460
- process.exit(failed > 0 ? 1 : 0);