@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.
- package/lib/server.js +97 -80
- 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
|
-
|
|
478
|
-
|
|
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
|
-
//
|
|
486
|
-
const figmaConfigPath = path.join(os.homedir(),
|
|
487
|
-
|
|
488
|
-
|
|
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
|
|
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
|
-
//
|
|
510
|
-
const
|
|
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
|
-
|
|
513
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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[
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
556
|
-
|
|
557
|
-
|
|
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
|
-
//
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
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
|
-
|
|
580
|
-
|
|
581
|
-
|
|
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.
|
|
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",
|