@muggleai/mcp 1.0.0 → 1.0.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/package.json CHANGED
@@ -1,75 +1,81 @@
1
- {
2
- "name": "@muggleai/mcp",
3
- "version": "1.0.0",
4
- "description": "Unified MCP server for Muggle AI - Cloud QA and Local Testing tools",
5
- "type": "module",
6
- "main": "dist/index.js",
7
- "types": "dist/index.d.ts",
8
- "bin": {
9
- "muggle-mcp": "bin/muggle-mcp.js"
10
- },
11
- "files": [
12
- "dist",
13
- "bin/muggle-mcp.js",
14
- "scripts/postinstall.mjs"
15
- ],
16
- "scripts": {
17
- "clean": "rimraf dist",
18
- "build": "tsup && tsc --emitDeclarationOnly --declaration --outDir dist",
19
- "build:release": "npm run build",
20
- "postinstall": "node scripts/postinstall.mjs",
21
- "start": "node dist/index.js",
22
- "dev": "tsx watch src/index.ts",
23
- "lint": "eslint . --fix",
24
- "lint:check": "eslint .",
25
- "test": "vitest run",
26
- "test:watch": "vitest"
27
- },
28
- "muggleConfig": {
29
- "electronAppVersion": "1.0.0",
30
- "downloadBaseUrl": "https://github.com/multiplex-ai/muggle-ai-mcp/releases/download"
31
- },
32
- "dependencies": {
33
- "@modelcontextprotocol/sdk": "^1.25.3",
34
- "axios": "^1.7.9",
35
- "commander": "^12.0.0",
36
- "open": "^10.0.0",
37
- "ulid": "^2.3.0",
38
- "uuid": "^10.0.0",
39
- "winston": "^3.17.0",
40
- "zod": "^3.24.1"
41
- },
42
- "devDependencies": {
43
- "@eslint/js": "^9.21.0",
44
- "@types/node": "^22.0.0",
45
- "@types/uuid": "^10.0.0",
46
- "@typescript-eslint/eslint-plugin": "^8.34.0",
47
- "@typescript-eslint/parser": "^8.34.0",
48
- "eslint": "^9.28.0",
49
- "eslint-plugin-unused-imports": "^4.2.0",
50
- "rimraf": "^6.0.1",
51
- "tsup": "^8.5.1",
52
- "tsx": "^4.19.2",
53
- "typescript": "^5.7.3",
54
- "vitest": "^4.0.18"
55
- },
56
- "engines": {
57
- "node": ">=22.0.0"
58
- },
59
- "keywords": [
60
- "mcp",
61
- "model-context-protocol",
62
- "muggle-ai",
63
- "qa",
64
- "testing",
65
- "automation",
66
- "localhost"
67
- ],
68
- "author": "Muggle AI",
69
- "license": "MIT",
70
- "repository": {
71
- "type": "git",
72
- "url": "https://github.com/multiplex-ai/muggle-ai-mcp.git"
73
- },
74
- "homepage": "https://www.muggle-ai.com/muggleTestV0/docs/mcp/mcp-overview"
75
- }
1
+ {
2
+ "name": "@muggleai/mcp",
3
+ "version": "1.0.1",
4
+ "description": "Unified MCP server for Muggle AI - Cloud QA and Local Testing tools",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "muggle-mcp": "bin/muggle-mcp.js"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "bin/muggle-mcp.js",
14
+ "scripts/postinstall.mjs"
15
+ ],
16
+ "scripts": {
17
+ "clean": "rimraf dist",
18
+ "build": "tsup && tsc --emitDeclarationOnly --declaration --outDir dist",
19
+ "build:release": "npm run build",
20
+ "postinstall": "node scripts/postinstall.mjs",
21
+ "start": "node dist/index.js",
22
+ "dev": "tsx watch src/index.ts",
23
+ "lint": "eslint . --fix",
24
+ "lint:check": "eslint .",
25
+ "test": "vitest run",
26
+ "test:watch": "vitest"
27
+ },
28
+ "muggleConfig": {
29
+ "electronAppVersion": "1.0.1",
30
+ "downloadBaseUrl": "https://github.com/multiplex-ai/muggle-ai-mcp/releases/download",
31
+ "checksums": {
32
+ "darwin-arm64": "",
33
+ "darwin-x64": "",
34
+ "win32-x64": "",
35
+ "linux-x64": ""
36
+ }
37
+ },
38
+ "dependencies": {
39
+ "@modelcontextprotocol/sdk": "^1.25.3",
40
+ "axios": "^1.7.9",
41
+ "commander": "^12.0.0",
42
+ "open": "^10.0.0",
43
+ "ulid": "^2.3.0",
44
+ "uuid": "^10.0.0",
45
+ "winston": "^3.17.0",
46
+ "zod": "^3.24.1"
47
+ },
48
+ "devDependencies": {
49
+ "@eslint/js": "^9.21.0",
50
+ "@types/node": "^22.0.0",
51
+ "@types/uuid": "^10.0.0",
52
+ "@typescript-eslint/eslint-plugin": "^8.34.0",
53
+ "@typescript-eslint/parser": "^8.34.0",
54
+ "eslint": "^9.28.0",
55
+ "eslint-plugin-unused-imports": "^4.2.0",
56
+ "rimraf": "^6.0.1",
57
+ "tsup": "^8.5.1",
58
+ "tsx": "^4.19.2",
59
+ "typescript": "^5.7.3",
60
+ "vitest": "^4.0.18"
61
+ },
62
+ "engines": {
63
+ "node": ">=22.0.0"
64
+ },
65
+ "keywords": [
66
+ "mcp",
67
+ "model-context-protocol",
68
+ "muggle-ai",
69
+ "qa",
70
+ "testing",
71
+ "automation",
72
+ "localhost"
73
+ ],
74
+ "author": "Muggle AI",
75
+ "license": "MIT",
76
+ "repository": {
77
+ "type": "git",
78
+ "url": "git+https://github.com/multiplex-ai/muggle-ai-mcp.git"
79
+ },
80
+ "homepage": "https://www.muggle-ai.com/muggleTestV0/docs/mcp/mcp-overview"
81
+ }
@@ -1,153 +1,247 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Postinstall script for @muggleai/mcp.
4
- * Downloads the Electron app binary for local testing.
5
- */
6
-
7
- import { exec } from "child_process";
8
- import { createWriteStream, existsSync, mkdirSync, rmSync } from "fs";
9
- import { homedir, platform } from "os";
10
- import { join } from "path";
11
- import { pipeline } from "stream/promises";
12
- import { createRequire } from "module";
13
-
14
- const require = createRequire(import.meta.url);
15
-
16
- /**
17
- * Get the Muggle AI data directory.
18
- * @returns {string} Path to ~/.muggle-ai
19
- */
20
- function getDataDir() {
21
- return join(homedir(), ".muggle-ai");
22
- }
23
-
24
- /**
25
- * Get the Electron app directory.
26
- * @returns {string} Path to ~/.muggle-ai/electron-app
27
- */
28
- function getElectronAppDir() {
29
- return join(getDataDir(), "electron-app");
30
- }
31
-
32
- /**
33
- * Get platform-specific binary name.
34
- * @returns {string} Binary filename
35
- */
36
- function getBinaryName() {
37
- const os = platform();
38
- const arch = process.arch;
39
-
40
- switch (os) {
41
- case "darwin":
42
- // Support both Apple Silicon (arm64) and Intel (x64) Macs
43
- return arch === "arm64"
44
- ? "MuggleAI-darwin-arm64.zip"
45
- : "MuggleAI-darwin-x64.zip";
46
- case "win32":
47
- return "MuggleAI-win32-x64.zip";
48
- case "linux":
49
- return "MuggleAI-linux-x64.zip";
50
- default:
51
- throw new Error(`Unsupported platform: ${os}`);
52
- }
53
- }
54
-
55
- /**
56
- * Download and extract the Electron app.
57
- */
58
- async function downloadElectronApp() {
59
- try {
60
- // Read config from package.json
61
- const packageJson = require("../package.json");
62
- const config = packageJson.muggleConfig || {};
63
- const version = config.electronAppVersion || "1.0.0";
64
- const baseUrl = config.downloadBaseUrl || "https://github.com/multiplex-ai/muggle-ai-mcp/releases/download";
65
-
66
- const binaryName = getBinaryName();
67
- const downloadUrl = `${baseUrl}/electron-app-v${version}/${binaryName}`;
68
-
69
- const appDir = getElectronAppDir();
70
- const versionDir = join(appDir, version);
71
-
72
- // Check if already downloaded
73
- if (existsSync(versionDir)) {
74
- console.log(`Electron app v${version} already installed at ${versionDir}`);
75
- return;
76
- }
77
-
78
- console.log(`Downloading Muggle Test Electron app v${version}...`);
79
- console.log(`URL: ${downloadUrl}`);
80
-
81
- // Create directories
82
- mkdirSync(versionDir, { recursive: true });
83
-
84
- // Download using fetch
85
- const response = await fetch(downloadUrl);
86
- if (!response.ok) {
87
- throw new Error(`Download failed: ${response.status} ${response.statusText}`);
88
- }
89
-
90
- const tempFile = join(versionDir, binaryName);
91
- const fileStream = createWriteStream(tempFile);
92
- await pipeline(response.body, fileStream);
93
-
94
- console.log("Download complete, extracting...");
95
-
96
- // Extract based on file type
97
- if (binaryName.endsWith(".zip")) {
98
- await extractZip(tempFile, versionDir);
99
- } else if (binaryName.endsWith(".tar.gz")) {
100
- await extractTarGz(tempFile, versionDir);
101
- }
102
-
103
- // Clean up temp file
104
- rmSync(tempFile, { force: true });
105
-
106
- console.log(`Electron app installed to ${versionDir}`);
107
- } catch (error) {
108
- console.warn("Warning: Failed to download Electron app.");
109
- console.warn("You can manually download it later using: muggle-mcp setup");
110
- console.warn(`Error: ${error.message}`);
111
- }
112
- }
113
-
114
- /**
115
- * Extract a zip file.
116
- * @param {string} zipPath - Path to zip file
117
- * @param {string} destDir - Destination directory
118
- */
119
- async function extractZip(zipPath, destDir) {
120
- return new Promise((resolve, reject) => {
121
- const cmd = platform() === "win32"
122
- ? `powershell -command "Expand-Archive -Path '${zipPath}' -DestinationPath '${destDir}' -Force"`
123
- : `unzip -o "${zipPath}" -d "${destDir}"`;
124
-
125
- exec(cmd, (error) => {
126
- if (error) {
127
- reject(error);
128
- } else {
129
- resolve();
130
- }
131
- });
132
- });
133
- }
134
-
135
- /**
136
- * Extract a tar.gz file.
137
- * @param {string} tarPath - Path to tar.gz file
138
- * @param {string} destDir - Destination directory
139
- */
140
- async function extractTarGz(tarPath, destDir) {
141
- return new Promise((resolve, reject) => {
142
- exec(`tar -xzf "${tarPath}" -C "${destDir}"`, (error) => {
143
- if (error) {
144
- reject(error);
145
- } else {
146
- resolve();
147
- }
148
- });
149
- });
150
- }
151
-
152
- // Run postinstall
153
- downloadElectronApp().catch(console.error);
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Postinstall script for @muggleai/mcp.
4
+ * Downloads the Electron app binary for local testing.
5
+ */
6
+
7
+ import { createHash } from "crypto";
8
+ import { exec } from "child_process";
9
+ import { createReadStream, createWriteStream, existsSync, mkdirSync, rmSync } from "fs";
10
+ import { homedir, platform } from "os";
11
+ import { join } from "path";
12
+ import { pipeline } from "stream/promises";
13
+ import { createRequire } from "module";
14
+
15
+ const require = createRequire(import.meta.url);
16
+
17
+ /**
18
+ * Get the Muggle AI data directory.
19
+ * @returns {string} Path to ~/.muggle-ai
20
+ */
21
+ function getDataDir() {
22
+ return join(homedir(), ".muggle-ai");
23
+ }
24
+
25
+ /**
26
+ * Get the Electron app directory.
27
+ * @returns {string} Path to ~/.muggle-ai/electron-app
28
+ */
29
+ function getElectronAppDir() {
30
+ return join(getDataDir(), "electron-app");
31
+ }
32
+
33
+ /**
34
+ * Get platform key for checksum lookup.
35
+ * @returns {string} Platform key (e.g., "darwin-arm64", "win32-x64")
36
+ */
37
+ function getPlatformKey() {
38
+ const os = platform();
39
+ const arch = process.arch;
40
+
41
+ switch (os) {
42
+ case "darwin":
43
+ return arch === "arm64" ? "darwin-arm64" : "darwin-x64";
44
+ case "win32":
45
+ return "win32-x64";
46
+ case "linux":
47
+ return "linux-x64";
48
+ default:
49
+ throw new Error(`Unsupported platform: ${os}`);
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Calculate SHA256 checksum of a file.
55
+ * @param {string} filePath - Path to the file
56
+ * @returns {Promise<string>} SHA256 checksum as hex string
57
+ */
58
+ async function calculateFileChecksum(filePath) {
59
+ return new Promise((resolve, reject) => {
60
+ const hash = createHash("sha256");
61
+ const stream = createReadStream(filePath);
62
+
63
+ stream.on("data", (data) => {
64
+ hash.update(data);
65
+ });
66
+
67
+ stream.on("end", () => {
68
+ resolve(hash.digest("hex"));
69
+ });
70
+
71
+ stream.on("error", (error) => {
72
+ reject(error);
73
+ });
74
+ });
75
+ }
76
+
77
+ /**
78
+ * Verify file checksum against expected value.
79
+ * @param {string} filePath - Path to the file
80
+ * @param {string} expectedChecksum - Expected SHA256 checksum
81
+ * @returns {Promise<{valid: boolean, actual: string}>} Verification result
82
+ */
83
+ async function verifyFileChecksum(filePath, expectedChecksum) {
84
+ if (!expectedChecksum || expectedChecksum.trim() === "") {
85
+ return { valid: true, actual: "", skipped: true };
86
+ }
87
+
88
+ const actualChecksum = await calculateFileChecksum(filePath);
89
+ const normalizedExpected = expectedChecksum.toLowerCase().trim();
90
+ const normalizedActual = actualChecksum.toLowerCase();
91
+
92
+ return {
93
+ valid: normalizedExpected === normalizedActual,
94
+ expected: normalizedExpected,
95
+ actual: normalizedActual,
96
+ skipped: false,
97
+ };
98
+ }
99
+
100
+ /**
101
+ * Get platform-specific binary name.
102
+ * @returns {string} Binary filename
103
+ */
104
+ function getBinaryName() {
105
+ const os = platform();
106
+ const arch = process.arch;
107
+
108
+ switch (os) {
109
+ case "darwin":
110
+ // Support both Apple Silicon (arm64) and Intel (x64) Macs
111
+ return arch === "arm64"
112
+ ? "MuggleAI-darwin-arm64.zip"
113
+ : "MuggleAI-darwin-x64.zip";
114
+ case "win32":
115
+ return "MuggleAI-win32-x64.zip";
116
+ case "linux":
117
+ return "MuggleAI-linux-x64.zip";
118
+ default:
119
+ throw new Error(`Unsupported platform: ${os}`);
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Download and extract the Electron app.
125
+ */
126
+ async function downloadElectronApp() {
127
+ try {
128
+ // Read config from package.json
129
+ const packageJson = require("../package.json");
130
+ const config = packageJson.muggleConfig || {};
131
+ const version = config.electronAppVersion || "1.0.0";
132
+ const baseUrl = config.downloadBaseUrl || "https://github.com/multiplex-ai/muggle-ai-mcp/releases/download";
133
+
134
+ const binaryName = getBinaryName();
135
+ const downloadUrl = `${baseUrl}/electron-app-v${version}/${binaryName}`;
136
+
137
+ const appDir = getElectronAppDir();
138
+ const versionDir = join(appDir, version);
139
+
140
+ // Check if already downloaded
141
+ if (existsSync(versionDir)) {
142
+ console.log(`Electron app v${version} already installed at ${versionDir}`);
143
+ return;
144
+ }
145
+
146
+ console.log(`Downloading Muggle Test Electron app v${version}...`);
147
+ console.log(`URL: ${downloadUrl}`);
148
+
149
+ // Create directories
150
+ mkdirSync(versionDir, { recursive: true });
151
+
152
+ // Download using fetch
153
+ const response = await fetch(downloadUrl);
154
+ if (!response.ok) {
155
+ throw new Error(`Download failed: ${response.status} ${response.statusText}`);
156
+ }
157
+
158
+ const tempFile = join(versionDir, binaryName);
159
+ const fileStream = createWriteStream(tempFile);
160
+ await pipeline(response.body, fileStream);
161
+
162
+ console.log("Download complete, verifying checksum...");
163
+
164
+ // Get expected checksum from config
165
+ const checksums = config.checksums || {};
166
+ const platformKey = getPlatformKey();
167
+ const expectedChecksum = checksums[platformKey] || "";
168
+
169
+ // Verify checksum
170
+ const checksumResult = await verifyFileChecksum(tempFile, expectedChecksum);
171
+
172
+ if (!checksumResult.valid && !checksumResult.skipped) {
173
+ rmSync(versionDir, { recursive: true, force: true });
174
+ throw new Error(
175
+ `Checksum verification failed!\n` +
176
+ `Expected: ${checksumResult.expected}\n` +
177
+ `Actual: ${checksumResult.actual}\n` +
178
+ `The downloaded file may be corrupted or tampered with.`
179
+ );
180
+ }
181
+
182
+ if (checksumResult.skipped) {
183
+ console.log("Warning: No checksum configured, skipping verification.");
184
+ } else {
185
+ console.log("Checksum verified successfully.");
186
+ }
187
+
188
+ console.log("Extracting...");
189
+
190
+ // Extract based on file type
191
+ if (binaryName.endsWith(".zip")) {
192
+ await extractZip(tempFile, versionDir);
193
+ } else if (binaryName.endsWith(".tar.gz")) {
194
+ await extractTarGz(tempFile, versionDir);
195
+ }
196
+
197
+ // Clean up temp file
198
+ rmSync(tempFile, { force: true });
199
+
200
+ console.log(`Electron app installed to ${versionDir}`);
201
+ } catch (error) {
202
+ console.warn("Warning: Failed to download Electron app.");
203
+ console.warn("You can manually download it later using: muggle-mcp setup");
204
+ console.warn(`Error: ${error.message}`);
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Extract a zip file.
210
+ * @param {string} zipPath - Path to zip file
211
+ * @param {string} destDir - Destination directory
212
+ */
213
+ async function extractZip(zipPath, destDir) {
214
+ return new Promise((resolve, reject) => {
215
+ const cmd = platform() === "win32"
216
+ ? `powershell -command "Expand-Archive -Path '${zipPath}' -DestinationPath '${destDir}' -Force"`
217
+ : `unzip -o "${zipPath}" -d "${destDir}"`;
218
+
219
+ exec(cmd, (error) => {
220
+ if (error) {
221
+ reject(error);
222
+ } else {
223
+ resolve();
224
+ }
225
+ });
226
+ });
227
+ }
228
+
229
+ /**
230
+ * Extract a tar.gz file.
231
+ * @param {string} tarPath - Path to tar.gz file
232
+ * @param {string} destDir - Destination directory
233
+ */
234
+ async function extractTarGz(tarPath, destDir) {
235
+ return new Promise((resolve, reject) => {
236
+ exec(`tar -xzf "${tarPath}" -C "${destDir}"`, (error) => {
237
+ if (error) {
238
+ reject(error);
239
+ } else {
240
+ resolve();
241
+ }
242
+ });
243
+ });
244
+ }
245
+
246
+ // Run postinstall
247
+ downloadElectronApp().catch(console.error);