@debugg-ai/debugg-ai-mcp 1.0.57 → 1.0.59
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 +23 -1
- package/dist/handlers/createProjectHandler.js +31 -0
- package/dist/handlers/index.js +3 -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/handlers/listReposHandler.js +27 -0
- package/dist/handlers/listTeamsHandler.js +27 -0
- package/dist/services/index.js +128 -11
- package/dist/services/workflows.js +3 -4
- package/dist/tools/createProject.js +25 -0
- package/dist/tools/index.js +9 -0
- 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/tools/listRepos.js +23 -0
- package/dist/tools/listTeams.js +23 -0
- package/dist/types/index.js +24 -1
- package/dist/utils/pagination.js +32 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ docker run -i --rm --init -e DEBUGGAI_API_KEY=your_api_key quinnosha/debugg-ai-m
|
|
|
32
32
|
|
|
33
33
|
## Tools
|
|
34
34
|
|
|
35
|
-
The server exposes **
|
|
35
|
+
The server exposes **21** tools. The headline one is `check_app_in_browser`; the rest manage projects, environments, credentials, workflow execution history, and the teams/repos needed to create new projects.
|
|
36
36
|
|
|
37
37
|
### `check_app_in_browser`
|
|
38
38
|
|
|
@@ -55,9 +55,17 @@ Runs an AI browser agent against your app. The agent navigates, interacts, and r
|
|
|
55
55
|
|------|---------|
|
|
56
56
|
| `list_projects` | List projects accessible to your API key. Optional `q` for name/repo search. |
|
|
57
57
|
| `get_project` | Fetch a project by `uuid`. Simplified shape (no team/runner internals). |
|
|
58
|
+
| `create_project` | Create a new project. Needs `name`, `platform` (e.g. `web`), `teamUuid` (from `list_teams`), and `repoUuid` (from `list_repos`). |
|
|
58
59
|
| `update_project` | PATCH a project's `name` or `description`. |
|
|
59
60
|
| `delete_project` | Destructive delete. Cascades envs, creds, and history. |
|
|
60
61
|
|
|
62
|
+
### Teams and repos (prerequisites for `create_project`)
|
|
63
|
+
|
|
64
|
+
| Tool | Purpose |
|
|
65
|
+
|------|---------|
|
|
66
|
+
| `list_teams` | Paginated list of teams accessible to the API key; optional `q` for search. |
|
|
67
|
+
| `list_repos` | Paginated list of GitHub-linked repos; optional `q` for search. Use repos with `isGithubAuthorized: true` when creating a project. |
|
|
68
|
+
|
|
61
69
|
### Environment management (scoped to a project)
|
|
62
70
|
|
|
63
71
|
| Tool | Purpose |
|
|
@@ -86,6 +94,20 @@ Runs an AI browser agent against your app. The agent navigates, interacts, and r
|
|
|
86
94
|
| `get_execution` | Full detail for a single execution including node-level state. |
|
|
87
95
|
| `cancel_execution` | Cancel an in-flight execution. |
|
|
88
96
|
|
|
97
|
+
### Pagination
|
|
98
|
+
|
|
99
|
+
Every `list_*` tool is paginated by default. Response shape:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"filter": { "...echoed query params..." },
|
|
104
|
+
"pageInfo": { "page": 1, "pageSize": 20, "totalCount": 47, "totalPages": 3, "hasMore": true },
|
|
105
|
+
"<items>": [ ... ]
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
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.
|
|
110
|
+
|
|
89
111
|
### Security invariants
|
|
90
112
|
|
|
91
113
|
- Passwords are write-only. They never appear in any response body from any tool.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Logger } from '../utils/logger.js';
|
|
2
|
+
import { handleExternalServiceError } from '../utils/errors.js';
|
|
3
|
+
import { DebuggAIServerClient } from '../services/index.js';
|
|
4
|
+
import { config } from '../config/index.js';
|
|
5
|
+
const logger = new Logger({ module: 'createProjectHandler' });
|
|
6
|
+
export async function createProjectHandler(input, _context) {
|
|
7
|
+
const start = Date.now();
|
|
8
|
+
logger.toolStart('create_project', {
|
|
9
|
+
name: input.name,
|
|
10
|
+
platform: input.platform,
|
|
11
|
+
teamUuid: input.teamUuid,
|
|
12
|
+
repoUuid: input.repoUuid,
|
|
13
|
+
});
|
|
14
|
+
try {
|
|
15
|
+
const client = new DebuggAIServerClient(config.api.key);
|
|
16
|
+
await client.init();
|
|
17
|
+
const project = await client.createProject({
|
|
18
|
+
name: input.name,
|
|
19
|
+
platform: input.platform,
|
|
20
|
+
teamUuid: input.teamUuid,
|
|
21
|
+
repoUuid: input.repoUuid,
|
|
22
|
+
});
|
|
23
|
+
const payload = { created: true, project };
|
|
24
|
+
logger.toolComplete('create_project', Date.now() - start);
|
|
25
|
+
return { content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }] };
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
logger.toolError('create_project', error, Date.now() - start);
|
|
29
|
+
throw handleExternalServiceError(error, 'DebuggAI', 'create_project');
|
|
30
|
+
}
|
|
31
|
+
}
|
package/dist/handlers/index.js
CHANGED
|
@@ -16,3 +16,6 @@ export * from './deleteProjectHandler.js';
|
|
|
16
16
|
export * from './listExecutionsHandler.js';
|
|
17
17
|
export * from './getExecutionHandler.js';
|
|
18
18
|
export * from './cancelExecutionHandler.js';
|
|
19
|
+
export * from './listTeamsHandler.js';
|
|
20
|
+
export * from './listReposHandler.js';
|
|
21
|
+
export * from './createProjectHandler.js';
|
|
@@ -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,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Logger } from '../utils/logger.js';
|
|
2
|
+
import { handleExternalServiceError } from '../utils/errors.js';
|
|
3
|
+
import { DebuggAIServerClient } from '../services/index.js';
|
|
4
|
+
import { config } from '../config/index.js';
|
|
5
|
+
import { toPaginationParams } from '../utils/pagination.js';
|
|
6
|
+
const logger = new Logger({ module: 'listReposHandler' });
|
|
7
|
+
export async function listReposHandler(input, _context) {
|
|
8
|
+
const start = Date.now();
|
|
9
|
+
const pagination = toPaginationParams({ page: input.page, pageSize: input.pageSize });
|
|
10
|
+
logger.toolStart('list_repos', { q: input.q, ...pagination });
|
|
11
|
+
try {
|
|
12
|
+
const client = new DebuggAIServerClient(config.api.key);
|
|
13
|
+
await client.init();
|
|
14
|
+
const { pageInfo, repos } = await client.listRepos(pagination, input.q);
|
|
15
|
+
const payload = {
|
|
16
|
+
filter: { q: input.q ?? null },
|
|
17
|
+
pageInfo,
|
|
18
|
+
repos,
|
|
19
|
+
};
|
|
20
|
+
logger.toolComplete('list_repos', Date.now() - start);
|
|
21
|
+
return { content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }] };
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
logger.toolError('list_repos', error, Date.now() - start);
|
|
25
|
+
throw handleExternalServiceError(error, 'DebuggAI', 'list_repos');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Logger } from '../utils/logger.js';
|
|
2
|
+
import { handleExternalServiceError } from '../utils/errors.js';
|
|
3
|
+
import { DebuggAIServerClient } from '../services/index.js';
|
|
4
|
+
import { config } from '../config/index.js';
|
|
5
|
+
import { toPaginationParams } from '../utils/pagination.js';
|
|
6
|
+
const logger = new Logger({ module: 'listTeamsHandler' });
|
|
7
|
+
export async function listTeamsHandler(input, _context) {
|
|
8
|
+
const start = Date.now();
|
|
9
|
+
const pagination = toPaginationParams({ page: input.page, pageSize: input.pageSize });
|
|
10
|
+
logger.toolStart('list_teams', { q: input.q, ...pagination });
|
|
11
|
+
try {
|
|
12
|
+
const client = new DebuggAIServerClient(config.api.key);
|
|
13
|
+
await client.init();
|
|
14
|
+
const { pageInfo, teams } = await client.listTeams(pagination, input.q);
|
|
15
|
+
const payload = {
|
|
16
|
+
filter: { q: input.q ?? null },
|
|
17
|
+
pageInfo,
|
|
18
|
+
teams,
|
|
19
|
+
};
|
|
20
|
+
logger.toolComplete('list_teams', Date.now() - start);
|
|
21
|
+
return { content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }] };
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
logger.toolError('list_teams', error, Date.now() - start);
|
|
25
|
+
throw handleExternalServiceError(error, 'DebuggAI', 'list_teams');
|
|
26
|
+
}
|
|
27
|
+
}
|
package/dist/services/index.js
CHANGED
|
@@ -116,24 +116,89 @@ 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
|
+
};
|
|
134
|
+
}
|
|
135
|
+
async listTeams(pagination, q) {
|
|
136
|
+
if (!this.tx)
|
|
137
|
+
throw new Error('Client not initialized — call init() first');
|
|
138
|
+
const { makePageInfo } = await import('../utils/pagination.js');
|
|
139
|
+
const params = { page: pagination.page, pageSize: pagination.pageSize };
|
|
140
|
+
if (q)
|
|
141
|
+
params.search = q;
|
|
142
|
+
const response = await this.tx.get('api/v1/teams/', params);
|
|
143
|
+
return {
|
|
144
|
+
pageInfo: makePageInfo(pagination.page, pagination.pageSize, response?.count ?? 0, response?.next),
|
|
145
|
+
teams: (response?.results ?? []).map((t) => ({
|
|
146
|
+
uuid: t.uuid,
|
|
147
|
+
name: t.name,
|
|
148
|
+
description: t.description ?? null,
|
|
149
|
+
memberCount: t.memberCount ?? 0,
|
|
150
|
+
ownerCount: t.ownerCount ?? 0,
|
|
151
|
+
currentUserRole: t.currentUserRole ?? null,
|
|
152
|
+
})),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
async listRepos(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/repos/', params);
|
|
163
|
+
return {
|
|
164
|
+
pageInfo: makePageInfo(pagination.page, pagination.pageSize, response?.count ?? 0, response?.next),
|
|
165
|
+
repos: (response?.results ?? []).map((r) => ({
|
|
166
|
+
uuid: r.uuid,
|
|
167
|
+
name: r.name,
|
|
168
|
+
url: r.url ?? '',
|
|
169
|
+
description: r.description ?? null,
|
|
170
|
+
isPrivate: !!r.isPrivate,
|
|
171
|
+
isGithubAuthorized: !!r.isGithubAuthorized,
|
|
172
|
+
githubAccountLogin: r.githubAccountLogin ?? null,
|
|
173
|
+
})),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
async createProject(input) {
|
|
177
|
+
if (!this.tx)
|
|
178
|
+
throw new Error('Client not initialized — call init() first');
|
|
179
|
+
// Backend expects `team` and `repo` keys (UUIDs). MCP surfaces them as
|
|
180
|
+
// teamUuid/repoUuid for clarity about what kind of UUID they are.
|
|
181
|
+
const body = {
|
|
182
|
+
name: input.name,
|
|
183
|
+
platform: input.platform,
|
|
184
|
+
team: input.teamUuid,
|
|
185
|
+
repo: input.repoUuid,
|
|
186
|
+
};
|
|
187
|
+
const p = await this.tx.post('api/v1/projects/', body);
|
|
188
|
+
return this.mapProjectDetail(p);
|
|
129
189
|
}
|
|
130
190
|
/**
|
|
131
|
-
* List environments for a project.
|
|
191
|
+
* List environments for a project. Paginated.
|
|
192
|
+
* Optional q filters by name via backend ?search=.
|
|
193
|
+
* The bare-array variant (no pagination) is still used internally by
|
|
194
|
+
* list_credentials when iterating across all envs.
|
|
132
195
|
*/
|
|
133
196
|
async listEnvironmentsForProject(projectUuid, q) {
|
|
134
197
|
if (!this.tx)
|
|
135
198
|
throw new Error('Client not initialized — call init() first');
|
|
136
|
-
const params =
|
|
199
|
+
const params = { pageSize: 200 };
|
|
200
|
+
if (q)
|
|
201
|
+
params.search = q;
|
|
137
202
|
const response = await this.tx.get(`api/v1/projects/${projectUuid}/environments/`, params);
|
|
138
203
|
return (response?.results ?? []).map((e) => ({
|
|
139
204
|
uuid: e.uuid,
|
|
@@ -142,6 +207,24 @@ export class DebuggAIServerClient {
|
|
|
142
207
|
isActive: e.isActive,
|
|
143
208
|
}));
|
|
144
209
|
}
|
|
210
|
+
async listEnvironmentsPaginated(projectUuid, pagination, q) {
|
|
211
|
+
if (!this.tx)
|
|
212
|
+
throw new Error('Client not initialized — call init() first');
|
|
213
|
+
const { makePageInfo } = await import('../utils/pagination.js');
|
|
214
|
+
const params = { page: pagination.page, pageSize: pagination.pageSize };
|
|
215
|
+
if (q)
|
|
216
|
+
params.search = q;
|
|
217
|
+
const response = await this.tx.get(`api/v1/projects/${projectUuid}/environments/`, params);
|
|
218
|
+
return {
|
|
219
|
+
pageInfo: makePageInfo(pagination.page, pagination.pageSize, response?.count ?? 0, response?.next),
|
|
220
|
+
environments: (response?.results ?? []).map((e) => ({
|
|
221
|
+
uuid: e.uuid,
|
|
222
|
+
name: e.name,
|
|
223
|
+
url: e.url || e.activeUrl || '',
|
|
224
|
+
isActive: e.isActive,
|
|
225
|
+
})),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
145
228
|
/**
|
|
146
229
|
* Create a new environment under a project.
|
|
147
230
|
* Backend requires `name`. Other fields optional.
|
|
@@ -213,13 +296,44 @@ export class DebuggAIServerClient {
|
|
|
213
296
|
};
|
|
214
297
|
}
|
|
215
298
|
/**
|
|
216
|
-
* List credentials for a specific environment.
|
|
217
|
-
*
|
|
299
|
+
* List credentials for a specific environment. Unpaginated (fetches up to
|
|
300
|
+
* backend max pageSize). q filters label/username client-side (backend
|
|
301
|
+
* ?search= is inconsistent on this endpoint); role filters server-side.
|
|
302
|
+
* Used internally by list_credentials when iterating across envs.
|
|
218
303
|
*/
|
|
219
304
|
async listCredentialsForEnvironment(projectUuid, envUuid, q, role) {
|
|
220
305
|
if (!this.tx)
|
|
221
306
|
throw new Error('Client not initialized — call init() first');
|
|
222
|
-
const
|
|
307
|
+
const params = { pageSize: 200 };
|
|
308
|
+
if (role)
|
|
309
|
+
params.role = role;
|
|
310
|
+
const response = await this.tx.get(`api/v1/projects/${projectUuid}/environments/${envUuid}/credentials/`, params);
|
|
311
|
+
let creds = (response?.results ?? [])
|
|
312
|
+
.filter((c) => c.isActive)
|
|
313
|
+
.map((c) => ({
|
|
314
|
+
uuid: c.uuid,
|
|
315
|
+
label: c.label || c.username,
|
|
316
|
+
username: c.username,
|
|
317
|
+
role: c.role,
|
|
318
|
+
environmentUuid: envUuid,
|
|
319
|
+
}));
|
|
320
|
+
if (q) {
|
|
321
|
+
const needle = q.toLowerCase();
|
|
322
|
+
creds = creds.filter(c => c.label.toLowerCase().includes(needle) ||
|
|
323
|
+
c.username.toLowerCase().includes(needle));
|
|
324
|
+
}
|
|
325
|
+
return creds;
|
|
326
|
+
}
|
|
327
|
+
async listCredentialsPaginated(projectUuid, envUuid, pagination, q, role) {
|
|
328
|
+
if (!this.tx)
|
|
329
|
+
throw new Error('Client not initialized — call init() first');
|
|
330
|
+
const { makePageInfo } = await import('../utils/pagination.js');
|
|
331
|
+
const params = { page: pagination.page, pageSize: pagination.pageSize };
|
|
332
|
+
// Backend ?role= filter is currently ignored (bead hpo) — pass it anyway for future fix-forward,
|
|
333
|
+
// but re-apply the filter client-side so behavior is correct today.
|
|
334
|
+
if (role)
|
|
335
|
+
params.role = role;
|
|
336
|
+
const response = await this.tx.get(`api/v1/projects/${projectUuid}/environments/${envUuid}/credentials/`, params);
|
|
223
337
|
let creds = (response?.results ?? [])
|
|
224
338
|
.filter((c) => c.isActive)
|
|
225
339
|
.map((c) => ({
|
|
@@ -237,7 +351,10 @@ export class DebuggAIServerClient {
|
|
|
237
351
|
if (role) {
|
|
238
352
|
creds = creds.filter(c => c.role === role);
|
|
239
353
|
}
|
|
240
|
-
return
|
|
354
|
+
return {
|
|
355
|
+
pageInfo: makePageInfo(pagination.page, pagination.pageSize, response?.count ?? 0, response?.next),
|
|
356
|
+
credentials: creds,
|
|
357
|
+
};
|
|
241
358
|
}
|
|
242
359
|
/**
|
|
243
360
|
* 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,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { CreateProjectInputSchema } from '../types/index.js';
|
|
2
|
+
import { createProjectHandler } from '../handlers/createProjectHandler.js';
|
|
3
|
+
const DESCRIPTION = `Create a new DebuggAI project. Required: name, platform (e.g. "web"), teamUuid (from list_teams), repoUuid (from list_repos). Returns {created: true, project: {uuid, name, slug, platform, repoName, ...}}. The repo must be GitHub-linked to the account. Use list_teams + list_repos first to discover valid UUIDs.`;
|
|
4
|
+
export function buildCreateProjectTool() {
|
|
5
|
+
return {
|
|
6
|
+
name: 'create_project',
|
|
7
|
+
title: 'Create Project',
|
|
8
|
+
description: DESCRIPTION,
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
name: { type: 'string', description: 'Project name. Required.', minLength: 1 },
|
|
13
|
+
platform: { type: 'string', description: 'Platform (e.g. "web"). Required.', minLength: 1 },
|
|
14
|
+
teamUuid: { type: 'string', description: 'Team UUID (from list_teams). Required.' },
|
|
15
|
+
repoUuid: { type: 'string', description: 'GitHub repo UUID (from list_repos). Required — repo must be GitHub-linked.' },
|
|
16
|
+
},
|
|
17
|
+
required: ['name', 'platform', 'teamUuid', 'repoUuid'],
|
|
18
|
+
additionalProperties: false,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function buildValidatedCreateProjectTool() {
|
|
23
|
+
const tool = buildCreateProjectTool();
|
|
24
|
+
return { ...tool, inputSchema: CreateProjectInputSchema, handler: createProjectHandler };
|
|
25
|
+
}
|
package/dist/tools/index.js
CHANGED
|
@@ -16,6 +16,9 @@ import { buildDeleteProjectTool, buildValidatedDeleteProjectTool } from './delet
|
|
|
16
16
|
import { buildListExecutionsTool, buildValidatedListExecutionsTool } from './listExecutions.js';
|
|
17
17
|
import { buildGetExecutionTool, buildValidatedGetExecutionTool } from './getExecution.js';
|
|
18
18
|
import { buildCancelExecutionTool, buildValidatedCancelExecutionTool } from './cancelExecution.js';
|
|
19
|
+
import { buildListTeamsTool, buildValidatedListTeamsTool } from './listTeams.js';
|
|
20
|
+
import { buildListReposTool, buildValidatedListReposTool } from './listRepos.js';
|
|
21
|
+
import { buildCreateProjectTool, buildValidatedCreateProjectTool } from './createProject.js';
|
|
19
22
|
let _tools = null;
|
|
20
23
|
let _validatedTools = null;
|
|
21
24
|
const toolRegistry = new Map();
|
|
@@ -42,6 +45,9 @@ export function initTools(ctx) {
|
|
|
42
45
|
buildListExecutionsTool(),
|
|
43
46
|
buildGetExecutionTool(),
|
|
44
47
|
buildCancelExecutionTool(),
|
|
48
|
+
buildListTeamsTool(),
|
|
49
|
+
buildListReposTool(),
|
|
50
|
+
buildCreateProjectTool(),
|
|
45
51
|
];
|
|
46
52
|
const validated = [
|
|
47
53
|
buildValidatedTestPageChangesTool(ctx),
|
|
@@ -62,6 +68,9 @@ export function initTools(ctx) {
|
|
|
62
68
|
buildValidatedListExecutionsTool(),
|
|
63
69
|
buildValidatedGetExecutionTool(),
|
|
64
70
|
buildValidatedCancelExecutionTool(),
|
|
71
|
+
buildValidatedListTeamsTool(),
|
|
72
|
+
buildValidatedListReposTool(),
|
|
73
|
+
buildValidatedCreateProjectTool(),
|
|
65
74
|
];
|
|
66
75
|
_tools = tools;
|
|
67
76
|
_validatedTools = validated;
|
|
@@ -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
|
},
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ListReposInputSchema } from '../types/index.js';
|
|
2
|
+
import { listReposHandler } from '../handlers/listReposHandler.js';
|
|
3
|
+
const DESCRIPTION = `List GitHub repos linked to the current account. Paginated — every response includes pageInfo {page, pageSize, totalCount, totalPages, hasMore}; default pageSize 20, max 200. Optional q filters by repo name via backend search. Use this to discover repoUuid values required by create_project. Prefer repos with isGithubAuthorized:true since the backend needs a valid GitHub installation.`;
|
|
4
|
+
export function buildListReposTool() {
|
|
5
|
+
return {
|
|
6
|
+
name: 'list_repos',
|
|
7
|
+
title: 'List Linked Repos',
|
|
8
|
+
description: DESCRIPTION,
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
q: { type: 'string', description: 'Optional: filter by repo name.' },
|
|
13
|
+
page: { type: 'number', description: 'Optional: 1-indexed page number.', minimum: 1 },
|
|
14
|
+
pageSize: { type: 'number', description: 'Optional: items per page. Default 20, max 200.', minimum: 1, maximum: 200 },
|
|
15
|
+
},
|
|
16
|
+
additionalProperties: false,
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function buildValidatedListReposTool() {
|
|
21
|
+
const tool = buildListReposTool();
|
|
22
|
+
return { ...tool, inputSchema: ListReposInputSchema, handler: listReposHandler };
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ListTeamsInputSchema } from '../types/index.js';
|
|
2
|
+
import { listTeamsHandler } from '../handlers/listTeamsHandler.js';
|
|
3
|
+
const DESCRIPTION = `List teams accessible to the current API key. Paginated — every response includes pageInfo {page, pageSize, totalCount, totalPages, hasMore}; default pageSize 20, max 200. Optional q filters by team name via backend search. Use this to discover teamUuid values required by create_project.`;
|
|
4
|
+
export function buildListTeamsTool() {
|
|
5
|
+
return {
|
|
6
|
+
name: 'list_teams',
|
|
7
|
+
title: 'List Teams',
|
|
8
|
+
description: DESCRIPTION,
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
q: { type: 'string', description: 'Optional: filter by team name.' },
|
|
13
|
+
page: { type: 'number', description: 'Optional: 1-indexed page number.', minimum: 1 },
|
|
14
|
+
pageSize: { type: 'number', description: 'Optional: items per page. Default 20, max 200.', minimum: 1, maximum: 200 },
|
|
15
|
+
},
|
|
16
|
+
additionalProperties: false,
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function buildValidatedListTeamsTool() {
|
|
21
|
+
const tool = buildListTeamsTool();
|
|
22
|
+
return { ...tool, inputSchema: ListTeamsInputSchema, handler: listTeamsHandler };
|
|
23
|
+
}
|
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,24 @@ 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(),
|
|
108
|
+
}).strict();
|
|
109
|
+
export const ListTeamsInputSchema = z.object({
|
|
110
|
+
q: z.string().min(1).optional(),
|
|
111
|
+
page: z.number().int().min(1).optional(),
|
|
112
|
+
pageSize: z.number().int().min(1).optional(),
|
|
113
|
+
}).strict();
|
|
114
|
+
export const ListReposInputSchema = z.object({
|
|
115
|
+
q: z.string().min(1).optional(),
|
|
116
|
+
page: z.number().int().min(1).optional(),
|
|
117
|
+
pageSize: z.number().int().min(1).optional(),
|
|
118
|
+
}).strict();
|
|
119
|
+
export const CreateProjectInputSchema = z.object({
|
|
120
|
+
name: z.string().min(1),
|
|
121
|
+
platform: z.string().min(1),
|
|
122
|
+
teamUuid: z.string().uuid(),
|
|
123
|
+
repoUuid: z.string().uuid(),
|
|
101
124
|
}).strict();
|
|
102
125
|
/**
|
|
103
126
|
* 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
|
+
}
|