@nimbus-dev/connectors 0.2.1 → 0.2.3

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 (220) hide show
  1. package/README.md +19 -7
  2. package/connectors/airflow/src/search-filter.ts +2 -15
  3. package/connectors/airflow/src/server.ts +2 -87
  4. package/connectors/airflow/src/tools.ts +74 -0
  5. package/connectors/apple/src/adapters.ts +326 -0
  6. package/connectors/apple/src/apple-mail-core.ts +4 -8
  7. package/connectors/apple/src/calendar-tools.ts +28 -8
  8. package/connectors/apple/src/server.ts +23 -512
  9. package/connectors/apple/src/tools.ts +18 -44
  10. package/connectors/argocd/src/server.ts +25 -38
  11. package/connectors/athena/src/tools.ts +26 -43
  12. package/connectors/aws/src/server.ts +2 -183
  13. package/connectors/aws/src/tools.ts +199 -0
  14. package/connectors/azure/src/server.ts +2 -131
  15. package/connectors/azure/src/tools.ts +134 -0
  16. package/connectors/bigeye/src/search-filter.ts +4 -15
  17. package/connectors/bigeye/src/server.ts +38 -38
  18. package/connectors/bigquery/src/tools.ts +1 -26
  19. package/connectors/bitbucket/src/server.ts +2 -244
  20. package/connectors/bitbucket/src/tools.ts +241 -0
  21. package/connectors/bitrise/src/server.ts +2 -79
  22. package/connectors/bitrise/src/tools.ts +76 -0
  23. package/connectors/canva/src/search-filter.ts +2 -11
  24. package/connectors/canva/src/server.ts +2 -73
  25. package/connectors/canva/src/tools.ts +66 -0
  26. package/connectors/circleci/src/server.ts +2 -176
  27. package/connectors/circleci/src/tools.ts +193 -0
  28. package/connectors/cloud-logging/src/tools.ts +19 -35
  29. package/connectors/cloudwatch/src/tools.ts +20 -38
  30. package/connectors/codemagic/src/server.ts +2 -70
  31. package/connectors/codemagic/src/tools.ts +71 -0
  32. package/connectors/confluence/src/server.ts +2 -301
  33. package/connectors/confluence/src/tools.ts +320 -0
  34. package/connectors/dagster/src/server.ts +2 -214
  35. package/connectors/dagster/src/tools.ts +187 -0
  36. package/connectors/databricks/src/search-filter.ts +7 -7
  37. package/connectors/databricks/src/server.ts +2 -77
  38. package/connectors/databricks/src/tools.ts +66 -0
  39. package/connectors/datadog/src/server.ts +2 -48
  40. package/connectors/datadog/src/tools.ts +48 -0
  41. package/connectors/dataprofile/src/profile.ts +16 -46
  42. package/connectors/dbt/src/server.ts +2 -92
  43. package/connectors/dbt/src/tools.ts +80 -0
  44. package/connectors/dependencytrack/src/server.ts +2 -76
  45. package/connectors/dependencytrack/src/tools.ts +65 -0
  46. package/connectors/discord/src/server.ts +2 -85
  47. package/connectors/discord/src/tools.ts +82 -0
  48. package/connectors/elasticsearch/src/tools.ts +7 -37
  49. package/connectors/fastmail/src/jmap-client.ts +202 -0
  50. package/connectors/fastmail/src/jmap-core.ts +8 -21
  51. package/connectors/fastmail/src/server.ts +2 -187
  52. package/connectors/fastmail/src/tools.ts +4 -23
  53. package/connectors/figma/src/search-filter.ts +2 -11
  54. package/connectors/figma/src/server.ts +2 -113
  55. package/connectors/figma/src/tools.ts +99 -0
  56. package/connectors/firebase/src/server.ts +2 -90
  57. package/connectors/firebase/src/token.ts +2 -5
  58. package/connectors/firebase/src/tools.ts +106 -0
  59. package/connectors/flagsmith/src/server.ts +2 -98
  60. package/connectors/flagsmith/src/tools.ts +95 -0
  61. package/connectors/flux/src/server.ts +52 -53
  62. package/connectors/gcp/src/server.ts +2 -132
  63. package/connectors/gcp/src/tools.ts +123 -0
  64. package/connectors/github/src/server.ts +2 -328
  65. package/connectors/github/src/tools.ts +311 -0
  66. package/connectors/github-actions/src/server.ts +2 -205
  67. package/connectors/github-actions/src/tools.ts +209 -0
  68. package/connectors/gitlab/src/server.ts +2 -384
  69. package/connectors/gitlab/src/tools.ts +392 -0
  70. package/connectors/gmail/src/server.ts +2 -270
  71. package/connectors/gmail/src/tools.ts +209 -0
  72. package/connectors/google-drive/src/server.ts +2 -524
  73. package/connectors/google-drive/src/tools.ts +529 -0
  74. package/connectors/google-meet/src/server.ts +2 -82
  75. package/connectors/google-meet/src/tools.ts +87 -0
  76. package/connectors/google-photos/src/server.ts +2 -132
  77. package/connectors/google-photos/src/tools.ts +138 -0
  78. package/connectors/grafana/src/server.ts +2 -53
  79. package/connectors/grafana/src/tools.ts +53 -0
  80. package/connectors/great-expectations/src/gx-parse.ts +14 -45
  81. package/connectors/greenhouse/src/server.ts +2 -63
  82. package/connectors/greenhouse/src/tools.ts +68 -0
  83. package/connectors/hubspot/src/server.ts +2 -77
  84. package/connectors/hubspot/src/tools.ts +70 -0
  85. package/connectors/iac/src/server.ts +2 -137
  86. package/connectors/iac/src/tools.ts +153 -0
  87. package/connectors/imap/src/imap-core.ts +29 -82
  88. package/connectors/imap/src/server.ts +33 -220
  89. package/connectors/intercom/src/server.ts +2 -61
  90. package/connectors/intercom/src/tools.ts +58 -0
  91. package/connectors/jenkins/src/jenkins-api.ts +2 -6
  92. package/connectors/jenkins/src/server.ts +2 -215
  93. package/connectors/jenkins/src/tools.ts +240 -0
  94. package/connectors/jira/src/server.ts +2 -301
  95. package/connectors/jira/src/tools.ts +325 -0
  96. package/connectors/kubernetes/src/server.ts +2 -155
  97. package/connectors/kubernetes/src/tools.ts +160 -0
  98. package/connectors/launchdarkly/src/server.ts +2 -75
  99. package/connectors/launchdarkly/src/tools.ts +72 -0
  100. package/connectors/lever/src/search-filter.ts +4 -12
  101. package/connectors/lever/src/server.ts +2 -60
  102. package/connectors/lever/src/tools.ts +61 -0
  103. package/connectors/linear/src/server.ts +2 -459
  104. package/connectors/linear/src/tools.ts +482 -0
  105. package/connectors/localdb/src/sql-scan.ts +13 -41
  106. package/connectors/localdb/src/tools.ts +1 -1
  107. package/connectors/looker/src/search-filter.ts +2 -15
  108. package/connectors/looker/src/server.ts +35 -70
  109. package/connectors/mendeley/src/server.ts +2 -55
  110. package/connectors/mendeley/src/tools.ts +54 -0
  111. package/connectors/mercury/src/server.ts +2 -57
  112. package/connectors/mercury/src/tools.ts +50 -0
  113. package/connectors/metabase/src/search-filter.ts +4 -15
  114. package/connectors/metabase/src/server.ts +2 -79
  115. package/connectors/metabase/src/tools.ts +64 -0
  116. package/connectors/miro/src/server.ts +2 -72
  117. package/connectors/miro/src/tools.ts +65 -0
  118. package/connectors/mlflow/src/search-filter.ts +3 -7
  119. package/connectors/mlflow/src/server.ts +20 -40
  120. package/connectors/monte-carlo/src/server.ts +50 -57
  121. package/connectors/netlify/src/search-filter.ts +6 -14
  122. package/connectors/netlify/src/server.ts +2 -60
  123. package/connectors/netlify/src/tools.ts +53 -0
  124. package/connectors/newrelic/src/server.ts +2 -40
  125. package/connectors/newrelic/src/tools.ts +37 -0
  126. package/connectors/notion/src/server.ts +2 -335
  127. package/connectors/notion/src/tools.ts +344 -0
  128. package/connectors/obsidian/src/server.ts +2 -425
  129. package/connectors/obsidian/src/tools.ts +466 -0
  130. package/connectors/onedrive/src/server.ts +2 -249
  131. package/connectors/onedrive/src/tools.ts +253 -0
  132. package/connectors/outlook/src/server.ts +2 -351
  133. package/connectors/outlook/src/tools.ts +357 -0
  134. package/connectors/pagerduty/src/server.ts +2 -132
  135. package/connectors/pagerduty/src/tools.ts +126 -0
  136. package/connectors/pipedrive/src/server.ts +2 -58
  137. package/connectors/pipedrive/src/tools.ts +63 -0
  138. package/connectors/powerbi/src/search-filter.ts +2 -15
  139. package/connectors/powerbi/src/server.ts +43 -68
  140. package/connectors/prefect/src/search-filter.ts +3 -16
  141. package/connectors/prefect/src/server.ts +2 -90
  142. package/connectors/prefect/src/tools.ts +83 -0
  143. package/connectors/protonmail/src/mail-core.ts +16 -75
  144. package/connectors/protonmail/src/server.ts +34 -214
  145. package/connectors/raindrop/src/server.ts +2 -103
  146. package/connectors/raindrop/src/tools.ts +103 -0
  147. package/connectors/ramp/src/server.ts +2 -105
  148. package/connectors/ramp/src/tools.ts +98 -0
  149. package/connectors/readwise/src/server.ts +2 -88
  150. package/connectors/readwise/src/tools.ts +81 -0
  151. package/connectors/sagemaker/src/tools.ts +21 -40
  152. package/connectors/salesforce/src/search-filter.ts +4 -11
  153. package/connectors/salesforce/src/server.ts +2 -90
  154. package/connectors/salesforce/src/tools.ts +75 -0
  155. package/connectors/semgrep/src/search-filter.ts +3 -10
  156. package/connectors/semgrep/src/server.ts +2 -104
  157. package/connectors/semgrep/src/tools.ts +92 -0
  158. package/connectors/sentry/src/server.ts +2 -58
  159. package/connectors/sentry/src/tools.ts +53 -0
  160. package/connectors/slack/src/server.ts +2 -352
  161. package/connectors/slack/src/tools.ts +376 -0
  162. package/connectors/snowflake/src/search-filter.ts +4 -19
  163. package/connectors/snowflake/src/server.ts +6 -25
  164. package/connectors/snyk/src/server.ts +2 -140
  165. package/connectors/snyk/src/tools.ts +136 -0
  166. package/connectors/sonarqube/src/server.ts +2 -111
  167. package/connectors/sonarqube/src/tools.ts +98 -0
  168. package/connectors/stackoverflow/src/server.ts +2 -77
  169. package/connectors/stackoverflow/src/tools.ts +66 -0
  170. package/connectors/storybook/src/storybook-parse.ts +3 -6
  171. package/connectors/stripe/src/server.ts +2 -61
  172. package/connectors/stripe/src/tools.ts +54 -0
  173. package/connectors/superset/src/search-filter.ts +4 -15
  174. package/connectors/superset/src/server.ts +2 -115
  175. package/connectors/superset/src/tools.ts +98 -0
  176. package/connectors/tableau/src/search-filter.ts +2 -15
  177. package/connectors/tableau/src/server.ts +13 -65
  178. package/connectors/teams/src/server.ts +2 -249
  179. package/connectors/teams/src/tools.ts +283 -0
  180. package/connectors/testflight/src/server.ts +2 -70
  181. package/connectors/testflight/src/tools.ts +79 -0
  182. package/connectors/vercel/src/search-filter.ts +2 -10
  183. package/connectors/vercel/src/server.ts +2 -75
  184. package/connectors/vercel/src/tools.ts +73 -0
  185. package/connectors/vertex-ai/src/tools.ts +23 -33
  186. package/connectors/wiz/src/search-filter.ts +6 -30
  187. package/connectors/wiz/src/server.ts +2 -188
  188. package/connectors/wiz/src/tools.ts +168 -0
  189. package/connectors/zendesk/src/server.ts +2 -73
  190. package/connectors/zendesk/src/tools.ts +60 -0
  191. package/connectors/zoom/src/search-filter.ts +1 -7
  192. package/connectors/zoom/src/server.ts +2 -94
  193. package/connectors/zoom/src/tools.ts +93 -0
  194. package/connectors/zotero/src/server.ts +2 -78
  195. package/connectors/zotero/src/tools.ts +67 -0
  196. package/package.json +14 -12
  197. package/shared/access-token-cache.ts +123 -0
  198. package/shared/atlassian-json-fetch.ts +6 -16
  199. package/shared/audit-chain.ts +601 -34
  200. package/shared/cli-json-kit.ts +191 -0
  201. package/shared/collection-tool-kit.ts +143 -0
  202. package/shared/connector-mode.ts +3 -2
  203. package/shared/consent-kit.ts +81 -26
  204. package/shared/cursor-list-tool.ts +29 -0
  205. package/shared/env-json-api.ts +209 -0
  206. package/shared/fetch-json-text.ts +27 -0
  207. package/shared/github-rest.ts +62 -0
  208. package/shared/graphql-json.ts +60 -0
  209. package/shared/imap-mail-core.ts +91 -0
  210. package/shared/imap-tool-kit.ts +57 -23
  211. package/shared/imapflow-adapter.ts +417 -0
  212. package/shared/nimbus-spawn.ts +84 -27
  213. package/shared/rest-tool-kit.ts +77 -7
  214. package/shared/run-cli-json.ts +38 -16
  215. package/shared/safe-cli-arg.ts +139 -17
  216. package/shared/search-filter.ts +58 -18
  217. package/shared/walk-files.ts +118 -0
  218. package/shared/windows-batch-args.ts +273 -0
  219. package/shared/write-budget.ts +100 -0
  220. package/standalone/src/launcher.ts +19 -7
@@ -0,0 +1,311 @@
1
+ import { z } from "zod";
2
+ import {
3
+ type ConsentServer,
4
+ createWriteToolRegistrar,
5
+ type WriteToolConfig,
6
+ } from "../../../shared/consent-kit.ts";
7
+ import { ghFetch, ghQueryPath, ghRepoPath, setGhPaging } from "../../../shared/github-rest.ts";
8
+ import {
9
+ createRegisterSimpleTool,
10
+ createZodToolRegistrar,
11
+ mcpJsonResult as jsonResult,
12
+ requireProcessEnv,
13
+ type ZodObjectSchema,
14
+ } from "../../../shared/mcp-tool-kit.ts";
15
+ import {
16
+ makeRestToolRegistrar,
17
+ makeRestWriteToolRegistrar,
18
+ } from "../../../shared/rest-tool-kit.ts";
19
+
20
+ /** Tool names exposed by this connector — for contract/introspection tests. */
21
+ export const GITHUB_TOOL_NAMES = [
22
+ "github_repo_list",
23
+ "github_repo_get",
24
+ "github_pr_list",
25
+ "github_pr_get",
26
+ "github_pr_merge",
27
+ "github_pr_close",
28
+ "github_issue_list",
29
+ "github_issue_get",
30
+ "github_issue_create",
31
+ "github_ci_runs",
32
+ "github_ci_run_get",
33
+ "github_branch_delete",
34
+ "github_tag_create",
35
+ "github_commit_push",
36
+ ] as const;
37
+
38
+ export function registerGithubTools(
39
+ server: ConsentServer & { tool: (...args: never) => unknown },
40
+ ): void {
41
+ const registerSimpleTool = createRegisterSimpleTool(server);
42
+ const reg = createZodToolRegistrar(registerSimpleTool);
43
+
44
+ /**
45
+ * Every MUTATING GitHub tool goes through here. Outside the gateway this adds the consent gate,
46
+ * the write-scope allow-list, the mutation budget and the audit record; inside the gateway it is a
47
+ * pass-through, because executor.ts (I2) is the gate there.
48
+ */
49
+ const registerWriteTool = createWriteToolRegistrar(server, {
50
+ connector: "github",
51
+ scopeEnv: "NIMBUS_MCP_GITHUB_WRITE_SCOPE",
52
+ scopeKinds: ["repo"],
53
+ });
54
+
55
+ /** Standard GitHub tool, read or write: token → ghFetch(buildPath[, buildInit]) → mcpJsonResultIfOk("GitHub"). */
56
+ const githubRest = { tokenEnv: "GITHUB_PAT", serviceLabel: "GitHub", fetch: ghFetch } as const;
57
+ const registerGithubTool = makeRestToolRegistrar({ registrar: reg, ...githubRest });
58
+ const registerRestWriteTool = makeRestWriteToolRegistrar({ registerWriteTool, ...githubRest });
59
+
60
+ /**
61
+ * The write-tool equivalent of `registerGithubTool`, routed through the write registrar.
62
+ * `scopeTargetOf` is supplied here rather than per tool — every GitHub mutation is scoped to one
63
+ * `owner/repo`, and deriving it centrally means a new write tool cannot forget it.
64
+ */
65
+ function registerGithubWriteTool<T extends { owner: string; repo: string }>(
66
+ name: string,
67
+ cfg: Omit<WriteToolConfig<T>, "scopeTargetOf">,
68
+ description: string,
69
+ schema: ZodObjectSchema<T>,
70
+ buildPath: (parsed: T) => string,
71
+ buildInit?: (parsed: T) => RequestInit,
72
+ ): void {
73
+ registerRestWriteTool(
74
+ name,
75
+ { ...cfg, scopeTargetOf: (p) => ({ kind: "repo", value: `${p.owner}/${p.repo}` }) },
76
+ description,
77
+ schema,
78
+ buildPath,
79
+ buildInit,
80
+ );
81
+ }
82
+
83
+ const jsonInit = (method: string, body: unknown): RequestInit => ({
84
+ method,
85
+ headers: { "Content-Type": "application/json" },
86
+ body: JSON.stringify(body),
87
+ });
88
+
89
+ const repoSlugArgs = z.object({
90
+ owner: z.string().min(1),
91
+ repo: z.string().min(1),
92
+ });
93
+
94
+ const githubRepoListSchema = z.object({
95
+ perPage: z.number().int().min(1).max(100).optional(),
96
+ page: z.number().int().min(1).optional(),
97
+ });
98
+
99
+ registerGithubTool(
100
+ "github_repo_list",
101
+ "List repositories for the authenticated user (affiliation: owner, collaborator, organization_member).",
102
+ githubRepoListSchema,
103
+ (parsed) =>
104
+ ghQueryPath("/user/repos", (q) => {
105
+ setGhPaging(q, parsed);
106
+ q.set("sort", "updated");
107
+ q.set("affiliation", "owner,collaborator,organization_member");
108
+ }),
109
+ );
110
+
111
+ registerGithubTool(
112
+ "github_repo_get",
113
+ "Get repository metadata (owner/repo).",
114
+ repoSlugArgs,
115
+ (parsed) => ghRepoPath(parsed.owner, parsed.repo),
116
+ );
117
+
118
+ /** Pulls and issues take one list query: a state filter, paging, newest activity first. */
119
+ const githubStateListSchema = repoSlugArgs.extend({
120
+ state: z.enum(["open", "closed", "all"]).optional(),
121
+ perPage: z.number().int().min(1).max(100).optional(),
122
+ page: z.number().int().min(1).optional(),
123
+ });
124
+ const stateListPath =
125
+ (collection: "pulls" | "issues") =>
126
+ (parsed: z.infer<typeof githubStateListSchema>): string =>
127
+ ghQueryPath(`${ghRepoPath(parsed.owner, parsed.repo)}/${collection}`, (q) => {
128
+ q.set("state", parsed.state ?? "open");
129
+ setGhPaging(q, parsed);
130
+ q.set("sort", "updated");
131
+ q.set("direction", "desc");
132
+ });
133
+
134
+ registerGithubTool(
135
+ "github_pr_list",
136
+ "List pull requests for a repository.",
137
+ githubStateListSchema,
138
+ stateListPath("pulls"),
139
+ );
140
+
141
+ const githubPrNumberSchema = repoSlugArgs.extend({
142
+ pullNumber: z.number().int().min(1),
143
+ });
144
+
145
+ registerGithubTool(
146
+ "github_pr_get",
147
+ "Get a single pull request by number.",
148
+ githubPrNumberSchema,
149
+ (parsed) => `${ghRepoPath(parsed.owner, parsed.repo)}/pulls/${String(parsed.pullNumber)}`,
150
+ );
151
+
152
+ const githubPrMergeSchema = repoSlugArgs.extend({
153
+ pullNumber: z.number().int().min(1),
154
+ mergeMethod: z.enum(["merge", "squash", "rebase"]).optional(),
155
+ commitTitle: z.string().max(500).optional(),
156
+ });
157
+
158
+ registerGithubWriteTool(
159
+ "github_pr_merge",
160
+ { mutates: "github.pr.merge", recoverable: true },
161
+ "Merge a pull request.",
162
+ githubPrMergeSchema,
163
+ (parsed) => `${ghRepoPath(parsed.owner, parsed.repo)}/pulls/${String(parsed.pullNumber)}/merge`,
164
+ (parsed) => {
165
+ const body: Record<string, string> = {};
166
+ if (parsed.mergeMethod !== undefined) {
167
+ body["merge_method"] = parsed.mergeMethod;
168
+ }
169
+ if (parsed.commitTitle !== undefined && parsed.commitTitle !== "") {
170
+ body["commit_title"] = parsed.commitTitle;
171
+ }
172
+ return jsonInit("PUT", body);
173
+ },
174
+ );
175
+
176
+ registerGithubWriteTool(
177
+ "github_pr_close",
178
+ { mutates: "github.pr.close", recoverable: true },
179
+ "Close a pull request without merging.",
180
+ githubPrNumberSchema,
181
+ (parsed) => `${ghRepoPath(parsed.owner, parsed.repo)}/pulls/${String(parsed.pullNumber)}`,
182
+ () => jsonInit("PATCH", { state: "closed" }),
183
+ );
184
+
185
+ registerGithubTool(
186
+ "github_issue_list",
187
+ "List issues for a repository.",
188
+ githubStateListSchema,
189
+ stateListPath("issues"),
190
+ );
191
+
192
+ const githubIssueGetSchema = repoSlugArgs.extend({
193
+ issueNumber: z.number().int().min(1),
194
+ });
195
+
196
+ registerGithubTool(
197
+ "github_issue_get",
198
+ "Get a single issue by number.",
199
+ githubIssueGetSchema,
200
+ (parsed) => `${ghRepoPath(parsed.owner, parsed.repo)}/issues/${String(parsed.issueNumber)}`,
201
+ );
202
+
203
+ const githubIssueCreateSchema = repoSlugArgs.extend({
204
+ title: z.string().min(1).max(500),
205
+ body: z.string().max(65_000).optional(),
206
+ });
207
+
208
+ registerGithubWriteTool(
209
+ "github_issue_create",
210
+ { mutates: "github.issue.create", recoverable: true },
211
+ "Create a new issue in a repository.",
212
+ githubIssueCreateSchema,
213
+ (parsed) => `${ghRepoPath(parsed.owner, parsed.repo)}/issues`,
214
+ (parsed) => jsonInit("POST", { title: parsed.title, body: parsed.body }),
215
+ );
216
+
217
+ const githubCiRunsSchema = repoSlugArgs.extend({
218
+ perPage: z.number().int().min(1).max(100).optional(),
219
+ page: z.number().int().min(1).optional(),
220
+ });
221
+
222
+ registerGithubTool(
223
+ "github_ci_runs",
224
+ "List GitHub Actions workflow runs for a repository.",
225
+ githubCiRunsSchema,
226
+ (parsed) =>
227
+ ghQueryPath(`${ghRepoPath(parsed.owner, parsed.repo)}/actions/runs`, (q) => {
228
+ setGhPaging(q, parsed);
229
+ }),
230
+ );
231
+
232
+ const githubCiRunGetSchema = repoSlugArgs.extend({
233
+ runId: z.number().int().min(1),
234
+ });
235
+
236
+ registerGithubTool(
237
+ "github_ci_run_get",
238
+ "Get a single workflow run including jobs URL reference.",
239
+ githubCiRunGetSchema,
240
+ (parsed) => `${ghRepoPath(parsed.owner, parsed.repo)}/actions/runs/${String(parsed.runId)}`,
241
+ );
242
+
243
+ const githubBranchDeleteSchema = repoSlugArgs.extend({
244
+ branch: z.string().min(1).max(255),
245
+ });
246
+
247
+ registerWriteTool(
248
+ "github_branch_delete",
249
+ {
250
+ mutates: "github.branch.delete",
251
+ // The only GitHub mutation here that cannot be undone from its own result, so the ref's SHA is
252
+ // captured BEFORE the delete. With it the branch can be recreated; without it the commit is
253
+ // only reachable by reflog on someone's clone.
254
+ recoverable: false,
255
+ capturePreState: async (parsed) => {
256
+ const token = requireProcessEnv("GITHUB_PAT");
257
+ const ref = `heads/${parsed.branch}`;
258
+ const res = await ghFetch(
259
+ token,
260
+ `${ghRepoPath(parsed.owner, parsed.repo)}/git/ref/${encodeURIComponent(ref)}`,
261
+ );
262
+ return { ref, resolved: res.ok, sha: res.json };
263
+ },
264
+ scopeTargetOf: (parsed) => ({ kind: "repo", value: `${parsed.owner}/${parsed.repo}` }),
265
+ },
266
+ "Delete a branch by ref name.",
267
+ githubBranchDeleteSchema,
268
+ async (parsed) => {
269
+ const token = requireProcessEnv("GITHUB_PAT");
270
+ const ref = `heads/${parsed.branch}`;
271
+ const path = `${ghRepoPath(parsed.owner, parsed.repo)}/git/refs/${encodeURIComponent(ref)}`;
272
+ const res = await ghFetch(token, path, { method: "DELETE" });
273
+ if (!res.ok && res.status !== 204) {
274
+ throw new Error(`GitHub ${String(res.status)}: ${res.text.slice(0, 300)}`);
275
+ }
276
+ return jsonResult({ ok: true, deleted: ref });
277
+ },
278
+ );
279
+
280
+ const githubTagCreateSchema = repoSlugArgs.extend({
281
+ tag: z.string().min(1).max(255),
282
+ sha: z.string().min(7).max(40),
283
+ });
284
+
285
+ registerGithubWriteTool(
286
+ "github_tag_create",
287
+ { mutates: "github.tag.create", recoverable: true },
288
+ "Create a lightweight tag pointing at a commit SHA.",
289
+ githubTagCreateSchema,
290
+ (parsed) => `${ghRepoPath(parsed.owner, parsed.repo)}/git/refs`,
291
+ (parsed) => jsonInit("POST", { ref: `refs/tags/${parsed.tag}`, sha: parsed.sha }),
292
+ );
293
+
294
+ const githubCommitPushSchema = repoSlugArgs.extend({
295
+ branch: z.string().min(1).optional(),
296
+ });
297
+
298
+ reg(
299
+ "github_commit_push",
300
+ "Push commits is not available via this tool — use local git with your own remote credentials (requires HITL repo.commit.push if ever implemented).",
301
+ githubCommitPushSchema,
302
+ () =>
303
+ Promise.resolve(
304
+ jsonResult({
305
+ code: "NOT_IMPLEMENTED",
306
+ message:
307
+ "Pushing commits requires local git and is not executed by this MCP server. Clone the repo and push with git.",
308
+ }),
309
+ ),
310
+ );
311
+ }
@@ -1,213 +1,10 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
- import { z } from "zod";
4
- import { createWriteToolRegistrar } from "../../../shared/consent-kit.ts";
5
- import {
6
- createRegisterSimpleTool,
7
- createZodToolRegistrar,
8
- mcpJsonResult as jsonResult,
9
- requireProcessEnv,
10
- } from "../../../shared/mcp-tool-kit.ts";
11
- import { makeRestFetcher, makeRestToolRegistrar } from "../../../shared/rest-tool-kit.ts";
12
-
13
- const GH_API = "https://api.github.com";
14
- const GH_HEADERS: Record<string, string> = {
15
- Accept: "application/vnd.github+json",
16
- "X-GitHub-Api-Version": "2022-11-28",
17
- };
18
-
19
- function ghFetch(
20
- token: string,
21
- path: string,
22
- init?: RequestInit,
23
- ): Promise<{ ok: boolean; status: number; json: unknown; text: string }> {
24
- return makeRestFetcher({ apiBase: GH_API, token, defaultHeaders: GH_HEADERS })(path, init);
25
- }
3
+ import { registerGithubActionsTools } from "./tools.ts";
26
4
 
27
5
  const mcp = new McpServer({ name: "nimbus-github-actions", version: "0.1.0" });
28
- const reg = createZodToolRegistrar(createRegisterSimpleTool(mcp));
29
-
30
- /**
31
- * Every MUTATING github-actions tool goes through here. Outside the gateway this adds the
32
- * consent gate, the write-scope allow-list, the mutation budget and the audit record; inside
33
- * the gateway it is a pass-through, because executor.ts (I2) is the gate there.
34
- */
35
- const registerWriteTool = createWriteToolRegistrar(mcp, {
36
- connector: "github-actions",
37
- scopeEnv: "NIMBUS_MCP_GITHUB_ACTIONS_WRITE_SCOPE",
38
- scopeKinds: ["repo"],
39
- });
40
-
41
- /** Standard GitHub Actions tool: token → ghFetch(buildPath) → mcpJsonResultIfOk("GitHub Actions"). */
42
- const registerGhaTool = makeRestToolRegistrar({
43
- registrar: reg,
44
- tokenEnv: "GITHUB_PAT",
45
- serviceLabel: "GitHub Actions",
46
- fetch: ghFetch,
47
- });
48
-
49
- const slug = (owner: string, repo: string): string =>
50
- `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`;
51
-
52
- const repoSlugArgs = z.object({
53
- owner: z.string().min(1),
54
- repo: z.string().min(1),
55
- });
56
-
57
- const runListSchema = repoSlugArgs.extend({
58
- perPage: z.number().int().min(1).max(100).optional(),
59
- page: z.number().int().min(1).optional(),
60
- branch: z.string().optional(),
61
- event: z.string().optional(),
62
- status: z.string().optional(),
63
- });
64
-
65
- registerGhaTool(
66
- "gha_workflow_list",
67
- "List GitHub Actions workflows for a repository.",
68
- repoSlugArgs.extend({
69
- perPage: z.number().int().min(1).max(100).optional(),
70
- page: z.number().int().min(1).optional(),
71
- }),
72
- (parsed) => {
73
- const u = new URL(`${GH_API}${slug(parsed.owner, parsed.repo)}/actions/workflows`);
74
- u.searchParams.set("per_page", String(parsed.perPage ?? 30));
75
- if (parsed.page !== undefined) {
76
- u.searchParams.set("page", String(parsed.page));
77
- }
78
- return `${u.pathname}${u.search}`;
79
- },
80
- );
81
-
82
- registerGhaTool("gha_run_list", "List workflow runs for a repository.", runListSchema, (parsed) => {
83
- const u = new URL(`${GH_API}${slug(parsed.owner, parsed.repo)}/actions/runs`);
84
- u.searchParams.set("per_page", String(parsed.perPage ?? 30));
85
- if (parsed.page !== undefined) {
86
- u.searchParams.set("page", String(parsed.page));
87
- }
88
- if (parsed.branch !== undefined) {
89
- u.searchParams.set("branch", parsed.branch);
90
- }
91
- if (parsed.event !== undefined) {
92
- u.searchParams.set("event", parsed.event);
93
- }
94
- if (parsed.status !== undefined) {
95
- u.searchParams.set("status", parsed.status);
96
- }
97
- return `${u.pathname}${u.search}`;
98
- });
99
-
100
- const runIdSchema = repoSlugArgs.extend({
101
- runId: z.number().int().min(1),
102
- });
103
-
104
- registerGhaTool(
105
- "gha_run_get",
106
- "Get a single workflow run by id.",
107
- runIdSchema,
108
- (parsed) => `${slug(parsed.owner, parsed.repo)}/actions/runs/${String(parsed.runId)}`,
109
- );
110
-
111
- registerGhaTool(
112
- "gha_run_jobs",
113
- "List jobs for a workflow run.",
114
- runIdSchema,
115
- (parsed) => `${slug(parsed.owner, parsed.repo)}/actions/runs/${String(parsed.runId)}/jobs`,
116
- );
117
-
118
- reg(
119
- "gha_run_log",
120
- "Download console log text for a job (truncated).",
121
- repoSlugArgs.extend({
122
- jobId: z.number().int().min(1),
123
- maxChars: z.number().int().min(1000).max(500_000).optional(),
124
- }),
125
- async (parsed) => {
126
- const token = requireProcessEnv("GITHUB_PAT");
127
- const url = `${GH_API}/repos/${encodeURIComponent(parsed.owner)}/${encodeURIComponent(parsed.repo)}/actions/jobs/${String(parsed.jobId)}/logs`;
128
- const res = await fetch(url, {
129
- headers: {
130
- Accept: "application/vnd.github+json",
131
- Authorization: `Bearer ${token}`,
132
- "X-GitHub-Api-Version": "2022-11-28",
133
- },
134
- redirect: "follow",
135
- });
136
- const text = await res.text();
137
- if (!res.ok) {
138
- throw new Error(`GitHub Actions logs ${String(res.status)}: ${text.slice(0, 400)}`);
139
- }
140
- const max = parsed.maxChars ?? 64_000;
141
- const tail = text.length > max ? text.slice(-max) : text;
142
- return jsonResult({
143
- jobId: parsed.jobId,
144
- truncated: text.length > max,
145
- totalChars: text.length,
146
- text: tail,
147
- });
148
- },
149
- );
150
-
151
- registerWriteTool(
152
- "gha_run_trigger",
153
- {
154
- mutates: "github_actions.run.trigger",
155
- recoverable: true,
156
- scopeTargetOf: (p) => ({ kind: "repo", value: `${p.owner}/${p.repo}` }),
157
- },
158
- "Dispatch a workflow (workflow_dispatch).",
159
- repoSlugArgs.extend({
160
- workflowId: z
161
- .string()
162
- .min(1)
163
- .describe("Numeric workflow id or workflow file name (e.g. ci.yml)"),
164
- ref: z.string().min(1).optional(),
165
- inputs: z.record(z.string(), z.string()).optional(),
166
- }),
167
- async (parsed) => {
168
- const token = requireProcessEnv("GITHUB_PAT");
169
- const encWf = encodeURIComponent(parsed.workflowId);
170
- const path = `${slug(parsed.owner, parsed.repo)}/actions/workflows/${encWf}/dispatches`;
171
- const ref = parsed.ref ?? "main";
172
- const res = await ghFetch(token, path, {
173
- method: "POST",
174
- headers: { "Content-Type": "application/json" },
175
- body: JSON.stringify({
176
- ref,
177
- inputs: parsed.inputs ?? {},
178
- }),
179
- });
180
- if (!res.ok) {
181
- throw new Error(`GitHub Actions dispatch ${String(res.status)}: ${res.text.slice(0, 400)}`);
182
- }
183
- return jsonResult({
184
- ok: true,
185
- owner: parsed.owner,
186
- repo: parsed.repo,
187
- workflowId: parsed.workflowId,
188
- });
189
- },
190
- );
191
6
 
192
- registerWriteTool(
193
- "gha_run_cancel",
194
- {
195
- mutates: "github_actions.run.cancel",
196
- recoverable: true,
197
- scopeTargetOf: (p) => ({ kind: "repo", value: `${p.owner}/${p.repo}` }),
198
- },
199
- "Cancel a workflow run.",
200
- runIdSchema,
201
- async (parsed) => {
202
- const token = requireProcessEnv("GITHUB_PAT");
203
- const path = `${slug(parsed.owner, parsed.repo)}/actions/runs/${String(parsed.runId)}/cancel`;
204
- const res = await ghFetch(token, path, { method: "POST" });
205
- if (!res.ok) {
206
- throw new Error(`GitHub Actions cancel ${String(res.status)}: ${res.text.slice(0, 400)}`);
207
- }
208
- return jsonResult({ ok: true, runId: parsed.runId });
209
- },
210
- );
7
+ registerGithubActionsTools(mcp);
211
8
 
212
9
  const transport = new StdioServerTransport();
213
10
  await mcp.connect(transport);