@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,92 @@
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 { filterReadwiseBooks, filterReadwiseHighlights } from "./search-filter.ts";
6
+
7
+ const BASE = "https://readwise.io";
8
+
9
+ function apiToken(): string {
10
+ const t = process.env["READWISE_TOKEN"]?.trim();
11
+ if (t === undefined || t === "") {
12
+ throw new Error("READWISE_TOKEN is not set");
13
+ }
14
+ return t;
15
+ }
16
+
17
+ function authHeader(): Record<string, string> {
18
+ return { Authorization: `Token ${apiToken()}`, Accept: "application/json" };
19
+ }
20
+
21
+ async function readwiseGet(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(`Readwise ${String(res.status)}: ${text.slice(0, 400)}`);
26
+ }
27
+ return JSON.parse(text) as unknown;
28
+ }
29
+
30
+ await runReadOnlyMcpConnector("nimbus-readwise", (reg) => {
31
+ reg(
32
+ "readwise_list",
33
+ "List the user's Readwise highlights (`GET /api/v2/highlights/?page_size=1000`). Returns the DRF `{ count, next, previous, results: [...] }` envelope — `results` holds the highlight objects and `next` is the next-page URL (null on the last page).",
34
+ z.object({}),
35
+ async () => {
36
+ return jsonResult(await readwiseGet(`/api/v2/highlights/?page_size=1000`));
37
+ },
38
+ );
39
+
40
+ reg(
41
+ "readwise_get",
42
+ "Fetch one Readwise highlight by its id (`GET /api/v2/highlights/{id}/`). Returns the highlight object directly. Throws when no match is found.",
43
+ z.object({
44
+ id: z.string().min(1),
45
+ }),
46
+ async (p) => {
47
+ return jsonResult(await readwiseGet(`/api/v2/highlights/${encodeURIComponent(p.id)}/`));
48
+ },
49
+ );
50
+
51
+ reg(
52
+ "readwise_search",
53
+ "Substring search across the user's Readwise highlights (first page only). Matches the query against the highlight text, the user's note, color, location_type, and tag names (case-insensitive). Returns a `{ matches: [...] }` envelope.",
54
+ searchToolInputSchema(100),
55
+ async (p) => {
56
+ const root = await readwiseGet(`/api/v2/highlights/?page_size=1000`);
57
+ const results = (root as { results?: unknown[] } | null)?.results;
58
+ return matchesResult(results, filterReadwiseHighlights, p);
59
+ },
60
+ );
61
+
62
+ reg(
63
+ "readwise_books_list",
64
+ "List the user's Readwise books/articles (`GET /api/v2/books/?page_size=1000`) — the parent records a highlight's `book_id` points at. Returns the same DRF `{ count, next, previous, results: [...] }` envelope as `readwise_list`; `results` holds the book objects (`id`, `title`, `author`, `category`, `source`, `num_highlights`, `highlights_url`, `source_url`, `asin`, `tags`, `document_note`).",
65
+ z.object({}),
66
+ async () => {
67
+ return jsonResult(await readwiseGet(`/api/v2/books/?page_size=1000`));
68
+ },
69
+ );
70
+
71
+ reg(
72
+ "readwise_book_get",
73
+ "Fetch one Readwise book/article by its id (`GET /api/v2/books/{id}/`). Note the id space is SEPARATE from the highlight id space — pass a highlight's `book_id`, not its `id`. Returns the book object directly. Throws when no match is found.",
74
+ z.object({
75
+ id: z.string().min(1),
76
+ }),
77
+ async (p) => {
78
+ return jsonResult(await readwiseGet(`/api/v2/books/${encodeURIComponent(p.id)}/`));
79
+ },
80
+ );
81
+
82
+ reg(
83
+ "readwise_books_search",
84
+ "Substring search across the user's Readwise books/articles (first page only). Matches the query against the title, author, category, source, the user's document note, and tag names (case-insensitive). Returns a `{ matches: [...] }` envelope.",
85
+ searchToolInputSchema(100),
86
+ async (p) => {
87
+ const root = await readwiseGet(`/api/v2/books/?page_size=1000`);
88
+ const results = (root as { results?: unknown[] } | null)?.results;
89
+ return matchesResult(results, filterReadwiseBooks, p);
90
+ },
91
+ );
92
+ });
@@ -0,0 +1,15 @@
1
+ {
2
+ "id": "com.nimbus.sagemaker",
3
+ "displayName": "Amazon SageMaker",
4
+ "version": "0.1.0",
5
+ "description": "Amazon SageMaker model-registry metadata connector (Tier-3, no-row-data). Indexes ML-model REGISTRY metadata only — model name, ARN, primary-container image reference, model-data S3 URL (a pointer string, not the bytes), execution-role ARN, and creation time — via the AWS CLI's SageMaker list-models and describe-model commands. Never invokes an endpoint and never fetches inference data, training data, or model-artifact bytes: there is no sagemaker-runtime invoke-endpoint and no predict/query/records path. Reuses the existing AWS credentials via the aws CLI. Read-only; no write tools.",
6
+ "author": "Nimbus",
7
+ "entrypoint": "dist/server.js",
8
+ "runtime": "bun",
9
+ "permissions": {
10
+ "network": ["sts.amazonaws.com"]
11
+ },
12
+ "hitlRequired": [],
13
+ "syncInterval": 600,
14
+ "minNimbusVersion": "0.2.0"
15
+ }
@@ -0,0 +1,6 @@
1
+ import { runReadOnlyMcpConnector } from "../../../shared/run-read-only-mcp-connector.ts";
2
+ import { registerSagemakerTools } from "./tools.ts";
3
+
4
+ await runReadOnlyMcpConnector("nimbus-sagemaker", (reg) => {
5
+ registerSagemakerTools(reg);
6
+ });
@@ -0,0 +1,121 @@
1
+ import { z } from "zod";
2
+ import { mcpJsonResult as jsonResult } from "../../../shared/mcp-tool-kit.ts";
3
+ import { nimbusSpawn } from "../../../shared/nimbus-spawn.ts";
4
+ import type { ZodToolRegistrar } from "../../../shared/run-read-only-mcp-connector.ts";
5
+ import { isSafeCliArg } from "../../../shared/safe-cli-arg.ts";
6
+
7
+ /**
8
+ * A SageMaker model name passed as a value to the `aws sagemaker` CLI. Rejected
9
+ * at the schema boundary if it begins with `-` (argv flag smuggling) or contains
10
+ * control characters. Exported so the contract test can assert the guard rejects
11
+ * a `-`-prefixed value.
12
+ */
13
+ export const cliArg = z
14
+ .string()
15
+ .min(1)
16
+ .refine(isSafeCliArg, { message: 'must not start with "-" or contain control characters' });
17
+
18
+ /**
19
+ * Amazon SageMaker (Tier-3, metadata-only) MCP tool surface. ALL tools index
20
+ * model-REGISTRY metadata only (model name, ARN, primary-container image
21
+ * reference, model-data S3 URL pointer, execution-role ARN, creation time).
22
+ * NONE fetch inference / training / model-artifact data — there is NO
23
+ * `invoke-endpoint`, NO `sagemaker-runtime`, NO predict/query/records path. The
24
+ * names are introspected by the `assertNoRowDataTools` contract test, so they
25
+ * must never contain a row-data segment (invoke/predict/query/records/scan/…).
26
+ */
27
+ export const SAGEMAKER_TOOL_NAMES = [
28
+ "sagemaker_list",
29
+ "sagemaker_get",
30
+ "sagemaker_search",
31
+ ] as const;
32
+
33
+ const PAGE = "50";
34
+
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
+ /**
53
+ * Run `aws sagemaker <args> --output json` and parse stdout. The AWS CLI reads
54
+ * AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_DEFAULT_REGION / AWS_PROFILE
55
+ * natively from the process env injected at spawn time. Throws on non-zero exit.
56
+ */
57
+ async function sagemakerCli(args: string[]): Promise<unknown> {
58
+ const { code, stdout, stderr } = await nimbusSpawn(
59
+ ["aws", "sagemaker", ...args, "--output", "json"],
60
+ {},
61
+ );
62
+ const out = stdout.trim();
63
+ const err = stderr.trim();
64
+ if (code !== 0) {
65
+ throw new Error(`aws sagemaker ${args[0] ?? ""} failed: ${err.slice(0, 400)}`);
66
+ }
67
+ return out === "" ? {} : (JSON.parse(out) as unknown);
68
+ }
69
+
70
+ function modelMatches(entry: unknown, q: string): boolean {
71
+ if (!isRecord(entry)) {
72
+ return false;
73
+ }
74
+ return strField(entry, "ModelName").toLowerCase().includes(q.toLowerCase());
75
+ }
76
+
77
+ /**
78
+ * Register the read-only, metadata-only SageMaker tools onto the given
79
+ * registrar. Shared between `server.ts` (live) and the contract test
80
+ * (introspection).
81
+ */
82
+ export function registerSagemakerTools(reg: ZodToolRegistrar): void {
83
+ reg(
84
+ "sagemaker_list",
85
+ "List Amazon SageMaker models — METADATA ONLY (`aws sagemaker list-models`). Each entry carries `ModelName`, `ModelArn`, and `CreationTime`. Optionally filter by a `nameContains` substring on the model name. Never invokes an endpoint and never fetches inference / training / model-artifact data.",
86
+ z.object({
87
+ nameContains: cliArg.optional(),
88
+ }),
89
+ async (p) => {
90
+ const args = ["list-models", "--max-results", PAGE];
91
+ if (p.nameContains !== undefined) {
92
+ args.push("--name-contains", p.nameContains);
93
+ }
94
+ return jsonResult(await sagemakerCli(args));
95
+ },
96
+ );
97
+
98
+ reg(
99
+ "sagemaker_get",
100
+ "Fetch one Amazon SageMaker model's METADATA (`aws sagemaker describe-model`). Returns the model object including `ModelName`, `ModelArn`, `PrimaryContainer` (container `Image` reference + `ModelDataUrl` S3 pointer — a URI string, NOT the model bytes), `ExecutionRoleArn`, and `CreationTime`. No inference, training, or model-artifact data is returned — registry metadata only.",
101
+ z.object({
102
+ modelName: cliArg,
103
+ }),
104
+ async (p) => {
105
+ return jsonResult(await sagemakerCli(["describe-model", "--model-name", p.modelName]));
106
+ },
107
+ );
108
+
109
+ reg(
110
+ "sagemaker_search",
111
+ "Substring search over Amazon SageMaker model NAMES (case-insensitive) — METADATA ONLY. Returns a `{ matches: [...] }` envelope of `list-models` metadata entries whose `ModelName` contains the query. Never invokes an endpoint and never fetches inference / training / model-artifact data.",
112
+ z.object({
113
+ query: z.string().min(1),
114
+ }),
115
+ async (p) => {
116
+ const root = await sagemakerCli(["list-models", "--max-results", PAGE]);
117
+ const matches = asArray(root, "Models").filter((m) => modelMatches(m, p.query));
118
+ return jsonResult({ matches });
119
+ },
120
+ );
121
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "id": "com.nimbus.salesforce",
3
+ "displayName": "Salesforce",
4
+ "version": "0.1.0",
5
+ "description": "Salesforce connector (read-only). Surfaces a tenant's Opportunities as salesforce:opportunity items in the local index via the SOQL query API — GET <instance_url>/services/data/v60.0/query?q=<SOQL> over the Opportunity sObject — capturing each opportunity's name, stage, amount, close date, probability, type, and won/closed flags. Three-legged OAuth auth (Authorization: Bearer <token>; short-lived access token refreshed by the Gateway via the authorization-code-with-PKCE flow; never logged). The per-tenant API host (instance_url) is discovered at OAuth time and injected at spawn; the only fixed host declared here is login.salesforce.com (the authorize/token host) — the instance host is added to the sandbox manifest at spawn via the lazy-mesh extra-hosts path. SOQL query responses page via { records, done, nextRecordsUrl }, walked forward per cycle, page-capped. Read-only — hitlRequired is intentionally empty. Sparse structured opportunity metadata.",
6
+ "author": "Nimbus",
7
+ "entrypoint": "dist/server.js",
8
+ "runtime": "bun",
9
+ "permissions": {
10
+ "network": ["login.salesforce.com"]
11
+ },
12
+ "hitlRequired": [],
13
+ "syncInterval": 600,
14
+ "minNimbusVersion": "0.2.0"
15
+ }
@@ -0,0 +1,23 @@
1
+ import {
2
+ asObjectish,
3
+ makeQueryFilter,
4
+ type SearchMatchOptions,
5
+ stringField,
6
+ } from "../../../shared/search-filter.ts";
7
+
8
+ export type SalesforceSearchMatchOptions = SearchMatchOptions;
9
+
10
+ /**
11
+ * Salesforce Opportunity records are flat objects with PascalCase fields:
12
+ * `{ Id, Name, StageName, Amount, CloseDate, Type, ... }`. Match (case-insensitive
13
+ * substring) against the opportunity name, its stage, and its type.
14
+ */
15
+ function fieldsOf(item: unknown): readonly string[] | null {
16
+ const row = asObjectish(item);
17
+ if (row === undefined) {
18
+ return null;
19
+ }
20
+ return [stringField(row, "Name"), stringField(row, "StageName"), stringField(row, "Type")];
21
+ }
22
+
23
+ export const filterSalesforceOpportunities = makeQueryFilter(fieldsOf);
@@ -0,0 +1,94 @@
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 { filterSalesforceOpportunities } from "./search-filter.ts";
6
+
7
+ const API_VERSION = "v60.0";
8
+ const OPPORTUNITY_FIELDS =
9
+ "Id, Name, StageName, Amount, CloseDate, Probability, Type, IsClosed, IsWon, LastModifiedDate, CreatedDate";
10
+
11
+ function accessToken(): string {
12
+ const t = process.env["SALESFORCE_ACCESS_TOKEN"]?.trim();
13
+ if (t === undefined || t === "") {
14
+ throw new Error("SALESFORCE_ACCESS_TOKEN is not set");
15
+ }
16
+ return t;
17
+ }
18
+
19
+ function instanceUrl(): string {
20
+ let raw = process.env["SALESFORCE_INSTANCE_URL"]?.trim();
21
+ if (raw === undefined || raw === "") {
22
+ throw new Error("SALESFORCE_INSTANCE_URL is not set");
23
+ }
24
+ // Strip trailing slashes without a backtracking regex.
25
+ while (raw.endsWith("/")) {
26
+ raw = raw.slice(0, -1);
27
+ }
28
+ return raw;
29
+ }
30
+
31
+ function authHeader(): Record<string, string> {
32
+ return { Authorization: `Bearer ${accessToken()}`, Accept: "application/json" };
33
+ }
34
+
35
+ async function salesforceGet(path: string): Promise<unknown> {
36
+ const res = await fetch(`${instanceUrl()}${path}`, { headers: authHeader() });
37
+ const text = await res.text();
38
+ if (!res.ok) {
39
+ throw new Error(`Salesforce ${String(res.status)}: ${text.slice(0, 400)}`);
40
+ }
41
+ return JSON.parse(text) as unknown;
42
+ }
43
+
44
+ function recordsFrom(root: unknown): unknown[] {
45
+ const records = (root as { records?: unknown } | null)?.records;
46
+ return Array.isArray(records) ? records : [];
47
+ }
48
+
49
+ function listQueryPath(limit: number): string {
50
+ const soql = `SELECT ${OPPORTUNITY_FIELDS} FROM Opportunity ORDER BY LastModifiedDate DESC LIMIT ${String(limit)}`;
51
+ return `/services/data/${API_VERSION}/query?q=${encodeURIComponent(soql)}`;
52
+ }
53
+
54
+ await runReadOnlyMcpConnector("nimbus-salesforce", (reg) => {
55
+ reg(
56
+ "salesforce_list",
57
+ "List the authenticated org's Salesforce Opportunities via the SOQL query API (GET <instance_url>/services/data/v60.0/query?q=SELECT ... FROM Opportunity ORDER BY LastModifiedDate DESC LIMIT N). Returns the { totalSize, done, records: [...], nextRecordsUrl? } envelope — `records` holds the Opportunity objects, each shaped { Id, Name, StageName, Amount, CloseDate, Probability, Type, IsClosed, IsWon, LastModifiedDate, CreatedDate }. `limit` (default 200) caps the single first-page request; when `done` is false a `nextRecordsUrl` cursor is present for the next page (the local Nimbus index has full coverage).",
58
+ z.object({
59
+ limit: z.number().int().min(1).max(2000).optional(),
60
+ }),
61
+ async (p) => {
62
+ return jsonResult(await salesforceGet(listQueryPath(p.limit ?? 200)));
63
+ },
64
+ );
65
+
66
+ reg(
67
+ "salesforce_get",
68
+ "Fetch one Salesforce Opportunity by its 15- or 18-character Id (GET <instance_url>/services/data/v60.0/sobjects/Opportunity/<id>). Returns the Opportunity object directly (NOT wrapped in `records`). Throws when no Opportunity with that id exists or is accessible.",
69
+ z.object({
70
+ id: z.string().min(1),
71
+ }),
72
+ async (p) => {
73
+ return jsonResult(
74
+ await salesforceGet(
75
+ `/services/data/${API_VERSION}/sobjects/Opportunity/${encodeURIComponent(p.id)}`,
76
+ ),
77
+ );
78
+ },
79
+ );
80
+
81
+ reg(
82
+ "salesforce_search",
83
+ "**Substring search over the FIRST PAGE only** (up to the queried limit of most-recently-modified Opportunities) of the authenticated org's Salesforce Opportunities. This tool runs the SOQL query once and matches the query locally (case-insensitive) against each opportunity's name, stage, and type. **Opportunities beyond the first page are not searchable here — query the local Nimbus index instead for full coverage.** Returns a { matches: [...] } envelope.",
84
+ searchToolInputSchema(2000),
85
+ async (p) => {
86
+ const root = await salesforceGet(listQueryPath(2000));
87
+ const matches = filterSalesforceOpportunities(recordsFrom(root), {
88
+ query: p.query,
89
+ limit: p.limit,
90
+ });
91
+ return jsonResult({ matches });
92
+ },
93
+ );
94
+ });
@@ -0,0 +1,15 @@
1
+ {
2
+ "id": "com.nimbus.semgrep",
3
+ "displayName": "Semgrep",
4
+ "version": "0.1.0",
5
+ "description": "Semgrep AppSec Platform connector (SAST findings, rule matches, triage state). Read-only; surfaces severity, rule_id, file/line, repository, triage_state into the local index. Self-hosted Semgrep Enterprise instances are a known Task 14 runtime-merge follow-up — the SaaS host (semgrep.dev) is the only one in the static sandbox manifest today.",
6
+ "author": "Nimbus",
7
+ "entrypoint": "dist/server.js",
8
+ "runtime": "bun",
9
+ "permissions": {
10
+ "network": ["semgrep.dev"]
11
+ },
12
+ "hitlRequired": [],
13
+ "syncInterval": 600,
14
+ "minNimbusVersion": "0.2.0"
15
+ }
@@ -0,0 +1,31 @@
1
+ import {
2
+ asObjectish,
3
+ makeQueryFilter,
4
+ type SearchMatchOptions,
5
+ stringField,
6
+ } from "../../../shared/search-filter.ts";
7
+
8
+ export type SemgrepSearchMatchOptions = SearchMatchOptions;
9
+
10
+ function nestedString(row: Record<string, unknown>, parent: string, key: string): string {
11
+ const p = row[parent];
12
+ if (p === null || typeof p !== "object") {
13
+ return "";
14
+ }
15
+ return stringField(p as Record<string, unknown>, key);
16
+ }
17
+
18
+ function fieldsOf(item: unknown): readonly string[] | null {
19
+ const row = asObjectish(item);
20
+ if (row === undefined) {
21
+ return null;
22
+ }
23
+ return [
24
+ stringField(row, "rule_name"),
25
+ stringField(row, "rule_message"),
26
+ nestedString(row, "location", "file_path"),
27
+ nestedString(row, "repository", "name"),
28
+ ];
29
+ }
30
+
31
+ export const filterSemgrepFindings = makeQueryFilter(fieldsOf);
@@ -0,0 +1,108 @@
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 { filterSemgrepFindings } from "./search-filter.ts";
6
+
7
+ const SEMGREP_API = "https://semgrep.dev/api/v1";
8
+ const SEVERITIES = ["critical", "high", "medium", "low", "info"] as const;
9
+ const STATUSES = ["open", "ignored", "fixed", "removed"] as const;
10
+
11
+ function deploymentSlug(): string | undefined {
12
+ const v = process.env["SEMGREP_DEPLOYMENT_SLUG"]?.trim();
13
+ return v === undefined || v === "" ? undefined : v;
14
+ }
15
+
16
+ function authHeader(): Record<string, string> {
17
+ const t = process.env["SEMGREP_TOKEN"]?.trim();
18
+ if (t === undefined || t === "") {
19
+ throw new Error("SEMGREP_TOKEN is not set");
20
+ }
21
+ return { Authorization: `Bearer ${t}`, Accept: "application/json" };
22
+ }
23
+
24
+ async function semgrepGet(path: string): Promise<unknown> {
25
+ const res = await fetch(`${SEMGREP_API}${path}`, { headers: authHeader() });
26
+ const text = await res.text();
27
+ if (!res.ok) {
28
+ throw new Error(`Semgrep ${String(res.status)}: ${text.slice(0, 400)}`);
29
+ }
30
+ return JSON.parse(text) as unknown;
31
+ }
32
+
33
+ function requireSlug(): string {
34
+ const slug = deploymentSlug();
35
+ if (slug === undefined) {
36
+ throw new Error("SEMGREP_DEPLOYMENT_SLUG is not set");
37
+ }
38
+ return slug;
39
+ }
40
+
41
+ await runReadOnlyMcpConnector("nimbus-semgrep", (reg) => {
42
+ reg(
43
+ "semgrep_list",
44
+ "List Semgrep deployments, or open findings for the configured deployment. When called without filters, returns the user's deployments (`/deployments`). Provide a `severity` or `status` filter to query findings under the deployment slug from `SEMGREP_DEPLOYMENT_SLUG` env.",
45
+ z.object({
46
+ severity: z.array(z.enum(SEVERITIES)).min(1).optional(),
47
+ status: z.array(z.enum(STATUSES)).min(1).optional(),
48
+ repository: z.string().min(1).optional(),
49
+ limit: z.number().int().min(1).max(500).optional(),
50
+ }),
51
+ async (p) => {
52
+ if (p.severity === undefined && p.status === undefined && p.repository === undefined) {
53
+ return jsonResult(await semgrepGet("/deployments"));
54
+ }
55
+ const slug = requireSlug();
56
+ const search = new URLSearchParams({
57
+ page_size: String(p.limit ?? 100),
58
+ });
59
+ if (p.severity !== undefined && p.severity.length > 0) {
60
+ search.set("severities", p.severity.join(","));
61
+ }
62
+ if (p.status !== undefined && p.status.length > 0) {
63
+ search.set("statuses", p.status.join(","));
64
+ }
65
+ if (p.repository !== undefined) {
66
+ search.set("repos", p.repository);
67
+ }
68
+ const path = `/deployments/${encodeURIComponent(slug)}/findings?${search.toString()}`;
69
+ return jsonResult(await semgrepGet(path));
70
+ },
71
+ );
72
+
73
+ reg(
74
+ "semgrep_get",
75
+ "Fetch one Semgrep finding by id from the configured deployment. Returns the matching finding envelope; throws when no match is found.",
76
+ z.object({
77
+ findingId: z.string().min(1),
78
+ }),
79
+ async (p) => {
80
+ const slug = requireSlug();
81
+ const search = new URLSearchParams({ ids: p.findingId, page_size: "1" });
82
+ const path = `/deployments/${encodeURIComponent(slug)}/findings?${search.toString()}`;
83
+ const root = await semgrepGet(path);
84
+ const findings = (root as { findings?: unknown[] } | null)?.findings;
85
+ if (!Array.isArray(findings) || findings.length === 0) {
86
+ throw new Error(`Semgrep: finding ${p.findingId} not found`);
87
+ }
88
+ return jsonResult(findings[0]);
89
+ },
90
+ );
91
+
92
+ reg(
93
+ "semgrep_search",
94
+ "Substring search across the deployment's open findings. Matches the query against `rule_name`, `rule_message`, `location.file_path`, and `repository.name` (case-insensitive). Returns a `{ matches: [...] }` envelope.",
95
+ searchToolInputSchema(200),
96
+ async (p) => {
97
+ const slug = requireSlug();
98
+ const search = new URLSearchParams({
99
+ statuses: "open",
100
+ page_size: "500",
101
+ });
102
+ const path = `/deployments/${encodeURIComponent(slug)}/findings?${search.toString()}`;
103
+ const root = await semgrepGet(path);
104
+ const findings = (root as { findings?: unknown[] } | null)?.findings;
105
+ return matchesResult(findings, filterSemgrepFindings, p);
106
+ },
107
+ );
108
+ });
@@ -0,0 +1,15 @@
1
+ {
2
+ "id": "com.nimbus.sentry",
3
+ "displayName": "Sentry",
4
+ "version": "0.1.0",
5
+ "description": "Sentry REST connector (issues, events, releases). Self-hosted Sentry (sentry.url) is a known Task 14 follow-up. Read-focused.",
6
+ "author": "Nimbus",
7
+ "entrypoint": "dist/server.js",
8
+ "runtime": "bun",
9
+ "permissions": {
10
+ "network": ["sentry.io"]
11
+ },
12
+ "hitlRequired": [],
13
+ "syncInterval": 300,
14
+ "minNimbusVersion": "0.2.0"
15
+ }
@@ -0,0 +1,66 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { z } from "zod";
4
+
5
+ import {
6
+ createRegisterSimpleTool,
7
+ createZodToolRegistrar,
8
+ mcpJsonResult as jsonResult,
9
+ } from "../../../shared/mcp-tool-kit.ts";
10
+
11
+ function apiRoot(): string {
12
+ const u = process.env["SENTRY_URL"]?.trim() || "https://sentry.io";
13
+ return `${u.replace(/\/$/, "")}/api/0`;
14
+ }
15
+
16
+ function org(): string {
17
+ const o = process.env["SENTRY_ORG_SLUG"]?.trim();
18
+ if (o === undefined || o === "") {
19
+ throw new Error("SENTRY_ORG_SLUG is not set");
20
+ }
21
+ return o;
22
+ }
23
+
24
+ function headers(): Record<string, string> {
25
+ const t = process.env["SENTRY_AUTH_TOKEN"]?.trim();
26
+ if (t === undefined || t === "") {
27
+ throw new Error("SENTRY_AUTH_TOKEN is not set");
28
+ }
29
+ return { Authorization: `Bearer ${t}`, Accept: "application/json" };
30
+ }
31
+
32
+ async function sentryGet(path: string): Promise<unknown> {
33
+ const res = await fetch(`${apiRoot()}${path}`, { headers: headers() });
34
+ const text = await res.text();
35
+ if (!res.ok) {
36
+ throw new Error(`Sentry ${String(res.status)}: ${text.slice(0, 400)}`);
37
+ }
38
+ return JSON.parse(text) as unknown;
39
+ }
40
+
41
+ const mcp = new McpServer({ name: "nimbus-sentry", version: "0.1.0" });
42
+ const reg = createZodToolRegistrar(createRegisterSimpleTool(mcp));
43
+
44
+ reg(
45
+ "sentry_issue_list",
46
+ "List unresolved issues for a project.",
47
+ z.object({ projectSlug: z.string().min(1), limit: z.number().int().min(1).max(100).optional() }),
48
+ async (p) => {
49
+ const lim = p.limit ?? 20;
50
+ return jsonResult(
51
+ await sentryGet(
52
+ `/projects/${org()}/${p.projectSlug}/issues/?query=is:unresolved&limit=${String(lim)}`,
53
+ ),
54
+ );
55
+ },
56
+ );
57
+
58
+ reg(
59
+ "sentry_release_list",
60
+ "List releases for a project.",
61
+ z.object({ projectSlug: z.string().min(1) }),
62
+ async (p) => jsonResult(await sentryGet(`/projects/${org()}/${p.projectSlug}/releases/`)),
63
+ );
64
+
65
+ const transport = new StdioServerTransport();
66
+ await mcp.connect(transport);
@@ -0,0 +1,15 @@
1
+ {
2
+ "id": "com.nimbus.slack",
3
+ "displayName": "Slack",
4
+ "version": "0.1.0",
5
+ "description": "Slack connector (channels, messages, search). Mutations require Gateway HITL.",
6
+ "author": "Nimbus",
7
+ "entrypoint": "dist/server.js",
8
+ "runtime": "bun",
9
+ "permissions": {
10
+ "network": ["slack.com", "api.slack.com", "wss-primary.slack.com", "wss-backup.slack.com"]
11
+ },
12
+ "hitlRequired": ["write", "delete"],
13
+ "syncInterval": 60,
14
+ "minNimbusVersion": "0.2.0"
15
+ }