@arii/boomtick-mcp 0.6.1 → 0.7.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 (141) hide show
  1. package/.mcp/schemas/boomtick.health.json +22 -3
  2. package/.mcp/schemas/boomtick_health.json +22 -3
  3. package/.mcp/schemas/github.checkout_branch.json +16 -3
  4. package/.mcp/schemas/github.comment_triage_summary.json +16 -3
  5. package/.mcp/schemas/github.create_issue.json +72 -3
  6. package/.mcp/schemas/github.create_pull_request.json +16 -3
  7. package/.mcp/schemas/github.get_merge_conflict_files.json +40 -3
  8. package/.mcp/schemas/github.get_pr.json +12 -3
  9. package/.mcp/schemas/github.get_pr_commits.json +12 -3
  10. package/.mcp/schemas/github.get_pr_diff.json +30 -3
  11. package/.mcp/schemas/github.get_pr_reviews.json +24 -3
  12. package/.mcp/schemas/github.issue_comment.json +19 -3
  13. package/.mcp/schemas/github.issue_update.json +72 -3
  14. package/.mcp/schemas/github.issue_view.json +17 -3
  15. package/.mcp/schemas/github.open_replacement_pr.json +16 -3
  16. package/.mcp/schemas/github.search_open_prs.json +130 -3
  17. package/.mcp/schemas/github_checkout_branch.json +16 -3
  18. package/.mcp/schemas/github_comment_triage_summary.json +16 -3
  19. package/.mcp/schemas/github_create_issue.json +72 -3
  20. package/.mcp/schemas/github_create_pull_request.json +16 -3
  21. package/.mcp/schemas/github_get_merge_conflict_files.json +40 -3
  22. package/.mcp/schemas/github_get_pr.json +12 -3
  23. package/.mcp/schemas/github_get_pr_commits.json +12 -3
  24. package/.mcp/schemas/github_get_pr_diff.json +30 -3
  25. package/.mcp/schemas/github_get_pr_reviews.json +24 -3
  26. package/.mcp/schemas/github_issue_comment.json +19 -3
  27. package/.mcp/schemas/github_issue_update.json +72 -3
  28. package/.mcp/schemas/github_issue_view.json +17 -3
  29. package/.mcp/schemas/github_open_replacement_pr.json +16 -3
  30. package/.mcp/schemas/github_search_open_prs.json +130 -3
  31. package/.mcp/schemas/jules.cancel_session.json +38 -3
  32. package/.mcp/schemas/jules.create_session.json +52 -3
  33. package/.mcp/schemas/jules.get_messages.json +19 -3
  34. package/.mcp/schemas/jules.get_pr.json +23 -3
  35. package/.mcp/schemas/jules.get_session.json +38 -3
  36. package/.mcp/schemas/jules.list_sessions.json +53 -3
  37. package/.mcp/schemas/jules.send_message.json +31 -3
  38. package/.mcp/schemas/jules.trigger_feedback.json +28 -3
  39. package/.mcp/schemas/jules_cancel_session.json +38 -3
  40. package/.mcp/schemas/jules_create_session.json +52 -3
  41. package/.mcp/schemas/jules_get_messages.json +19 -3
  42. package/.mcp/schemas/jules_get_pr.json +23 -3
  43. package/.mcp/schemas/jules_get_session.json +38 -3
  44. package/.mcp/schemas/jules_list_sessions.json +53 -3
  45. package/.mcp/schemas/jules_send_message.json +31 -3
  46. package/.mcp/schemas/jules_trigger_feedback.json +28 -3
  47. package/.mcp/schemas/repo.check_jules_status.json +128 -0
  48. package/.mcp/schemas/repo.commit_patch.json +23 -3
  49. package/.mcp/schemas/repo.create_branch.json +28 -3
  50. package/.mcp/schemas/repo.create_repair_branch.json +31 -3
  51. package/.mcp/schemas/repo.get_changed_files.json +11 -3
  52. package/.mcp/schemas/repo.get_command_schema.json +29 -3
  53. package/.mcp/schemas/repo.get_package_scripts.json +11 -3
  54. package/.mcp/schemas/repo.get_workflow_runs.json +12 -3
  55. package/.mcp/schemas/repo.logs.json +14 -3
  56. package/.mcp/schemas/repo.multi_exec.json +89 -0
  57. package/.mcp/schemas/repo.read_agent_context.json +117 -5
  58. package/.mcp/schemas/repo.read_ci_logs.json +46 -3
  59. package/.mcp/schemas/repo.run_tests.json +31 -3
  60. package/.mcp/schemas/repo_check_jules_status.json +128 -0
  61. package/.mcp/schemas/repo_commit_patch.json +23 -3
  62. package/.mcp/schemas/repo_create_branch.json +28 -3
  63. package/.mcp/schemas/repo_create_repair_branch.json +31 -3
  64. package/.mcp/schemas/repo_get_changed_files.json +11 -3
  65. package/.mcp/schemas/repo_get_command_schema.json +29 -3
  66. package/.mcp/schemas/repo_get_package_scripts.json +11 -3
  67. package/.mcp/schemas/repo_get_workflow_runs.json +12 -3
  68. package/.mcp/schemas/repo_logs.json +14 -3
  69. package/.mcp/schemas/repo_multi_exec.json +89 -0
  70. package/.mcp/schemas/repo_read_agent_context.json +117 -5
  71. package/.mcp/schemas/repo_read_ci_logs.json +46 -3
  72. package/.mcp/schemas/repo_run_tests.json +31 -3
  73. package/CHANGELOG.md +22 -0
  74. package/README.md +29 -6
  75. package/actions/run-project-gate/action.yml +1 -1
  76. package/dist/config/dynamic.js +34 -0
  77. package/dist/config/github.js +16 -0
  78. package/dist/config/repo.js +28 -0
  79. package/dist/config.js +16 -81
  80. package/dist/config.test.js +10 -3
  81. package/dist/http.js +10 -0
  82. package/dist/mcp/definitions.js +76 -41
  83. package/dist/mcp/http.js +32 -9
  84. package/dist/mcp/http.test.js +62 -4
  85. package/dist/mcp/server.js +69 -10
  86. package/dist/mcp/server.test.js +59 -1
  87. package/dist/tools/contract.js +1079 -45
  88. package/dist/tools/github.comment_triage_summary.js +3 -10
  89. package/dist/tools/github.get_merge_conflict_files.js +4 -2
  90. package/dist/tools/github.get_pr_diff.js +3 -11
  91. package/dist/tools/jules/cancel-session.js +2 -9
  92. package/dist/tools/jules/cli.js +31 -0
  93. package/dist/tools/jules/create-session.js +12 -2
  94. package/dist/tools/jules/create-session.test.js +7 -0
  95. package/dist/tools/jules/get-messages.js +2 -9
  96. package/dist/tools/jules/list-sessions.js +4 -35
  97. package/dist/tools/jules/send-message.js +2 -9
  98. package/dist/tools/jules/session.js +26 -0
  99. package/dist/tools/jules/shared.js +2 -50
  100. package/dist/tools/jules/trigger-feedback.js +2 -10
  101. package/dist/tools/repo.check_jules_status.js +148 -0
  102. package/dist/tools/repo.check_jules_status.test.js +47 -0
  103. package/dist/tools/repo.multi_exec.js +21 -0
  104. package/dist/tools/repo.multi_exec.test.js +13 -0
  105. package/dist/tools/repo.read_agent_context.js +65 -3
  106. package/dist/tools/repo.read_agent_context.test.js +30 -1
  107. package/dist/tools/repo.run_tests.js +13 -1
  108. package/package.json +3 -2
  109. package/scripts/sync-contracts.ts +11 -2
  110. package/src/config/dynamic.ts +37 -0
  111. package/src/config/github.ts +16 -0
  112. package/src/config/repo.ts +36 -0
  113. package/src/config.test.ts +11 -3
  114. package/src/config.ts +16 -89
  115. package/src/http.ts +13 -0
  116. package/src/mcp/definitions.ts +110 -41
  117. package/src/mcp/http.test.ts +75 -5
  118. package/src/mcp/http.ts +40 -14
  119. package/src/mcp/server.test.ts +72 -1
  120. package/src/mcp/server.ts +72 -11
  121. package/src/tools/contract.ts +1194 -36
  122. package/src/tools/github.comment_triage_summary.ts +3 -12
  123. package/src/tools/github.get_merge_conflict_files.ts +5 -2
  124. package/src/tools/github.get_pr_diff.ts +3 -13
  125. package/src/tools/jules/cancel-session.ts +2 -11
  126. package/src/tools/jules/cli.ts +42 -0
  127. package/src/tools/jules/create-session.test.ts +9 -0
  128. package/src/tools/jules/create-session.ts +13 -2
  129. package/src/tools/jules/get-messages.ts +2 -11
  130. package/src/tools/jules/list-sessions.ts +5 -39
  131. package/src/tools/jules/send-message.ts +2 -11
  132. package/src/tools/jules/session.ts +28 -0
  133. package/src/tools/jules/shared.ts +2 -63
  134. package/src/tools/jules/trigger-feedback.ts +2 -13
  135. package/src/tools/repo.check_jules_status.test.ts +55 -0
  136. package/src/tools/repo.check_jules_status.ts +176 -0
  137. package/src/tools/repo.multi_exec.test.ts +15 -0
  138. package/src/tools/repo.multi_exec.ts +23 -0
  139. package/src/tools/repo.read_agent_context.test.ts +35 -1
  140. package/src/tools/repo.read_agent_context.ts +68 -3
  141. package/src/tools/repo.run_tests.ts +14 -1
@@ -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, HealthCheckResponseJsonSchema, SearchPRsInputJsonSchema, SearchPRsResponseJsonSchema, GetPrDiffInputJsonSchema, PrDiffResponseJsonSchema, CheckoutBranchInputJsonSchema, CheckoutBranchResponseJsonSchema, GetMergeConflictFilesInputJsonSchema, GetMergeConflictFilesResponseJsonSchema, GetChangedFilesInputJsonSchema, GetChangedFilesResponseJsonSchema, GetPackageScriptsInputJsonSchema, GetPackageScriptsResponseJsonSchema, ReadCiLogsInputJsonSchema, ReadCiLogsResponseJsonSchema, RepoLogsInputJsonSchema, RepoLogsResponseJsonSchema, CreateBranchInputJsonSchema, CreateBranchResponseJsonSchema, CreateRepairBranchInputJsonSchema, CreateRepairBranchResponseJsonSchema, CheckJulesStatusResponseJsonSchema, RunTestsInputJsonSchema, RunTestsResponseJsonSchema, CommitPatchInputJsonSchema, CommitPatchResponseJsonSchema, OpenReplacementPrInputJsonSchema, OpenReplacementPrResponseJsonSchema, CreatePullRequestInputJsonSchema, CreatePullRequestResponseJsonSchema, CommentTriageSummaryInputJsonSchema, CommentTriageSummaryResponseJsonSchema, GetPrInputJsonSchema, GetPrResponseJsonSchema, IssueViewInputJsonSchema, IssueViewResponseJsonSchema, IssueUpdateInputJsonSchema, IssueUpdateResponseJsonSchema, IssueCommentInputJsonSchema, IssueCommentResponseJsonSchema, CreateIssueInputJsonSchema, CreateIssueResponseJsonSchema, GetCommandSchemaInputJsonSchema, GetCommandSchemaResponseJsonSchema, ReadAgentContextInputJsonSchema as ReadAgentContextSchema, ReadAgentContextResponseJsonSchema, MultiExecInputJsonSchema, MultiExecResponseJsonSchema, CreateJulesSessionInputJsonSchema, JulesSessionIdInputJsonSchema, JulesSendMessageInputJsonSchema, JulesSendMessageResponseJsonSchema, JulesGetMessagesResponseJsonSchema, JulesListSessionsInputJsonSchema, JulesListSessionsResponseJsonSchema, JulesSessionResponseJsonSchema, JulesGetPrResponseJsonSchema, JulesTriggerFeedbackResponseJsonSchema, GetPrReviewsInputJsonSchema, GetPrReviewsResponseJsonSchema, GetPrCommitsInputJsonSchema, GetPrCommitsResponseJsonSchema, GetWorkflowRunsInputJsonSchema, GetWorkflowRunsResponseJsonSchema, } from "../tools/contract.js";
2
2
  export const MCP_PROMPTS = [
3
3
  {
4
4
  name: "conflict-scout",
@@ -38,12 +38,30 @@ export const MCP_RESOURCES = [
38
38
  mimeType: "text/markdown",
39
39
  description: "The agent contract specification file (.agents/AGENT_CONTRACT.md).",
40
40
  },
41
+ {
42
+ uri: "mcp://workspace/project-config",
43
+ name: "Project Configuration",
44
+ mimeType: "application/json",
45
+ description: "The project configuration (project_config.json).",
46
+ },
41
47
  {
42
48
  uri: "mcp://git/changed-files",
43
49
  name: "Git Changed Files",
44
50
  mimeType: "application/json",
45
51
  description: "List of changed files in the repository.",
46
52
  },
53
+ {
54
+ uri: "mcp://git/status",
55
+ name: "Git Status",
56
+ mimeType: "application/json",
57
+ description: "Current working directory git status.",
58
+ },
59
+ {
60
+ uri: "mcp://ci/latest-run",
61
+ name: "Latest CI Run",
62
+ mimeType: "application/json",
63
+ description: "Recent CI exit statuses, workflow runs, and annotations.",
64
+ },
47
65
  {
48
66
  uri: "repo://package-json",
49
67
  name: "package.json",
@@ -77,215 +95,232 @@ export const MCP_RESOURCE_TEMPLATES = [
77
95
  description: "The validation report for a specific repair branch.",
78
96
  },
79
97
  ];
80
- const DEFAULT_OUTPUT_SCHEMA = {
81
- type: "object",
82
- properties: {},
83
- additionalProperties: true,
84
- };
85
98
  const BASE_MCP_TOOLS = [
86
99
  {
87
100
  name: "boomtick.health",
88
101
  description: "Check the health and configuration of the MCP server.",
89
102
  inputSchema: HealthCheckInputJsonSchema,
90
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
103
+ outputSchema: HealthCheckResponseJsonSchema,
91
104
  },
92
105
  {
93
106
  name: "github.search_open_prs",
94
107
  description: "Search for open pull requests in the repository.",
95
108
  inputSchema: SearchPRsInputJsonSchema,
96
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
109
+ outputSchema: SearchPRsResponseJsonSchema,
97
110
  },
98
111
  {
99
112
  name: "repo.read_agent_context",
100
- description: "Read the full repository index including file tree, CLI schema, and MCP tools.",
113
+ description: "Read the repository index including metadata, model review chains, or multi-repo ecosystem status.",
101
114
  inputSchema: ReadAgentContextSchema,
102
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
115
+ outputSchema: ReadAgentContextResponseJsonSchema,
116
+ },
117
+ {
118
+ name: "repo.multi_exec",
119
+ description: "Execute a shell command sequentially across ecosystem repositories (or a filtered subset).",
120
+ inputSchema: MultiExecInputJsonSchema,
121
+ outputSchema: MultiExecResponseJsonSchema,
103
122
  },
104
123
  {
105
124
  name: "github.get_pr_diff",
106
125
  description: "Get the diff and changed files for a pull request.",
107
126
  inputSchema: GetPrDiffInputJsonSchema,
108
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
127
+ outputSchema: PrDiffResponseJsonSchema,
109
128
  },
110
129
  {
111
130
  name: "github.checkout_branch",
112
131
  description: "Checkout a specific branch in the repository or worktree.",
113
132
  inputSchema: CheckoutBranchInputJsonSchema,
114
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
133
+ outputSchema: CheckoutBranchResponseJsonSchema,
115
134
  },
116
135
  {
117
136
  name: "github.get_merge_conflict_files",
118
137
  description: "Detect files that conflict when a PR is merged with the base branch.",
119
138
  inputSchema: GetMergeConflictFilesInputJsonSchema,
120
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
139
+ outputSchema: GetMergeConflictFilesResponseJsonSchema,
121
140
  },
122
141
  {
123
142
  name: "repo.get_changed_files",
124
143
  description: "Get the list of changed files between two refs.",
125
144
  inputSchema: GetChangedFilesInputJsonSchema,
126
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
145
+ outputSchema: GetChangedFilesResponseJsonSchema,
127
146
  },
128
147
  {
129
148
  name: "repo.get_command_schema",
130
149
  description: "Retrieve a lightweight, targeted schema for a specific td subcommand. Use this instead of reading the entire cli-schema.json to save tokens.",
131
150
  inputSchema: GetCommandSchemaInputJsonSchema,
132
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
151
+ outputSchema: GetCommandSchemaResponseJsonSchema,
133
152
  },
134
153
  {
135
154
  name: "repo.get_package_scripts",
136
155
  description: "Get the scripts defined in package.json.",
137
156
  inputSchema: GetPackageScriptsInputJsonSchema,
138
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
157
+ outputSchema: GetPackageScriptsResponseJsonSchema,
139
158
  },
140
159
  {
141
160
  name: "repo.read_ci_logs",
142
161
  description: "Read CI logs for a given pull request.",
143
162
  inputSchema: ReadCiLogsInputJsonSchema,
144
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
163
+ outputSchema: ReadCiLogsResponseJsonSchema,
145
164
  },
146
165
  {
147
166
  name: "repo.logs",
148
167
  description: "Stream or grep combined CI logs for all jobs in a pull request.",
149
168
  inputSchema: RepoLogsInputJsonSchema,
150
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
169
+ outputSchema: RepoLogsResponseJsonSchema,
151
170
  },
152
171
  {
153
172
  name: "repo.create_branch",
154
173
  description: "Creates a new clean git branch from a target base branch.",
155
174
  inputSchema: CreateBranchInputJsonSchema,
156
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
175
+ outputSchema: CreateBranchResponseJsonSchema,
157
176
  },
158
177
  {
159
178
  name: "repo.create_repair_branch",
160
179
  description: "Creates a fresh worktree and a new repair branch for an existing PR. This isolates the repair work from the main codebase.",
161
180
  inputSchema: CreateRepairBranchInputJsonSchema,
162
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
181
+ outputSchema: CreateRepairBranchResponseJsonSchema,
182
+ },
183
+ {
184
+ name: "repo.check_jules_status",
185
+ description: "Check or poll the status of a Jules remediation session and verify its branch or PR status.",
186
+ inputSchema: {
187
+ type: "object",
188
+ properties: {
189
+ sessionId: { type: "string", description: "The unique ID or task ID of the Jules session." },
190
+ commitSha: { type: "string", description: "Commit SHA for idempotency key verification." },
191
+ failureLog: { type: "string", description: "Failure log content for idempotency key calculation." },
192
+ poll: { type: "boolean", description: "Whether to poll until session completion or timeout." },
193
+ timeoutMs: { type: "integer", description: "Timeout in milliseconds when polling." },
194
+ intervalMs: { type: "integer", description: "Polling interval in milliseconds." },
195
+ },
196
+ },
197
+ outputSchema: CheckJulesStatusResponseJsonSchema,
163
198
  },
164
199
  {
165
200
  name: "repo.run_tests",
166
201
  description: "Run repository tests and checks.",
167
202
  inputSchema: RunTestsInputJsonSchema,
168
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
203
+ outputSchema: RunTestsResponseJsonSchema,
169
204
  },
170
205
  {
171
206
  name: "repo.commit_patch",
172
207
  description: "Commit verified repair changes in a worktree.",
173
208
  inputSchema: CommitPatchInputJsonSchema,
174
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
209
+ outputSchema: CommitPatchResponseJsonSchema,
175
210
  },
176
211
  {
177
212
  name: "github.open_replacement_pr",
178
213
  description: "Open a new PR that replaces or repairs the original PR.",
179
214
  inputSchema: OpenReplacementPrInputJsonSchema,
180
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
215
+ outputSchema: OpenReplacementPrResponseJsonSchema,
181
216
  },
182
217
  {
183
218
  name: "github.create_pull_request",
184
219
  description: "Creates a pull request on GitHub using the MCP server's integrated credentials. Bypasses terminal CLI constraints.",
185
220
  inputSchema: CreatePullRequestInputJsonSchema,
186
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
221
+ outputSchema: CreatePullRequestResponseJsonSchema,
187
222
  },
188
223
  {
189
224
  name: "github.comment_triage_summary",
190
225
  description: "Comment on the original PR with a diagnosis and replacement link.",
191
226
  inputSchema: CommentTriageSummaryInputJsonSchema,
192
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
227
+ outputSchema: CommentTriageSummaryResponseJsonSchema,
193
228
  },
194
229
  {
195
230
  name: "github.get_pr",
196
231
  description: "Get details of a GitHub PR including title, state, and URLs.",
197
232
  inputSchema: GetPrInputJsonSchema,
198
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
233
+ outputSchema: GetPrResponseJsonSchema,
199
234
  },
200
235
  {
201
236
  name: "github.get_pr_reviews",
202
237
  description: "Get top-level reviews, verdicts, states, and bodies for a GitHub PR.",
203
238
  inputSchema: GetPrReviewsInputJsonSchema,
204
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
239
+ outputSchema: GetPrReviewsResponseJsonSchema,
205
240
  },
206
241
  {
207
242
  name: "github.get_pr_commits",
208
243
  description: "List commits and commit metadata (SHAs, authors, messages) for a GitHub PR.",
209
244
  inputSchema: GetPrCommitsInputJsonSchema,
210
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
245
+ outputSchema: GetPrCommitsResponseJsonSchema,
211
246
  },
212
247
  {
213
248
  name: "repo.get_workflow_runs",
214
249
  description: "List recent GitHub Actions workflow runs for the repository.",
215
250
  inputSchema: GetWorkflowRunsInputJsonSchema,
216
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
251
+ outputSchema: GetWorkflowRunsResponseJsonSchema,
217
252
  },
218
253
  {
219
254
  name: "github.issue_view",
220
255
  description: "View details of a GitHub issue including title, body, and state.",
221
256
  inputSchema: IssueViewInputJsonSchema,
222
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
257
+ outputSchema: IssueViewResponseJsonSchema,
223
258
  },
224
259
  {
225
260
  name: "github.issue_update",
226
261
  description: "Update a GitHub issue's body, labels, and/or state.",
227
262
  inputSchema: IssueUpdateInputJsonSchema,
228
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
263
+ outputSchema: IssueUpdateResponseJsonSchema,
229
264
  },
230
265
  {
231
266
  name: "github.issue_comment",
232
267
  description: "Add a new comment to a GitHub issue.",
233
268
  inputSchema: IssueCommentInputJsonSchema,
234
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
269
+ outputSchema: IssueCommentResponseJsonSchema,
235
270
  },
236
271
  {
237
272
  name: "github.create_issue",
238
273
  description: "Create a new GitHub issue.",
239
274
  inputSchema: CreateIssueInputJsonSchema,
240
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
275
+ outputSchema: CreateIssueResponseJsonSchema,
241
276
  },
242
277
  {
243
278
  name: "jules.create_session",
244
279
  description: "Create a Jules session that performs work externally and may generate a GitHub pull request.",
245
280
  inputSchema: CreateJulesSessionInputJsonSchema,
246
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
281
+ outputSchema: JulesSessionResponseJsonSchema,
247
282
  },
248
283
  {
249
284
  name: "jules.get_session",
250
285
  description: "Get the status and details of a Jules session.",
251
286
  inputSchema: JulesSessionIdInputJsonSchema,
252
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
287
+ outputSchema: JulesSessionResponseJsonSchema,
253
288
  },
254
289
  {
255
290
  name: "jules.send_message",
256
291
  description: "Send a message to an active Jules session.",
257
292
  inputSchema: JulesSendMessageInputJsonSchema,
258
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
293
+ outputSchema: JulesSendMessageResponseJsonSchema,
259
294
  },
260
295
  {
261
296
  name: "jules.get_messages",
262
297
  description: "Get the message history of a Jules session.",
263
298
  inputSchema: JulesSessionIdInputJsonSchema,
264
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
299
+ outputSchema: JulesGetMessagesResponseJsonSchema,
265
300
  },
266
301
  {
267
302
  name: "jules.list_sessions",
268
303
  description: "List all Jules sessions.",
269
304
  inputSchema: JulesListSessionsInputJsonSchema,
270
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
305
+ outputSchema: JulesListSessionsResponseJsonSchema,
271
306
  },
272
307
  {
273
308
  name: "jules.cancel_session",
274
309
  description: "Cancel an ongoing Jules session.",
275
310
  inputSchema: JulesSessionIdInputJsonSchema,
276
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
311
+ outputSchema: JulesSessionResponseJsonSchema,
277
312
  },
278
313
  {
279
314
  name: "jules.get_pr",
280
315
  description: "Get the generated pull request url associated with an active Jules agent session.",
281
316
  inputSchema: JulesSessionIdInputJsonSchema,
282
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
317
+ outputSchema: JulesGetPrResponseJsonSchema,
283
318
  },
284
319
  {
285
320
  name: "jules.trigger_feedback",
286
321
  description: "Automatically collect CI status/logs for the PR associated with a Jules session and send them back as feedback.",
287
322
  inputSchema: JulesSessionIdInputJsonSchema,
288
- outputSchema: DEFAULT_OUTPUT_SCHEMA,
323
+ outputSchema: JulesTriggerFeedbackResponseJsonSchema,
289
324
  },
290
325
  ];
291
326
  export const MCP_TOOLS = BASE_MCP_TOOLS.flatMap((tool) => {
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,33 @@ 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 metadata
19
- if (url.pathname === "/.well-known/oauth-protected-resource" && req.method === "GET") {
18
+ // Endpoint 0: Health check endpoint for readiness probes / load balancers
19
+ if (url.pathname === "/health" && req.method === "GET") {
20
20
  res.writeHead(200, { "Content-Type": "application/json" });
21
- res.end(JSON.stringify({
22
- resource: "mcp://boomtick-mcp",
23
- "authorization_servers": [
21
+ res.end(JSON.stringify({ status: "ok", transport: "http" }));
22
+ return;
23
+ }
24
+ // Endpoint 1: RFC 9728 OAuth Protected Resource Metadata Discovery
25
+ if (url.pathname === "/.well-known/oauth-protected-resource" && (req.method === "GET" || req.method === "HEAD")) {
26
+ const metadata = {
27
+ resource: process.env.MCP_RESOURCE_URI || process.env.BOOMTICK_RESOURCE_URI || "mcp://boomtick-mcp",
28
+ authorization_servers: [
24
29
  process.env.MCP_OAUTH_AUTH_SERVER || "https://auth.boomtick.dev",
25
30
  ],
26
31
  scopes_supported: ["mcp:read", "mcp:write"],
27
- }));
32
+ bearer_methods_supported: ["header"],
33
+ resource_documentation: process.env.MCP_RESOURCE_DOCS || "https://github.com/arii/boomtick",
34
+ };
35
+ res.writeHead(200, { "Content-Type": "application/json" });
36
+ if (req.method === "HEAD") {
37
+ res.end();
38
+ }
39
+ else {
40
+ res.end(JSON.stringify(metadata, null, 2));
41
+ }
28
42
  return;
29
43
  }
30
- // Bearer Token Validation for SSE & Messages endpoints
44
+ // Bearer Token / OAuth 2.1 Validation for Protected Routes (/sse & /messages)
31
45
  if (url.pathname === "/sse" || url.pathname === "/messages") {
32
46
  if (!bearerToken) {
33
47
  res.writeHead(500, { "Content-Type": "application/json" });
@@ -44,7 +58,7 @@ export function createMcpHttpServer(options) {
44
58
  if (!isMatch) {
45
59
  res.writeHead(401, {
46
60
  "Content-Type": "application/json",
47
- "WWW-Authenticate": 'Bearer realm="boomtick-mcp", error="invalid_token"',
61
+ "WWW-Authenticate": 'Bearer realm="boomtick-mcp", error="invalid_token", error_description="Invalid or missing Bearer token"',
48
62
  });
49
63
  res.end(JSON.stringify({ error: "unauthorized", message: "Invalid or missing Bearer token" }));
50
64
  return;
@@ -52,9 +66,18 @@ export function createMcpHttpServer(options) {
52
66
  }
53
67
  // Endpoint 2: SSE stream setup
54
68
  if (url.pathname === "/sse" && req.method === "GET") {
69
+ res.setHeader("Cache-Control", "no-cache");
70
+ res.setHeader("Content-Type", "text/event-stream");
71
+ res.setHeader("Connection", "keep-alive");
55
72
  const transport = new SSEServerTransport("/messages", res);
56
73
  transports.set(transport.sessionId, transport);
74
+ const keepAliveInterval = setInterval(() => {
75
+ if (!res.writableEnded) {
76
+ res.write(": keep-alive\n\n");
77
+ }
78
+ }, 15000);
57
79
  transport.onclose = () => {
80
+ clearInterval(keepAliveInterval);
58
81
  transports.delete(transport.sessionId);
59
82
  };
60
83
  await mcpServer.connect(transport);
@@ -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,62 @@ 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 200 on GET /health endpoint", async () => {
18
+ httpServerObj = createMcpHttpServer({ port: 0, mcpServer });
19
+ port = await httpServerObj.listen(0);
20
+ const res = await fetch(`http://localhost:${port}/health`);
21
+ expect(res.status).toBe(200);
22
+ const body = await res.json();
23
+ expect(body).toEqual({ status: "ok", transport: "http" });
24
+ });
25
+ it("should respond with RFC 9728 OAuth protected resource metadata on /.well-known/oauth-protected-resource", async () => {
17
26
  httpServerObj = createMcpHttpServer({ port: 0, mcpServer });
18
27
  port = await httpServerObj.listen(0);
19
28
  const res = await fetch(`http://localhost:${port}/.well-known/oauth-protected-resource`);
20
29
  expect(res.status).toBe(200);
30
+ expect(res.headers.get("content-type")).toContain("application/json");
21
31
  const body = await res.json();
22
32
  expect(body.resource).toBe("mcp://boomtick-mcp");
23
- expect(body.authorization_servers).toBeDefined();
33
+ expect(body.authorization_servers).toEqual(["https://auth.boomtick.dev"]);
24
34
  expect(body.scopes_supported).toEqual(["mcp:read", "mcp:write"]);
35
+ expect(body.bearer_methods_supported).toEqual(["header"]);
36
+ expect(body.resource_documentation).toBe("https://github.com/arii/boomtick");
37
+ });
38
+ it("should handle HEAD requests for /.well-known/oauth-protected-resource", async () => {
39
+ httpServerObj = createMcpHttpServer({ port: 0, mcpServer });
40
+ port = await httpServerObj.listen(0);
41
+ const res = await fetch(`http://localhost:${port}/.well-known/oauth-protected-resource`, {
42
+ method: "HEAD",
43
+ });
44
+ expect(res.status).toBe(200);
45
+ const text = await res.text();
46
+ expect(text).toBe("");
25
47
  });
26
- it("should reject requests when Bearer token is missing or invalid", async () => {
48
+ it("should handle OPTIONS preflight CORS requests", async () => {
49
+ httpServerObj = createMcpHttpServer({ port: 0, mcpServer });
50
+ port = await httpServerObj.listen(0);
51
+ const res = await fetch(`http://localhost:${port}/sse`, {
52
+ method: "OPTIONS",
53
+ });
54
+ expect(res.status).toBe(204);
55
+ expect(res.headers.get("access-control-allow-origin")).toBe("*");
56
+ expect(res.headers.get("access-control-allow-methods")).toContain("GET");
57
+ expect(res.headers.get("access-control-allow-headers")).toContain("Authorization");
58
+ });
59
+ it("should reject protected requests when Bearer token is missing or invalid", async () => {
27
60
  httpServerObj = createMcpHttpServer({ port: 0, bearerToken: "secret123", mcpServer });
28
61
  port = await httpServerObj.listen(0);
29
62
  // Request without token
30
63
  const res1 = await fetch(`http://localhost:${port}/sse`);
31
64
  expect(res1.status).toBe(401);
32
65
  expect(res1.headers.get("www-authenticate")).toContain('Bearer realm="boomtick-mcp"');
66
+ expect(res1.headers.get("www-authenticate")).toContain('error="invalid_token"');
33
67
  // Request with wrong token
34
68
  const res2 = await fetch(`http://localhost:${port}/sse`, {
35
69
  headers: { Authorization: "Bearer wrong" },
36
70
  });
37
71
  expect(res2.status).toBe(401);
38
- // Request with valid token
72
+ // Public resource metadata accessible with or without token
39
73
  const res3 = await fetch(`http://localhost:${port}/.well-known/oauth-protected-resource`, {
40
74
  headers: { Authorization: "Bearer secret123" },
41
75
  });
@@ -55,4 +89,28 @@ describe("createMcpHttpServer", () => {
55
89
  const body = await res.json();
56
90
  expect(body.error).toContain("Server misconfiguration");
57
91
  });
92
+ it("should return 400 for message requests without session or invalid session", async () => {
93
+ httpServerObj = createMcpHttpServer({ port: 0, bearerToken: "secret123", mcpServer });
94
+ port = await httpServerObj.listen(0);
95
+ const res = await fetch(`http://localhost:${port}/messages?sessionId=invalid_session`, {
96
+ method: "POST",
97
+ headers: {
98
+ Authorization: "Bearer secret123",
99
+ "Content-Type": "application/json",
100
+ },
101
+ body: JSON.stringify({ jsonrpc: "2.0", method: "ping", id: 1 }),
102
+ });
103
+ expect(res.status).toBe(400);
104
+ const body = await res.json();
105
+ expect(body.error).toContain("Session not found or expired");
106
+ });
107
+ it("should allow BoomtickMCPServer to start HTTP server via run() when transport is http", async () => {
108
+ process.env.MCP_TRANSPORT = "http";
109
+ process.env.PORT = "0";
110
+ process.env.BOOMTICK_MCP_BEARER_TOKEN = "test-token";
111
+ const boomtickServer = new BoomtickMCPServer();
112
+ await boomtickServer.run();
113
+ // Verify instance initialized properly
114
+ expect(boomtickServer).toBeInstanceOf(BoomtickMCPServer);
115
+ });
58
116
  });
@@ -12,6 +12,7 @@ import { getMergeConflictFilesHandler, GetMergeConflictFilesInputSchema } from "
12
12
  import { checkoutBranchHandler, CheckoutBranchInputSchema } from "../tools/github.checkout_branch.js";
13
13
  import { getChangedFilesHandler, GetChangedFilesInputSchema } from "../tools/repo.get_changed_files.js";
14
14
  import { readAgentContextHandler, ReadAgentContextInputSchema } from "../tools/repo.read_agent_context.js";
15
+ import { multiExecHandler, MultiExecInputSchema } from "../tools/repo.multi_exec.js";
15
16
  import { getCommandSchemaHandler, GetCommandSchemaInputSchema } from "../tools/repo.get_command_schema.js";
16
17
  import { getPackageScriptsHandler, GetPackageScriptsInputSchema } from "../tools/repo.get_package_scripts.js";
17
18
  import { readCiLogsHandler, ReadCiLogsInputSchema } from "../tools/repo.read_ci_logs.js";
@@ -20,6 +21,7 @@ import { getWorkflowRunsHandler, GetWorkflowRunsInputSchema } from "../tools/rep
20
21
  import { createRepairBranchHandler, CreateRepairBranchInputSchema } from "../tools/repo.create_repair_branch.js";
21
22
  import { createBranchHandler, CreateBranchInputSchema } from "../tools/repo.create_branch.js";
22
23
  import { runTestsHandler, RunTestsInputSchema } from "../tools/repo.run_tests.js";
24
+ import { checkJulesStatusHandler, CheckJulesStatusInputSchema } from "../tools/repo.check_jules_status.js";
23
25
  import { commitPatchHandler, CommitPatchInputSchema } from "../tools/repo.commit_patch.js";
24
26
  import { openReplacementPrHandler, OpenReplacementPrInputSchema } from "../tools/github.open_replacement_pr.js";
25
27
  import { commentTriageSummaryHandler, CommentTriageSummaryInputSchema } from "../tools/github.comment_triage_summary.js";
@@ -113,6 +115,21 @@ export class BoomtickMCPServer {
113
115
  contents: [{ uri, mimeType: "text/markdown", text: content }],
114
116
  };
115
117
  }
118
+ if (uri === "mcp://workspace/project-config" ||
119
+ uri === "mcp://workspace/project_config" ||
120
+ uri === "repo://project-config") {
121
+ const configPath = path.join(config.repoPath, "project_config.json");
122
+ let content = "{}";
123
+ try {
124
+ content = await fs.readFile(configPath, "utf-8");
125
+ }
126
+ catch {
127
+ // Fallback if file doesn't exist
128
+ }
129
+ return {
130
+ contents: [{ uri, mimeType: "application/json", text: content }],
131
+ };
132
+ }
116
133
  if (uri === "mcp://git/changed-files" ||
117
134
  uri === "repo://changed-files") {
118
135
  const changedFiles = await getChangedFilesHandler(GetChangedFilesInputSchema.parse({}));
@@ -120,6 +137,29 @@ export class BoomtickMCPServer {
120
137
  contents: [{ uri, mimeType: "application/json", text: JSON.stringify(changedFiles, null, 2) }],
121
138
  };
122
139
  }
140
+ if (uri === "mcp://git/status" ||
141
+ uri === "repo://git-status" ||
142
+ uri === "repo://status") {
143
+ let statusText = "";
144
+ try {
145
+ const res = spawnSync("git", ["status", "--porcelain"], { cwd: config.repoPath, encoding: "utf-8" });
146
+ statusText = res.stdout || "";
147
+ }
148
+ catch {
149
+ // Fallback if git fails
150
+ }
151
+ return {
152
+ contents: [{ uri, mimeType: "application/json", text: JSON.stringify({ status: statusText.trim() }, null, 2) }],
153
+ };
154
+ }
155
+ if (uri === "mcp://ci/latest-run" ||
156
+ uri === "repo://ci/latest-run" ||
157
+ uri === "repo://ci-latest-run") {
158
+ const workflowRuns = await getWorkflowRunsHandler(GetWorkflowRunsInputSchema.parse({}));
159
+ return {
160
+ contents: [{ uri, mimeType: "application/json", text: JSON.stringify(workflowRuns, null, 2) }],
161
+ };
162
+ }
123
163
  if (uri === "repo://package-json" || uri === "mcp://workspace/package-json") {
124
164
  const content = await fs.readFile(path.join(config.repoPath, "package.json"), "utf-8");
125
165
  return {
@@ -169,6 +209,9 @@ export class BoomtickMCPServer {
169
209
  case "repo.read.agent.context":
170
210
  case "repo.read_agent_context":
171
211
  return createSuccessResult(await readAgentContextHandler(ReadAgentContextInputSchema.parse(normArgs)));
212
+ case "repo.multi.exec":
213
+ case "repo.multi_exec":
214
+ return createSuccessResult(await multiExecHandler(MultiExecInputSchema.parse(normArgs)));
172
215
  case "github.get.pr.diff":
173
216
  case "github.get_pr_diff":
174
217
  return createSuccessResult(await getPrDiffHandler(GetPrDiffInputSchema.parse(normArgs)));
@@ -201,6 +244,9 @@ export class BoomtickMCPServer {
201
244
  case "repo.create.repair.branch":
202
245
  case "repo.create_repair_branch":
203
246
  return createSuccessResult(await createRepairBranchHandler(CreateRepairBranchInputSchema.parse(normArgs)));
247
+ case "repo.check.jules.status":
248
+ case "repo.check_jules_status":
249
+ return createSuccessResult(await checkJulesStatusHandler(CheckJulesStatusInputSchema.parse(normArgs)));
204
250
  case "repo.run.tests":
205
251
  case "repo.run_tests":
206
252
  return createSuccessResult(await runTestsHandler(RunTestsInputSchema.parse(normArgs)));
@@ -335,20 +381,33 @@ export class BoomtickMCPServer {
335
381
  process.exit(1);
336
382
  }
337
383
  }
338
- async startHttp(port = parseInt(process.env.PORT || process.env.HTTP_PORT || "3000", 10), bearerToken) {
339
- this.prepareEnvironmentAndPreflight();
340
- const mcpHttp = createMcpHttpServer({ port, bearerToken, mcpServer: this.server });
341
- return await mcpHttp.listen(port);
342
- }
343
384
  async run() {
344
385
  this.prepareEnvironmentAndPreflight();
386
+ const args = process.argv.slice(2);
387
+ let port = parseInt(process.env.PORT || process.env.HTTP_PORT || "3000", 10);
388
+ let bearerToken = process.env.BOOMTICK_MCP_BEARER_TOKEN || process.env.MCP_BEARER_TOKEN || process.env.AUTH_TOKEN;
389
+ for (let i = 0; i < args.length; i++) {
390
+ if (args[i] === "--port" && i + 1 < args.length) {
391
+ port = parseInt(args[i + 1], 10);
392
+ i++;
393
+ }
394
+ else if (args[i].startsWith("--port=")) {
395
+ port = parseInt(args[i].split("=")[1], 10);
396
+ }
397
+ else if (args[i] === "--bearer-token" && i + 1 < args.length) {
398
+ bearerToken = args[i + 1];
399
+ i++;
400
+ }
401
+ else if (args[i].startsWith("--bearer-token=")) {
402
+ bearerToken = args[i].split("=")[1];
403
+ }
404
+ }
345
405
  const isHttp = process.env.MCP_TRANSPORT === "http" ||
346
- process.argv.includes("--http") ||
347
- (process.env.PORT !== undefined && !process.argv.includes("--stdio")) ||
348
- (process.env.HTTP_PORT !== undefined && !process.argv.includes("--stdio"));
406
+ args.includes("--http") ||
407
+ (process.env.PORT !== undefined && !args.includes("--stdio")) ||
408
+ (process.env.HTTP_PORT !== undefined && !args.includes("--stdio"));
349
409
  if (isHttp) {
350
- const port = parseInt(process.env.PORT || process.env.HTTP_PORT || "3000", 10);
351
- const mcpHttp = createMcpHttpServer({ port, mcpServer: this.server });
410
+ const mcpHttp = createMcpHttpServer({ port, bearerToken, mcpServer: this.server });
352
411
  await mcpHttp.listen(port);
353
412
  return;
354
413
  }