@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/README.md CHANGED
@@ -1,160 +1,160 @@
1
- # @muggleai/mcp
2
-
3
- Unified MCP server for Muggle AI - combines Cloud QA and Local Testing tools into a single package.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install -g @muggleai/mcp
9
- ```
10
-
11
- This automatically:
12
- 1. Installs the package
13
- 2. Downloads the Electron app binary (via postinstall)
14
- 3. Sets up CLI commands
15
-
16
- ## Quick Start
17
-
18
- ### 1. Add to your MCP client
19
-
20
- **Cursor (`~/.cursor/mcp.json`):**
21
-
22
- ```json
23
- {
24
- "mcpServers": {
25
- "muggle": {
26
- "command": "muggle-mcp",
27
- "args": ["serve"]
28
- }
29
- }
30
- }
31
- ```
32
-
33
- ### 2. Start using MCP tools
34
-
35
- Ask your AI assistant to test your application! Authentication happens automatically when needed.
36
-
37
- ## CLI Commands
38
-
39
- ```bash
40
- # Server (main command - starts MCP server for AI clients)
41
- muggle-mcp serve # Start server with all tools (default)
42
- muggle-mcp serve --qa # Cloud QA tools only
43
- muggle-mcp serve --local # Local testing tools only
44
-
45
- # Setup & Diagnostics
46
- muggle-mcp setup # Download/update Electron app
47
- muggle-mcp setup --force # Force re-download
48
- muggle-mcp doctor # Diagnose installation issues
49
-
50
- # Authentication (optional - auth happens automatically)
51
- muggle-mcp login # Manually trigger login
52
- muggle-mcp logout # Clear credentials
53
- muggle-mcp status # Show auth status
54
-
55
- # Info
56
- muggle-mcp --version # Show version
57
- muggle-mcp --help # Show help
58
- ```
59
-
60
- ## Authentication
61
-
62
- Authentication happens automatically when you first use a tool that requires it:
63
-
64
- 1. A browser window opens with a verification code
65
- 2. You log in with your Muggle AI account
66
- 3. The tool call continues with your credentials
67
-
68
- Your credentials are stored in `~/.muggle-ai/credentials.json` and persist across sessions.
69
-
70
- ## Available Tools
71
-
72
- ### Cloud QA Tools
73
-
74
- Tools that work with the Muggle AI cloud backend:
75
-
76
- - `qa_project_create` - Create QA project
77
- - `qa_project_list` - List projects
78
- - `qa_use_case_create_from_prompts` - Create use cases
79
- - `qa_test_case_generate_from_prompt` - Generate test cases
80
- - `qa_workflow_start_*` - Start various workflows
81
- - And more...
82
-
83
- ### Local QA Tools
84
-
85
- Tools that work with local testing:
86
-
87
- - `muggle_project_create` - Create local project
88
- - `muggle_test_case_save` - Save test case locally
89
- - `muggle_execute_test_generation` - Generate test script
90
- - `muggle_execute_replay` - Replay test script
91
- - `muggle_cloud_pull_project` - Pull from cloud
92
- - `muggle_publish_project` - Publish to cloud
93
- - And more...
94
-
95
- ## Data Directory
96
-
97
- All Muggle AI data is stored in `~/.muggle-ai/`:
98
-
99
- ```
100
- ~/.muggle-ai/
101
- ├── credentials.json # Auth credentials (auto-generated)
102
- ├── projects/ # Local test projects
103
- ├── sessions/ # Test execution sessions
104
- └── electron-app/ # Downloaded Electron app
105
- └── 1.0.0/
106
- └── MuggleAI.exe
107
- ```
108
-
109
- ## Requirements
110
-
111
- - Node.js 22 or higher
112
- - For local testing: Electron app (downloaded automatically)
113
-
114
- ## Development
115
-
116
- ### Building
117
-
118
- ```bash
119
- npm install
120
- npm run build
121
- ```
122
-
123
- ### Testing
124
-
125
- ```bash
126
- npm test # Run tests once
127
- npm run test:watch # Watch mode
128
- ```
129
-
130
- ### Linting
131
-
132
- ```bash
133
- npm run lint # Auto-fix issues
134
- npm run lint:check # Check only
135
- ```
136
-
137
- ## CI/CD Workflows
138
-
139
- | Workflow | Trigger | Description |
140
- | :------- | :------ | :---------- |
141
- | `ci.yml` | Push/PR to main | Lint, test, build on multiple platforms/versions |
142
- | `publish-mcp.yml` | Tag `v*` | Publish package to npm |
143
- | `release-electron-app.yml` | Tag `electron-app@*` | Build and release Electron binaries |
144
-
145
- ### Publishing a new version
146
-
147
- 1. Update version in `package.json`
148
- 2. Commit and push
149
- 3. Create a git tag: `git tag v1.0.1 && git push --tags`
150
- 4. The `publish-mcp.yml` workflow publishes to npm automatically
151
-
152
- ### Releasing Electron app
153
-
154
- 1. Update `muggleConfig.electronAppVersion` in `package.json`
155
- 2. Run the `release-electron-app.yml` workflow manually
156
- 3. Or create a tag: `git tag electron-app@1.0.1 && git push --tags`
157
-
158
- ## License
159
-
160
- MIT
1
+ # @muggleai/mcp
2
+
3
+ Unified MCP server for Muggle AI - combines Cloud QA and Local Testing tools into a single package.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g @muggleai/mcp
9
+ ```
10
+
11
+ This automatically:
12
+ 1. Installs the package
13
+ 2. Downloads the Electron app binary (via postinstall)
14
+ 3. Sets up CLI commands
15
+
16
+ ## Quick Start
17
+
18
+ ### 1. Add to your MCP client
19
+
20
+ **Cursor (`~/.cursor/mcp.json`):**
21
+
22
+ ```json
23
+ {
24
+ "mcpServers": {
25
+ "muggle": {
26
+ "command": "muggle-mcp",
27
+ "args": ["serve"]
28
+ }
29
+ }
30
+ }
31
+ ```
32
+
33
+ ### 2. Start using MCP tools
34
+
35
+ Ask your AI assistant to test your application! Authentication happens automatically when needed.
36
+
37
+ ## CLI Commands
38
+
39
+ ```bash
40
+ # Server (main command - starts MCP server for AI clients)
41
+ muggle-mcp serve # Start server with all tools (default)
42
+ muggle-mcp serve --qa # Cloud QA tools only
43
+ muggle-mcp serve --local # Local testing tools only
44
+
45
+ # Setup & Diagnostics
46
+ muggle-mcp setup # Download/update Electron app
47
+ muggle-mcp setup --force # Force re-download
48
+ muggle-mcp doctor # Diagnose installation issues
49
+
50
+ # Authentication (optional - auth happens automatically)
51
+ muggle-mcp login # Manually trigger login
52
+ muggle-mcp logout # Clear credentials
53
+ muggle-mcp status # Show auth status
54
+
55
+ # Info
56
+ muggle-mcp --version # Show version
57
+ muggle-mcp --help # Show help
58
+ ```
59
+
60
+ ## Authentication
61
+
62
+ Authentication happens automatically when you first use a tool that requires it:
63
+
64
+ 1. A browser window opens with a verification code
65
+ 2. You log in with your Muggle AI account
66
+ 3. The tool call continues with your credentials
67
+
68
+ Your credentials are stored in `~/.muggle-ai/credentials.json` and persist across sessions.
69
+
70
+ ## Available Tools
71
+
72
+ ### Cloud QA Tools
73
+
74
+ Tools that work with the Muggle AI cloud backend:
75
+
76
+ - `qa_project_create` - Create QA project
77
+ - `qa_project_list` - List projects
78
+ - `qa_use_case_create_from_prompts` - Create use cases
79
+ - `qa_test_case_generate_from_prompt` - Generate test cases
80
+ - `qa_workflow_start_*` - Start various workflows
81
+ - And more...
82
+
83
+ ### Local QA Tools
84
+
85
+ Tools that work with local testing:
86
+
87
+ - `muggle_project_create` - Create local project
88
+ - `muggle_test_case_save` - Save test case locally
89
+ - `muggle_execute_test_generation` - Generate test script
90
+ - `muggle_execute_replay` - Replay test script
91
+ - `muggle_cloud_pull_project` - Pull from cloud
92
+ - `muggle_publish_project` - Publish to cloud
93
+ - And more...
94
+
95
+ ## Data Directory
96
+
97
+ All Muggle AI data is stored in `~/.muggle-ai/`:
98
+
99
+ ```
100
+ ~/.muggle-ai/
101
+ ├── credentials.json # Auth credentials (auto-generated)
102
+ ├── projects/ # Local test projects
103
+ ├── sessions/ # Test execution sessions
104
+ └── electron-app/ # Downloaded Electron app
105
+ └── 1.0.0/
106
+ └── MuggleAI.exe
107
+ ```
108
+
109
+ ## Requirements
110
+
111
+ - Node.js 22 or higher
112
+ - For local testing: Electron app (downloaded automatically)
113
+
114
+ ## Development
115
+
116
+ ### Building
117
+
118
+ ```bash
119
+ npm install
120
+ npm run build
121
+ ```
122
+
123
+ ### Testing
124
+
125
+ ```bash
126
+ npm test # Run tests once
127
+ npm run test:watch # Watch mode
128
+ ```
129
+
130
+ ### Linting
131
+
132
+ ```bash
133
+ npm run lint # Auto-fix issues
134
+ npm run lint:check # Check only
135
+ ```
136
+
137
+ ## CI/CD Workflows
138
+
139
+ | Workflow | Trigger | Description |
140
+ | :------- | :------ | :---------- |
141
+ | `ci.yml` | Push/PR to main | Lint, test, build on multiple platforms/versions |
142
+ | `publish-mcp.yml` | Tag `v*` | Publish package to npm |
143
+ | `release-electron-app.yml` | Tag `electron-app@*` | Build and release Electron binaries |
144
+
145
+ ### Publishing a new version
146
+
147
+ 1. Update version in `package.json`
148
+ 2. Commit and push
149
+ 3. Create a git tag: `git tag v1.0.1 && git push --tags`
150
+ 4. The `publish-mcp.yml` workflow publishes to npm automatically
151
+
152
+ ### Releasing Electron app
153
+
154
+ 1. Update `muggleConfig.electronAppVersion` in `package.json`
155
+ 2. Run the `release-electron-app.yml` workflow manually
156
+ 3. Or create a tag: `git tag electron-app@1.0.1 && git push --tags`
157
+
158
+ ## License
159
+
160
+ MIT
package/bin/muggle-mcp.js CHANGED
@@ -1,2 +1,2 @@
1
- #!/usr/bin/env node
2
- import "../dist/cli.js";
1
+ #!/usr/bin/env node
2
+ import "../dist/cli.js";
@@ -15,6 +15,12 @@ import { ulid } from 'ulid';
15
15
  import * as fs6 from 'fs/promises';
16
16
 
17
17
  var __defProp = Object.defineProperty;
18
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
19
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
20
+ }) : x)(function(x) {
21
+ if (typeof require !== "undefined") return require.apply(this, arguments);
22
+ throw Error('Dynamic require of "' + x + '" is not supported');
23
+ });
18
24
  var __export = (target, all) => {
19
25
  for (var name in all)
20
26
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -51,14 +57,19 @@ function getMuggleConfig() {
51
57
  const packageJson = JSON.parse(fs4.readFileSync(packageJsonPath, "utf-8"));
52
58
  const config = packageJson.muggleConfig;
53
59
  if (config?.electronAppVersion && config?.downloadBaseUrl) {
54
- muggleConfigCache = config;
55
- return config;
60
+ muggleConfigCache = {
61
+ electronAppVersion: config.electronAppVersion,
62
+ downloadBaseUrl: config.downloadBaseUrl,
63
+ checksums: config.checksums
64
+ };
65
+ return muggleConfigCache;
56
66
  }
57
67
  } catch {
58
68
  }
59
69
  muggleConfigCache = {
60
- electronAppVersion: "1.0.0",
61
- downloadBaseUrl: "https://github.com/multiplex-ai/muggle-ai-mcp/releases/download"
70
+ electronAppVersion: "1.0.1",
71
+ downloadBaseUrl: "https://github.com/multiplex-ai/muggle-ai-mcp/releases/download",
72
+ checksums: {}
62
73
  };
63
74
  return muggleConfigCache;
64
75
  }
@@ -201,12 +212,51 @@ function resetConfig() {
201
212
  configInstance = null;
202
213
  muggleConfigCache = null;
203
214
  }
215
+ var VERSION_OVERRIDE_FILE = "electron-app-version-override.json";
216
+ var ELECTRON_APP_VERSION_ENV = "ELECTRON_APP_VERSION";
204
217
  function getElectronAppVersion() {
218
+ const envVersion = process.env[ELECTRON_APP_VERSION_ENV];
219
+ if (envVersion && /^\d+\.\d+\.\d+$/.test(envVersion)) {
220
+ return envVersion;
221
+ }
222
+ const overridePath = path4.join(getDataDir(), VERSION_OVERRIDE_FILE);
223
+ if (fs4.existsSync(overridePath)) {
224
+ try {
225
+ const content = JSON.parse(fs4.readFileSync(overridePath, "utf-8"));
226
+ if (content.version && typeof content.version === "string") {
227
+ return content.version;
228
+ }
229
+ } catch {
230
+ }
231
+ }
232
+ return getMuggleConfig().electronAppVersion;
233
+ }
234
+ function getElectronAppVersionSource() {
235
+ const envVersion = process.env[ELECTRON_APP_VERSION_ENV];
236
+ if (envVersion && /^\d+\.\d+\.\d+$/.test(envVersion)) {
237
+ return "env";
238
+ }
239
+ const overridePath = path4.join(getDataDir(), VERSION_OVERRIDE_FILE);
240
+ if (fs4.existsSync(overridePath)) {
241
+ try {
242
+ const content = JSON.parse(fs4.readFileSync(overridePath, "utf-8"));
243
+ if (content.version && typeof content.version === "string") {
244
+ return "override";
245
+ }
246
+ } catch {
247
+ }
248
+ }
249
+ return "bundled";
250
+ }
251
+ function getBundledElectronAppVersion() {
205
252
  return getMuggleConfig().electronAppVersion;
206
253
  }
207
254
  function getDownloadBaseUrl() {
208
255
  return getMuggleConfig().downloadBaseUrl;
209
256
  }
257
+ function getElectronAppChecksums() {
258
+ return getMuggleConfig().checksums;
259
+ }
210
260
  function isElectronAppInstalled() {
211
261
  return getDownloadedElectronAppPath() !== null;
212
262
  }
@@ -5656,8 +5706,37 @@ async function spawnElectronApp(params) {
5656
5706
  startedAt: Date.now(),
5657
5707
  status: "running" /* RUNNING */,
5658
5708
  inputFilePath,
5659
- outputFilePath
5709
+ outputFilePath,
5710
+ capturedStdout: "",
5711
+ capturedStderr: "",
5712
+ hasExited: false
5660
5713
  };
5714
+ childProcess.stdout?.on("data", (data) => {
5715
+ executionProcess.capturedStdout += data.toString();
5716
+ });
5717
+ childProcess.stderr?.on("data", (data) => {
5718
+ executionProcess.capturedStderr += data.toString();
5719
+ });
5720
+ childProcess.on("close", (code, signal) => {
5721
+ executionProcess.hasExited = true;
5722
+ executionProcess.earlyExitInfo = {
5723
+ code,
5724
+ signal,
5725
+ stdout: executionProcess.capturedStdout,
5726
+ stderr: executionProcess.capturedStderr
5727
+ };
5728
+ });
5729
+ childProcess.on("error", (error) => {
5730
+ executionProcess.hasExited = true;
5731
+ executionProcess.capturedStderr += `
5732
+ Spawn error: ${error.message}`;
5733
+ executionProcess.earlyExitInfo = {
5734
+ code: -1,
5735
+ signal: null,
5736
+ stdout: executionProcess.capturedStdout,
5737
+ stderr: executionProcess.capturedStderr
5738
+ };
5739
+ });
5661
5740
  const timeoutTimer = setTimeout(() => {
5662
5741
  handleTimeout({ executionProcess });
5663
5742
  }, timeoutMs);
@@ -5675,57 +5754,85 @@ function handleTimeout(params) {
5675
5754
  }
5676
5755
  }
5677
5756
  }
5757
+ async function handleProcessExit(params) {
5758
+ const { executionProcess, code, stderr } = params;
5759
+ if (executionProcess.timeoutTimer) {
5760
+ clearTimeout(executionProcess.timeoutTimer);
5761
+ }
5762
+ activeProcesses.delete(executionProcess.runId);
5763
+ if (executionProcess.status === "cancelled" /* CANCELLED */) {
5764
+ return {
5765
+ success: false,
5766
+ status: "FAILURE",
5767
+ summary: "Execution was cancelled",
5768
+ error: "Cancelled by user"
5769
+ };
5770
+ }
5771
+ if (code !== 0 || executionProcess.status === "failed" /* FAILED */) {
5772
+ executionProcess.status = "failed" /* FAILED */;
5773
+ const errorMessage = stderr || `Process exited with code ${code}`;
5774
+ return {
5775
+ success: false,
5776
+ status: "FAILURE",
5777
+ summary: "Execution failed",
5778
+ error: code === -1 ? errorMessage : `Process crashed with exit code ${code}. ${errorMessage}`
5779
+ };
5780
+ }
5781
+ const outputData = await readTempFile(executionProcess.outputFilePath);
5782
+ executionProcess.status = "passed" /* PASSED */;
5783
+ return {
5784
+ success: true,
5785
+ status: "SUCCESS",
5786
+ summary: "Execution completed successfully",
5787
+ actionScript: outputData
5788
+ };
5789
+ }
5678
5790
  async function waitForCompletion(params) {
5679
5791
  const { executionProcess } = params;
5680
- return new Promise((resolve2) => {
5681
- let stdout = "";
5682
- let stderr = "";
5683
- executionProcess.process.stdout?.on("data", (data) => {
5684
- stdout += data.toString();
5685
- });
5686
- executionProcess.process.stderr?.on("data", (data) => {
5687
- stderr += data.toString();
5792
+ if (executionProcess.hasExited && executionProcess.earlyExitInfo) {
5793
+ const earlyExit = executionProcess.earlyExitInfo;
5794
+ return handleProcessExit({
5795
+ executionProcess,
5796
+ code: earlyExit.code,
5797
+ stderr: earlyExit.stderr
5688
5798
  });
5689
- executionProcess.process.on("close", async (code) => {
5690
- if (executionProcess.timeoutTimer) {
5691
- clearTimeout(executionProcess.timeoutTimer);
5692
- }
5693
- activeProcesses.delete(executionProcess.runId);
5694
- if (executionProcess.status === "cancelled" /* CANCELLED */) {
5695
- resolve2({
5696
- success: false,
5697
- status: "FAILURE",
5698
- summary: "Execution was cancelled",
5699
- error: "Cancelled by user"
5700
- });
5701
- return;
5799
+ }
5800
+ return new Promise((resolve2) => {
5801
+ let resolved = false;
5802
+ const resolveOnce = (result) => {
5803
+ if (!resolved) {
5804
+ resolved = true;
5805
+ resolve2(result);
5702
5806
  }
5703
- if (code !== 0 || executionProcess.status === "failed" /* FAILED */) {
5704
- executionProcess.status = "failed" /* FAILED */;
5705
- resolve2({
5706
- success: false,
5707
- status: "FAILURE",
5708
- summary: "Execution failed",
5709
- error: stderr || `Process exited with code ${code}`
5710
- });
5711
- return;
5807
+ };
5808
+ const checkEarlyExit = setInterval(() => {
5809
+ if (executionProcess.hasExited && executionProcess.earlyExitInfo) {
5810
+ clearInterval(checkEarlyExit);
5811
+ const earlyExit = executionProcess.earlyExitInfo;
5812
+ handleProcessExit({
5813
+ executionProcess,
5814
+ code: earlyExit.code,
5815
+ stderr: earlyExit.stderr
5816
+ }).then(resolveOnce);
5712
5817
  }
5713
- const outputData = await readTempFile(executionProcess.outputFilePath);
5714
- executionProcess.status = "passed" /* PASSED */;
5715
- resolve2({
5716
- success: true,
5717
- status: "SUCCESS",
5718
- summary: "Execution completed successfully",
5719
- actionScript: outputData
5818
+ }, 100);
5819
+ executionProcess.process.on("close", async (code) => {
5820
+ clearInterval(checkEarlyExit);
5821
+ const result = await handleProcessExit({
5822
+ executionProcess,
5823
+ code,
5824
+ stderr: executionProcess.capturedStderr
5720
5825
  });
5826
+ resolveOnce(result);
5721
5827
  });
5722
- executionProcess.process.on("error", (error) => {
5828
+ executionProcess.process.on("error", async (error) => {
5829
+ clearInterval(checkEarlyExit);
5723
5830
  if (executionProcess.timeoutTimer) {
5724
5831
  clearTimeout(executionProcess.timeoutTimer);
5725
5832
  }
5726
5833
  activeProcesses.delete(executionProcess.runId);
5727
5834
  executionProcess.status = "failed" /* FAILED */;
5728
- resolve2({
5835
+ resolveOnce({
5729
5836
  success: false,
5730
5837
  status: "FAILURE",
5731
5838
  summary: "Execution error",
@@ -7621,6 +7728,6 @@ function isLocalOnlyTool(toolName) {
7621
7728
  return localOnlyTools.includes(toolName);
7622
7729
  }
7623
7730
 
7624
- export { __export, createApiKeyWithToken, createChildLogger, createUnifiedMcpServer, deleteCredentials, getAuthStatus, getCallerCredentials, getConfig, getCredentialsFilePath, getDataDir, getDownloadBaseUrl, getElectronAppDir, getElectronAppVersion, getLocalQaTools, getLogger, getQaTools, getValidCredentials, isCredentialsExpired, isElectronAppInstalled, loadCredentials, local_qa_exports, openBrowserUrl, performLogin, performLogout, pollDeviceCode, qa_exports, registerTools, resetConfig, resetLogger, saveCredentials, server_exports, startDeviceCodeFlow, startStdioServer, toolRequiresAuth };
7625
- //# sourceMappingURL=chunk-6DQWAMGJ.js.map
7626
- //# sourceMappingURL=chunk-6DQWAMGJ.js.map
7731
+ export { __export, __require, createApiKeyWithToken, createChildLogger, createUnifiedMcpServer, deleteCredentials, getAuthStatus, getBundledElectronAppVersion, getCallerCredentials, getConfig, getCredentialsFilePath, getDataDir, getDownloadBaseUrl, getElectronAppChecksums, getElectronAppDir, getElectronAppVersion, getElectronAppVersionSource, getLocalQaTools, getLogger, getQaTools, getValidCredentials, isCredentialsExpired, isElectronAppInstalled, loadCredentials, local_qa_exports, openBrowserUrl, performLogin, performLogout, pollDeviceCode, qa_exports, registerTools, resetConfig, resetLogger, saveCredentials, server_exports, startDeviceCodeFlow, startStdioServer, toolRequiresAuth };
7732
+ //# sourceMappingURL=chunk-IGLYJZH6.js.map
7733
+ //# sourceMappingURL=chunk-IGLYJZH6.js.map