@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,65 @@
1
+ import { z } from "zod";
2
+ import { matchesResult, 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 { filterStripeInvoices } from "./search-filter.ts";
6
+
7
+ const BASE = "https://api.stripe.com";
8
+
9
+ function apiKey(): string {
10
+ const t = process.env["STRIPE_API_KEY"]?.trim();
11
+ if (t === undefined || t === "") {
12
+ throw new Error("STRIPE_API_KEY is not set");
13
+ }
14
+ return t;
15
+ }
16
+
17
+ function authHeader(): Record<string, string> {
18
+ return { Authorization: `Bearer ${apiKey()}`, Accept: "application/json" };
19
+ }
20
+
21
+ async function stripeGet(path: string): Promise<unknown> {
22
+ const res = await fetch(`${BASE}${path}`, { headers: authHeader() });
23
+ const text = await res.text();
24
+ if (!res.ok) {
25
+ throw new Error(`Stripe ${String(res.status)}: ${text.slice(0, 400)}`);
26
+ }
27
+ return JSON.parse(text) as unknown;
28
+ }
29
+
30
+ await runReadOnlyMcpConnector("nimbus-stripe", (reg) => {
31
+ reg(
32
+ "stripe_list",
33
+ "List recent Stripe invoices (`GET /v1/invoices`), most-recent-first, capped at `limit` (default 100). Returns the `{ object: 'list', data, has_more }` envelope.",
34
+ z.object({
35
+ limit: z.number().int().min(1).max(100).optional(),
36
+ }),
37
+ async (p) => {
38
+ const search = new URLSearchParams({ limit: String(p.limit ?? 100) });
39
+ return jsonResult(await stripeGet(`/v1/invoices?${search.toString()}`));
40
+ },
41
+ );
42
+
43
+ reg(
44
+ "stripe_get",
45
+ "Fetch one Stripe invoice by its id (`GET /v1/invoices/{id}`, e.g. `in_1A2b...`). Returns the invoice object directly. Throws when no match is found.",
46
+ z.object({
47
+ id: z.string().min(1),
48
+ }),
49
+ async (p) => {
50
+ return jsonResult(await stripeGet(`/v1/invoices/${encodeURIComponent(p.id)}`));
51
+ },
52
+ );
53
+
54
+ reg(
55
+ "stripe_search",
56
+ "Substring search across recent Stripe invoices. Matches the query against id, number, status, customer id, customer name, customer email, and the description (case-insensitive). Returns a `{ matches: [...] }` envelope.",
57
+ searchToolInputSchema(100),
58
+ async (p) => {
59
+ const search = new URLSearchParams({ limit: "100" });
60
+ const root = await stripeGet(`/v1/invoices?${search.toString()}`);
61
+ const data = (root as { data?: unknown[] } | null)?.data;
62
+ return matchesResult(data, filterStripeInvoices, p);
63
+ },
64
+ );
65
+ });
@@ -0,0 +1,16 @@
1
+ {
2
+ "id": "com.nimbus.superset",
3
+ "displayName": "Superset",
4
+ "version": "0.1.0",
5
+ "description": "Apache Superset connector (read-only). Surfaces Superset dashboards — title, slug, published flag, status, owner count, last editor, changed timestamp — into the local index as `superset:dashboard` items. Superset has no static API key: the connector authenticates with username/password against `POST /api/v1/security/login` to obtain a short-lived JWT, then calls the dashboards API with `Authorization: Bearer <access_token>`. Superset has no universal SaaS host: the server host is supplied via the `superset.url` vault key and added to the sandbox network list at spawn time by `phase3AddSupersetMcp`, so the static manifest network list is intentionally empty. v1 indexes dashboards only — charts, datasets, saved queries, and write tools 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,22 @@
1
+ import {
2
+ asRecord,
3
+ makeQueryFilter,
4
+ type SearchMatchOptions,
5
+ } from "../../../shared/search-filter.ts";
6
+
7
+ export type SupersetSearchMatchOptions = 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 fieldsOf(item: unknown): readonly string[] | null {
15
+ const row = asRecord(item);
16
+ if (row === undefined) {
17
+ return null;
18
+ }
19
+ return [stringAt(row, "dashboard_title"), stringAt(row, "slug")];
20
+ }
21
+
22
+ export const filterSupersetDashboards = makeQueryFilter(fieldsOf);
@@ -0,0 +1,119 @@
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 { filterSupersetDashboards } 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["SUPERSET_URL"]?.trim();
13
+ if (v === undefined || v === "") {
14
+ throw new Error("SUPERSET_URL is not set");
15
+ }
16
+ return trimTrailingSlash(v);
17
+ }
18
+
19
+ function requiredEnv(name: string): string {
20
+ const v = process.env[name]?.trim();
21
+ if (v === undefined || v === "") {
22
+ throw new Error(`${name} is not set`);
23
+ }
24
+ return v;
25
+ }
26
+
27
+ let cachedToken: string | null = null;
28
+
29
+ async function login(): Promise<string> {
30
+ if (cachedToken !== null) {
31
+ return cachedToken;
32
+ }
33
+ const username = requiredEnv("SUPERSET_USERNAME");
34
+ const password = requiredEnv("SUPERSET_PASSWORD");
35
+ const res = await fetch(`${apiBase()}/api/v1/security/login`, {
36
+ method: "POST",
37
+ headers: { "Content-Type": "application/json", Accept: "application/json" },
38
+ body: JSON.stringify({ username, password, provider: "db", refresh: true }),
39
+ });
40
+ const text = await res.text();
41
+ if (!res.ok) {
42
+ throw new Error(`Superset login ${String(res.status)}: ${text.slice(0, 400)}`);
43
+ }
44
+ let parsed: { access_token?: unknown };
45
+ try {
46
+ parsed = JSON.parse(text) as { access_token?: unknown };
47
+ } catch {
48
+ throw new Error("Superset login: invalid JSON response");
49
+ }
50
+ if (typeof parsed.access_token !== "string" || parsed.access_token === "") {
51
+ throw new Error("Superset login: no access_token in response");
52
+ }
53
+ cachedToken = parsed.access_token;
54
+ return cachedToken;
55
+ }
56
+
57
+ async function supersetGet(path: string): Promise<unknown> {
58
+ const token = await login();
59
+ const res = await fetch(`${apiBase()}${path}`, {
60
+ headers: { Authorization: `Bearer ${token}`, Accept: "application/json" },
61
+ });
62
+ const text = await res.text();
63
+ if (!res.ok) {
64
+ throw new Error(`Superset ${String(res.status)}: ${text.slice(0, 400)}`);
65
+ }
66
+ return JSON.parse(text) as unknown;
67
+ }
68
+
69
+ function dashboardsFrom(root: unknown): unknown[] {
70
+ const result = (root as { result?: unknown } | null)?.result;
71
+ if (Array.isArray(result)) {
72
+ return result;
73
+ }
74
+ return Array.isArray(root) ? root : [];
75
+ }
76
+
77
+ function dashboardListPath(page: number, pageSize: number): string {
78
+ const q = encodeURIComponent(`(page:${String(page)},page_size:${String(pageSize)})`);
79
+ return `/api/v1/dashboard/?q=${q}`;
80
+ }
81
+
82
+ await runReadOnlyMcpConnector("nimbus-superset", (reg) => {
83
+ reg(
84
+ "superset_list",
85
+ "List Apache Superset dashboards (`GET /api/v1/dashboard/`). `limit` (default 100) caps the page_size of the single first-page request. Returns the `.result` array from the Superset envelope.",
86
+ z.object({
87
+ limit: z.number().int().min(1).max(500).optional(),
88
+ }),
89
+ async (p) => {
90
+ const root = await supersetGet(dashboardListPath(0, p.limit ?? 100));
91
+ return jsonResult(dashboardsFrom(root));
92
+ },
93
+ );
94
+
95
+ reg(
96
+ "superset_get",
97
+ "Fetch one Apache Superset dashboard by numeric id (`/api/v1/dashboard/{id}`). Throws when no dashboard with that id exists.",
98
+ z.object({
99
+ id: z.number().int(),
100
+ }),
101
+ async (p) => {
102
+ return jsonResult(await supersetGet(`/api/v1/dashboard/${encodeURIComponent(String(p.id))}`));
103
+ },
104
+ );
105
+
106
+ reg(
107
+ "superset_search",
108
+ "Substring search across Apache Superset dashboards. Matches the query (case-insensitive) against dashboard title and slug. Returns a `{ matches: [...] }` envelope.",
109
+ searchToolInputSchema(200),
110
+ async (p) => {
111
+ const root = await supersetGet(dashboardListPath(0, 500));
112
+ const matches = filterSupersetDashboards(dashboardsFrom(root), {
113
+ query: p.query,
114
+ limit: p.limit,
115
+ });
116
+ return jsonResult({ matches });
117
+ },
118
+ );
119
+ });
@@ -0,0 +1,16 @@
1
+ {
2
+ "id": "com.nimbus.tableau",
3
+ "displayName": "Tableau",
4
+ "version": "0.1.0",
5
+ "description": "Tableau connector. Indexes Tableau dashboards and views as `tableau:dashboard` items in the local index and exposes three read-only tools (`tableau_list`, `tableau_get`, `tableau_search`) plus two HITL-gated write tools: `tableau_datasource_refresh` (trigger an extract refresh for a published datasource) and `tableau_workbook_refresh` (trigger an extract refresh for a workbook). Uses the Tableau REST API with Personal Access Token (PAT) auth. The per-tenant server URL is supplied via the `tableau.url` vault key and its hostname is added to the sandbox network list at spawn time by `phase3AddTableauMcp`, so the static manifest network list is intentionally empty.",
6
+ "author": "Nimbus",
7
+ "entrypoint": "dist/server.js",
8
+ "runtime": "bun",
9
+ "permissions": {
10
+ "network": [],
11
+ "filesystem": { "read": [], "write": [] }
12
+ },
13
+ "hitlRequired": ["write"],
14
+ "syncInterval": 600,
15
+ "minNimbusVersion": "0.5.0"
16
+ }
@@ -0,0 +1,22 @@
1
+ import {
2
+ asRecord,
3
+ makeQueryFilter,
4
+ type SearchMatchOptions,
5
+ } from "../../../shared/search-filter.ts";
6
+
7
+ export type TableauSearchMatchOptions = 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 fieldsOf(item: unknown): readonly string[] | null {
15
+ const row = asRecord(item);
16
+ if (row === undefined) {
17
+ return null;
18
+ }
19
+ return [stringAt(row, "name"), stringAt(row, "luid")];
20
+ }
21
+
22
+ export const filterTableauViews = makeQueryFilter(fieldsOf);
@@ -0,0 +1,248 @@
1
+ import { z } from "zod";
2
+ import { type ConsentServer, createWriteToolRegistrar } from "../../../shared/consent-kit.ts";
3
+ import { searchToolInputSchema } from "../../../shared/mcp-search-tool.ts";
4
+ import { fetchWithTimeout, mcpJsonResult as jsonResult } from "../../../shared/mcp-tool-kit.ts";
5
+ import {
6
+ runReadOnlyMcpConnector,
7
+ type ZodToolRegistrar,
8
+ } from "../../../shared/run-read-only-mcp-connector.ts";
9
+ import { filterTableauViews } from "./search-filter.ts";
10
+
11
+ function trimTrailingSlash(s: string): string {
12
+ return s.endsWith("/") ? s.slice(0, -1) : s;
13
+ }
14
+
15
+ function apiBase(): string {
16
+ const v = process.env["TABLEAU_URL"]?.trim();
17
+ if (v === undefined || v === "") {
18
+ throw new Error("TABLEAU_URL is not set");
19
+ }
20
+ return trimTrailingSlash(v);
21
+ }
22
+
23
+ function patName(): string {
24
+ const v = process.env["TABLEAU_PAT_NAME"]?.trim();
25
+ if (v === undefined || v === "") {
26
+ throw new Error("TABLEAU_PAT_NAME is not set");
27
+ }
28
+ return v;
29
+ }
30
+
31
+ function patSecret(): string {
32
+ const v = process.env["TABLEAU_PAT_SECRET"]?.trim();
33
+ if (v === undefined || v === "") {
34
+ throw new Error("TABLEAU_PAT_SECRET is not set");
35
+ }
36
+ return v;
37
+ }
38
+
39
+ interface SigninResult {
40
+ token: string;
41
+ siteId: string;
42
+ }
43
+
44
+ async function tableauSignin(): Promise<SigninResult> {
45
+ const base = apiBase();
46
+ const res = await fetchWithTimeout(`${base}/api/3.4/auth/signin`, {
47
+ method: "POST",
48
+ headers: { "Content-Type": "application/json", Accept: "application/json" },
49
+ body: JSON.stringify({
50
+ credentials: {
51
+ personalAccessTokenName: patName(),
52
+ personalAccessTokenSecret: patSecret(),
53
+ site: { contentUrl: "" },
54
+ },
55
+ }),
56
+ });
57
+ const text = await res.text();
58
+ if (!res.ok) {
59
+ throw new Error(`Tableau signin ${String(res.status)}: ${text.slice(0, 400)}`);
60
+ }
61
+ const parsed = JSON.parse(text) as unknown;
62
+ const root =
63
+ parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)
64
+ ? (parsed as Record<string, unknown>)
65
+ : null;
66
+ const creds = root?.["credentials"];
67
+ const credsObj =
68
+ creds !== null && typeof creds === "object" && !Array.isArray(creds)
69
+ ? (creds as Record<string, unknown>)
70
+ : null;
71
+ const token = typeof credsObj?.["token"] === "string" ? credsObj["token"] : null;
72
+ const site = credsObj?.["site"];
73
+ const siteObj =
74
+ site !== null && typeof site === "object" && !Array.isArray(site)
75
+ ? (site as Record<string, unknown>)
76
+ : null;
77
+ const siteId = typeof siteObj?.["id"] === "string" ? siteObj["id"] : null;
78
+ if (token === null || siteId === null) {
79
+ throw new Error("Tableau signin response missing credentials.token or credentials.site.id");
80
+ }
81
+ return { token, siteId };
82
+ }
83
+
84
+ /**
85
+ * List views for a site. With a `page`, requests Tableau's 1-based `pageSize`/`pageNumber` paging and
86
+ * returns `pagination.totalAvailable`; without it (used by `_get`/`_search`), returns the default page
87
+ * with `totalAvailable: 0`.
88
+ */
89
+ async function listViews(
90
+ token: string,
91
+ siteId: string,
92
+ page?: { pageSize: number; pageNumber: number },
93
+ ): Promise<{ views: unknown[]; totalAvailable: number }> {
94
+ const base = apiBase();
95
+ const qs = page === undefined ? "" : `?pageSize=${page.pageSize}&pageNumber=${page.pageNumber}`;
96
+ const res = await fetchWithTimeout(
97
+ `${base}/api/3.4/sites/${encodeURIComponent(siteId)}/views${qs}`,
98
+ { headers: { "X-Tableau-Auth": token, Accept: "application/json" } },
99
+ );
100
+ const text = await res.text();
101
+ if (!res.ok) {
102
+ throw new Error(`Tableau views ${String(res.status)}: ${text.slice(0, 400)}`);
103
+ }
104
+ const parsed = JSON.parse(text) as unknown;
105
+ const root =
106
+ parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)
107
+ ? (parsed as Record<string, unknown>)
108
+ : null;
109
+ const views = root?.["views"];
110
+ const viewsObj =
111
+ views !== null && typeof views === "object" && !Array.isArray(views)
112
+ ? (views as Record<string, unknown>)
113
+ : null;
114
+ const pagination = root?.["pagination"];
115
+ const paginationObj =
116
+ pagination !== null && typeof pagination === "object" && !Array.isArray(pagination)
117
+ ? (pagination as Record<string, unknown>)
118
+ : null;
119
+ return {
120
+ views: Array.isArray(viewsObj?.["view"]) ? (viewsObj["view"] as unknown[]) : [],
121
+ totalAvailable: Number(paginationObj?.["totalAvailable"]) || 0,
122
+ };
123
+ }
124
+
125
+ async function tableauRefresh(kind: "datasources" | "workbooks", id: string): Promise<string> {
126
+ const { token, siteId } = await tableauSignin();
127
+ const base = apiBase();
128
+ const res = await fetchWithTimeout(
129
+ `${base}/api/3.4/sites/${encodeURIComponent(siteId)}/${kind}/${encodeURIComponent(id)}/refresh`,
130
+ {
131
+ method: "POST",
132
+ headers: {
133
+ "X-Tableau-Auth": token,
134
+ Accept: "application/json",
135
+ "Content-Type": "application/json",
136
+ },
137
+ body: "{}",
138
+ },
139
+ );
140
+ const text = await res.text();
141
+ if (!res.ok)
142
+ throw new Error(`Tableau ${kind} refresh ${String(res.status)}: ${text.slice(0, 400)}`);
143
+ const root = JSON.parse(text) as { job?: { id?: string } };
144
+ const jobId = root.job?.id;
145
+ if (jobId === undefined || jobId === "") {
146
+ // Fail closed: a 2xx with no job id is a malformed response; do not report it as a queued
147
+ // refresh (which would make the agent treat a non-started refresh as success).
148
+ throw new Error(`Tableau ${kind} refresh: response missing job.id: ${text.slice(0, 400)}`);
149
+ }
150
+ return jobId;
151
+ }
152
+
153
+ export function registerTableauTools(reg: ZodToolRegistrar, server: unknown): void {
154
+ // Despite the read-only helper's name, this connector exposes write tools. The consent
155
+ // kit needs the real server, which the helper now passes through as its second argument.
156
+ const registerWriteTool = createWriteToolRegistrar(server as ConsentServer, {
157
+ connector: "tableau",
158
+ scopeEnv: "NIMBUS_MCP_TABLEAU_WRITE_SCOPE",
159
+ scopeKinds: ["resource"],
160
+ });
161
+
162
+ reg(
163
+ "tableau_list",
164
+ "List Tableau views/dashboards (`GET /api/3.4/sites/{siteId}/views`). Requires a PAT sign-in first. Paginated (1-based): `cursor` (page number) + `limit` (default 200, max 500) → `{ items, nextCursor }`.",
165
+ z.object({
166
+ cursor: z.string().nullable().optional(),
167
+ limit: z.number().int().min(1).max(500).optional(),
168
+ }),
169
+ async (p) => {
170
+ const pageSize = p.limit ?? 200;
171
+ // 1-based: null / "" / "0" / non-numeric / negative / fractional all resolve to page 1
172
+ // (never page 0 / a negative or out-of-bounds page).
173
+ const parsedPage = Math.trunc(Number(p.cursor));
174
+ const pageNumber = Number.isFinite(parsedPage) && parsedPage >= 1 ? parsedPage : 1;
175
+ const { token, siteId } = await tableauSignin();
176
+ const { views, totalAvailable } = await listViews(token, siteId, { pageSize, pageNumber });
177
+ const nextCursor = pageNumber * pageSize < totalAvailable ? String(pageNumber + 1) : null;
178
+ return jsonResult({ items: views, nextCursor });
179
+ },
180
+ );
181
+
182
+ reg(
183
+ "tableau_get",
184
+ "Fetch one Tableau view by its luid. Throws when no view with that id exists.",
185
+ z.object({
186
+ id: z.string().min(1),
187
+ }),
188
+ async (p) => {
189
+ const { token, siteId } = await tableauSignin();
190
+ const { views } = await listViews(token, siteId);
191
+ const found = views.find((v) => {
192
+ const obj =
193
+ v !== null && typeof v === "object" && !Array.isArray(v)
194
+ ? (v as Record<string, unknown>)
195
+ : null;
196
+ return obj?.["luid"] === p.id || obj?.["id"] === p.id;
197
+ });
198
+ if (found === undefined) {
199
+ throw new Error(`Tableau view not found: ${p.id}`);
200
+ }
201
+ return jsonResult(found);
202
+ },
203
+ );
204
+
205
+ reg(
206
+ "tableau_search",
207
+ "Substring search across Tableau views. Matches the query (case-insensitive) against view name and luid. Returns a `{ matches: [...] }` envelope.",
208
+ searchToolInputSchema(200),
209
+ async (p) => {
210
+ const { token, siteId } = await tableauSignin();
211
+ const { views } = await listViews(token, siteId);
212
+ const matches = filterTableauViews(views, { query: p.query, limit: p.limit });
213
+ return jsonResult({ matches });
214
+ },
215
+ );
216
+
217
+ registerWriteTool(
218
+ "tableau_datasource_refresh",
219
+ {
220
+ mutates: "tableau.datasource.refresh",
221
+ recoverable: true,
222
+ scopeTargetOf: (p) => ({ kind: "resource", value: p.id }),
223
+ },
224
+ "Trigger an extract refresh for a published datasource. Async — returns the job id.",
225
+ z.object({ id: z.string().min(1) }),
226
+ async (p) => jsonResult({ status: "queued", jobId: await tableauRefresh("datasources", p.id) }),
227
+ );
228
+
229
+ registerWriteTool(
230
+ "tableau_workbook_refresh",
231
+ {
232
+ mutates: "tableau.workbook.refresh",
233
+ recoverable: true,
234
+ scopeTargetOf: (p) => ({ kind: "resource", value: p.id }),
235
+ },
236
+ "Trigger an extract refresh for a workbook. Async — returns the job id.",
237
+ z.object({ id: z.string().min(1) }),
238
+ async (p) => jsonResult({ status: "queued", jobId: await tableauRefresh("workbooks", p.id) }),
239
+ );
240
+ }
241
+
242
+ // Exported so the bundled-connector registry can start this server explicitly: `import.meta.main`
243
+ // is false under an import, and the module must stay importable by tests without connecting stdio.
244
+ export async function startConnector(): Promise<void> {
245
+ await runReadOnlyMcpConnector("nimbus-tableau", registerTableauTools);
246
+ }
247
+
248
+ if (import.meta.main) await startConnector();
@@ -0,0 +1,20 @@
1
+ {
2
+ "id": "com.nimbus.teams",
3
+ "displayName": "Microsoft Teams",
4
+ "version": "0.1.0",
5
+ "description": "Microsoft Graph Teams connector (teams, channels, messages). Mutations require Gateway HITL.",
6
+ "author": "Nimbus",
7
+ "entrypoint": "dist/server.js",
8
+ "runtime": "bun",
9
+ "permissions": {
10
+ "network": [
11
+ "graph.microsoft.com",
12
+ "login.microsoftonline.com",
13
+ "smba.trafficmanager.net",
14
+ "api.botframework.com"
15
+ ]
16
+ },
17
+ "hitlRequired": ["write", "delete"],
18
+ "syncInterval": 300,
19
+ "minNimbusVersion": "0.2.0"
20
+ }
@@ -0,0 +1,72 @@
1
+ import { fetchBearerAuthorizedJson } from "../../../shared/fetch-bearer-json.ts";
2
+ import { requireProcessEnv } from "../../../shared/mcp-tool-kit.ts";
3
+
4
+ /**
5
+ * Bot Framework outbound send-activity for the ChatOps operational reply surface (Slice 5).
6
+ *
7
+ * A Teams bot does not use the signed-in user's Graph token to reply; it authenticates with its own
8
+ * app credentials (`TEAMS_BOT_APP_ID` / `TEAMS_BOT_APP_PASSWORD`) to obtain an app-only token from the
9
+ * Bot Framework token endpoint, then POSTs an activity to the conversation's service URL. The service
10
+ * URL is region-specific and is provided by the inbound activity; we read it from
11
+ * `TEAMS_BOT_SERVICE_URL` (the adapter captures it from the activity and injects it), defaulting to
12
+ * the global Teams endpoint.
13
+ */
14
+
15
+ const BOT_TOKEN_ENDPOINT = "https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token";
16
+ const BOT_TOKEN_SCOPE = "https://api.botframework.com/.default";
17
+ const DEFAULT_SERVICE_URL = "https://smba.trafficmanager.net/teams/";
18
+
19
+ type GraphLikeResult = { ok: boolean; status: number; json: unknown; text: string };
20
+
21
+ async function acquireBotToken(appId: string, appPassword: string): Promise<string> {
22
+ const res = await fetch(BOT_TOKEN_ENDPOINT, {
23
+ method: "POST",
24
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
25
+ body: new URLSearchParams({
26
+ grant_type: "client_credentials",
27
+ client_id: appId,
28
+ client_secret: appPassword,
29
+ scope: BOT_TOKEN_SCOPE,
30
+ }).toString(),
31
+ });
32
+ const text = await res.text();
33
+ if (!res.ok) {
34
+ throw new Error(`Bot Framework token: ${text.slice(0, 400)}`);
35
+ }
36
+ let parsed: unknown;
37
+ try {
38
+ parsed = JSON.parse(text) as unknown;
39
+ } catch {
40
+ throw new Error("Bot Framework token: non-JSON response");
41
+ }
42
+ const access =
43
+ parsed !== null && typeof parsed === "object"
44
+ ? (parsed as Record<string, unknown>)["access_token"]
45
+ : undefined;
46
+ if (typeof access !== "string" || access === "") {
47
+ throw new Error("Bot Framework token: missing access_token");
48
+ }
49
+ return access;
50
+ }
51
+
52
+ function serviceUrlBase(): string {
53
+ const raw = process.env["TEAMS_BOT_SERVICE_URL"];
54
+ const base = raw !== undefined && raw !== "" ? raw : DEFAULT_SERVICE_URL;
55
+ return base.endsWith("/") ? base : `${base}/`;
56
+ }
57
+
58
+ /** Send a plain-text message activity to a Teams conversation via the Bot Framework. */
59
+ export async function teamsBotSendActivity(
60
+ conversationId: string,
61
+ text: string,
62
+ ): Promise<GraphLikeResult> {
63
+ const appId = requireProcessEnv("TEAMS_BOT_APP_ID");
64
+ const appPassword = requireProcessEnv("TEAMS_BOT_APP_PASSWORD");
65
+ const token = await acquireBotToken(appId, appPassword);
66
+ const url = `${serviceUrlBase()}v3/conversations/${encodeURIComponent(conversationId)}/activities`;
67
+ return fetchBearerAuthorizedJson(url, token, {
68
+ method: "POST",
69
+ headers: { "Content-Type": "application/json" },
70
+ body: JSON.stringify({ type: "message", text }),
71
+ });
72
+ }