@nimbus-dev/connectors 0.2.0 → 0.2.2
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/README.md +18 -4
- package/connectors/airflow/src/search-filter.ts +2 -15
- package/connectors/airflow/src/server.ts +2 -87
- package/connectors/airflow/src/tools.ts +74 -0
- package/connectors/apple/src/adapters.ts +326 -0
- package/connectors/apple/src/apple-mail-core.ts +4 -8
- package/connectors/apple/src/calendar-tools.ts +28 -8
- package/connectors/apple/src/server.ts +23 -512
- package/connectors/apple/src/tools.ts +18 -44
- package/connectors/argocd/src/server.ts +25 -38
- package/connectors/athena/src/tools.ts +15 -34
- package/connectors/aws/src/server.ts +2 -183
- package/connectors/aws/src/tools.ts +197 -0
- package/connectors/azure/src/server.ts +2 -131
- package/connectors/azure/src/tools.ts +133 -0
- package/connectors/bigeye/src/search-filter.ts +4 -15
- package/connectors/bigeye/src/server.ts +38 -38
- package/connectors/bigquery/src/tools.ts +1 -26
- package/connectors/bitbucket/src/server.ts +2 -244
- package/connectors/bitbucket/src/tools.ts +241 -0
- package/connectors/bitrise/src/server.ts +2 -79
- package/connectors/bitrise/src/tools.ts +76 -0
- package/connectors/canva/src/search-filter.ts +2 -11
- package/connectors/canva/src/server.ts +2 -73
- package/connectors/canva/src/tools.ts +66 -0
- package/connectors/circleci/src/server.ts +2 -176
- package/connectors/circleci/src/tools.ts +193 -0
- package/connectors/cloud-logging/src/tools.ts +14 -27
- package/connectors/cloudwatch/src/tools.ts +15 -34
- package/connectors/codemagic/src/server.ts +2 -70
- package/connectors/codemagic/src/tools.ts +71 -0
- package/connectors/confluence/src/server.ts +2 -301
- package/connectors/confluence/src/tools.ts +320 -0
- package/connectors/dagster/src/server.ts +2 -214
- package/connectors/dagster/src/tools.ts +187 -0
- package/connectors/databricks/src/search-filter.ts +7 -7
- package/connectors/databricks/src/server.ts +2 -77
- package/connectors/databricks/src/tools.ts +66 -0
- package/connectors/datadog/src/server.ts +2 -48
- package/connectors/datadog/src/tools.ts +48 -0
- package/connectors/dataprofile/src/profile.ts +16 -46
- package/connectors/dbt/src/server.ts +2 -92
- package/connectors/dbt/src/tools.ts +80 -0
- package/connectors/dependencytrack/src/server.ts +2 -76
- package/connectors/dependencytrack/src/tools.ts +65 -0
- package/connectors/discord/src/server.ts +2 -85
- package/connectors/discord/src/tools.ts +82 -0
- package/connectors/elasticsearch/src/tools.ts +7 -37
- package/connectors/fastmail/src/jmap-client.ts +202 -0
- package/connectors/fastmail/src/jmap-core.ts +8 -21
- package/connectors/fastmail/src/server.ts +2 -187
- package/connectors/fastmail/src/tools.ts +4 -23
- package/connectors/figma/src/search-filter.ts +2 -11
- package/connectors/figma/src/server.ts +2 -113
- package/connectors/figma/src/tools.ts +99 -0
- package/connectors/firebase/src/server.ts +2 -90
- package/connectors/firebase/src/token.ts +2 -5
- package/connectors/firebase/src/tools.ts +106 -0
- package/connectors/flagsmith/src/server.ts +2 -98
- package/connectors/flagsmith/src/tools.ts +95 -0
- package/connectors/flux/src/server.ts +52 -53
- package/connectors/gcp/src/server.ts +2 -132
- package/connectors/gcp/src/tools.ts +123 -0
- package/connectors/github/src/server.ts +2 -328
- package/connectors/github/src/tools.ts +311 -0
- package/connectors/github-actions/src/server.ts +2 -205
- package/connectors/github-actions/src/tools.ts +209 -0
- package/connectors/gitlab/src/server.ts +2 -384
- package/connectors/gitlab/src/tools.ts +392 -0
- package/connectors/gmail/src/server.ts +2 -270
- package/connectors/gmail/src/tools.ts +209 -0
- package/connectors/google-drive/src/server.ts +2 -524
- package/connectors/google-drive/src/tools.ts +529 -0
- package/connectors/google-meet/src/server.ts +2 -82
- package/connectors/google-meet/src/tools.ts +87 -0
- package/connectors/google-photos/src/server.ts +2 -132
- package/connectors/google-photos/src/tools.ts +138 -0
- package/connectors/grafana/src/server.ts +2 -53
- package/connectors/grafana/src/tools.ts +53 -0
- package/connectors/great-expectations/src/gx-parse.ts +14 -45
- package/connectors/greenhouse/src/server.ts +2 -63
- package/connectors/greenhouse/src/tools.ts +68 -0
- package/connectors/hubspot/src/server.ts +2 -77
- package/connectors/hubspot/src/tools.ts +70 -0
- package/connectors/iac/src/server.ts +2 -137
- package/connectors/iac/src/tools.ts +152 -0
- package/connectors/imap/src/imap-core.ts +29 -82
- package/connectors/imap/src/server.ts +33 -220
- package/connectors/intercom/src/server.ts +2 -61
- package/connectors/intercom/src/tools.ts +58 -0
- package/connectors/jenkins/src/jenkins-api.ts +2 -6
- package/connectors/jenkins/src/server.ts +2 -215
- package/connectors/jenkins/src/tools.ts +240 -0
- package/connectors/jira/src/server.ts +2 -301
- package/connectors/jira/src/tools.ts +325 -0
- package/connectors/kubernetes/src/server.ts +2 -155
- package/connectors/kubernetes/src/tools.ts +159 -0
- package/connectors/launchdarkly/src/server.ts +2 -75
- package/connectors/launchdarkly/src/tools.ts +72 -0
- package/connectors/lever/src/search-filter.ts +4 -12
- package/connectors/lever/src/server.ts +2 -60
- package/connectors/lever/src/tools.ts +61 -0
- package/connectors/linear/src/server.ts +2 -459
- package/connectors/linear/src/tools.ts +482 -0
- package/connectors/localdb/src/sql-scan.ts +13 -41
- package/connectors/localdb/src/tools.ts +1 -1
- package/connectors/looker/src/search-filter.ts +2 -15
- package/connectors/looker/src/server.ts +35 -70
- package/connectors/mendeley/src/server.ts +2 -55
- package/connectors/mendeley/src/tools.ts +54 -0
- package/connectors/mercury/src/server.ts +2 -57
- package/connectors/mercury/src/tools.ts +50 -0
- package/connectors/metabase/src/search-filter.ts +4 -15
- package/connectors/metabase/src/server.ts +2 -79
- package/connectors/metabase/src/tools.ts +64 -0
- package/connectors/miro/src/server.ts +2 -72
- package/connectors/miro/src/tools.ts +65 -0
- package/connectors/mlflow/src/search-filter.ts +3 -7
- package/connectors/mlflow/src/server.ts +20 -40
- package/connectors/monte-carlo/src/server.ts +50 -57
- package/connectors/netlify/src/search-filter.ts +6 -14
- package/connectors/netlify/src/server.ts +2 -60
- package/connectors/netlify/src/tools.ts +53 -0
- package/connectors/newrelic/src/server.ts +2 -40
- package/connectors/newrelic/src/tools.ts +37 -0
- package/connectors/notion/src/server.ts +2 -335
- package/connectors/notion/src/tools.ts +344 -0
- package/connectors/obsidian/src/server.ts +2 -425
- package/connectors/obsidian/src/tools.ts +466 -0
- package/connectors/onedrive/src/server.ts +2 -249
- package/connectors/onedrive/src/tools.ts +253 -0
- package/connectors/outlook/src/server.ts +2 -351
- package/connectors/outlook/src/tools.ts +357 -0
- package/connectors/pagerduty/src/server.ts +2 -132
- package/connectors/pagerduty/src/tools.ts +126 -0
- package/connectors/pipedrive/src/server.ts +2 -58
- package/connectors/pipedrive/src/tools.ts +63 -0
- package/connectors/powerbi/src/search-filter.ts +2 -15
- package/connectors/powerbi/src/server.ts +43 -68
- package/connectors/prefect/src/search-filter.ts +3 -16
- package/connectors/prefect/src/server.ts +2 -90
- package/connectors/prefect/src/tools.ts +83 -0
- package/connectors/protonmail/src/mail-core.ts +16 -75
- package/connectors/protonmail/src/server.ts +34 -214
- package/connectors/raindrop/src/server.ts +2 -103
- package/connectors/raindrop/src/tools.ts +103 -0
- package/connectors/ramp/src/server.ts +2 -105
- package/connectors/ramp/src/tools.ts +98 -0
- package/connectors/readwise/src/server.ts +2 -88
- package/connectors/readwise/src/tools.ts +81 -0
- package/connectors/sagemaker/src/tools.ts +19 -38
- package/connectors/salesforce/src/search-filter.ts +4 -11
- package/connectors/salesforce/src/server.ts +2 -90
- package/connectors/salesforce/src/tools.ts +75 -0
- package/connectors/semgrep/src/search-filter.ts +3 -10
- package/connectors/semgrep/src/server.ts +2 -104
- package/connectors/semgrep/src/tools.ts +92 -0
- package/connectors/sentry/src/server.ts +2 -58
- package/connectors/sentry/src/tools.ts +53 -0
- package/connectors/slack/src/server.ts +2 -352
- package/connectors/slack/src/tools.ts +376 -0
- package/connectors/snowflake/src/search-filter.ts +4 -19
- package/connectors/snowflake/src/server.ts +6 -25
- package/connectors/snyk/src/server.ts +2 -140
- package/connectors/snyk/src/tools.ts +136 -0
- package/connectors/sonarqube/src/server.ts +2 -111
- package/connectors/sonarqube/src/tools.ts +98 -0
- package/connectors/stackoverflow/src/server.ts +2 -77
- package/connectors/stackoverflow/src/tools.ts +66 -0
- package/connectors/storybook/src/storybook-parse.ts +3 -6
- package/connectors/stripe/src/server.ts +2 -61
- package/connectors/stripe/src/tools.ts +54 -0
- package/connectors/superset/src/search-filter.ts +4 -15
- package/connectors/superset/src/server.ts +2 -115
- package/connectors/superset/src/tools.ts +98 -0
- package/connectors/tableau/src/search-filter.ts +2 -15
- package/connectors/tableau/src/server.ts +13 -65
- package/connectors/teams/src/server.ts +2 -249
- package/connectors/teams/src/tools.ts +283 -0
- package/connectors/testflight/src/server.ts +2 -70
- package/connectors/testflight/src/tools.ts +79 -0
- package/connectors/vercel/src/search-filter.ts +2 -10
- package/connectors/vercel/src/server.ts +2 -75
- package/connectors/vercel/src/tools.ts +73 -0
- package/connectors/vertex-ai/src/tools.ts +23 -33
- package/connectors/wiz/src/search-filter.ts +6 -30
- package/connectors/wiz/src/server.ts +2 -188
- package/connectors/wiz/src/tools.ts +168 -0
- package/connectors/zendesk/src/server.ts +2 -73
- package/connectors/zendesk/src/tools.ts +60 -0
- package/connectors/zoom/src/search-filter.ts +1 -7
- package/connectors/zoom/src/server.ts +2 -94
- package/connectors/zoom/src/tools.ts +93 -0
- package/connectors/zotero/src/server.ts +2 -78
- package/connectors/zotero/src/tools.ts +67 -0
- package/package.json +15 -12
- package/shared/access-token-cache.ts +123 -0
- package/shared/atlassian-json-fetch.ts +6 -16
- package/shared/audit-chain.ts +13 -6
- package/shared/cli-json-kit.ts +150 -0
- package/shared/collection-tool-kit.ts +143 -0
- package/shared/connector-mode.ts +3 -2
- package/shared/cursor-list-tool.ts +29 -0
- package/shared/env-json-api.ts +209 -0
- package/shared/fetch-json-text.ts +27 -0
- package/shared/github-rest.ts +62 -0
- package/shared/graphql-json.ts +60 -0
- package/shared/imap-mail-core.ts +91 -0
- package/shared/imap-tool-kit.ts +57 -23
- package/shared/imapflow-adapter.ts +417 -0
- package/shared/rest-tool-kit.ts +77 -7
- package/shared/run-cli-json.ts +38 -16
- package/shared/search-filter.ts +58 -18
- package/shared/walk-files.ts +118 -0
- package/standalone/src/launcher.ts +19 -7
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { ConsentServer } from "../../../shared/consent-kit.ts";
|
|
3
|
+
import { createWriteToolRegistrar } from "../../../shared/consent-kit.ts";
|
|
4
|
+
import {
|
|
5
|
+
createRegisterSimpleTool,
|
|
6
|
+
createZodToolRegistrar,
|
|
7
|
+
mcpJsonResult as jsonResult,
|
|
8
|
+
} from "../../../shared/mcp-tool-kit.ts";
|
|
9
|
+
import { runCliJsonThrowing, runCliOkThrowing } from "../../../shared/run-cli-json.ts";
|
|
10
|
+
|
|
11
|
+
function azEnv(): Record<string, string | undefined> {
|
|
12
|
+
return { ...process.env } as Record<string, string | undefined>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function azJson(args: string[]): Promise<unknown> {
|
|
16
|
+
const cmd = ["az", ...args, "-o", "json"];
|
|
17
|
+
return (await runCliJsonThrowing(cmd, azEnv())) ?? {};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Tool names exposed by this connector — for contract/introspection tests. */
|
|
21
|
+
export const AZURE_TOOL_NAMES = [
|
|
22
|
+
"azure_app_service_list",
|
|
23
|
+
"azure_app_service_restart",
|
|
24
|
+
"azure_aks_node_pool_scale",
|
|
25
|
+
] as const;
|
|
26
|
+
|
|
27
|
+
export function registerAzureTools(
|
|
28
|
+
server: ConsentServer & { tool: (...args: never) => unknown },
|
|
29
|
+
): void {
|
|
30
|
+
const reg = createZodToolRegistrar(createRegisterSimpleTool(server));
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Every MUTATING azure tool goes through here. Outside the gateway this adds the
|
|
34
|
+
* consent gate, the write-scope allow-list, the mutation budget and the audit record; inside
|
|
35
|
+
* the gateway it is a pass-through, because executor.ts (I2) is the gate there.
|
|
36
|
+
*/
|
|
37
|
+
const registerWriteTool = createWriteToolRegistrar(server, {
|
|
38
|
+
connector: "azure",
|
|
39
|
+
scopeEnv: "NIMBUS_MCP_AZURE_WRITE_SCOPE",
|
|
40
|
+
scopeKinds: ["resource_group"],
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
reg(
|
|
44
|
+
"azure_app_service_list",
|
|
45
|
+
"List App Services in a resource group.",
|
|
46
|
+
z.object({
|
|
47
|
+
subscriptionId: z.string().min(1),
|
|
48
|
+
resourceGroup: z.string().min(1),
|
|
49
|
+
}),
|
|
50
|
+
async (p) =>
|
|
51
|
+
jsonResult(
|
|
52
|
+
await azJson([
|
|
53
|
+
"webapp",
|
|
54
|
+
"list",
|
|
55
|
+
"--subscription",
|
|
56
|
+
p.subscriptionId,
|
|
57
|
+
"--resource-group",
|
|
58
|
+
p.resourceGroup,
|
|
59
|
+
]),
|
|
60
|
+
),
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
registerWriteTool(
|
|
64
|
+
"azure_app_service_restart",
|
|
65
|
+
{
|
|
66
|
+
mutates: "azure.app_service.restart",
|
|
67
|
+
recoverable: true,
|
|
68
|
+
scopeTargetOf: (p) => ({ kind: "resource_group", value: p.resourceGroup }),
|
|
69
|
+
},
|
|
70
|
+
"Restart an App Service.",
|
|
71
|
+
z.object({
|
|
72
|
+
subscriptionId: z.string().min(1),
|
|
73
|
+
resourceGroup: z.string().min(1),
|
|
74
|
+
name: z.string().min(1),
|
|
75
|
+
}),
|
|
76
|
+
async (p) => {
|
|
77
|
+
await runCliOkThrowing(
|
|
78
|
+
[
|
|
79
|
+
"az",
|
|
80
|
+
"webapp",
|
|
81
|
+
"restart",
|
|
82
|
+
"--subscription",
|
|
83
|
+
p.subscriptionId,
|
|
84
|
+
"--resource-group",
|
|
85
|
+
p.resourceGroup,
|
|
86
|
+
"--name",
|
|
87
|
+
p.name,
|
|
88
|
+
],
|
|
89
|
+
azEnv(),
|
|
90
|
+
);
|
|
91
|
+
return jsonResult({ ok: true });
|
|
92
|
+
},
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
registerWriteTool(
|
|
96
|
+
"azure_aks_node_pool_scale",
|
|
97
|
+
{
|
|
98
|
+
mutates: "azure.aks.node_pool.scale",
|
|
99
|
+
recoverable: true,
|
|
100
|
+
scopeTargetOf: (p) => ({ kind: "resource_group", value: p.resourceGroup }),
|
|
101
|
+
},
|
|
102
|
+
"Scale an AKS node pool.",
|
|
103
|
+
z.object({
|
|
104
|
+
subscriptionId: z.string().min(1),
|
|
105
|
+
resourceGroup: z.string().min(1),
|
|
106
|
+
clusterName: z.string().min(1),
|
|
107
|
+
poolName: z.string().min(1),
|
|
108
|
+
nodeCount: z.number().int().min(0),
|
|
109
|
+
}),
|
|
110
|
+
async (p) => {
|
|
111
|
+
await runCliOkThrowing(
|
|
112
|
+
[
|
|
113
|
+
"az",
|
|
114
|
+
"aks",
|
|
115
|
+
"nodepool",
|
|
116
|
+
"scale",
|
|
117
|
+
"--subscription",
|
|
118
|
+
p.subscriptionId,
|
|
119
|
+
"--resource-group",
|
|
120
|
+
p.resourceGroup,
|
|
121
|
+
"--cluster-name",
|
|
122
|
+
p.clusterName,
|
|
123
|
+
"--name",
|
|
124
|
+
p.poolName,
|
|
125
|
+
"--node-count",
|
|
126
|
+
String(p.nodeCount),
|
|
127
|
+
],
|
|
128
|
+
azEnv(),
|
|
129
|
+
);
|
|
130
|
+
return jsonResult({ ok: true });
|
|
131
|
+
},
|
|
132
|
+
);
|
|
133
|
+
}
|
|
@@ -1,22 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
asRecord,
|
|
3
2
|
makeQueryFilter,
|
|
3
|
+
recordFieldsFromKeys,
|
|
4
4
|
type SearchMatchOptions,
|
|
5
5
|
} from "../../../shared/search-filter.ts";
|
|
6
6
|
|
|
7
7
|
export type BigeyeSearchMatchOptions = SearchMatchOptions;
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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, "summary"), stringAt(row, "title"), stringAt(row, "description")];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export const filterBigeyeIssues = makeQueryFilter(fieldsOf);
|
|
9
|
+
export const filterBigeyeIssues = makeQueryFilter(
|
|
10
|
+
recordFieldsFromKeys(["summary", "title", "description"]),
|
|
11
|
+
);
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { type ConsentServer, createWriteToolRegistrar } from "../../../shared/consent-kit.ts";
|
|
3
|
+
import { cursorListInputSchema } from "../../../shared/cursor-list-tool.ts";
|
|
4
|
+
import { envAuthHeaders, requiredEnv } from "../../../shared/env-json-api.ts";
|
|
3
5
|
import { searchToolInputSchema } from "../../../shared/mcp-search-tool.ts";
|
|
4
6
|
import { fetchWithTimeout, mcpJsonResult as jsonResult } from "../../../shared/mcp-tool-kit.ts";
|
|
5
7
|
import {
|
|
@@ -8,21 +10,13 @@ import {
|
|
|
8
10
|
} from "../../../shared/run-read-only-mcp-connector.ts";
|
|
9
11
|
import { filterBigeyeIssues } from "./search-filter.ts";
|
|
10
12
|
|
|
13
|
+
/** Only ONE trailing slash is dropped here — not `requiredBaseUrl`'s strip-them-all. */
|
|
11
14
|
function apiBase(): string {
|
|
12
|
-
const v =
|
|
13
|
-
if (v === undefined || v === "") {
|
|
14
|
-
throw new Error("BIGEYE_BASE_URL is not set");
|
|
15
|
-
}
|
|
15
|
+
const v = requiredEnv("BIGEYE_BASE_URL");
|
|
16
16
|
return v.endsWith("/") ? v.slice(0, -1) : v;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
const k = process.env["BIGEYE_API_KEY"]?.trim();
|
|
21
|
-
if (k === undefined || k === "") {
|
|
22
|
-
throw new Error("BIGEYE_API_KEY is not set");
|
|
23
|
-
}
|
|
24
|
-
return { Authorization: `Bearer ${k}`, Accept: "application/json" };
|
|
25
|
-
}
|
|
19
|
+
const authHeader = envAuthHeaders({ env: "BIGEYE_API_KEY" });
|
|
26
20
|
|
|
27
21
|
/** One page of issues (`GET /api/v1/issues?limit&offset`), tolerant of array / `{issues}` / `{data}`. */
|
|
28
22
|
async function fetchIssues(limit: number, offset: number): Promise<unknown[]> {
|
|
@@ -80,10 +74,7 @@ export function registerBigeyeTools(reg: ZodToolRegistrar, server: unknown): voi
|
|
|
80
74
|
reg(
|
|
81
75
|
"bigeye_list",
|
|
82
76
|
"List Bigeye data-quality issues (`GET /api/v1/issues`). Paginated: `cursor` (offset) + `limit` (default 200, max 500) → `{ items, nextCursor }`.",
|
|
83
|
-
|
|
84
|
-
cursor: z.string().nullable().optional(),
|
|
85
|
-
limit: z.number().int().min(1).max(500).optional(),
|
|
86
|
-
}),
|
|
77
|
+
cursorListInputSchema(),
|
|
87
78
|
async (p) => {
|
|
88
79
|
const limit = p.limit ?? 200;
|
|
89
80
|
// null / undefined / "" / non-numeric / negative all clamp to offset 0; fractional truncates.
|
|
@@ -121,34 +112,43 @@ export function registerBigeyeTools(reg: ZodToolRegistrar, server: unknown): voi
|
|
|
121
112
|
},
|
|
122
113
|
);
|
|
123
114
|
|
|
124
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Acknowledge and resolve are ONE mutation, `updateIssueStatus`, with a different status, so
|
|
117
|
+
* they share everything except their name, action type, description and that status.
|
|
118
|
+
*/
|
|
119
|
+
function registerStatusTool(
|
|
120
|
+
name: string,
|
|
121
|
+
mutates: string,
|
|
122
|
+
description: string,
|
|
123
|
+
status: "ISSUE_STATUS_ACKNOWLEDGED" | "ISSUE_STATUS_CLOSED",
|
|
124
|
+
): void {
|
|
125
|
+
registerWriteTool(
|
|
126
|
+
name,
|
|
127
|
+
{
|
|
128
|
+
mutates,
|
|
129
|
+
recoverable: true,
|
|
130
|
+
scopeTargetOf: (p) => ({ kind: "issue", value: p.issueId }),
|
|
131
|
+
},
|
|
132
|
+
description,
|
|
133
|
+
z.object({ issueId: z.string().min(1) }),
|
|
134
|
+
async (p) => {
|
|
135
|
+
await updateIssueStatus(p.issueId, status);
|
|
136
|
+
return jsonResult({ status: "ok", issueId: p.issueId });
|
|
137
|
+
},
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
registerStatusTool(
|
|
125
142
|
"bigeye_issue_acknowledge",
|
|
126
|
-
|
|
127
|
-
mutates: "bigeye.issue.acknowledge",
|
|
128
|
-
recoverable: true,
|
|
129
|
-
scopeTargetOf: (p) => ({ kind: "issue", value: p.issueId }),
|
|
130
|
-
},
|
|
143
|
+
"bigeye.issue.acknowledge",
|
|
131
144
|
"Acknowledge a Bigeye issue.",
|
|
132
|
-
|
|
133
|
-
async (p) => {
|
|
134
|
-
await updateIssueStatus(p.issueId, "ISSUE_STATUS_ACKNOWLEDGED");
|
|
135
|
-
return jsonResult({ status: "ok", issueId: p.issueId });
|
|
136
|
-
},
|
|
145
|
+
"ISSUE_STATUS_ACKNOWLEDGED",
|
|
137
146
|
);
|
|
138
|
-
|
|
139
|
-
registerWriteTool(
|
|
147
|
+
registerStatusTool(
|
|
140
148
|
"bigeye_issue_resolve",
|
|
141
|
-
|
|
142
|
-
mutates: "bigeye.issue.resolve",
|
|
143
|
-
recoverable: true,
|
|
144
|
-
scopeTargetOf: (p) => ({ kind: "issue", value: p.issueId }),
|
|
145
|
-
},
|
|
149
|
+
"bigeye.issue.resolve",
|
|
146
150
|
"Resolve (close) a Bigeye issue.",
|
|
147
|
-
|
|
148
|
-
async (p) => {
|
|
149
|
-
await updateIssueStatus(p.issueId, "ISSUE_STATUS_CLOSED");
|
|
150
|
-
return jsonResult({ status: "ok", issueId: p.issueId });
|
|
151
|
-
},
|
|
151
|
+
"ISSUE_STATUS_CLOSED",
|
|
152
152
|
);
|
|
153
153
|
}
|
|
154
154
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { asArray, gcloudEnv, isRecord, strField } from "../../../shared/cli-json-kit.ts";
|
|
2
3
|
import { mcpJsonResult as jsonResult } from "../../../shared/mcp-tool-kit.ts";
|
|
3
4
|
import { nimbusSpawn } from "../../../shared/nimbus-spawn.ts";
|
|
4
5
|
import type { ZodToolRegistrar } from "../../../shared/run-read-only-mcp-connector.ts";
|
|
@@ -23,15 +24,6 @@ function project(explicit?: string): string {
|
|
|
23
24
|
return p;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
function gcloudEnv(): Record<string, string | undefined> {
|
|
27
|
-
const e = { ...process.env } as Record<string, string | undefined>;
|
|
28
|
-
const cf = process.env["GOOGLE_APPLICATION_CREDENTIALS"]?.trim();
|
|
29
|
-
if (cf !== undefined && cf !== "") {
|
|
30
|
-
e["GOOGLE_APPLICATION_CREDENTIALS"] = cf;
|
|
31
|
-
}
|
|
32
|
-
return e;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
27
|
/** Mint a short-lived access token via `gcloud auth print-access-token`. */
|
|
36
28
|
async function accessToken(): Promise<string> {
|
|
37
29
|
const { code, stdout, stderr } = await nimbusSpawn(
|
|
@@ -58,15 +50,6 @@ async function bqGet(token: string, path: string): Promise<unknown> {
|
|
|
58
50
|
return JSON.parse(text) as unknown;
|
|
59
51
|
}
|
|
60
52
|
|
|
61
|
-
function isRecord(v: unknown): v is Record<string, unknown> {
|
|
62
|
-
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function strField(r: Record<string, unknown>, key: string): string {
|
|
66
|
-
const v = r[key];
|
|
67
|
-
return typeof v === "string" ? v : "";
|
|
68
|
-
}
|
|
69
|
-
|
|
70
53
|
function tableMatches(entry: unknown, q: string): boolean {
|
|
71
54
|
if (!isRecord(entry)) {
|
|
72
55
|
return false;
|
|
@@ -88,14 +71,6 @@ function datasetMatches(entry: unknown, q: string): boolean {
|
|
|
88
71
|
return datasetId.toLowerCase().includes(q.toLowerCase());
|
|
89
72
|
}
|
|
90
73
|
|
|
91
|
-
function asArray(parsed: unknown, key: string): unknown[] {
|
|
92
|
-
if (!isRecord(parsed)) {
|
|
93
|
-
return [];
|
|
94
|
-
}
|
|
95
|
-
const arr = parsed[key];
|
|
96
|
-
return Array.isArray(arr) ? arr : [];
|
|
97
|
-
}
|
|
98
|
-
|
|
99
74
|
/**
|
|
100
75
|
* Register the read-only, metadata-only BigQuery tools onto the given registrar.
|
|
101
76
|
* Shared between `server.ts` (live) and the contract test (introspection).
|
|
@@ -1,252 +1,10 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
-
import {
|
|
4
|
-
import { createWriteToolRegistrar } from "../../../shared/consent-kit.ts";
|
|
5
|
-
import { joinApiPath } from "../../../shared/join-api-path.ts";
|
|
6
|
-
import {
|
|
7
|
-
createRegisterSimpleTool,
|
|
8
|
-
createZodToolRegistrar,
|
|
9
|
-
mcpJsonResultIfOk,
|
|
10
|
-
} from "../../../shared/mcp-tool-kit.ts";
|
|
11
|
-
|
|
12
|
-
const BB_API = "https://api.bitbucket.org/2.0";
|
|
13
|
-
|
|
14
|
-
function requireUsername(): string {
|
|
15
|
-
const t = process.env["BITBUCKET_USERNAME"];
|
|
16
|
-
if (t === undefined || t === "") {
|
|
17
|
-
throw new Error("BITBUCKET_USERNAME is not set");
|
|
18
|
-
}
|
|
19
|
-
return t;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function requireAppPassword(): string {
|
|
23
|
-
const t = process.env["BITBUCKET_APP_PASSWORD"];
|
|
24
|
-
if (t === undefined || t === "") {
|
|
25
|
-
throw new Error("BITBUCKET_APP_PASSWORD is not set");
|
|
26
|
-
}
|
|
27
|
-
return t;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function basicAuthHeader(): string {
|
|
31
|
-
const user = requireUsername();
|
|
32
|
-
const pass = requireAppPassword();
|
|
33
|
-
const b = Buffer.from(`${user}:${pass}`, "utf8").toString("base64");
|
|
34
|
-
return `Basic ${b}`;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function splitRepoFull(full: string): { workspace: string; repoSlug: string } {
|
|
38
|
-
const i = full.indexOf("/");
|
|
39
|
-
if (i <= 0 || i === full.length - 1) {
|
|
40
|
-
throw new Error("repoFull must be workspace/repo_slug");
|
|
41
|
-
}
|
|
42
|
-
return { workspace: full.slice(0, i), repoSlug: full.slice(i + 1) };
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async function bbFetch(
|
|
46
|
-
path: string,
|
|
47
|
-
init?: RequestInit,
|
|
48
|
-
): Promise<{ ok: boolean; status: number; json: unknown; text: string }> {
|
|
49
|
-
const url = joinApiPath(BB_API, path);
|
|
50
|
-
const baseHeaders: Record<string, string> = {
|
|
51
|
-
Authorization: basicAuthHeader(),
|
|
52
|
-
Accept: "application/json",
|
|
53
|
-
};
|
|
54
|
-
const extra = init?.headers as Record<string, string> | undefined;
|
|
55
|
-
const headers = extra === undefined ? baseHeaders : { ...baseHeaders, ...extra };
|
|
56
|
-
const res = await fetch(url, {
|
|
57
|
-
...init,
|
|
58
|
-
headers,
|
|
59
|
-
});
|
|
60
|
-
const text = await res.text();
|
|
61
|
-
let json: unknown;
|
|
62
|
-
try {
|
|
63
|
-
json = JSON.parse(text) as unknown;
|
|
64
|
-
} catch {
|
|
65
|
-
json = null;
|
|
66
|
-
}
|
|
67
|
-
return { ok: res.ok, status: res.status, json, text };
|
|
68
|
-
}
|
|
3
|
+
import { registerBitbucketTools } from "./tools.ts";
|
|
69
4
|
|
|
70
5
|
const server = new McpServer({ name: "nimbus-bitbucket", version: "0.1.0" });
|
|
71
6
|
|
|
72
|
-
|
|
73
|
-
const reg = createZodToolRegistrar(registerSimpleTool);
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Every MUTATING bitbucket tool goes through here. Outside the gateway this adds the
|
|
77
|
-
* consent gate, the write-scope allow-list, the mutation budget and the audit record; inside
|
|
78
|
-
* the gateway it is a pass-through, because executor.ts (I2) is the gate there.
|
|
79
|
-
*/
|
|
80
|
-
const registerWriteTool = createWriteToolRegistrar(server, {
|
|
81
|
-
connector: "bitbucket",
|
|
82
|
-
scopeEnv: "NIMBUS_MCP_BITBUCKET_WRITE_SCOPE",
|
|
83
|
-
scopeKinds: ["repo"],
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
const repoFullArg = z.object({
|
|
87
|
-
repoFull: z
|
|
88
|
-
.string()
|
|
89
|
-
.min(3)
|
|
90
|
-
.describe("Repository full name: workspace/repo_slug (e.g. myteam/my-service)"),
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
const bitbucketRepoListSchema = z.object({
|
|
94
|
-
pagelen: z.number().int().min(1).max(100).optional(),
|
|
95
|
-
page: z.string().max(2000).optional().describe("Opaque page URL or token from a prior next link"),
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
reg(
|
|
99
|
-
"bitbucket_repo_list",
|
|
100
|
-
"List repositories where the authenticated user is a member.",
|
|
101
|
-
bitbucketRepoListSchema,
|
|
102
|
-
async (parsed) => {
|
|
103
|
-
if (parsed.page?.startsWith("http")) {
|
|
104
|
-
const res = await bbFetch(parsed.page);
|
|
105
|
-
return mcpJsonResultIfOk("Bitbucket", res);
|
|
106
|
-
}
|
|
107
|
-
const qs = new URLSearchParams();
|
|
108
|
-
qs.set("role", "member");
|
|
109
|
-
qs.set("pagelen", String(parsed.pagelen ?? 30));
|
|
110
|
-
const res = await bbFetch(`/repositories?${qs.toString()}`);
|
|
111
|
-
return mcpJsonResultIfOk("Bitbucket", res);
|
|
112
|
-
},
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
const bitbucketPrListSchema = repoFullArg.extend({
|
|
116
|
-
state: z.enum(["OPEN", "MERGED", "DECLINED", "SUPERSEDED"]).optional(),
|
|
117
|
-
pagelen: z.number().int().min(1).max(100).optional(),
|
|
118
|
-
page: z.string().max(2000).optional().describe("Opaque next URL from a prior response"),
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
reg(
|
|
122
|
-
"bitbucket_pr_list",
|
|
123
|
-
"List pull requests for a repository.",
|
|
124
|
-
bitbucketPrListSchema,
|
|
125
|
-
async (parsed) => {
|
|
126
|
-
if (parsed.page?.startsWith("http")) {
|
|
127
|
-
const res = await bbFetch(parsed.page);
|
|
128
|
-
return mcpJsonResultIfOk("Bitbucket", res);
|
|
129
|
-
}
|
|
130
|
-
const { workspace, repoSlug } = splitRepoFull(parsed.repoFull);
|
|
131
|
-
const base = `/repositories/${encodeURIComponent(workspace)}/${encodeURIComponent(repoSlug)}/pullrequests`;
|
|
132
|
-
const qs = new URLSearchParams();
|
|
133
|
-
qs.set("pagelen", String(parsed.pagelen ?? 30));
|
|
134
|
-
qs.set("sort", "-updated_on");
|
|
135
|
-
if (parsed.state !== undefined) {
|
|
136
|
-
qs.set("q", `state="${parsed.state}"`);
|
|
137
|
-
}
|
|
138
|
-
const res = await bbFetch(`${base}?${qs.toString()}`);
|
|
139
|
-
return mcpJsonResultIfOk("Bitbucket", res);
|
|
140
|
-
},
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
const bitbucketPrGetSchema = repoFullArg.extend({
|
|
144
|
-
pullRequestId: z.number().int().min(1),
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
reg(
|
|
148
|
-
"bitbucket_pr_get",
|
|
149
|
-
"Get a single pull request by numeric id.",
|
|
150
|
-
bitbucketPrGetSchema,
|
|
151
|
-
async (parsed) => {
|
|
152
|
-
const { workspace, repoSlug } = splitRepoFull(parsed.repoFull);
|
|
153
|
-
const path = `/repositories/${encodeURIComponent(workspace)}/${encodeURIComponent(repoSlug)}/pullrequests/${String(parsed.pullRequestId)}`;
|
|
154
|
-
const res = await bbFetch(path);
|
|
155
|
-
return mcpJsonResultIfOk("Bitbucket", res);
|
|
156
|
-
},
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
const bitbucketPrMergeSchema = repoFullArg.extend({
|
|
160
|
-
pullRequestId: z.number().int().min(1),
|
|
161
|
-
mergeStrategy: z.enum(["merge_commit", "squash", "fast_forward"]).optional(),
|
|
162
|
-
message: z.string().max(32_768).optional(),
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
registerWriteTool(
|
|
166
|
-
"bitbucket_pr_merge",
|
|
167
|
-
{
|
|
168
|
-
mutates: "bitbucket.pr.merge",
|
|
169
|
-
recoverable: true,
|
|
170
|
-
scopeTargetOf: (p) => ({ kind: "repo", value: p.repoFull }),
|
|
171
|
-
},
|
|
172
|
-
"Merge a pull request.",
|
|
173
|
-
bitbucketPrMergeSchema,
|
|
174
|
-
async (parsed) => {
|
|
175
|
-
const { workspace, repoSlug } = splitRepoFull(parsed.repoFull);
|
|
176
|
-
const path = `/repositories/${encodeURIComponent(workspace)}/${encodeURIComponent(repoSlug)}/pullrequests/${String(parsed.pullRequestId)}/merge`;
|
|
177
|
-
const body: Record<string, unknown> = { type: "pullrequest" };
|
|
178
|
-
if (parsed.mergeStrategy !== undefined) {
|
|
179
|
-
body["merge_strategy"] = parsed.mergeStrategy;
|
|
180
|
-
}
|
|
181
|
-
if (parsed.message !== undefined) {
|
|
182
|
-
body["message"] = parsed.message;
|
|
183
|
-
}
|
|
184
|
-
const res = await bbFetch(path, {
|
|
185
|
-
method: "POST",
|
|
186
|
-
headers: { "Content-Type": "application/json" },
|
|
187
|
-
body: JSON.stringify(body),
|
|
188
|
-
});
|
|
189
|
-
return mcpJsonResultIfOk("Bitbucket", res);
|
|
190
|
-
},
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
const bitbucketRepoPagedSchema = repoFullArg.extend({
|
|
194
|
-
pagelen: z.number().int().min(1).max(100).optional(),
|
|
195
|
-
page: z.string().max(2000).optional(),
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
reg(
|
|
199
|
-
"bitbucket_pipeline_list",
|
|
200
|
-
"List Pipelines runs for a repository.",
|
|
201
|
-
bitbucketRepoPagedSchema,
|
|
202
|
-
async (parsed) => {
|
|
203
|
-
if (parsed.page?.startsWith("http")) {
|
|
204
|
-
const res = await bbFetch(parsed.page);
|
|
205
|
-
return mcpJsonResultIfOk("Bitbucket", res);
|
|
206
|
-
}
|
|
207
|
-
const { workspace, repoSlug } = splitRepoFull(parsed.repoFull);
|
|
208
|
-
const base = `/repositories/${encodeURIComponent(workspace)}/${encodeURIComponent(repoSlug)}/pipelines/`;
|
|
209
|
-
const qs = new URLSearchParams();
|
|
210
|
-
qs.set("pagelen", String(parsed.pagelen ?? 30));
|
|
211
|
-
const res = await bbFetch(`${base}?${qs.toString()}`);
|
|
212
|
-
return mcpJsonResultIfOk("Bitbucket", res);
|
|
213
|
-
},
|
|
214
|
-
);
|
|
215
|
-
|
|
216
|
-
const bitbucketPipelineGetSchema = repoFullArg.extend({
|
|
217
|
-
pipelineUuid: z.string().min(8).max(128),
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
reg(
|
|
221
|
-
"bitbucket_pipeline_get",
|
|
222
|
-
"Get a single pipeline run by UUID.",
|
|
223
|
-
bitbucketPipelineGetSchema,
|
|
224
|
-
async (parsed) => {
|
|
225
|
-
const { workspace, repoSlug } = splitRepoFull(parsed.repoFull);
|
|
226
|
-
const encUuid = encodeURIComponent(parsed.pipelineUuid);
|
|
227
|
-
const path = `/repositories/${encodeURIComponent(workspace)}/${encodeURIComponent(repoSlug)}/pipelines/${encUuid}`;
|
|
228
|
-
const res = await bbFetch(path);
|
|
229
|
-
return mcpJsonResultIfOk("Bitbucket", res);
|
|
230
|
-
},
|
|
231
|
-
);
|
|
232
|
-
|
|
233
|
-
reg(
|
|
234
|
-
"bitbucket_issue_list",
|
|
235
|
-
"List issues for a repository (issue tracker must be enabled).",
|
|
236
|
-
bitbucketRepoPagedSchema,
|
|
237
|
-
async (parsed) => {
|
|
238
|
-
if (parsed.page?.startsWith("http")) {
|
|
239
|
-
const res = await bbFetch(parsed.page);
|
|
240
|
-
return mcpJsonResultIfOk("Bitbucket", res);
|
|
241
|
-
}
|
|
242
|
-
const { workspace, repoSlug } = splitRepoFull(parsed.repoFull);
|
|
243
|
-
const base = `/repositories/${encodeURIComponent(workspace)}/${encodeURIComponent(repoSlug)}/issues`;
|
|
244
|
-
const qs = new URLSearchParams();
|
|
245
|
-
qs.set("pagelen", String(parsed.pagelen ?? 30));
|
|
246
|
-
const res = await bbFetch(`${base}?${qs.toString()}`);
|
|
247
|
-
return mcpJsonResultIfOk("Bitbucket", res);
|
|
248
|
-
},
|
|
249
|
-
);
|
|
7
|
+
registerBitbucketTools(server);
|
|
250
8
|
|
|
251
9
|
const transport = new StdioServerTransport();
|
|
252
10
|
await server.connect(transport);
|