@arii/boomtick-mcp 0.6.0 → 0.6.2
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/.mcp/schemas/github.create_issue.json +72 -3
- package/.mcp/schemas/github.get_merge_conflict_files.json +40 -3
- package/.mcp/schemas/github.get_pr_diff.json +30 -3
- package/.mcp/schemas/github.issue_update.json +72 -3
- package/.mcp/schemas/github.search_open_prs.json +130 -3
- package/.mcp/schemas/github_create_issue.json +72 -3
- package/.mcp/schemas/github_get_merge_conflict_files.json +40 -3
- package/.mcp/schemas/github_get_pr_diff.json +30 -3
- package/.mcp/schemas/github_issue_update.json +72 -3
- package/.mcp/schemas/github_search_open_prs.json +130 -3
- package/.mcp/schemas/repo.check_jules_status.json +38 -0
- package/.mcp/schemas/repo.run_tests.json +31 -3
- package/.mcp/schemas/repo_check_jules_status.json +38 -0
- package/.mcp/schemas/repo_run_tests.json +31 -3
- package/CHANGELOG.md +15 -0
- package/README.md +22 -1
- package/actions/run-project-gate/action.yml +1 -1
- package/dist/config/dynamic.js +33 -0
- package/dist/config/github.js +16 -0
- package/dist/config/repo.js +28 -0
- package/dist/config.js +16 -81
- package/dist/http.js +10 -0
- package/dist/mcp/definitions.js +29 -7
- package/dist/mcp/http.js +18 -10
- package/dist/mcp/http.test.js +54 -4
- package/dist/mcp/server.js +47 -10
- package/dist/mcp/server.test.js +52 -1
- package/dist/tools/contract.js +77 -0
- package/dist/tools/github.comment_triage_summary.js +3 -10
- package/dist/tools/github.get_merge_conflict_files.js +4 -2
- package/dist/tools/github.get_pr_commits.js +1 -1
- package/dist/tools/github.get_pr_diff.js +3 -11
- package/dist/tools/github.get_pr_reviews.js +1 -1
- package/dist/tools/jules/cancel-session.js +2 -9
- package/dist/tools/jules/cli.js +31 -0
- package/dist/tools/jules/get-messages.js +2 -9
- package/dist/tools/jules/list-sessions.js +4 -35
- package/dist/tools/jules/send-message.js +2 -9
- package/dist/tools/jules/session.js +26 -0
- package/dist/tools/jules/shared.js +2 -50
- package/dist/tools/jules/trigger-feedback.js +2 -10
- package/dist/tools/repo.check_jules_status.js +148 -0
- package/dist/tools/repo.check_jules_status.test.js +47 -0
- package/dist/tools/repo.get_workflow_runs.js +1 -1
- package/dist/tools/repo.run_tests.js +13 -1
- package/package.json +3 -2
- package/scripts/sync-contracts.ts +11 -2
- package/src/config/dynamic.ts +36 -0
- package/src/config/github.ts +16 -0
- package/src/config/repo.ts +36 -0
- package/src/config.ts +16 -89
- package/src/http.ts +13 -0
- package/src/mcp/definitions.ts +34 -6
- package/src/mcp/http.test.ts +65 -5
- package/src/mcp/http.ts +20 -14
- package/src/mcp/server.test.ts +62 -1
- package/src/mcp/server.ts +48 -11
- package/src/tools/contract.ts +85 -0
- package/src/tools/github.comment_triage_summary.ts +3 -12
- package/src/tools/github.get_merge_conflict_files.ts +5 -2
- package/src/tools/github.get_pr_commits.ts +1 -1
- package/src/tools/github.get_pr_diff.ts +3 -13
- package/src/tools/github.get_pr_reviews.ts +1 -1
- package/src/tools/jules/cancel-session.ts +2 -11
- package/src/tools/jules/cli.ts +42 -0
- package/src/tools/jules/get-messages.ts +2 -11
- package/src/tools/jules/list-sessions.ts +5 -39
- package/src/tools/jules/send-message.ts +2 -11
- package/src/tools/jules/session.ts +28 -0
- package/src/tools/jules/shared.ts +2 -63
- package/src/tools/jules/trigger-feedback.ts +2 -13
- package/src/tools/repo.check_jules_status.test.ts +55 -0
- package/src/tools/repo.check_jules_status.ts +176 -0
- package/src/tools/repo.get_workflow_runs.ts +1 -1
- package/src/tools/repo.run_tests.ts +14 -1
package/dist/mcp/definitions.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HealthCheckInputJsonSchema, SearchPRsInputJsonSchema, GetPrDiffInputJsonSchema, CheckoutBranchInputJsonSchema, GetMergeConflictFilesInputJsonSchema, GetChangedFilesInputJsonSchema, GetPackageScriptsInputJsonSchema, ReadCiLogsInputJsonSchema, RepoLogsInputJsonSchema, CreateBranchInputJsonSchema, CreateRepairBranchInputJsonSchema, RunTestsInputJsonSchema, CommitPatchInputJsonSchema, OpenReplacementPrInputJsonSchema, CreatePullRequestInputJsonSchema, CommentTriageSummaryInputJsonSchema, GetPrInputJsonSchema, IssueViewInputJsonSchema, IssueUpdateInputJsonSchema, IssueCommentInputJsonSchema, CreateIssueInputJsonSchema, GetCommandSchemaInputJsonSchema, ReadAgentContextInputJsonSchema as ReadAgentContextSchema, CreateJulesSessionInputJsonSchema, JulesSessionIdInputJsonSchema, JulesSendMessageInputJsonSchema, JulesListSessionsInputJsonSchema, GetPrReviewsInputJsonSchema, GetPrCommitsInputJsonSchema, GetWorkflowRunsInputJsonSchema, } from "../tools/contract.js";
|
|
1
|
+
import { HealthCheckInputJsonSchema, SearchPRsInputJsonSchema, SearchPRsResponseJsonSchema, GetPrDiffInputJsonSchema, PrDiffResponseJsonSchema, CheckoutBranchInputJsonSchema, GetMergeConflictFilesInputJsonSchema, GetMergeConflictFilesResponseJsonSchema, GetChangedFilesInputJsonSchema, GetPackageScriptsInputJsonSchema, ReadCiLogsInputJsonSchema, RepoLogsInputJsonSchema, CreateBranchInputJsonSchema, CreateRepairBranchInputJsonSchema, RunTestsInputJsonSchema, RunTestsResponseJsonSchema, CommitPatchInputJsonSchema, OpenReplacementPrInputJsonSchema, CreatePullRequestInputJsonSchema, CommentTriageSummaryInputJsonSchema, GetPrInputJsonSchema, IssueViewInputJsonSchema, IssueUpdateInputJsonSchema, IssueUpdateResponseJsonSchema, IssueCommentInputJsonSchema, CreateIssueInputJsonSchema, CreateIssueResponseJsonSchema, GetCommandSchemaInputJsonSchema, ReadAgentContextInputJsonSchema as ReadAgentContextSchema, CreateJulesSessionInputJsonSchema, JulesSessionIdInputJsonSchema, JulesSendMessageInputJsonSchema, JulesListSessionsInputJsonSchema, GetPrReviewsInputJsonSchema, GetPrCommitsInputJsonSchema, GetWorkflowRunsInputJsonSchema, } from "../tools/contract.js";
|
|
2
2
|
export const MCP_PROMPTS = [
|
|
3
3
|
{
|
|
4
4
|
name: "conflict-scout",
|
|
@@ -44,6 +44,12 @@ export const MCP_RESOURCES = [
|
|
|
44
44
|
mimeType: "application/json",
|
|
45
45
|
description: "List of changed files in the repository.",
|
|
46
46
|
},
|
|
47
|
+
{
|
|
48
|
+
uri: "mcp://ci/latest-run",
|
|
49
|
+
name: "Latest CI Run",
|
|
50
|
+
mimeType: "application/json",
|
|
51
|
+
description: "Recent CI exit statuses, workflow runs, and annotations.",
|
|
52
|
+
},
|
|
47
53
|
{
|
|
48
54
|
uri: "repo://package-json",
|
|
49
55
|
name: "package.json",
|
|
@@ -93,7 +99,7 @@ const BASE_MCP_TOOLS = [
|
|
|
93
99
|
name: "github.search_open_prs",
|
|
94
100
|
description: "Search for open pull requests in the repository.",
|
|
95
101
|
inputSchema: SearchPRsInputJsonSchema,
|
|
96
|
-
outputSchema:
|
|
102
|
+
outputSchema: SearchPRsResponseJsonSchema,
|
|
97
103
|
},
|
|
98
104
|
{
|
|
99
105
|
name: "repo.read_agent_context",
|
|
@@ -105,7 +111,7 @@ const BASE_MCP_TOOLS = [
|
|
|
105
111
|
name: "github.get_pr_diff",
|
|
106
112
|
description: "Get the diff and changed files for a pull request.",
|
|
107
113
|
inputSchema: GetPrDiffInputJsonSchema,
|
|
108
|
-
outputSchema:
|
|
114
|
+
outputSchema: PrDiffResponseJsonSchema,
|
|
109
115
|
},
|
|
110
116
|
{
|
|
111
117
|
name: "github.checkout_branch",
|
|
@@ -117,7 +123,7 @@ const BASE_MCP_TOOLS = [
|
|
|
117
123
|
name: "github.get_merge_conflict_files",
|
|
118
124
|
description: "Detect files that conflict when a PR is merged with the base branch.",
|
|
119
125
|
inputSchema: GetMergeConflictFilesInputJsonSchema,
|
|
120
|
-
outputSchema:
|
|
126
|
+
outputSchema: GetMergeConflictFilesResponseJsonSchema,
|
|
121
127
|
},
|
|
122
128
|
{
|
|
123
129
|
name: "repo.get_changed_files",
|
|
@@ -161,11 +167,27 @@ const BASE_MCP_TOOLS = [
|
|
|
161
167
|
inputSchema: CreateRepairBranchInputJsonSchema,
|
|
162
168
|
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
163
169
|
},
|
|
170
|
+
{
|
|
171
|
+
name: "repo.check_jules_status",
|
|
172
|
+
description: "Check or poll the status of a Jules remediation session and verify its branch or PR status.",
|
|
173
|
+
inputSchema: {
|
|
174
|
+
type: "object",
|
|
175
|
+
properties: {
|
|
176
|
+
sessionId: { type: "string", description: "The unique ID or task ID of the Jules session." },
|
|
177
|
+
commitSha: { type: "string", description: "Commit SHA for idempotency key verification." },
|
|
178
|
+
failureLog: { type: "string", description: "Failure log content for idempotency key calculation." },
|
|
179
|
+
poll: { type: "boolean", description: "Whether to poll until session completion or timeout." },
|
|
180
|
+
timeoutMs: { type: "integer", description: "Timeout in milliseconds when polling." },
|
|
181
|
+
intervalMs: { type: "integer", description: "Polling interval in milliseconds." },
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
185
|
+
},
|
|
164
186
|
{
|
|
165
187
|
name: "repo.run_tests",
|
|
166
188
|
description: "Run repository tests and checks.",
|
|
167
189
|
inputSchema: RunTestsInputJsonSchema,
|
|
168
|
-
outputSchema:
|
|
190
|
+
outputSchema: RunTestsResponseJsonSchema,
|
|
169
191
|
},
|
|
170
192
|
{
|
|
171
193
|
name: "repo.commit_patch",
|
|
@@ -225,7 +247,7 @@ const BASE_MCP_TOOLS = [
|
|
|
225
247
|
name: "github.issue_update",
|
|
226
248
|
description: "Update a GitHub issue's body, labels, and/or state.",
|
|
227
249
|
inputSchema: IssueUpdateInputJsonSchema,
|
|
228
|
-
outputSchema:
|
|
250
|
+
outputSchema: IssueUpdateResponseJsonSchema,
|
|
229
251
|
},
|
|
230
252
|
{
|
|
231
253
|
name: "github.issue_comment",
|
|
@@ -237,7 +259,7 @@ const BASE_MCP_TOOLS = [
|
|
|
237
259
|
name: "github.create_issue",
|
|
238
260
|
description: "Create a new GitHub issue.",
|
|
239
261
|
inputSchema: CreateIssueInputJsonSchema,
|
|
240
|
-
outputSchema:
|
|
262
|
+
outputSchema: CreateIssueResponseJsonSchema,
|
|
241
263
|
},
|
|
242
264
|
{
|
|
243
265
|
name: "jules.create_session",
|
package/dist/mcp/http.js
CHANGED
|
@@ -7,7 +7,7 @@ export function createMcpHttpServer(options) {
|
|
|
7
7
|
const server = http.createServer(async (req, res) => {
|
|
8
8
|
// CORS Headers
|
|
9
9
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
10
|
-
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
10
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, HEAD");
|
|
11
11
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
12
12
|
if (req.method === "OPTIONS") {
|
|
13
13
|
res.writeHead(204);
|
|
@@ -15,19 +15,27 @@ export function createMcpHttpServer(options) {
|
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
17
17
|
const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
|
|
18
|
-
// Endpoint 1: OAuth Protected Resource
|
|
19
|
-
if (url.pathname === "/.well-known/oauth-protected-resource" && req.method === "GET") {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"authorization_servers": [
|
|
18
|
+
// Endpoint 1: RFC 9728 OAuth Protected Resource Metadata Discovery
|
|
19
|
+
if (url.pathname === "/.well-known/oauth-protected-resource" && (req.method === "GET" || req.method === "HEAD")) {
|
|
20
|
+
const metadata = {
|
|
21
|
+
resource: process.env.MCP_RESOURCE_URI || process.env.BOOMTICK_RESOURCE_URI || "mcp://boomtick-mcp",
|
|
22
|
+
authorization_servers: [
|
|
24
23
|
process.env.MCP_OAUTH_AUTH_SERVER || "https://auth.boomtick.dev",
|
|
25
24
|
],
|
|
26
25
|
scopes_supported: ["mcp:read", "mcp:write"],
|
|
27
|
-
|
|
26
|
+
bearer_methods_supported: ["header"],
|
|
27
|
+
resource_documentation: process.env.MCP_RESOURCE_DOCS || "https://github.com/arii/boomtick",
|
|
28
|
+
};
|
|
29
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
30
|
+
if (req.method === "HEAD") {
|
|
31
|
+
res.end();
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
res.end(JSON.stringify(metadata, null, 2));
|
|
35
|
+
}
|
|
28
36
|
return;
|
|
29
37
|
}
|
|
30
|
-
// Bearer Token Validation for
|
|
38
|
+
// Bearer Token / OAuth 2.1 Validation for Protected Routes (/sse & /messages)
|
|
31
39
|
if (url.pathname === "/sse" || url.pathname === "/messages") {
|
|
32
40
|
if (!bearerToken) {
|
|
33
41
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
@@ -44,7 +52,7 @@ export function createMcpHttpServer(options) {
|
|
|
44
52
|
if (!isMatch) {
|
|
45
53
|
res.writeHead(401, {
|
|
46
54
|
"Content-Type": "application/json",
|
|
47
|
-
"WWW-Authenticate": 'Bearer realm="boomtick-mcp", error="invalid_token"',
|
|
55
|
+
"WWW-Authenticate": 'Bearer realm="boomtick-mcp", error="invalid_token", error_description="Invalid or missing Bearer token"',
|
|
48
56
|
});
|
|
49
57
|
res.end(JSON.stringify({ error: "unauthorized", message: "Invalid or missing Bearer token" }));
|
|
50
58
|
return;
|
package/dist/mcp/http.test.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
2
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
3
|
import { createMcpHttpServer } from "./http.js";
|
|
4
|
+
import { BoomtickMCPServer } from "./server.js";
|
|
4
5
|
describe("createMcpHttpServer", () => {
|
|
5
6
|
let mcpServer;
|
|
6
7
|
let httpServerObj;
|
|
@@ -13,29 +14,54 @@ describe("createMcpHttpServer", () => {
|
|
|
13
14
|
httpServerObj.server.close();
|
|
14
15
|
}
|
|
15
16
|
});
|
|
16
|
-
it("should respond with OAuth protected resource metadata on /.well-known/oauth-protected-resource", async () => {
|
|
17
|
+
it("should respond with RFC 9728 OAuth protected resource metadata on /.well-known/oauth-protected-resource", async () => {
|
|
17
18
|
httpServerObj = createMcpHttpServer({ port: 0, mcpServer });
|
|
18
19
|
port = await httpServerObj.listen(0);
|
|
19
20
|
const res = await fetch(`http://localhost:${port}/.well-known/oauth-protected-resource`);
|
|
20
21
|
expect(res.status).toBe(200);
|
|
22
|
+
expect(res.headers.get("content-type")).toContain("application/json");
|
|
21
23
|
const body = await res.json();
|
|
22
24
|
expect(body.resource).toBe("mcp://boomtick-mcp");
|
|
23
|
-
expect(body.authorization_servers).
|
|
25
|
+
expect(body.authorization_servers).toEqual(["https://auth.boomtick.dev"]);
|
|
24
26
|
expect(body.scopes_supported).toEqual(["mcp:read", "mcp:write"]);
|
|
27
|
+
expect(body.bearer_methods_supported).toEqual(["header"]);
|
|
28
|
+
expect(body.resource_documentation).toBe("https://github.com/arii/boomtick");
|
|
25
29
|
});
|
|
26
|
-
it("should
|
|
30
|
+
it("should handle HEAD requests for /.well-known/oauth-protected-resource", async () => {
|
|
31
|
+
httpServerObj = createMcpHttpServer({ port: 0, mcpServer });
|
|
32
|
+
port = await httpServerObj.listen(0);
|
|
33
|
+
const res = await fetch(`http://localhost:${port}/.well-known/oauth-protected-resource`, {
|
|
34
|
+
method: "HEAD",
|
|
35
|
+
});
|
|
36
|
+
expect(res.status).toBe(200);
|
|
37
|
+
const text = await res.text();
|
|
38
|
+
expect(text).toBe("");
|
|
39
|
+
});
|
|
40
|
+
it("should handle OPTIONS preflight CORS requests", async () => {
|
|
41
|
+
httpServerObj = createMcpHttpServer({ port: 0, mcpServer });
|
|
42
|
+
port = await httpServerObj.listen(0);
|
|
43
|
+
const res = await fetch(`http://localhost:${port}/sse`, {
|
|
44
|
+
method: "OPTIONS",
|
|
45
|
+
});
|
|
46
|
+
expect(res.status).toBe(204);
|
|
47
|
+
expect(res.headers.get("access-control-allow-origin")).toBe("*");
|
|
48
|
+
expect(res.headers.get("access-control-allow-methods")).toContain("GET");
|
|
49
|
+
expect(res.headers.get("access-control-allow-headers")).toContain("Authorization");
|
|
50
|
+
});
|
|
51
|
+
it("should reject protected requests when Bearer token is missing or invalid", async () => {
|
|
27
52
|
httpServerObj = createMcpHttpServer({ port: 0, bearerToken: "secret123", mcpServer });
|
|
28
53
|
port = await httpServerObj.listen(0);
|
|
29
54
|
// Request without token
|
|
30
55
|
const res1 = await fetch(`http://localhost:${port}/sse`);
|
|
31
56
|
expect(res1.status).toBe(401);
|
|
32
57
|
expect(res1.headers.get("www-authenticate")).toContain('Bearer realm="boomtick-mcp"');
|
|
58
|
+
expect(res1.headers.get("www-authenticate")).toContain('error="invalid_token"');
|
|
33
59
|
// Request with wrong token
|
|
34
60
|
const res2 = await fetch(`http://localhost:${port}/sse`, {
|
|
35
61
|
headers: { Authorization: "Bearer wrong" },
|
|
36
62
|
});
|
|
37
63
|
expect(res2.status).toBe(401);
|
|
38
|
-
//
|
|
64
|
+
// Public resource metadata accessible with or without token
|
|
39
65
|
const res3 = await fetch(`http://localhost:${port}/.well-known/oauth-protected-resource`, {
|
|
40
66
|
headers: { Authorization: "Bearer secret123" },
|
|
41
67
|
});
|
|
@@ -55,4 +81,28 @@ describe("createMcpHttpServer", () => {
|
|
|
55
81
|
const body = await res.json();
|
|
56
82
|
expect(body.error).toContain("Server misconfiguration");
|
|
57
83
|
});
|
|
84
|
+
it("should return 400 for message requests without session or invalid session", async () => {
|
|
85
|
+
httpServerObj = createMcpHttpServer({ port: 0, bearerToken: "secret123", mcpServer });
|
|
86
|
+
port = await httpServerObj.listen(0);
|
|
87
|
+
const res = await fetch(`http://localhost:${port}/messages?sessionId=invalid_session`, {
|
|
88
|
+
method: "POST",
|
|
89
|
+
headers: {
|
|
90
|
+
Authorization: "Bearer secret123",
|
|
91
|
+
"Content-Type": "application/json",
|
|
92
|
+
},
|
|
93
|
+
body: JSON.stringify({ jsonrpc: "2.0", method: "ping", id: 1 }),
|
|
94
|
+
});
|
|
95
|
+
expect(res.status).toBe(400);
|
|
96
|
+
const body = await res.json();
|
|
97
|
+
expect(body.error).toContain("Session not found or expired");
|
|
98
|
+
});
|
|
99
|
+
it("should allow BoomtickMCPServer to start HTTP server via run() when transport is http", async () => {
|
|
100
|
+
process.env.MCP_TRANSPORT = "http";
|
|
101
|
+
process.env.PORT = "0";
|
|
102
|
+
process.env.BOOMTICK_MCP_BEARER_TOKEN = "test-token";
|
|
103
|
+
const boomtickServer = new BoomtickMCPServer();
|
|
104
|
+
await boomtickServer.run();
|
|
105
|
+
// Verify instance initialized properly
|
|
106
|
+
expect(boomtickServer).toBeInstanceOf(BoomtickMCPServer);
|
|
107
|
+
});
|
|
58
108
|
});
|
package/dist/mcp/server.js
CHANGED
|
@@ -16,14 +16,18 @@ import { getCommandSchemaHandler, GetCommandSchemaInputSchema } from "../tools/r
|
|
|
16
16
|
import { getPackageScriptsHandler, GetPackageScriptsInputSchema } from "../tools/repo.get_package_scripts.js";
|
|
17
17
|
import { readCiLogsHandler, ReadCiLogsInputSchema } from "../tools/repo.read_ci_logs.js";
|
|
18
18
|
import { repoLogsHandler, RepoLogsInputSchema } from "../tools/repo.logs.js";
|
|
19
|
+
import { getWorkflowRunsHandler, GetWorkflowRunsInputSchema } from "../tools/repo.get_workflow_runs.js";
|
|
19
20
|
import { createRepairBranchHandler, CreateRepairBranchInputSchema } from "../tools/repo.create_repair_branch.js";
|
|
20
21
|
import { createBranchHandler, CreateBranchInputSchema } from "../tools/repo.create_branch.js";
|
|
21
22
|
import { runTestsHandler, RunTestsInputSchema } from "../tools/repo.run_tests.js";
|
|
23
|
+
import { checkJulesStatusHandler, CheckJulesStatusInputSchema } from "../tools/repo.check_jules_status.js";
|
|
22
24
|
import { commitPatchHandler, CommitPatchInputSchema } from "../tools/repo.commit_patch.js";
|
|
23
25
|
import { openReplacementPrHandler, OpenReplacementPrInputSchema } from "../tools/github.open_replacement_pr.js";
|
|
24
26
|
import { commentTriageSummaryHandler, CommentTriageSummaryInputSchema } from "../tools/github.comment_triage_summary.js";
|
|
25
27
|
import { createPullRequestHandler, CreatePullRequestInputSchema } from "../tools/github.create_pull_request.js";
|
|
26
28
|
import { getPrHandler, GetPrInputSchema } from "../tools/github.get_pr.js";
|
|
29
|
+
import { getPrReviewsHandler, GetPrReviewsInputSchema } from "../tools/github.get_pr_reviews.js";
|
|
30
|
+
import { getPrCommitsHandler, GetPrCommitsInputSchema } from "../tools/github.get_pr_commits.js";
|
|
27
31
|
import { issueViewHandler, IssueViewInputSchema } from "../tools/github.issue_view.js";
|
|
28
32
|
import { issueUpdateHandler, IssueUpdateInputSchema } from "../tools/github.issue_update.js";
|
|
29
33
|
import { issueCommentHandler, IssueCommentInputSchema } from "../tools/github.issue_comment.js";
|
|
@@ -117,6 +121,14 @@ export class BoomtickMCPServer {
|
|
|
117
121
|
contents: [{ uri, mimeType: "application/json", text: JSON.stringify(changedFiles, null, 2) }],
|
|
118
122
|
};
|
|
119
123
|
}
|
|
124
|
+
if (uri === "mcp://ci/latest-run" ||
|
|
125
|
+
uri === "repo://ci/latest-run" ||
|
|
126
|
+
uri === "repo://ci-latest-run") {
|
|
127
|
+
const workflowRuns = await getWorkflowRunsHandler(GetWorkflowRunsInputSchema.parse({}));
|
|
128
|
+
return {
|
|
129
|
+
contents: [{ uri, mimeType: "application/json", text: JSON.stringify(workflowRuns, null, 2) }],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
120
132
|
if (uri === "repo://package-json" || uri === "mcp://workspace/package-json") {
|
|
121
133
|
const content = await fs.readFile(path.join(config.repoPath, "package.json"), "utf-8");
|
|
122
134
|
return {
|
|
@@ -189,12 +201,18 @@ export class BoomtickMCPServer {
|
|
|
189
201
|
return createSuccessResult(await readCiLogsHandler(ReadCiLogsInputSchema.parse(normArgs)));
|
|
190
202
|
case "repo.logs":
|
|
191
203
|
return createSuccessResult(await repoLogsHandler(RepoLogsInputSchema.parse(normArgs)));
|
|
204
|
+
case "repo.get.workflow.runs":
|
|
205
|
+
case "repo.get_workflow_runs":
|
|
206
|
+
return createSuccessResult(await getWorkflowRunsHandler(GetWorkflowRunsInputSchema.parse(normArgs)));
|
|
192
207
|
case "repo.create.branch":
|
|
193
208
|
case "repo.create_branch":
|
|
194
209
|
return createSuccessResult(await createBranchHandler(CreateBranchInputSchema.parse(normArgs)));
|
|
195
210
|
case "repo.create.repair.branch":
|
|
196
211
|
case "repo.create_repair_branch":
|
|
197
212
|
return createSuccessResult(await createRepairBranchHandler(CreateRepairBranchInputSchema.parse(normArgs)));
|
|
213
|
+
case "repo.check.jules.status":
|
|
214
|
+
case "repo.check_jules_status":
|
|
215
|
+
return createSuccessResult(await checkJulesStatusHandler(CheckJulesStatusInputSchema.parse(normArgs)));
|
|
198
216
|
case "repo.run.tests":
|
|
199
217
|
case "repo.run_tests":
|
|
200
218
|
return createSuccessResult(await runTestsHandler(RunTestsInputSchema.parse(normArgs)));
|
|
@@ -213,6 +231,12 @@ export class BoomtickMCPServer {
|
|
|
213
231
|
case "github.get.pr":
|
|
214
232
|
case "github.get_pr":
|
|
215
233
|
return createSuccessResult(await getPrHandler(GetPrInputSchema.parse(normArgs)));
|
|
234
|
+
case "github.get.pr.reviews":
|
|
235
|
+
case "github.get_pr_reviews":
|
|
236
|
+
return createSuccessResult(await getPrReviewsHandler(GetPrReviewsInputSchema.parse(normArgs)));
|
|
237
|
+
case "github.get.pr.commits":
|
|
238
|
+
case "github.get_pr_commits":
|
|
239
|
+
return createSuccessResult(await getPrCommitsHandler(GetPrCommitsInputSchema.parse(normArgs)));
|
|
216
240
|
case "github.issue.view":
|
|
217
241
|
case "github.issue_view":
|
|
218
242
|
return createSuccessResult(await issueViewHandler(IssueViewInputSchema.parse(normArgs)));
|
|
@@ -323,20 +347,33 @@ export class BoomtickMCPServer {
|
|
|
323
347
|
process.exit(1);
|
|
324
348
|
}
|
|
325
349
|
}
|
|
326
|
-
async startHttp(port = parseInt(process.env.PORT || process.env.HTTP_PORT || "3000", 10), bearerToken) {
|
|
327
|
-
this.prepareEnvironmentAndPreflight();
|
|
328
|
-
const mcpHttp = createMcpHttpServer({ port, bearerToken, mcpServer: this.server });
|
|
329
|
-
return await mcpHttp.listen(port);
|
|
330
|
-
}
|
|
331
350
|
async run() {
|
|
332
351
|
this.prepareEnvironmentAndPreflight();
|
|
352
|
+
const args = process.argv.slice(2);
|
|
353
|
+
let port = parseInt(process.env.PORT || process.env.HTTP_PORT || "3000", 10);
|
|
354
|
+
let bearerToken = process.env.BOOMTICK_MCP_BEARER_TOKEN || process.env.MCP_BEARER_TOKEN || process.env.AUTH_TOKEN;
|
|
355
|
+
for (let i = 0; i < args.length; i++) {
|
|
356
|
+
if (args[i] === "--port" && i + 1 < args.length) {
|
|
357
|
+
port = parseInt(args[i + 1], 10);
|
|
358
|
+
i++;
|
|
359
|
+
}
|
|
360
|
+
else if (args[i].startsWith("--port=")) {
|
|
361
|
+
port = parseInt(args[i].split("=")[1], 10);
|
|
362
|
+
}
|
|
363
|
+
else if (args[i] === "--bearer-token" && i + 1 < args.length) {
|
|
364
|
+
bearerToken = args[i + 1];
|
|
365
|
+
i++;
|
|
366
|
+
}
|
|
367
|
+
else if (args[i].startsWith("--bearer-token=")) {
|
|
368
|
+
bearerToken = args[i].split("=")[1];
|
|
369
|
+
}
|
|
370
|
+
}
|
|
333
371
|
const isHttp = process.env.MCP_TRANSPORT === "http" ||
|
|
334
|
-
|
|
335
|
-
(process.env.PORT !== undefined && !
|
|
336
|
-
(process.env.HTTP_PORT !== undefined && !
|
|
372
|
+
args.includes("--http") ||
|
|
373
|
+
(process.env.PORT !== undefined && !args.includes("--stdio")) ||
|
|
374
|
+
(process.env.HTTP_PORT !== undefined && !args.includes("--stdio"));
|
|
337
375
|
if (isHttp) {
|
|
338
|
-
const
|
|
339
|
-
const mcpHttp = createMcpHttpServer({ port, mcpServer: this.server });
|
|
376
|
+
const mcpHttp = createMcpHttpServer({ port, bearerToken, mcpServer: this.server });
|
|
340
377
|
await mcpHttp.listen(port);
|
|
341
378
|
return;
|
|
342
379
|
}
|
package/dist/mcp/server.test.js
CHANGED
|
@@ -4,6 +4,9 @@ import fs from "fs/promises";
|
|
|
4
4
|
import * as healthModule from "./tools.js";
|
|
5
5
|
import * as prDiffModule from "../tools/github.get_pr_diff.js";
|
|
6
6
|
import * as ciLogsModule from "../tools/repo.read_ci_logs.js";
|
|
7
|
+
import * as prReviewsModule from "../tools/github.get_pr_reviews.js";
|
|
8
|
+
import * as prCommitsModule from "../tools/github.get_pr_commits.js";
|
|
9
|
+
import * as workflowRunsModule from "../tools/repo.get_workflow_runs.js";
|
|
7
10
|
vi.mock("fs/promises");
|
|
8
11
|
vi.mock("../config.js", () => ({
|
|
9
12
|
config: {
|
|
@@ -76,12 +79,16 @@ describe("BoomtickMCPServer", () => {
|
|
|
76
79
|
});
|
|
77
80
|
});
|
|
78
81
|
describe("ListResources", () => {
|
|
79
|
-
it("should return resources list", async () => {
|
|
82
|
+
it("should return resources list containing workspace resources", async () => {
|
|
80
83
|
const handler = requestHandlers.get("resources/list");
|
|
81
84
|
expect(handler).toBeDefined();
|
|
82
85
|
const result = await handler({ method: "resources/list" });
|
|
83
86
|
expect(result.resources).toBeDefined();
|
|
84
87
|
expect(Array.isArray(result.resources)).toBe(true);
|
|
88
|
+
const uris = result.resources.map((r) => r.uri);
|
|
89
|
+
expect(uris).toContain("mcp://workspace/agent-contract");
|
|
90
|
+
expect(uris).toContain("mcp://git/changed-files");
|
|
91
|
+
expect(uris).toContain("mcp://ci/latest-run");
|
|
85
92
|
});
|
|
86
93
|
it("should return resource templates list", async () => {
|
|
87
94
|
const handler = requestHandlers.get("resources/templates/list");
|
|
@@ -147,6 +154,29 @@ describe("BoomtickMCPServer", () => {
|
|
|
147
154
|
expect(result.contents[0].uri).toBe("repo://ci/42");
|
|
148
155
|
expect(JSON.parse(result.contents[0].text)).toEqual({ status: "success" });
|
|
149
156
|
});
|
|
157
|
+
it("should handle mcp://git/changed-files", async () => {
|
|
158
|
+
const handler = requestHandlers.get("resources/read");
|
|
159
|
+
const changedFilesModule = await import("../tools/repo.get_changed_files.js");
|
|
160
|
+
vi.spyOn(changedFilesModule, "getChangedFilesHandler").mockResolvedValue({ files: ["src/index.ts"] });
|
|
161
|
+
const result = await handler({
|
|
162
|
+
method: "resources/read",
|
|
163
|
+
params: { uri: "mcp://git/changed-files" },
|
|
164
|
+
});
|
|
165
|
+
expect(result.contents[0].uri).toBe("mcp://git/changed-files");
|
|
166
|
+
expect(result.contents[0].mimeType).toBe("application/json");
|
|
167
|
+
expect(JSON.parse(result.contents[0].text)).toEqual({ files: ["src/index.ts"] });
|
|
168
|
+
});
|
|
169
|
+
it("should handle mcp://ci/latest-run", async () => {
|
|
170
|
+
const handler = requestHandlers.get("resources/read");
|
|
171
|
+
vi.spyOn(workflowRunsModule, "getWorkflowRunsHandler").mockResolvedValue({ workflowRuns: [{ id: 1, status: "completed", conclusion: "success" }] });
|
|
172
|
+
const result = await handler({
|
|
173
|
+
method: "resources/read",
|
|
174
|
+
params: { uri: "mcp://ci/latest-run" },
|
|
175
|
+
});
|
|
176
|
+
expect(result.contents[0].uri).toBe("mcp://ci/latest-run");
|
|
177
|
+
expect(result.contents[0].mimeType).toBe("application/json");
|
|
178
|
+
expect(JSON.parse(result.contents[0].text)).toEqual({ workflowRuns: [{ id: 1, status: "completed", conclusion: "success" }] });
|
|
179
|
+
});
|
|
150
180
|
it("should throw when resource is unknown", async () => {
|
|
151
181
|
const handler = requestHandlers.get("resources/read");
|
|
152
182
|
await expect(handler({
|
|
@@ -177,6 +207,27 @@ describe("BoomtickMCPServer", () => {
|
|
|
177
207
|
structuredContent: { status: "ok" },
|
|
178
208
|
});
|
|
179
209
|
});
|
|
210
|
+
it("should execute github_get_pr_reviews and github_get_pr_commits via underscore or dot name", async () => {
|
|
211
|
+
const handler = requestHandlers.get("tools/call");
|
|
212
|
+
vi.spyOn(prReviewsModule, "getPrReviewsHandler").mockResolvedValue({ reviews: [] });
|
|
213
|
+
vi.spyOn(prCommitsModule, "getPrCommitsHandler").mockResolvedValue({ commits: [] });
|
|
214
|
+
vi.spyOn(workflowRunsModule, "getWorkflowRunsHandler").mockResolvedValue({ runs: [] });
|
|
215
|
+
const resReviews = await handler({
|
|
216
|
+
method: "tools/call",
|
|
217
|
+
params: { name: "github_get_pr_reviews", arguments: { prNumber: 123 } },
|
|
218
|
+
});
|
|
219
|
+
expect(resReviews.structuredContent).toEqual({ reviews: [] });
|
|
220
|
+
const resCommits = await handler({
|
|
221
|
+
method: "tools/call",
|
|
222
|
+
params: { name: "github_get_pr_commits", arguments: { prNumber: 123 } },
|
|
223
|
+
});
|
|
224
|
+
expect(resCommits.structuredContent).toEqual({ commits: [] });
|
|
225
|
+
const resWorkflow = await handler({
|
|
226
|
+
method: "tools/call",
|
|
227
|
+
params: { name: "repo_get_workflow_runs", arguments: {} },
|
|
228
|
+
});
|
|
229
|
+
expect(resWorkflow.structuredContent).toEqual({ runs: [] });
|
|
230
|
+
});
|
|
180
231
|
it("should return error result for unknown tool", async () => {
|
|
181
232
|
const handler = requestHandlers.get("tools/call");
|
|
182
233
|
const result = await handler({
|
package/dist/tools/contract.js
CHANGED
|
@@ -1564,6 +1564,49 @@ export const GetMergeConflictFilesInputJsonSchema = {
|
|
|
1564
1564
|
"title": "GetMergeConflictFilesInput",
|
|
1565
1565
|
"type": "object"
|
|
1566
1566
|
};
|
|
1567
|
+
export const GetMergeConflictFilesResponseSchema = z.object({ "hasConflicts": z.boolean(), "conflictedFiles": z.array(z.string()).optional(), "prNumber": z.union([z.number().int(), z.null()]).default(null), "baseBranch": z.string(), "headRef": z.string() });
|
|
1568
|
+
export const GetMergeConflictFilesResponseJsonSchema = {
|
|
1569
|
+
"properties": {
|
|
1570
|
+
"hasConflicts": {
|
|
1571
|
+
"title": "Hasconflicts",
|
|
1572
|
+
"type": "boolean"
|
|
1573
|
+
},
|
|
1574
|
+
"conflictedFiles": {
|
|
1575
|
+
"items": {
|
|
1576
|
+
"type": "string"
|
|
1577
|
+
},
|
|
1578
|
+
"title": "Conflictedfiles",
|
|
1579
|
+
"type": "array"
|
|
1580
|
+
},
|
|
1581
|
+
"prNumber": {
|
|
1582
|
+
"anyOf": [
|
|
1583
|
+
{
|
|
1584
|
+
"type": "integer"
|
|
1585
|
+
},
|
|
1586
|
+
{
|
|
1587
|
+
"type": "null"
|
|
1588
|
+
}
|
|
1589
|
+
],
|
|
1590
|
+
"default": null,
|
|
1591
|
+
"title": "Prnumber"
|
|
1592
|
+
},
|
|
1593
|
+
"baseBranch": {
|
|
1594
|
+
"title": "Basebranch",
|
|
1595
|
+
"type": "string"
|
|
1596
|
+
},
|
|
1597
|
+
"headRef": {
|
|
1598
|
+
"title": "Headref",
|
|
1599
|
+
"type": "string"
|
|
1600
|
+
}
|
|
1601
|
+
},
|
|
1602
|
+
"required": [
|
|
1603
|
+
"hasConflicts",
|
|
1604
|
+
"baseBranch",
|
|
1605
|
+
"headRef"
|
|
1606
|
+
],
|
|
1607
|
+
"title": "GetMergeConflictFilesResponse",
|
|
1608
|
+
"type": "object"
|
|
1609
|
+
};
|
|
1567
1610
|
export const GetPackageScriptsInputSchema = z.object({ "filter": z.union([z.string(), z.null()]).describe("Optional glob pattern to filter script names.").default(null) });
|
|
1568
1611
|
export const GetPackageScriptsInputJsonSchema = {
|
|
1569
1612
|
"properties": {
|
|
@@ -3086,6 +3129,40 @@ export const RunTestsInputJsonSchema = {
|
|
|
3086
3129
|
"title": "RunTestsInput",
|
|
3087
3130
|
"type": "object"
|
|
3088
3131
|
};
|
|
3132
|
+
export const RunTestsResponseSchema = z.object({ "passed": z.boolean(), "exitCode": z.number().int(), "failedTests": z.array(z.string()).optional(), "results": z.array(z.record(z.string(), z.any())).optional() });
|
|
3133
|
+
export const RunTestsResponseJsonSchema = {
|
|
3134
|
+
"properties": {
|
|
3135
|
+
"passed": {
|
|
3136
|
+
"title": "Passed",
|
|
3137
|
+
"type": "boolean"
|
|
3138
|
+
},
|
|
3139
|
+
"exitCode": {
|
|
3140
|
+
"title": "Exitcode",
|
|
3141
|
+
"type": "integer"
|
|
3142
|
+
},
|
|
3143
|
+
"failedTests": {
|
|
3144
|
+
"items": {
|
|
3145
|
+
"type": "string"
|
|
3146
|
+
},
|
|
3147
|
+
"title": "Failedtests",
|
|
3148
|
+
"type": "array"
|
|
3149
|
+
},
|
|
3150
|
+
"results": {
|
|
3151
|
+
"items": {
|
|
3152
|
+
"additionalProperties": true,
|
|
3153
|
+
"type": "object"
|
|
3154
|
+
},
|
|
3155
|
+
"title": "Results",
|
|
3156
|
+
"type": "array"
|
|
3157
|
+
}
|
|
3158
|
+
},
|
|
3159
|
+
"required": [
|
|
3160
|
+
"passed",
|
|
3161
|
+
"exitCode"
|
|
3162
|
+
],
|
|
3163
|
+
"title": "RunTestsResponse",
|
|
3164
|
+
"type": "object"
|
|
3165
|
+
};
|
|
3089
3166
|
export const SearchDdgsInputSchema = z.object({ "query": z.string().describe("The search query."), "maxResults": z.union([z.number().int(), z.null()]).describe("Maximum number of results to return.").default(null) });
|
|
3090
3167
|
export const SearchDdgsInputJsonSchema = {
|
|
3091
3168
|
"properties": {
|
|
@@ -1,23 +1,16 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { runTDCli } from "./jules/cli.js";
|
|
3
3
|
export const CommentTriageSummaryInputSchema = z.object({
|
|
4
4
|
prNumber: z.number(),
|
|
5
5
|
body: z.string(),
|
|
6
6
|
});
|
|
7
7
|
export async function commentTriageSummaryHandler(args) {
|
|
8
|
-
const
|
|
8
|
+
const output = await runTDCli([
|
|
9
9
|
"gh",
|
|
10
10
|
"post-comment",
|
|
11
11
|
"--pr", args.prNumber.toString(),
|
|
12
12
|
"--body", args.body
|
|
13
|
-
]);
|
|
14
|
-
if (result.exitCode !== 0) {
|
|
15
|
-
throw new Error(`Failed to comment on PR: ${result.stderr}`);
|
|
16
|
-
}
|
|
17
|
-
const output = JSON.parse(result.stdout);
|
|
18
|
-
if (output.status === "error") {
|
|
19
|
-
throw new Error(`Failed to comment on PR: ${output.message}`);
|
|
20
|
-
}
|
|
13
|
+
], "comment on PR");
|
|
21
14
|
return {
|
|
22
15
|
success: true,
|
|
23
16
|
commentUrl: output.html_url || output.url
|
|
@@ -9,10 +9,12 @@ export async function getMergeConflictFilesHandler(args) {
|
|
|
9
9
|
const baseBranch = args.baseBranch || "main";
|
|
10
10
|
const headRef = args.headRef || "HEAD";
|
|
11
11
|
const conflictFiles = await getMergeConflictFiles(baseBranch, headRef);
|
|
12
|
+
const hasConflicts = conflictFiles.length > 0;
|
|
12
13
|
return {
|
|
13
|
-
|
|
14
|
+
hasConflicts,
|
|
15
|
+
conflictedFiles: conflictFiles,
|
|
16
|
+
prNumber: args.prNumber ?? null,
|
|
14
17
|
baseBranch,
|
|
15
18
|
headRef,
|
|
16
|
-
conflictFiles,
|
|
17
19
|
};
|
|
18
20
|
}
|
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { runCommand } from "../lib/shell.js";
|
|
3
3
|
import { resolveRepo } from "../lib/git.js";
|
|
4
4
|
import { sanitizeError } from "../lib/error_utils.js";
|
|
5
|
-
const GetPrCommitsInputSchema = z.object({
|
|
5
|
+
export const GetPrCommitsInputSchema = z.object({
|
|
6
6
|
prNumber: z.number().describe("The number of the PR to list commits for."),
|
|
7
7
|
repo: z.string().optional().describe("The target repository override (e.g. org/repo)."),
|
|
8
8
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { runCommand } from "../lib/shell.js";
|
|
3
2
|
import { resolveRepo } from "../lib/git.js";
|
|
3
|
+
import { runTDCli } from "./jules/cli.js";
|
|
4
4
|
export const GetPrDiffInputSchema = z.object({
|
|
5
5
|
prNumber: z.number().describe("The number of the pull request to get the diff for."),
|
|
6
6
|
repo: z.string().optional().describe("The target repository override (e.g. org/repo)."),
|
|
@@ -8,17 +8,9 @@ export const GetPrDiffInputSchema = z.object({
|
|
|
8
8
|
export async function getPrDiffHandler(args) {
|
|
9
9
|
const params = GetPrDiffInputSchema.parse(args);
|
|
10
10
|
const targetRepo = resolveRepo(params.repo);
|
|
11
|
-
const
|
|
11
|
+
const output = await runTDCli([
|
|
12
12
|
"gh", "pr-diff", params.prNumber.toString(), "--repo", targetRepo
|
|
13
|
-
]);
|
|
14
|
-
if (result.exitCode !== 0) {
|
|
15
|
-
throw new Error(`Failed to get PR diff: ${result.stderr}`);
|
|
16
|
-
}
|
|
17
|
-
const output = JSON.parse(result.stdout);
|
|
18
|
-
if (output.status === "error") {
|
|
19
|
-
throw new Error(`Failed to get PR diff: ${output.message}`);
|
|
20
|
-
}
|
|
21
|
-
// Every TypeScript tool file must be a pure routing shim
|
|
13
|
+
], "get PR diff");
|
|
22
14
|
return {
|
|
23
15
|
prNumber: output.prNumber,
|
|
24
16
|
files: output.files,
|
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { runCommand } from "../lib/shell.js";
|
|
3
3
|
import { resolveRepo } from "../lib/git.js";
|
|
4
4
|
import { sanitizeError } from "../lib/error_utils.js";
|
|
5
|
-
const GetPrReviewsInputSchema = z.object({
|
|
5
|
+
export const GetPrReviewsInputSchema = z.object({
|
|
6
6
|
prNumber: z.number().describe("The number of the PR to view reviews for."),
|
|
7
7
|
repo: z.string().optional().describe("The target repository override (e.g. org/repo)."),
|
|
8
8
|
});
|
|
@@ -1,17 +1,10 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { runTDCli } from "./cli.js";
|
|
3
3
|
export const CancelJulesSessionInputSchema = z.object({
|
|
4
4
|
sessionId: z.string(),
|
|
5
5
|
});
|
|
6
6
|
export async function cancelJulesSessionHandler(input) {
|
|
7
|
-
|
|
8
|
-
if (result.exitCode !== 0) {
|
|
9
|
-
throw new Error(`Failed to cancel session: ${result.stderr}`);
|
|
10
|
-
}
|
|
11
|
-
const output = JSON.parse(result.stdout);
|
|
12
|
-
if (output.status === "error") {
|
|
13
|
-
throw new Error(`Failed to cancel session: ${output.message}`);
|
|
14
|
-
}
|
|
7
|
+
await runTDCli(["agent", "cancel", input.sessionId], "cancel session");
|
|
15
8
|
return {
|
|
16
9
|
id: input.sessionId.replace("sessions/", ""),
|
|
17
10
|
status: "FAILED",
|