@nbakka/mcp-appium 2.0.47 → 2.0.48

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 +90 -134
  2. package/package.json +5 -2
package/lib/server.js CHANGED
@@ -15,6 +15,8 @@ const png_1 = require("./png");
15
15
  const image_utils_1 = require("./image-utils");
16
16
  const { google } = require('googleapis');
17
17
  const axios = require('axios');
18
+ import OpenAI from "openai";
19
+ import { tool } from "ai";
18
20
  const getAgentVersion = () => {
19
21
  const json = require("../package.json");
20
22
  return json.version;
@@ -474,179 +476,133 @@ const createMcpServer = () => {
474
476
  return [...new Set(figmaLinks)];
475
477
  }
476
478
 
477
- tool(
478
- "mobile_export_figma_images",
479
+ // ----------------------
480
+ // Helper: Extract File ID
481
+ // ----------------------
482
+ function extractFileIdFromUrl(url) {
483
+ const patterns = [
484
+ /figma\.com\/file\/([a-zA-Z0-9]+)/,
485
+ /figma\.com\/design\/([a-zA-Z0-9]+)/,
486
+ /figma\.com\/proto\/([a-zA-Z0-9]+)/
487
+ ];
488
+
489
+ for (const pattern of patterns) {
490
+ const match = url.match(pattern);
491
+ if (match) return match[1];
492
+ }
493
+ return null;
494
+ }
495
+
496
+ // ----------------------
497
+ // TOOL 1: Export Figma to PDF
498
+ // ----------------------
499
+ export const mobile_export_figma_pdf = tool(
500
+ "mobile_export_figma_pdf",
479
501
  "Export Figma file as PDF",
480
502
  {
481
503
  figmaUrl: zod_1.z.string().describe("The Figma file URL to export as PDF")
482
504
  },
483
505
  async ({ figmaUrl }) => {
484
506
  try {
485
- // Read Figma credentials from desktop/figma.json file
486
- const figmaConfigPath = path.join(os.homedir(), 'Desktop', 'figma.json');
487
-
488
- let figmaConfig;
489
- try {
490
- const configContent = await fs.readFile(figmaConfigPath, 'utf-8');
491
- figmaConfig = JSON.parse(configContent);
492
- } catch (error) {
493
- throw new Error(`Failed to read Figma config from ${figmaConfigPath}: ${error.message}`);
494
- }
495
-
496
- // Extract API token from config
497
- const { token: figmaToken } = figmaConfig;
507
+ // Load Figma token from Desktop/figma.json
508
+ const figmaConfigPath = path.join(os.homedir(), "Desktop", "figma.json");
509
+ const configContent = await fs.readFile(figmaConfigPath, "utf-8");
510
+ const { token: figmaToken } = JSON.parse(configContent);
498
511
 
499
- if (!figmaToken) {
500
- throw new Error('Figma API token not found in figma.json file. Please ensure the file contains "token" field.');
501
- }
512
+ if (!figmaToken) throw new Error("Figma API token missing in figma.json");
502
513
 
503
- // Extract file ID from Figma URL
514
+ // Extract file ID from URL
504
515
  const fileId = extractFileIdFromUrl(figmaUrl);
505
- if (!fileId) {
506
- throw new Error('Invalid Figma URL. Unable to extract file ID.');
507
- }
516
+ if (!fileId) throw new Error("Invalid Figma URL - cannot extract fileId");
508
517
 
509
- // Set up export directory
510
- const exportPath = path.join(os.homedir(), 'Desktop', 'figma');
511
-
512
- // Create export directory if it doesn't exist
513
- await fs.mkdir(exportPath, { recursive: true });
514
-
515
- // Clear existing PDF files in the directory
516
- try {
517
- const existingFiles = await fs.readdir(exportPath);
518
- const pdfFiles = existingFiles.filter(file => file.endsWith('.pdf'));
519
- for (const file of pdfFiles) {
520
- await fs.unlink(path.join(exportPath, file));
521
- }
522
- } catch (cleanupError) {
523
- // Continue if cleanup fails
524
- }
525
-
526
- // First, get the file structure to find all top-level frames
518
+ // Get file structure to find frames
527
519
  const fileResponse = await axios.get(
528
520
  `https://api.figma.com/v1/files/${fileId}`,
529
- {
530
- headers: {
531
- 'X-Figma-Token': figmaToken
532
- }
533
- }
521
+ { headers: { "X-Figma-Token": figmaToken } }
534
522
  );
535
523
 
536
- const fileData = fileResponse.data;
537
-
538
- // Extract all top-level frame IDs
539
524
  const frameIds = [];
540
- if (fileData.document.children && Array.isArray(fileData.document.children)) {
541
- fileData.document.children.forEach(page => {
542
- if (page.children && Array.isArray(page.children)) {
543
- page.children.forEach(child => {
544
- if (child.type === 'FRAME') {
545
- frameIds.push(child.id);
546
- }
547
- });
548
- }
525
+ fileResponse.data.document.children?.forEach(page => {
526
+ page.children?.forEach(child => {
527
+ if (child.type === "FRAME") frameIds.push(child.id);
549
528
  });
550
- }
551
-
552
- if (frameIds.length === 0) {
553
- throw new Error('No frames found in the Figma file');
554
- }
529
+ });
555
530
 
556
- console.log(`Found ${frameIds.length} top-level frames:`, frameIds.slice(0, 5));
531
+ if (frameIds.length === 0) throw new Error("No frames found in Figma file");
557
532
 
558
- // Request PDF export with specific frame IDs
533
+ // Request PDF export
559
534
  const exportResponse = await axios.get(
560
535
  `https://api.figma.com/v1/images/${fileId}`,
561
536
  {
562
- headers: {
563
- 'X-Figma-Token': figmaToken
564
- },
565
- params: {
566
- ids: frameIds.join(','),
567
- format: 'pdf',
568
- use_absolute_bounds: false
569
- }
537
+ headers: { "X-Figma-Token": figmaToken },
538
+ params: { ids: frameIds.join(","), format: "pdf" }
570
539
  }
571
540
  );
572
541
 
573
- console.log('Export response:', exportResponse.data);
574
-
575
- // For PDF export, the response structure might be different
576
- let pdfUrl = null;
577
- if (exportResponse.data.images) {
578
- // Try to get PDF URL - might be under file ID or first frame ID
579
- pdfUrl = exportResponse.data.images[fileId] ||
580
- exportResponse.data.images[frameIds[0]] ||
581
- Object.values(exportResponse.data.images)[0];
582
- }
583
-
584
- if (!pdfUrl) {
585
- throw new Error('Failed to get PDF export URL from Figma');
586
- }
542
+ const pdfUrl = Object.values(exportResponse.data.images)[0];
543
+ if (!pdfUrl) throw new Error("No PDF export URL returned from Figma");
587
544
 
588
545
  // Download PDF
589
- const pdfResponse = await axios.get(pdfUrl, {
590
- responseType: 'arraybuffer'
591
- });
546
+ const pdfResponse = await axios.get(pdfUrl, { responseType: "arraybuffer" });
547
+
548
+ const exportPath = path.join(os.homedir(), "Desktop", "figma");
549
+ await fs.mkdir(exportPath, { recursive: true });
592
550
 
593
- // Save PDF to file with timestamp
594
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
551
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, -5);
595
552
  const pdfPath = path.join(exportPath, `figma-export-${timestamp}.pdf`);
596
553
  await fs.writeFile(pdfPath, pdfResponse.data);
597
554
 
598
- return `Figma PDF Export Complete!
599
- Export Path: ${pdfPath}
600
- File: figma-export-${timestamp}.pdf`;
555
+ return `✅ PDF Export Complete: ${pdfPath}`;
556
+ } catch (err) {
557
+ return `❌ Error exporting Figma PDF: ${err.message}`;
558
+ }
559
+ }
560
+ );
601
561
 
602
- } catch (error) {
603
- // Enhanced error logging
604
- let errorMessage = error.message;
562
+ // ----------------------
563
+ // TOOL 2: Upload PDF to OpenAI with Jira Info
564
+ // ----------------------
565
+ export const upload_pdf_to_openai = tool(
566
+ "upload_pdf_to_openai",
567
+ "Upload a PDF to OpenAI API along with Jira summary & description",
568
+ {
569
+ jiraSummary: zod_1.z.string().describe("Jira issue summary"),
570
+ jiraDescription: zod_1.z.string().describe("Jira issue description"),
571
+ pdfPath: zod_1.z.string().describe("Path to PDF file from tool 1")
572
+ },
573
+ async ({ jiraSummary, jiraDescription, pdfPath }) => {
574
+ try {
575
+ // Load OpenAI API key
576
+ const openaiConfigPath = path.join(os.homedir(), "Desktop", "openai.json");
577
+ const configContent = await fs.readFile(openaiConfigPath, "utf-8");
578
+ const { apiKey: openaiKey } = JSON.parse(configContent);
605
579
 
606
- if (error.response) {
607
- const status = error.response.status;
608
- const responseData = error.response.data;
580
+ if (!openaiKey) throw new Error("OpenAI API key missing in openai.json");
609
581
 
610
- console.error('Figma API Error:', {
611
- status,
612
- statusText: error.response.statusText,
613
- data: responseData,
614
- url: error.config?.url,
615
- params: error.config?.params
616
- });
582
+ const client = new OpenAI({ apiKey: openaiKey });
617
583
 
618
- if (status === 400) {
619
- errorMessage = `Bad Request (400): ${responseData?.message || responseData?.err || 'Invalid request parameters'}. This might be due to PDF export limitations or invalid frame IDs.`;
620
- } else if (status === 403) {
621
- return `Error: Access denied. Please check your Figma API token and file permissions.`;
622
- } else if (status === 404) {
623
- return `Error: Figma file not found. Please check the URL and ensure the file is accessible.`;
624
- } else {
625
- errorMessage = `HTTP ${status}: ${responseData?.message || responseData?.err || error.response.statusText}`;
626
- }
627
- }
584
+ // Read PDF file
585
+ const pdfData = await fs.readFile(pdfPath);
628
586
 
629
- return `Error exporting from Figma: ${errorMessage}`;
630
- }
631
- }
632
- );
587
+ // Upload file to OpenAI
588
+ const fileUpload = await client.files.create({
589
+ file: new Blob([pdfData], { type: "application/pdf" }),
590
+ purpose: "assistants"
591
+ });
633
592
 
634
- // Helper function to extract file ID from Figma URL
635
- function extractFileIdFromUrl(url) {
636
- const patterns = [
637
- /figma\.com\/file\/([a-zA-Z0-9]+)/,
638
- /figma\.com\/design\/([a-zA-Z0-9]+)/,
639
- /figma\.com\/proto\/([a-zA-Z0-9]+)/
640
- ];
593
+ // Create a random prompt with Jira details
594
+ const prompt = `Analyze this design based on Jira details:\n\nSummary: ${jiraSummary}\nDescription: ${jiraDescription}\n\nProvide suggestions and insights.`;
641
595
 
642
- for (const pattern of patterns) {
643
- const match = url.match(pattern);
644
- if (match) {
645
- return match[1];
596
+ return {
597
+ message: "✅ PDF uploaded to OpenAI successfully!",
598
+ fileId: fileUpload.id,
599
+ promptUsed: prompt
600
+ };
601
+ } catch (err) {
602
+ return `❌ Error uploading PDF to OpenAI: ${err.message}`;
646
603
  }
647
604
  }
648
- return null;
649
- }
605
+ );
650
606
  return server;
651
607
  };
652
608
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nbakka/mcp-appium",
3
- "version": "2.0.47",
3
+ "version": "2.0.48",
4
4
  "description": "Appium MCP",
5
5
  "engines": {
6
6
  "node": ">=18"
@@ -22,7 +22,10 @@
22
22
  "fast-xml-parser": "^5.0.9",
23
23
  "googleapis": "^149.0.0",
24
24
  "zod-to-json-schema": "^3.24.4",
25
- "axios": "^1.6.0"
25
+ "axios": "^1.6.0",
26
+ "openai": "^5.20.0",
27
+ "zod": "^4.1.0",
28
+ "form-data": "^4.0.0"
26
29
  },
27
30
  "devDependencies": {
28
31
  "@eslint/eslintrc": "^3.2.0",