@m00nsolutions/mcp-server 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 M00n Solutions
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,236 @@
1
+ # @m00nsolutions/mcp-server
2
+
3
+ MCP (Model Context Protocol) server for M00n Report. Enables AI assistants like **Claude** and **Cursor** to query your test analytics.
4
+
5
+ ## Features
6
+
7
+ - 📊 **Query test trends** - Pass rates, failing tests, flaky tests over time
8
+ - 📈 **Get launch statistics** - Test run summaries with filtering
9
+ - 📁 **List projects** - Browse accessible projects
10
+ - 🔒 **Secure** - Uses your MCP key with your permissions
11
+ - 🏢 **Self-hosted ready** - Works with self-hosted M00n Report instances
12
+
13
+ ## Quick Start
14
+
15
+ ### 1. Get an MCP Key
16
+
17
+ 1. Log in to M00n Report
18
+ 2. Go to **Settings → MCP Keys**
19
+ 3. Create a new MCP key
20
+ 4. Copy the key (starts with `m00n_mcp_`)
21
+
22
+ ### 2. Configure Cursor
23
+
24
+ Add to your Cursor MCP configuration (`~/.cursor/mcp.json` or workspace `.cursor/mcp.json`):
25
+
26
+ ```json
27
+ {
28
+ "mcpServers": {
29
+ "m00n": {
30
+ "command": "npx",
31
+ "args": ["-y", "@m00nsolutions/mcp-server"],
32
+ "env": {
33
+ "M00N_API_URL": "https://app.m00nreport.com",
34
+ "M00N_API_KEY": "m00n_mcp_your_key_here"
35
+ }
36
+ }
37
+ }
38
+ }
39
+ ```
40
+
41
+ ### 3. Configure Claude Desktop
42
+
43
+ Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
44
+
45
+ ```json
46
+ {
47
+ "mcpServers": {
48
+ "m00n": {
49
+ "command": "npx",
50
+ "args": ["-y", "@m00nsolutions/mcp-server"],
51
+ "env": {
52
+ "M00N_API_URL": "https://app.m00nreport.com",
53
+ "M00N_API_KEY": "m00n_mcp_your_key_here"
54
+ }
55
+ }
56
+ }
57
+ }
58
+ ```
59
+
60
+ ## Available Tools
61
+
62
+ ### `list_projects`
63
+
64
+ List all projects accessible to your MCP key.
65
+
66
+ **Example prompt:** "What projects do I have access to in M00n Report?"
67
+
68
+ ### `get_launches_trends`
69
+
70
+ Get comprehensive trend data for a project including pass rates, failing tests, flaky tests, and duration trends.
71
+
72
+ **Parameters:**
73
+ - `projectId` (required) - Project UUID
74
+ - `days` (optional) - Lookback period, 1-365 (default: 30)
75
+ - `tags` (optional) - Filter runs by tags
76
+ - `topN` (optional) - Limit for top-N lists (default: 10)
77
+ - `include` (optional) - Which trends to include: `passRate`, `duration`, `failing`, `flaky`, `slowest`, `testsByTags`
78
+
79
+ **Example prompt:** "Show me the test pass rate trends for project abc-123 over the last 14 days"
80
+
81
+ ### `get_launches_statistics`
82
+
83
+ Get a list of test runs with aggregated statistics for a project.
84
+
85
+ **Parameters:**
86
+ - `projectId` (required) - Project UUID
87
+ - `days` (optional) - Lookback period, 1-365 (default: 30)
88
+ - `tags` (optional) - Filter by tags
89
+ - `statuses` (optional) - Filter by status: `passed`, `failed`, `running`, `interrupted`
90
+ - `search` (optional) - Search in launch title/ID/tags
91
+ - `limit` (optional) - Number of launches to return (default: 20)
92
+ - `offset` (optional) - Pagination offset
93
+
94
+ **Example prompt:** "What were the last 10 failed test runs in project abc-123?"
95
+
96
+ ## Environment Variables
97
+
98
+ | Variable | Required | Default | Description |
99
+ |----------|----------|---------|-------------|
100
+ | `M00N_API_URL` | ✅ | - | M00n Report API URL (e.g., `https://app.m00nreport.com`) |
101
+ | `M00N_API_KEY` | ✅ | - | MCP key from Settings → MCP Keys |
102
+ | `M00N_INSECURE_SSL` | | `false` | Skip SSL verification (for self-signed certs) |
103
+ | `M00N_DEBUG` | | `false` | Enable verbose logging |
104
+ | `M00N_TIMEOUT_MS` | | `30000` | Request timeout in milliseconds |
105
+
106
+ ## Self-Hosted Installation
107
+
108
+ ### Standard Installation
109
+
110
+ Works with any self-hosted M00n Report instance:
111
+
112
+ ```json
113
+ {
114
+ "mcpServers": {
115
+ "m00n": {
116
+ "command": "npx",
117
+ "args": ["-y", "@m00nsolutions/mcp-server"],
118
+ "env": {
119
+ "M00N_API_URL": "https://your-m00n-instance.company.com",
120
+ "M00N_API_KEY": "m00n_mcp_your_key_here"
121
+ }
122
+ }
123
+ }
124
+ }
125
+ ```
126
+
127
+ ### Self-Signed Certificates
128
+
129
+ For self-hosted instances with self-signed SSL certificates:
130
+
131
+ ```json
132
+ {
133
+ "mcpServers": {
134
+ "m00n": {
135
+ "command": "npx",
136
+ "args": ["-y", "@m00nsolutions/mcp-server"],
137
+ "env": {
138
+ "M00N_API_URL": "https://your-m00n-instance.company.com",
139
+ "M00N_API_KEY": "m00n_mcp_your_key_here",
140
+ "M00N_INSECURE_SSL": "true"
141
+ }
142
+ }
143
+ }
144
+ }
145
+ ```
146
+
147
+ ⚠️ **Security Note:** Only use `M00N_INSECURE_SSL=true` for trusted internal servers with self-signed certificates.
148
+
149
+ ### Offline/Air-Gapped Installation
150
+
151
+ For environments without internet access:
152
+
153
+ 1. On a machine with internet, create a tarball:
154
+ ```bash
155
+ npm pack @m00nsolutions/mcp-server
156
+ ```
157
+
158
+ 2. Transfer `m00nsolutions-mcp-server-1.0.0.tgz` to the air-gapped machine
159
+
160
+ 3. Install from tarball:
161
+ ```bash
162
+ npm install -g ./m00nsolutions-mcp-server-1.0.0.tgz
163
+ ```
164
+
165
+ 4. Configure to use the global installation:
166
+ ```json
167
+ {
168
+ "mcpServers": {
169
+ "m00n": {
170
+ "command": "m00n-mcp",
171
+ "env": {
172
+ "M00N_API_URL": "https://your-m00n-instance.company.com",
173
+ "M00N_API_KEY": "m00n_mcp_your_key_here"
174
+ }
175
+ }
176
+ }
177
+ }
178
+ ```
179
+
180
+ ## Troubleshooting
181
+
182
+ ### "Cannot connect to M00n Report API"
183
+
184
+ - Verify `M00N_API_URL` is correct and accessible
185
+ - Check network connectivity to the M00n Report server
186
+ - For self-hosted: ensure the API port (default 4000) is accessible
187
+
188
+ ### "Authentication failed: Invalid or expired MCP key"
189
+
190
+ - Verify `M00N_API_KEY` starts with `m00n_mcp_`
191
+ - Check the key hasn't been revoked in Settings → MCP Keys
192
+ - Ensure you're using an MCP key, not a project API key
193
+
194
+ ### "SSL certificate error"
195
+
196
+ - For self-signed certificates, set `M00N_INSECURE_SSL=true`
197
+ - Verify the certificate chain if using a private CA
198
+
199
+ ### "No tools available"
200
+
201
+ - Check your MCP key has appropriate permissions
202
+ - Verify your user account has access to at least one project
203
+
204
+ ### Enable Debug Logging
205
+
206
+ Set `M00N_DEBUG=true` to see detailed logs:
207
+
208
+ ```json
209
+ {
210
+ "env": {
211
+ "M00N_API_URL": "...",
212
+ "M00N_API_KEY": "...",
213
+ "M00N_DEBUG": "true"
214
+ }
215
+ }
216
+ ```
217
+
218
+ ## Example Prompts
219
+
220
+ Once configured, try these prompts in Claude or Cursor:
221
+
222
+ - "List my M00n Report projects"
223
+ - "Show me the test pass rate trends for the last 30 days"
224
+ - "What are the top 10 flaky tests in project X?"
225
+ - "Get me the failed test runs from this week"
226
+ - "Which tests are taking the longest to run?"
227
+
228
+ ## Support
229
+
230
+ - **Documentation:** https://m00nreport.com/docs
231
+ - **Issues:** https://github.com/m00nsolutions/m00nreport/issues
232
+ - **Email:** support@m00nreport.com
233
+
234
+ ## License
235
+
236
+ MIT
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+
3
+ // M00n Report MCP Server
4
+ // Enables AI assistants (Claude, Cursor) to query test analytics
5
+ //
6
+ // Usage:
7
+ // M00N_API_URL=https://app.m00nreport.com M00N_API_KEY=your-key npx @m00nsolutions/mcp-server
8
+ //
9
+ // For Cursor/Claude Desktop config, see README.md
10
+
11
+ import { existsSync } from 'fs';
12
+ import { fileURLToPath } from 'url';
13
+ import { dirname, join } from 'path';
14
+
15
+ const __dirname = dirname(fileURLToPath(import.meta.url));
16
+ const distPath = join(__dirname, '..', 'dist', 'cli.js');
17
+
18
+ // Check if the package has been built (helpful during development)
19
+ if (!existsSync(distPath)) {
20
+ console.error('Error: Package not built. Run "npm run build" first.');
21
+ console.error(`Expected: ${distPath}`);
22
+ process.exit(1);
23
+ }
24
+
25
+ import('../dist/cli.js').catch((err) => {
26
+ console.error('Failed to start M00n MCP server:', err.message);
27
+ process.exit(1);
28
+ });
@@ -0,0 +1,48 @@
1
+ import { Config } from './config.js';
2
+ export interface Tool {
3
+ name: string;
4
+ description: string;
5
+ inputSchema: {
6
+ type: 'object';
7
+ properties: Record<string, unknown>;
8
+ required?: string[];
9
+ };
10
+ }
11
+ export interface HealthResponse {
12
+ status: string;
13
+ version?: string;
14
+ mode?: string;
15
+ }
16
+ export interface ToolCallResult {
17
+ [key: string]: any;
18
+ }
19
+ export interface ApiError {
20
+ error: string;
21
+ message: string;
22
+ code?: string;
23
+ }
24
+ /**
25
+ * M00n Report API Client
26
+ */
27
+ export declare class M00nApiClient {
28
+ private client;
29
+ private config;
30
+ constructor(config: Config);
31
+ /**
32
+ * Check API health and get version info
33
+ */
34
+ checkHealth(): Promise<HealthResponse>;
35
+ /**
36
+ * Fetch available tools from API
37
+ */
38
+ fetchTools(): Promise<Tool[]>;
39
+ /**
40
+ * Execute a tool with given arguments
41
+ */
42
+ callTool(name: string, args: Record<string, unknown>): Promise<ToolCallResult>;
43
+ /**
44
+ * Handle API errors with meaningful messages
45
+ */
46
+ private handleError;
47
+ }
48
+ //# sourceMappingURL=api-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,MAAM,EAAS,MAAM,aAAa,CAAC;AAI5C,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ,CAAC;QACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACpC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;CACH;AAGD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAGD,MAAM,WAAW,cAAc;IAE7B,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAGD,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,MAAM;IAoB1B;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,cAAc,CAAC;IAU5C;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;IAUnC;;OAEG;IACG,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC;IAapF;;OAEG;IACH,OAAO,CAAC,WAAW;CA2EpB"}
@@ -0,0 +1,133 @@
1
+ // api-client.ts - HTTP client for M00n Report API
2
+ //
3
+ // Handles:
4
+ // - Authentication via X-MCP-Key header
5
+ // - SSL verification bypass for self-hosted
6
+ // - Timeout and error handling
7
+ // - Tool discovery and execution
8
+ import axios from 'axios';
9
+ import https from 'https';
10
+ import { debug } from './config.js';
11
+ import { VERSION } from './version.js';
12
+ /**
13
+ * M00n Report API Client
14
+ */
15
+ export class M00nApiClient {
16
+ client;
17
+ config;
18
+ constructor(config) {
19
+ this.config = config;
20
+ // Create axios instance with configuration
21
+ this.client = axios.create({
22
+ baseURL: config.apiUrl,
23
+ timeout: config.timeoutMs,
24
+ headers: {
25
+ 'X-MCP-Key': config.apiKey,
26
+ 'Content-Type': 'application/json',
27
+ 'User-Agent': `m00n-mcp-server/${VERSION}`,
28
+ },
29
+ // HTTPS agent with connection reuse and optional SSL verification skip
30
+ httpsAgent: new https.Agent({
31
+ rejectUnauthorized: !config.insecureSsl,
32
+ keepAlive: true,
33
+ }),
34
+ });
35
+ }
36
+ /**
37
+ * Check API health and get version info
38
+ */
39
+ async checkHealth() {
40
+ try {
41
+ debug(`Checking health at ${this.config.apiUrl}/api/health`, this.config);
42
+ const response = await this.client.get('/api/health');
43
+ return response.data;
44
+ }
45
+ catch (error) {
46
+ throw this.handleError(error, 'health check');
47
+ }
48
+ }
49
+ /**
50
+ * Fetch available tools from API
51
+ */
52
+ async fetchTools() {
53
+ try {
54
+ debug(`Fetching tools from ${this.config.apiUrl}/api/mcp/tools`, this.config);
55
+ const response = await this.client.get('/api/mcp/tools');
56
+ return response.data.tools;
57
+ }
58
+ catch (error) {
59
+ throw this.handleError(error, 'fetch tools');
60
+ }
61
+ }
62
+ /**
63
+ * Execute a tool with given arguments
64
+ */
65
+ async callTool(name, args) {
66
+ try {
67
+ debug(`Calling tool: ${name} with args: ${JSON.stringify(args)}`, this.config);
68
+ const response = await this.client.post('/api/mcp/tools/call', {
69
+ name,
70
+ arguments: args,
71
+ });
72
+ return response.data;
73
+ }
74
+ catch (error) {
75
+ throw this.handleError(error, `tool call: ${name}`);
76
+ }
77
+ }
78
+ /**
79
+ * Handle API errors with meaningful messages
80
+ */
81
+ handleError(error, context) {
82
+ if (axios.isAxiosError(error)) {
83
+ const axiosError = error;
84
+ // Network error (no response)
85
+ if (!axiosError.response) {
86
+ if (axiosError.code === 'ECONNREFUSED') {
87
+ return new Error(`Cannot connect to M00n Report API at ${this.config.apiUrl}. ` +
88
+ 'Please verify the URL is correct and the server is running.');
89
+ }
90
+ if (axiosError.code === 'ENOTFOUND') {
91
+ return new Error(`Cannot resolve M00n Report API host: ${this.config.apiUrl}. ` +
92
+ 'Please check the URL is correct.');
93
+ }
94
+ if (axiosError.code === 'ETIMEDOUT' || axiosError.code === 'ECONNABORTED') {
95
+ return new Error(`Request to M00n Report API timed out. ` +
96
+ 'Try increasing M00N_TIMEOUT_MS or check network connectivity.');
97
+ }
98
+ if (axiosError.message.includes('self-signed') || axiosError.message.includes('certificate')) {
99
+ return new Error(`SSL certificate error connecting to ${this.config.apiUrl}. ` +
100
+ 'For self-signed certificates, set M00N_INSECURE_SSL=true');
101
+ }
102
+ return new Error(`Network error during ${context}: ${axiosError.message}`);
103
+ }
104
+ // HTTP error response
105
+ const status = axiosError.response.status;
106
+ const data = axiosError.response.data;
107
+ switch (status) {
108
+ case 401:
109
+ return new Error('Authentication failed: Invalid or expired MCP key. ' +
110
+ 'Please check M00N_API_KEY is a valid MCP key from Settings → MCP Keys.');
111
+ case 403:
112
+ return new Error(`Access denied: ${data?.message || 'You do not have permission for this operation.'}`);
113
+ case 404:
114
+ return new Error(`API endpoint not found: ${context}. ` +
115
+ 'This may indicate an API version mismatch or incorrect URL.');
116
+ case 429:
117
+ return new Error('Rate limit exceeded. Please wait before making more requests.');
118
+ case 500:
119
+ case 502:
120
+ case 503:
121
+ return new Error(`M00n Report API server error (${status}): ${data?.message || 'Please try again later.'}`);
122
+ default:
123
+ return new Error(`API error (${status}) during ${context}: ${data?.message || axiosError.message}`);
124
+ }
125
+ }
126
+ // Non-axios error
127
+ if (error instanceof Error) {
128
+ return error;
129
+ }
130
+ return new Error(`Unknown error during ${context}: ${String(error)}`);
131
+ }
132
+ }
133
+ //# sourceMappingURL=api-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,55 @@
1
+ // cli.ts - CLI entry point for M00n MCP Server
2
+ //
3
+ // This is the main entry point invoked by bin/m00n-mcp.js
4
+ // It loads configuration from environment variables and starts the server.
5
+ import { loadConfig, log } from './config.js';
6
+ import { createServer } from './server.js';
7
+ /**
8
+ * Main entry point
9
+ */
10
+ async function main() {
11
+ try {
12
+ // Load and validate configuration
13
+ const config = loadConfig();
14
+ // Start the server
15
+ await createServer(config);
16
+ }
17
+ catch (error) {
18
+ const message = error instanceof Error ? error.message : String(error);
19
+ log(`Fatal error: ${message}`);
20
+ // Print helpful message for common issues
21
+ if (message.includes('M00N_API_URL') || message.includes('M00N_API_KEY')) {
22
+ console.error('\n' +
23
+ '╔═══════════════════════════════════════════════════════════════╗\n' +
24
+ '║ CONFIGURATION REQUIRED ║\n' +
25
+ '╠═══════════════════════════════════════════════════════════════╣\n' +
26
+ '║ Set the following environment variables: ║\n' +
27
+ '║ ║\n' +
28
+ '║ M00N_API_URL - Your M00n Report URL ║\n' +
29
+ '║ (e.g., https://app.m00nreport.com) ║\n' +
30
+ '║ ║\n' +
31
+ '║ M00N_API_KEY - MCP key from Settings → MCP Keys ║\n' +
32
+ '║ (starts with m00n_mcp_) ║\n' +
33
+ '║ ║\n' +
34
+ '║ For Cursor, add to your MCP config: ║\n' +
35
+ '║ ║\n' +
36
+ '║ { ║\n' +
37
+ '║ "mcpServers": { ║\n' +
38
+ '║ "m00n": { ║\n' +
39
+ '║ "command": "npx", ║\n' +
40
+ '║ "args": ["@m00nsolutions/mcp-server"], ║\n' +
41
+ '║ "env": { ║\n' +
42
+ '║ "M00N_API_URL": "https://app.m00nreport.com", ║\n' +
43
+ '║ "M00N_API_KEY": "m00n_mcp_..." ║\n' +
44
+ '║ } ║\n' +
45
+ '║ } ║\n' +
46
+ '║ } ║\n' +
47
+ '║ } ║\n' +
48
+ '╚═══════════════════════════════════════════════════════════════╝\n');
49
+ }
50
+ process.exit(1);
51
+ }
52
+ }
53
+ // Run the main function
54
+ main();
55
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,EAAE;AACF,0DAA0D;AAC1D,2EAA2E;AAE3E,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C;;GAEG;AACH,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QACH,kCAAkC;QAClC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAE5B,mBAAmB;QACnB,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,GAAG,CAAC,gBAAgB,OAAO,EAAE,CAAC,CAAC;QAE/B,0CAA0C;QAC1C,IAAI,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;YACzE,OAAO,CAAC,KAAK,CAAC,IAAI;gBAChB,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE;gBACrE,qEAAqE,CACtE,CAAC;QACJ,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,wBAAwB;AACxB,IAAI,EAAE,CAAC"}
@@ -0,0 +1,36 @@
1
+ import { z } from 'zod';
2
+ declare const ConfigSchema: z.ZodObject<{
3
+ apiUrl: z.ZodEffects<z.ZodString, string, string>;
4
+ apiKey: z.ZodEffects<z.ZodString, string, string>;
5
+ insecureSsl: z.ZodDefault<z.ZodBoolean>;
6
+ debug: z.ZodDefault<z.ZodBoolean>;
7
+ timeoutMs: z.ZodDefault<z.ZodNumber>;
8
+ }, "strip", z.ZodTypeAny, {
9
+ apiUrl: string;
10
+ apiKey: string;
11
+ insecureSsl: boolean;
12
+ debug: boolean;
13
+ timeoutMs: number;
14
+ }, {
15
+ apiUrl: string;
16
+ apiKey: string;
17
+ insecureSsl?: boolean | undefined;
18
+ debug?: boolean | undefined;
19
+ timeoutMs?: number | undefined;
20
+ }>;
21
+ export type Config = z.infer<typeof ConfigSchema>;
22
+ /**
23
+ * Parse and validate configuration from environment variables
24
+ * @throws Error if required variables are missing or invalid
25
+ */
26
+ export declare function loadConfig(): Config;
27
+ /**
28
+ * Log message to stderr (MCP uses stdout for protocol, stderr for logs)
29
+ */
30
+ export declare function log(message: string, config?: Config): void;
31
+ /**
32
+ * Log debug message (only when M00N_DEBUG=true)
33
+ */
34
+ export declare function debug(message: string, config: Config): void;
35
+ export {};
36
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;EAehB,CAAC;AAEH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAElD;;;GAGG;AACH,wBAAgB,UAAU,IAAI,MAAM,CA2BnC;AAmBD;;GAEG;AACH,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAI1D;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAI3D"}
package/dist/config.js ADDED
@@ -0,0 +1,85 @@
1
+ // config.ts - Environment variable validation and configuration
2
+ //
3
+ // Required:
4
+ // M00N_API_URL - M00n Report API URL (e.g., https://app.m00nreport.com)
5
+ // M00N_API_KEY - MCP API key from Settings → MCP Keys
6
+ //
7
+ // Optional:
8
+ // M00N_INSECURE_SSL - Skip SSL verification (for self-signed certs)
9
+ // M00N_DEBUG - Enable verbose logging
10
+ // M00N_TIMEOUT_MS - Request timeout in ms (default: 30000)
11
+ import { z } from 'zod';
12
+ // Configuration schema with validation
13
+ const ConfigSchema = z.object({
14
+ apiUrl: z
15
+ .string()
16
+ .url('M00N_API_URL must be a valid URL')
17
+ .transform((url) => url.replace(/\/+$/, '')), // Remove trailing slashes
18
+ apiKey: z
19
+ .string()
20
+ .min(1, 'M00N_API_KEY is required')
21
+ .refine((key) => key.startsWith('m00n_mcp_'), 'M00N_API_KEY should start with "m00n_mcp_" - make sure you\'re using an MCP key, not a project API key'),
22
+ insecureSsl: z.boolean().default(false),
23
+ debug: z.boolean().default(false),
24
+ timeoutMs: z.number().int().positive().default(30000),
25
+ });
26
+ /**
27
+ * Parse and validate configuration from environment variables
28
+ * @throws Error if required variables are missing or invalid
29
+ */
30
+ export function loadConfig() {
31
+ const rawConfig = {
32
+ apiUrl: process.env.M00N_API_URL,
33
+ apiKey: process.env.M00N_API_KEY,
34
+ insecureSsl: parseBooleanEnv(process.env.M00N_INSECURE_SSL),
35
+ debug: parseBooleanEnv(process.env.M00N_DEBUG),
36
+ timeoutMs: parseIntEnv(process.env.M00N_TIMEOUT_MS, 30000),
37
+ };
38
+ const result = ConfigSchema.safeParse(rawConfig);
39
+ if (!result.success) {
40
+ const errors = result.error.issues
41
+ .map((issue) => ` - ${issue.path.join('.')}: ${issue.message}`)
42
+ .join('\n');
43
+ throw new Error(`Invalid M00n MCP configuration:\n${errors}\n\n` +
44
+ 'Required environment variables:\n' +
45
+ ' M00N_API_URL - Your M00n Report API URL\n' +
46
+ ' M00N_API_KEY - MCP key from Settings → MCP Keys\n\n' +
47
+ 'Example:\n' +
48
+ ' M00N_API_URL=https://app.m00nreport.com M00N_API_KEY=m00n_mcp_xxx...');
49
+ }
50
+ return result.data;
51
+ }
52
+ /**
53
+ * Parse boolean from environment variable
54
+ */
55
+ function parseBooleanEnv(value) {
56
+ if (!value)
57
+ return false;
58
+ return ['true', '1', 'yes'].includes(value.toLowerCase());
59
+ }
60
+ /**
61
+ * Parse integer from environment variable with default
62
+ */
63
+ function parseIntEnv(value, defaultValue) {
64
+ if (!value)
65
+ return defaultValue;
66
+ const parsed = parseInt(value, 10);
67
+ return isNaN(parsed) ? defaultValue : parsed;
68
+ }
69
+ /**
70
+ * Log message to stderr (MCP uses stdout for protocol, stderr for logs)
71
+ */
72
+ export function log(message, config) {
73
+ if (config?.debug || !config) {
74
+ console.error(`[m00n-mcp] ${message}`);
75
+ }
76
+ }
77
+ /**
78
+ * Log debug message (only when M00N_DEBUG=true)
79
+ */
80
+ export function debug(message, config) {
81
+ if (config.debug) {
82
+ console.error(`[m00n-mcp:debug] ${message}`);
83
+ }
84
+ }
85
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { Config } from './config.js';
2
+ /**
3
+ * Create and start the MCP server
4
+ */
5
+ export declare function createServer(config: Config): Promise<void>;
6
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,MAAM,EAAc,MAAM,aAAa,CAAC;AAGjD;;GAEG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0HhE"}
package/dist/server.js ADDED
@@ -0,0 +1,123 @@
1
+ // server.ts - MCP Server setup and tool registration
2
+ //
3
+ // This module:
4
+ // 1. Fetches available tools from M00n Report API
5
+ // 2. Registers them with the MCP SDK
6
+ // 3. Handles tool invocations by proxying to the API
7
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
8
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
9
+ import { CallToolRequestSchema, ListToolsRequestSchema, ErrorCode, McpError, } from '@modelcontextprotocol/sdk/types.js';
10
+ import { M00nApiClient } from './api-client.js';
11
+ import { log, debug } from './config.js';
12
+ import { VERSION, PACKAGE_NAME } from './version.js';
13
+ /**
14
+ * Create and start the MCP server
15
+ */
16
+ export async function createServer(config) {
17
+ log('Starting M00n MCP Server...', config);
18
+ // Create API client
19
+ const apiClient = new M00nApiClient(config);
20
+ // Step 1: Check API health
21
+ log(`Connecting to M00n Report API at ${config.apiUrl}...`, config);
22
+ let healthInfo;
23
+ try {
24
+ healthInfo = await apiClient.checkHealth();
25
+ log(`Connected to M00n Report API (version: ${healthInfo.version || 'unknown'}, mode: ${healthInfo.mode || 'unknown'})`, config);
26
+ }
27
+ catch (error) {
28
+ const message = error instanceof Error ? error.message : String(error);
29
+ throw new Error(`Failed to connect to M00n Report API: ${message}`);
30
+ }
31
+ // Step 2: Fetch available tools
32
+ log('Fetching available tools...', config);
33
+ let tools;
34
+ try {
35
+ tools = await apiClient.fetchTools();
36
+ log(`Loaded ${tools.length} tools: ${tools.map((t) => t.name).join(', ')}`, config);
37
+ }
38
+ catch (error) {
39
+ const message = error instanceof Error ? error.message : String(error);
40
+ throw new Error(`Failed to fetch tools from API: ${message}`);
41
+ }
42
+ if (tools.length === 0) {
43
+ throw new Error('No tools available from M00n Report API. Please check your API key permissions.');
44
+ }
45
+ // Step 3: Create MCP server
46
+ const server = new Server({
47
+ name: PACKAGE_NAME,
48
+ version: VERSION,
49
+ }, {
50
+ capabilities: {
51
+ tools: {},
52
+ },
53
+ });
54
+ // Register list_tools handler
55
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
56
+ debug('Handling list_tools request', config);
57
+ return {
58
+ tools: tools.map((tool) => ({
59
+ name: tool.name,
60
+ description: tool.description,
61
+ inputSchema: tool.inputSchema,
62
+ })),
63
+ };
64
+ });
65
+ // Register call_tool handler
66
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
67
+ const { name, arguments: args } = request.params;
68
+ debug(`Handling call_tool request: ${name}`, config);
69
+ // Validate tool exists
70
+ const tool = tools.find((t) => t.name === name);
71
+ if (!tool) {
72
+ throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
73
+ }
74
+ try {
75
+ // Execute tool via API
76
+ const result = await apiClient.callTool(name, args || {});
77
+ // Return result as JSON content
78
+ return {
79
+ content: [
80
+ {
81
+ type: 'text',
82
+ text: JSON.stringify(result, null, 2),
83
+ },
84
+ ],
85
+ };
86
+ }
87
+ catch (error) {
88
+ const message = error instanceof Error ? error.message : String(error);
89
+ // Return error as content (not throwing, so Claude can see the error)
90
+ return {
91
+ content: [
92
+ {
93
+ type: 'text',
94
+ text: `Error executing ${name}: ${message}`,
95
+ },
96
+ ],
97
+ isError: true,
98
+ };
99
+ }
100
+ });
101
+ // Step 4: Connect via stdio transport
102
+ const transport = new StdioServerTransport();
103
+ // Handle server errors
104
+ server.onerror = (error) => {
105
+ log(`Server error: ${error.message}`, config);
106
+ };
107
+ // Handle process signals for graceful shutdown
108
+ process.on('SIGINT', async () => {
109
+ log('Received SIGINT, shutting down...', config);
110
+ await server.close();
111
+ process.exit(0);
112
+ });
113
+ process.on('SIGTERM', async () => {
114
+ log('Received SIGTERM, shutting down...', config);
115
+ await server.close();
116
+ process.exit(0);
117
+ });
118
+ // Connect and start serving
119
+ await server.connect(transport);
120
+ log('M00n MCP Server running (stdio transport)', config);
121
+ log('Ready to receive requests from Claude/Cursor', config);
122
+ }
123
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare const VERSION: string;
2
+ export declare const PACKAGE_NAME: string;
3
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAwCA,eAAO,MAAM,OAAO,QAAc,CAAC;AACnC,eAAO,MAAM,YAAY,QAAW,CAAC"}
@@ -0,0 +1,34 @@
1
+ // version.ts - Package version constant
2
+ //
3
+ // Reads version from package.json to ensure consistency across:
4
+ // - package.json
5
+ // - User-Agent header
6
+ // - MCP server version
7
+ import { createRequire } from 'module';
8
+ import { fileURLToPath } from 'url';
9
+ import { dirname, join } from 'path';
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = dirname(__filename);
12
+ // Read version from package.json at runtime
13
+ const require = createRequire(import.meta.url);
14
+ // Try to load from dist/../package.json (runtime) or src/../package.json (dev)
15
+ function loadPackageJson() {
16
+ try {
17
+ // When running from dist/, go up one level to find package.json
18
+ return require(join(__dirname, '..', 'package.json'));
19
+ }
20
+ catch {
21
+ // Fallback for development
22
+ try {
23
+ return require(join(__dirname, '..', '..', 'package.json'));
24
+ }
25
+ catch {
26
+ // Ultimate fallback
27
+ return { name: '@m00nsolutions/mcp-server', version: '1.0.0' };
28
+ }
29
+ }
30
+ }
31
+ const pkg = loadPackageJson();
32
+ export const VERSION = pkg.version;
33
+ export const PACKAGE_NAME = pkg.name;
34
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,EAAE;AACF,gEAAgE;AAChE,iBAAiB;AACjB,sBAAsB;AACtB,uBAAuB;AAEvB,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAErC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,4CAA4C;AAC5C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAO/C,+EAA+E;AAC/E,SAAS,eAAe;IACtB,IAAI,CAAC;QACH,gEAAgE;QAChE,OAAO,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,2BAA2B;QAC3B,IAAI,CAAC;YACH,OAAO,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC;QAC9D,CAAC;QAAC,MAAM,CAAC;YACP,oBAAoB;YACpB,OAAO,EAAE,IAAI,EAAE,2BAA2B,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;QACjE,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,GAAG,GAAG,eAAe,EAAE,CAAC;AAE9B,MAAM,CAAC,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;AACnC,MAAM,CAAC,MAAM,YAAY,GAAG,GAAG,CAAC,IAAI,CAAC"}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@m00nsolutions/mcp-server",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for M00n Report - enables AI assistants like Claude and Cursor to query test analytics",
5
+ "type": "module",
6
+ "main": "dist/cli.js",
7
+ "types": "dist/cli.d.ts",
8
+ "bin": {
9
+ "m00n-mcp": "./bin/m00n-mcp.js"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "bin",
14
+ "README.md",
15
+ "LICENSE"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "prepublishOnly": "npm run build",
20
+ "clean": "rimraf dist"
21
+ },
22
+ "keywords": [
23
+ "mcp",
24
+ "model-context-protocol",
25
+ "m00n",
26
+ "m00nreport",
27
+ "test-reporting",
28
+ "ai",
29
+ "claude",
30
+ "cursor",
31
+ "testing",
32
+ "analytics",
33
+ "playwright",
34
+ "jest"
35
+ ],
36
+ "author": "M00n Solutions",
37
+ "license": "MIT",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "https://github.com/m00nsolutions/m00nreport.git",
41
+ "directory": "packages/m00n-mcp-server"
42
+ },
43
+ "homepage": "https://m00nreport.com",
44
+ "bugs": {
45
+ "url": "https://github.com/m00nsolutions/m00nreport/issues"
46
+ },
47
+ "engines": {
48
+ "node": ">=18.0.0"
49
+ },
50
+ "dependencies": {
51
+ "@modelcontextprotocol/sdk": "^1.0.0",
52
+ "axios": "^1.6.0",
53
+ "zod": "^3.22.0"
54
+ },
55
+ "devDependencies": {
56
+ "@types/node": "^20.0.0",
57
+ "rimraf": "^5.0.0",
58
+ "typescript": "^5.3.0"
59
+ }
60
+ }