@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
|
@@ -1,140 +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 {
|
|
5
|
-
createRegisterSimpleTool,
|
|
6
|
-
createZodToolRegistrar,
|
|
7
|
-
mcpJsonResult as jsonResult,
|
|
8
|
-
} from "../../../shared/mcp-tool-kit.ts";
|
|
9
|
-
import { runCliJson, runCliOk } from "../../../shared/run-cli-json.ts";
|
|
10
|
-
|
|
11
|
-
function gcloudEnv(): Record<string, string | undefined> {
|
|
12
|
-
const e = { ...process.env } as Record<string, string | undefined>;
|
|
13
|
-
const cf = process.env["GOOGLE_APPLICATION_CREDENTIALS"]?.trim();
|
|
14
|
-
if (cf !== undefined && cf !== "") {
|
|
15
|
-
e["GOOGLE_APPLICATION_CREDENTIALS"] = cf;
|
|
16
|
-
}
|
|
17
|
-
return e;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async function gcloudJson(args: string[]): Promise<unknown> {
|
|
21
|
-
const cmd = ["gcloud", ...args, "--format", "json"];
|
|
22
|
-
const r = await runCliJson(cmd, gcloudEnv());
|
|
23
|
-
if (!r.ok) {
|
|
24
|
-
throw new Error(r.message);
|
|
25
|
-
}
|
|
26
|
-
return r.data ?? {};
|
|
27
|
-
}
|
|
3
|
+
import { registerGcpTools } from "./tools.ts";
|
|
28
4
|
|
|
29
5
|
const mcp = new McpServer({ name: "nimbus-gcp", version: "0.1.0" });
|
|
30
6
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const reg = createZodToolRegistrar(createRegisterSimpleTool(mcp));
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Every MUTATING gcp tool goes through here. Outside the gateway this adds the
|
|
37
|
-
* consent gate, the write-scope allow-list, the mutation budget and the audit record; inside
|
|
38
|
-
* the gateway it is a pass-through, because executor.ts (I2) is the gate there.
|
|
39
|
-
*/
|
|
40
|
-
const registerWriteTool = createWriteToolRegistrar(mcp, {
|
|
41
|
-
connector: "gcp",
|
|
42
|
-
scopeEnv: "NIMBUS_MCP_GCP_WRITE_SCOPE",
|
|
43
|
-
scopeKinds: ["project"],
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
reg(
|
|
47
|
-
"gcp_cloud_run_service_list",
|
|
48
|
-
"List Cloud Run services in a region.",
|
|
49
|
-
z.object({ projectId: z.string().min(1), region: z.string().min(1) }),
|
|
50
|
-
async (p) =>
|
|
51
|
-
jsonResult(
|
|
52
|
-
await gcloudJson([
|
|
53
|
-
"run",
|
|
54
|
-
"services",
|
|
55
|
-
"list",
|
|
56
|
-
`--project=${p.projectId}`,
|
|
57
|
-
`--region=${p.region}`,
|
|
58
|
-
]),
|
|
59
|
-
),
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
registerWriteTool(
|
|
63
|
-
"gcp_cloud_run_deploy",
|
|
64
|
-
{
|
|
65
|
-
mutates: "gcp.cloud_run.deploy",
|
|
66
|
-
recoverable: true,
|
|
67
|
-
scopeTargetOf: (p) => ({ kind: "project", value: p.projectId }),
|
|
68
|
-
},
|
|
69
|
-
"Deploy a container image to Cloud Run.",
|
|
70
|
-
z.object({
|
|
71
|
-
projectId: z.string().min(1),
|
|
72
|
-
region: z.string().min(1),
|
|
73
|
-
service: z.string().min(1),
|
|
74
|
-
image: z.string().min(1),
|
|
75
|
-
}),
|
|
76
|
-
async (p) => {
|
|
77
|
-
const r = await runCliOk(
|
|
78
|
-
[
|
|
79
|
-
"gcloud",
|
|
80
|
-
"run",
|
|
81
|
-
"deploy",
|
|
82
|
-
p.service,
|
|
83
|
-
`--project=${p.projectId}`,
|
|
84
|
-
`--region=${p.region}`,
|
|
85
|
-
`--image=${p.image}`,
|
|
86
|
-
"--quiet",
|
|
87
|
-
],
|
|
88
|
-
gcloudEnv(),
|
|
89
|
-
);
|
|
90
|
-
if (!r.ok) {
|
|
91
|
-
throw new Error(r.message);
|
|
92
|
-
}
|
|
93
|
-
return jsonResult({ ok: true });
|
|
94
|
-
},
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
registerWriteTool(
|
|
98
|
-
"gcp_gke_workload_restart",
|
|
99
|
-
{
|
|
100
|
-
mutates: "gcp.gke.workload.restart",
|
|
101
|
-
recoverable: true,
|
|
102
|
-
scopeTargetOf: (p) => ({ kind: "project", value: p.projectId }),
|
|
103
|
-
},
|
|
104
|
-
"Restart a GKE deployment rollout via kubectl (uses current cluster credentials).",
|
|
105
|
-
z.object({
|
|
106
|
-
projectId: z.string().min(1),
|
|
107
|
-
location: z.string().min(1),
|
|
108
|
-
cluster: z.string().min(1),
|
|
109
|
-
namespace: z.string().min(1),
|
|
110
|
-
deployment: z.string().min(1),
|
|
111
|
-
}),
|
|
112
|
-
async (p) => {
|
|
113
|
-
const getCreds = await runCliOk(
|
|
114
|
-
[
|
|
115
|
-
"gcloud",
|
|
116
|
-
"container",
|
|
117
|
-
"clusters",
|
|
118
|
-
"get-credentials",
|
|
119
|
-
p.cluster,
|
|
120
|
-
`--project=${p.projectId}`,
|
|
121
|
-
`--zone=${p.location}`,
|
|
122
|
-
],
|
|
123
|
-
gcloudEnv(),
|
|
124
|
-
);
|
|
125
|
-
if (!getCreds.ok) {
|
|
126
|
-
throw new Error(getCreds.message);
|
|
127
|
-
}
|
|
128
|
-
const r = await runCliOk(
|
|
129
|
-
["kubectl", "rollout", "restart", "deployment", p.deployment, "-n", p.namespace],
|
|
130
|
-
gcloudEnv(),
|
|
131
|
-
);
|
|
132
|
-
if (!r.ok) {
|
|
133
|
-
throw new Error(r.message);
|
|
134
|
-
}
|
|
135
|
-
return jsonResult({ ok: true });
|
|
136
|
-
},
|
|
137
|
-
);
|
|
7
|
+
registerGcpTools(mcp);
|
|
138
8
|
|
|
139
9
|
const transport = new StdioServerTransport();
|
|
140
10
|
await mcp.connect(transport);
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { gcloudEnv } from "../../../shared/cli-json-kit.ts";
|
|
3
|
+
import type { ConsentServer } from "../../../shared/consent-kit.ts";
|
|
4
|
+
import { createWriteToolRegistrar } from "../../../shared/consent-kit.ts";
|
|
5
|
+
import {
|
|
6
|
+
createRegisterSimpleTool,
|
|
7
|
+
createZodToolRegistrar,
|
|
8
|
+
mcpJsonResult as jsonResult,
|
|
9
|
+
} from "../../../shared/mcp-tool-kit.ts";
|
|
10
|
+
import { runCliJsonThrowing, runCliOkThrowing } from "../../../shared/run-cli-json.ts";
|
|
11
|
+
|
|
12
|
+
async function gcloudJson(args: string[]): Promise<unknown> {
|
|
13
|
+
const cmd = ["gcloud", ...args, "--format", "json"];
|
|
14
|
+
return (await runCliJsonThrowing(cmd, gcloudEnv())) ?? {};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Tool names exposed by this connector — for contract/introspection tests. */
|
|
18
|
+
export const GCP_TOOL_NAMES = [
|
|
19
|
+
"gcp_cloud_run_service_list",
|
|
20
|
+
"gcp_cloud_run_deploy",
|
|
21
|
+
"gcp_gke_workload_restart",
|
|
22
|
+
] as const;
|
|
23
|
+
|
|
24
|
+
export function registerGcpTools(
|
|
25
|
+
server: ConsentServer & { tool: (...args: never) => unknown },
|
|
26
|
+
): void {
|
|
27
|
+
const reg = createZodToolRegistrar(createRegisterSimpleTool(server));
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Every MUTATING gcp tool goes through here. Outside the gateway this adds the
|
|
31
|
+
* consent gate, the write-scope allow-list, the mutation budget and the audit record; inside
|
|
32
|
+
* the gateway it is a pass-through, because executor.ts (I2) is the gate there.
|
|
33
|
+
*/
|
|
34
|
+
const registerWriteTool = createWriteToolRegistrar(server, {
|
|
35
|
+
connector: "gcp",
|
|
36
|
+
scopeEnv: "NIMBUS_MCP_GCP_WRITE_SCOPE",
|
|
37
|
+
scopeKinds: ["project"],
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
reg(
|
|
41
|
+
"gcp_cloud_run_service_list",
|
|
42
|
+
"List Cloud Run services in a region.",
|
|
43
|
+
z.object({ projectId: z.string().min(1), region: z.string().min(1) }),
|
|
44
|
+
async (p) =>
|
|
45
|
+
jsonResult(
|
|
46
|
+
await gcloudJson([
|
|
47
|
+
"run",
|
|
48
|
+
"services",
|
|
49
|
+
"list",
|
|
50
|
+
`--project=${p.projectId}`,
|
|
51
|
+
`--region=${p.region}`,
|
|
52
|
+
]),
|
|
53
|
+
),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
registerWriteTool(
|
|
57
|
+
"gcp_cloud_run_deploy",
|
|
58
|
+
{
|
|
59
|
+
mutates: "gcp.cloud_run.deploy",
|
|
60
|
+
recoverable: true,
|
|
61
|
+
scopeTargetOf: (p) => ({ kind: "project", value: p.projectId }),
|
|
62
|
+
},
|
|
63
|
+
"Deploy a container image to Cloud Run.",
|
|
64
|
+
z.object({
|
|
65
|
+
projectId: z.string().min(1),
|
|
66
|
+
region: z.string().min(1),
|
|
67
|
+
service: z.string().min(1),
|
|
68
|
+
image: z.string().min(1),
|
|
69
|
+
}),
|
|
70
|
+
async (p) => {
|
|
71
|
+
await runCliOkThrowing(
|
|
72
|
+
[
|
|
73
|
+
"gcloud",
|
|
74
|
+
"run",
|
|
75
|
+
"deploy",
|
|
76
|
+
p.service,
|
|
77
|
+
`--project=${p.projectId}`,
|
|
78
|
+
`--region=${p.region}`,
|
|
79
|
+
`--image=${p.image}`,
|
|
80
|
+
"--quiet",
|
|
81
|
+
],
|
|
82
|
+
gcloudEnv(),
|
|
83
|
+
);
|
|
84
|
+
return jsonResult({ ok: true });
|
|
85
|
+
},
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
registerWriteTool(
|
|
89
|
+
"gcp_gke_workload_restart",
|
|
90
|
+
{
|
|
91
|
+
mutates: "gcp.gke.workload.restart",
|
|
92
|
+
recoverable: true,
|
|
93
|
+
scopeTargetOf: (p) => ({ kind: "project", value: p.projectId }),
|
|
94
|
+
},
|
|
95
|
+
"Restart a GKE deployment rollout via kubectl (uses current cluster credentials).",
|
|
96
|
+
z.object({
|
|
97
|
+
projectId: z.string().min(1),
|
|
98
|
+
location: z.string().min(1),
|
|
99
|
+
cluster: z.string().min(1),
|
|
100
|
+
namespace: z.string().min(1),
|
|
101
|
+
deployment: z.string().min(1),
|
|
102
|
+
}),
|
|
103
|
+
async (p) => {
|
|
104
|
+
await runCliOkThrowing(
|
|
105
|
+
[
|
|
106
|
+
"gcloud",
|
|
107
|
+
"container",
|
|
108
|
+
"clusters",
|
|
109
|
+
"get-credentials",
|
|
110
|
+
p.cluster,
|
|
111
|
+
`--project=${p.projectId}`,
|
|
112
|
+
`--zone=${p.location}`,
|
|
113
|
+
],
|
|
114
|
+
gcloudEnv(),
|
|
115
|
+
);
|
|
116
|
+
await runCliOkThrowing(
|
|
117
|
+
["kubectl", "rollout", "restart", "deployment", p.deployment, "-n", p.namespace],
|
|
118
|
+
gcloudEnv(),
|
|
119
|
+
);
|
|
120
|
+
return jsonResult({ ok: true });
|
|
121
|
+
},
|
|
122
|
+
);
|
|
123
|
+
}
|
|
@@ -1,31 +1,6 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
import { createWriteToolRegistrar, type WriteToolConfig } from "../../../shared/consent-kit.ts";
|
|
6
|
-
import {
|
|
7
|
-
createRegisterSimpleTool,
|
|
8
|
-
createZodToolRegistrar,
|
|
9
|
-
mcpJsonResult as jsonResult,
|
|
10
|
-
mcpJsonResultIfOk,
|
|
11
|
-
requireProcessEnv,
|
|
12
|
-
type ZodObjectSchema,
|
|
13
|
-
} from "../../../shared/mcp-tool-kit.ts";
|
|
14
|
-
import { makeRestFetcher, makeRestToolRegistrar } from "../../../shared/rest-tool-kit.ts";
|
|
15
|
-
|
|
16
|
-
const GH_API = "https://api.github.com";
|
|
17
|
-
const GH_HEADERS: Record<string, string> = {
|
|
18
|
-
Accept: "application/vnd.github+json",
|
|
19
|
-
"X-GitHub-Api-Version": "2022-11-28",
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
function ghFetch(
|
|
23
|
-
token: string,
|
|
24
|
-
path: string,
|
|
25
|
-
init?: RequestInit,
|
|
26
|
-
): Promise<{ ok: boolean; status: number; json: unknown; text: string }> {
|
|
27
|
-
return makeRestFetcher({ apiBase: GH_API, token, defaultHeaders: GH_HEADERS })(path, init);
|
|
28
|
-
}
|
|
3
|
+
import { registerGithubTools } from "./tools.ts";
|
|
29
4
|
|
|
30
5
|
const server = new McpServer(
|
|
31
6
|
{ name: "nimbus-github", version: "0.1.0" },
|
|
@@ -40,308 +15,7 @@ const server = new McpServer(
|
|
|
40
15
|
},
|
|
41
16
|
);
|
|
42
17
|
|
|
43
|
-
|
|
44
|
-
const reg = createZodToolRegistrar(registerSimpleTool);
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Every MUTATING GitHub tool goes through here. Outside the gateway this adds the consent gate,
|
|
48
|
-
* the write-scope allow-list, the mutation budget and the audit record; inside the gateway it is a
|
|
49
|
-
* pass-through, because executor.ts (I2) is the gate there.
|
|
50
|
-
*/
|
|
51
|
-
const registerWriteTool = createWriteToolRegistrar(server, {
|
|
52
|
-
connector: "github",
|
|
53
|
-
scopeEnv: "NIMBUS_MCP_GITHUB_WRITE_SCOPE",
|
|
54
|
-
scopeKinds: ["repo"],
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* The write-tool equivalent of `registerGithubTool`: same buildPath/buildInit shape, same fetch and
|
|
59
|
-
* result handling, routed through the write registrar. `scopeTargetOf` is supplied here rather
|
|
60
|
-
* than per tool — every GitHub mutation is scoped to one `owner/repo`, and deriving it centrally
|
|
61
|
-
* means a new write tool cannot forget it.
|
|
62
|
-
*/
|
|
63
|
-
function registerGithubWriteTool<T extends { owner: string; repo: string }>(
|
|
64
|
-
name: string,
|
|
65
|
-
cfg: Omit<WriteToolConfig<T>, "scopeTargetOf">,
|
|
66
|
-
description: string,
|
|
67
|
-
schema: ZodObjectSchema<T>,
|
|
68
|
-
buildPath: (parsed: T) => string,
|
|
69
|
-
buildInit?: (parsed: T) => RequestInit,
|
|
70
|
-
): void {
|
|
71
|
-
registerWriteTool(
|
|
72
|
-
name,
|
|
73
|
-
{ ...cfg, scopeTargetOf: (p) => ({ kind: "repo", value: `${p.owner}/${p.repo}` }) },
|
|
74
|
-
description,
|
|
75
|
-
schema,
|
|
76
|
-
async (parsed) => {
|
|
77
|
-
const token = requireProcessEnv("GITHUB_PAT");
|
|
78
|
-
const res = await ghFetch(token, buildPath(parsed), buildInit?.(parsed));
|
|
79
|
-
return mcpJsonResultIfOk("GitHub", res);
|
|
80
|
-
},
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** Standard GitHub tool: token → ghFetch(buildPath[, buildInit]) → mcpJsonResultIfOk("GitHub"). */
|
|
85
|
-
const registerGithubTool = makeRestToolRegistrar({
|
|
86
|
-
registrar: reg,
|
|
87
|
-
tokenEnv: "GITHUB_PAT",
|
|
88
|
-
serviceLabel: "GitHub",
|
|
89
|
-
fetch: ghFetch,
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
const slug = (owner: string, repo: string): string =>
|
|
93
|
-
`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`;
|
|
94
|
-
|
|
95
|
-
const jsonInit = (method: string, body: unknown): RequestInit => ({
|
|
96
|
-
method,
|
|
97
|
-
headers: { "Content-Type": "application/json" },
|
|
98
|
-
body: JSON.stringify(body),
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
const repoSlugArgs = z.object({
|
|
102
|
-
owner: z.string().min(1),
|
|
103
|
-
repo: z.string().min(1),
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
const githubRepoListSchema = z.object({
|
|
107
|
-
perPage: z.number().int().min(1).max(100).optional(),
|
|
108
|
-
page: z.number().int().min(1).optional(),
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
registerGithubTool(
|
|
112
|
-
"github_repo_list",
|
|
113
|
-
"List repositories for the authenticated user (affiliation: owner, collaborator, organization_member).",
|
|
114
|
-
githubRepoListSchema,
|
|
115
|
-
(parsed) => {
|
|
116
|
-
const u = new URL(`${GH_API}/user/repos`);
|
|
117
|
-
u.searchParams.set("per_page", String(parsed.perPage ?? 30));
|
|
118
|
-
if (parsed.page !== undefined) {
|
|
119
|
-
u.searchParams.set("page", String(parsed.page));
|
|
120
|
-
}
|
|
121
|
-
u.searchParams.set("sort", "updated");
|
|
122
|
-
u.searchParams.set("affiliation", "owner,collaborator,organization_member");
|
|
123
|
-
return `${u.pathname}${u.search}`;
|
|
124
|
-
},
|
|
125
|
-
);
|
|
126
|
-
|
|
127
|
-
registerGithubTool(
|
|
128
|
-
"github_repo_get",
|
|
129
|
-
"Get repository metadata (owner/repo).",
|
|
130
|
-
repoSlugArgs,
|
|
131
|
-
(parsed) => slug(parsed.owner, parsed.repo),
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
const githubPrListSchema = repoSlugArgs.extend({
|
|
135
|
-
state: z.enum(["open", "closed", "all"]).optional(),
|
|
136
|
-
perPage: z.number().int().min(1).max(100).optional(),
|
|
137
|
-
page: z.number().int().min(1).optional(),
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
registerGithubTool(
|
|
141
|
-
"github_pr_list",
|
|
142
|
-
"List pull requests for a repository.",
|
|
143
|
-
githubPrListSchema,
|
|
144
|
-
(parsed) => {
|
|
145
|
-
const u = new URL(`${GH_API}${slug(parsed.owner, parsed.repo)}/pulls`);
|
|
146
|
-
u.searchParams.set("state", parsed.state ?? "open");
|
|
147
|
-
u.searchParams.set("per_page", String(parsed.perPage ?? 30));
|
|
148
|
-
if (parsed.page !== undefined) {
|
|
149
|
-
u.searchParams.set("page", String(parsed.page));
|
|
150
|
-
}
|
|
151
|
-
u.searchParams.set("sort", "updated");
|
|
152
|
-
u.searchParams.set("direction", "desc");
|
|
153
|
-
return `${u.pathname}${u.search}`;
|
|
154
|
-
},
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
const githubPrNumberSchema = repoSlugArgs.extend({
|
|
158
|
-
pullNumber: z.number().int().min(1),
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
registerGithubTool(
|
|
162
|
-
"github_pr_get",
|
|
163
|
-
"Get a single pull request by number.",
|
|
164
|
-
githubPrNumberSchema,
|
|
165
|
-
(parsed) => `${slug(parsed.owner, parsed.repo)}/pulls/${String(parsed.pullNumber)}`,
|
|
166
|
-
);
|
|
167
|
-
|
|
168
|
-
const githubPrMergeSchema = repoSlugArgs.extend({
|
|
169
|
-
pullNumber: z.number().int().min(1),
|
|
170
|
-
mergeMethod: z.enum(["merge", "squash", "rebase"]).optional(),
|
|
171
|
-
commitTitle: z.string().max(500).optional(),
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
registerGithubWriteTool(
|
|
175
|
-
"github_pr_merge",
|
|
176
|
-
{ mutates: "github.pr.merge", recoverable: true },
|
|
177
|
-
"Merge a pull request.",
|
|
178
|
-
githubPrMergeSchema,
|
|
179
|
-
(parsed) => `${slug(parsed.owner, parsed.repo)}/pulls/${String(parsed.pullNumber)}/merge`,
|
|
180
|
-
(parsed) => {
|
|
181
|
-
const body: Record<string, string> = {};
|
|
182
|
-
if (parsed.mergeMethod !== undefined) {
|
|
183
|
-
body["merge_method"] = parsed.mergeMethod;
|
|
184
|
-
}
|
|
185
|
-
if (parsed.commitTitle !== undefined && parsed.commitTitle !== "") {
|
|
186
|
-
body["commit_title"] = parsed.commitTitle;
|
|
187
|
-
}
|
|
188
|
-
return jsonInit("PUT", body);
|
|
189
|
-
},
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
registerGithubWriteTool(
|
|
193
|
-
"github_pr_close",
|
|
194
|
-
{ mutates: "github.pr.close", recoverable: true },
|
|
195
|
-
"Close a pull request without merging.",
|
|
196
|
-
githubPrNumberSchema,
|
|
197
|
-
(parsed) => `${slug(parsed.owner, parsed.repo)}/pulls/${String(parsed.pullNumber)}`,
|
|
198
|
-
() => jsonInit("PATCH", { state: "closed" }),
|
|
199
|
-
);
|
|
200
|
-
|
|
201
|
-
const githubIssueListSchema = repoSlugArgs.extend({
|
|
202
|
-
state: z.enum(["open", "closed", "all"]).optional(),
|
|
203
|
-
perPage: z.number().int().min(1).max(100).optional(),
|
|
204
|
-
page: z.number().int().min(1).optional(),
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
registerGithubTool(
|
|
208
|
-
"github_issue_list",
|
|
209
|
-
"List issues for a repository.",
|
|
210
|
-
githubIssueListSchema,
|
|
211
|
-
(parsed) => {
|
|
212
|
-
const u = new URL(`${GH_API}${slug(parsed.owner, parsed.repo)}/issues`);
|
|
213
|
-
u.searchParams.set("state", parsed.state ?? "open");
|
|
214
|
-
u.searchParams.set("per_page", String(parsed.perPage ?? 30));
|
|
215
|
-
if (parsed.page !== undefined) {
|
|
216
|
-
u.searchParams.set("page", String(parsed.page));
|
|
217
|
-
}
|
|
218
|
-
u.searchParams.set("sort", "updated");
|
|
219
|
-
u.searchParams.set("direction", "desc");
|
|
220
|
-
return `${u.pathname}${u.search}`;
|
|
221
|
-
},
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
const githubIssueGetSchema = repoSlugArgs.extend({
|
|
225
|
-
issueNumber: z.number().int().min(1),
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
registerGithubTool(
|
|
229
|
-
"github_issue_get",
|
|
230
|
-
"Get a single issue by number.",
|
|
231
|
-
githubIssueGetSchema,
|
|
232
|
-
(parsed) => `${slug(parsed.owner, parsed.repo)}/issues/${String(parsed.issueNumber)}`,
|
|
233
|
-
);
|
|
234
|
-
|
|
235
|
-
const githubIssueCreateSchema = repoSlugArgs.extend({
|
|
236
|
-
title: z.string().min(1).max(500),
|
|
237
|
-
body: z.string().max(65_000).optional(),
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
registerGithubWriteTool(
|
|
241
|
-
"github_issue_create",
|
|
242
|
-
{ mutates: "github.issue.create", recoverable: true },
|
|
243
|
-
"Create a new issue in a repository.",
|
|
244
|
-
githubIssueCreateSchema,
|
|
245
|
-
(parsed) => `${slug(parsed.owner, parsed.repo)}/issues`,
|
|
246
|
-
(parsed) => jsonInit("POST", { title: parsed.title, body: parsed.body }),
|
|
247
|
-
);
|
|
248
|
-
|
|
249
|
-
const githubCiRunsSchema = repoSlugArgs.extend({
|
|
250
|
-
perPage: z.number().int().min(1).max(100).optional(),
|
|
251
|
-
page: z.number().int().min(1).optional(),
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
registerGithubTool(
|
|
255
|
-
"github_ci_runs",
|
|
256
|
-
"List GitHub Actions workflow runs for a repository.",
|
|
257
|
-
githubCiRunsSchema,
|
|
258
|
-
(parsed) => {
|
|
259
|
-
const u = new URL(`${GH_API}${slug(parsed.owner, parsed.repo)}/actions/runs`);
|
|
260
|
-
u.searchParams.set("per_page", String(parsed.perPage ?? 30));
|
|
261
|
-
if (parsed.page !== undefined) {
|
|
262
|
-
u.searchParams.set("page", String(parsed.page));
|
|
263
|
-
}
|
|
264
|
-
return `${u.pathname}${u.search}`;
|
|
265
|
-
},
|
|
266
|
-
);
|
|
267
|
-
|
|
268
|
-
const githubCiRunGetSchema = repoSlugArgs.extend({
|
|
269
|
-
runId: z.number().int().min(1),
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
registerGithubTool(
|
|
273
|
-
"github_ci_run_get",
|
|
274
|
-
"Get a single workflow run including jobs URL reference.",
|
|
275
|
-
githubCiRunGetSchema,
|
|
276
|
-
(parsed) => `${slug(parsed.owner, parsed.repo)}/actions/runs/${String(parsed.runId)}`,
|
|
277
|
-
);
|
|
278
|
-
|
|
279
|
-
const githubBranchDeleteSchema = repoSlugArgs.extend({
|
|
280
|
-
branch: z.string().min(1).max(255),
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
registerWriteTool(
|
|
284
|
-
"github_branch_delete",
|
|
285
|
-
{
|
|
286
|
-
mutates: "github.branch.delete",
|
|
287
|
-
// The only GitHub mutation here that cannot be undone from its own result, so the ref's SHA is
|
|
288
|
-
// captured BEFORE the delete. With it the branch can be recreated; without it the commit is
|
|
289
|
-
// only reachable by reflog on someone's clone.
|
|
290
|
-
recoverable: false,
|
|
291
|
-
capturePreState: async (parsed) => {
|
|
292
|
-
const token = requireProcessEnv("GITHUB_PAT");
|
|
293
|
-
const ref = `heads/${parsed.branch}`;
|
|
294
|
-
const res = await ghFetch(
|
|
295
|
-
token,
|
|
296
|
-
`${slug(parsed.owner, parsed.repo)}/git/ref/${encodeURIComponent(ref)}`,
|
|
297
|
-
);
|
|
298
|
-
return { ref, resolved: res.ok, sha: res.json };
|
|
299
|
-
},
|
|
300
|
-
scopeTargetOf: (parsed) => ({ kind: "repo", value: `${parsed.owner}/${parsed.repo}` }),
|
|
301
|
-
},
|
|
302
|
-
"Delete a branch by ref name.",
|
|
303
|
-
githubBranchDeleteSchema,
|
|
304
|
-
async (parsed) => {
|
|
305
|
-
const token = requireProcessEnv("GITHUB_PAT");
|
|
306
|
-
const ref = `heads/${parsed.branch}`;
|
|
307
|
-
const path = `${slug(parsed.owner, parsed.repo)}/git/refs/${encodeURIComponent(ref)}`;
|
|
308
|
-
const res = await ghFetch(token, path, { method: "DELETE" });
|
|
309
|
-
if (!res.ok && res.status !== 204) {
|
|
310
|
-
throw new Error(`GitHub ${String(res.status)}: ${res.text.slice(0, 300)}`);
|
|
311
|
-
}
|
|
312
|
-
return jsonResult({ ok: true, deleted: ref });
|
|
313
|
-
},
|
|
314
|
-
);
|
|
315
|
-
|
|
316
|
-
const githubTagCreateSchema = repoSlugArgs.extend({
|
|
317
|
-
tag: z.string().min(1).max(255),
|
|
318
|
-
sha: z.string().min(7).max(40),
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
registerGithubWriteTool(
|
|
322
|
-
"github_tag_create",
|
|
323
|
-
{ mutates: "github.tag.create", recoverable: true },
|
|
324
|
-
"Create a lightweight tag pointing at a commit SHA.",
|
|
325
|
-
githubTagCreateSchema,
|
|
326
|
-
(parsed) => `${slug(parsed.owner, parsed.repo)}/git/refs`,
|
|
327
|
-
(parsed) => jsonInit("POST", { ref: `refs/tags/${parsed.tag}`, sha: parsed.sha }),
|
|
328
|
-
);
|
|
329
|
-
|
|
330
|
-
const githubCommitPushSchema = repoSlugArgs.extend({
|
|
331
|
-
branch: z.string().min(1).optional(),
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
reg(
|
|
335
|
-
"github_commit_push",
|
|
336
|
-
"Push commits is not available via this tool — use local git with your own remote credentials (requires HITL repo.commit.push if ever implemented).",
|
|
337
|
-
githubCommitPushSchema,
|
|
338
|
-
async () =>
|
|
339
|
-
jsonResult({
|
|
340
|
-
code: "NOT_IMPLEMENTED",
|
|
341
|
-
message:
|
|
342
|
-
"Pushing commits requires local git and is not executed by this MCP server. Clone the repo and push with git.",
|
|
343
|
-
}),
|
|
344
|
-
);
|
|
18
|
+
registerGithubTools(server);
|
|
345
19
|
|
|
346
20
|
const transport = new StdioServerTransport();
|
|
347
21
|
await server.connect(transport);
|