@nimbus-dev/connectors 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (301) hide show
  1. package/LICENSE +662 -0
  2. package/NOTICE +40 -0
  3. package/README.md +111 -0
  4. package/connectors/airflow/nimbus.extension.json +16 -0
  5. package/connectors/airflow/src/search-filter.ts +38 -0
  6. package/connectors/airflow/src/server.ts +91 -0
  7. package/connectors/apple/nimbus.extension.json +40 -0
  8. package/connectors/apple/src/apple-mail-core.ts +43 -0
  9. package/connectors/apple/src/caldav-core.ts +116 -0
  10. package/connectors/apple/src/calendar-tools.ts +278 -0
  11. package/connectors/apple/src/server.ts +541 -0
  12. package/connectors/apple/src/tools.ts +138 -0
  13. package/connectors/argocd/nimbus.extension.json +16 -0
  14. package/connectors/argocd/src/search-filter.ts +24 -0
  15. package/connectors/argocd/src/server.ts +158 -0
  16. package/connectors/athena/nimbus.extension.json +15 -0
  17. package/connectors/athena/src/server.ts +6 -0
  18. package/connectors/athena/src/tools.ts +181 -0
  19. package/connectors/aws/nimbus.extension.json +21 -0
  20. package/connectors/aws/src/server.ts +191 -0
  21. package/connectors/azure/nimbus.extension.json +15 -0
  22. package/connectors/azure/src/server.ts +139 -0
  23. package/connectors/bigeye/nimbus.extension.json +16 -0
  24. package/connectors/bigeye/src/search-filter.ts +22 -0
  25. package/connectors/bigeye/src/server.ts +161 -0
  26. package/connectors/bigquery/nimbus.extension.json +15 -0
  27. package/connectors/bigquery/src/server.ts +6 -0
  28. package/connectors/bigquery/src/tools.ts +175 -0
  29. package/connectors/bitbucket/nimbus.extension.json +15 -0
  30. package/connectors/bitbucket/src/server.ts +252 -0
  31. package/connectors/bitrise/nimbus.extension.json +15 -0
  32. package/connectors/bitrise/src/search-filter.ts +11 -0
  33. package/connectors/bitrise/src/server.ts +83 -0
  34. package/connectors/canva/nimbus.extension.json +15 -0
  35. package/connectors/canva/src/search-filter.ts +23 -0
  36. package/connectors/canva/src/server.ts +77 -0
  37. package/connectors/circleci/nimbus.extension.json +15 -0
  38. package/connectors/circleci/src/server.ts +184 -0
  39. package/connectors/cloud-logging/nimbus.extension.json +15 -0
  40. package/connectors/cloud-logging/src/server.ts +6 -0
  41. package/connectors/cloud-logging/src/tools.ts +115 -0
  42. package/connectors/cloudwatch/nimbus.extension.json +15 -0
  43. package/connectors/cloudwatch/src/server.ts +6 -0
  44. package/connectors/cloudwatch/src/tools.ts +144 -0
  45. package/connectors/codemagic/nimbus.extension.json +15 -0
  46. package/connectors/codemagic/src/search-filter.ts +11 -0
  47. package/connectors/codemagic/src/server.ts +74 -0
  48. package/connectors/confluence/nimbus.extension.json +15 -0
  49. package/connectors/confluence/src/kb-append.ts +25 -0
  50. package/connectors/confluence/src/server.ts +309 -0
  51. package/connectors/dagster/nimbus.extension.json +16 -0
  52. package/connectors/dagster/src/search-filter.ts +53 -0
  53. package/connectors/dagster/src/server.ts +218 -0
  54. package/connectors/databricks/nimbus.extension.json +16 -0
  55. package/connectors/databricks/src/search-filter.ts +28 -0
  56. package/connectors/databricks/src/server.ts +81 -0
  57. package/connectors/datadog/nimbus.extension.json +15 -0
  58. package/connectors/datadog/src/server.ts +56 -0
  59. package/connectors/dataprofile/nimbus.extension.json +16 -0
  60. package/connectors/dataprofile/src/profile.ts +298 -0
  61. package/connectors/dataprofile/src/server.ts +6 -0
  62. package/connectors/dataprofile/src/tools.ts +69 -0
  63. package/connectors/dbt/nimbus.extension.json +16 -0
  64. package/connectors/dbt/src/search-filter.ts +28 -0
  65. package/connectors/dbt/src/server.ts +96 -0
  66. package/connectors/dependencytrack/nimbus.extension.json +16 -0
  67. package/connectors/dependencytrack/src/search-filter.ts +24 -0
  68. package/connectors/dependencytrack/src/server.ts +80 -0
  69. package/connectors/discord/nimbus.extension.json +15 -0
  70. package/connectors/discord/src/server.ts +93 -0
  71. package/connectors/elasticsearch/nimbus.extension.json +16 -0
  72. package/connectors/elasticsearch/src/server.ts +6 -0
  73. package/connectors/elasticsearch/src/tools.ts +110 -0
  74. package/connectors/fastmail/nimbus.extension.json +16 -0
  75. package/connectors/fastmail/src/jmap-core.ts +86 -0
  76. package/connectors/fastmail/src/server.ts +194 -0
  77. package/connectors/fastmail/src/tools.ts +143 -0
  78. package/connectors/figma/nimbus.extension.json +15 -0
  79. package/connectors/figma/src/search-filter.ts +24 -0
  80. package/connectors/figma/src/server.ts +117 -0
  81. package/connectors/firebase/nimbus.extension.json +15 -0
  82. package/connectors/firebase/src/search-filter.ts +30 -0
  83. package/connectors/firebase/src/server.ts +94 -0
  84. package/connectors/firebase/src/token.ts +41 -0
  85. package/connectors/flagsmith/nimbus.extension.json +15 -0
  86. package/connectors/flagsmith/src/search-filter.ts +29 -0
  87. package/connectors/flagsmith/src/server.ts +102 -0
  88. package/connectors/flux/nimbus.extension.json +16 -0
  89. package/connectors/flux/src/search-filter.ts +42 -0
  90. package/connectors/flux/src/server.ts +182 -0
  91. package/connectors/gcp/nimbus.extension.json +15 -0
  92. package/connectors/gcp/src/server.ts +140 -0
  93. package/connectors/github/nimbus.extension.json +15 -0
  94. package/connectors/github/src/server.ts +347 -0
  95. package/connectors/github-actions/nimbus.extension.json +15 -0
  96. package/connectors/github-actions/src/server.ts +213 -0
  97. package/connectors/gitlab/nimbus.extension.json +15 -0
  98. package/connectors/gitlab/src/server.ts +392 -0
  99. package/connectors/gmail/nimbus.extension.json +15 -0
  100. package/connectors/gmail/src/server.ts +277 -0
  101. package/connectors/google-drive/nimbus.extension.json +15 -0
  102. package/connectors/google-drive/src/drive-query.ts +3 -0
  103. package/connectors/google-drive/src/server.ts +531 -0
  104. package/connectors/google-meet/nimbus.extension.json +15 -0
  105. package/connectors/google-meet/src/server.ts +89 -0
  106. package/connectors/google-photos/nimbus.extension.json +15 -0
  107. package/connectors/google-photos/src/server.ts +139 -0
  108. package/connectors/grafana/nimbus.extension.json +15 -0
  109. package/connectors/grafana/src/server.ts +61 -0
  110. package/connectors/great-expectations/nimbus.extension.json +16 -0
  111. package/connectors/great-expectations/src/gx-parse.ts +292 -0
  112. package/connectors/great-expectations/src/server.ts +6 -0
  113. package/connectors/great-expectations/src/tools.ts +91 -0
  114. package/connectors/greenhouse/nimbus.extension.json +15 -0
  115. package/connectors/greenhouse/src/search-filter.ts +65 -0
  116. package/connectors/greenhouse/src/server.ts +67 -0
  117. package/connectors/hubspot/nimbus.extension.json +15 -0
  118. package/connectors/hubspot/src/search-filter.ts +28 -0
  119. package/connectors/hubspot/src/server.ts +81 -0
  120. package/connectors/iac/nimbus.extension.json +14 -0
  121. package/connectors/iac/src/server.ts +145 -0
  122. package/connectors/imap/nimbus.extension.json +16 -0
  123. package/connectors/imap/src/imap-core.ts +110 -0
  124. package/connectors/imap/src/server.ts +232 -0
  125. package/connectors/imap/src/tools.ts +40 -0
  126. package/connectors/intercom/nimbus.extension.json +15 -0
  127. package/connectors/intercom/src/search-filter.ts +46 -0
  128. package/connectors/intercom/src/server.ts +65 -0
  129. package/connectors/jenkins/nimbus.extension.json +15 -0
  130. package/connectors/jenkins/src/jenkins-api.ts +156 -0
  131. package/connectors/jenkins/src/server.ts +223 -0
  132. package/connectors/jira/nimbus.extension.json +15 -0
  133. package/connectors/jira/src/server.ts +309 -0
  134. package/connectors/kubernetes/nimbus.extension.json +15 -0
  135. package/connectors/kubernetes/src/server.ts +163 -0
  136. package/connectors/launchdarkly/nimbus.extension.json +15 -0
  137. package/connectors/launchdarkly/src/search-filter.ts +31 -0
  138. package/connectors/launchdarkly/src/server.ts +79 -0
  139. package/connectors/lever/nimbus.extension.json +15 -0
  140. package/connectors/lever/src/search-filter.ts +35 -0
  141. package/connectors/lever/src/server.ts +64 -0
  142. package/connectors/linear/nimbus.extension.json +15 -0
  143. package/connectors/linear/src/server.ts +467 -0
  144. package/connectors/localdb/nimbus.extension.json +16 -0
  145. package/connectors/localdb/src/server.ts +6 -0
  146. package/connectors/localdb/src/sql-scan.ts +158 -0
  147. package/connectors/localdb/src/tools.ts +67 -0
  148. package/connectors/looker/nimbus.extension.json +16 -0
  149. package/connectors/looker/src/search-filter.ts +22 -0
  150. package/connectors/looker/src/server.ts +254 -0
  151. package/connectors/mendeley/nimbus.extension.json +15 -0
  152. package/connectors/mendeley/src/search-filter.ts +68 -0
  153. package/connectors/mendeley/src/server.ts +59 -0
  154. package/connectors/mercury/nimbus.extension.json +15 -0
  155. package/connectors/mercury/src/search-filter.ts +11 -0
  156. package/connectors/mercury/src/server.ts +61 -0
  157. package/connectors/metabase/nimbus.extension.json +16 -0
  158. package/connectors/metabase/src/search-filter.ts +22 -0
  159. package/connectors/metabase/src/server.ts +83 -0
  160. package/connectors/miro/nimbus.extension.json +15 -0
  161. package/connectors/miro/src/search-filter.ts +24 -0
  162. package/connectors/miro/src/server.ts +76 -0
  163. package/connectors/mlflow/nimbus.extension.json +16 -0
  164. package/connectors/mlflow/src/search-filter.ts +38 -0
  165. package/connectors/mlflow/src/server.ts +167 -0
  166. package/connectors/monte-carlo/nimbus.extension.json +16 -0
  167. package/connectors/monte-carlo/src/search-filter.ts +11 -0
  168. package/connectors/monte-carlo/src/server.ts +241 -0
  169. package/connectors/netlify/nimbus.extension.json +15 -0
  170. package/connectors/netlify/src/search-filter.ts +37 -0
  171. package/connectors/netlify/src/server.ts +64 -0
  172. package/connectors/newrelic/nimbus.extension.json +15 -0
  173. package/connectors/newrelic/src/server.ts +48 -0
  174. package/connectors/notion/nimbus.extension.json +15 -0
  175. package/connectors/notion/src/kb-append.ts +23 -0
  176. package/connectors/notion/src/server.ts +343 -0
  177. package/connectors/obsidian/nimbus.extension.json +19 -0
  178. package/connectors/obsidian/src/server.ts +433 -0
  179. package/connectors/onedrive/nimbus.extension.json +15 -0
  180. package/connectors/onedrive/src/server.ts +256 -0
  181. package/connectors/outlook/nimbus.extension.json +15 -0
  182. package/connectors/outlook/src/server.ts +358 -0
  183. package/connectors/outlook/src/tool-scope-policy.ts +53 -0
  184. package/connectors/pagerduty/nimbus.extension.json +15 -0
  185. package/connectors/pagerduty/src/server.ts +140 -0
  186. package/connectors/pipedrive/nimbus.extension.json +15 -0
  187. package/connectors/pipedrive/src/search-filter.ts +11 -0
  188. package/connectors/pipedrive/src/server.ts +62 -0
  189. package/connectors/powerbi/nimbus.extension.json +16 -0
  190. package/connectors/powerbi/src/search-filter.ts +22 -0
  191. package/connectors/powerbi/src/server.ts +249 -0
  192. package/connectors/prefect/nimbus.extension.json +16 -0
  193. package/connectors/prefect/src/search-filter.ts +40 -0
  194. package/connectors/prefect/src/server.ts +94 -0
  195. package/connectors/protonmail/nimbus.extension.json +16 -0
  196. package/connectors/protonmail/src/mail-core.ts +96 -0
  197. package/connectors/protonmail/src/server.ts +231 -0
  198. package/connectors/protonmail/src/tools.ts +45 -0
  199. package/connectors/raindrop/nimbus.extension.json +15 -0
  200. package/connectors/raindrop/src/search-filter.ts +20 -0
  201. package/connectors/raindrop/src/server.ts +107 -0
  202. package/connectors/ramp/nimbus.extension.json +15 -0
  203. package/connectors/ramp/src/search-filter.ts +37 -0
  204. package/connectors/ramp/src/server.ts +109 -0
  205. package/connectors/readwise/nimbus.extension.json +15 -0
  206. package/connectors/readwise/src/search-filter.ts +64 -0
  207. package/connectors/readwise/src/server.ts +92 -0
  208. package/connectors/sagemaker/nimbus.extension.json +15 -0
  209. package/connectors/sagemaker/src/server.ts +6 -0
  210. package/connectors/sagemaker/src/tools.ts +121 -0
  211. package/connectors/salesforce/nimbus.extension.json +15 -0
  212. package/connectors/salesforce/src/search-filter.ts +23 -0
  213. package/connectors/salesforce/src/server.ts +94 -0
  214. package/connectors/semgrep/nimbus.extension.json +15 -0
  215. package/connectors/semgrep/src/search-filter.ts +31 -0
  216. package/connectors/semgrep/src/server.ts +108 -0
  217. package/connectors/sentry/nimbus.extension.json +15 -0
  218. package/connectors/sentry/src/server.ts +66 -0
  219. package/connectors/slack/nimbus.extension.json +15 -0
  220. package/connectors/slack/src/server.ts +360 -0
  221. package/connectors/snowflake/nimbus.extension.json +16 -0
  222. package/connectors/snowflake/src/search-filter.ts +26 -0
  223. package/connectors/snowflake/src/server.ts +236 -0
  224. package/connectors/snyk/nimbus.extension.json +15 -0
  225. package/connectors/snyk/src/search-filter.ts +28 -0
  226. package/connectors/snyk/src/server.ts +144 -0
  227. package/connectors/sonarqube/nimbus.extension.json +15 -0
  228. package/connectors/sonarqube/src/search-filter.ts +28 -0
  229. package/connectors/sonarqube/src/server.ts +115 -0
  230. package/connectors/stackoverflow/nimbus.extension.json +15 -0
  231. package/connectors/stackoverflow/src/search-filter.ts +52 -0
  232. package/connectors/stackoverflow/src/server.ts +81 -0
  233. package/connectors/storybook/nimbus.extension.json +16 -0
  234. package/connectors/storybook/src/server.ts +6 -0
  235. package/connectors/storybook/src/storybook-parse.ts +67 -0
  236. package/connectors/storybook/src/tools.ts +68 -0
  237. package/connectors/stripe/nimbus.extension.json +15 -0
  238. package/connectors/stripe/src/search-filter.ts +19 -0
  239. package/connectors/stripe/src/server.ts +65 -0
  240. package/connectors/superset/nimbus.extension.json +16 -0
  241. package/connectors/superset/src/search-filter.ts +22 -0
  242. package/connectors/superset/src/server.ts +119 -0
  243. package/connectors/tableau/nimbus.extension.json +16 -0
  244. package/connectors/tableau/src/search-filter.ts +22 -0
  245. package/connectors/tableau/src/server.ts +248 -0
  246. package/connectors/teams/nimbus.extension.json +20 -0
  247. package/connectors/teams/src/bot-send.ts +72 -0
  248. package/connectors/teams/src/server.ts +257 -0
  249. package/connectors/testflight/nimbus.extension.json +15 -0
  250. package/connectors/testflight/src/jwt.ts +27 -0
  251. package/connectors/testflight/src/search-filter.ts +30 -0
  252. package/connectors/testflight/src/server.ts +74 -0
  253. package/connectors/vercel/nimbus.extension.json +15 -0
  254. package/connectors/vercel/src/search-filter.ts +34 -0
  255. package/connectors/vercel/src/server.ts +79 -0
  256. package/connectors/vertex-ai/nimbus.extension.json +15 -0
  257. package/connectors/vertex-ai/src/server.ts +6 -0
  258. package/connectors/vertex-ai/src/tools.ts +145 -0
  259. package/connectors/wiz/nimbus.extension.json +15 -0
  260. package/connectors/wiz/src/search-filter.ts +50 -0
  261. package/connectors/wiz/src/server.ts +192 -0
  262. package/connectors/workday/nimbus.extension.json +19 -0
  263. package/connectors/workday/src/search-filter.ts +5 -0
  264. package/connectors/workday/src/server.ts +78 -0
  265. package/connectors/workday/src/tools.ts +1 -0
  266. package/connectors/zendesk/nimbus.extension.json +16 -0
  267. package/connectors/zendesk/src/search-filter.ts +11 -0
  268. package/connectors/zendesk/src/server.ts +77 -0
  269. package/connectors/zoom/nimbus.extension.json +15 -0
  270. package/connectors/zoom/src/encode-meeting-path.ts +5 -0
  271. package/connectors/zoom/src/recordings-window.ts +34 -0
  272. package/connectors/zoom/src/search-filter.ts +42 -0
  273. package/connectors/zoom/src/server.ts +98 -0
  274. package/connectors/zotero/nimbus.extension.json +15 -0
  275. package/connectors/zotero/src/search-filter.ts +81 -0
  276. package/connectors/zotero/src/server.ts +82 -0
  277. package/package.json +152 -0
  278. package/shared/atlassian-json-fetch.ts +42 -0
  279. package/shared/audit-chain.ts +133 -0
  280. package/shared/connector-mode.ts +60 -0
  281. package/shared/consent-kit.ts +323 -0
  282. package/shared/fetch-bearer-json.ts +18 -0
  283. package/shared/header-safe.ts +28 -0
  284. package/shared/imap-bodystructure.ts +113 -0
  285. package/shared/imap-mail-core.ts +57 -0
  286. package/shared/imap-tool-kit.ts +293 -0
  287. package/shared/join-api-path.ts +18 -0
  288. package/shared/kb-markdown.ts +142 -0
  289. package/shared/mcp-search-tool.ts +37 -0
  290. package/shared/mcp-tool-kit.ts +35 -0
  291. package/shared/nimbus-spawn.ts +128 -0
  292. package/shared/rest-tool-kit.ts +18 -0
  293. package/shared/run-cli-json.ts +55 -0
  294. package/shared/run-read-only-mcp-connector.ts +59 -0
  295. package/shared/safe-cli-arg.ts +55 -0
  296. package/shared/search-filter.ts +140 -0
  297. package/shared/strip-trailing-slashes.ts +7 -0
  298. package/shared/write-scope.ts +58 -0
  299. package/standalone/src/bin.ts +19 -0
  300. package/standalone/src/launcher.ts +186 -0
  301. package/standalone/tsconfig.json +8 -0
@@ -0,0 +1,309 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
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
+ }
42
+
43
+ const server = new McpServer({ name: "nimbus-confluence", version: "0.1.0" });
44
+
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
+ );
307
+
308
+ const transport = new StdioServerTransport();
309
+ await server.connect(transport);
@@ -0,0 +1,16 @@
1
+ {
2
+ "id": "com.nimbus.dagster",
3
+ "displayName": "Dagster",
4
+ "version": "0.1.0",
5
+ "description": "Dagster connector (read-only). Surfaces the user's data-orchestration jobs from a Dagster Cloud deployment or a self-hosted Dagster OSS instance as dagster:job items in the local index — each job's name, repository, code location, description, isJob flag, and tags. Useful for answering orchestration questions (\"which jobs live in the analytics repository?\", \"what tags does the nightly job carry?\") without leaving Nimbus. Dagster exposes a single GraphQL endpoint at <base_url>/graphql; auth is the Dagster-Cloud-Api-Token header (required for Dagster Cloud; self-hosted OSS users may set any non-empty placeholder). Dagster has no universal SaaS host: the per-tenant host root is supplied via the dagster base_url vault key and added to the sandbox network list at spawn time by phase3AddDagsterMcp, so the static manifest network list is intentionally empty. The list query walks the repositories→jobs catalog (repositoriesOrError → nodes[].pipelines[]) in one response and flattens to jobs, capturing each job's repository + location; a PythonError typename or a top-level GraphQL errors array degrades gracefully. v1 indexes jobs only — runs and assets are a deferred follow-up. No write tools.",
6
+ "author": "Nimbus",
7
+ "entrypoint": "dist/server.js",
8
+ "runtime": "bun",
9
+ "permissions": {
10
+ "network": [],
11
+ "filesystem": { "read": [], "write": [] }
12
+ },
13
+ "hitlRequired": [],
14
+ "syncInterval": 600,
15
+ "minNimbusVersion": "0.2.0"
16
+ }
@@ -0,0 +1,53 @@
1
+ import {
2
+ asObjectish,
3
+ makeQueryFilter,
4
+ type SearchMatchOptions,
5
+ stringField,
6
+ } from "../../../shared/search-filter.ts";
7
+
8
+ export type DagsterSearchMatchOptions = SearchMatchOptions;
9
+
10
+ /** Dagster pipeline tags are `{ key, value }` objects, not bare strings. */
11
+ function tagText(row: Record<string, unknown>): string {
12
+ const tags = row["tags"];
13
+ if (!Array.isArray(tags)) {
14
+ return "";
15
+ }
16
+ const parts: string[] = [];
17
+ for (const t of tags) {
18
+ const tr = asObjectish(t);
19
+ if (tr === undefined) {
20
+ continue;
21
+ }
22
+ const key = stringField(tr, "key");
23
+ const value = stringField(tr, "value");
24
+ if (key !== "") {
25
+ parts.push(key);
26
+ }
27
+ if (value !== "") {
28
+ parts.push(value);
29
+ }
30
+ }
31
+ return parts.join(" ");
32
+ }
33
+
34
+ /**
35
+ * The connector flattens each job to a row carrying its name, repository,
36
+ * location, plus the raw pipeline fields (description, tags). Search matches the
37
+ * query against all of those.
38
+ */
39
+ function fieldsOf(item: unknown): readonly string[] | null {
40
+ const row = asObjectish(item);
41
+ if (row === undefined) {
42
+ return null;
43
+ }
44
+ return [
45
+ stringField(row, "name"),
46
+ stringField(row, "repository"),
47
+ stringField(row, "location"),
48
+ stringField(row, "description"),
49
+ tagText(row),
50
+ ];
51
+ }
52
+
53
+ export const filterDagsterJobs = makeQueryFilter(fieldsOf);
@@ -0,0 +1,218 @@
1
+ import { z } from "zod";
2
+ import { searchToolInputSchema } from "../../../shared/mcp-search-tool.ts";
3
+ import { mcpJsonResult as jsonResult } from "../../../shared/mcp-tool-kit.ts";
4
+ import { runReadOnlyMcpConnector } from "../../../shared/run-read-only-mcp-connector.ts";
5
+ import { filterDagsterJobs } from "./search-filter.ts";
6
+
7
+ function trimTrailingSlash(s: string): string {
8
+ return s.endsWith("/") ? s.slice(0, -1) : s;
9
+ }
10
+
11
+ function apiBase(): string {
12
+ const v = process.env["DAGSTER_BASE_URL"]?.trim();
13
+ if (v === undefined || v === "") {
14
+ throw new Error("DAGSTER_BASE_URL is not set");
15
+ }
16
+ return trimTrailingSlash(v);
17
+ }
18
+
19
+ function apiToken(): string {
20
+ // Required for Dagster Cloud; self-hosted OSS may use a placeholder. The
21
+ // gateway injects it regardless to keep spawn wiring uniform.
22
+ const v = process.env["DAGSTER_API_TOKEN"]?.trim();
23
+ if (v === undefined || v === "") {
24
+ throw new Error("DAGSTER_API_TOKEN is not set");
25
+ }
26
+ return v;
27
+ }
28
+
29
+ const JOBS_QUERY = `
30
+ query NimbusJobs {
31
+ repositoriesOrError {
32
+ __typename
33
+ ... on RepositoryConnection {
34
+ nodes {
35
+ name
36
+ location { name }
37
+ pipelines { id name description isJob tags { key value } }
38
+ }
39
+ }
40
+ ... on PythonError { message }
41
+ }
42
+ }
43
+ `;
44
+
45
+ async function dagsterGraphql<T>(query: string): Promise<T> {
46
+ const res = await fetch(`${apiBase()}/graphql`, {
47
+ method: "POST",
48
+ headers: {
49
+ "Dagster-Cloud-Api-Token": apiToken(),
50
+ "Content-Type": "application/json",
51
+ Accept: "application/json",
52
+ },
53
+ body: JSON.stringify({ query }),
54
+ });
55
+ const text = await res.text();
56
+ if (!res.ok) {
57
+ throw new Error(`Dagster ${String(res.status)}: ${text.slice(0, 400)}`);
58
+ }
59
+ const parsed = JSON.parse(text) as { data?: T; errors?: unknown };
60
+ if (parsed.errors !== undefined) {
61
+ throw new Error(`Dagster GraphQL error: ${JSON.stringify(parsed.errors).slice(0, 400)}`);
62
+ }
63
+ if (parsed.data === undefined) {
64
+ throw new Error("Dagster GraphQL: response missing `data` field");
65
+ }
66
+ return parsed.data;
67
+ }
68
+
69
+ interface FlatJob {
70
+ name: string;
71
+ repository: string;
72
+ location: string | null;
73
+ id: string | null;
74
+ description: string | null;
75
+ isJob: boolean;
76
+ tags: Array<{ key: string; value: string }>;
77
+ }
78
+
79
+ function asRecord(v: unknown): Record<string, unknown> | undefined {
80
+ return v !== null && typeof v === "object" && !Array.isArray(v)
81
+ ? (v as Record<string, unknown>)
82
+ : undefined;
83
+ }
84
+
85
+ function str(row: Record<string, unknown>, key: string): string | null {
86
+ const v = row[key];
87
+ return typeof v === "string" && v !== "" ? v : null;
88
+ }
89
+
90
+ function tagPairs(raw: unknown): Array<{ key: string; value: string }> {
91
+ if (!Array.isArray(raw)) {
92
+ return [];
93
+ }
94
+ const out: Array<{ key: string; value: string }> = [];
95
+ for (const t of raw) {
96
+ const tr = asRecord(t);
97
+ if (tr === undefined) {
98
+ continue;
99
+ }
100
+ const key = str(tr, "key");
101
+ if (key === null) {
102
+ continue;
103
+ }
104
+ out.push({ key, value: str(tr, "value") ?? "" });
105
+ }
106
+ return out;
107
+ }
108
+
109
+ function pipelinesOf(node: unknown): {
110
+ repository: string;
111
+ location: string | null;
112
+ rows: unknown[];
113
+ } {
114
+ const repo = asRecord(node);
115
+ if (repo === undefined) {
116
+ return { repository: "", location: null, rows: [] };
117
+ }
118
+ const locationRow = asRecord(repo["location"]);
119
+ const pipelines = repo["pipelines"];
120
+ return {
121
+ repository: str(repo, "name") ?? "",
122
+ location: locationRow === undefined ? null : str(locationRow, "name"),
123
+ rows: Array.isArray(pipelines) ? pipelines : [],
124
+ };
125
+ }
126
+
127
+ function pipelineToJob(p: unknown, repository: string, location: string | null): FlatJob | null {
128
+ const pr = asRecord(p);
129
+ if (pr === undefined) {
130
+ return null;
131
+ }
132
+ const name = str(pr, "name");
133
+ if (name === null) {
134
+ return null;
135
+ }
136
+ return {
137
+ name,
138
+ repository,
139
+ location,
140
+ id: str(pr, "id"),
141
+ description: str(pr, "description"),
142
+ isJob: pr["isJob"] === true,
143
+ tags: tagPairs(pr["tags"]),
144
+ };
145
+ }
146
+
147
+ function flattenJobs(data: unknown): FlatJob[] {
148
+ const root = asRecord(data) ?? {};
149
+ const repos = asRecord(root["repositoriesOrError"]) ?? {};
150
+ if (repos["__typename"] === "PythonError") {
151
+ const message = str(repos, "message") ?? "unknown error";
152
+ throw new Error(`Dagster repositoriesOrError PythonError: ${message}`);
153
+ }
154
+ const nodes = repos["nodes"];
155
+ if (!Array.isArray(nodes)) {
156
+ return [];
157
+ }
158
+ const out: FlatJob[] = [];
159
+ for (const node of nodes) {
160
+ const { repository, location, rows } = pipelinesOf(node);
161
+ for (const p of rows) {
162
+ const job = pipelineToJob(p, repository, location);
163
+ if (job !== null) {
164
+ out.push(job);
165
+ }
166
+ }
167
+ }
168
+ return out;
169
+ }
170
+
171
+ async function listJobs(): Promise<FlatJob[]> {
172
+ return flattenJobs(await dagsterGraphql<unknown>(JOBS_QUERY));
173
+ }
174
+
175
+ await runReadOnlyMcpConnector("nimbus-dagster", (reg) => {
176
+ reg(
177
+ "dagster_list",
178
+ "List Dagster jobs. Walks the repositories catalog via a single GraphQL query (repositoriesOrError → nodes[].pipelines[]) against POST <base_url>/graphql and flattens to jobs, each carrying its repository and code location. Returns an { items: [...] } envelope.",
179
+ z.object({
180
+ limit: z.number().int().min(1).max(2000).optional(),
181
+ }),
182
+ async (p) => {
183
+ const jobs = await listJobs();
184
+ const limited = p.limit === undefined ? jobs : jobs.slice(0, p.limit);
185
+ return jsonResult({ items: limited });
186
+ },
187
+ );
188
+
189
+ reg(
190
+ "dagster_get",
191
+ "Fetch one Dagster job by its stable id. The id is either the full location:repository:jobName triple or just the bare jobName (the first match wins when only a name is given). Throws when no job matches.",
192
+ z.object({
193
+ id: z.string().min(1),
194
+ }),
195
+ async (p) => {
196
+ const jobs = await listJobs();
197
+ const match = jobs.find((j) => {
198
+ const triple = `${j.location ?? "_"}:${j.repository}:${j.name}`;
199
+ return triple === p.id || j.name === p.id;
200
+ });
201
+ if (match === undefined) {
202
+ throw new Error(`Dagster: job ${p.id} not found`);
203
+ }
204
+ return jsonResult(match);
205
+ },
206
+ );
207
+
208
+ reg(
209
+ "dagster_search",
210
+ "Substring search across Dagster jobs. Matches the query (case-insensitive) against the job name, repository, code location, description, and tag keys/values. Returns a { matches: [...] } envelope.",
211
+ searchToolInputSchema(2000),
212
+ async (p) => {
213
+ const jobs = await listJobs();
214
+ const matches = filterDagsterJobs(jobs, { query: p.query, limit: p.limit });
215
+ return jsonResult({ matches });
216
+ },
217
+ );
218
+ });
@@ -0,0 +1,16 @@
1
+ {
2
+ "id": "com.nimbus.databricks",
3
+ "displayName": "Databricks",
4
+ "version": "0.1.0",
5
+ "description": "Databricks connector (read-only). Surfaces Databricks Jobs — name, creator, schedule, format, created timestamp, and the latest run status (life-cycle/result state, run id, started-at, duration, cluster, triggered-by) — into the local index as `databricks:data_pipeline` items via the Jobs API 2.1 (`GET /api/2.1/jobs/list`, token-paginated, enriched from one page of `GET /api/2.1/jobs/runs/list`). Databricks Personal Access Token auth (sent as `Authorization: Bearer <token>`). Databricks has no universal SaaS host: the per-workspace URL is supplied via the `databricks.host` vault key and added to the sandbox network list at spawn time by `phase3AddDatabricksMcp`, so the static manifest network list is intentionally empty. v1 indexes jobs only — clusters, SQL warehouses, and notebooks/workspace listing are a deferred follow-up; `job.trigger` / `job.cancel` / `cluster.restart` (HITL) writes are deferred to Phase 6. No write tools.",
6
+ "author": "Nimbus",
7
+ "entrypoint": "dist/server.js",
8
+ "runtime": "bun",
9
+ "permissions": {
10
+ "network": [],
11
+ "filesystem": { "read": [], "write": [] }
12
+ },
13
+ "hitlRequired": [],
14
+ "syncInterval": 600,
15
+ "minNimbusVersion": "0.2.0"
16
+ }
@@ -0,0 +1,28 @@
1
+ import {
2
+ asRecord,
3
+ makeQueryFilter,
4
+ type SearchMatchOptions,
5
+ } from "../../../shared/search-filter.ts";
6
+
7
+ export type DatabricksSearchMatchOptions = SearchMatchOptions;
8
+
9
+ function stringAt(row: Record<string, unknown>, key: string): string {
10
+ const v = row[key];
11
+ return typeof v === "string" ? v : "";
12
+ }
13
+
14
+ function numberAt(row: Record<string, unknown>, key: string): string {
15
+ const v = row[key];
16
+ return typeof v === "number" && Number.isFinite(v) ? String(v) : "";
17
+ }
18
+
19
+ function fieldsOf(item: unknown): readonly string[] | null {
20
+ const row = asRecord(item);
21
+ if (row === undefined) {
22
+ return null;
23
+ }
24
+ const settings = asRecord(row["settings"]) ?? {};
25
+ return [stringAt(settings, "name"), stringAt(row, "creator_user_name"), numberAt(row, "job_id")];
26
+ }
27
+
28
+ export const filterDatabricksJobs = makeQueryFilter(fieldsOf);