@nbakka/mcp-appium 2.0.46 → 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 +97 -80
  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,118 +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');
518
+ // Get file structure to find frames
519
+ const fileResponse = await axios.get(
520
+ `https://api.figma.com/v1/files/${fileId}`,
521
+ { headers: { "X-Figma-Token": figmaToken } }
522
+ );
511
523
 
512
- // Create export directory if it doesn't exist
513
- await fs.mkdir(exportPath, { recursive: true });
524
+ const frameIds = [];
525
+ fileResponse.data.document.children?.forEach(page => {
526
+ page.children?.forEach(child => {
527
+ if (child.type === "FRAME") frameIds.push(child.id);
528
+ });
529
+ });
514
530
 
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
- }
531
+ if (frameIds.length === 0) throw new Error("No frames found in Figma file");
525
532
 
526
- // Request PDF export of entire file
533
+ // Request PDF export
527
534
  const exportResponse = await axios.get(
528
535
  `https://api.figma.com/v1/images/${fileId}`,
529
536
  {
530
- headers: {
531
- 'X-Figma-Token': figmaToken
532
- },
533
- params: {
534
- format: 'pdf'
535
- }
537
+ headers: { "X-Figma-Token": figmaToken },
538
+ params: { ids: frameIds.join(","), format: "pdf" }
536
539
  }
537
540
  );
538
541
 
539
- const pdfUrl = exportResponse.data.images[fileId];
540
-
541
- if (!pdfUrl) {
542
- throw new Error('Failed to get PDF export URL from Figma');
543
- }
542
+ const pdfUrl = Object.values(exportResponse.data.images)[0];
543
+ if (!pdfUrl) throw new Error("No PDF export URL returned from Figma");
544
544
 
545
545
  // Download PDF
546
- const pdfResponse = await axios.get(pdfUrl, {
547
- responseType: 'arraybuffer'
548
- });
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 });
549
550
 
550
- // Save PDF to file with timestamp
551
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
551
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, -5);
552
552
  const pdfPath = path.join(exportPath, `figma-export-${timestamp}.pdf`);
553
553
  await fs.writeFile(pdfPath, pdfResponse.data);
554
554
 
555
- return `Figma PDF Export Complete!
556
- Export Path: ${pdfPath}
557
- File: figma-export-${timestamp}.pdf`;
558
-
559
- } catch (error) {
560
- if (error.response && error.response.status === 403) {
561
- return `Error: Access denied. Please check your Figma API token and file permissions.`;
562
- } else if (error.response && error.response.status === 404) {
563
- return `Error: Figma file not found. Please check the URL and ensure the file is accessible.`;
564
- } else {
565
- return `Error exporting from Figma: ${error.message}`;
566
- }
555
+ return `✅ PDF Export Complete: ${pdfPath}`;
556
+ } catch (err) {
557
+ return `❌ Error exporting Figma PDF: ${err.message}`;
567
558
  }
568
559
  }
569
560
  );
570
561
 
571
- // Helper function to extract file ID from Figma URL
572
- function extractFileIdFromUrl(url) {
573
- const patterns = [
574
- /figma\.com\/file\/([a-zA-Z0-9]+)/,
575
- /figma\.com\/design\/([a-zA-Z0-9]+)/,
576
- /figma\.com\/proto\/([a-zA-Z0-9]+)/
577
- ];
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);
578
579
 
579
- for (const pattern of patterns) {
580
- const match = url.match(pattern);
581
- if (match) {
582
- return match[1];
583
- }
584
- }
585
- return null;
586
- }
580
+ if (!openaiKey) throw new Error("OpenAI API key missing in openai.json");
581
+
582
+ const client = new OpenAI({ apiKey: openaiKey });
587
583
 
584
+ // Read PDF file
585
+ const pdfData = await fs.readFile(pdfPath);
588
586
 
587
+ // Upload file to OpenAI
588
+ const fileUpload = await client.files.create({
589
+ file: new Blob([pdfData], { type: "application/pdf" }),
590
+ purpose: "assistants"
591
+ });
592
+
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.`;
595
+
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}`;
603
+ }
604
+ }
605
+ );
589
606
  return server;
590
607
  };
591
608
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nbakka/mcp-appium",
3
- "version": "2.0.46",
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",