@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.
Files changed (205) hide show
  1. package/.mcp/schemas/boomtick.health.json +5 -0
  2. package/.mcp/schemas/boomtick_health.json +5 -0
  3. package/.mcp/schemas/github.checkout_branch.json +5 -0
  4. package/.mcp/schemas/github.comment_triage_summary.json +5 -0
  5. package/.mcp/schemas/github.create_issue.json +5 -0
  6. package/.mcp/schemas/github.create_pull_request.json +5 -0
  7. package/.mcp/schemas/github.get_merge_conflict_files.json +5 -0
  8. package/.mcp/schemas/github.get_pr.json +5 -0
  9. package/.mcp/schemas/github.get_pr_commits.json +37 -0
  10. package/.mcp/schemas/github.get_pr_diff.json +5 -0
  11. package/.mcp/schemas/github.get_pr_reviews.json +37 -0
  12. package/.mcp/schemas/github.issue_comment.json +5 -0
  13. package/.mcp/schemas/github.issue_update.json +5 -0
  14. package/.mcp/schemas/github.issue_view.json +5 -0
  15. package/.mcp/schemas/github.open_replacement_pr.json +5 -0
  16. package/.mcp/schemas/github.search_open_prs.json +5 -0
  17. package/.mcp/schemas/github_checkout_branch.json +5 -0
  18. package/.mcp/schemas/github_comment_triage_summary.json +5 -0
  19. package/.mcp/schemas/github_create_issue.json +5 -0
  20. package/.mcp/schemas/github_create_pull_request.json +5 -0
  21. package/.mcp/schemas/github_get_merge_conflict_files.json +5 -0
  22. package/.mcp/schemas/github_get_pr.json +5 -0
  23. package/.mcp/schemas/github_get_pr_commits.json +37 -0
  24. package/.mcp/schemas/github_get_pr_diff.json +5 -0
  25. package/.mcp/schemas/github_get_pr_reviews.json +37 -0
  26. package/.mcp/schemas/github_issue_comment.json +5 -0
  27. package/.mcp/schemas/github_issue_update.json +5 -0
  28. package/.mcp/schemas/github_issue_view.json +5 -0
  29. package/.mcp/schemas/github_open_replacement_pr.json +5 -0
  30. package/.mcp/schemas/github_search_open_prs.json +5 -0
  31. package/.mcp/schemas/jules.cancel_session.json +5 -0
  32. package/.mcp/schemas/jules.create_session.json +5 -0
  33. package/.mcp/schemas/jules.get_messages.json +5 -0
  34. package/.mcp/schemas/jules.get_pr.json +5 -0
  35. package/.mcp/schemas/jules.get_session.json +5 -0
  36. package/.mcp/schemas/jules.list_sessions.json +5 -0
  37. package/.mcp/schemas/jules.send_message.json +5 -0
  38. package/.mcp/schemas/jules.trigger_feedback.json +5 -0
  39. package/.mcp/schemas/jules_cancel_session.json +5 -0
  40. package/.mcp/schemas/jules_create_session.json +5 -0
  41. package/.mcp/schemas/jules_get_messages.json +5 -0
  42. package/.mcp/schemas/jules_get_pr.json +5 -0
  43. package/.mcp/schemas/jules_get_session.json +5 -0
  44. package/.mcp/schemas/jules_list_sessions.json +5 -0
  45. package/.mcp/schemas/jules_send_message.json +5 -0
  46. package/.mcp/schemas/jules_trigger_feedback.json +5 -0
  47. package/.mcp/schemas/repo.commit_patch.json +5 -0
  48. package/.mcp/schemas/repo.create_branch.json +5 -0
  49. package/.mcp/schemas/repo.create_repair_branch.json +5 -0
  50. package/.mcp/schemas/repo.get_changed_files.json +5 -0
  51. package/.mcp/schemas/repo.get_command_schema.json +6 -1
  52. package/.mcp/schemas/repo.get_package_scripts.json +5 -0
  53. package/.mcp/schemas/repo.get_workflow_runs.json +55 -0
  54. package/.mcp/schemas/repo.logs.json +19 -0
  55. package/.mcp/schemas/repo.read_agent_context.json +5 -0
  56. package/.mcp/schemas/repo.read_ci_logs.json +19 -0
  57. package/.mcp/schemas/repo.run_tests.json +5 -0
  58. package/.mcp/schemas/repo_commit_patch.json +5 -0
  59. package/.mcp/schemas/repo_create_branch.json +5 -0
  60. package/.mcp/schemas/repo_create_repair_branch.json +5 -0
  61. package/.mcp/schemas/repo_get_changed_files.json +5 -0
  62. package/.mcp/schemas/repo_get_command_schema.json +6 -1
  63. package/.mcp/schemas/repo_get_package_scripts.json +5 -0
  64. package/.mcp/schemas/repo_get_workflow_runs.json +55 -0
  65. package/.mcp/schemas/repo_logs.json +19 -0
  66. package/.mcp/schemas/repo_read_agent_context.json +5 -0
  67. package/.mcp/schemas/repo_read_ci_logs.json +19 -0
  68. package/.mcp/schemas/repo_run_tests.json +5 -0
  69. package/CHANGELOG.md +14 -0
  70. package/README.md +0 -1
  71. package/actions/ai-review/action.yml +2 -2
  72. package/actions/audit/action.yml +1 -1
  73. package/actions/ci-validate/action.yml +2 -2
  74. package/actions/impact-analysis/action.yml +7 -19
  75. package/actions/run-project-gate/action.yml +7 -14
  76. package/actions/setup/action.yml +1 -1
  77. package/actions/test-build/action.yml +2 -20
  78. package/dist/config.js +1 -1
  79. package/dist/config.test.js +3 -3
  80. package/dist/lib/git.js +44 -0
  81. package/dist/lib/git.test.js +82 -0
  82. package/dist/lib/result.js +4 -0
  83. package/dist/lib/result.test.js +4 -2
  84. package/dist/lib/shell.js +6 -6
  85. package/dist/mcp/definitions.js +86 -38
  86. package/dist/mcp/http.js +91 -0
  87. package/dist/mcp/http.test.js +58 -0
  88. package/dist/mcp/server.js +51 -57
  89. package/dist/mcp/server.test.js +19 -32
  90. package/dist/tools/contract.js +244 -3
  91. package/dist/tools/github.checkout_branch.js +1 -1
  92. package/dist/tools/github.comment_triage_summary.js +1 -1
  93. package/dist/tools/github.create_issue.js +1 -1
  94. package/dist/tools/github.create_issue.test.js +6 -6
  95. package/dist/tools/github.create_pull_request.js +1 -1
  96. package/dist/tools/github.create_pull_request.test.js +4 -4
  97. package/dist/tools/github.get_merge_conflict_files.js +10 -18
  98. package/dist/tools/github.get_pr.js +1 -1
  99. package/dist/tools/github.get_pr_commits.js +21 -0
  100. package/dist/tools/github.get_pr_diff.js +1 -1
  101. package/dist/tools/github.get_pr_reviews.js +21 -0
  102. package/dist/tools/github.get_pr_reviews.test.js +99 -0
  103. package/dist/tools/github.issue_comment.js +1 -1
  104. package/dist/tools/github.issue_update.js +1 -1
  105. package/dist/tools/github.issue_view.js +1 -1
  106. package/dist/tools/github.search_open_prs.js +1 -1
  107. package/dist/tools/jules/cancel-session.js +1 -1
  108. package/dist/tools/jules/create-session.js +1 -2
  109. package/dist/tools/jules/create-session.test.js +11 -11
  110. package/dist/tools/jules/get-messages.js +1 -1
  111. package/dist/tools/jules/get-messages.test.js +3 -3
  112. package/dist/tools/jules/get-pr.js +1 -2
  113. package/dist/tools/jules/get-pr.test.js +3 -3
  114. package/dist/tools/jules/get-session.js +1 -2
  115. package/dist/tools/jules/list-sessions.js +1 -1
  116. package/dist/tools/jules/send-message.js +1 -1
  117. package/dist/tools/jules/send-message.test.js +5 -5
  118. package/dist/tools/jules/shared.js +24 -0
  119. package/dist/tools/jules/trigger-feedback.js +1 -1
  120. package/dist/tools/jules/trigger-feedback.test.js +5 -5
  121. package/dist/tools/repo.get_changed_files.js +2 -13
  122. package/dist/tools/repo.get_command_schema.js +2 -2
  123. package/dist/tools/repo.get_workflow_runs.js +29 -0
  124. package/dist/tools/repo.logs.js +5 -1
  125. package/dist/tools/repo.read_agent_context.js +27 -1
  126. package/dist/tools/repo.read_agent_context.test.js +1 -1
  127. package/dist/tools/repo.read_ci_logs.js +5 -1
  128. package/dist/tools/repo.run_tests.js +1 -1
  129. package/package.json +3 -6
  130. package/scripts/sync-mcp-schemas.ts +2 -0
  131. package/src/agents/verifier-agent.prompt.md +0 -4
  132. package/src/config.test.ts +3 -3
  133. package/src/config.ts +1 -1
  134. package/src/lib/git.test.ts +110 -0
  135. package/src/lib/git.ts +75 -0
  136. package/src/lib/result.test.ts +4 -2
  137. package/src/lib/result.ts +6 -0
  138. package/src/lib/shell.ts +6 -6
  139. package/src/mcp/definitions.ts +91 -42
  140. package/src/mcp/http.test.ts +72 -0
  141. package/src/mcp/http.ts +118 -0
  142. package/src/mcp/server.test.ts +20 -38
  143. package/src/mcp/server.ts +60 -54
  144. package/src/tools/contract.ts +256 -3
  145. package/src/tools/github.checkout_branch.ts +1 -1
  146. package/src/tools/github.comment_triage_summary.ts +1 -1
  147. package/src/tools/github.create_issue.test.ts +6 -6
  148. package/src/tools/github.create_issue.ts +1 -1
  149. package/src/tools/github.create_pull_request.test.ts +4 -4
  150. package/src/tools/github.create_pull_request.ts +1 -1
  151. package/src/tools/github.get_merge_conflict_files.ts +10 -20
  152. package/src/tools/github.get_pr.ts +1 -1
  153. package/src/tools/github.get_pr_commits.ts +27 -0
  154. package/src/tools/github.get_pr_diff.ts +1 -1
  155. package/src/tools/github.get_pr_reviews.test.ts +110 -0
  156. package/src/tools/github.get_pr_reviews.ts +27 -0
  157. package/src/tools/github.issue_comment.ts +1 -1
  158. package/src/tools/github.issue_update.ts +1 -1
  159. package/src/tools/github.issue_view.ts +1 -1
  160. package/src/tools/github.search_open_prs.ts +1 -1
  161. package/src/tools/jules/cancel-session.ts +1 -1
  162. package/src/tools/jules/create-session.test.ts +11 -11
  163. package/src/tools/jules/create-session.ts +1 -2
  164. package/src/tools/jules/get-messages.test.ts +3 -3
  165. package/src/tools/jules/get-messages.ts +1 -1
  166. package/src/tools/jules/get-pr.test.ts +3 -3
  167. package/src/tools/jules/get-pr.ts +1 -2
  168. package/src/tools/jules/get-session.ts +1 -2
  169. package/src/tools/jules/list-sessions.ts +1 -1
  170. package/src/tools/jules/send-message.test.ts +5 -5
  171. package/src/tools/jules/send-message.ts +1 -1
  172. package/src/tools/jules/shared.ts +35 -0
  173. package/src/tools/jules/trigger-feedback.test.ts +5 -5
  174. package/src/tools/jules/trigger-feedback.ts +1 -1
  175. package/src/tools/repo.get_changed_files.ts +2 -16
  176. package/src/tools/repo.get_command_schema.ts +2 -2
  177. package/src/tools/repo.get_workflow_runs.ts +36 -0
  178. package/src/tools/repo.logs.ts +5 -1
  179. package/src/tools/repo.read_agent_context.test.ts +1 -1
  180. package/src/tools/repo.read_agent_context.ts +35 -1
  181. package/src/tools/repo.read_ci_logs.ts +5 -1
  182. package/src/tools/repo.run_tests.ts +1 -1
  183. package/.mcp/schemas/agent.search_ddgs.json +0 -31
  184. package/.mcp/schemas/agent_search_ddgs.json +0 -31
  185. package/.mcp/schemas/repo.get_route_map.json +0 -23
  186. package/.mcp/schemas/repo.run_lighthouse.json +0 -29
  187. package/.mcp/schemas/repo.run_playwright.json +0 -36
  188. package/.mcp/schemas/repo_get_route_map.json +0 -23
  189. package/.mcp/schemas/repo_run_lighthouse.json +0 -29
  190. package/.mcp/schemas/repo_run_playwright.json +0 -36
  191. package/dist/lib/td-cli.js +0 -54
  192. package/dist/tools/ddgs.search.js +0 -50
  193. package/dist/tools/ddgs.search.test.js +0 -67
  194. package/dist/tools/ddgs_search.py +0 -23
  195. package/dist/tools/repo.get_route_map.js +0 -37
  196. package/dist/tools/repo.run_lighthouse.js +0 -37
  197. package/dist/tools/repo.run_playwright.js +0 -34
  198. package/scripts/export-mcp-schema.ts +0 -7
  199. package/src/lib/td-cli.ts +0 -73
  200. package/src/tools/ddgs.search.test.ts +0 -92
  201. package/src/tools/ddgs.search.ts +0 -58
  202. package/src/tools/ddgs_search.py +0 -23
  203. package/src/tools/repo.get_route_map.ts +0 -41
  204. package/src/tools/repo.run_lighthouse.ts +0 -40
  205. 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
+ });
@@ -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 { fileURLToPath } from "url";
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 === "repo://package-json") {
104
- const content = await fs.readFile(path.join(config.repoPath, "package.json"), "utf-8");
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: "application/json", text: content }],
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://routes") {
110
- const routeMap = await getRouteMapHandler({});
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: JSON.stringify(routeMap, null, 2) }],
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
- async run() {
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-CLI
301
+ // 4. SECURE, UNIX-ONLY PRE-FLIGHT CHECK FOR TD
317
302
  try {
318
- const result = spawnSync("td-cli", ["doctor"], {
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-cli doctor returned exit code: ${result.status}`);
315
+ throw new Error(`td doctor returned exit code: ${result.status}`);
331
316
  }
332
- console.error("✅ td-cli verified on PATH securely");
317
+ console.error("✅ td verified on PATH securely");
333
318
  }
334
319
  catch (error) {
335
- console.error("❌ Fatal: td-cli is not resolvable on PATH. MCP tools will fail.");
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-cli pre-flight check failed:", error instanceof Error ? error.message : String(error));
322
+ console.error("❌ Fatal: td pre-flight check failed:", error instanceof Error ? error.message : String(error));
338
323
  process.exit(1);
339
324
  }
340
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
341
- const ddgsScriptPath = path.join(__dirname, "..", "tools", "ddgs_search.py");
342
- try {
343
- await fs.access(ddgsScriptPath);
344
- }
345
- catch (err) {
346
- console.error(`❌ Fatal: ddgs_search.py not found at ${ddgsScriptPath}`);
347
- process.exit(1);
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);
@@ -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 repo://package-json", async () => {
95
+ it("should handle mcp://workspace/agent-contract", async () => {
92
96
  const handler = requestHandlers.get("resources/read");
93
- vi.mocked(fs.readFile).mockResolvedValue('{"name": "test"}');
97
+ vi.mocked(fs.readFile).mockResolvedValue("# Agent Contract\nRule 1");
94
98
  const result = await handler({
95
99
  method: "resources/read",
96
- params: { uri: "repo://package-json" },
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: "repo://package-json", mimeType: "application/json", text: '{"name": "test"}' }],
104
+ contents: [{ uri: "mcp://workspace/agent-contract", mimeType: "text/markdown", text: "# Agent Contract\nRule 1" }],
100
105
  });
101
106
  });
102
- it("should handle repo://routes", async () => {
107
+ it("should handle repo://package-json", async () => {
103
108
  const handler = requestHandlers.get("resources/read");
104
- vi.spyOn(routeMapModule, "getRouteMapHandler").mockResolvedValue({ routes: ["/"] });
109
+ vi.mocked(fs.readFile).mockResolvedValue('{"name": "test"}');
105
110
  const result = await handler({
106
111
  method: "resources/read",
107
- params: { uri: "repo://routes" },
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 () => {