@arii/boomtick-mcp 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mcp/schemas/boomtick.health.json +5 -0
- package/.mcp/schemas/boomtick_health.json +5 -0
- package/.mcp/schemas/github.checkout_branch.json +5 -0
- package/.mcp/schemas/github.comment_triage_summary.json +5 -0
- package/.mcp/schemas/github.create_issue.json +5 -0
- package/.mcp/schemas/github.create_pull_request.json +5 -0
- package/.mcp/schemas/github.get_merge_conflict_files.json +5 -0
- package/.mcp/schemas/github.get_pr.json +5 -0
- package/.mcp/schemas/github.get_pr_commits.json +5 -0
- package/.mcp/schemas/github.get_pr_diff.json +5 -0
- package/.mcp/schemas/github.get_pr_reviews.json +5 -0
- package/.mcp/schemas/github.issue_comment.json +5 -0
- package/.mcp/schemas/github.issue_update.json +5 -0
- package/.mcp/schemas/github.issue_view.json +5 -0
- package/.mcp/schemas/github.open_replacement_pr.json +5 -0
- package/.mcp/schemas/github.search_open_prs.json +5 -0
- package/.mcp/schemas/github_checkout_branch.json +5 -0
- package/.mcp/schemas/github_comment_triage_summary.json +5 -0
- package/.mcp/schemas/github_create_issue.json +5 -0
- package/.mcp/schemas/github_create_pull_request.json +5 -0
- package/.mcp/schemas/github_get_merge_conflict_files.json +5 -0
- package/.mcp/schemas/github_get_pr.json +5 -0
- package/.mcp/schemas/github_get_pr_commits.json +5 -0
- package/.mcp/schemas/github_get_pr_diff.json +5 -0
- package/.mcp/schemas/github_get_pr_reviews.json +5 -0
- package/.mcp/schemas/github_issue_comment.json +5 -0
- package/.mcp/schemas/github_issue_update.json +5 -0
- package/.mcp/schemas/github_issue_view.json +5 -0
- package/.mcp/schemas/github_open_replacement_pr.json +5 -0
- package/.mcp/schemas/github_search_open_prs.json +5 -0
- package/.mcp/schemas/jules.cancel_session.json +5 -0
- package/.mcp/schemas/jules.create_session.json +5 -0
- package/.mcp/schemas/jules.get_messages.json +5 -0
- package/.mcp/schemas/jules.get_pr.json +5 -0
- package/.mcp/schemas/jules.get_session.json +5 -0
- package/.mcp/schemas/jules.list_sessions.json +5 -0
- package/.mcp/schemas/jules.send_message.json +5 -0
- package/.mcp/schemas/jules.trigger_feedback.json +5 -0
- package/.mcp/schemas/jules_cancel_session.json +5 -0
- package/.mcp/schemas/jules_create_session.json +5 -0
- package/.mcp/schemas/jules_get_messages.json +5 -0
- package/.mcp/schemas/jules_get_pr.json +5 -0
- package/.mcp/schemas/jules_get_session.json +5 -0
- package/.mcp/schemas/jules_list_sessions.json +5 -0
- package/.mcp/schemas/jules_send_message.json +5 -0
- package/.mcp/schemas/jules_trigger_feedback.json +5 -0
- package/.mcp/schemas/repo.commit_patch.json +5 -0
- package/.mcp/schemas/repo.create_branch.json +5 -0
- package/.mcp/schemas/repo.create_repair_branch.json +5 -0
- package/.mcp/schemas/repo.get_changed_files.json +5 -0
- package/.mcp/schemas/repo.get_command_schema.json +6 -1
- package/.mcp/schemas/repo.get_package_scripts.json +5 -0
- package/.mcp/schemas/repo.get_workflow_runs.json +5 -0
- package/.mcp/schemas/repo.logs.json +5 -0
- package/.mcp/schemas/repo.read_agent_context.json +5 -0
- package/.mcp/schemas/repo.read_ci_logs.json +5 -0
- package/.mcp/schemas/repo.run_tests.json +5 -0
- package/.mcp/schemas/repo_commit_patch.json +5 -0
- package/.mcp/schemas/repo_create_branch.json +5 -0
- package/.mcp/schemas/repo_create_repair_branch.json +5 -0
- package/.mcp/schemas/repo_get_changed_files.json +5 -0
- package/.mcp/schemas/repo_get_command_schema.json +6 -1
- package/.mcp/schemas/repo_get_package_scripts.json +5 -0
- package/.mcp/schemas/repo_get_workflow_runs.json +5 -0
- package/.mcp/schemas/repo_logs.json +5 -0
- package/.mcp/schemas/repo_read_agent_context.json +5 -0
- package/.mcp/schemas/repo_read_ci_logs.json +5 -0
- package/.mcp/schemas/repo_run_tests.json +5 -0
- package/CHANGELOG.md +7 -0
- package/actions/ai-review/action.yml +2 -2
- package/actions/audit/action.yml +1 -1
- package/actions/ci-validate/action.yml +2 -2
- package/actions/impact-analysis/action.yml +3 -3
- package/actions/run-project-gate/action.yml +5 -5
- package/actions/setup/action.yml +1 -1
- package/actions/test-build/action.yml +1 -1
- package/dist/config.js +1 -1
- package/dist/config.test.js +3 -3
- package/dist/lib/result.js +4 -0
- package/dist/lib/result.test.js +4 -2
- package/dist/lib/shell.js +6 -6
- package/dist/mcp/definitions.js +73 -2
- package/dist/mcp/http.js +91 -0
- package/dist/mcp/http.test.js +58 -0
- package/dist/mcp/server.js +53 -13
- package/dist/mcp/server.test.js +20 -0
- package/dist/tools/contract.js +109 -1
- package/dist/tools/github.checkout_branch.js +1 -1
- package/dist/tools/github.comment_triage_summary.js +1 -1
- package/dist/tools/github.create_issue.js +1 -1
- package/dist/tools/github.create_issue.test.js +6 -6
- package/dist/tools/github.create_pull_request.js +1 -1
- package/dist/tools/github.create_pull_request.test.js +4 -4
- package/dist/tools/github.get_pr.js +1 -1
- package/dist/tools/github.get_pr_commits.js +1 -1
- package/dist/tools/github.get_pr_diff.js +1 -1
- package/dist/tools/github.get_pr_reviews.js +1 -1
- package/dist/tools/github.get_pr_reviews.test.js +9 -9
- package/dist/tools/github.issue_comment.js +1 -1
- package/dist/tools/github.issue_update.js +1 -1
- package/dist/tools/github.issue_view.js +1 -1
- package/dist/tools/github.search_open_prs.js +1 -1
- package/dist/tools/jules/cancel-session.js +1 -1
- package/dist/tools/jules/create-session.test.js +11 -11
- package/dist/tools/jules/get-messages.js +1 -1
- package/dist/tools/jules/get-messages.test.js +3 -3
- package/dist/tools/jules/get-pr.test.js +3 -3
- package/dist/tools/jules/list-sessions.js +1 -1
- package/dist/tools/jules/send-message.js +1 -1
- package/dist/tools/jules/send-message.test.js +5 -5
- package/dist/tools/jules/shared.js +4 -4
- package/dist/tools/jules/trigger-feedback.js +1 -1
- package/dist/tools/jules/trigger-feedback.test.js +5 -5
- package/dist/tools/repo.get_command_schema.js +2 -2
- package/dist/tools/repo.get_workflow_runs.js +1 -1
- package/package.json +1 -1
- package/scripts/sync-mcp-schemas.ts +2 -0
- package/src/config.test.ts +3 -3
- package/src/config.ts +1 -1
- package/src/lib/result.test.ts +4 -2
- package/src/lib/result.ts +6 -0
- package/src/lib/shell.ts +6 -6
- package/src/mcp/definitions.ts +76 -3
- package/src/mcp/http.test.ts +72 -0
- package/src/mcp/http.ts +118 -0
- package/src/mcp/server.test.ts +24 -0
- package/src/mcp/server.ts +63 -12
- package/src/tools/contract.ts +109 -1
- package/src/tools/github.checkout_branch.ts +1 -1
- package/src/tools/github.comment_triage_summary.ts +1 -1
- package/src/tools/github.create_issue.test.ts +6 -6
- package/src/tools/github.create_issue.ts +1 -1
- package/src/tools/github.create_pull_request.test.ts +4 -4
- package/src/tools/github.create_pull_request.ts +1 -1
- package/src/tools/github.get_pr.ts +1 -1
- package/src/tools/github.get_pr_commits.ts +1 -1
- package/src/tools/github.get_pr_diff.ts +1 -1
- package/src/tools/github.get_pr_reviews.test.ts +9 -9
- package/src/tools/github.get_pr_reviews.ts +1 -1
- package/src/tools/github.issue_comment.ts +1 -1
- package/src/tools/github.issue_update.ts +1 -1
- package/src/tools/github.issue_view.ts +1 -1
- package/src/tools/github.search_open_prs.ts +1 -1
- package/src/tools/jules/cancel-session.ts +1 -1
- package/src/tools/jules/create-session.test.ts +11 -11
- package/src/tools/jules/get-messages.test.ts +3 -3
- package/src/tools/jules/get-messages.ts +1 -1
- package/src/tools/jules/get-pr.test.ts +3 -3
- package/src/tools/jules/list-sessions.ts +1 -1
- package/src/tools/jules/send-message.test.ts +5 -5
- package/src/tools/jules/send-message.ts +1 -1
- package/src/tools/jules/shared.ts +4 -4
- package/src/tools/jules/trigger-feedback.test.ts +5 -5
- package/src/tools/jules/trigger-feedback.ts +1 -1
- package/src/tools/repo.get_command_schema.ts +2 -2
- package/src/tools/repo.get_workflow_runs.ts +1 -1
package/dist/mcp/definitions.js
CHANGED
|
@@ -26,6 +26,24 @@ export const MCP_PROMPTS = [
|
|
|
26
26
|
},
|
|
27
27
|
];
|
|
28
28
|
export const MCP_RESOURCES = [
|
|
29
|
+
{
|
|
30
|
+
uri: "mcp://workspace/agent-contract",
|
|
31
|
+
name: "Agent Contract",
|
|
32
|
+
mimeType: "text/markdown",
|
|
33
|
+
description: "The agent contract specification file (.agents/AGENT_CONTRACT.md).",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
uri: "repo://agent-contract",
|
|
37
|
+
name: "Agent Contract (Legacy URI)",
|
|
38
|
+
mimeType: "text/markdown",
|
|
39
|
+
description: "The agent contract specification file (.agents/AGENT_CONTRACT.md).",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
uri: "mcp://git/changed-files",
|
|
43
|
+
name: "Git Changed Files",
|
|
44
|
+
mimeType: "application/json",
|
|
45
|
+
description: "List of changed files in the repository.",
|
|
46
|
+
},
|
|
29
47
|
{
|
|
30
48
|
uri: "repo://package-json",
|
|
31
49
|
name: "package.json",
|
|
@@ -38,183 +56,236 @@ export const MCP_RESOURCES = [
|
|
|
38
56
|
mimeType: "application/json",
|
|
39
57
|
description: "The design tokens used in the repository.",
|
|
40
58
|
},
|
|
59
|
+
];
|
|
60
|
+
export const MCP_RESOURCE_TEMPLATES = [
|
|
61
|
+
{
|
|
62
|
+
uriTemplate: "mcp://git/diff/{prNumber}",
|
|
63
|
+
name: "Git PR Diff",
|
|
64
|
+
mimeType: "text/plain",
|
|
65
|
+
description: "Diff and changed files for a pull request.",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
uriTemplate: "mcp://ci/logs/{prNumber}",
|
|
69
|
+
name: "CI Logs",
|
|
70
|
+
mimeType: "application/json",
|
|
71
|
+
description: "CI logs for a pull request.",
|
|
72
|
+
},
|
|
41
73
|
{
|
|
42
|
-
|
|
74
|
+
uriTemplate: "repo://repair-report/{branch}",
|
|
43
75
|
name: "Repair Report",
|
|
44
76
|
mimeType: "application/json",
|
|
45
77
|
description: "The validation report for a specific repair branch.",
|
|
46
78
|
},
|
|
47
79
|
];
|
|
80
|
+
const DEFAULT_OUTPUT_SCHEMA = {
|
|
81
|
+
type: "object",
|
|
82
|
+
properties: {},
|
|
83
|
+
additionalProperties: true,
|
|
84
|
+
};
|
|
48
85
|
const BASE_MCP_TOOLS = [
|
|
49
86
|
{
|
|
50
87
|
name: "boomtick.health",
|
|
51
88
|
description: "Check the health and configuration of the MCP server.",
|
|
52
89
|
inputSchema: HealthCheckInputJsonSchema,
|
|
90
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
53
91
|
},
|
|
54
92
|
{
|
|
55
93
|
name: "github.search_open_prs",
|
|
56
94
|
description: "Search for open pull requests in the repository.",
|
|
57
95
|
inputSchema: SearchPRsInputJsonSchema,
|
|
96
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
58
97
|
},
|
|
59
98
|
{
|
|
60
99
|
name: "repo.read_agent_context",
|
|
61
100
|
description: "Read the full repository index including file tree, CLI schema, and MCP tools.",
|
|
62
101
|
inputSchema: ReadAgentContextSchema,
|
|
102
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
63
103
|
},
|
|
64
104
|
{
|
|
65
105
|
name: "github.get_pr_diff",
|
|
66
106
|
description: "Get the diff and changed files for a pull request.",
|
|
67
107
|
inputSchema: GetPrDiffInputJsonSchema,
|
|
108
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
68
109
|
},
|
|
69
110
|
{
|
|
70
111
|
name: "github.checkout_branch",
|
|
71
112
|
description: "Checkout a specific branch in the repository or worktree.",
|
|
72
113
|
inputSchema: CheckoutBranchInputJsonSchema,
|
|
114
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
73
115
|
},
|
|
74
116
|
{
|
|
75
117
|
name: "github.get_merge_conflict_files",
|
|
76
118
|
description: "Detect files that conflict when a PR is merged with the base branch.",
|
|
77
119
|
inputSchema: GetMergeConflictFilesInputJsonSchema,
|
|
120
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
78
121
|
},
|
|
79
122
|
{
|
|
80
123
|
name: "repo.get_changed_files",
|
|
81
124
|
description: "Get the list of changed files between two refs.",
|
|
82
125
|
inputSchema: GetChangedFilesInputJsonSchema,
|
|
126
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
83
127
|
},
|
|
84
128
|
{
|
|
85
129
|
name: "repo.get_command_schema",
|
|
86
|
-
description: "Retrieve a lightweight, targeted schema for a specific td
|
|
130
|
+
description: "Retrieve a lightweight, targeted schema for a specific td subcommand. Use this instead of reading the entire cli-schema.json to save tokens.",
|
|
87
131
|
inputSchema: GetCommandSchemaInputJsonSchema,
|
|
132
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
88
133
|
},
|
|
89
134
|
{
|
|
90
135
|
name: "repo.get_package_scripts",
|
|
91
136
|
description: "Get the scripts defined in package.json.",
|
|
92
137
|
inputSchema: GetPackageScriptsInputJsonSchema,
|
|
138
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
93
139
|
},
|
|
94
140
|
{
|
|
95
141
|
name: "repo.read_ci_logs",
|
|
96
142
|
description: "Read CI logs for a given pull request.",
|
|
97
143
|
inputSchema: ReadCiLogsInputJsonSchema,
|
|
144
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
98
145
|
},
|
|
99
146
|
{
|
|
100
147
|
name: "repo.logs",
|
|
101
148
|
description: "Stream or grep combined CI logs for all jobs in a pull request.",
|
|
102
149
|
inputSchema: RepoLogsInputJsonSchema,
|
|
150
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
103
151
|
},
|
|
104
152
|
{
|
|
105
153
|
name: "repo.create_branch",
|
|
106
154
|
description: "Creates a new clean git branch from a target base branch.",
|
|
107
155
|
inputSchema: CreateBranchInputJsonSchema,
|
|
156
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
108
157
|
},
|
|
109
158
|
{
|
|
110
159
|
name: "repo.create_repair_branch",
|
|
111
160
|
description: "Creates a fresh worktree and a new repair branch for an existing PR. This isolates the repair work from the main codebase.",
|
|
112
161
|
inputSchema: CreateRepairBranchInputJsonSchema,
|
|
162
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
113
163
|
},
|
|
114
164
|
{
|
|
115
165
|
name: "repo.run_tests",
|
|
116
166
|
description: "Run repository tests and checks.",
|
|
117
167
|
inputSchema: RunTestsInputJsonSchema,
|
|
168
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
118
169
|
},
|
|
119
170
|
{
|
|
120
171
|
name: "repo.commit_patch",
|
|
121
172
|
description: "Commit verified repair changes in a worktree.",
|
|
122
173
|
inputSchema: CommitPatchInputJsonSchema,
|
|
174
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
123
175
|
},
|
|
124
176
|
{
|
|
125
177
|
name: "github.open_replacement_pr",
|
|
126
178
|
description: "Open a new PR that replaces or repairs the original PR.",
|
|
127
179
|
inputSchema: OpenReplacementPrInputJsonSchema,
|
|
180
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
128
181
|
},
|
|
129
182
|
{
|
|
130
183
|
name: "github.create_pull_request",
|
|
131
184
|
description: "Creates a pull request on GitHub using the MCP server's integrated credentials. Bypasses terminal CLI constraints.",
|
|
132
185
|
inputSchema: CreatePullRequestInputJsonSchema,
|
|
186
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
133
187
|
},
|
|
134
188
|
{
|
|
135
189
|
name: "github.comment_triage_summary",
|
|
136
190
|
description: "Comment on the original PR with a diagnosis and replacement link.",
|
|
137
191
|
inputSchema: CommentTriageSummaryInputJsonSchema,
|
|
192
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
138
193
|
},
|
|
139
194
|
{
|
|
140
195
|
name: "github.get_pr",
|
|
141
196
|
description: "Get details of a GitHub PR including title, state, and URLs.",
|
|
142
197
|
inputSchema: GetPrInputJsonSchema,
|
|
198
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
143
199
|
},
|
|
144
200
|
{
|
|
145
201
|
name: "github.get_pr_reviews",
|
|
146
202
|
description: "Get top-level reviews, verdicts, states, and bodies for a GitHub PR.",
|
|
147
203
|
inputSchema: GetPrReviewsInputJsonSchema,
|
|
204
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
148
205
|
},
|
|
149
206
|
{
|
|
150
207
|
name: "github.get_pr_commits",
|
|
151
208
|
description: "List commits and commit metadata (SHAs, authors, messages) for a GitHub PR.",
|
|
152
209
|
inputSchema: GetPrCommitsInputJsonSchema,
|
|
210
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
153
211
|
},
|
|
154
212
|
{
|
|
155
213
|
name: "repo.get_workflow_runs",
|
|
156
214
|
description: "List recent GitHub Actions workflow runs for the repository.",
|
|
157
215
|
inputSchema: GetWorkflowRunsInputJsonSchema,
|
|
216
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
158
217
|
},
|
|
159
218
|
{
|
|
160
219
|
name: "github.issue_view",
|
|
161
220
|
description: "View details of a GitHub issue including title, body, and state.",
|
|
162
221
|
inputSchema: IssueViewInputJsonSchema,
|
|
222
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
163
223
|
},
|
|
164
224
|
{
|
|
165
225
|
name: "github.issue_update",
|
|
166
226
|
description: "Update a GitHub issue's body, labels, and/or state.",
|
|
167
227
|
inputSchema: IssueUpdateInputJsonSchema,
|
|
228
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
168
229
|
},
|
|
169
230
|
{
|
|
170
231
|
name: "github.issue_comment",
|
|
171
232
|
description: "Add a new comment to a GitHub issue.",
|
|
172
233
|
inputSchema: IssueCommentInputJsonSchema,
|
|
234
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
173
235
|
},
|
|
174
236
|
{
|
|
175
237
|
name: "github.create_issue",
|
|
176
238
|
description: "Create a new GitHub issue.",
|
|
177
239
|
inputSchema: CreateIssueInputJsonSchema,
|
|
240
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
178
241
|
},
|
|
179
242
|
{
|
|
180
243
|
name: "jules.create_session",
|
|
181
244
|
description: "Create a Jules session that performs work externally and may generate a GitHub pull request.",
|
|
182
245
|
inputSchema: CreateJulesSessionInputJsonSchema,
|
|
246
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
183
247
|
},
|
|
184
248
|
{
|
|
185
249
|
name: "jules.get_session",
|
|
186
250
|
description: "Get the status and details of a Jules session.",
|
|
187
251
|
inputSchema: JulesSessionIdInputJsonSchema,
|
|
252
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
188
253
|
},
|
|
189
254
|
{
|
|
190
255
|
name: "jules.send_message",
|
|
191
256
|
description: "Send a message to an active Jules session.",
|
|
192
257
|
inputSchema: JulesSendMessageInputJsonSchema,
|
|
258
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
193
259
|
},
|
|
194
260
|
{
|
|
195
261
|
name: "jules.get_messages",
|
|
196
262
|
description: "Get the message history of a Jules session.",
|
|
197
263
|
inputSchema: JulesSessionIdInputJsonSchema,
|
|
264
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
198
265
|
},
|
|
199
266
|
{
|
|
200
267
|
name: "jules.list_sessions",
|
|
201
268
|
description: "List all Jules sessions.",
|
|
202
269
|
inputSchema: JulesListSessionsInputJsonSchema,
|
|
270
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
203
271
|
},
|
|
204
272
|
{
|
|
205
273
|
name: "jules.cancel_session",
|
|
206
274
|
description: "Cancel an ongoing Jules session.",
|
|
207
275
|
inputSchema: JulesSessionIdInputJsonSchema,
|
|
276
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
208
277
|
},
|
|
209
278
|
{
|
|
210
279
|
name: "jules.get_pr",
|
|
211
280
|
description: "Get the generated pull request url associated with an active Jules agent session.",
|
|
212
281
|
inputSchema: JulesSessionIdInputJsonSchema,
|
|
282
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
213
283
|
},
|
|
214
284
|
{
|
|
215
285
|
name: "jules.trigger_feedback",
|
|
216
286
|
description: "Automatically collect CI status/logs for the PR associated with a Jules session and send them back as feedback.",
|
|
217
287
|
inputSchema: JulesSessionIdInputJsonSchema,
|
|
288
|
+
outputSchema: DEFAULT_OUTPUT_SCHEMA,
|
|
218
289
|
},
|
|
219
290
|
];
|
|
220
291
|
export const MCP_TOOLS = BASE_MCP_TOOLS.flatMap((tool) => {
|
package/dist/mcp/http.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import http from "node:http";
|
|
2
|
+
import crypto from "node:crypto";
|
|
3
|
+
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
4
|
+
export function createMcpHttpServer(options) {
|
|
5
|
+
const { port = 3000, bearerToken = process.env.BOOMTICK_MCP_BEARER_TOKEN || process.env.MCP_BEARER_TOKEN || process.env.AUTH_TOKEN, mcpServer, } = options;
|
|
6
|
+
const transports = new Map();
|
|
7
|
+
const server = http.createServer(async (req, res) => {
|
|
8
|
+
// CORS Headers
|
|
9
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
10
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
11
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
12
|
+
if (req.method === "OPTIONS") {
|
|
13
|
+
res.writeHead(204);
|
|
14
|
+
res.end();
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
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") {
|
|
20
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
21
|
+
res.end(JSON.stringify({
|
|
22
|
+
resource: "mcp://boomtick-mcp",
|
|
23
|
+
"authorization_servers": [
|
|
24
|
+
process.env.MCP_OAUTH_AUTH_SERVER || "https://auth.boomtick.dev",
|
|
25
|
+
],
|
|
26
|
+
scopes_supported: ["mcp:read", "mcp:write"],
|
|
27
|
+
}));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// Bearer Token Validation for SSE & Messages endpoints
|
|
31
|
+
if (url.pathname === "/sse" || url.pathname === "/messages") {
|
|
32
|
+
if (!bearerToken) {
|
|
33
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
34
|
+
res.end(JSON.stringify({ error: "Server misconfiguration: bearerToken is required for HTTP transport" }));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const authHeader = req.headers.authorization;
|
|
38
|
+
const expectedAuth = `Bearer ${bearerToken}`;
|
|
39
|
+
const authBuffer = Buffer.from(authHeader || "");
|
|
40
|
+
const expectedBuffer = Buffer.from(expectedAuth);
|
|
41
|
+
const isMatch = Boolean(authHeader) &&
|
|
42
|
+
authBuffer.length === expectedBuffer.length &&
|
|
43
|
+
crypto.timingSafeEqual(authBuffer, expectedBuffer);
|
|
44
|
+
if (!isMatch) {
|
|
45
|
+
res.writeHead(401, {
|
|
46
|
+
"Content-Type": "application/json",
|
|
47
|
+
"WWW-Authenticate": 'Bearer realm="boomtick-mcp", error="invalid_token"',
|
|
48
|
+
});
|
|
49
|
+
res.end(JSON.stringify({ error: "unauthorized", message: "Invalid or missing Bearer token" }));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Endpoint 2: SSE stream setup
|
|
54
|
+
if (url.pathname === "/sse" && req.method === "GET") {
|
|
55
|
+
const transport = new SSEServerTransport("/messages", res);
|
|
56
|
+
transports.set(transport.sessionId, transport);
|
|
57
|
+
transport.onclose = () => {
|
|
58
|
+
transports.delete(transport.sessionId);
|
|
59
|
+
};
|
|
60
|
+
await mcpServer.connect(transport);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
// Endpoint 3: POST messages
|
|
64
|
+
if (url.pathname === "/messages" && req.method === "POST") {
|
|
65
|
+
const sessionId = url.searchParams.get("sessionId");
|
|
66
|
+
const transport = sessionId ? transports.get(sessionId) : null;
|
|
67
|
+
if (transport) {
|
|
68
|
+
await transport.handlePostMessage(req, res);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
72
|
+
res.end(JSON.stringify({ error: "Session not found or expired" }));
|
|
73
|
+
}
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
77
|
+
res.end(JSON.stringify({ error: "Not found" }));
|
|
78
|
+
});
|
|
79
|
+
return {
|
|
80
|
+
server,
|
|
81
|
+
listen: (customPort) => new Promise((resolve) => {
|
|
82
|
+
const p = customPort ?? port;
|
|
83
|
+
server.listen(p, () => {
|
|
84
|
+
const addr = server.address();
|
|
85
|
+
const actualPort = typeof addr === "object" && addr ? addr.port : p;
|
|
86
|
+
console.error(`Boomtick MCP Server running on HTTP port ${actualPort}`);
|
|
87
|
+
resolve(actualPort);
|
|
88
|
+
});
|
|
89
|
+
}),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { createMcpHttpServer } from "./http.js";
|
|
4
|
+
describe("createMcpHttpServer", () => {
|
|
5
|
+
let mcpServer;
|
|
6
|
+
let httpServerObj;
|
|
7
|
+
let port;
|
|
8
|
+
beforeEach(async () => {
|
|
9
|
+
mcpServer = new Server({ name: "test-server", version: "1.0.0" }, { capabilities: {} });
|
|
10
|
+
});
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
if (httpServerObj?.server) {
|
|
13
|
+
httpServerObj.server.close();
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
it("should respond with OAuth protected resource metadata on /.well-known/oauth-protected-resource", async () => {
|
|
17
|
+
httpServerObj = createMcpHttpServer({ port: 0, mcpServer });
|
|
18
|
+
port = await httpServerObj.listen(0);
|
|
19
|
+
const res = await fetch(`http://localhost:${port}/.well-known/oauth-protected-resource`);
|
|
20
|
+
expect(res.status).toBe(200);
|
|
21
|
+
const body = await res.json();
|
|
22
|
+
expect(body.resource).toBe("mcp://boomtick-mcp");
|
|
23
|
+
expect(body.authorization_servers).toBeDefined();
|
|
24
|
+
expect(body.scopes_supported).toEqual(["mcp:read", "mcp:write"]);
|
|
25
|
+
});
|
|
26
|
+
it("should reject requests when Bearer token is missing or invalid", async () => {
|
|
27
|
+
httpServerObj = createMcpHttpServer({ port: 0, bearerToken: "secret123", mcpServer });
|
|
28
|
+
port = await httpServerObj.listen(0);
|
|
29
|
+
// Request without token
|
|
30
|
+
const res1 = await fetch(`http://localhost:${port}/sse`);
|
|
31
|
+
expect(res1.status).toBe(401);
|
|
32
|
+
expect(res1.headers.get("www-authenticate")).toContain('Bearer realm="boomtick-mcp"');
|
|
33
|
+
// Request with wrong token
|
|
34
|
+
const res2 = await fetch(`http://localhost:${port}/sse`, {
|
|
35
|
+
headers: { Authorization: "Bearer wrong" },
|
|
36
|
+
});
|
|
37
|
+
expect(res2.status).toBe(401);
|
|
38
|
+
// Request with valid token
|
|
39
|
+
const res3 = await fetch(`http://localhost:${port}/.well-known/oauth-protected-resource`, {
|
|
40
|
+
headers: { Authorization: "Bearer secret123" },
|
|
41
|
+
});
|
|
42
|
+
expect(res3.status).toBe(200);
|
|
43
|
+
});
|
|
44
|
+
it("should handle 404 for unknown endpoints", async () => {
|
|
45
|
+
httpServerObj = createMcpHttpServer({ port: 0, mcpServer });
|
|
46
|
+
port = await httpServerObj.listen(0);
|
|
47
|
+
const res = await fetch(`http://localhost:${port}/unknown-path`);
|
|
48
|
+
expect(res.status).toBe(404);
|
|
49
|
+
});
|
|
50
|
+
it("should return 500 if bearer token is not configured on server for protected endpoints", async () => {
|
|
51
|
+
httpServerObj = createMcpHttpServer({ port: 0, mcpServer });
|
|
52
|
+
port = await httpServerObj.listen(0);
|
|
53
|
+
const res = await fetch(`http://localhost:${port}/sse`);
|
|
54
|
+
expect(res.status).toBe(500);
|
|
55
|
+
const body = await res.json();
|
|
56
|
+
expect(body.error).toContain("Server misconfiguration");
|
|
57
|
+
});
|
|
58
|
+
});
|
package/dist/mcp/server.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
-
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
4
|
import { config } from "../config.js";
|
|
5
5
|
import { createSuccessResult, createErrorResult } from "../lib/result.js";
|
|
6
|
-
import { MCP_TOOLS, MCP_PROMPTS, MCP_RESOURCES } from "./definitions.js";
|
|
6
|
+
import { MCP_TOOLS, MCP_PROMPTS, MCP_RESOURCES, MCP_RESOURCE_TEMPLATES } from "./definitions.js";
|
|
7
7
|
import { normalizeArguments } from "../lib/param_utils.js";
|
|
8
8
|
import { healthHandler, HealthCheckInputSchema } from "./tools.js";
|
|
9
9
|
import { searchOpenPrsHandler, SearchOpenPrsInputSchema } from "../tools/github.search_open_prs.js";
|
|
@@ -40,6 +40,7 @@ import fs from "fs/promises";
|
|
|
40
40
|
import { existsSync, readFileSync } from "fs";
|
|
41
41
|
import path from "path";
|
|
42
42
|
import { spawnSync } from "child_process";
|
|
43
|
+
import { createMcpHttpServer } from "./http.js";
|
|
43
44
|
export class BoomtickMCPServer {
|
|
44
45
|
server;
|
|
45
46
|
constructor() {
|
|
@@ -93,29 +94,50 @@ export class BoomtickMCPServer {
|
|
|
93
94
|
resources: MCP_RESOURCES,
|
|
94
95
|
};
|
|
95
96
|
});
|
|
97
|
+
this.server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
|
|
98
|
+
return {
|
|
99
|
+
resourceTemplates: MCP_RESOURCE_TEMPLATES,
|
|
100
|
+
};
|
|
101
|
+
});
|
|
96
102
|
this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
97
103
|
const uri = request.params.uri;
|
|
98
|
-
if (uri === "
|
|
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");
|
|
109
|
+
return {
|
|
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) }],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
if (uri === "repo://package-json" || uri === "mcp://workspace/package-json") {
|
|
99
121
|
const content = await fs.readFile(path.join(config.repoPath, "package.json"), "utf-8");
|
|
100
122
|
return {
|
|
101
123
|
contents: [{ uri, mimeType: "application/json", text: content }],
|
|
102
124
|
};
|
|
103
125
|
}
|
|
104
|
-
if (uri === "repo://design-tokens") {
|
|
126
|
+
if (uri === "repo://design-tokens" || uri === "mcp://workspace/design-tokens") {
|
|
105
127
|
const tokensPath = path.join(config.repoPath, "src/styles/design-tokens.ts");
|
|
106
128
|
const content = await fs.readFile(tokensPath, "utf-8");
|
|
107
129
|
return {
|
|
108
130
|
contents: [{ uri, mimeType: "text/typescript", text: content }],
|
|
109
131
|
};
|
|
110
132
|
}
|
|
111
|
-
if (uri.startsWith("repo://diff/")) {
|
|
133
|
+
if (uri.startsWith("mcp://git/diff/") || uri.startsWith("repo://diff/")) {
|
|
112
134
|
const prNumber = parseInt(uri.split("/").pop() || "");
|
|
113
135
|
const diff = await getPrDiffHandler({ prNumber });
|
|
114
136
|
return {
|
|
115
137
|
contents: [{ uri, mimeType: "text/plain", text: diff.diffText }],
|
|
116
138
|
};
|
|
117
139
|
}
|
|
118
|
-
if (uri.startsWith("repo://ci/")) {
|
|
140
|
+
if (uri.startsWith("mcp://ci/logs/") || uri.startsWith("repo://ci/")) {
|
|
119
141
|
const prNumber = parseInt(uri.split("/").pop() || "");
|
|
120
142
|
const logs = await readCiLogsHandler({ prNumber });
|
|
121
143
|
return {
|
|
@@ -236,7 +258,7 @@ export class BoomtickMCPServer {
|
|
|
236
258
|
}
|
|
237
259
|
});
|
|
238
260
|
}
|
|
239
|
-
|
|
261
|
+
prepareEnvironmentAndPreflight() {
|
|
240
262
|
const projectRoot = config.repoPath || process.cwd();
|
|
241
263
|
const homeDir = process.env.HOME || "";
|
|
242
264
|
const extraPaths = [];
|
|
@@ -276,9 +298,9 @@ export class BoomtickMCPServer {
|
|
|
276
298
|
.join(path.delimiter);
|
|
277
299
|
// 3. SAFE REPOSITORY RESOLUTION
|
|
278
300
|
const repoPath = config.repoPath || projectRoot;
|
|
279
|
-
// 4. SECURE, UNIX-ONLY PRE-FLIGHT CHECK FOR TD
|
|
301
|
+
// 4. SECURE, UNIX-ONLY PRE-FLIGHT CHECK FOR TD
|
|
280
302
|
try {
|
|
281
|
-
const result = spawnSync("td
|
|
303
|
+
const result = spawnSync("td", ["doctor"], {
|
|
282
304
|
encoding: "utf-8",
|
|
283
305
|
cwd: repoPath,
|
|
284
306
|
shell: false // Explicitly disabled to clear Semgrep rules
|
|
@@ -290,16 +312,34 @@ export class BoomtickMCPServer {
|
|
|
290
312
|
console.error("status:", result.status);
|
|
291
313
|
console.error("stdout:", result.stdout);
|
|
292
314
|
console.error("stderr:", result.stderr);
|
|
293
|
-
throw new Error(`td
|
|
315
|
+
throw new Error(`td doctor returned exit code: ${result.status}`);
|
|
294
316
|
}
|
|
295
|
-
console.error("✅ td
|
|
317
|
+
console.error("✅ td verified on PATH securely");
|
|
296
318
|
}
|
|
297
319
|
catch (error) {
|
|
298
|
-
console.error("❌ Fatal: td
|
|
320
|
+
console.error("❌ Fatal: td is not resolvable on PATH. MCP tools will fail.");
|
|
299
321
|
console.error("Please ensure the project is installed: pip install -e .");
|
|
300
|
-
console.error("❌ Fatal: td
|
|
322
|
+
console.error("❌ Fatal: td pre-flight check failed:", error instanceof Error ? error.message : String(error));
|
|
301
323
|
process.exit(1);
|
|
302
324
|
}
|
|
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;
|
|
342
|
+
}
|
|
303
343
|
const transport = new StdioServerTransport();
|
|
304
344
|
await this.server.connect(transport);
|
|
305
345
|
console.error("Boomtick MCP Server running on stdio");
|
package/dist/mcp/server.test.js
CHANGED
|
@@ -83,8 +83,27 @@ describe("BoomtickMCPServer", () => {
|
|
|
83
83
|
expect(result.resources).toBeDefined();
|
|
84
84
|
expect(Array.isArray(result.resources)).toBe(true);
|
|
85
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
|
+
});
|
|
86
93
|
});
|
|
87
94
|
describe("ReadResource", () => {
|
|
95
|
+
it("should handle mcp://workspace/agent-contract", async () => {
|
|
96
|
+
const handler = requestHandlers.get("resources/read");
|
|
97
|
+
vi.mocked(fs.readFile).mockResolvedValue("# Agent Contract\nRule 1");
|
|
98
|
+
const result = await handler({
|
|
99
|
+
method: "resources/read",
|
|
100
|
+
params: { uri: "mcp://workspace/agent-contract" },
|
|
101
|
+
});
|
|
102
|
+
expect(fs.readFile).toHaveBeenCalledWith("/mock/repo/.agents/AGENT_CONTRACT.md", "utf-8");
|
|
103
|
+
expect(result).toEqual({
|
|
104
|
+
contents: [{ uri: "mcp://workspace/agent-contract", mimeType: "text/markdown", text: "# Agent Contract\nRule 1" }],
|
|
105
|
+
});
|
|
106
|
+
});
|
|
88
107
|
it("should handle repo://package-json", async () => {
|
|
89
108
|
const handler = requestHandlers.get("resources/read");
|
|
90
109
|
vi.mocked(fs.readFile).mockResolvedValue('{"name": "test"}');
|
|
@@ -155,6 +174,7 @@ describe("BoomtickMCPServer", () => {
|
|
|
155
174
|
});
|
|
156
175
|
expect(result).toEqual({
|
|
157
176
|
content: [{ type: "text", text: JSON.stringify({ status: "ok" }, null, 2) }],
|
|
177
|
+
structuredContent: { status: "ok" },
|
|
158
178
|
});
|
|
159
179
|
});
|
|
160
180
|
it("should return error result for unknown tool", async () => {
|