@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
@@ -1,18 +1,21 @@
1
1
  import { z } from "zod";
2
+ import {
3
+ asArray,
4
+ awsCliArg,
5
+ createCliJsonRunner,
6
+ isRecord,
7
+ strField,
8
+ } from "../../../shared/cli-json-kit.ts";
2
9
  import { mcpJsonResult as jsonResult } from "../../../shared/mcp-tool-kit.ts";
3
10
  import { nimbusSpawn } from "../../../shared/nimbus-spawn.ts";
4
11
  import type { ZodToolRegistrar } from "../../../shared/run-read-only-mcp-connector.ts";
5
- import { isSafeCliArg } from "../../../shared/safe-cli-arg.ts";
6
12
 
7
13
  /**
8
14
  * A log-group name / prefix passed as a value to the `aws logs` CLI. Rejected at
9
- * the schema boundary if it begins with `-` (argv flag smuggling) or contains
10
- * control characters.
15
+ * the schema boundary (`awsCliArg`) if it begins with `-` (argv flag smuggling),
16
+ * contains control characters, or starts with a prefix that makes the CLI read the
17
+ * value from a file or a URL instead.
11
18
  */
12
- const cliArg = z
13
- .string()
14
- .min(1)
15
- .refine(isSafeCliArg, { message: 'must not start with "-" or contain control characters' });
16
19
 
17
20
  /**
18
21
  * Amazon CloudWatch Logs (Tier-3, no-row-data) MCP tool surface. ALL tools
@@ -32,40 +35,19 @@ export const CLOUDWATCH_TOOL_NAMES = [
32
35
 
33
36
  const LIMIT = "50";
34
37
 
35
- function isRecord(v: unknown): v is Record<string, unknown> {
36
- return v !== null && typeof v === "object" && !Array.isArray(v);
37
- }
38
-
39
- function strField(r: Record<string, unknown>, key: string): string {
40
- const v = r[key];
41
- return typeof v === "string" ? v : "";
42
- }
43
-
44
- function asArray(parsed: unknown, key: string): unknown[] {
45
- if (!isRecord(parsed)) {
46
- return [];
47
- }
48
- const arr = parsed[key];
49
- return Array.isArray(arr) ? arr : [];
50
- }
51
-
52
38
  /**
53
39
  * Run `aws logs <args> --output json` and parse stdout. The AWS CLI reads
54
40
  * AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_DEFAULT_REGION / AWS_PROFILE
55
41
  * natively from the process env injected at spawn time. Throws on non-zero exit.
56
42
  */
57
- async function logsCli(args: string[]): Promise<unknown> {
58
- const { code, stdout, stderr } = await nimbusSpawn(
59
- ["aws", "logs", ...args, "--output", "json"],
60
- {},
61
- );
62
- const out = stdout.trim();
63
- const err = stderr.trim();
64
- if (code !== 0) {
65
- throw new Error(`aws logs ${args[0] ?? ""} failed: ${err.slice(0, 400)}`);
66
- }
67
- return out === "" ? {} : (JSON.parse(out) as unknown);
68
- }
43
+ const logsCli = createCliJsonRunner(
44
+ {
45
+ argv: (args) => ["aws", "logs", ...args, "--output", "json"],
46
+ label: "aws logs",
47
+ emptyResult: {},
48
+ },
49
+ nimbusSpawn,
50
+ );
69
51
 
70
52
  function groupMatches(entry: unknown, q: string): boolean {
71
53
  if (!isRecord(entry)) {
@@ -84,7 +66,7 @@ export function registerCloudwatchTools(reg: ZodToolRegistrar): void {
84
66
  "cloudwatch_list",
85
67
  "List CloudWatch log GROUPS — METADATA ONLY (`aws logs describe-log-groups`). Each entry carries `logGroupName`, `arn`, `creationTime`, `retentionInDays`, `storedBytes`, and `metricFilterCount`. Optionally filter by a `prefix` on the log-group name. Never fetches log-event contents / row data.",
86
68
  z.object({
87
- prefix: cliArg.optional(),
69
+ prefix: awsCliArg.optional(),
88
70
  }),
89
71
  async (p) => {
90
72
  const args = ["describe-log-groups", "--limit", LIMIT];
@@ -99,7 +81,7 @@ export function registerCloudwatchTools(reg: ZodToolRegistrar): void {
99
81
  "cloudwatch_get",
100
82
  "Fetch one CloudWatch log group's METADATA plus a stream summary. Returns the matching `describe-log-groups` entry (name, ARN, retention, stored bytes, metric-filter count) and the group's `describe-log-streams` METADATA (stream names, last-event timestamps, stored bytes) ordered by last activity. No log-event contents are returned — metadata only.",
101
83
  z.object({
102
- logGroupName: cliArg,
84
+ logGroupName: awsCliArg,
103
85
  }),
104
86
  async (p) => {
105
87
  const group = await logsCli([
@@ -1,74 +1,6 @@
1
- import { z } from "zod";
2
- import { matchesResult } from "../../../shared/mcp-search-tool.ts";
3
- import { mcpJsonResult as jsonResult } from "../../../shared/mcp-tool-kit.ts";
4
1
  import { runReadOnlyMcpConnector } from "../../../shared/run-read-only-mcp-connector.ts";
5
- import { filterCodemagicBuilds } from "./search-filter.ts";
6
-
7
- const CODEMAGIC_API = "https://api.codemagic.io";
8
-
9
- function authHeader(): Record<string, string> {
10
- const t = process.env["CODEMAGIC_TOKEN"]?.trim();
11
- if (t === undefined || t === "") {
12
- throw new Error("CODEMAGIC_TOKEN is not set");
13
- }
14
- return { "x-auth-token": t, Accept: "application/json" };
15
- }
16
-
17
- async function codemagicGet(path: string): Promise<unknown> {
18
- const res = await fetch(`${CODEMAGIC_API}${path}`, { headers: authHeader() });
19
- const text = await res.text();
20
- if (!res.ok) {
21
- throw new Error(`Codemagic ${String(res.status)}: ${text.slice(0, 400)}`);
22
- }
23
- return JSON.parse(text) as unknown;
24
- }
2
+ import { registerCodemagicTools } from "./tools.ts";
25
3
 
26
4
  await runReadOnlyMcpConnector("nimbus-codemagic", (reg) => {
27
- reg(
28
- "codemagic_list",
29
- "List Codemagic resources. When `appId` is omitted, returns the user's accessible apps via `/apps`. When `appId` is provided, returns the app's recent builds via `/builds?appId=<id>` (limit optional).",
30
- z.object({
31
- appId: z.string().min(1).optional(),
32
- limit: z.number().int().min(1).max(50).optional(),
33
- }),
34
- async (p) => {
35
- if (p.appId === undefined) {
36
- return jsonResult(await codemagicGet("/apps"));
37
- }
38
- const limit = p.limit ?? 50;
39
- return jsonResult(
40
- await codemagicGet(`/builds?appId=${encodeURIComponent(p.appId)}&limit=${String(limit)}`),
41
- );
42
- },
43
- );
44
-
45
- reg(
46
- "codemagic_get",
47
- "Fetch a single Codemagic resource. When `buildId` is provided, returns the build via `/builds/<buildId>`. When omitted, returns the app via `/apps` filtered to `appId`.",
48
- z.object({
49
- appId: z.string().min(1),
50
- buildId: z.string().min(1).optional(),
51
- }),
52
- async (p) => {
53
- if (p.buildId === undefined) {
54
- return jsonResult(await codemagicGet("/apps"));
55
- }
56
- return jsonResult(await codemagicGet(`/builds/${encodeURIComponent(p.buildId)}`));
57
- },
58
- );
59
-
60
- reg(
61
- "codemagic_search",
62
- "Substring search across a Codemagic app's recent builds. Matches the query against `branch`, `message`, `workflowId`, and `status` (case-insensitive). Returns a `{ matches: [...] }` envelope.",
63
- z.object({
64
- appId: z.string().min(1),
65
- query: z.string().min(1),
66
- limit: z.number().int().min(1).max(200).optional(),
67
- }),
68
- async (p) => {
69
- const root = await codemagicGet(`/builds?appId=${encodeURIComponent(p.appId)}&limit=50`);
70
- const builds = (root as { builds?: unknown[] } | null)?.builds;
71
- return matchesResult(builds, filterCodemagicBuilds, p);
72
- },
73
- );
5
+ registerCodemagicTools(reg);
74
6
  });
@@ -0,0 +1,71 @@
1
+ import { z } from "zod";
2
+ import { createJsonGetter, envAuthHeaders } from "../../../shared/env-json-api.ts";
3
+ import { matchesResult } from "../../../shared/mcp-search-tool.ts";
4
+ import { mcpJsonResult as jsonResult } from "../../../shared/mcp-tool-kit.ts";
5
+ import type { ZodToolRegistrar } from "../../../shared/run-read-only-mcp-connector.ts";
6
+ import { filterCodemagicBuilds } from "./search-filter.ts";
7
+
8
+ const CODEMAGIC_API = "https://api.codemagic.io";
9
+
10
+ const codemagicGet = createJsonGetter({
11
+ base: CODEMAGIC_API,
12
+ label: "Codemagic",
13
+ headers: envAuthHeaders({ env: "CODEMAGIC_TOKEN", scheme: "", header: "x-auth-token" }),
14
+ });
15
+
16
+ /** Tool names exposed by this connector — for contract/introspection tests. */
17
+ export const CODEMAGIC_TOOL_NAMES = [
18
+ "codemagic_list",
19
+ "codemagic_get",
20
+ "codemagic_search",
21
+ ] as const;
22
+
23
+ export function registerCodemagicTools(reg: ZodToolRegistrar): void {
24
+ reg(
25
+ "codemagic_list",
26
+ "List Codemagic resources. When `appId` is omitted, returns the user's accessible apps via `/apps`. When `appId` is provided, returns the app's recent builds via `/builds?appId=<id>` (limit optional).",
27
+ z.object({
28
+ appId: z.string().min(1).optional(),
29
+ limit: z.number().int().min(1).max(50).optional(),
30
+ }),
31
+ async (p) => {
32
+ if (p.appId === undefined) {
33
+ return jsonResult(await codemagicGet("/apps"));
34
+ }
35
+ const limit = p.limit ?? 50;
36
+ return jsonResult(
37
+ await codemagicGet(`/builds?appId=${encodeURIComponent(p.appId)}&limit=${String(limit)}`),
38
+ );
39
+ },
40
+ );
41
+
42
+ reg(
43
+ "codemagic_get",
44
+ "Fetch a single Codemagic resource. When `buildId` is provided, returns the build via `/builds/<buildId>`. When omitted, returns the app via `/apps` filtered to `appId`.",
45
+ z.object({
46
+ appId: z.string().min(1),
47
+ buildId: z.string().min(1).optional(),
48
+ }),
49
+ async (p) => {
50
+ if (p.buildId === undefined) {
51
+ return jsonResult(await codemagicGet("/apps"));
52
+ }
53
+ return jsonResult(await codemagicGet(`/builds/${encodeURIComponent(p.buildId)}`));
54
+ },
55
+ );
56
+
57
+ reg(
58
+ "codemagic_search",
59
+ "Substring search across a Codemagic app's recent builds. Matches the query against `branch`, `message`, `workflowId`, and `status` (case-insensitive). Returns a `{ matches: [...] }` envelope.",
60
+ z.object({
61
+ appId: z.string().min(1),
62
+ query: z.string().min(1),
63
+ limit: z.number().int().min(1).max(200).optional(),
64
+ }),
65
+ async (p) => {
66
+ const root = await codemagicGet(`/builds?appId=${encodeURIComponent(p.appId)}&limit=50`);
67
+ const builds = (root as { builds?: unknown[] } | null)?.builds;
68
+ return matchesResult(builds, filterCodemagicBuilds, p);
69
+ },
70
+ );
71
+ }
@@ -1,309 +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 {
5
- fetchAtlassianBasicAuthJsonText,
6
- normalizeRequiredSiteBaseUrl,
7
- requireTrimmedEnv,
8
- } from "../../../shared/atlassian-json-fetch.ts";
9
- import { joinApiPath } from "../../../shared/join-api-path.ts";
10
- import { parseCitationsJson } from "../../../shared/kb-markdown.ts";
11
- import {
12
- createRegisterSimpleTool,
13
- createZodToolRegistrar,
14
- mcpJsonResultFromTextIfOk,
15
- } from "../../../shared/mcp-tool-kit.ts";
16
- import { buildConfluenceKbPageBody } from "./kb-append.ts";
17
-
18
- function wikiRoot(siteBase: string): string {
19
- const b = normalizeRequiredSiteBaseUrl(siteBase, "CONFLUENCE_BASE_URL is empty");
20
- return b.endsWith("/wiki") ? b : `${b}/wiki`;
21
- }
22
-
23
- function requireConfluenceConfig(): { wikiApi: string; email: string; token: string } {
24
- const baseRaw = requireTrimmedEnv("CONFLUENCE_BASE_URL", "CONFLUENCE_BASE_URL is not set");
25
- const email = requireTrimmedEnv("CONFLUENCE_EMAIL", "CONFLUENCE_EMAIL is not set");
26
- const token = requireTrimmedEnv("CONFLUENCE_API_TOKEN", "CONFLUENCE_API_TOKEN is not set");
27
- return {
28
- wikiApi: `${wikiRoot(baseRaw)}/rest/api`,
29
- email,
30
- token,
31
- };
32
- }
33
-
34
- async function confFetch(
35
- path: string,
36
- init?: RequestInit,
37
- ): Promise<{ ok: boolean; status: number; text: string }> {
38
- const { wikiApi, email, token } = requireConfluenceConfig();
39
- const url = joinApiPath(wikiApi, path);
40
- return fetchAtlassianBasicAuthJsonText(url, email, token, init);
41
- }
3
+ import { registerConfluenceTools } from "./tools.ts";
42
4
 
43
5
  const server = new McpServer({ name: "nimbus-confluence", version: "0.1.0" });
44
6
 
45
- const registerSimpleTool = createRegisterSimpleTool(server);
46
-
47
- import { createWriteToolRegistrar } from "../../../shared/consent-kit.ts";
48
-
49
- const reg = createZodToolRegistrar(registerSimpleTool);
50
-
51
- /**
52
- * Every MUTATING confluence tool goes through here. Outside the gateway this adds the
53
- * consent gate, the write-scope allow-list, the mutation budget and the audit record; inside
54
- * the gateway it is a pass-through, because executor.ts (I2) is the gate there.
55
- */
56
- const registerWriteTool = createWriteToolRegistrar(server, {
57
- connector: "confluence",
58
- scopeEnv: "NIMBUS_MCP_CONFLUENCE_WRITE_SCOPE",
59
- scopeKinds: ["space", "page"],
60
- });
61
-
62
- const confluenceLimitStartSchema = z.object({
63
- limit: z.number().int().min(1).max(100).optional(),
64
- start: z.number().int().min(0).optional(),
65
- });
66
-
67
- reg(
68
- "confluence_space_list",
69
- "List Confluence spaces (GET /space).",
70
- confluenceLimitStartSchema,
71
- async (parsed) => {
72
- const qs = new URLSearchParams({
73
- limit: String(parsed.limit ?? 25),
74
- start: String(parsed.start ?? 0),
75
- });
76
- const res = await confFetch(`/space?${qs.toString()}`);
77
- return mcpJsonResultFromTextIfOk("Confluence", res);
78
- },
79
- );
80
-
81
- const confluenceSpaceContentSchema = z.object({
82
- spaceKey: z.string().min(1),
83
- limit: z.number().int().min(1).max(100).optional(),
84
- start: z.number().int().min(0).optional(),
85
- });
86
-
87
- reg(
88
- "confluence_page_list",
89
- "List pages in a space (GET /content, type=page).",
90
- confluenceSpaceContentSchema,
91
- async (parsed) => {
92
- const qs = new URLSearchParams({
93
- type: "page",
94
- spaceKey: parsed.spaceKey,
95
- limit: String(parsed.limit ?? 50),
96
- start: String(parsed.start ?? 0),
97
- expand: "history.lastUpdated,version",
98
- });
99
- const res = await confFetch(`/content?${qs.toString()}`);
100
- return mcpJsonResultFromTextIfOk("Confluence", res);
101
- },
102
- );
103
-
104
- const confluencePageIdSchema = z.object({ pageId: z.string().min(1) });
105
-
106
- reg(
107
- "confluence_page_get",
108
- "Get a Confluence page with body.storage (GET /content/{id}).",
109
- confluencePageIdSchema,
110
- async (parsed) => {
111
- const id = encodeURIComponent(parsed.pageId);
112
- const res = await confFetch(
113
- `/content/${id}?expand=body.storage,version,history.lastUpdated,space`,
114
- );
115
- return mcpJsonResultFromTextIfOk("Confluence", res);
116
- },
117
- );
118
-
119
- reg(
120
- "confluence_blogpost_list",
121
- "List blog posts in a space (GET /content, type=blogpost).",
122
- confluenceSpaceContentSchema,
123
- async (parsed) => {
124
- const qs = new URLSearchParams({
125
- type: "blogpost",
126
- spaceKey: parsed.spaceKey,
127
- limit: String(parsed.limit ?? 25),
128
- start: String(parsed.start ?? 0),
129
- expand: "history.lastUpdated,version",
130
- });
131
- const res = await confFetch(`/content?${qs.toString()}`);
132
- return mcpJsonResultFromTextIfOk("Confluence", res);
133
- },
134
- );
135
-
136
- const confluencePostIdSchema = z.object({ postId: z.string().min(1) });
137
-
138
- reg(
139
- "confluence_blogpost_get",
140
- "Get a blog post by id (GET /content/{id}).",
141
- confluencePostIdSchema,
142
- async (parsed) => {
143
- const id = encodeURIComponent(parsed.postId);
144
- const res = await confFetch(
145
- `/content/${id}?expand=body.storage,version,history.lastUpdated,space`,
146
- );
147
- return mcpJsonResultFromTextIfOk("Confluence", res);
148
- },
149
- );
150
-
151
- const confluenceCommentListSchema = z.object({
152
- pageId: z.string().min(1),
153
- limit: z.number().int().min(1).max(100).optional(),
154
- start: z.number().int().min(0).optional(),
155
- });
156
-
157
- reg(
158
- "confluence_comment_list",
159
- "List footer comments on a page (GET /content/{id}/child/comment).",
160
- confluenceCommentListSchema,
161
- async (parsed) => {
162
- const id = encodeURIComponent(parsed.pageId);
163
- const qs = new URLSearchParams({
164
- limit: String(parsed.limit ?? 50),
165
- start: String(parsed.start ?? 0),
166
- expand: "body.storage,version",
167
- });
168
- const res = await confFetch(`/content/${id}/child/comment?${qs.toString()}`);
169
- return mcpJsonResultFromTextIfOk("Confluence", res);
170
- },
171
- );
172
-
173
- const confluencePageCreateSchema = z.object({
174
- spaceKey: z.string().min(1),
175
- title: z.string().min(1),
176
- storageHtml: z.string().min(1),
177
- parentPageId: z.string().min(1).optional(),
178
- });
179
-
180
- registerWriteTool(
181
- "confluence_page_create",
182
- {
183
- mutates: "confluence.page.create",
184
- recoverable: true,
185
- scopeTargetOf: (p) => ({ kind: "space", value: p.spaceKey }),
186
- },
187
- "Create a page in a space (POST /content). Optional parentPageId.",
188
- confluencePageCreateSchema,
189
- async (parsed) => {
190
- const body: Record<string, unknown> = {
191
- type: "page",
192
- title: parsed.title,
193
- space: { key: parsed.spaceKey },
194
- body: {
195
- storage: {
196
- value: parsed.storageHtml,
197
- representation: "storage",
198
- },
199
- },
200
- };
201
- if (parsed.parentPageId !== undefined) {
202
- body["ancestors"] = [{ id: parsed.parentPageId }];
203
- }
204
- const res = await confFetch("/content", { method: "POST", body: JSON.stringify(body) });
205
- return mcpJsonResultFromTextIfOk("Confluence", res);
206
- },
207
- );
208
-
209
- const confluenceKbAppendSchema = z.object({
210
- spaceKey: z.string().min(1),
211
- parentPageId: z.string().min(1),
212
- title: z.string().min(1),
213
- bodyMarkdown: z.string().min(1),
214
- citationsJson: z.string().optional(),
215
- });
216
-
217
- registerWriteTool(
218
- "confluence_kb_append",
219
- {
220
- mutates: "confluence.knowledge.write",
221
- recoverable: true,
222
- scopeTargetOf: (p) => ({ kind: "space", value: p.spaceKey }),
223
- },
224
- "Create a knowledge-base page under a parent from simple markdown + citations (POST /content). " +
225
- "Used by Nimbus tribal-knowledge capture; the destination space/parent is supplied by the gateway from local config only.",
226
- confluenceKbAppendSchema,
227
- async (parsed) => {
228
- const body = buildConfluenceKbPageBody({
229
- spaceKey: parsed.spaceKey,
230
- parentPageId: parsed.parentPageId,
231
- title: parsed.title,
232
- bodyMarkdown: parsed.bodyMarkdown,
233
- citations: parseCitationsJson(parsed.citationsJson ?? "[]"),
234
- });
235
- const res = await confFetch("/content", { method: "POST", body: JSON.stringify(body) });
236
- return mcpJsonResultFromTextIfOk("Confluence", res);
237
- },
238
- );
239
-
240
- const confluencePageUpdateSchema = z.object({
241
- pageId: z.string().min(1),
242
- versionNumber: z.number().int().min(1),
243
- title: z.string().min(1),
244
- storageHtml: z.string().min(1),
245
- });
246
-
247
- registerWriteTool(
248
- "confluence_page_update",
249
- {
250
- mutates: "confluence.page.update",
251
- recoverable: true,
252
- scopeTargetOf: (p) => ({ kind: "page", value: p.pageId }),
253
- },
254
- "Update page body and bump version (PUT /content/{id}). Pass current version number and title.",
255
- confluencePageUpdateSchema,
256
- async (parsed) => {
257
- const id = encodeURIComponent(parsed.pageId);
258
- const body: Record<string, unknown> = {
259
- type: "page",
260
- title: parsed.title,
261
- version: { number: parsed.versionNumber + 1, message: "nimbus" },
262
- body: {
263
- storage: {
264
- value: parsed.storageHtml,
265
- representation: "storage",
266
- },
267
- },
268
- };
269
- const res = await confFetch(`/content/${id}`, { method: "PUT", body: JSON.stringify(body) });
270
- return mcpJsonResultFromTextIfOk("Confluence", res);
271
- },
272
- );
273
-
274
- const confluenceCommentAddSchema = z.object({
275
- pageId: z.string().min(1),
276
- storageHtml: z.string().min(1),
277
- });
278
-
279
- registerWriteTool(
280
- "confluence_comment_add",
281
- {
282
- mutates: "confluence.comment.add",
283
- recoverable: true,
284
- scopeTargetOf: (p) => ({ kind: "page", value: p.pageId }),
285
- },
286
- "Add a footer comment to a page (POST /content/{id}/child/comment).",
287
- confluenceCommentAddSchema,
288
- async (parsed) => {
289
- const id = encodeURIComponent(parsed.pageId);
290
- const body = {
291
- type: "comment",
292
- container: { id: parsed.pageId, type: "page" },
293
- body: {
294
- storage: {
295
- value: parsed.storageHtml,
296
- representation: "storage",
297
- },
298
- },
299
- };
300
- const res = await confFetch(`/content/${id}/child/comment`, {
301
- method: "POST",
302
- body: JSON.stringify(body),
303
- });
304
- return mcpJsonResultFromTextIfOk("Confluence", res);
305
- },
306
- );
7
+ registerConfluenceTools(server);
307
8
 
308
9
  const transport = new StdioServerTransport();
309
10
  await server.connect(transport);