@debugg-ai/debugg-ai-mcp 2.9.7 → 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 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 **12** tools grouped into Browser (3), Search (3), Projects (3), and Environments (3). The headline tools are `check_app_in_browser` (full AI agent) and `probe_page` (lightweight no-LLM page probe); the rest manage projects, environments + their credentials, and execution history through a uniform `search_*` + CRUD pattern.
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 `search_executions` 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 `search_executions` with the returned `executionId` until status reaches `'downloaded'`. Authorization / Cookie / `token`/`secret`/`api_key` headers are scrubbed server-side before the artifacts are persisted.
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
- ### Search (dual-mode: uuid detail OR filtered list)
98
+ ### `project`
99
99
 
100
- Each `search_*` tool has two modes. Pass `{uuid}` for a single-record detail response. Pass filter params for a paginated summary list. 404 from the backend surfaces as `isError: true` with `{error: 'NotFound', message, uuid}`.
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
- | Tool | UUID mode | Filter mode |
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
- `projectUuid` is optional on `search_environments` — if omitted, it auto-resolves from the git repo. Credentials are **always** returned without passwords.
108
+ ### `environment`
109
109
 
110
- ### Projects
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
- | Tool | Purpose |
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
- ### Environments (credential sub-actions folded in)
120
+ ### `test_suite`
119
121
 
120
- | Tool | Purpose |
121
- |------|---------|
122
- | `create_environment` | Requires `name` + `url`. Optional `credentials: [{label, username, password, role?}]` seeds credentials in the same call. Per-cred failures surface in `credentialWarnings[]` without blocking env creation. |
123
- | `update_environment` | PATCH env fields (`name`, `url`, `description`) plus credential sub-actions in one call: `addCredentials[]`, `updateCredentials: [{uuid, ...patch}]`, `removeCredentialIds: [uuid]`. Execution order: **remove → update → add** (freed labels can be re-added in one request). |
124
- | `delete_environment` | Destructive cascades credentials. |
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
+ }
@@ -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
- export * from './updateProjectHandler.js';
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
- if (typeof input.headless === 'boolean')
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 = {};
@@ -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
+ }
@@ -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 { buildSearchProjectsTool, buildValidatedSearchProjectsTool } from './searchProjects.js';
5
- import { buildSearchEnvironmentsTool, buildValidatedSearchEnvironmentsTool } from './searchEnvironments.js';
6
- import { buildSearchExecutionsTool, buildValidatedSearchExecutionsTool } from './searchExecutions.js';
7
- import { buildCreateEnvironmentTool, buildValidatedCreateEnvironmentTool } from './createEnvironment.js';
8
- import { buildUpdateEnvironmentTool, buildValidatedUpdateEnvironmentTool } from './updateEnvironment.js';
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
- buildSearchProjectsTool(),
26
- buildSearchEnvironmentsTool(),
27
- buildCreateEnvironmentTool(),
28
- buildUpdateEnvironmentTool(),
29
- buildDeleteEnvironmentTool(),
30
- buildUpdateProjectTool(),
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
- buildValidatedSearchProjectsTool(),
48
- buildValidatedSearchEnvironmentsTool(),
49
- buildValidatedCreateEnvironmentTool(),
50
- buildValidatedUpdateEnvironmentTool(),
51
- buildValidatedDeleteEnvironmentTool(),
52
- buildValidatedUpdateProjectTool(),
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
+ }
@@ -0,0 +1,50 @@
1
+ import { TestSuiteInputSchema } from '../types/index.js';
2
+ import { testSuiteHandler } from '../handlers/testSuiteHandler.js';
3
+ const DESCRIPTION = `Manage and run test suites. Identify a suite by suiteUuid, or suiteName + a project identifier (projectUuid|projectName). Pass an "action":
4
+ - "list" {projectUuid|projectName, search?, page?, pageSize?} → paginated suites with status/pass-rate.
5
+ - "create" {name, description, projectUuid|projectName} → create a suite.
6
+ - "run" {suiteUuid|(suiteName+project), targetUrl?} → run all tests async. Poll with action:"results".
7
+ - "results" {suiteUuid|(suiteName+project)} → suite + per-test outcomes.
8
+ - "delete" {suiteUuid|(suiteName+project), confirm?} → soft-delete (DESTRUCTIVE; requires confirmation).`;
9
+ const PROJECT_PROPS = {
10
+ projectUuid: { type: 'string', description: 'Project UUID (or projectName).' },
11
+ projectName: { type: 'string', description: 'Project name (or projectUuid).' },
12
+ };
13
+ const SUITE_PROPS = {
14
+ suiteUuid: { type: 'string', description: 'Test suite UUID.' },
15
+ suiteName: { type: 'string', description: 'Test suite name (requires a project identifier).' },
16
+ };
17
+ export function buildTestSuiteTool() {
18
+ return {
19
+ name: 'test_suite',
20
+ title: 'Test Suite',
21
+ description: DESCRIPTION,
22
+ inputSchema: {
23
+ type: 'object',
24
+ properties: {
25
+ action: { type: 'string', enum: ['list', 'create', 'run', 'results', 'delete'], description: 'Operation to perform.' },
26
+ ...SUITE_PROPS,
27
+ ...PROJECT_PROPS,
28
+ name: { type: 'string', description: '[create] Suite name.' },
29
+ description: { type: 'string', description: '[create] Suite description.' },
30
+ search: { type: 'string', description: '[list] Text filter over name/description.' },
31
+ page: { type: 'number', description: '[list] Page (1-indexed).' },
32
+ pageSize: { type: 'number', description: '[list] Page size (1..100).' },
33
+ targetUrl: { type: 'string', description: '[run] Override the default test target (full URL).' },
34
+ confirm: { type: 'boolean', description: '[delete] Set true to confirm deletion (when the client cannot prompt).' },
35
+ },
36
+ required: ['action'],
37
+ oneOf: [
38
+ { properties: { action: { const: 'list' } }, required: ['action'] },
39
+ { properties: { action: { const: 'create' } }, required: ['action', 'name', 'description'] },
40
+ { properties: { action: { const: 'run' } }, required: ['action'] },
41
+ { properties: { action: { const: 'results' } }, required: ['action'] },
42
+ { properties: { action: { const: 'delete' } }, required: ['action'] },
43
+ ],
44
+ additionalProperties: false,
45
+ },
46
+ };
47
+ }
48
+ export function buildValidatedTestSuiteTool() {
49
+ return { ...buildTestSuiteTool(), inputSchema: TestSuiteInputSchema, handler: testSuiteHandler };
50
+ }
@@ -71,10 +71,6 @@ export function buildTriggerCrawlTool(ctx) {
71
71
  type: 'string',
72
72
  description: 'The real password for the username above. Do NOT guess.',
73
73
  },
74
- headless: {
75
- type: 'boolean',
76
- description: 'Run the browser in headless mode. Defaults to backend configuration.',
77
- },
78
74
  timeoutSeconds: {
79
75
  type: 'number',
80
76
  description: 'Maximum wall-time the crawl may run, in seconds (1..1800). Backend enforces this per workflow execution.',