@debugg-ai/debugg-ai-mcp 1.0.57 → 1.0.58
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 +14 -0
- package/dist/handlers/listCredentialsHandler.js +26 -4
- package/dist/handlers/listEnvironmentsHandler.js +8 -4
- package/dist/handlers/listExecutionsHandler.js +8 -8
- package/dist/handlers/listProjectsHandler.js +6 -4
- package/dist/services/index.js +73 -11
- package/dist/services/workflows.js +3 -4
- package/dist/tools/listCredentials.js +7 -17
- package/dist/tools/listEnvironments.js +5 -9
- package/dist/tools/listExecutions.js +3 -2
- package/dist/tools/listProjects.js +4 -5
- package/dist/types/index.js +8 -1
- package/dist/utils/pagination.js +32 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -86,6 +86,20 @@ Runs an AI browser agent against your app. The agent navigates, interacts, and r
|
|
|
86
86
|
| `get_execution` | Full detail for a single execution including node-level state. |
|
|
87
87
|
| `cancel_execution` | Cancel an in-flight execution. |
|
|
88
88
|
|
|
89
|
+
### Pagination
|
|
90
|
+
|
|
91
|
+
Every `list_*` tool is paginated by default. Response shape:
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"filter": { "...echoed query params..." },
|
|
96
|
+
"pageInfo": { "page": 1, "pageSize": 20, "totalCount": 47, "totalPages": 3, "hasMore": true },
|
|
97
|
+
"<items>": [ ... ]
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Pass optional `page` (1-indexed, default 1) and `pageSize` (default 20, max 200; oversized values are clamped) to any list tool. No tool ever silently truncates results.
|
|
102
|
+
|
|
89
103
|
### Security invariants
|
|
90
104
|
|
|
91
105
|
- Passwords are write-only. They never appear in any response body from any tool.
|
|
@@ -3,14 +3,17 @@ import { handleExternalServiceError } from '../utils/errors.js';
|
|
|
3
3
|
import { DebuggAIServerClient } from '../services/index.js';
|
|
4
4
|
import { config } from '../config/index.js';
|
|
5
5
|
import { detectRepoName } from '../utils/gitContext.js';
|
|
6
|
+
import { toPaginationParams, makePageInfo } from '../utils/pagination.js';
|
|
6
7
|
const logger = new Logger({ module: 'listCredentialsHandler' });
|
|
7
8
|
export async function listCredentialsHandler(input, _context) {
|
|
8
9
|
const start = Date.now();
|
|
10
|
+
const pagination = toPaginationParams({ page: input.page, pageSize: input.pageSize });
|
|
9
11
|
logger.toolStart('list_credentials', {
|
|
10
12
|
environmentId: input.environmentId,
|
|
11
13
|
projectUuid: input.projectUuid,
|
|
12
14
|
q: input.q,
|
|
13
15
|
role: input.role,
|
|
16
|
+
...pagination,
|
|
14
17
|
});
|
|
15
18
|
try {
|
|
16
19
|
const client = new DebuggAIServerClient(config.api.key);
|
|
@@ -22,6 +25,7 @@ export async function listCredentialsHandler(input, _context) {
|
|
|
22
25
|
const payload = {
|
|
23
26
|
error: 'NoProjectResolved',
|
|
24
27
|
message: 'No git repo detected and no projectUuid provided. Pass projectUuid (get it from list_projects) or invoke from a directory with a git origin.',
|
|
28
|
+
pageInfo: makePageInfo(pagination.page, pagination.pageSize, 0, null),
|
|
25
29
|
credentials: [],
|
|
26
30
|
};
|
|
27
31
|
logger.toolComplete('list_credentials', Date.now() - start);
|
|
@@ -32,6 +36,7 @@ export async function listCredentialsHandler(input, _context) {
|
|
|
32
36
|
const payload = {
|
|
33
37
|
error: 'NoProjectResolved',
|
|
34
38
|
message: `No DebuggAI project found for repo "${repoName}". Pass projectUuid explicitly.`,
|
|
39
|
+
pageInfo: makePageInfo(pagination.page, pagination.pageSize, 0, null),
|
|
35
40
|
credentials: [],
|
|
36
41
|
};
|
|
37
42
|
logger.toolComplete('list_credentials', Date.now() - start);
|
|
@@ -39,17 +44,34 @@ export async function listCredentialsHandler(input, _context) {
|
|
|
39
44
|
}
|
|
40
45
|
projectUuid = project.uuid;
|
|
41
46
|
}
|
|
47
|
+
let pageInfo;
|
|
42
48
|
let credentials = [];
|
|
43
49
|
if (input.environmentId) {
|
|
44
|
-
|
|
50
|
+
// Paginated path — scoped to a single env.
|
|
51
|
+
const result = await client.listCredentialsPaginated(projectUuid, input.environmentId, pagination, input.q, input.role);
|
|
52
|
+
pageInfo = result.pageInfo;
|
|
53
|
+
credentials = result.credentials;
|
|
45
54
|
}
|
|
46
55
|
else {
|
|
47
|
-
// No
|
|
56
|
+
// No env filter — iterate all envs and merge. Synthesize pageInfo from the full
|
|
57
|
+
// result (client-side paginate the merged list for consistent shape).
|
|
48
58
|
const envs = await client.listEnvironmentsForProject(projectUuid);
|
|
59
|
+
const all = [];
|
|
49
60
|
for (const env of envs) {
|
|
50
61
|
const credsForEnv = await client.listCredentialsForEnvironment(projectUuid, env.uuid, input.q, input.role);
|
|
51
|
-
|
|
62
|
+
all.push(...credsForEnv);
|
|
52
63
|
}
|
|
64
|
+
const offset = (pagination.page - 1) * pagination.pageSize;
|
|
65
|
+
credentials = all.slice(offset, offset + pagination.pageSize);
|
|
66
|
+
const totalCount = all.length;
|
|
67
|
+
const totalPages = totalCount === 0 ? 0 : Math.ceil(totalCount / pagination.pageSize);
|
|
68
|
+
pageInfo = {
|
|
69
|
+
page: pagination.page,
|
|
70
|
+
pageSize: pagination.pageSize,
|
|
71
|
+
totalCount,
|
|
72
|
+
totalPages,
|
|
73
|
+
hasMore: offset + credentials.length < totalCount,
|
|
74
|
+
};
|
|
53
75
|
}
|
|
54
76
|
const payload = {
|
|
55
77
|
project: { uuid: projectUuid },
|
|
@@ -58,7 +80,7 @@ export async function listCredentialsHandler(input, _context) {
|
|
|
58
80
|
q: input.q ?? null,
|
|
59
81
|
role: input.role ?? null,
|
|
60
82
|
},
|
|
61
|
-
|
|
83
|
+
pageInfo,
|
|
62
84
|
credentials,
|
|
63
85
|
};
|
|
64
86
|
logger.toolComplete('list_credentials', Date.now() - start);
|
|
@@ -3,10 +3,12 @@ import { handleExternalServiceError } from '../utils/errors.js';
|
|
|
3
3
|
import { DebuggAIServerClient } from '../services/index.js';
|
|
4
4
|
import { config } from '../config/index.js';
|
|
5
5
|
import { detectRepoName } from '../utils/gitContext.js';
|
|
6
|
+
import { toPaginationParams, makePageInfo } from '../utils/pagination.js';
|
|
6
7
|
const logger = new Logger({ module: 'listEnvironmentsHandler' });
|
|
7
8
|
export async function listEnvironmentsHandler(input, _context) {
|
|
8
9
|
const start = Date.now();
|
|
9
|
-
|
|
10
|
+
const pagination = toPaginationParams({ page: input.page, pageSize: input.pageSize });
|
|
11
|
+
logger.toolStart('list_environments', { projectUuid: input.projectUuid, q: input.q, ...pagination });
|
|
10
12
|
try {
|
|
11
13
|
const client = new DebuggAIServerClient(config.api.key);
|
|
12
14
|
await client.init();
|
|
@@ -19,6 +21,7 @@ export async function listEnvironmentsHandler(input, _context) {
|
|
|
19
21
|
const payload = {
|
|
20
22
|
error: 'NoProjectResolved',
|
|
21
23
|
message: 'No git repo detected and no projectUuid provided. Pass projectUuid (get it from list_projects) or invoke from a directory with a git origin.',
|
|
24
|
+
pageInfo: makePageInfo(pagination.page, pagination.pageSize, 0, null),
|
|
22
25
|
environments: [],
|
|
23
26
|
};
|
|
24
27
|
logger.toolComplete('list_environments', Date.now() - start);
|
|
@@ -29,6 +32,7 @@ export async function listEnvironmentsHandler(input, _context) {
|
|
|
29
32
|
const payload = {
|
|
30
33
|
error: 'NoProjectResolved',
|
|
31
34
|
message: `No DebuggAI project found for repo "${repoName}". Pass projectUuid explicitly or call list_projects to discover.`,
|
|
35
|
+
pageInfo: makePageInfo(pagination.page, pagination.pageSize, 0, null),
|
|
32
36
|
environments: [],
|
|
33
37
|
};
|
|
34
38
|
logger.toolComplete('list_environments', Date.now() - start);
|
|
@@ -38,15 +42,15 @@ export async function listEnvironmentsHandler(input, _context) {
|
|
|
38
42
|
projectName = project.name;
|
|
39
43
|
projectRepoName = project.repo?.name ?? repoName;
|
|
40
44
|
}
|
|
41
|
-
const environments = await client.
|
|
45
|
+
const { pageInfo, environments } = await client.listEnvironmentsPaginated(projectUuid, pagination, input.q);
|
|
42
46
|
const payload = {
|
|
43
47
|
project: {
|
|
44
48
|
uuid: projectUuid,
|
|
45
49
|
name: projectName,
|
|
46
50
|
repoName: projectRepoName,
|
|
47
51
|
},
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
filter: { q: input.q ?? null },
|
|
53
|
+
pageInfo,
|
|
50
54
|
environments,
|
|
51
55
|
};
|
|
52
56
|
logger.toolComplete('list_environments', Date.now() - start);
|
|
@@ -2,23 +2,23 @@ import { Logger } from '../utils/logger.js';
|
|
|
2
2
|
import { handleExternalServiceError } from '../utils/errors.js';
|
|
3
3
|
import { DebuggAIServerClient } from '../services/index.js';
|
|
4
4
|
import { config } from '../config/index.js';
|
|
5
|
+
import { toPaginationParams } from '../utils/pagination.js';
|
|
5
6
|
const logger = new Logger({ module: 'listExecutionsHandler' });
|
|
6
7
|
export async function listExecutionsHandler(input, _context) {
|
|
7
8
|
const start = Date.now();
|
|
8
|
-
|
|
9
|
+
const pagination = toPaginationParams({ page: input.page, pageSize: input.pageSize });
|
|
10
|
+
logger.toolStart('list_executions', { status: input.status, ...pagination });
|
|
9
11
|
try {
|
|
10
12
|
const client = new DebuggAIServerClient(config.api.key);
|
|
11
13
|
await client.init();
|
|
12
|
-
const {
|
|
14
|
+
const { pageInfo, executions } = await client.workflows.listExecutions({
|
|
13
15
|
status: input.status,
|
|
14
|
-
|
|
16
|
+
page: pagination.page,
|
|
17
|
+
pageSize: pagination.pageSize,
|
|
15
18
|
});
|
|
16
19
|
const payload = {
|
|
17
|
-
filter: {
|
|
18
|
-
|
|
19
|
-
limit: input.limit ?? null,
|
|
20
|
-
},
|
|
21
|
-
count,
|
|
20
|
+
filter: { status: input.status ?? null },
|
|
21
|
+
pageInfo,
|
|
22
22
|
executions,
|
|
23
23
|
};
|
|
24
24
|
logger.toolComplete('list_executions', Date.now() - start);
|
|
@@ -2,17 +2,19 @@ import { Logger } from '../utils/logger.js';
|
|
|
2
2
|
import { handleExternalServiceError } from '../utils/errors.js';
|
|
3
3
|
import { DebuggAIServerClient } from '../services/index.js';
|
|
4
4
|
import { config } from '../config/index.js';
|
|
5
|
+
import { toPaginationParams } from '../utils/pagination.js';
|
|
5
6
|
const logger = new Logger({ module: 'listProjectsHandler' });
|
|
6
7
|
export async function listProjectsHandler(input, _context) {
|
|
7
8
|
const start = Date.now();
|
|
8
|
-
|
|
9
|
+
const pagination = toPaginationParams({ page: input.page, pageSize: input.pageSize });
|
|
10
|
+
logger.toolStart('list_projects', { q: input.q, ...pagination });
|
|
9
11
|
try {
|
|
10
12
|
const client = new DebuggAIServerClient(config.api.key);
|
|
11
13
|
await client.init();
|
|
12
|
-
const projects = await client.listProjects(input.q);
|
|
14
|
+
const { pageInfo, projects } = await client.listProjects(pagination, input.q);
|
|
13
15
|
const payload = {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
filter: { q: input.q ?? null },
|
|
17
|
+
pageInfo,
|
|
16
18
|
projects: projects.map(p => ({
|
|
17
19
|
uuid: p.uuid,
|
|
18
20
|
name: p.name,
|
package/dist/services/index.js
CHANGED
|
@@ -116,24 +116,34 @@ export class DebuggAIServerClient {
|
|
|
116
116
|
await this.tx.delete(`api/v1/projects/${uuid}/`);
|
|
117
117
|
}
|
|
118
118
|
/**
|
|
119
|
-
* List projects accessible to the current API key.
|
|
119
|
+
* List projects accessible to the current API key. Paginated.
|
|
120
120
|
* Optional q filters by project name / repo name server-side (backend `?search=`).
|
|
121
|
-
* Returns the first page only; pagination can be added if needed.
|
|
122
121
|
*/
|
|
123
|
-
async listProjects(q) {
|
|
122
|
+
async listProjects(pagination, q) {
|
|
124
123
|
if (!this.tx)
|
|
125
124
|
throw new Error('Client not initialized — call init() first');
|
|
126
|
-
const
|
|
125
|
+
const { makePageInfo } = await import('../utils/pagination.js');
|
|
126
|
+
const params = { page: pagination.page, pageSize: pagination.pageSize };
|
|
127
|
+
if (q)
|
|
128
|
+
params.search = q;
|
|
127
129
|
const response = await this.tx.get('api/v1/projects/', params);
|
|
128
|
-
return
|
|
130
|
+
return {
|
|
131
|
+
pageInfo: makePageInfo(pagination.page, pagination.pageSize, response?.count ?? 0, response?.next),
|
|
132
|
+
projects: response?.results ?? [],
|
|
133
|
+
};
|
|
129
134
|
}
|
|
130
135
|
/**
|
|
131
|
-
* List environments for a project.
|
|
136
|
+
* List environments for a project. Paginated.
|
|
137
|
+
* Optional q filters by name via backend ?search=.
|
|
138
|
+
* The bare-array variant (no pagination) is still used internally by
|
|
139
|
+
* list_credentials when iterating across all envs.
|
|
132
140
|
*/
|
|
133
141
|
async listEnvironmentsForProject(projectUuid, q) {
|
|
134
142
|
if (!this.tx)
|
|
135
143
|
throw new Error('Client not initialized — call init() first');
|
|
136
|
-
const params =
|
|
144
|
+
const params = { pageSize: 200 };
|
|
145
|
+
if (q)
|
|
146
|
+
params.search = q;
|
|
137
147
|
const response = await this.tx.get(`api/v1/projects/${projectUuid}/environments/`, params);
|
|
138
148
|
return (response?.results ?? []).map((e) => ({
|
|
139
149
|
uuid: e.uuid,
|
|
@@ -142,6 +152,24 @@ export class DebuggAIServerClient {
|
|
|
142
152
|
isActive: e.isActive,
|
|
143
153
|
}));
|
|
144
154
|
}
|
|
155
|
+
async listEnvironmentsPaginated(projectUuid, pagination, q) {
|
|
156
|
+
if (!this.tx)
|
|
157
|
+
throw new Error('Client not initialized — call init() first');
|
|
158
|
+
const { makePageInfo } = await import('../utils/pagination.js');
|
|
159
|
+
const params = { page: pagination.page, pageSize: pagination.pageSize };
|
|
160
|
+
if (q)
|
|
161
|
+
params.search = q;
|
|
162
|
+
const response = await this.tx.get(`api/v1/projects/${projectUuid}/environments/`, params);
|
|
163
|
+
return {
|
|
164
|
+
pageInfo: makePageInfo(pagination.page, pagination.pageSize, response?.count ?? 0, response?.next),
|
|
165
|
+
environments: (response?.results ?? []).map((e) => ({
|
|
166
|
+
uuid: e.uuid,
|
|
167
|
+
name: e.name,
|
|
168
|
+
url: e.url || e.activeUrl || '',
|
|
169
|
+
isActive: e.isActive,
|
|
170
|
+
})),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
145
173
|
/**
|
|
146
174
|
* Create a new environment under a project.
|
|
147
175
|
* Backend requires `name`. Other fields optional.
|
|
@@ -213,13 +241,44 @@ export class DebuggAIServerClient {
|
|
|
213
241
|
};
|
|
214
242
|
}
|
|
215
243
|
/**
|
|
216
|
-
* List credentials for a specific environment.
|
|
217
|
-
*
|
|
244
|
+
* List credentials for a specific environment. Unpaginated (fetches up to
|
|
245
|
+
* backend max pageSize). q filters label/username client-side (backend
|
|
246
|
+
* ?search= is inconsistent on this endpoint); role filters server-side.
|
|
247
|
+
* Used internally by list_credentials when iterating across envs.
|
|
218
248
|
*/
|
|
219
249
|
async listCredentialsForEnvironment(projectUuid, envUuid, q, role) {
|
|
220
250
|
if (!this.tx)
|
|
221
251
|
throw new Error('Client not initialized — call init() first');
|
|
222
|
-
const
|
|
252
|
+
const params = { pageSize: 200 };
|
|
253
|
+
if (role)
|
|
254
|
+
params.role = role;
|
|
255
|
+
const response = await this.tx.get(`api/v1/projects/${projectUuid}/environments/${envUuid}/credentials/`, params);
|
|
256
|
+
let creds = (response?.results ?? [])
|
|
257
|
+
.filter((c) => c.isActive)
|
|
258
|
+
.map((c) => ({
|
|
259
|
+
uuid: c.uuid,
|
|
260
|
+
label: c.label || c.username,
|
|
261
|
+
username: c.username,
|
|
262
|
+
role: c.role,
|
|
263
|
+
environmentUuid: envUuid,
|
|
264
|
+
}));
|
|
265
|
+
if (q) {
|
|
266
|
+
const needle = q.toLowerCase();
|
|
267
|
+
creds = creds.filter(c => c.label.toLowerCase().includes(needle) ||
|
|
268
|
+
c.username.toLowerCase().includes(needle));
|
|
269
|
+
}
|
|
270
|
+
return creds;
|
|
271
|
+
}
|
|
272
|
+
async listCredentialsPaginated(projectUuid, envUuid, pagination, q, role) {
|
|
273
|
+
if (!this.tx)
|
|
274
|
+
throw new Error('Client not initialized — call init() first');
|
|
275
|
+
const { makePageInfo } = await import('../utils/pagination.js');
|
|
276
|
+
const params = { page: pagination.page, pageSize: pagination.pageSize };
|
|
277
|
+
// Backend ?role= filter is currently ignored (bead hpo) — pass it anyway for future fix-forward,
|
|
278
|
+
// but re-apply the filter client-side so behavior is correct today.
|
|
279
|
+
if (role)
|
|
280
|
+
params.role = role;
|
|
281
|
+
const response = await this.tx.get(`api/v1/projects/${projectUuid}/environments/${envUuid}/credentials/`, params);
|
|
223
282
|
let creds = (response?.results ?? [])
|
|
224
283
|
.filter((c) => c.isActive)
|
|
225
284
|
.map((c) => ({
|
|
@@ -237,7 +296,10 @@ export class DebuggAIServerClient {
|
|
|
237
296
|
if (role) {
|
|
238
297
|
creds = creds.filter(c => c.role === role);
|
|
239
298
|
}
|
|
240
|
-
return
|
|
299
|
+
return {
|
|
300
|
+
pageInfo: makePageInfo(pagination.page, pagination.pageSize, response?.count ?? 0, response?.next),
|
|
301
|
+
credentials: creds,
|
|
302
|
+
};
|
|
241
303
|
}
|
|
242
304
|
/**
|
|
243
305
|
* Create a credential on an environment. password is write-only — never echoed back.
|
|
@@ -48,14 +48,13 @@ export const createWorkflowsService = (tx) => {
|
|
|
48
48
|
return response;
|
|
49
49
|
},
|
|
50
50
|
async listExecutions(filters) {
|
|
51
|
-
const
|
|
51
|
+
const { makePageInfo } = await import('../utils/pagination.js');
|
|
52
|
+
const params = { page: filters.page, pageSize: filters.pageSize };
|
|
52
53
|
if (filters.status)
|
|
53
54
|
params.status = filters.status;
|
|
54
|
-
if (filters.limit)
|
|
55
|
-
params.pageSize = filters.limit; // backend uses page_size (snake_case via transport)
|
|
56
55
|
const response = await tx.get('api/v1/workflows/executions/', params);
|
|
57
56
|
return {
|
|
58
|
-
|
|
57
|
+
pageInfo: makePageInfo(filters.page, filters.pageSize, response?.count ?? 0, response?.next),
|
|
59
58
|
executions: (response?.results ?? []).map((e) => ({
|
|
60
59
|
uuid: e.uuid,
|
|
61
60
|
workflow: e.workflow,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ListCredentialsInputSchema } from '../types/index.js';
|
|
2
2
|
import { listCredentialsHandler } from '../handlers/listCredentialsHandler.js';
|
|
3
|
-
const DESCRIPTION = `List credentials for a DebuggAI project.
|
|
3
|
+
const DESCRIPTION = `List credentials for a DebuggAI project. Paginated when scoped to a single environment (pass environmentId); otherwise iterates all envs and returns everything with pageInfo reflecting the total. Default pageSize 20, max 200. Optional q filters label/username (client-side); role filters server-side. Never returns passwords.`;
|
|
4
4
|
export function buildListCredentialsTool() {
|
|
5
5
|
return {
|
|
6
6
|
name: 'list_credentials',
|
|
@@ -9,22 +9,12 @@ export function buildListCredentialsTool() {
|
|
|
9
9
|
inputSchema: {
|
|
10
10
|
type: 'object',
|
|
11
11
|
properties: {
|
|
12
|
-
environmentId: {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
},
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
description: 'Optional: UUID of the target project. Defaults to the project resolved from the current git repo.',
|
|
19
|
-
},
|
|
20
|
-
q: {
|
|
21
|
-
type: 'string',
|
|
22
|
-
description: 'Optional: filter by label or username (case-insensitive substring).',
|
|
23
|
-
},
|
|
24
|
-
role: {
|
|
25
|
-
type: 'string',
|
|
26
|
-
description: 'Optional: filter by exact role match (e.g. "admin", "guest").',
|
|
27
|
-
},
|
|
12
|
+
environmentId: { type: 'string', description: 'Optional: filter to a single environment. Required for true pagination.' },
|
|
13
|
+
projectUuid: { type: 'string', description: 'Optional: UUID of the target project. Defaults to git-auto-detect.' },
|
|
14
|
+
q: { type: 'string', description: 'Optional: filter by label or username.' },
|
|
15
|
+
role: { type: 'string', description: 'Optional: filter by exact role match.' },
|
|
16
|
+
page: { type: 'number', description: 'Optional: 1-indexed page number. Default 1.', minimum: 1 },
|
|
17
|
+
pageSize: { type: 'number', description: 'Optional: items per page. Default 20, max 200.', minimum: 1, maximum: 200 },
|
|
28
18
|
},
|
|
29
19
|
additionalProperties: false,
|
|
30
20
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ListEnvironmentsInputSchema } from '../types/index.js';
|
|
2
2
|
import { listEnvironmentsHandler } from '../handlers/listEnvironmentsHandler.js';
|
|
3
|
-
const DESCRIPTION = `List environments for a DebuggAI project. By default targets the project resolved from the current git repo; pass projectUuid to target a different project
|
|
3
|
+
const DESCRIPTION = `List environments for a DebuggAI project. Paginated — every response includes pageInfo {page, pageSize, totalCount, totalPages, hasMore}; default pageSize 20, max 200. By default targets the project resolved from the current git repo; pass projectUuid to target a different project. Optional q filters by environment name via backend search.`;
|
|
4
4
|
export function buildListEnvironmentsTool() {
|
|
5
5
|
return {
|
|
6
6
|
name: 'list_environments',
|
|
@@ -9,14 +9,10 @@ export function buildListEnvironmentsTool() {
|
|
|
9
9
|
inputSchema: {
|
|
10
10
|
type: 'object',
|
|
11
11
|
properties: {
|
|
12
|
-
projectUuid: {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
},
|
|
16
|
-
q: {
|
|
17
|
-
type: 'string',
|
|
18
|
-
description: 'Optional: filter environments by name (server-side search).',
|
|
19
|
-
},
|
|
12
|
+
projectUuid: { type: 'string', description: 'Optional: UUID of the project to query. Defaults to git-auto-detect.' },
|
|
13
|
+
q: { type: 'string', description: 'Optional: filter by environment name.' },
|
|
14
|
+
page: { type: 'number', description: 'Optional: 1-indexed page number. Default 1.', minimum: 1 },
|
|
15
|
+
pageSize: { type: 'number', description: 'Optional: items per page. Default 20, max 200.', minimum: 1, maximum: 200 },
|
|
20
16
|
},
|
|
21
17
|
additionalProperties: false,
|
|
22
18
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ListExecutionsInputSchema } from '../types/index.js';
|
|
2
2
|
import { listExecutionsHandler } from '../handlers/listExecutionsHandler.js';
|
|
3
|
-
const DESCRIPTION = `List workflow execution history. Optional status filter (e.g. "completed", "running", "failed", "cancelled") passed to backend ?status=.
|
|
3
|
+
const DESCRIPTION = `List workflow execution history. Paginated — every response includes pageInfo {page, pageSize, totalCount, totalPages, hasMore}; default pageSize 20, max 200. Optional status filter (e.g. "completed", "running", "failed", "cancelled") passed to backend ?status=. Returns summary shape; use get_execution for full detail on a single uuid.`;
|
|
4
4
|
export function buildListExecutionsTool() {
|
|
5
5
|
return {
|
|
6
6
|
name: 'list_executions',
|
|
@@ -10,7 +10,8 @@ export function buildListExecutionsTool() {
|
|
|
10
10
|
type: 'object',
|
|
11
11
|
properties: {
|
|
12
12
|
status: { type: 'string', description: 'Optional: filter by execution status.' },
|
|
13
|
-
|
|
13
|
+
page: { type: 'number', description: 'Optional: 1-indexed page number. Default 1.', minimum: 1 },
|
|
14
|
+
pageSize: { type: 'number', description: 'Optional: items per page. Default 20, max 200.', minimum: 1, maximum: 200 },
|
|
14
15
|
},
|
|
15
16
|
additionalProperties: false,
|
|
16
17
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ListProjectsInputSchema } from '../types/index.js';
|
|
2
2
|
import { listProjectsHandler } from '../handlers/listProjectsHandler.js';
|
|
3
|
-
const DESCRIPTION = `List DebuggAI projects accessible to the current API key. Optional "q" input filters by project name or repo name
|
|
3
|
+
const DESCRIPTION = `List DebuggAI projects accessible to the current API key. Paginated — every response includes pageInfo {page, pageSize, totalCount, totalPages, hasMore}; default pageSize 20, max 200. Optional "q" input filters by project name or repo name via backend search. Use this when you don't know which project to target or when the current git repo doesn't resolve to a DebuggAI project.`;
|
|
4
4
|
export function buildListProjectsTool() {
|
|
5
5
|
return {
|
|
6
6
|
name: 'list_projects',
|
|
@@ -9,10 +9,9 @@ export function buildListProjectsTool() {
|
|
|
9
9
|
inputSchema: {
|
|
10
10
|
type: 'object',
|
|
11
11
|
properties: {
|
|
12
|
-
q: {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
},
|
|
12
|
+
q: { type: 'string', description: 'Optional: search by name or repo name.' },
|
|
13
|
+
page: { type: 'number', description: 'Optional: 1-indexed page number. Default 1.', minimum: 1 },
|
|
14
|
+
pageSize: { type: 'number', description: 'Optional: items per page. Default 20, max 200.', minimum: 1, maximum: 200 },
|
|
16
15
|
},
|
|
17
16
|
additionalProperties: false,
|
|
18
17
|
},
|
package/dist/types/index.js
CHANGED
|
@@ -20,6 +20,8 @@ export const TestPageChangesInputSchema = z.object({
|
|
|
20
20
|
export const ListEnvironmentsInputSchema = z.object({
|
|
21
21
|
projectUuid: z.string().uuid().optional(),
|
|
22
22
|
q: z.string().min(1).optional(),
|
|
23
|
+
page: z.number().int().min(1).optional(),
|
|
24
|
+
pageSize: z.number().int().min(1).optional(),
|
|
23
25
|
}).strict();
|
|
24
26
|
export const CreateEnvironmentInputSchema = z.object({
|
|
25
27
|
name: z.string().min(1, 'name is required'),
|
|
@@ -74,7 +76,8 @@ export const DeleteProjectInputSchema = z.object({
|
|
|
74
76
|
}).strict();
|
|
75
77
|
export const ListExecutionsInputSchema = z.object({
|
|
76
78
|
status: z.string().min(1).optional(),
|
|
77
|
-
|
|
79
|
+
page: z.number().int().min(1).optional(),
|
|
80
|
+
pageSize: z.number().int().min(1).optional(),
|
|
78
81
|
}).strict();
|
|
79
82
|
export const GetExecutionInputSchema = z.object({
|
|
80
83
|
uuid: z.string().uuid(),
|
|
@@ -87,6 +90,8 @@ export const ListCredentialsInputSchema = z.object({
|
|
|
87
90
|
projectUuid: z.string().uuid().optional(),
|
|
88
91
|
q: z.string().min(1).optional(),
|
|
89
92
|
role: z.string().min(1).optional(),
|
|
93
|
+
page: z.number().int().min(1).optional(),
|
|
94
|
+
pageSize: z.number().int().min(1).optional(),
|
|
90
95
|
}).strict();
|
|
91
96
|
export const CreateCredentialInputSchema = z.object({
|
|
92
97
|
environmentId: z.string().uuid(),
|
|
@@ -98,6 +103,8 @@ export const CreateCredentialInputSchema = z.object({
|
|
|
98
103
|
}).strict();
|
|
99
104
|
export const ListProjectsInputSchema = z.object({
|
|
100
105
|
q: z.string().min(1).optional(),
|
|
106
|
+
page: z.number().int().min(1).optional(),
|
|
107
|
+
pageSize: z.number().int().min(1).optional(),
|
|
101
108
|
}).strict();
|
|
102
109
|
/**
|
|
103
110
|
* Error types
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared pagination helpers for list_* tools. Every list tool must expose
|
|
3
|
+
* {page?, pageSize?} inputs and return a consistent pageInfo object so
|
|
4
|
+
* callers never silently get truncated results.
|
|
5
|
+
*/
|
|
6
|
+
export const DEFAULT_PAGE_SIZE = 20;
|
|
7
|
+
export const MAX_PAGE_SIZE = 200;
|
|
8
|
+
/**
|
|
9
|
+
* Translate caller inputs into safe backend query params.
|
|
10
|
+
* Defaults page=1, pageSize=20. Clamps pageSize to [1, MAX_PAGE_SIZE=200].
|
|
11
|
+
* Returns snake_case keys matching the backend's `?page=&page_size=` convention.
|
|
12
|
+
*/
|
|
13
|
+
export function toPaginationParams(input) {
|
|
14
|
+
const page = Math.max(1, Math.floor(input.page ?? 1));
|
|
15
|
+
const pageSize = Math.min(MAX_PAGE_SIZE, Math.max(1, Math.floor(input.pageSize ?? DEFAULT_PAGE_SIZE)));
|
|
16
|
+
return { page, pageSize };
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Build the pageInfo block for the MCP response from the backend's
|
|
20
|
+
* DRF-style list envelope (count + next + previous).
|
|
21
|
+
*/
|
|
22
|
+
export function makePageInfo(page, pageSize, count, next) {
|
|
23
|
+
const totalCount = count ?? 0;
|
|
24
|
+
const totalPages = totalCount === 0 ? 0 : Math.ceil(totalCount / pageSize);
|
|
25
|
+
return {
|
|
26
|
+
page,
|
|
27
|
+
pageSize,
|
|
28
|
+
totalCount,
|
|
29
|
+
totalPages,
|
|
30
|
+
hasMore: !!next,
|
|
31
|
+
};
|
|
32
|
+
}
|