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