@arii/boomtick-mcp 0.4.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mcp/schemas/boomtick.health.json +5 -0
- package/.mcp/schemas/boomtick_health.json +5 -0
- package/.mcp/schemas/github.checkout_branch.json +5 -0
- package/.mcp/schemas/github.comment_triage_summary.json +5 -0
- package/.mcp/schemas/github.create_issue.json +5 -0
- package/.mcp/schemas/github.create_pull_request.json +5 -0
- package/.mcp/schemas/github.get_merge_conflict_files.json +5 -0
- package/.mcp/schemas/github.get_pr.json +5 -0
- package/.mcp/schemas/github.get_pr_commits.json +37 -0
- package/.mcp/schemas/github.get_pr_diff.json +5 -0
- package/.mcp/schemas/github.get_pr_reviews.json +37 -0
- package/.mcp/schemas/github.issue_comment.json +5 -0
- package/.mcp/schemas/github.issue_update.json +5 -0
- package/.mcp/schemas/github.issue_view.json +5 -0
- package/.mcp/schemas/github.open_replacement_pr.json +5 -0
- package/.mcp/schemas/github.search_open_prs.json +5 -0
- package/.mcp/schemas/github_checkout_branch.json +5 -0
- package/.mcp/schemas/github_comment_triage_summary.json +5 -0
- package/.mcp/schemas/github_create_issue.json +5 -0
- package/.mcp/schemas/github_create_pull_request.json +5 -0
- package/.mcp/schemas/github_get_merge_conflict_files.json +5 -0
- package/.mcp/schemas/github_get_pr.json +5 -0
- package/.mcp/schemas/github_get_pr_commits.json +37 -0
- package/.mcp/schemas/github_get_pr_diff.json +5 -0
- package/.mcp/schemas/github_get_pr_reviews.json +37 -0
- package/.mcp/schemas/github_issue_comment.json +5 -0
- package/.mcp/schemas/github_issue_update.json +5 -0
- package/.mcp/schemas/github_issue_view.json +5 -0
- package/.mcp/schemas/github_open_replacement_pr.json +5 -0
- package/.mcp/schemas/github_search_open_prs.json +5 -0
- package/.mcp/schemas/jules.cancel_session.json +5 -0
- package/.mcp/schemas/jules.create_session.json +5 -0
- package/.mcp/schemas/jules.get_messages.json +5 -0
- package/.mcp/schemas/jules.get_pr.json +5 -0
- package/.mcp/schemas/jules.get_session.json +5 -0
- package/.mcp/schemas/jules.list_sessions.json +5 -0
- package/.mcp/schemas/jules.send_message.json +5 -0
- package/.mcp/schemas/jules.trigger_feedback.json +5 -0
- package/.mcp/schemas/jules_cancel_session.json +5 -0
- package/.mcp/schemas/jules_create_session.json +5 -0
- package/.mcp/schemas/jules_get_messages.json +5 -0
- package/.mcp/schemas/jules_get_pr.json +5 -0
- package/.mcp/schemas/jules_get_session.json +5 -0
- package/.mcp/schemas/jules_list_sessions.json +5 -0
- package/.mcp/schemas/jules_send_message.json +5 -0
- package/.mcp/schemas/jules_trigger_feedback.json +5 -0
- package/.mcp/schemas/repo.commit_patch.json +5 -0
- package/.mcp/schemas/repo.create_branch.json +5 -0
- package/.mcp/schemas/repo.create_repair_branch.json +5 -0
- package/.mcp/schemas/repo.get_changed_files.json +5 -0
- package/.mcp/schemas/repo.get_command_schema.json +6 -1
- package/.mcp/schemas/repo.get_package_scripts.json +5 -0
- package/.mcp/schemas/repo.get_workflow_runs.json +55 -0
- package/.mcp/schemas/repo.logs.json +19 -0
- package/.mcp/schemas/repo.read_agent_context.json +5 -0
- package/.mcp/schemas/repo.read_ci_logs.json +19 -0
- package/.mcp/schemas/repo.run_tests.json +5 -0
- package/.mcp/schemas/repo_commit_patch.json +5 -0
- package/.mcp/schemas/repo_create_branch.json +5 -0
- package/.mcp/schemas/repo_create_repair_branch.json +5 -0
- package/.mcp/schemas/repo_get_changed_files.json +5 -0
- package/.mcp/schemas/repo_get_command_schema.json +6 -1
- package/.mcp/schemas/repo_get_package_scripts.json +5 -0
- package/.mcp/schemas/repo_get_workflow_runs.json +55 -0
- package/.mcp/schemas/repo_logs.json +19 -0
- package/.mcp/schemas/repo_read_agent_context.json +5 -0
- package/.mcp/schemas/repo_read_ci_logs.json +19 -0
- package/.mcp/schemas/repo_run_tests.json +5 -0
- package/CHANGELOG.md +14 -0
- package/README.md +0 -1
- package/actions/ai-review/action.yml +2 -2
- package/actions/audit/action.yml +1 -1
- package/actions/ci-validate/action.yml +2 -2
- package/actions/impact-analysis/action.yml +7 -19
- package/actions/run-project-gate/action.yml +7 -14
- package/actions/setup/action.yml +1 -1
- package/actions/test-build/action.yml +2 -20
- package/dist/config.js +1 -1
- package/dist/config.test.js +3 -3
- package/dist/lib/git.js +44 -0
- package/dist/lib/git.test.js +82 -0
- package/dist/lib/result.js +4 -0
- package/dist/lib/result.test.js +4 -2
- package/dist/lib/shell.js +6 -6
- package/dist/mcp/definitions.js +86 -38
- package/dist/mcp/http.js +91 -0
- package/dist/mcp/http.test.js +58 -0
- package/dist/mcp/server.js +51 -57
- package/dist/mcp/server.test.js +19 -32
- package/dist/tools/contract.js +244 -3
- package/dist/tools/github.checkout_branch.js +1 -1
- package/dist/tools/github.comment_triage_summary.js +1 -1
- package/dist/tools/github.create_issue.js +1 -1
- package/dist/tools/github.create_issue.test.js +6 -6
- package/dist/tools/github.create_pull_request.js +1 -1
- package/dist/tools/github.create_pull_request.test.js +4 -4
- package/dist/tools/github.get_merge_conflict_files.js +10 -18
- package/dist/tools/github.get_pr.js +1 -1
- package/dist/tools/github.get_pr_commits.js +21 -0
- package/dist/tools/github.get_pr_diff.js +1 -1
- package/dist/tools/github.get_pr_reviews.js +21 -0
- package/dist/tools/github.get_pr_reviews.test.js +99 -0
- package/dist/tools/github.issue_comment.js +1 -1
- package/dist/tools/github.issue_update.js +1 -1
- package/dist/tools/github.issue_view.js +1 -1
- package/dist/tools/github.search_open_prs.js +1 -1
- package/dist/tools/jules/cancel-session.js +1 -1
- package/dist/tools/jules/create-session.js +1 -2
- package/dist/tools/jules/create-session.test.js +11 -11
- package/dist/tools/jules/get-messages.js +1 -1
- package/dist/tools/jules/get-messages.test.js +3 -3
- package/dist/tools/jules/get-pr.js +1 -2
- package/dist/tools/jules/get-pr.test.js +3 -3
- package/dist/tools/jules/get-session.js +1 -2
- package/dist/tools/jules/list-sessions.js +1 -1
- package/dist/tools/jules/send-message.js +1 -1
- package/dist/tools/jules/send-message.test.js +5 -5
- package/dist/tools/jules/shared.js +24 -0
- package/dist/tools/jules/trigger-feedback.js +1 -1
- package/dist/tools/jules/trigger-feedback.test.js +5 -5
- package/dist/tools/repo.get_changed_files.js +2 -13
- package/dist/tools/repo.get_command_schema.js +2 -2
- package/dist/tools/repo.get_workflow_runs.js +29 -0
- package/dist/tools/repo.logs.js +5 -1
- package/dist/tools/repo.read_agent_context.js +27 -1
- package/dist/tools/repo.read_agent_context.test.js +1 -1
- package/dist/tools/repo.read_ci_logs.js +5 -1
- package/dist/tools/repo.run_tests.js +1 -1
- package/package.json +3 -6
- package/scripts/sync-mcp-schemas.ts +2 -0
- package/src/agents/verifier-agent.prompt.md +0 -4
- package/src/config.test.ts +3 -3
- package/src/config.ts +1 -1
- package/src/lib/git.test.ts +110 -0
- package/src/lib/git.ts +75 -0
- package/src/lib/result.test.ts +4 -2
- package/src/lib/result.ts +6 -0
- package/src/lib/shell.ts +6 -6
- package/src/mcp/definitions.ts +91 -42
- package/src/mcp/http.test.ts +72 -0
- package/src/mcp/http.ts +118 -0
- package/src/mcp/server.test.ts +20 -38
- package/src/mcp/server.ts +60 -54
- package/src/tools/contract.ts +256 -3
- package/src/tools/github.checkout_branch.ts +1 -1
- package/src/tools/github.comment_triage_summary.ts +1 -1
- package/src/tools/github.create_issue.test.ts +6 -6
- package/src/tools/github.create_issue.ts +1 -1
- package/src/tools/github.create_pull_request.test.ts +4 -4
- package/src/tools/github.create_pull_request.ts +1 -1
- package/src/tools/github.get_merge_conflict_files.ts +10 -20
- package/src/tools/github.get_pr.ts +1 -1
- package/src/tools/github.get_pr_commits.ts +27 -0
- package/src/tools/github.get_pr_diff.ts +1 -1
- package/src/tools/github.get_pr_reviews.test.ts +110 -0
- package/src/tools/github.get_pr_reviews.ts +27 -0
- package/src/tools/github.issue_comment.ts +1 -1
- package/src/tools/github.issue_update.ts +1 -1
- package/src/tools/github.issue_view.ts +1 -1
- package/src/tools/github.search_open_prs.ts +1 -1
- package/src/tools/jules/cancel-session.ts +1 -1
- package/src/tools/jules/create-session.test.ts +11 -11
- package/src/tools/jules/create-session.ts +1 -2
- package/src/tools/jules/get-messages.test.ts +3 -3
- package/src/tools/jules/get-messages.ts +1 -1
- package/src/tools/jules/get-pr.test.ts +3 -3
- package/src/tools/jules/get-pr.ts +1 -2
- package/src/tools/jules/get-session.ts +1 -2
- package/src/tools/jules/list-sessions.ts +1 -1
- package/src/tools/jules/send-message.test.ts +5 -5
- package/src/tools/jules/send-message.ts +1 -1
- package/src/tools/jules/shared.ts +35 -0
- package/src/tools/jules/trigger-feedback.test.ts +5 -5
- package/src/tools/jules/trigger-feedback.ts +1 -1
- package/src/tools/repo.get_changed_files.ts +2 -16
- package/src/tools/repo.get_command_schema.ts +2 -2
- package/src/tools/repo.get_workflow_runs.ts +36 -0
- package/src/tools/repo.logs.ts +5 -1
- package/src/tools/repo.read_agent_context.test.ts +1 -1
- package/src/tools/repo.read_agent_context.ts +35 -1
- package/src/tools/repo.read_ci_logs.ts +5 -1
- package/src/tools/repo.run_tests.ts +1 -1
- package/.mcp/schemas/agent.search_ddgs.json +0 -31
- package/.mcp/schemas/agent_search_ddgs.json +0 -31
- package/.mcp/schemas/repo.get_route_map.json +0 -23
- package/.mcp/schemas/repo.run_lighthouse.json +0 -29
- package/.mcp/schemas/repo.run_playwright.json +0 -36
- package/.mcp/schemas/repo_get_route_map.json +0 -23
- package/.mcp/schemas/repo_run_lighthouse.json +0 -29
- package/.mcp/schemas/repo_run_playwright.json +0 -36
- package/dist/lib/td-cli.js +0 -54
- package/dist/tools/ddgs.search.js +0 -50
- package/dist/tools/ddgs.search.test.js +0 -67
- package/dist/tools/ddgs_search.py +0 -23
- package/dist/tools/repo.get_route_map.js +0 -37
- package/dist/tools/repo.run_lighthouse.js +0 -37
- package/dist/tools/repo.run_playwright.js +0 -34
- package/scripts/export-mcp-schema.ts +0 -7
- package/src/lib/td-cli.ts +0 -73
- package/src/tools/ddgs.search.test.ts +0 -92
- package/src/tools/ddgs.search.ts +0 -58
- package/src/tools/ddgs_search.py +0 -23
- package/src/tools/repo.get_route_map.ts +0 -41
- package/src/tools/repo.run_lighthouse.ts +0 -40
- package/src/tools/repo.run_playwright.ts +0 -42
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { createMcpHttpServer } from "./http.js";
|
|
4
|
+
describe("createMcpHttpServer", () => {
|
|
5
|
+
let mcpServer;
|
|
6
|
+
let httpServerObj;
|
|
7
|
+
let port;
|
|
8
|
+
beforeEach(async () => {
|
|
9
|
+
mcpServer = new Server({ name: "test-server", version: "1.0.0" }, { capabilities: {} });
|
|
10
|
+
});
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
if (httpServerObj?.server) {
|
|
13
|
+
httpServerObj.server.close();
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
it("should respond with OAuth protected resource metadata on /.well-known/oauth-protected-resource", async () => {
|
|
17
|
+
httpServerObj = createMcpHttpServer({ port: 0, mcpServer });
|
|
18
|
+
port = await httpServerObj.listen(0);
|
|
19
|
+
const res = await fetch(`http://localhost:${port}/.well-known/oauth-protected-resource`);
|
|
20
|
+
expect(res.status).toBe(200);
|
|
21
|
+
const body = await res.json();
|
|
22
|
+
expect(body.resource).toBe("mcp://boomtick-mcp");
|
|
23
|
+
expect(body.authorization_servers).toBeDefined();
|
|
24
|
+
expect(body.scopes_supported).toEqual(["mcp:read", "mcp:write"]);
|
|
25
|
+
});
|
|
26
|
+
it("should reject requests when Bearer token is missing or invalid", async () => {
|
|
27
|
+
httpServerObj = createMcpHttpServer({ port: 0, bearerToken: "secret123", mcpServer });
|
|
28
|
+
port = await httpServerObj.listen(0);
|
|
29
|
+
// Request without token
|
|
30
|
+
const res1 = await fetch(`http://localhost:${port}/sse`);
|
|
31
|
+
expect(res1.status).toBe(401);
|
|
32
|
+
expect(res1.headers.get("www-authenticate")).toContain('Bearer realm="boomtick-mcp"');
|
|
33
|
+
// Request with wrong token
|
|
34
|
+
const res2 = await fetch(`http://localhost:${port}/sse`, {
|
|
35
|
+
headers: { Authorization: "Bearer wrong" },
|
|
36
|
+
});
|
|
37
|
+
expect(res2.status).toBe(401);
|
|
38
|
+
// Request with valid token
|
|
39
|
+
const res3 = await fetch(`http://localhost:${port}/.well-known/oauth-protected-resource`, {
|
|
40
|
+
headers: { Authorization: "Bearer secret123" },
|
|
41
|
+
});
|
|
42
|
+
expect(res3.status).toBe(200);
|
|
43
|
+
});
|
|
44
|
+
it("should handle 404 for unknown endpoints", async () => {
|
|
45
|
+
httpServerObj = createMcpHttpServer({ port: 0, mcpServer });
|
|
46
|
+
port = await httpServerObj.listen(0);
|
|
47
|
+
const res = await fetch(`http://localhost:${port}/unknown-path`);
|
|
48
|
+
expect(res.status).toBe(404);
|
|
49
|
+
});
|
|
50
|
+
it("should return 500 if bearer token is not configured on server for protected endpoints", async () => {
|
|
51
|
+
httpServerObj = createMcpHttpServer({ port: 0, mcpServer });
|
|
52
|
+
port = await httpServerObj.listen(0);
|
|
53
|
+
const res = await fetch(`http://localhost:${port}/sse`);
|
|
54
|
+
expect(res.status).toBe(500);
|
|
55
|
+
const body = await res.json();
|
|
56
|
+
expect(body.error).toContain("Server misconfiguration");
|
|
57
|
+
});
|
|
58
|
+
});
|
package/dist/mcp/server.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
-
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
4
|
import { config } from "../config.js";
|
|
5
5
|
import { createSuccessResult, createErrorResult } from "../lib/result.js";
|
|
6
|
-
import { MCP_TOOLS, MCP_PROMPTS, MCP_RESOURCES } from "./definitions.js";
|
|
6
|
+
import { MCP_TOOLS, MCP_PROMPTS, MCP_RESOURCES, MCP_RESOURCE_TEMPLATES } from "./definitions.js";
|
|
7
7
|
import { normalizeArguments } from "../lib/param_utils.js";
|
|
8
8
|
import { healthHandler, HealthCheckInputSchema } from "./tools.js";
|
|
9
9
|
import { searchOpenPrsHandler, SearchOpenPrsInputSchema } from "../tools/github.search_open_prs.js";
|
|
@@ -14,14 +14,11 @@ import { getChangedFilesHandler, GetChangedFilesInputSchema } from "../tools/rep
|
|
|
14
14
|
import { readAgentContextHandler, ReadAgentContextInputSchema } from "../tools/repo.read_agent_context.js";
|
|
15
15
|
import { getCommandSchemaHandler, GetCommandSchemaInputSchema } from "../tools/repo.get_command_schema.js";
|
|
16
16
|
import { getPackageScriptsHandler, GetPackageScriptsInputSchema } from "../tools/repo.get_package_scripts.js";
|
|
17
|
-
import { getRouteMapHandler, GetRouteMapInputSchema } from "../tools/repo.get_route_map.js";
|
|
18
17
|
import { readCiLogsHandler, ReadCiLogsInputSchema } from "../tools/repo.read_ci_logs.js";
|
|
19
18
|
import { repoLogsHandler, RepoLogsInputSchema } from "../tools/repo.logs.js";
|
|
20
19
|
import { createRepairBranchHandler, CreateRepairBranchInputSchema } from "../tools/repo.create_repair_branch.js";
|
|
21
20
|
import { createBranchHandler, CreateBranchInputSchema } from "../tools/repo.create_branch.js";
|
|
22
21
|
import { runTestsHandler, RunTestsInputSchema } from "../tools/repo.run_tests.js";
|
|
23
|
-
import { runLighthouseHandler, RunLighthouseInputSchema } from "../tools/repo.run_lighthouse.js";
|
|
24
|
-
import { runPlaywrightHandler, RunPlaywrightInputSchema } from "../tools/repo.run_playwright.js";
|
|
25
22
|
import { commitPatchHandler, CommitPatchInputSchema } from "../tools/repo.commit_patch.js";
|
|
26
23
|
import { openReplacementPrHandler, OpenReplacementPrInputSchema } from "../tools/github.open_replacement_pr.js";
|
|
27
24
|
import { commentTriageSummaryHandler, CommentTriageSummaryInputSchema } from "../tools/github.comment_triage_summary.js";
|
|
@@ -39,12 +36,11 @@ import { listJulesSessionsHandler, ListJulesSessionsInputSchema } from "../tools
|
|
|
39
36
|
import { cancelJulesSessionHandler, CancelJulesSessionInputSchema } from "../tools/jules/cancel-session.js";
|
|
40
37
|
import { getJulesPullRequestHandler, GetJulesPullRequestInputSchema } from "../tools/jules/get-pr.js";
|
|
41
38
|
import { triggerJulesFeedbackHandler, TriggerJulesFeedbackInputSchema } from "../tools/jules/trigger-feedback.js";
|
|
42
|
-
import { ddgsSearchHandler, DdgsSearchInputSchema } from "../tools/ddgs.search.js";
|
|
43
39
|
import fs from "fs/promises";
|
|
44
40
|
import { existsSync, readFileSync } from "fs";
|
|
45
41
|
import path from "path";
|
|
46
42
|
import { spawnSync } from "child_process";
|
|
47
|
-
import {
|
|
43
|
+
import { createMcpHttpServer } from "./http.js";
|
|
48
44
|
export class BoomtickMCPServer {
|
|
49
45
|
server;
|
|
50
46
|
constructor() {
|
|
@@ -98,55 +94,56 @@ export class BoomtickMCPServer {
|
|
|
98
94
|
resources: MCP_RESOURCES,
|
|
99
95
|
};
|
|
100
96
|
});
|
|
97
|
+
this.server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
|
|
98
|
+
return {
|
|
99
|
+
resourceTemplates: MCP_RESOURCE_TEMPLATES,
|
|
100
|
+
};
|
|
101
|
+
});
|
|
101
102
|
this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
102
103
|
const uri = request.params.uri;
|
|
103
|
-
if (uri === "
|
|
104
|
-
|
|
104
|
+
if (uri === "mcp://workspace/agent-contract" ||
|
|
105
|
+
uri === "mcp://workspace/agent_contract" ||
|
|
106
|
+
uri === "repo://agent-contract") {
|
|
107
|
+
const contractPath = path.join(config.repoPath, ".agents", "AGENT_CONTRACT.md");
|
|
108
|
+
const content = await fs.readFile(contractPath, "utf-8");
|
|
105
109
|
return {
|
|
106
|
-
contents: [{ uri, mimeType: "
|
|
110
|
+
contents: [{ uri, mimeType: "text/markdown", text: content }],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if (uri === "mcp://git/changed-files" ||
|
|
114
|
+
uri === "repo://changed-files") {
|
|
115
|
+
const changedFiles = await getChangedFilesHandler(GetChangedFilesInputSchema.parse({}));
|
|
116
|
+
return {
|
|
117
|
+
contents: [{ uri, mimeType: "application/json", text: JSON.stringify(changedFiles, null, 2) }],
|
|
107
118
|
};
|
|
108
119
|
}
|
|
109
|
-
if (uri === "repo://
|
|
110
|
-
const
|
|
120
|
+
if (uri === "repo://package-json" || uri === "mcp://workspace/package-json") {
|
|
121
|
+
const content = await fs.readFile(path.join(config.repoPath, "package.json"), "utf-8");
|
|
111
122
|
return {
|
|
112
|
-
contents: [{ uri, mimeType: "application/json", text:
|
|
123
|
+
contents: [{ uri, mimeType: "application/json", text: content }],
|
|
113
124
|
};
|
|
114
125
|
}
|
|
115
|
-
if (uri === "repo://design-tokens") {
|
|
126
|
+
if (uri === "repo://design-tokens" || uri === "mcp://workspace/design-tokens") {
|
|
116
127
|
const tokensPath = path.join(config.repoPath, "src/styles/design-tokens.ts");
|
|
117
128
|
const content = await fs.readFile(tokensPath, "utf-8");
|
|
118
129
|
return {
|
|
119
130
|
contents: [{ uri, mimeType: "text/typescript", text: content }],
|
|
120
131
|
};
|
|
121
132
|
}
|
|
122
|
-
if (uri.startsWith("repo://diff/")) {
|
|
133
|
+
if (uri.startsWith("mcp://git/diff/") || uri.startsWith("repo://diff/")) {
|
|
123
134
|
const prNumber = parseInt(uri.split("/").pop() || "");
|
|
124
135
|
const diff = await getPrDiffHandler({ prNumber });
|
|
125
136
|
return {
|
|
126
137
|
contents: [{ uri, mimeType: "text/plain", text: diff.diffText }],
|
|
127
138
|
};
|
|
128
139
|
}
|
|
129
|
-
if (uri.startsWith("repo://ci/")) {
|
|
140
|
+
if (uri.startsWith("mcp://ci/logs/") || uri.startsWith("repo://ci/")) {
|
|
130
141
|
const prNumber = parseInt(uri.split("/").pop() || "");
|
|
131
142
|
const logs = await readCiLogsHandler({ prNumber });
|
|
132
143
|
return {
|
|
133
144
|
contents: [{ uri, mimeType: "application/json", text: JSON.stringify(logs, null, 2) }],
|
|
134
145
|
};
|
|
135
146
|
}
|
|
136
|
-
if (uri.startsWith("repo://lighthouse/")) {
|
|
137
|
-
const branch = uri.split("/").pop() || "";
|
|
138
|
-
const report = await runLighthouseHandler({ route: "/", worktreePath: `../boomtick-mcp-rescue-${branch}` });
|
|
139
|
-
return {
|
|
140
|
-
contents: [{ uri, mimeType: "application/json", text: JSON.stringify(report, null, 2) }],
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
if (uri.startsWith("repo://playwright/")) {
|
|
144
|
-
const branch = uri.split("/").pop() || "";
|
|
145
|
-
const report = await runPlaywrightHandler({ worktreePath: `../boomtick-mcp-rescue-${branch}` });
|
|
146
|
-
return {
|
|
147
|
-
contents: [{ uri, mimeType: "application/json", text: JSON.stringify(report, null, 2) }],
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
147
|
throw new Error(`Resource not found: ${uri}`);
|
|
151
148
|
});
|
|
152
149
|
}
|
|
@@ -187,9 +184,6 @@ export class BoomtickMCPServer {
|
|
|
187
184
|
case "repo.get.package.scripts":
|
|
188
185
|
case "repo.get_package_scripts":
|
|
189
186
|
return createSuccessResult(await getPackageScriptsHandler(GetPackageScriptsInputSchema.parse(normArgs)));
|
|
190
|
-
case "repo.get.route.map":
|
|
191
|
-
case "repo.get_route_map":
|
|
192
|
-
return createSuccessResult(await getRouteMapHandler(GetRouteMapInputSchema.parse(normArgs)));
|
|
193
187
|
case "repo.read.ci.logs":
|
|
194
188
|
case "repo.read_ci_logs":
|
|
195
189
|
return createSuccessResult(await readCiLogsHandler(ReadCiLogsInputSchema.parse(normArgs)));
|
|
@@ -204,12 +198,6 @@ export class BoomtickMCPServer {
|
|
|
204
198
|
case "repo.run.tests":
|
|
205
199
|
case "repo.run_tests":
|
|
206
200
|
return createSuccessResult(await runTestsHandler(RunTestsInputSchema.parse(normArgs)));
|
|
207
|
-
case "repo.run.lighthouse":
|
|
208
|
-
case "repo.run_lighthouse":
|
|
209
|
-
return createSuccessResult(await runLighthouseHandler(RunLighthouseInputSchema.parse(normArgs)));
|
|
210
|
-
case "repo.run.playwright":
|
|
211
|
-
case "repo.run_playwright":
|
|
212
|
-
return createSuccessResult(await runPlaywrightHandler(RunPlaywrightInputSchema.parse(normArgs)));
|
|
213
201
|
case "repo.commit.patch":
|
|
214
202
|
case "repo.commit_patch":
|
|
215
203
|
return createSuccessResult(await commitPatchHandler(CommitPatchInputSchema.parse(normArgs)));
|
|
@@ -261,9 +249,6 @@ export class BoomtickMCPServer {
|
|
|
261
249
|
case "jules.trigger.feedback":
|
|
262
250
|
case "jules.trigger_feedback":
|
|
263
251
|
return createSuccessResult(await triggerJulesFeedbackHandler(TriggerJulesFeedbackInputSchema.parse(normArgs)));
|
|
264
|
-
case "agent.search.ddgs":
|
|
265
|
-
case "agent.search_ddgs":
|
|
266
|
-
return createSuccessResult(await ddgsSearchHandler(DdgsSearchInputSchema.parse(normArgs)));
|
|
267
252
|
default:
|
|
268
253
|
return createErrorResult(`Tool not found: ${request.params.name}`);
|
|
269
254
|
}
|
|
@@ -273,7 +258,7 @@ export class BoomtickMCPServer {
|
|
|
273
258
|
}
|
|
274
259
|
});
|
|
275
260
|
}
|
|
276
|
-
|
|
261
|
+
prepareEnvironmentAndPreflight() {
|
|
277
262
|
const projectRoot = config.repoPath || process.cwd();
|
|
278
263
|
const homeDir = process.env.HOME || "";
|
|
279
264
|
const extraPaths = [];
|
|
@@ -313,9 +298,9 @@ export class BoomtickMCPServer {
|
|
|
313
298
|
.join(path.delimiter);
|
|
314
299
|
// 3. SAFE REPOSITORY RESOLUTION
|
|
315
300
|
const repoPath = config.repoPath || projectRoot;
|
|
316
|
-
// 4. SECURE, UNIX-ONLY PRE-FLIGHT CHECK FOR TD
|
|
301
|
+
// 4. SECURE, UNIX-ONLY PRE-FLIGHT CHECK FOR TD
|
|
317
302
|
try {
|
|
318
|
-
const result = spawnSync("td
|
|
303
|
+
const result = spawnSync("td", ["doctor"], {
|
|
319
304
|
encoding: "utf-8",
|
|
320
305
|
cwd: repoPath,
|
|
321
306
|
shell: false // Explicitly disabled to clear Semgrep rules
|
|
@@ -327,24 +312,33 @@ export class BoomtickMCPServer {
|
|
|
327
312
|
console.error("status:", result.status);
|
|
328
313
|
console.error("stdout:", result.stdout);
|
|
329
314
|
console.error("stderr:", result.stderr);
|
|
330
|
-
throw new Error(`td
|
|
315
|
+
throw new Error(`td doctor returned exit code: ${result.status}`);
|
|
331
316
|
}
|
|
332
|
-
console.error("✅ td
|
|
317
|
+
console.error("✅ td verified on PATH securely");
|
|
333
318
|
}
|
|
334
319
|
catch (error) {
|
|
335
|
-
console.error("❌ Fatal: td
|
|
320
|
+
console.error("❌ Fatal: td is not resolvable on PATH. MCP tools will fail.");
|
|
336
321
|
console.error("Please ensure the project is installed: pip install -e .");
|
|
337
|
-
console.error("❌ Fatal: td
|
|
322
|
+
console.error("❌ Fatal: td pre-flight check failed:", error instanceof Error ? error.message : String(error));
|
|
338
323
|
process.exit(1);
|
|
339
324
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
325
|
+
}
|
|
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
|
+
async run() {
|
|
332
|
+
this.prepareEnvironmentAndPreflight();
|
|
333
|
+
const isHttp = process.env.MCP_TRANSPORT === "http" ||
|
|
334
|
+
process.argv.includes("--http") ||
|
|
335
|
+
(process.env.PORT !== undefined && !process.argv.includes("--stdio")) ||
|
|
336
|
+
(process.env.HTTP_PORT !== undefined && !process.argv.includes("--stdio"));
|
|
337
|
+
if (isHttp) {
|
|
338
|
+
const port = parseInt(process.env.PORT || process.env.HTTP_PORT || "3000", 10);
|
|
339
|
+
const mcpHttp = createMcpHttpServer({ port, mcpServer: this.server });
|
|
340
|
+
await mcpHttp.listen(port);
|
|
341
|
+
return;
|
|
348
342
|
}
|
|
349
343
|
const transport = new StdioServerTransport();
|
|
350
344
|
await this.server.connect(transport);
|
package/dist/mcp/server.test.js
CHANGED
|
@@ -2,11 +2,8 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
|
2
2
|
import { BoomtickMCPServer } from "./server.js";
|
|
3
3
|
import fs from "fs/promises";
|
|
4
4
|
import * as healthModule from "./tools.js";
|
|
5
|
-
import * as routeMapModule from "../tools/repo.get_route_map.js";
|
|
6
5
|
import * as prDiffModule from "../tools/github.get_pr_diff.js";
|
|
7
6
|
import * as ciLogsModule from "../tools/repo.read_ci_logs.js";
|
|
8
|
-
import * as lighthouseModule from "../tools/repo.run_lighthouse.js";
|
|
9
|
-
import * as playwrightModule from "../tools/repo.run_playwright.js";
|
|
10
7
|
vi.mock("fs/promises");
|
|
11
8
|
vi.mock("../config.js", () => ({
|
|
12
9
|
config: {
|
|
@@ -86,28 +83,37 @@ describe("BoomtickMCPServer", () => {
|
|
|
86
83
|
expect(result.resources).toBeDefined();
|
|
87
84
|
expect(Array.isArray(result.resources)).toBe(true);
|
|
88
85
|
});
|
|
86
|
+
it("should return resource templates list", async () => {
|
|
87
|
+
const handler = requestHandlers.get("resources/templates/list");
|
|
88
|
+
expect(handler).toBeDefined();
|
|
89
|
+
const result = await handler({ method: "resources/templates/list" });
|
|
90
|
+
expect(result.resourceTemplates).toBeDefined();
|
|
91
|
+
expect(Array.isArray(result.resourceTemplates)).toBe(true);
|
|
92
|
+
});
|
|
89
93
|
});
|
|
90
94
|
describe("ReadResource", () => {
|
|
91
|
-
it("should handle
|
|
95
|
+
it("should handle mcp://workspace/agent-contract", async () => {
|
|
92
96
|
const handler = requestHandlers.get("resources/read");
|
|
93
|
-
vi.mocked(fs.readFile).mockResolvedValue(
|
|
97
|
+
vi.mocked(fs.readFile).mockResolvedValue("# Agent Contract\nRule 1");
|
|
94
98
|
const result = await handler({
|
|
95
99
|
method: "resources/read",
|
|
96
|
-
params: { uri: "
|
|
100
|
+
params: { uri: "mcp://workspace/agent-contract" },
|
|
97
101
|
});
|
|
102
|
+
expect(fs.readFile).toHaveBeenCalledWith("/mock/repo/.agents/AGENT_CONTRACT.md", "utf-8");
|
|
98
103
|
expect(result).toEqual({
|
|
99
|
-
contents: [{ uri: "
|
|
104
|
+
contents: [{ uri: "mcp://workspace/agent-contract", mimeType: "text/markdown", text: "# Agent Contract\nRule 1" }],
|
|
100
105
|
});
|
|
101
106
|
});
|
|
102
|
-
it("should handle repo://
|
|
107
|
+
it("should handle repo://package-json", async () => {
|
|
103
108
|
const handler = requestHandlers.get("resources/read");
|
|
104
|
-
vi.
|
|
109
|
+
vi.mocked(fs.readFile).mockResolvedValue('{"name": "test"}');
|
|
105
110
|
const result = await handler({
|
|
106
111
|
method: "resources/read",
|
|
107
|
-
params: { uri: "repo://
|
|
112
|
+
params: { uri: "repo://package-json" },
|
|
113
|
+
});
|
|
114
|
+
expect(result).toEqual({
|
|
115
|
+
contents: [{ uri: "repo://package-json", mimeType: "application/json", text: '{"name": "test"}' }],
|
|
108
116
|
});
|
|
109
|
-
expect(result.contents[0].uri).toBe("repo://routes");
|
|
110
|
-
expect(JSON.parse(result.contents[0].text)).toEqual({ routes: ["/"] });
|
|
111
117
|
});
|
|
112
118
|
it("should handle repo://design-tokens", async () => {
|
|
113
119
|
const handler = requestHandlers.get("resources/read");
|
|
@@ -141,26 +147,6 @@ describe("BoomtickMCPServer", () => {
|
|
|
141
147
|
expect(result.contents[0].uri).toBe("repo://ci/42");
|
|
142
148
|
expect(JSON.parse(result.contents[0].text)).toEqual({ status: "success" });
|
|
143
149
|
});
|
|
144
|
-
it("should handle repo://lighthouse/:branch", async () => {
|
|
145
|
-
const handler = requestHandlers.get("resources/read");
|
|
146
|
-
vi.spyOn(lighthouseModule, "runLighthouseHandler").mockResolvedValue({ score: 100 });
|
|
147
|
-
const result = await handler({
|
|
148
|
-
method: "resources/read",
|
|
149
|
-
params: { uri: "repo://lighthouse/feature-x" },
|
|
150
|
-
});
|
|
151
|
-
expect(result.contents[0].uri).toBe("repo://lighthouse/feature-x");
|
|
152
|
-
expect(JSON.parse(result.contents[0].text)).toEqual({ score: 100 });
|
|
153
|
-
});
|
|
154
|
-
it("should handle repo://playwright/:branch", async () => {
|
|
155
|
-
const handler = requestHandlers.get("resources/read");
|
|
156
|
-
vi.spyOn(playwrightModule, "runPlaywrightHandler").mockResolvedValue({ passed: true });
|
|
157
|
-
const result = await handler({
|
|
158
|
-
method: "resources/read",
|
|
159
|
-
params: { uri: "repo://playwright/feature-x" },
|
|
160
|
-
});
|
|
161
|
-
expect(result.contents[0].uri).toBe("repo://playwright/feature-x");
|
|
162
|
-
expect(JSON.parse(result.contents[0].text)).toEqual({ passed: true });
|
|
163
|
-
});
|
|
164
150
|
it("should throw when resource is unknown", async () => {
|
|
165
151
|
const handler = requestHandlers.get("resources/read");
|
|
166
152
|
await expect(handler({
|
|
@@ -188,6 +174,7 @@ describe("BoomtickMCPServer", () => {
|
|
|
188
174
|
});
|
|
189
175
|
expect(result).toEqual({
|
|
190
176
|
content: [{ type: "text", text: JSON.stringify({ status: "ok" }, null, 2) }],
|
|
177
|
+
structuredContent: { status: "ok" },
|
|
191
178
|
});
|
|
192
179
|
});
|
|
193
180
|
it("should return error result for unknown tool", async () => {
|