@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.
- package/LICENSE +662 -0
- package/NOTICE +40 -0
- package/README.md +111 -0
- package/connectors/airflow/nimbus.extension.json +16 -0
- package/connectors/airflow/src/search-filter.ts +38 -0
- package/connectors/airflow/src/server.ts +91 -0
- package/connectors/apple/nimbus.extension.json +40 -0
- package/connectors/apple/src/apple-mail-core.ts +43 -0
- package/connectors/apple/src/caldav-core.ts +116 -0
- package/connectors/apple/src/calendar-tools.ts +278 -0
- package/connectors/apple/src/server.ts +541 -0
- package/connectors/apple/src/tools.ts +138 -0
- package/connectors/argocd/nimbus.extension.json +16 -0
- package/connectors/argocd/src/search-filter.ts +24 -0
- package/connectors/argocd/src/server.ts +158 -0
- package/connectors/athena/nimbus.extension.json +15 -0
- package/connectors/athena/src/server.ts +6 -0
- package/connectors/athena/src/tools.ts +181 -0
- package/connectors/aws/nimbus.extension.json +21 -0
- package/connectors/aws/src/server.ts +191 -0
- package/connectors/azure/nimbus.extension.json +15 -0
- package/connectors/azure/src/server.ts +139 -0
- package/connectors/bigeye/nimbus.extension.json +16 -0
- package/connectors/bigeye/src/search-filter.ts +22 -0
- package/connectors/bigeye/src/server.ts +161 -0
- package/connectors/bigquery/nimbus.extension.json +15 -0
- package/connectors/bigquery/src/server.ts +6 -0
- package/connectors/bigquery/src/tools.ts +175 -0
- package/connectors/bitbucket/nimbus.extension.json +15 -0
- package/connectors/bitbucket/src/server.ts +252 -0
- package/connectors/bitrise/nimbus.extension.json +15 -0
- package/connectors/bitrise/src/search-filter.ts +11 -0
- package/connectors/bitrise/src/server.ts +83 -0
- package/connectors/canva/nimbus.extension.json +15 -0
- package/connectors/canva/src/search-filter.ts +23 -0
- package/connectors/canva/src/server.ts +77 -0
- package/connectors/circleci/nimbus.extension.json +15 -0
- package/connectors/circleci/src/server.ts +184 -0
- package/connectors/cloud-logging/nimbus.extension.json +15 -0
- package/connectors/cloud-logging/src/server.ts +6 -0
- package/connectors/cloud-logging/src/tools.ts +115 -0
- package/connectors/cloudwatch/nimbus.extension.json +15 -0
- package/connectors/cloudwatch/src/server.ts +6 -0
- package/connectors/cloudwatch/src/tools.ts +144 -0
- package/connectors/codemagic/nimbus.extension.json +15 -0
- package/connectors/codemagic/src/search-filter.ts +11 -0
- package/connectors/codemagic/src/server.ts +74 -0
- package/connectors/confluence/nimbus.extension.json +15 -0
- package/connectors/confluence/src/kb-append.ts +25 -0
- package/connectors/confluence/src/server.ts +309 -0
- package/connectors/dagster/nimbus.extension.json +16 -0
- package/connectors/dagster/src/search-filter.ts +53 -0
- package/connectors/dagster/src/server.ts +218 -0
- package/connectors/databricks/nimbus.extension.json +16 -0
- package/connectors/databricks/src/search-filter.ts +28 -0
- package/connectors/databricks/src/server.ts +81 -0
- package/connectors/datadog/nimbus.extension.json +15 -0
- package/connectors/datadog/src/server.ts +56 -0
- package/connectors/dataprofile/nimbus.extension.json +16 -0
- package/connectors/dataprofile/src/profile.ts +298 -0
- package/connectors/dataprofile/src/server.ts +6 -0
- package/connectors/dataprofile/src/tools.ts +69 -0
- package/connectors/dbt/nimbus.extension.json +16 -0
- package/connectors/dbt/src/search-filter.ts +28 -0
- package/connectors/dbt/src/server.ts +96 -0
- package/connectors/dependencytrack/nimbus.extension.json +16 -0
- package/connectors/dependencytrack/src/search-filter.ts +24 -0
- package/connectors/dependencytrack/src/server.ts +80 -0
- package/connectors/discord/nimbus.extension.json +15 -0
- package/connectors/discord/src/server.ts +93 -0
- package/connectors/elasticsearch/nimbus.extension.json +16 -0
- package/connectors/elasticsearch/src/server.ts +6 -0
- package/connectors/elasticsearch/src/tools.ts +110 -0
- package/connectors/fastmail/nimbus.extension.json +16 -0
- package/connectors/fastmail/src/jmap-core.ts +86 -0
- package/connectors/fastmail/src/server.ts +194 -0
- package/connectors/fastmail/src/tools.ts +143 -0
- package/connectors/figma/nimbus.extension.json +15 -0
- package/connectors/figma/src/search-filter.ts +24 -0
- package/connectors/figma/src/server.ts +117 -0
- package/connectors/firebase/nimbus.extension.json +15 -0
- package/connectors/firebase/src/search-filter.ts +30 -0
- package/connectors/firebase/src/server.ts +94 -0
- package/connectors/firebase/src/token.ts +41 -0
- package/connectors/flagsmith/nimbus.extension.json +15 -0
- package/connectors/flagsmith/src/search-filter.ts +29 -0
- package/connectors/flagsmith/src/server.ts +102 -0
- package/connectors/flux/nimbus.extension.json +16 -0
- package/connectors/flux/src/search-filter.ts +42 -0
- package/connectors/flux/src/server.ts +182 -0
- package/connectors/gcp/nimbus.extension.json +15 -0
- package/connectors/gcp/src/server.ts +140 -0
- package/connectors/github/nimbus.extension.json +15 -0
- package/connectors/github/src/server.ts +347 -0
- package/connectors/github-actions/nimbus.extension.json +15 -0
- package/connectors/github-actions/src/server.ts +213 -0
- package/connectors/gitlab/nimbus.extension.json +15 -0
- package/connectors/gitlab/src/server.ts +392 -0
- package/connectors/gmail/nimbus.extension.json +15 -0
- package/connectors/gmail/src/server.ts +277 -0
- package/connectors/google-drive/nimbus.extension.json +15 -0
- package/connectors/google-drive/src/drive-query.ts +3 -0
- package/connectors/google-drive/src/server.ts +531 -0
- package/connectors/google-meet/nimbus.extension.json +15 -0
- package/connectors/google-meet/src/server.ts +89 -0
- package/connectors/google-photos/nimbus.extension.json +15 -0
- package/connectors/google-photos/src/server.ts +139 -0
- package/connectors/grafana/nimbus.extension.json +15 -0
- package/connectors/grafana/src/server.ts +61 -0
- package/connectors/great-expectations/nimbus.extension.json +16 -0
- package/connectors/great-expectations/src/gx-parse.ts +292 -0
- package/connectors/great-expectations/src/server.ts +6 -0
- package/connectors/great-expectations/src/tools.ts +91 -0
- package/connectors/greenhouse/nimbus.extension.json +15 -0
- package/connectors/greenhouse/src/search-filter.ts +65 -0
- package/connectors/greenhouse/src/server.ts +67 -0
- package/connectors/hubspot/nimbus.extension.json +15 -0
- package/connectors/hubspot/src/search-filter.ts +28 -0
- package/connectors/hubspot/src/server.ts +81 -0
- package/connectors/iac/nimbus.extension.json +14 -0
- package/connectors/iac/src/server.ts +145 -0
- package/connectors/imap/nimbus.extension.json +16 -0
- package/connectors/imap/src/imap-core.ts +110 -0
- package/connectors/imap/src/server.ts +232 -0
- package/connectors/imap/src/tools.ts +40 -0
- package/connectors/intercom/nimbus.extension.json +15 -0
- package/connectors/intercom/src/search-filter.ts +46 -0
- package/connectors/intercom/src/server.ts +65 -0
- package/connectors/jenkins/nimbus.extension.json +15 -0
- package/connectors/jenkins/src/jenkins-api.ts +156 -0
- package/connectors/jenkins/src/server.ts +223 -0
- package/connectors/jira/nimbus.extension.json +15 -0
- package/connectors/jira/src/server.ts +309 -0
- package/connectors/kubernetes/nimbus.extension.json +15 -0
- package/connectors/kubernetes/src/server.ts +163 -0
- package/connectors/launchdarkly/nimbus.extension.json +15 -0
- package/connectors/launchdarkly/src/search-filter.ts +31 -0
- package/connectors/launchdarkly/src/server.ts +79 -0
- package/connectors/lever/nimbus.extension.json +15 -0
- package/connectors/lever/src/search-filter.ts +35 -0
- package/connectors/lever/src/server.ts +64 -0
- package/connectors/linear/nimbus.extension.json +15 -0
- package/connectors/linear/src/server.ts +467 -0
- package/connectors/localdb/nimbus.extension.json +16 -0
- package/connectors/localdb/src/server.ts +6 -0
- package/connectors/localdb/src/sql-scan.ts +158 -0
- package/connectors/localdb/src/tools.ts +67 -0
- package/connectors/looker/nimbus.extension.json +16 -0
- package/connectors/looker/src/search-filter.ts +22 -0
- package/connectors/looker/src/server.ts +254 -0
- package/connectors/mendeley/nimbus.extension.json +15 -0
- package/connectors/mendeley/src/search-filter.ts +68 -0
- package/connectors/mendeley/src/server.ts +59 -0
- package/connectors/mercury/nimbus.extension.json +15 -0
- package/connectors/mercury/src/search-filter.ts +11 -0
- package/connectors/mercury/src/server.ts +61 -0
- package/connectors/metabase/nimbus.extension.json +16 -0
- package/connectors/metabase/src/search-filter.ts +22 -0
- package/connectors/metabase/src/server.ts +83 -0
- package/connectors/miro/nimbus.extension.json +15 -0
- package/connectors/miro/src/search-filter.ts +24 -0
- package/connectors/miro/src/server.ts +76 -0
- package/connectors/mlflow/nimbus.extension.json +16 -0
- package/connectors/mlflow/src/search-filter.ts +38 -0
- package/connectors/mlflow/src/server.ts +167 -0
- package/connectors/monte-carlo/nimbus.extension.json +16 -0
- package/connectors/monte-carlo/src/search-filter.ts +11 -0
- package/connectors/monte-carlo/src/server.ts +241 -0
- package/connectors/netlify/nimbus.extension.json +15 -0
- package/connectors/netlify/src/search-filter.ts +37 -0
- package/connectors/netlify/src/server.ts +64 -0
- package/connectors/newrelic/nimbus.extension.json +15 -0
- package/connectors/newrelic/src/server.ts +48 -0
- package/connectors/notion/nimbus.extension.json +15 -0
- package/connectors/notion/src/kb-append.ts +23 -0
- package/connectors/notion/src/server.ts +343 -0
- package/connectors/obsidian/nimbus.extension.json +19 -0
- package/connectors/obsidian/src/server.ts +433 -0
- package/connectors/onedrive/nimbus.extension.json +15 -0
- package/connectors/onedrive/src/server.ts +256 -0
- package/connectors/outlook/nimbus.extension.json +15 -0
- package/connectors/outlook/src/server.ts +358 -0
- package/connectors/outlook/src/tool-scope-policy.ts +53 -0
- package/connectors/pagerduty/nimbus.extension.json +15 -0
- package/connectors/pagerduty/src/server.ts +140 -0
- package/connectors/pipedrive/nimbus.extension.json +15 -0
- package/connectors/pipedrive/src/search-filter.ts +11 -0
- package/connectors/pipedrive/src/server.ts +62 -0
- package/connectors/powerbi/nimbus.extension.json +16 -0
- package/connectors/powerbi/src/search-filter.ts +22 -0
- package/connectors/powerbi/src/server.ts +249 -0
- package/connectors/prefect/nimbus.extension.json +16 -0
- package/connectors/prefect/src/search-filter.ts +40 -0
- package/connectors/prefect/src/server.ts +94 -0
- package/connectors/protonmail/nimbus.extension.json +16 -0
- package/connectors/protonmail/src/mail-core.ts +96 -0
- package/connectors/protonmail/src/server.ts +231 -0
- package/connectors/protonmail/src/tools.ts +45 -0
- package/connectors/raindrop/nimbus.extension.json +15 -0
- package/connectors/raindrop/src/search-filter.ts +20 -0
- package/connectors/raindrop/src/server.ts +107 -0
- package/connectors/ramp/nimbus.extension.json +15 -0
- package/connectors/ramp/src/search-filter.ts +37 -0
- package/connectors/ramp/src/server.ts +109 -0
- package/connectors/readwise/nimbus.extension.json +15 -0
- package/connectors/readwise/src/search-filter.ts +64 -0
- package/connectors/readwise/src/server.ts +92 -0
- package/connectors/sagemaker/nimbus.extension.json +15 -0
- package/connectors/sagemaker/src/server.ts +6 -0
- package/connectors/sagemaker/src/tools.ts +121 -0
- package/connectors/salesforce/nimbus.extension.json +15 -0
- package/connectors/salesforce/src/search-filter.ts +23 -0
- package/connectors/salesforce/src/server.ts +94 -0
- package/connectors/semgrep/nimbus.extension.json +15 -0
- package/connectors/semgrep/src/search-filter.ts +31 -0
- package/connectors/semgrep/src/server.ts +108 -0
- package/connectors/sentry/nimbus.extension.json +15 -0
- package/connectors/sentry/src/server.ts +66 -0
- package/connectors/slack/nimbus.extension.json +15 -0
- package/connectors/slack/src/server.ts +360 -0
- package/connectors/snowflake/nimbus.extension.json +16 -0
- package/connectors/snowflake/src/search-filter.ts +26 -0
- package/connectors/snowflake/src/server.ts +236 -0
- package/connectors/snyk/nimbus.extension.json +15 -0
- package/connectors/snyk/src/search-filter.ts +28 -0
- package/connectors/snyk/src/server.ts +144 -0
- package/connectors/sonarqube/nimbus.extension.json +15 -0
- package/connectors/sonarqube/src/search-filter.ts +28 -0
- package/connectors/sonarqube/src/server.ts +115 -0
- package/connectors/stackoverflow/nimbus.extension.json +15 -0
- package/connectors/stackoverflow/src/search-filter.ts +52 -0
- package/connectors/stackoverflow/src/server.ts +81 -0
- package/connectors/storybook/nimbus.extension.json +16 -0
- package/connectors/storybook/src/server.ts +6 -0
- package/connectors/storybook/src/storybook-parse.ts +67 -0
- package/connectors/storybook/src/tools.ts +68 -0
- package/connectors/stripe/nimbus.extension.json +15 -0
- package/connectors/stripe/src/search-filter.ts +19 -0
- package/connectors/stripe/src/server.ts +65 -0
- package/connectors/superset/nimbus.extension.json +16 -0
- package/connectors/superset/src/search-filter.ts +22 -0
- package/connectors/superset/src/server.ts +119 -0
- package/connectors/tableau/nimbus.extension.json +16 -0
- package/connectors/tableau/src/search-filter.ts +22 -0
- package/connectors/tableau/src/server.ts +248 -0
- package/connectors/teams/nimbus.extension.json +20 -0
- package/connectors/teams/src/bot-send.ts +72 -0
- package/connectors/teams/src/server.ts +257 -0
- package/connectors/testflight/nimbus.extension.json +15 -0
- package/connectors/testflight/src/jwt.ts +27 -0
- package/connectors/testflight/src/search-filter.ts +30 -0
- package/connectors/testflight/src/server.ts +74 -0
- package/connectors/vercel/nimbus.extension.json +15 -0
- package/connectors/vercel/src/search-filter.ts +34 -0
- package/connectors/vercel/src/server.ts +79 -0
- package/connectors/vertex-ai/nimbus.extension.json +15 -0
- package/connectors/vertex-ai/src/server.ts +6 -0
- package/connectors/vertex-ai/src/tools.ts +145 -0
- package/connectors/wiz/nimbus.extension.json +15 -0
- package/connectors/wiz/src/search-filter.ts +50 -0
- package/connectors/wiz/src/server.ts +192 -0
- package/connectors/workday/nimbus.extension.json +19 -0
- package/connectors/workday/src/search-filter.ts +5 -0
- package/connectors/workday/src/server.ts +78 -0
- package/connectors/workday/src/tools.ts +1 -0
- package/connectors/zendesk/nimbus.extension.json +16 -0
- package/connectors/zendesk/src/search-filter.ts +11 -0
- package/connectors/zendesk/src/server.ts +77 -0
- package/connectors/zoom/nimbus.extension.json +15 -0
- package/connectors/zoom/src/encode-meeting-path.ts +5 -0
- package/connectors/zoom/src/recordings-window.ts +34 -0
- package/connectors/zoom/src/search-filter.ts +42 -0
- package/connectors/zoom/src/server.ts +98 -0
- package/connectors/zotero/nimbus.extension.json +15 -0
- package/connectors/zotero/src/search-filter.ts +81 -0
- package/connectors/zotero/src/server.ts +82 -0
- package/package.json +152 -0
- package/shared/atlassian-json-fetch.ts +42 -0
- package/shared/audit-chain.ts +133 -0
- package/shared/connector-mode.ts +60 -0
- package/shared/consent-kit.ts +323 -0
- package/shared/fetch-bearer-json.ts +18 -0
- package/shared/header-safe.ts +28 -0
- package/shared/imap-bodystructure.ts +113 -0
- package/shared/imap-mail-core.ts +57 -0
- package/shared/imap-tool-kit.ts +293 -0
- package/shared/join-api-path.ts +18 -0
- package/shared/kb-markdown.ts +142 -0
- package/shared/mcp-search-tool.ts +37 -0
- package/shared/mcp-tool-kit.ts +35 -0
- package/shared/nimbus-spawn.ts +128 -0
- package/shared/rest-tool-kit.ts +18 -0
- package/shared/run-cli-json.ts +55 -0
- package/shared/run-read-only-mcp-connector.ts +59 -0
- package/shared/safe-cli-arg.ts +55 -0
- package/shared/search-filter.ts +140 -0
- package/shared/strip-trailing-slashes.ts +7 -0
- package/shared/write-scope.ts +58 -0
- package/standalone/src/bin.ts +19 -0
- package/standalone/src/launcher.ts +186 -0
- package/standalone/tsconfig.json +8 -0
|
@@ -0,0 +1,83 @@
|
|
|
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 { filterMetabaseDashboards } 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["METABASE_URL"]?.trim();
|
|
13
|
+
if (v === undefined || v === "") {
|
|
14
|
+
throw new Error("METABASE_URL is not set");
|
|
15
|
+
}
|
|
16
|
+
return trimTrailingSlash(v);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function authHeader(): Record<string, string> {
|
|
20
|
+
const k = process.env["METABASE_API_KEY"]?.trim();
|
|
21
|
+
if (k === undefined || k === "") {
|
|
22
|
+
throw new Error("METABASE_API_KEY is not set");
|
|
23
|
+
}
|
|
24
|
+
return { "x-api-key": k, Accept: "application/json" };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function mbGet(path: string): Promise<unknown> {
|
|
28
|
+
const res = await fetch(`${apiBase()}${path}`, { headers: authHeader() });
|
|
29
|
+
const text = await res.text();
|
|
30
|
+
if (!res.ok) {
|
|
31
|
+
throw new Error(`Metabase ${String(res.status)}: ${text.slice(0, 400)}`);
|
|
32
|
+
}
|
|
33
|
+
return JSON.parse(text) as unknown;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function dashboardsFrom(root: unknown): unknown[] {
|
|
37
|
+
if (Array.isArray(root)) {
|
|
38
|
+
return root;
|
|
39
|
+
}
|
|
40
|
+
const data = (root as { data?: unknown } | null)?.data;
|
|
41
|
+
return Array.isArray(data) ? data : [];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
await runReadOnlyMcpConnector("nimbus-metabase", (reg) => {
|
|
45
|
+
reg(
|
|
46
|
+
"metabase_list",
|
|
47
|
+
"List Metabase dashboards (`GET /api/dashboard`). Metabase returns the full list in one response; `limit` (default 200) caps the returned list client-side.",
|
|
48
|
+
z.object({
|
|
49
|
+
limit: z.number().int().min(1).max(500).optional(),
|
|
50
|
+
}),
|
|
51
|
+
async (p) => {
|
|
52
|
+
const root = await mbGet("/api/dashboard");
|
|
53
|
+
const dashboards = dashboardsFrom(root);
|
|
54
|
+
const cap = p.limit ?? 200;
|
|
55
|
+
return jsonResult({ items: dashboards.slice(0, cap) });
|
|
56
|
+
},
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
reg(
|
|
60
|
+
"metabase_get",
|
|
61
|
+
"Fetch one Metabase dashboard by numeric id (`/api/dashboard/{id}`). Throws when no dashboard with that id exists.",
|
|
62
|
+
z.object({
|
|
63
|
+
id: z.number().int(),
|
|
64
|
+
}),
|
|
65
|
+
async (p) => {
|
|
66
|
+
return jsonResult(await mbGet(`/api/dashboard/${encodeURIComponent(String(p.id))}`));
|
|
67
|
+
},
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
reg(
|
|
71
|
+
"metabase_search",
|
|
72
|
+
"Substring search across Metabase dashboards. Matches the query (case-insensitive) against dashboard name and description. Returns a `{ matches: [...] }` envelope.",
|
|
73
|
+
searchToolInputSchema(200),
|
|
74
|
+
async (p) => {
|
|
75
|
+
const root = await mbGet("/api/dashboard");
|
|
76
|
+
const matches = filterMetabaseDashboards(dashboardsFrom(root), {
|
|
77
|
+
query: p.query,
|
|
78
|
+
limit: p.limit,
|
|
79
|
+
});
|
|
80
|
+
return jsonResult({ matches });
|
|
81
|
+
},
|
|
82
|
+
);
|
|
83
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "com.nimbus.miro",
|
|
3
|
+
"displayName": "Miro",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Miro connector (read-only). Surfaces the authenticated user's boards as miro:board items in the local index via GET /v2/boards — the board name, the description, the owner name, the create date, the last-modified timestamp, and the view link. Bearer OAuth auth (Authorization: Bearer <token>; short-lived access token refreshed by the Gateway via the 3-legged OAuth authorization-code flow; never logged); fixed SaaS host api.miro.com. The list endpoint paginates by the response cursor query param until absent; the gateway-side syncable walks a single forward pass per cycle, page-capped. Read-only — hitlRequired is intentionally empty. Sparse structured board metadata; the canonical url is the board viewLink.",
|
|
6
|
+
"author": "Nimbus",
|
|
7
|
+
"entrypoint": "dist/server.js",
|
|
8
|
+
"runtime": "bun",
|
|
9
|
+
"permissions": {
|
|
10
|
+
"network": ["api.miro.com"]
|
|
11
|
+
},
|
|
12
|
+
"hitlRequired": [],
|
|
13
|
+
"syncInterval": 600,
|
|
14
|
+
"minNimbusVersion": "0.2.0"
|
|
15
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import {
|
|
2
|
+
asObjectish,
|
|
3
|
+
makeQueryFilter,
|
|
4
|
+
type SearchMatchOptions,
|
|
5
|
+
stringField,
|
|
6
|
+
} from "../../../shared/search-filter.ts";
|
|
7
|
+
|
|
8
|
+
export type MiroSearchMatchOptions = SearchMatchOptions;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Miro board objects are flat — `{ id, name, description, owner: { name }, ... }`.
|
|
12
|
+
* Match against the board name, description, and owner name (case-insensitive
|
|
13
|
+
* substring). The owner is itself a nested object.
|
|
14
|
+
*/
|
|
15
|
+
function fieldsOf(item: unknown): readonly string[] | null {
|
|
16
|
+
const row = asObjectish(item);
|
|
17
|
+
if (row === undefined) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const owner = asObjectish(row["owner"]) ?? {};
|
|
21
|
+
return [stringField(row, "name"), stringField(row, "description"), stringField(owner, "name")];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const filterMiroBoards = makeQueryFilter(fieldsOf);
|
|
@@ -0,0 +1,76 @@
|
|
|
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 { filterMiroBoards } from "./search-filter.ts";
|
|
6
|
+
|
|
7
|
+
const BASE = "https://api.miro.com";
|
|
8
|
+
|
|
9
|
+
function apiToken(): string {
|
|
10
|
+
const t = process.env["MIRO_TOKEN"]?.trim();
|
|
11
|
+
if (t === undefined || t === "") {
|
|
12
|
+
throw new Error("MIRO_TOKEN is not set");
|
|
13
|
+
}
|
|
14
|
+
return t;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function authHeader(): Record<string, string> {
|
|
18
|
+
return { Authorization: `Bearer ${apiToken()}`, Accept: "application/json" };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function miroGet(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(`Miro ${String(res.status)}: ${text.slice(0, 400)}`);
|
|
26
|
+
}
|
|
27
|
+
return JSON.parse(text) as unknown;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function boardsFrom(root: unknown): unknown[] {
|
|
31
|
+
const data = (root as { data?: unknown } | null)?.data;
|
|
32
|
+
return Array.isArray(data) ? data : [];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
await runReadOnlyMcpConnector("nimbus-miro", (reg) => {
|
|
36
|
+
reg(
|
|
37
|
+
"miro_list",
|
|
38
|
+
"List the authenticated user's Miro boards (GET /v2/boards?limit=N&cursor=<cursor>). Returns the { data: [...], cursor?, total, size } envelope — `data` holds the board objects, each shaped { id, name, description, createdAt, modifiedAt, owner: { name }, viewLink, ... }; `cursor` (when present) is the opaque token for the next page. `limit` (default 50, max 50) caps the page_size of the single first-page request.",
|
|
39
|
+
z.object({
|
|
40
|
+
limit: z.number().int().min(1).max(50).optional(),
|
|
41
|
+
cursor: z.string().optional(),
|
|
42
|
+
}),
|
|
43
|
+
async (p) => {
|
|
44
|
+
const params = new URLSearchParams({ limit: String(p.limit ?? 50) });
|
|
45
|
+
if (p.cursor !== undefined && p.cursor !== "") {
|
|
46
|
+
params.set("cursor", p.cursor);
|
|
47
|
+
}
|
|
48
|
+
return jsonResult(await miroGet(`/v2/boards?${params.toString()}`));
|
|
49
|
+
},
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
reg(
|
|
53
|
+
"miro_get",
|
|
54
|
+
"Fetch one Miro board by its id (GET /v2/boards/{boardId}). Returns the board object directly (NOT wrapped in `data`). Throws when no board with that id exists.",
|
|
55
|
+
z.object({
|
|
56
|
+
id: z.string().min(1),
|
|
57
|
+
}),
|
|
58
|
+
async (p) => {
|
|
59
|
+
return jsonResult(await miroGet(`/v2/boards/${encodeURIComponent(p.id)}`));
|
|
60
|
+
},
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
reg(
|
|
64
|
+
"miro_search",
|
|
65
|
+
"**Substring search over the FIRST PAGE only** (up to 50 most-recently-listed boards) of the authenticated user's Miro boards. This tool fetches GET /v2/boards?limit=50 once and matches the query locally (case-insensitive) against the board name, description, and owner name. **Boards beyond the first page are not searchable here — query the local Nimbus index instead for full coverage.** Returns a { matches: [...] } envelope.",
|
|
66
|
+
searchToolInputSchema(50),
|
|
67
|
+
async (p) => {
|
|
68
|
+
const root = await miroGet("/v2/boards?limit=50");
|
|
69
|
+
const matches = filterMiroBoards(boardsFrom(root), {
|
|
70
|
+
query: p.query,
|
|
71
|
+
limit: p.limit,
|
|
72
|
+
});
|
|
73
|
+
return jsonResult({ matches });
|
|
74
|
+
},
|
|
75
|
+
);
|
|
76
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "com.nimbus.mlflow",
|
|
3
|
+
"displayName": "MLflow",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "MLflow connector. Surfaces MLflow registered models — name, description, latest version/stage/status/run, created + updated timestamps, and tags — into the local index as `mlflow:ml_model` items via the Model Registry API (`GET /api/2.0/mlflow/registered-models/search`, token-paginated on `next_page_token`). API-token auth (sent as `Authorization: Bearer <token>`). MLflow has no universal SaaS host: the tracking-server URL is supplied via the `mlflow.host` vault key and added to the sandbox network list at spawn time by `phase3AddMlflowMcp`, so the static manifest network list is intentionally empty. v1 indexes registered models only — experiments, runs, metrics, params, and artifacts are a deferred follow-up. Two HITL-gated write tools transition a model version's stage (`POST /api/2.0/mlflow/model-versions/transition-stage`): `mlflow_model_promote` (promote to Production, HITL `mlflow.model.promote`; `archiveExisting` defaults true) and `mlflow_model_transition_stage` (transition to any stage, HITL `mlflow.model.transition_stage`; `archiveExisting` defaults false). Stage changes are reversible.",
|
|
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.2.0"
|
|
16
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {
|
|
2
|
+
asRecord,
|
|
3
|
+
makeQueryFilter,
|
|
4
|
+
type SearchMatchOptions,
|
|
5
|
+
} from "../../../shared/search-filter.ts";
|
|
6
|
+
|
|
7
|
+
export type MlflowSearchMatchOptions = 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 tagsHaystack(row: Record<string, unknown>): string {
|
|
15
|
+
const tags = row["tags"];
|
|
16
|
+
if (!Array.isArray(tags)) {
|
|
17
|
+
return "";
|
|
18
|
+
}
|
|
19
|
+
const parts: string[] = [];
|
|
20
|
+
for (const t of tags) {
|
|
21
|
+
const tag = asRecord(t);
|
|
22
|
+
if (tag === undefined) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
parts.push(`${stringAt(tag, "key")}=${stringAt(tag, "value")}`);
|
|
26
|
+
}
|
|
27
|
+
return parts.join(" ");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function fieldsOf(item: unknown): readonly string[] | null {
|
|
31
|
+
const row = asRecord(item);
|
|
32
|
+
if (row === undefined) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
return [stringAt(row, "name"), stringAt(row, "description"), tagsHaystack(row)];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const filterMlflowModels = makeQueryFilter(fieldsOf);
|
|
@@ -0,0 +1,167 @@
|
|
|
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 { filterMlflowModels } 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["MLFLOW_HOST"]?.trim();
|
|
17
|
+
if (v === undefined || v === "") {
|
|
18
|
+
throw new Error("MLFLOW_HOST is not set");
|
|
19
|
+
}
|
|
20
|
+
return trimTrailingSlash(v);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function authHeader(): Record<string, string> {
|
|
24
|
+
const k = process.env["MLFLOW_TOKEN"]?.trim();
|
|
25
|
+
if (k === undefined || k === "") {
|
|
26
|
+
throw new Error("MLFLOW_TOKEN is not set");
|
|
27
|
+
}
|
|
28
|
+
return { Authorization: `Bearer ${k}`, Accept: "application/json" };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function mlflowGet(path: string): Promise<unknown> {
|
|
32
|
+
const res = await fetchWithTimeout(`${apiBase()}${path}`, { headers: authHeader() });
|
|
33
|
+
const text = await res.text();
|
|
34
|
+
if (!res.ok) {
|
|
35
|
+
throw new Error(`MLflow ${String(res.status)}: ${text.slice(0, 400)}`);
|
|
36
|
+
}
|
|
37
|
+
return JSON.parse(text) as unknown;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function mlflowPost(path: string, body: unknown): Promise<unknown> {
|
|
41
|
+
const res = await fetchWithTimeout(`${apiBase()}${path}`, {
|
|
42
|
+
method: "POST",
|
|
43
|
+
headers: { ...authHeader(), "Content-Type": "application/json" },
|
|
44
|
+
body: JSON.stringify(body),
|
|
45
|
+
});
|
|
46
|
+
const text = await res.text();
|
|
47
|
+
if (!res.ok) {
|
|
48
|
+
throw new Error(`MLflow ${path} ${String(res.status)}: ${text.slice(0, 400)}`);
|
|
49
|
+
}
|
|
50
|
+
return text === "" ? {} : (JSON.parse(text) as unknown);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const TRANSITION_PATH = "/api/2.0/mlflow/model-versions/transition-stage";
|
|
54
|
+
|
|
55
|
+
function modelsFrom(root: unknown): unknown[] {
|
|
56
|
+
const models = (root as { registered_models?: unknown } | null)?.registered_models;
|
|
57
|
+
return Array.isArray(models) ? models : [];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function registerMlflowTools(reg: ZodToolRegistrar, server: unknown): void {
|
|
61
|
+
// Despite the read-only helper's name, this connector exposes write tools. The consent
|
|
62
|
+
// kit needs the real server, which the helper now passes through as its second argument.
|
|
63
|
+
const registerWriteTool = createWriteToolRegistrar(server as ConsentServer, {
|
|
64
|
+
connector: "mlflow",
|
|
65
|
+
scopeEnv: "NIMBUS_MCP_MLFLOW_WRITE_SCOPE",
|
|
66
|
+
scopeKinds: ["model"],
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
reg(
|
|
70
|
+
"mlflow_list",
|
|
71
|
+
"List MLflow registered models (`GET /api/2.0/mlflow/registered-models/search`). Returns a single page; `limit` (1..100, default 100) caps the page size. Returns the raw `{ registered_models, next_page_token }` envelope.",
|
|
72
|
+
z.object({
|
|
73
|
+
limit: z.number().int().min(1).max(100).optional(),
|
|
74
|
+
}),
|
|
75
|
+
async (p) => {
|
|
76
|
+
const limit = p.limit ?? 100;
|
|
77
|
+
const params = new URLSearchParams({ max_results: String(limit) });
|
|
78
|
+
return jsonResult(
|
|
79
|
+
await mlflowGet(`/api/2.0/mlflow/registered-models/search?${params.toString()}`),
|
|
80
|
+
);
|
|
81
|
+
},
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
reg(
|
|
85
|
+
"mlflow_get",
|
|
86
|
+
"Fetch one MLflow registered model by `name` (`GET /api/2.0/mlflow/registered-models/get?name={name}`). Throws when no registered model with that name exists.",
|
|
87
|
+
z.object({
|
|
88
|
+
name: z.string().min(1),
|
|
89
|
+
}),
|
|
90
|
+
async (p) => {
|
|
91
|
+
const params = new URLSearchParams({ name: p.name });
|
|
92
|
+
return jsonResult(
|
|
93
|
+
await mlflowGet(`/api/2.0/mlflow/registered-models/get?${params.toString()}`),
|
|
94
|
+
);
|
|
95
|
+
},
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
reg(
|
|
99
|
+
"mlflow_search",
|
|
100
|
+
"Substring search across MLflow registered models. Matches the query (case-insensitive) against the model's `name`, `description`, and flattened `key=value` tags. Returns a `{ matches: [...] }` envelope.",
|
|
101
|
+
searchToolInputSchema(100),
|
|
102
|
+
async (p) => {
|
|
103
|
+
const params = new URLSearchParams({ max_results: "100" });
|
|
104
|
+
const root = await mlflowGet(`/api/2.0/mlflow/registered-models/search?${params.toString()}`);
|
|
105
|
+
const matches = filterMlflowModels(modelsFrom(root), {
|
|
106
|
+
query: p.query,
|
|
107
|
+
limit: p.limit,
|
|
108
|
+
});
|
|
109
|
+
return jsonResult({ matches });
|
|
110
|
+
},
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
registerWriteTool(
|
|
114
|
+
"mlflow_model_promote",
|
|
115
|
+
{
|
|
116
|
+
mutates: "mlflow.model.promote",
|
|
117
|
+
recoverable: true,
|
|
118
|
+
scopeTargetOf: (p) => ({ kind: "model", value: p.name }),
|
|
119
|
+
},
|
|
120
|
+
"Promote a model version to Production (`POST /api/2.0/mlflow/model-versions/transition-stage`, stage=Production; requires HITL mlflow.model.promote). `archiveExisting` (default true) archives other Production versions so this becomes the single active one; pass false to keep them. Archiving is a reversible stage change.",
|
|
121
|
+
z.object({
|
|
122
|
+
name: z.string().min(1),
|
|
123
|
+
version: z.string().min(1),
|
|
124
|
+
archiveExisting: z.boolean().optional(),
|
|
125
|
+
}),
|
|
126
|
+
async (p) => {
|
|
127
|
+
await mlflowPost(TRANSITION_PATH, {
|
|
128
|
+
name: p.name,
|
|
129
|
+
version: p.version,
|
|
130
|
+
stage: "Production",
|
|
131
|
+
archive_existing_versions: p.archiveExisting ?? true, // promote defaults to archiving the incumbent
|
|
132
|
+
});
|
|
133
|
+
return jsonResult({ status: "ok", name: p.name, version: p.version, stage: "Production" });
|
|
134
|
+
},
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
registerWriteTool(
|
|
138
|
+
"mlflow_model_transition_stage",
|
|
139
|
+
{
|
|
140
|
+
mutates: "mlflow.model.transition_stage",
|
|
141
|
+
recoverable: true,
|
|
142
|
+
scopeTargetOf: (p) => ({ kind: "model", value: p.name }),
|
|
143
|
+
},
|
|
144
|
+
"Transition a model version to a chosen stage (`POST /api/2.0/mlflow/model-versions/transition-stage`; requires HITL mlflow.model.transition_stage). `archiveExisting` default false.",
|
|
145
|
+
z.object({
|
|
146
|
+
name: z.string().min(1),
|
|
147
|
+
version: z.string().min(1),
|
|
148
|
+
stage: z.enum(["None", "Staging", "Production", "Archived"]),
|
|
149
|
+
archiveExisting: z.boolean().optional(),
|
|
150
|
+
}),
|
|
151
|
+
async (p) => {
|
|
152
|
+
await mlflowPost(TRANSITION_PATH, {
|
|
153
|
+
name: p.name,
|
|
154
|
+
version: p.version,
|
|
155
|
+
stage: p.stage,
|
|
156
|
+
archive_existing_versions: p.archiveExisting ?? false,
|
|
157
|
+
});
|
|
158
|
+
return jsonResult({ status: "ok", name: p.name, version: p.version, stage: p.stage });
|
|
159
|
+
},
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export async function startConnector(): Promise<void> {
|
|
164
|
+
await runReadOnlyMcpConnector("nimbus-mlflow", registerMlflowTools);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (import.meta.main) await startConnector();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "com.nimbus.monte-carlo",
|
|
3
|
+
"displayName": "Monte Carlo",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Monte Carlo connector. Indexes Monte Carlo data-quality incidents as `montecarlo:data_quality_test` items in the local index and exposes three read-only tools (`montecarlo_list`, `montecarlo_get`, `montecarlo_search`) plus two HITL-gated write tools (`montecarlo_incident_acknowledge`, `montecarlo_incident_resolve`) to the Nimbus agent. Uses the Monte Carlo GraphQL API at `api.getmontecarlo.com` with API key pair auth (x-mcd-id / x-mcd-token headers). The gateway injects `MONTECARLO_API_ID` and `MONTECARLO_API_TOKEN` at spawn time.",
|
|
6
|
+
"author": "Nimbus",
|
|
7
|
+
"entrypoint": "dist/server.js",
|
|
8
|
+
"runtime": "bun",
|
|
9
|
+
"permissions": {
|
|
10
|
+
"network": ["api.getmontecarlo.com"],
|
|
11
|
+
"filesystem": { "read": [], "write": [] }
|
|
12
|
+
},
|
|
13
|
+
"hitlRequired": ["write"],
|
|
14
|
+
"syncInterval": 600,
|
|
15
|
+
"minNimbusVersion": "0.5.0"
|
|
16
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
fieldsFromKeys,
|
|
3
|
+
makeQueryFilter,
|
|
4
|
+
type SearchMatchOptions,
|
|
5
|
+
} from "../../../shared/search-filter.ts";
|
|
6
|
+
|
|
7
|
+
export type MonteCarloSearchMatchOptions = SearchMatchOptions;
|
|
8
|
+
|
|
9
|
+
export const filterMonteCarloIncidents = makeQueryFilter(
|
|
10
|
+
fieldsFromKeys(["incidentId", "status", "severity", "monitoredTable"]),
|
|
11
|
+
);
|