@debugg-ai/debugg-ai-mcp 2.9.3 → 3.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/CHANGELOG.md +29 -0
- package/README.md +61 -22
- package/dist/handlers/environmentHandler.js +27 -0
- package/dist/handlers/executionsHandler.js +9 -0
- package/dist/handlers/index.js +7 -2
- package/dist/handlers/projectHandler.js +14 -0
- package/dist/handlers/testCaseHandler.js +22 -0
- package/dist/handlers/testSuiteHandler.js +34 -0
- package/dist/handlers/triggerCrawlHandler.js +1 -2
- package/dist/services/workflows.js +23 -10
- package/dist/tools/environment.js +54 -0
- package/dist/tools/executions.js +34 -0
- package/dist/tools/index.js +21 -46
- package/dist/tools/project.js +41 -0
- package/dist/tools/testCase.js +40 -0
- package/dist/tools/testSuite.js +50 -0
- package/dist/tools/triggerCrawl.js +0 -4
- package/dist/types/index.js +55 -1
- package/dist/utils/confirmDestructive.js +48 -0
- package/package.json +5 -1
- package/dist/handlers/deleteProjectHandler.js +0 -39
- package/dist/handlers/updateProjectHandler.js +0 -45
- package/dist/tools/createEnvironment.js +0 -46
- package/dist/tools/createProject.js +0 -27
- package/dist/tools/deleteEnvironment.js +0 -23
- package/dist/tools/deleteProject.js +0 -22
- package/dist/tools/searchEnvironments.js +0 -35
- package/dist/tools/searchExecutions.js +0 -31
- package/dist/tools/searchProjects.js +0 -30
- package/dist/tools/testSuiteTools.js +0 -183
- package/dist/tools/updateEnvironment.js +0 -74
- package/dist/tools/updateProject.js +0 -24
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,35 @@ All notable changes to the DebuggAI MCP project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [3.0.0]
|
|
9
|
+
|
|
10
|
+
### Changed — Tool surface consolidated to 8 action-based tools (BREAKING)
|
|
11
|
+
|
|
12
|
+
The 20 per-verb tools were consolidated into **8** tools: three browser tools
|
|
13
|
+
(`check_app_in_browser`, `probe_page`, `trigger_crawl`) plus one action-based
|
|
14
|
+
tool per entity — `project`, `environment`, `test_suite`, `test_case`,
|
|
15
|
+
`executions` — each taking an `action` discriminator. Clients pick up the new
|
|
16
|
+
surface on MCP restart.
|
|
17
|
+
|
|
18
|
+
Migration (old tool → new tool + action):
|
|
19
|
+
|
|
20
|
+
- `search_projects` → `project {action:"get"|"list"}`
|
|
21
|
+
- `create_project` → `project {action:"create"}`
|
|
22
|
+
- `search_environments` → `environment {action:"get"|"list"}`
|
|
23
|
+
- `create_environment` / `update_environment` / `delete_environment` → `environment {action:"create"|"update"|"delete"}`
|
|
24
|
+
- `create_test_suite` / `search_test_suites` / `run_test_suite` / `get_test_suite_results` / `delete_test_suite` → `test_suite {action:"create"|"list"|"run"|"results"|"delete"}`
|
|
25
|
+
- `create_test_case` / `update_test_case` / `delete_test_case` → `test_case {action:"create"|"update"|"delete"}`
|
|
26
|
+
- `search_executions` → `executions {action:"get"|"list"}`
|
|
27
|
+
|
|
28
|
+
### Removed
|
|
29
|
+
|
|
30
|
+
- `update_project` and `delete_project` — rename/delete a project from the DebuggAI web app (both were effectively unused).
|
|
31
|
+
- `trigger_crawl`'s `headless` parameter — the MCP now always runs headless (no opt-out).
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
- Destructive `delete` actions require confirmation: an elicitation prompt when the client supports it, otherwise a required `confirm: true` argument.
|
|
36
|
+
|
|
8
37
|
## [Unreleased]
|
|
9
38
|
|
|
10
39
|
### Added — E2E test suite management (8 new MCP tools)
|
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ docker run -i --rm --init -e DEBUGGAI_API_KEY=your_api_key quinnosha/debugg-ai-m
|
|
|
34
34
|
|
|
35
35
|
## Tools
|
|
36
36
|
|
|
37
|
-
The server exposes **
|
|
37
|
+
The server exposes **8** tools: three **Browser** tools plus one **action-based** tool per managed entity. The headline tools are `check_app_in_browser` (full AI agent) and `probe_page` (lightweight no-LLM page probe). The rest — `project`, `environment`, `test_suite`, `test_case`, `executions` — each take an `action` discriminator (e.g. `{"action":"list"}`) that selects the operation. Destructive `delete` actions require confirmation (an elicitation prompt where supported, otherwise `confirm: true`).
|
|
38
38
|
|
|
39
39
|
### Browser
|
|
40
40
|
|
|
@@ -69,7 +69,7 @@ Every successful run returns a `browserSession` block alongside the screenshot
|
|
|
69
69
|
}
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
-
URLs are short-lived presigned S3 — refetch the parent execution via `
|
|
72
|
+
URLs are short-lived presigned S3 — refetch the parent execution via `executions {action:"get", uuid}` to renew. `harStatus` / `consoleLogStatus` disambiguate `'downloaded'` (URL fetchable), `'not_available'` (page emitted nothing), `'failed'` (capture broke). On a fresh run the URLs are commonly `null` because capture uploads async after the agent finishes — poll `executions {action:"get", uuid: executionId}` until status reaches `'downloaded'`. Authorization / Cookie / `token`/`secret`/`api_key` headers are scrubbed server-side before the artifacts are persisted.
|
|
73
73
|
|
|
74
74
|
#### `trigger_crawl`
|
|
75
75
|
|
|
@@ -95,33 +95,54 @@ The whole batch shares a single backend execution + browser session + tunnel —
|
|
|
95
95
|
|
|
96
96
|
Performance budget: <10s for 1 URL, <25s for 20. Localhost dead-port returns `LocalServerUnreachable` in <2s without burning a workflow execution.
|
|
97
97
|
|
|
98
|
-
###
|
|
98
|
+
### `project`
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
| Action | Params | Result |
|
|
101
|
+
|--------|--------|--------|
|
|
102
|
+
| `get` | `{uuid}` | Curated project detail |
|
|
103
|
+
| `list` | `{q?, page?, pageSize?}` | Paginated summaries |
|
|
104
|
+
| `create` | `{name, platform, (teamUuid\|teamName), (repoUuid\|repoName)}` | Created project |
|
|
101
105
|
|
|
102
|
-
|
|
103
|
-
|------|-----------|-------------|
|
|
104
|
-
| `search_projects` | `{uuid}` → curated project detail | `{q?, page?, pageSize?}` → paginated summaries |
|
|
105
|
-
| `search_environments` | `{uuid, projectUuid}` → env with credentials inlined | `{projectUuid?, q?, page?, pageSize?}` → paginated envs, each with credentials array |
|
|
106
|
-
| `search_executions` | `{uuid}` → full detail with `nodeExecutions` + state | `{status?, projectUuid?, page?, pageSize?}` → paginated summaries |
|
|
106
|
+
Team and repo resolve by **either** uuid **or** name (case-insensitive exact match; `NotFound` if none, `AmbiguousMatch` if multiple). There is **no** `update`/`delete` — rename or delete a project from the DebuggAI web app.
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
### `environment`
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
| Action | Params | Result |
|
|
111
|
+
|--------|--------|--------|
|
|
112
|
+
| `get` | `{uuid, projectUuid?}` | Env with credentials inlined (passwords never returned) |
|
|
113
|
+
| `list` | `{projectUuid?, q?, page?, pageSize?}` | Paginated envs, each with a credentials array |
|
|
114
|
+
| `create` | `{name, url, description?, projectUuid?, credentials?}` | Created env (optionally seeds credentials) |
|
|
115
|
+
| `update` | `{uuid, name?, url?, description?, addCredentials?, updateCredentials?, removeCredentialIds?}` | Patched env; credential ops run **remove → update → add** |
|
|
116
|
+
| `delete` | `{uuid, projectUuid?, confirm?}` | Deletes env (cascades credentials) — **requires confirmation** |
|
|
111
117
|
|
|
112
|
-
|
|
113
|
-
|------|---------|
|
|
114
|
-
| `create_project` | Requires `name` + `platform`. Team and repo resolve by **either** uuid **or** name: pass `teamUuid` OR `teamName`, and `repoUuid` OR `repoName`. Name resolution is case-insensitive exact match; `NotFound` if none, `AmbiguousMatch` with candidates if multiple. |
|
|
115
|
-
| `update_project` | PATCH `name`, `description`. |
|
|
116
|
-
| `delete_project` | Destructive — cascades environments, credentials, and execution history. |
|
|
118
|
+
`projectUuid` auto-resolves from the git repo when omitted. Per-cred failures surface in `credentialWarnings[]` without blocking the env op.
|
|
117
119
|
|
|
118
|
-
###
|
|
120
|
+
### `test_suite`
|
|
119
121
|
|
|
120
|
-
|
|
|
121
|
-
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
122
|
+
| Action | Params | Result |
|
|
123
|
+
|--------|--------|--------|
|
|
124
|
+
| `list` | `{projectUuid\|projectName, search?, page?, pageSize?}` | Paginated suites with status + pass rate |
|
|
125
|
+
| `create` | `{name, description, projectUuid\|projectName}` | Created suite |
|
|
126
|
+
| `run` | `{suiteUuid\|(suiteName+project), targetUrl?}` | Triggers all tests async |
|
|
127
|
+
| `results` | `{suiteUuid\|(suiteName+project)}` | Suite + per-test outcomes |
|
|
128
|
+
| `delete` | `{suiteUuid\|(suiteName+project), confirm?}` | Soft-delete — **requires confirmation** |
|
|
129
|
+
|
|
130
|
+
### `test_case`
|
|
131
|
+
|
|
132
|
+
| Action | Params | Result |
|
|
133
|
+
|--------|--------|--------|
|
|
134
|
+
| `create` | `{name, description, agentTaskDescription, suiteUuid\|(suiteName+project), relativeUrl?, maxSteps?}` | Created test case (not auto-run) |
|
|
135
|
+
| `update` | `{testUuid, name?, description?, agentTaskDescription?}` | Patched test case |
|
|
136
|
+
| `delete` | `{testUuid, confirm?}` | Soft-delete — **requires confirmation** |
|
|
137
|
+
|
|
138
|
+
### `executions`
|
|
139
|
+
|
|
140
|
+
| Action | Params | Result |
|
|
141
|
+
|--------|--------|--------|
|
|
142
|
+
| `get` | `{uuid}` | Full detail (`nodeExecutions` + state + errorInfo) + screenshot/gif artifacts |
|
|
143
|
+
| `list` | `{status?, projectUuid?, page?, pageSize?}` | Paginated summaries |
|
|
144
|
+
|
|
145
|
+
404 from the backend surfaces as `isError: true` with `{error: 'NotFound', message, uuid}`. Credentials are **always** returned without passwords.
|
|
125
146
|
|
|
126
147
|
### Pagination
|
|
127
148
|
|
|
@@ -144,6 +165,24 @@ Pass optional `page` (1-indexed, default 1) and `pageSize` (default 20, max 200;
|
|
|
144
165
|
- 404s from the backend surface as `isError: true` with `{error: 'NotFound', ...}`, never as thrown exceptions.
|
|
145
166
|
- Missing `DEBUGGAI_API_KEY` surfaces as a structured tool error on first invocation — the server still registers and lists tools normally.
|
|
146
167
|
|
|
168
|
+
## Migration to v3.0.0 (action-based tools)
|
|
169
|
+
|
|
170
|
+
v3 consolidated the 20 per-verb tools into 8 action-based tools. Old tool → new `tool {action}`:
|
|
171
|
+
|
|
172
|
+
| Removed | Replacement |
|
|
173
|
+
|---------|-------------|
|
|
174
|
+
| `search_projects` | `project {action:"get"}` / `project {action:"list"}` |
|
|
175
|
+
| `create_project` | `project {action:"create"}` |
|
|
176
|
+
| `update_project`, `delete_project` | **Dropped** — use the DebuggAI web app |
|
|
177
|
+
| `search_environments` | `environment {action:"get"}` / `{action:"list"}` |
|
|
178
|
+
| `create_environment` / `update_environment` / `delete_environment` | `environment {action:"create"\|"update"\|"delete"}` |
|
|
179
|
+
| `create_test_suite` / `search_test_suites` / `run_test_suite` / `get_test_suite_results` / `delete_test_suite` | `test_suite {action:"create"\|"list"\|"run"\|"results"\|"delete"}` |
|
|
180
|
+
| `create_test_case` / `update_test_case` / `delete_test_case` | `test_case {action:"create"\|"update"\|"delete"}` |
|
|
181
|
+
| `search_executions` | `executions {action:"get"\|"list"}` |
|
|
182
|
+
| `trigger_crawl` `headless` param | **Dropped** — always headless |
|
|
183
|
+
|
|
184
|
+
`delete` actions now require confirmation (elicitation prompt, or `confirm: true`). Clients pick up the new surface on MCP restart.
|
|
185
|
+
|
|
147
186
|
## Migration from v1.x (breaking change in v2.0.0)
|
|
148
187
|
|
|
149
188
|
v2 collapsed a 22-tool surface to 11. Old-tool → new-tool mapping:
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ensureConfirmed } from '../utils/confirmDestructive.js';
|
|
2
|
+
import { searchEnvironmentsHandler } from './searchEnvironmentsHandler.js';
|
|
3
|
+
import { createEnvironmentHandler } from './createEnvironmentHandler.js';
|
|
4
|
+
import { updateEnvironmentHandler } from './updateEnvironmentHandler.js';
|
|
5
|
+
import { deleteEnvironmentHandler } from './deleteEnvironmentHandler.js';
|
|
6
|
+
export async function environmentHandler(input, ctx) {
|
|
7
|
+
switch (input.action) {
|
|
8
|
+
case 'get':
|
|
9
|
+
return searchEnvironmentsHandler({ uuid: input.uuid, projectUuid: input.projectUuid }, ctx);
|
|
10
|
+
case 'list':
|
|
11
|
+
return searchEnvironmentsHandler({ projectUuid: input.projectUuid, q: input.q, page: input.page, pageSize: input.pageSize }, ctx);
|
|
12
|
+
case 'create': {
|
|
13
|
+
const { action, ...rest } = input;
|
|
14
|
+
return createEnvironmentHandler(rest, ctx);
|
|
15
|
+
}
|
|
16
|
+
case 'update': {
|
|
17
|
+
const { action, ...rest } = input;
|
|
18
|
+
return updateEnvironmentHandler(rest, ctx);
|
|
19
|
+
}
|
|
20
|
+
case 'delete': {
|
|
21
|
+
const refusal = await ensureConfirmed('delete', `environment ${input.uuid}`, input, ctx);
|
|
22
|
+
if (refusal)
|
|
23
|
+
return refusal;
|
|
24
|
+
return deleteEnvironmentHandler({ uuid: input.uuid, projectUuid: input.projectUuid }, ctx);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { searchExecutionsHandler } from './searchExecutionsHandler.js';
|
|
2
|
+
export async function executionsHandler(input, ctx) {
|
|
3
|
+
switch (input.action) {
|
|
4
|
+
case 'get':
|
|
5
|
+
return searchExecutionsHandler({ uuid: input.uuid }, ctx);
|
|
6
|
+
case 'list':
|
|
7
|
+
return searchExecutionsHandler({ projectUuid: input.projectUuid, status: input.status, page: input.page, pageSize: input.pageSize }, ctx);
|
|
8
|
+
}
|
|
9
|
+
}
|
package/dist/handlers/index.js
CHANGED
|
@@ -8,9 +8,14 @@ export * from './createEnvironmentHandler.js';
|
|
|
8
8
|
export * from './updateEnvironmentHandler.js';
|
|
9
9
|
export * from './deleteEnvironmentHandler.js';
|
|
10
10
|
// Credential mutations are folded into create_environment + update_environment.
|
|
11
|
-
|
|
12
|
-
export * from './deleteProjectHandler.js';
|
|
11
|
+
// update_project + delete_project were cut (epic yg7o6, D8).
|
|
13
12
|
export * from './createProjectHandler.js';
|
|
13
|
+
// Action-tool dispatchers (the registered surface).
|
|
14
|
+
export * from './projectHandler.js';
|
|
15
|
+
export * from './environmentHandler.js';
|
|
16
|
+
export * from './testSuiteHandler.js';
|
|
17
|
+
export * from './testCaseHandler.js';
|
|
18
|
+
export * from './executionsHandler.js';
|
|
14
19
|
export * from './createTestSuiteHandler.js';
|
|
15
20
|
export * from './searchTestSuitesHandler.js';
|
|
16
21
|
export * from './deleteTestSuiteHandler.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { searchProjectsHandler } from './searchProjectsHandler.js';
|
|
2
|
+
import { createProjectHandler } from './createProjectHandler.js';
|
|
3
|
+
export async function projectHandler(input, ctx) {
|
|
4
|
+
switch (input.action) {
|
|
5
|
+
case 'get':
|
|
6
|
+
return searchProjectsHandler({ uuid: input.uuid }, ctx);
|
|
7
|
+
case 'list':
|
|
8
|
+
return searchProjectsHandler({ q: input.q, page: input.page, pageSize: input.pageSize }, ctx);
|
|
9
|
+
case 'create': {
|
|
10
|
+
const { action, ...rest } = input;
|
|
11
|
+
return createProjectHandler(rest, ctx);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ensureConfirmed } from '../utils/confirmDestructive.js';
|
|
2
|
+
import { createTestCaseHandler } from './createTestCaseHandler.js';
|
|
3
|
+
import { updateTestCaseHandler } from './updateTestCaseHandler.js';
|
|
4
|
+
import { deleteTestCaseHandler } from './deleteTestCaseHandler.js';
|
|
5
|
+
export async function testCaseHandler(input, ctx) {
|
|
6
|
+
switch (input.action) {
|
|
7
|
+
case 'create': {
|
|
8
|
+
const { action, ...rest } = input;
|
|
9
|
+
return createTestCaseHandler(rest, ctx);
|
|
10
|
+
}
|
|
11
|
+
case 'update': {
|
|
12
|
+
const { action, ...rest } = input;
|
|
13
|
+
return updateTestCaseHandler(rest, ctx);
|
|
14
|
+
}
|
|
15
|
+
case 'delete': {
|
|
16
|
+
const refusal = await ensureConfirmed('delete', `test case ${input.testUuid}`, input, ctx);
|
|
17
|
+
if (refusal)
|
|
18
|
+
return refusal;
|
|
19
|
+
return deleteTestCaseHandler({ testUuid: input.testUuid }, ctx);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ensureConfirmed } from '../utils/confirmDestructive.js';
|
|
2
|
+
import { searchTestSuitesHandler } from './searchTestSuitesHandler.js';
|
|
3
|
+
import { createTestSuiteHandler } from './createTestSuiteHandler.js';
|
|
4
|
+
import { runTestSuiteHandler } from './runTestSuiteHandler.js';
|
|
5
|
+
import { getTestSuiteResultsHandler } from './getTestSuiteResultsHandler.js';
|
|
6
|
+
import { deleteTestSuiteHandler } from './deleteTestSuiteHandler.js';
|
|
7
|
+
export async function testSuiteHandler(input, ctx) {
|
|
8
|
+
switch (input.action) {
|
|
9
|
+
case 'list': {
|
|
10
|
+
const { action, ...rest } = input;
|
|
11
|
+
return searchTestSuitesHandler(rest, ctx);
|
|
12
|
+
}
|
|
13
|
+
case 'create': {
|
|
14
|
+
const { action, ...rest } = input;
|
|
15
|
+
return createTestSuiteHandler(rest, ctx);
|
|
16
|
+
}
|
|
17
|
+
case 'run': {
|
|
18
|
+
const { action, ...rest } = input;
|
|
19
|
+
return runTestSuiteHandler(rest, ctx);
|
|
20
|
+
}
|
|
21
|
+
case 'results': {
|
|
22
|
+
const { action, ...rest } = input;
|
|
23
|
+
return getTestSuiteResultsHandler(rest, ctx);
|
|
24
|
+
}
|
|
25
|
+
case 'delete': {
|
|
26
|
+
const label = `test suite ${input.suiteUuid ?? input.suiteName ?? ''}`.trim();
|
|
27
|
+
const refusal = await ensureConfirmed('delete', label, input, ctx);
|
|
28
|
+
if (refusal)
|
|
29
|
+
return refusal;
|
|
30
|
+
const { action, confirm, ...rest } = input;
|
|
31
|
+
return deleteTestSuiteHandler(rest, ctx);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -150,8 +150,7 @@ export async function triggerCrawlHandler(input, context, rawProgressCallback) {
|
|
|
150
150
|
};
|
|
151
151
|
if (input.projectUuid)
|
|
152
152
|
contextData.projectId = input.projectUuid;
|
|
153
|
-
|
|
154
|
-
contextData.headless = input.headless;
|
|
153
|
+
contextData.headless = true; // D7: the MCP always runs headless — no opt-out.
|
|
155
154
|
if (typeof input.timeoutSeconds === 'number')
|
|
156
155
|
contextData.timeoutSeconds = input.timeoutSeconds;
|
|
157
156
|
const env = {};
|
|
@@ -15,18 +15,31 @@ const EXECUTION_TIMEOUT_MS = 10 * 60 * 1000; // 10 min
|
|
|
15
15
|
export const createWorkflowsService = (tx) => {
|
|
16
16
|
const service = {
|
|
17
17
|
async findTemplateByName(keyword) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
// Narrow server-side with `search` AND walk every page. The backend caps
|
|
19
|
+
// the page size (it ignores page_size), so reading only page 1 silently
|
|
20
|
+
// hides templates that sort later — that bug made check_app_in_browser
|
|
21
|
+
// fail in prod because "App Evaluation Workflow Template" sat on page 2.
|
|
22
|
+
// `search` collapses the candidate set to one page on backends that
|
|
23
|
+
// support it; `page` paging is the fallback for those that ignore it.
|
|
22
24
|
const needle = keyword.toLowerCase();
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
const seenNames = [];
|
|
26
|
+
const MAX_PAGES = 50; // safety valve against a backend that always returns `next`
|
|
27
|
+
for (let page = 1; page <= MAX_PAGES; page++) {
|
|
28
|
+
const response = await tx.get('api/v1/workflows/', { isTemplate: true, search: keyword, page });
|
|
29
|
+
const templates = response?.results ?? [];
|
|
30
|
+
for (const t of templates) {
|
|
31
|
+
seenNames.push(t.name);
|
|
32
|
+
if (t.name.toLowerCase().includes(needle))
|
|
33
|
+
return t;
|
|
34
|
+
}
|
|
35
|
+
if (!response?.next)
|
|
36
|
+
break;
|
|
28
37
|
}
|
|
29
|
-
|
|
38
|
+
if (seenNames.length === 0)
|
|
39
|
+
return null;
|
|
40
|
+
throw new Error(`No workflow template matching "${keyword}" found. ` +
|
|
41
|
+
`Available templates: ${seenNames.map(n => `"${n}"`).join(', ')}. ` +
|
|
42
|
+
`Ensure the backend has a template with "${keyword}" in its name.`);
|
|
30
43
|
},
|
|
31
44
|
async findEvaluationTemplate() {
|
|
32
45
|
// 'app evaluation workflow' is specific enough to skip 'App Evaluation Brain'
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { EnvironmentInputSchema } from '../types/index.js';
|
|
2
|
+
import { environmentHandler } from '../handlers/environmentHandler.js';
|
|
3
|
+
const CRED_ITEM = {
|
|
4
|
+
type: 'object',
|
|
5
|
+
properties: {
|
|
6
|
+
label: { type: 'string' }, username: { type: 'string' }, password: { type: 'string', description: 'Write-only — never returned.' }, role: { type: 'string' },
|
|
7
|
+
},
|
|
8
|
+
required: ['label', 'username', 'password'],
|
|
9
|
+
additionalProperties: false,
|
|
10
|
+
};
|
|
11
|
+
const DESCRIPTION = `Manage environments (and their login credentials) under a project. Pass an "action":
|
|
12
|
+
- "get" {uuid, projectUuid?} → one environment with credentials inline (passwords never returned).
|
|
13
|
+
- "list" {projectUuid?, q?, page?, pageSize?} → paginated environments. projectUuid auto-resolves from the git repo if omitted.
|
|
14
|
+
- "create" {name, url, description?, projectUuid?, credentials?} → create an env, optionally seeding credentials.
|
|
15
|
+
- "update" {uuid, name?, url?, description?, addCredentials?, updateCredentials?, removeCredentialIds?} → patch env + manage credentials.
|
|
16
|
+
- "delete" {uuid, projectUuid?, confirm?} → delete env (DESTRUCTIVE; requires confirmation).`;
|
|
17
|
+
export function buildEnvironmentTool() {
|
|
18
|
+
return {
|
|
19
|
+
name: 'environment',
|
|
20
|
+
title: 'Environment',
|
|
21
|
+
description: DESCRIPTION,
|
|
22
|
+
inputSchema: {
|
|
23
|
+
type: 'object',
|
|
24
|
+
properties: {
|
|
25
|
+
action: { type: 'string', enum: ['get', 'list', 'create', 'update', 'delete'], description: 'Operation to perform.' },
|
|
26
|
+
uuid: { type: 'string', description: '[get/update/delete] Environment UUID.' },
|
|
27
|
+
projectUuid: { type: 'string', description: 'Target project (defaults to git auto-detect).' },
|
|
28
|
+
q: { type: 'string', description: '[list] Free-text search over env name.' },
|
|
29
|
+
page: { type: 'number', description: '[list] Page (1-indexed).' },
|
|
30
|
+
pageSize: { type: 'number', description: '[list] Page size (1..200).' },
|
|
31
|
+
name: { type: 'string', description: '[create/update] Environment name.' },
|
|
32
|
+
url: { type: 'string', description: '[create/update] Base URL.' },
|
|
33
|
+
description: { type: 'string', description: '[create/update] Free-text description.' },
|
|
34
|
+
credentials: { type: 'array', items: CRED_ITEM, description: '[create] Seed login credentials.' },
|
|
35
|
+
addCredentials: { type: 'array', items: CRED_ITEM, description: '[update] Add credentials.' },
|
|
36
|
+
updateCredentials: { type: 'array', items: { type: 'object', properties: { uuid: { type: 'string' }, label: { type: 'string' }, username: { type: 'string' }, password: { type: 'string' }, role: { type: 'string' } }, required: ['uuid'], additionalProperties: false }, description: '[update] Patch credentials by UUID.' },
|
|
37
|
+
removeCredentialIds: { type: 'array', items: { type: 'string' }, description: '[update] Delete credentials by UUID.' },
|
|
38
|
+
confirm: { type: 'boolean', description: '[delete] Set true to confirm deletion (when the client cannot prompt).' },
|
|
39
|
+
},
|
|
40
|
+
required: ['action'],
|
|
41
|
+
oneOf: [
|
|
42
|
+
{ properties: { action: { const: 'get' } }, required: ['action', 'uuid'] },
|
|
43
|
+
{ properties: { action: { const: 'list' } }, required: ['action'] },
|
|
44
|
+
{ properties: { action: { const: 'create' } }, required: ['action', 'name', 'url'] },
|
|
45
|
+
{ properties: { action: { const: 'update' } }, required: ['action', 'uuid'] },
|
|
46
|
+
{ properties: { action: { const: 'delete' } }, required: ['action', 'uuid'] },
|
|
47
|
+
],
|
|
48
|
+
additionalProperties: false,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export function buildValidatedEnvironmentTool() {
|
|
53
|
+
return { ...buildEnvironmentTool(), inputSchema: EnvironmentInputSchema, handler: environmentHandler };
|
|
54
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ExecutionsInputSchema } from '../types/index.js';
|
|
2
|
+
import { executionsHandler } from '../handlers/executionsHandler.js';
|
|
3
|
+
const DESCRIPTION = `Look up workflow executions (history of check_app_in_browser, trigger_crawl, and test-suite runs). Pass an "action":
|
|
4
|
+
- "get" {uuid} → one execution with FULL detail (nodeExecutions, state, errorInfo) + any screenshot/gif artifacts.
|
|
5
|
+
- "list" {projectUuid?, status?, page?, pageSize?} → paginated execution summaries. status ∈ completed|running|failed|cancelled|pending.
|
|
6
|
+
|
|
7
|
+
Tip: after a fresh check_app_in_browser run, poll action:"get" with the returned executionId until artifact URLs are available.`;
|
|
8
|
+
export function buildExecutionsTool() {
|
|
9
|
+
return {
|
|
10
|
+
name: 'executions',
|
|
11
|
+
title: 'Workflow Executions',
|
|
12
|
+
description: DESCRIPTION,
|
|
13
|
+
inputSchema: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
action: { type: 'string', enum: ['get', 'list'], description: 'Operation to perform.' },
|
|
17
|
+
uuid: { type: 'string', description: '[get] Execution UUID.' },
|
|
18
|
+
projectUuid: { type: 'string', description: '[list] Filter by project UUID.' },
|
|
19
|
+
status: { type: 'string', description: '[list] Filter by status.' },
|
|
20
|
+
page: { type: 'number', description: '[list] Page (1-indexed).' },
|
|
21
|
+
pageSize: { type: 'number', description: '[list] Page size (1..200).' },
|
|
22
|
+
},
|
|
23
|
+
required: ['action'],
|
|
24
|
+
oneOf: [
|
|
25
|
+
{ properties: { action: { const: 'get' } }, required: ['action', 'uuid'] },
|
|
26
|
+
{ properties: { action: { const: 'list' } }, required: ['action'] },
|
|
27
|
+
],
|
|
28
|
+
additionalProperties: false,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function buildValidatedExecutionsTool() {
|
|
33
|
+
return { ...buildExecutionsTool(), inputSchema: ExecutionsInputSchema, handler: executionsHandler };
|
|
34
|
+
}
|
package/dist/tools/index.js
CHANGED
|
@@ -1,66 +1,41 @@
|
|
|
1
1
|
import { buildTestPageChangesTool, buildValidatedTestPageChangesTool } from './testPageChanges.js';
|
|
2
2
|
import { buildTriggerCrawlTool, buildValidatedTriggerCrawlTool } from './triggerCrawl.js';
|
|
3
3
|
import { buildProbePageTool, buildValidatedProbePageTool } from './probePage.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { buildDeleteEnvironmentTool, buildValidatedDeleteEnvironmentTool } from './deleteEnvironment.js';
|
|
10
|
-
import { buildUpdateProjectTool, buildValidatedUpdateProjectTool } from './updateProject.js';
|
|
11
|
-
import { buildDeleteProjectTool, buildValidatedDeleteProjectTool } from './deleteProject.js';
|
|
12
|
-
import { buildCreateProjectTool, buildValidatedCreateProjectTool } from './createProject.js';
|
|
13
|
-
import { buildCreateTestSuiteTool, buildValidatedCreateTestSuiteTool, buildSearchTestSuitesTool, buildValidatedSearchTestSuitesTool, buildDeleteTestSuiteTool, buildValidatedDeleteTestSuiteTool, buildCreateTestCaseTool, buildValidatedCreateTestCaseTool, buildUpdateTestCaseTool, buildValidatedUpdateTestCaseTool, buildDeleteTestCaseTool, buildValidatedDeleteTestCaseTool, buildRunTestSuiteTool, buildValidatedRunTestSuiteTool, buildGetTestSuiteResultsTool, buildValidatedGetTestSuiteResultsTool, } from './testSuiteTools.js';
|
|
4
|
+
import { buildProjectTool, buildValidatedProjectTool } from './project.js';
|
|
5
|
+
import { buildEnvironmentTool, buildValidatedEnvironmentTool } from './environment.js';
|
|
6
|
+
import { buildExecutionsTool, buildValidatedExecutionsTool } from './executions.js';
|
|
7
|
+
import { buildTestSuiteTool, buildValidatedTestSuiteTool } from './testSuite.js';
|
|
8
|
+
import { buildTestCaseTool, buildValidatedTestCaseTool } from './testCase.js';
|
|
14
9
|
let _tools = null;
|
|
15
10
|
let _validatedTools = null;
|
|
16
11
|
const toolRegistry = new Map();
|
|
17
12
|
/**
|
|
18
13
|
* Initialize tools with project context (call once after resolveProjectContext).
|
|
14
|
+
*
|
|
15
|
+
* The surface is 8 action-based tools (epic yg7o6): 3 browser tools plus one
|
|
16
|
+
* tool per managed entity (project/environment/test_suite/test_case/executions),
|
|
17
|
+
* each routing an `action` discriminator to its handler.
|
|
19
18
|
*/
|
|
20
19
|
export function initTools(ctx) {
|
|
21
20
|
const tools = [
|
|
22
21
|
buildTestPageChangesTool(ctx),
|
|
23
|
-
buildTriggerCrawlTool(ctx),
|
|
24
22
|
buildProbePageTool(),
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
buildDeleteProjectTool(),
|
|
32
|
-
buildSearchExecutionsTool(),
|
|
33
|
-
buildCreateProjectTool(),
|
|
34
|
-
buildCreateTestSuiteTool(),
|
|
35
|
-
buildSearchTestSuitesTool(),
|
|
36
|
-
buildDeleteTestSuiteTool(),
|
|
37
|
-
buildCreateTestCaseTool(),
|
|
38
|
-
buildUpdateTestCaseTool(),
|
|
39
|
-
buildDeleteTestCaseTool(),
|
|
40
|
-
buildRunTestSuiteTool(),
|
|
41
|
-
buildGetTestSuiteResultsTool(),
|
|
23
|
+
buildTriggerCrawlTool(ctx),
|
|
24
|
+
buildProjectTool(),
|
|
25
|
+
buildEnvironmentTool(),
|
|
26
|
+
buildTestSuiteTool(),
|
|
27
|
+
buildTestCaseTool(),
|
|
28
|
+
buildExecutionsTool(),
|
|
42
29
|
];
|
|
43
30
|
const validated = [
|
|
44
31
|
buildValidatedTestPageChangesTool(ctx),
|
|
45
|
-
buildValidatedTriggerCrawlTool(ctx),
|
|
46
32
|
buildValidatedProbePageTool(),
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
buildValidatedDeleteProjectTool(),
|
|
54
|
-
buildValidatedSearchExecutionsTool(),
|
|
55
|
-
buildValidatedCreateProjectTool(),
|
|
56
|
-
buildValidatedCreateTestSuiteTool(),
|
|
57
|
-
buildValidatedSearchTestSuitesTool(),
|
|
58
|
-
buildValidatedDeleteTestSuiteTool(),
|
|
59
|
-
buildValidatedCreateTestCaseTool(),
|
|
60
|
-
buildValidatedUpdateTestCaseTool(),
|
|
61
|
-
buildValidatedDeleteTestCaseTool(),
|
|
62
|
-
buildValidatedRunTestSuiteTool(),
|
|
63
|
-
buildValidatedGetTestSuiteResultsTool(),
|
|
33
|
+
buildValidatedTriggerCrawlTool(ctx),
|
|
34
|
+
buildValidatedProjectTool(),
|
|
35
|
+
buildValidatedEnvironmentTool(),
|
|
36
|
+
buildValidatedTestSuiteTool(),
|
|
37
|
+
buildValidatedTestCaseTool(),
|
|
38
|
+
buildValidatedExecutionsTool(),
|
|
64
39
|
];
|
|
65
40
|
_tools = tools;
|
|
66
41
|
_validatedTools = validated;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ProjectInputSchema } from '../types/index.js';
|
|
2
|
+
import { projectHandler } from '../handlers/projectHandler.js';
|
|
3
|
+
const DESCRIPTION = `Manage DebuggAI projects. Pass an "action":
|
|
4
|
+
- "get" {uuid} → one project with full detail.
|
|
5
|
+
- "list" {q?, page?, pageSize?} → paginated project summaries.
|
|
6
|
+
- "create" {name, platform, (teamUuid|teamName), (repoUuid|repoName)} → create a project. The repo must be GitHub-linked; names resolve by case-insensitive exact match.
|
|
7
|
+
|
|
8
|
+
Note: there is no update/delete here — rename/delete a project from the DebuggAI web app.`;
|
|
9
|
+
export function buildProjectTool() {
|
|
10
|
+
return {
|
|
11
|
+
name: 'project',
|
|
12
|
+
title: 'Project',
|
|
13
|
+
description: DESCRIPTION,
|
|
14
|
+
inputSchema: {
|
|
15
|
+
type: 'object',
|
|
16
|
+
properties: {
|
|
17
|
+
action: { type: 'string', enum: ['get', 'list', 'create'], description: 'Operation to perform.' },
|
|
18
|
+
uuid: { type: 'string', description: '[get] Project UUID.' },
|
|
19
|
+
q: { type: 'string', description: '[list] Free-text search.' },
|
|
20
|
+
page: { type: 'number', description: '[list] Page (1-indexed).' },
|
|
21
|
+
pageSize: { type: 'number', description: '[list] Page size (1..200).' },
|
|
22
|
+
name: { type: 'string', description: '[create] Project name.' },
|
|
23
|
+
platform: { type: 'string', description: '[create] Platform, e.g. "web".' },
|
|
24
|
+
teamUuid: { type: 'string', description: '[create] Team UUID (or teamName).' },
|
|
25
|
+
teamName: { type: 'string', description: '[create] Team name (or teamUuid).' },
|
|
26
|
+
repoUuid: { type: 'string', description: '[create] GitHub repo UUID (or repoName).' },
|
|
27
|
+
repoName: { type: 'string', description: '[create] GitHub repo name "org/repo" (or repoUuid).' },
|
|
28
|
+
},
|
|
29
|
+
required: ['action'],
|
|
30
|
+
oneOf: [
|
|
31
|
+
{ properties: { action: { const: 'get' } }, required: ['action', 'uuid'] },
|
|
32
|
+
{ properties: { action: { const: 'list' } }, required: ['action'] },
|
|
33
|
+
{ properties: { action: { const: 'create' } }, required: ['action', 'name', 'platform'] },
|
|
34
|
+
],
|
|
35
|
+
additionalProperties: false,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function buildValidatedProjectTool() {
|
|
40
|
+
return { ...buildProjectTool(), inputSchema: ProjectInputSchema, handler: projectHandler };
|
|
41
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { TestCaseInputSchema } from '../types/index.js';
|
|
2
|
+
import { testCaseHandler } from '../handlers/testCaseHandler.js';
|
|
3
|
+
const DESCRIPTION = `Manage individual test cases within a suite. Pass an "action":
|
|
4
|
+
- "create" {name, description, agentTaskDescription, suiteUuid|(suiteName+project), relativeUrl?, maxSteps?} → add a test case (NOT auto-run).
|
|
5
|
+
- "update" {testUuid, name?, description?, agentTaskDescription?} → patch a test case.
|
|
6
|
+
- "delete" {testUuid, confirm?} → soft-delete (DESTRUCTIVE; requires confirmation).`;
|
|
7
|
+
export function buildTestCaseTool() {
|
|
8
|
+
return {
|
|
9
|
+
name: 'test_case',
|
|
10
|
+
title: 'Test Case',
|
|
11
|
+
description: DESCRIPTION,
|
|
12
|
+
inputSchema: {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
action: { type: 'string', enum: ['create', 'update', 'delete'], description: 'Operation to perform.' },
|
|
16
|
+
testUuid: { type: 'string', description: '[update/delete] Test case UUID.' },
|
|
17
|
+
name: { type: 'string', description: 'Test case name.' },
|
|
18
|
+
description: { type: 'string', description: 'Test case description.' },
|
|
19
|
+
agentTaskDescription: { type: 'string', description: "What the AI agent should do and verify." },
|
|
20
|
+
suiteUuid: { type: 'string', description: '[create] Suite UUID.' },
|
|
21
|
+
suiteName: { type: 'string', description: '[create] Suite name (requires a project identifier).' },
|
|
22
|
+
projectUuid: { type: 'string', description: '[create] Project UUID (or projectName).' },
|
|
23
|
+
projectName: { type: 'string', description: '[create] Project name (or projectUuid).' },
|
|
24
|
+
relativeUrl: { type: 'string', description: '[create] Starting path, must start with "/".' },
|
|
25
|
+
maxSteps: { type: 'number', description: '[create] Max agent steps (1..100).' },
|
|
26
|
+
confirm: { type: 'boolean', description: '[delete] Set true to confirm deletion (when the client cannot prompt).' },
|
|
27
|
+
},
|
|
28
|
+
required: ['action'],
|
|
29
|
+
oneOf: [
|
|
30
|
+
{ properties: { action: { const: 'create' } }, required: ['action', 'name', 'description', 'agentTaskDescription'] },
|
|
31
|
+
{ properties: { action: { const: 'update' } }, required: ['action', 'testUuid'] },
|
|
32
|
+
{ properties: { action: { const: 'delete' } }, required: ['action', 'testUuid'] },
|
|
33
|
+
],
|
|
34
|
+
additionalProperties: false,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export function buildValidatedTestCaseTool() {
|
|
39
|
+
return { ...buildTestCaseTool(), inputSchema: TestCaseInputSchema, handler: testCaseHandler };
|
|
40
|
+
}
|