@debugg-ai/debugg-ai-mcp 1.0.30 → 1.0.31
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 +21 -108
- package/dist/handlers/testPageChangesHandler.js +5 -1
- package/dist/services/workflows.js +21 -3
- package/dist/types/index.js +1 -73
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,42 +1,14 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Debugg AI — MCP Server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
AI-powered browser testing via the [Model Context Protocol](https://modelcontextprotocol.io). Point it at any URL (or localhost) and describe what to test — an AI agent browses your app and returns pass/fail with screenshots.
|
|
4
4
|
|
|
5
5
|
<a href="https://glama.ai/mcp/servers/@debugg-ai/debugg-ai-mcp">
|
|
6
6
|
<img width="380" height="200" src="https://glama.ai/mcp/servers/@debugg-ai/debugg-ai-mcp/badge" alt="Debugg AI MCP server" />
|
|
7
7
|
</a>
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
## What it does
|
|
12
|
-
|
|
13
|
-
- **Run browser tests with natural language** — describe what to test, the AI agent clicks through your app and returns screenshots + results
|
|
14
|
-
- **Monitor live browser sessions** — capture console logs, network requests, and screenshots in real time
|
|
15
|
-
- **Manage test suites** — create, organize, and track E2E tests tied to features or commits
|
|
16
|
-
- **Seamless CI/CD** — view all results in your [Debugg.AI dashboard](https://app.debugg.ai)
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## Demo
|
|
21
|
-
|
|
22
|
-
### Prompt: "Test the ability to create an account and login"
|
|
23
|
-
|
|
24
|
-

|
|
25
|
-
|
|
26
|
-
**Result:**
|
|
27
|
-
- Duration: 86.80 seconds
|
|
28
|
-
- Status: Success — signed up and logged in with `alice.wonderland1234@example.com`
|
|
9
|
+
## Setup
|
|
29
10
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
## Quick Setup
|
|
35
|
-
|
|
36
|
-
### 1. Get your API key
|
|
37
|
-
Create a free account at [debugg.ai](https://debugg.ai) and generate your API key.
|
|
38
|
-
|
|
39
|
-
### 2. Add to Claude Desktop
|
|
11
|
+
Get an API key at [debugg.ai](https://debugg.ai), then add to your MCP client config:
|
|
40
12
|
|
|
41
13
|
```json
|
|
42
14
|
{
|
|
@@ -52,101 +24,42 @@ Create a free account at [debugg.ai](https://debugg.ai) and generate your API ke
|
|
|
52
24
|
}
|
|
53
25
|
```
|
|
54
26
|
|
|
55
|
-
|
|
27
|
+
Or with Docker:
|
|
28
|
+
|
|
56
29
|
```bash
|
|
57
|
-
docker run -i --rm --init
|
|
58
|
-
-e DEBUGGAI_API_KEY=your_api_key \
|
|
59
|
-
quinnosha/debugg-ai-mcp
|
|
30
|
+
docker run -i --rm --init -e DEBUGGAI_API_KEY=your_api_key quinnosha/debugg-ai-mcp
|
|
60
31
|
```
|
|
61
32
|
|
|
62
|
-
|
|
33
|
+
## `check_app_in_browser`
|
|
63
34
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
### E2E Testing
|
|
67
|
-
| Tool | Description |
|
|
68
|
-
|------|-------------|
|
|
69
|
-
| `check_app_in_browser` | Run a browser test with a natural language description. Returns screenshots and pass/fail result. |
|
|
70
|
-
| `create_test_suite` | Generate a suite of browser tests for a feature or workflow |
|
|
71
|
-
| `create_commit_suite` | Auto-generate tests from recent git commits |
|
|
72
|
-
| `get_test_status` | Check progress and results of a running or completed test suite |
|
|
73
|
-
|
|
74
|
-
### Test Management
|
|
75
|
-
| Tool | Description |
|
|
76
|
-
|------|-------------|
|
|
77
|
-
| `list_tests` | List all E2E tests with filtering and pagination |
|
|
78
|
-
| `list_test_suites` | List all test suites |
|
|
79
|
-
| `list_commit_suites` | List all commit-based test suites |
|
|
80
|
-
|
|
81
|
-
### Live Session Monitoring
|
|
82
|
-
| Tool | Description |
|
|
83
|
-
|------|-------------|
|
|
84
|
-
| `start_live_session` | Launch a remote browser session with real-time monitoring |
|
|
85
|
-
| `stop_live_session` | Stop an active session and save captured data |
|
|
86
|
-
| `get_live_session_status` | Check session status, current URL, and uptime |
|
|
87
|
-
| `get_live_session_logs` | Retrieve console logs, network requests, and JS errors |
|
|
88
|
-
| `get_live_session_screenshot` | Capture a screenshot of what the browser currently shows |
|
|
89
|
-
|
|
90
|
-
### Quick Operations
|
|
91
|
-
| Tool | Description |
|
|
92
|
-
|------|-------------|
|
|
93
|
-
| `quick_screenshot` | Capture a screenshot of any URL — no session setup required |
|
|
35
|
+
Runs an AI browser agent against your app. The agent navigates, interacts, and reports back with screenshots.
|
|
94
36
|
|
|
95
|
-
|
|
37
|
+
| Parameter | Type | Description |
|
|
38
|
+
|-----------|------|-------------|
|
|
39
|
+
| `description` | string **required** | What to test (natural language) |
|
|
40
|
+
| `url` | string | Target URL — required if `localPort` not set |
|
|
41
|
+
| `localPort` | number | Local dev server port — tunnel created automatically |
|
|
42
|
+
| `environmentId` | string | UUID of a specific environment |
|
|
43
|
+
| `credentialId` | string | UUID of a specific credential |
|
|
44
|
+
| `credentialRole` | string | Pick a credential by role (e.g. `admin`, `guest`) |
|
|
45
|
+
| `username` | string | Username for login |
|
|
46
|
+
| `password` | string | Password for login |
|
|
96
47
|
|
|
97
48
|
## Configuration
|
|
98
49
|
|
|
99
50
|
```bash
|
|
100
|
-
# Required
|
|
101
51
|
DEBUGGAI_API_KEY=your_api_key
|
|
102
|
-
|
|
103
|
-
# Optional — provide defaults so you don't have to pass them every time
|
|
104
|
-
DEBUGGAI_LOCAL_PORT=3000 # Your app's local port
|
|
105
|
-
DEBUGGAI_LOCAL_REPO_NAME=your-org/repo # GitHub repo name
|
|
106
|
-
DEBUGGAI_LOCAL_REPO_PATH=/path/to/project # Absolute path to project root
|
|
107
|
-
DEBUGGAI_LOCAL_BRANCH_NAME=main # Current branch
|
|
108
|
-
|
|
109
|
-
# Override API endpoint (defaults to https://api.debugg.ai)
|
|
110
|
-
DEBUGGAI_API_URL=https://api.debugg.ai
|
|
111
52
|
```
|
|
112
53
|
|
|
113
|
-
---
|
|
114
|
-
|
|
115
|
-
## Usage examples
|
|
116
|
-
|
|
117
|
-
```
|
|
118
|
-
"Test the user login flow on my app running on port 3000"
|
|
119
|
-
|
|
120
|
-
"Check that the checkout process works end to end"
|
|
121
|
-
|
|
122
|
-
"Take a screenshot of localhost:3000 and tell me if anything looks broken"
|
|
123
|
-
|
|
124
|
-
"Create a test suite for the user authentication feature"
|
|
125
|
-
|
|
126
|
-
"Generate browser tests for my last 3 commits"
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
---
|
|
130
|
-
|
|
131
54
|
## Local Development
|
|
132
55
|
|
|
133
56
|
```bash
|
|
134
|
-
npm install
|
|
135
|
-
npm test
|
|
136
|
-
npm run build
|
|
137
|
-
|
|
138
|
-
# Test with MCP inspector
|
|
139
|
-
npx @modelcontextprotocol/inspector --config test-config.json --server debugg-ai
|
|
57
|
+
npm install && npm test && npm run build
|
|
140
58
|
```
|
|
141
59
|
|
|
142
|
-
---
|
|
143
|
-
|
|
144
60
|
## Links
|
|
145
61
|
|
|
146
|
-
|
|
147
|
-
- **Docs**: [debugg.ai/docs](https://debugg.ai/docs)
|
|
148
|
-
- **Issues**: [GitHub Issues](https://github.com/debugg-ai/debugg-ai-mcp/issues)
|
|
149
|
-
- **Discord**: [debugg.ai/discord](https://debugg.ai/discord)
|
|
62
|
+
[Dashboard](https://app.debugg.ai) · [Docs](https://debugg.ai/docs) · [Issues](https://github.com/debugg-ai/debugg-ai-mcp/issues) · [Discord](https://debugg.ai/discord)
|
|
150
63
|
|
|
151
64
|
---
|
|
152
65
|
|
|
@@ -20,6 +20,9 @@ export async function testPageChangesHandler(input, context, progressCallback) {
|
|
|
20
20
|
const originalUrl = resolveTargetUrl(input);
|
|
21
21
|
let ctx = buildContext(originalUrl);
|
|
22
22
|
let ngrokKeyId;
|
|
23
|
+
const abortController = new AbortController();
|
|
24
|
+
const onStdinClose = () => abortController.abort();
|
|
25
|
+
process.stdin.once('close', onStdinClose);
|
|
23
26
|
try {
|
|
24
27
|
// --- Find workflow template ---
|
|
25
28
|
if (progressCallback) {
|
|
@@ -93,7 +96,7 @@ export async function testPageChangesHandler(input, context, progressCallback) {
|
|
|
93
96
|
: exec.status;
|
|
94
97
|
await progressCallback({ progress, total: 10, message });
|
|
95
98
|
}
|
|
96
|
-
});
|
|
99
|
+
}, abortController.signal);
|
|
97
100
|
const duration = Date.now() - startTime;
|
|
98
101
|
// --- Format result ---
|
|
99
102
|
const outcome = finalExecution.state?.outcome ?? finalExecution.status;
|
|
@@ -152,6 +155,7 @@ export async function testPageChangesHandler(input, context, progressCallback) {
|
|
|
152
155
|
throw handleExternalServiceError(error, 'DebuggAI', 'test execution');
|
|
153
156
|
}
|
|
154
157
|
finally {
|
|
158
|
+
process.stdin.removeListener('close', onStdinClose);
|
|
155
159
|
if (ngrokKeyId) {
|
|
156
160
|
client.revokeNgrokKey(ngrokKeyId).catch(err => logger.warn(`Failed to revoke ngrok key ${ngrokKeyId}: ${err}`));
|
|
157
161
|
}
|
|
@@ -10,8 +10,15 @@ export const createWorkflowsService = (tx) => {
|
|
|
10
10
|
async findEvaluationTemplate() {
|
|
11
11
|
const response = await tx.get('api/v1/workflows/', { isTemplate: true });
|
|
12
12
|
const templates = response?.results ?? [];
|
|
13
|
+
if (templates.length === 0)
|
|
14
|
+
return null;
|
|
13
15
|
const evalTemplate = templates.find(t => t.name.toLowerCase().includes('app evaluation'));
|
|
14
|
-
|
|
16
|
+
if (!evalTemplate) {
|
|
17
|
+
throw new Error(`No "App Evaluation" workflow template found. ` +
|
|
18
|
+
`Available templates: ${templates.map(t => `"${t.name}"`).join(', ')}. ` +
|
|
19
|
+
`Ensure the backend has a template with "App Evaluation" in its name.`);
|
|
20
|
+
}
|
|
21
|
+
return evalTemplate;
|
|
15
22
|
},
|
|
16
23
|
async executeWorkflow(workflowUuid, contextData, env) {
|
|
17
24
|
const body = { contextData };
|
|
@@ -38,9 +45,12 @@ export const createWorkflowsService = (tx) => {
|
|
|
38
45
|
}
|
|
39
46
|
return response;
|
|
40
47
|
},
|
|
41
|
-
async pollExecution(executionUuid, onUpdate) {
|
|
48
|
+
async pollExecution(executionUuid, onUpdate, signal) {
|
|
42
49
|
const deadline = Date.now() + EXECUTION_TIMEOUT_MS;
|
|
43
50
|
while (Date.now() < deadline) {
|
|
51
|
+
if (signal?.aborted) {
|
|
52
|
+
throw new Error(`Polling cancelled for execution ${executionUuid}`);
|
|
53
|
+
}
|
|
44
54
|
const execution = await service.getExecution(executionUuid);
|
|
45
55
|
if (onUpdate) {
|
|
46
56
|
await onUpdate(execution).catch(() => { });
|
|
@@ -48,7 +58,15 @@ export const createWorkflowsService = (tx) => {
|
|
|
48
58
|
if (TERMINAL_STATUSES.has(execution.status)) {
|
|
49
59
|
return execution;
|
|
50
60
|
}
|
|
51
|
-
await new Promise(
|
|
61
|
+
await new Promise((resolve, reject) => {
|
|
62
|
+
const timer = setTimeout(resolve, POLL_INTERVAL_MS);
|
|
63
|
+
if (signal) {
|
|
64
|
+
signal.addEventListener('abort', () => {
|
|
65
|
+
clearTimeout(timer);
|
|
66
|
+
reject(new Error(`Polling cancelled for execution ${executionUuid}`));
|
|
67
|
+
}, { once: true });
|
|
68
|
+
}
|
|
69
|
+
});
|
|
52
70
|
}
|
|
53
71
|
throw new Error(`Execution ${executionUuid} timed out after ${EXECUTION_TIMEOUT_MS / 1000}s`);
|
|
54
72
|
}
|
package/dist/types/index.js
CHANGED
|
@@ -15,79 +15,7 @@ export const TestPageChangesInputSchema = z.object({
|
|
|
15
15
|
credentialRole: z.string().optional(),
|
|
16
16
|
username: z.string().optional(),
|
|
17
17
|
password: z.string().optional(),
|
|
18
|
-
});
|
|
19
|
-
/**
|
|
20
|
-
* E2E Suite Tool Schemas
|
|
21
|
-
*/
|
|
22
|
-
export const ListTestsInputSchema = z.object({
|
|
23
|
-
repoName: z.string().optional(),
|
|
24
|
-
branchName: z.string().optional(),
|
|
25
|
-
status: z.enum(['pending', 'running', 'completed', 'failed']).optional(),
|
|
26
|
-
page: z.number().int().positive().optional().default(1),
|
|
27
|
-
limit: z.number().int().min(1).max(100).optional().default(20),
|
|
28
|
-
});
|
|
29
|
-
export const ListTestSuitesInputSchema = z.object({
|
|
30
|
-
repoName: z.string().optional(),
|
|
31
|
-
branchName: z.string().optional(),
|
|
32
|
-
status: z.enum(['pending', 'running', 'completed', 'failed']).optional(),
|
|
33
|
-
page: z.number().int().positive().optional().default(1),
|
|
34
|
-
limit: z.number().int().min(1).max(100).optional().default(20),
|
|
35
|
-
});
|
|
36
|
-
export const CreateTestSuiteInputSchema = z.object({
|
|
37
|
-
description: z.string().min(1, 'Description is required'),
|
|
38
|
-
repoName: z.string().optional(),
|
|
39
|
-
branchName: z.string().optional(),
|
|
40
|
-
repoPath: z.string().optional(),
|
|
41
|
-
filePath: z.string().optional(),
|
|
42
|
-
});
|
|
43
|
-
export const CreateCommitSuiteInputSchema = z.object({
|
|
44
|
-
description: z.string().min(1, 'Description is required'),
|
|
45
|
-
repoName: z.string().optional(),
|
|
46
|
-
branchName: z.string().optional(),
|
|
47
|
-
repoPath: z.string().optional(),
|
|
48
|
-
filePath: z.string().optional(),
|
|
49
|
-
});
|
|
50
|
-
export const ListCommitSuitesInputSchema = z.object({
|
|
51
|
-
repoName: z.string().optional(),
|
|
52
|
-
branchName: z.string().optional(),
|
|
53
|
-
status: z.enum(['pending', 'running', 'completed', 'failed']).optional(),
|
|
54
|
-
page: z.number().int().positive().optional().default(1),
|
|
55
|
-
limit: z.number().int().min(1).max(100).optional().default(20),
|
|
56
|
-
});
|
|
57
|
-
export const GetTestStatusInputSchema = z.object({
|
|
58
|
-
suiteUuid: z.string().uuid('Invalid suite UUID'),
|
|
59
|
-
suiteType: z.enum(['test', 'commit']).optional().default('test'),
|
|
60
|
-
});
|
|
61
|
-
/**
|
|
62
|
-
* Live Session Tool Schemas
|
|
63
|
-
*/
|
|
64
|
-
export const StartLiveSessionInputSchema = z.object({
|
|
65
|
-
url: z.string().min(1, 'URL is required'),
|
|
66
|
-
localPort: z.number().int().min(1).max(65535).optional(),
|
|
67
|
-
sessionName: z.string().max(100).optional(),
|
|
68
|
-
monitorConsole: z.boolean().optional().default(true),
|
|
69
|
-
monitorNetwork: z.boolean().optional().default(true),
|
|
70
|
-
takeScreenshots: z.boolean().optional().default(false),
|
|
71
|
-
screenshotInterval: z.number().int().min(1).max(300).optional().default(10),
|
|
72
|
-
});
|
|
73
|
-
export const StopLiveSessionInputSchema = z.object({
|
|
74
|
-
sessionId: z.string().optional(),
|
|
75
|
-
});
|
|
76
|
-
export const GetLiveSessionStatusInputSchema = z.object({
|
|
77
|
-
sessionId: z.string().optional(),
|
|
78
|
-
});
|
|
79
|
-
export const GetLiveSessionLogsInputSchema = z.object({
|
|
80
|
-
sessionId: z.string().optional(),
|
|
81
|
-
logType: z.enum(['console', 'network', 'errors', 'all']).optional().default('all'),
|
|
82
|
-
since: z.string().datetime().optional(),
|
|
83
|
-
limit: z.number().int().min(1).max(1000).optional().default(100),
|
|
84
|
-
});
|
|
85
|
-
export const GetLiveSessionScreenshotInputSchema = z.object({
|
|
86
|
-
sessionId: z.string().optional(),
|
|
87
|
-
fullPage: z.boolean().optional().default(false),
|
|
88
|
-
quality: z.number().int().min(1).max(100).optional().default(90),
|
|
89
|
-
format: z.enum(['png', 'jpeg']).optional().default('png'),
|
|
90
|
-
});
|
|
18
|
+
}).refine((data) => data.url !== undefined || data.localPort !== undefined, { message: 'Provide a target via "url" (e.g. "https://example.com") or "localPort" for a local dev server' });
|
|
91
19
|
/**
|
|
92
20
|
* Error types
|
|
93
21
|
*/
|