@nbakka/mcp-appium 2.0.37 → 2.0.39

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.
Files changed (2) hide show
  1. package/lib/server.js +128 -0
  2. package/package.json +3 -2
package/lib/server.js CHANGED
@@ -14,6 +14,7 @@ const ios_1 = require("./ios");
14
14
  const png_1 = require("./png");
15
15
  const image_utils_1 = require("./image-utils");
16
16
  const { google } = require('googleapis');
17
+ const axios = require('axios');
17
18
  const getAgentVersion = () => {
18
19
  const json = require("../package.json");
19
20
  return json.version;
@@ -316,6 +317,133 @@ const createMcpServer = () => {
316
317
  }
317
318
  );
318
319
 
320
+ tool(
321
+ "mobile_fetch_jira_ticket",
322
+ "Fetch JIRA ticket information including summary, description, and extract Figma links from description",
323
+ {
324
+ ticketId: zod_1.z.string().describe("The JIRA ticket ID (e.g., HDA-434)"),
325
+ },
326
+ async ({ ticketId }) => {
327
+ try {
328
+ // Read JIRA credentials from desktop/jira.json file
329
+ const jiraConfigPath = path.join(os.homedir(), 'Desktop', 'jira.json');
330
+
331
+ let jiraConfig;
332
+ try {
333
+ const configContent = await fs.readFile(jiraConfigPath, 'utf-8');
334
+ jiraConfig = JSON.parse(configContent);
335
+ } catch (error) {
336
+ throw new Error(`Failed to read JIRA config from ${jiraConfigPath}: ${error.message}`);
337
+ }
338
+
339
+ // Extract all required values from JSON file
340
+ const { api: jiraApiToken, baseUrl: jiraBaseUrl, email: jiraEmail } = jiraConfig;
341
+
342
+ if (!jiraApiToken) {
343
+ throw new Error('JIRA API token not found in jira.json file. Please ensure the file contains "api" field.');
344
+ }
345
+
346
+ if (!jiraBaseUrl) {
347
+ throw new Error('JIRA base URL not found in jira.json file. Please ensure the file contains "baseUrl" field.');
348
+ }
349
+
350
+ if (!jiraEmail) {
351
+ throw new Error('JIRA email not found in jira.json file. Please ensure the file contains "email" field.');
352
+ }
353
+
354
+ // Create Basic Auth token
355
+ const auth = Buffer.from(`${jiraEmail}:${jiraApiToken}`).toString('base64');
356
+
357
+ // Fetch ticket from JIRA API
358
+ const response = await axios.get(
359
+ `${jiraBaseUrl}/rest/api/3/issue/${ticketId}`,
360
+ {
361
+ headers: {
362
+ 'Authorization': `Basic ${auth}`,
363
+ 'Accept': 'application/json',
364
+ 'Content-Type': 'application/json'
365
+ }
366
+ }
367
+ );
368
+
369
+ const issue = response.data;
370
+
371
+ // Extract summary and description
372
+ const summary = issue.fields.summary || 'No summary available';
373
+ const description = issue.fields.description ?
374
+ extractTextFromADF(issue.fields.description) :
375
+ 'No description available';
376
+
377
+ // Extract Figma links from description
378
+ const figmaLinks = extractFigmaLinks(description);
379
+
380
+ // Format response
381
+ const result = {
382
+ ticketId: ticketId,
383
+ summary: summary,
384
+ description: description,
385
+ figmaLinks: figmaLinks.length > 0 ? figmaLinks : ['No Figma links found']
386
+ };
387
+
388
+ return `JIRA Ticket Information:
389
+ Ticket ID: ${result.ticketId}
390
+ Summary: ${result.summary}
391
+ Description: ${result.description}
392
+ Figma Links: ${result.figmaLinks.join(', ')}`;
393
+
394
+ } catch (error) {
395
+ if (error.response && error.response.status === 404) {
396
+ return `Error: JIRA ticket ${ticketId} not found. Please check the ticket ID.`;
397
+ } else if (error.response && error.response.status === 401) {
398
+ return `Error: Authentication failed. Please check your JIRA credentials.`;
399
+ } else {
400
+ return `Error fetching JIRA ticket: ${error.message}`;
401
+ }
402
+ }
403
+ }
404
+ );
405
+
406
+ // Helper function to extract text from Atlassian Document Format (ADF)
407
+ function extractTextFromADF(adfContent) {
408
+ if (!adfContent || typeof adfContent !== 'object') {
409
+ return String(adfContent || '');
410
+ }
411
+
412
+ let text = '';
413
+
414
+ function traverse(node) {
415
+ if (node.type === 'text') {
416
+ text += node.text || '';
417
+ } else if (node.content && Array.isArray(node.content)) {
418
+ node.content.forEach(traverse);
419
+ }
420
+
421
+ // Add line breaks for paragraphs
422
+ if (node.type === 'paragraph') {
423
+ text += '\n';
424
+ }
425
+ }
426
+
427
+ if (adfContent.content) {
428
+ adfContent.content.forEach(traverse);
429
+ }
430
+
431
+ return text.trim();
432
+ }
433
+
434
+ // Helper function to extract Figma links from text
435
+ function extractFigmaLinks(text) {
436
+ if (!text || typeof text !== 'string') {
437
+ return [];
438
+ }
439
+
440
+ // Regular expression to match Figma URLs
441
+ const figmaRegex = /https?:\/\/(?:www\.)?figma\.com\/[^\s<>"{}|\\^`\[\]]+/gi;
442
+ const matches = text.match(figmaRegex);
443
+
444
+ return matches ? [...new Set(matches)] : []; // Remove duplicates
445
+ }
446
+
319
447
  return server;
320
448
  };
321
449
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nbakka/mcp-appium",
3
- "version": "2.0.37",
3
+ "version": "2.0.39",
4
4
  "description": "Appium MCP",
5
5
  "engines": {
6
6
  "node": ">=18"
@@ -21,7 +21,8 @@
21
21
  "@modelcontextprotocol/sdk": "^1.6.1",
22
22
  "fast-xml-parser": "^5.0.9",
23
23
  "googleapis": "^149.0.0",
24
- "zod-to-json-schema": "^3.24.4"
24
+ "zod-to-json-schema": "^3.24.4",
25
+ "axios": "^1.6.0"
25
26
  },
26
27
  "devDependencies": {
27
28
  "@eslint/eslintrc": "^3.2.0",