@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,392 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { createWriteToolRegistrar, type WriteToolConfig } from "../../../shared/consent-kit.ts";
|
|
5
|
+
import {
|
|
6
|
+
createRegisterSimpleTool,
|
|
7
|
+
createZodToolRegistrar,
|
|
8
|
+
mcpJsonResult as jsonResult,
|
|
9
|
+
mcpJsonResultIfOk,
|
|
10
|
+
requireProcessEnv,
|
|
11
|
+
type ZodObjectSchema,
|
|
12
|
+
} from "../../../shared/mcp-tool-kit.ts";
|
|
13
|
+
import { stripTrailingSlashes } from "../../../shared/strip-trailing-slashes.ts";
|
|
14
|
+
|
|
15
|
+
function apiBase(): string {
|
|
16
|
+
const b = process.env["GITLAB_API_BASE_URL"];
|
|
17
|
+
if (b !== undefined && b.trim() !== "") {
|
|
18
|
+
return stripTrailingSlashes(b);
|
|
19
|
+
}
|
|
20
|
+
return "https://gitlab.com/api/v4";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function glFetch(
|
|
24
|
+
token: string,
|
|
25
|
+
path: string,
|
|
26
|
+
init?: RequestInit,
|
|
27
|
+
): Promise<{ ok: boolean; status: number; json: unknown; text: string }> {
|
|
28
|
+
const base = apiBase();
|
|
29
|
+
const relativePath = path.startsWith("/") ? path : `/${path}`;
|
|
30
|
+
const url = path.startsWith("http") ? path : `${base}${relativePath}`;
|
|
31
|
+
const baseHeaders: Record<string, string> = { "PRIVATE-TOKEN": token };
|
|
32
|
+
const mergedHeaders =
|
|
33
|
+
init?.headers === undefined
|
|
34
|
+
? baseHeaders
|
|
35
|
+
: { ...baseHeaders, ...(init.headers as Record<string, string>) };
|
|
36
|
+
const res = await fetch(url, {
|
|
37
|
+
...init,
|
|
38
|
+
headers: mergedHeaders,
|
|
39
|
+
});
|
|
40
|
+
const text = await res.text();
|
|
41
|
+
let json: unknown;
|
|
42
|
+
try {
|
|
43
|
+
json = JSON.parse(text) as unknown;
|
|
44
|
+
} catch {
|
|
45
|
+
json = null;
|
|
46
|
+
}
|
|
47
|
+
return { ok: res.ok, status: res.status, json, text };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const server = new McpServer({ name: "nimbus-gitlab", version: "0.1.0" });
|
|
51
|
+
|
|
52
|
+
const registerSimpleTool = createRegisterSimpleTool(server);
|
|
53
|
+
const reg = createZodToolRegistrar(registerSimpleTool);
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Register a standard GitLab read/write tool whose body is the repeated shape
|
|
57
|
+
* `token → glFetch(buildUrl[, buildInit]) → mcpJsonResultIfOk("GitLab", res)`.
|
|
58
|
+
* `buildUrl` returns the relative path (or absolute URL) and `buildInit` the
|
|
59
|
+
* optional fetch init (method/body). Tools with a non-standard tail (raw text
|
|
60
|
+
* trace, custom error text) stay hand-written below.
|
|
61
|
+
*/
|
|
62
|
+
/**
|
|
63
|
+
* Every MUTATING gitlab tool goes through here. Outside the gateway this adds the
|
|
64
|
+
* consent gate, the write-scope allow-list, the mutation budget and the audit record; inside
|
|
65
|
+
* the gateway it is a pass-through, because executor.ts (I2) is the gate there.
|
|
66
|
+
*/
|
|
67
|
+
const registerWriteTool = createWriteToolRegistrar(server, {
|
|
68
|
+
connector: "gitlab",
|
|
69
|
+
scopeEnv: "NIMBUS_MCP_GITLAB_WRITE_SCOPE",
|
|
70
|
+
scopeKinds: ["repo"],
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The write-tool equivalent of `registerGitlabTool`: identical fetch and result handling, routed
|
|
75
|
+
* through the write registrar. `scopeTargetOf` is supplied here rather than per tool — every
|
|
76
|
+
* GitLab mutation is scoped to one project — so a new write tool cannot forget it.
|
|
77
|
+
*/
|
|
78
|
+
function registerGitlabWriteTool<T extends { projectPath: string }>(
|
|
79
|
+
name: string,
|
|
80
|
+
cfg: Omit<WriteToolConfig<T>, "scopeTargetOf">,
|
|
81
|
+
description: string,
|
|
82
|
+
schema: ZodObjectSchema<T>,
|
|
83
|
+
buildUrl: (p: T) => string,
|
|
84
|
+
buildInit?: (p: T) => RequestInit,
|
|
85
|
+
): void {
|
|
86
|
+
registerWriteTool(
|
|
87
|
+
name,
|
|
88
|
+
{ ...cfg, scopeTargetOf: (p) => ({ kind: "repo", value: p.projectPath }) },
|
|
89
|
+
description,
|
|
90
|
+
schema,
|
|
91
|
+
async (parsed) => {
|
|
92
|
+
const token = requireProcessEnv("GITLAB_PAT");
|
|
93
|
+
const res = await glFetch(token, buildUrl(parsed), buildInit?.(parsed));
|
|
94
|
+
return mcpJsonResultIfOk("GitLab", res);
|
|
95
|
+
},
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function registerGitlabTool<T>(
|
|
100
|
+
name: string,
|
|
101
|
+
description: string,
|
|
102
|
+
schema: ZodObjectSchema<T>,
|
|
103
|
+
buildUrl: (p: T) => string,
|
|
104
|
+
buildInit?: (p: T) => RequestInit,
|
|
105
|
+
): void {
|
|
106
|
+
reg(name, description, schema, async (parsed) => {
|
|
107
|
+
const token = requireProcessEnv("GITLAB_PAT");
|
|
108
|
+
const res = await glFetch(token, buildUrl(parsed), buildInit?.(parsed));
|
|
109
|
+
return mcpJsonResultIfOk("GitLab", res);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const projectPathArg = z.object({
|
|
114
|
+
projectPath: z
|
|
115
|
+
.string()
|
|
116
|
+
.min(1)
|
|
117
|
+
.describe("URL-encoded path or numeric project id, e.g. group/repo"),
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const gitlabProjectListSchema = z.object({
|
|
121
|
+
perPage: z.number().int().min(1).max(100).optional(),
|
|
122
|
+
page: z.number().int().min(1).optional(),
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
registerGitlabTool(
|
|
126
|
+
"gitlab_project_list",
|
|
127
|
+
"List projects visible to the authenticated user (membership).",
|
|
128
|
+
gitlabProjectListSchema,
|
|
129
|
+
(parsed) => {
|
|
130
|
+
const u = new URL(`${apiBase()}/projects`);
|
|
131
|
+
u.searchParams.set("membership", "true");
|
|
132
|
+
u.searchParams.set("order_by", "last_activity_at");
|
|
133
|
+
u.searchParams.set("sort", "desc");
|
|
134
|
+
u.searchParams.set("per_page", String(parsed.perPage ?? 30));
|
|
135
|
+
if (parsed.page !== undefined) {
|
|
136
|
+
u.searchParams.set("page", String(parsed.page));
|
|
137
|
+
}
|
|
138
|
+
// Absolute URL: u already includes apiBase()'s `/api/v4`; returning a
|
|
139
|
+
// relative path would let glFetch re-prefix apiBase() → `/api/v4/api/v4/…`.
|
|
140
|
+
return u.toString();
|
|
141
|
+
},
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const gitlabMrListSchema = projectPathArg.extend({
|
|
145
|
+
state: z.enum(["opened", "closed", "locked", "merged", "all"]).optional(),
|
|
146
|
+
perPage: z.number().int().min(1).max(100).optional(),
|
|
147
|
+
page: z.number().int().min(1).optional(),
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
registerGitlabTool(
|
|
151
|
+
"gitlab_mr_list",
|
|
152
|
+
"List merge requests for a project.",
|
|
153
|
+
gitlabMrListSchema,
|
|
154
|
+
(parsed) => {
|
|
155
|
+
const enc = encodeURIComponent(parsed.projectPath);
|
|
156
|
+
const u = new URL(`${apiBase()}/projects/${enc}/merge_requests`);
|
|
157
|
+
u.searchParams.set("state", parsed.state ?? "opened");
|
|
158
|
+
u.searchParams.set("per_page", String(parsed.perPage ?? 30));
|
|
159
|
+
if (parsed.page !== undefined) {
|
|
160
|
+
u.searchParams.set("page", String(parsed.page));
|
|
161
|
+
}
|
|
162
|
+
// Absolute URL: u already includes apiBase()'s `/api/v4`; returning a
|
|
163
|
+
// relative path would let glFetch re-prefix apiBase() → `/api/v4/api/v4/…`.
|
|
164
|
+
return u.toString();
|
|
165
|
+
},
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
const gitlabMrGetSchema = projectPathArg.extend({
|
|
169
|
+
mergeRequestIid: z.number().int().min(1),
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
registerGitlabTool(
|
|
173
|
+
"gitlab_mr_get",
|
|
174
|
+
"Get a single merge request by IID.",
|
|
175
|
+
gitlabMrGetSchema,
|
|
176
|
+
(parsed) =>
|
|
177
|
+
`/projects/${encodeURIComponent(parsed.projectPath)}/merge_requests/${String(parsed.mergeRequestIid)}`,
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
const gitlabMrMergeSchema = projectPathArg.extend({
|
|
181
|
+
mergeRequestIid: z.number().int().min(1),
|
|
182
|
+
mergeCommitMessage: z.string().max(10_000).optional(),
|
|
183
|
+
squash: z.boolean().optional(),
|
|
184
|
+
shouldRemoveSourceBranch: z.boolean().optional(),
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
registerGitlabWriteTool(
|
|
188
|
+
"gitlab_mr_merge",
|
|
189
|
+
{
|
|
190
|
+
mutates: "gitlab.mr.merge",
|
|
191
|
+
recoverable: true,
|
|
192
|
+
},
|
|
193
|
+
"Merge a merge request.",
|
|
194
|
+
gitlabMrMergeSchema,
|
|
195
|
+
(parsed) =>
|
|
196
|
+
`/projects/${encodeURIComponent(parsed.projectPath)}/merge_requests/${String(parsed.mergeRequestIid)}/merge`,
|
|
197
|
+
(parsed) => {
|
|
198
|
+
const body: Record<string, unknown> = {};
|
|
199
|
+
if (parsed.mergeCommitMessage !== undefined) {
|
|
200
|
+
body["merge_commit_message"] = parsed.mergeCommitMessage;
|
|
201
|
+
}
|
|
202
|
+
if (parsed.squash !== undefined) {
|
|
203
|
+
body["squash"] = parsed.squash;
|
|
204
|
+
}
|
|
205
|
+
if (parsed.shouldRemoveSourceBranch !== undefined) {
|
|
206
|
+
body["should_remove_source_branch"] = parsed.shouldRemoveSourceBranch;
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
method: "PUT",
|
|
210
|
+
headers: { "Content-Type": "application/json" },
|
|
211
|
+
body: JSON.stringify(body),
|
|
212
|
+
};
|
|
213
|
+
},
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
const gitlabIssueListSchema = projectPathArg.extend({
|
|
217
|
+
state: z.enum(["opened", "closed", "all"]).optional(),
|
|
218
|
+
perPage: z.number().int().min(1).max(100).optional(),
|
|
219
|
+
page: z.number().int().min(1).optional(),
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
registerGitlabTool(
|
|
223
|
+
"gitlab_issue_list",
|
|
224
|
+
"List issues for a project.",
|
|
225
|
+
gitlabIssueListSchema,
|
|
226
|
+
(parsed) => {
|
|
227
|
+
const enc = encodeURIComponent(parsed.projectPath);
|
|
228
|
+
const u = new URL(`${apiBase()}/projects/${enc}/issues`);
|
|
229
|
+
u.searchParams.set("state", parsed.state ?? "opened");
|
|
230
|
+
u.searchParams.set("per_page", String(parsed.perPage ?? 30));
|
|
231
|
+
if (parsed.page !== undefined) {
|
|
232
|
+
u.searchParams.set("page", String(parsed.page));
|
|
233
|
+
}
|
|
234
|
+
// Absolute URL: u already includes apiBase()'s `/api/v4`; returning a
|
|
235
|
+
// relative path would let glFetch re-prefix apiBase() → `/api/v4/api/v4/…`.
|
|
236
|
+
return u.toString();
|
|
237
|
+
},
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
const gitlabIssueGetSchema = projectPathArg.extend({
|
|
241
|
+
issueIid: z.number().int().min(1),
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
registerGitlabTool(
|
|
245
|
+
"gitlab_issue_get",
|
|
246
|
+
"Get a single issue by IID.",
|
|
247
|
+
gitlabIssueGetSchema,
|
|
248
|
+
(parsed) =>
|
|
249
|
+
`/projects/${encodeURIComponent(parsed.projectPath)}/issues/${String(parsed.issueIid)}`,
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
const gitlabPipelineListSchema = projectPathArg.extend({
|
|
253
|
+
ref: z.string().max(500).optional(),
|
|
254
|
+
status: z.string().max(64).optional(),
|
|
255
|
+
perPage: z.number().int().min(1).max(100).optional(),
|
|
256
|
+
page: z.number().int().min(1).optional(),
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
registerGitlabTool(
|
|
260
|
+
"gitlab_pipeline_list",
|
|
261
|
+
"List CI pipelines for a project.",
|
|
262
|
+
gitlabPipelineListSchema,
|
|
263
|
+
(parsed) => {
|
|
264
|
+
const enc = encodeURIComponent(parsed.projectPath);
|
|
265
|
+
const u = new URL(`${apiBase()}/projects/${enc}/pipelines`);
|
|
266
|
+
u.searchParams.set("per_page", String(parsed.perPage ?? 30));
|
|
267
|
+
if (parsed.page !== undefined) {
|
|
268
|
+
u.searchParams.set("page", String(parsed.page));
|
|
269
|
+
}
|
|
270
|
+
if (parsed.ref !== undefined) {
|
|
271
|
+
u.searchParams.set("ref", parsed.ref);
|
|
272
|
+
}
|
|
273
|
+
if (parsed.status !== undefined) {
|
|
274
|
+
u.searchParams.set("status", parsed.status);
|
|
275
|
+
}
|
|
276
|
+
// Absolute URL: u already includes apiBase()'s `/api/v4`; returning a
|
|
277
|
+
// relative path would let glFetch re-prefix apiBase() → `/api/v4/api/v4/…`.
|
|
278
|
+
return u.toString();
|
|
279
|
+
},
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
const gitlabPipelineGetSchema = projectPathArg.extend({
|
|
283
|
+
pipelineId: z.number().int().min(1),
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
registerGitlabTool(
|
|
287
|
+
"gitlab_pipeline_get",
|
|
288
|
+
"Get a single pipeline by id.",
|
|
289
|
+
gitlabPipelineGetSchema,
|
|
290
|
+
(parsed) =>
|
|
291
|
+
`/projects/${encodeURIComponent(parsed.projectPath)}/pipelines/${String(parsed.pipelineId)}`,
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
registerGitlabTool(
|
|
295
|
+
"gitlab_pipeline_jobs_get",
|
|
296
|
+
"List jobs for a CI pipeline (by pipeline id).",
|
|
297
|
+
gitlabPipelineGetSchema,
|
|
298
|
+
(parsed) =>
|
|
299
|
+
`/projects/${encodeURIComponent(parsed.projectPath)}/pipelines/${String(parsed.pipelineId)}/jobs`,
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
const gitlabJobTraceSchema = projectPathArg.extend({
|
|
303
|
+
jobId: z.number().int().min(1),
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
reg(
|
|
307
|
+
"gitlab_job_trace",
|
|
308
|
+
"Download plain-text trace for a CI job (by job id).",
|
|
309
|
+
gitlabJobTraceSchema,
|
|
310
|
+
async (parsed) => {
|
|
311
|
+
const token = requireProcessEnv("GITLAB_PAT");
|
|
312
|
+
const enc = encodeURIComponent(parsed.projectPath);
|
|
313
|
+
const url = `${apiBase()}/projects/${enc}/jobs/${String(parsed.jobId)}/trace`;
|
|
314
|
+
const res = await fetch(url, { headers: { "PRIVATE-TOKEN": token } });
|
|
315
|
+
const text = await res.text();
|
|
316
|
+
if (!res.ok) {
|
|
317
|
+
throw new Error(`GitLab ${String(res.status)}: ${text.slice(0, 300)}`);
|
|
318
|
+
}
|
|
319
|
+
return jsonResult({ trace: text });
|
|
320
|
+
},
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
reg(
|
|
324
|
+
"gitlab_job_log_tail",
|
|
325
|
+
"Download job trace text, optionally keeping only the last N characters (tail).",
|
|
326
|
+
gitlabJobTraceSchema.extend({
|
|
327
|
+
maxChars: z.number().int().min(1000).max(500_000).optional(),
|
|
328
|
+
}),
|
|
329
|
+
async (parsed) => {
|
|
330
|
+
const token = requireProcessEnv("GITLAB_PAT");
|
|
331
|
+
const enc = encodeURIComponent(parsed.projectPath);
|
|
332
|
+
const url = `${apiBase()}/projects/${enc}/jobs/${String(parsed.jobId)}/trace`;
|
|
333
|
+
const res = await fetch(url, { headers: { "PRIVATE-TOKEN": token } });
|
|
334
|
+
const text = await res.text();
|
|
335
|
+
if (!res.ok) {
|
|
336
|
+
throw new Error(`GitLab ${String(res.status)}: ${text.slice(0, 300)}`);
|
|
337
|
+
}
|
|
338
|
+
const max = parsed.maxChars ?? 64_000;
|
|
339
|
+
const tail = text.length > max ? text.slice(-max) : text;
|
|
340
|
+
return jsonResult({
|
|
341
|
+
jobId: parsed.jobId,
|
|
342
|
+
truncated: text.length > max,
|
|
343
|
+
totalChars: text.length,
|
|
344
|
+
trace: tail,
|
|
345
|
+
});
|
|
346
|
+
},
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
registerWriteTool(
|
|
350
|
+
"gitlab_pipeline_retry",
|
|
351
|
+
{
|
|
352
|
+
mutates: "gitlab.pipeline.retry",
|
|
353
|
+
recoverable: true,
|
|
354
|
+
scopeTargetOf: (p) => ({ kind: "repo", value: p.projectPath }),
|
|
355
|
+
},
|
|
356
|
+
"Retry failed jobs in a pipeline.",
|
|
357
|
+
gitlabPipelineGetSchema,
|
|
358
|
+
async (parsed) => {
|
|
359
|
+
const token = requireProcessEnv("GITLAB_PAT");
|
|
360
|
+
const enc = encodeURIComponent(parsed.projectPath);
|
|
361
|
+
const path = `/projects/${enc}/pipelines/${String(parsed.pipelineId)}/retry`;
|
|
362
|
+
const res = await glFetch(token, path, { method: "POST" });
|
|
363
|
+
if (!res.ok) {
|
|
364
|
+
throw new Error(`GitLab pipeline retry ${String(res.status)}: ${res.text.slice(0, 400)}`);
|
|
365
|
+
}
|
|
366
|
+
return mcpJsonResultIfOk("GitLab", res);
|
|
367
|
+
},
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
registerWriteTool(
|
|
371
|
+
"gitlab_pipeline_cancel",
|
|
372
|
+
{
|
|
373
|
+
mutates: "gitlab.pipeline.cancel",
|
|
374
|
+
recoverable: true,
|
|
375
|
+
scopeTargetOf: (p) => ({ kind: "repo", value: p.projectPath }),
|
|
376
|
+
},
|
|
377
|
+
"Cancel a pipeline.",
|
|
378
|
+
gitlabPipelineGetSchema,
|
|
379
|
+
async (parsed) => {
|
|
380
|
+
const token = requireProcessEnv("GITLAB_PAT");
|
|
381
|
+
const enc = encodeURIComponent(parsed.projectPath);
|
|
382
|
+
const path = `/projects/${enc}/pipelines/${String(parsed.pipelineId)}/cancel`;
|
|
383
|
+
const res = await glFetch(token, path, { method: "POST" });
|
|
384
|
+
if (!res.ok) {
|
|
385
|
+
throw new Error(`GitLab pipeline cancel ${String(res.status)}: ${res.text.slice(0, 400)}`);
|
|
386
|
+
}
|
|
387
|
+
return mcpJsonResultIfOk("GitLab", res);
|
|
388
|
+
},
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
const transport = new StdioServerTransport();
|
|
392
|
+
await server.connect(transport);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "com.nimbus.gmail",
|
|
3
|
+
"displayName": "Gmail",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Gmail REST connector (messages, threads, labels, search). Mutations require Gateway HITL.",
|
|
6
|
+
"author": "Nimbus",
|
|
7
|
+
"entrypoint": "dist/server.js",
|
|
8
|
+
"runtime": "bun",
|
|
9
|
+
"permissions": {
|
|
10
|
+
"network": ["www.googleapis.com", "gmail.googleapis.com", "oauth2.googleapis.com"]
|
|
11
|
+
},
|
|
12
|
+
"hitlRequired": ["write", "delete"],
|
|
13
|
+
"syncInterval": 120,
|
|
14
|
+
"minNimbusVersion": "0.2.0"
|
|
15
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { headerLine } from "../../../shared/header-safe.ts";
|
|
5
|
+
import {
|
|
6
|
+
createRegisterSimpleTool,
|
|
7
|
+
createZodToolRegistrar,
|
|
8
|
+
mcpJsonResultIfOk,
|
|
9
|
+
requireProcessEnv,
|
|
10
|
+
type ZodObjectSchema,
|
|
11
|
+
} from "../../../shared/mcp-tool-kit.ts";
|
|
12
|
+
import { makeRestFetcher, makeRestToolRegistrar } from "../../../shared/rest-tool-kit.ts";
|
|
13
|
+
|
|
14
|
+
const GMAIL_BASE = "https://gmail.googleapis.com/gmail/v1/users/me";
|
|
15
|
+
|
|
16
|
+
function gmailFetch(
|
|
17
|
+
token: string,
|
|
18
|
+
path: string,
|
|
19
|
+
init?: RequestInit,
|
|
20
|
+
): Promise<{ ok: boolean; status: number; json: unknown; text: string }> {
|
|
21
|
+
return makeRestFetcher({ apiBase: GMAIL_BASE, token })(path, init);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function buildRfc822Message(params: {
|
|
25
|
+
to: string;
|
|
26
|
+
subject: string;
|
|
27
|
+
body: string;
|
|
28
|
+
cc?: string;
|
|
29
|
+
bcc?: string;
|
|
30
|
+
}): string {
|
|
31
|
+
const lines: string[] = [
|
|
32
|
+
`To: ${params.to}`,
|
|
33
|
+
...(params.cc !== undefined && params.cc !== "" ? [`Cc: ${params.cc}`] : []),
|
|
34
|
+
...(params.bcc !== undefined && params.bcc !== "" ? [`Bcc: ${params.bcc}`] : []),
|
|
35
|
+
`Subject: ${params.subject}`,
|
|
36
|
+
"Content-Type: text/plain; charset=UTF-8",
|
|
37
|
+
"",
|
|
38
|
+
params.body,
|
|
39
|
+
];
|
|
40
|
+
return lines.join("\r\n");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function toRawBase64Url(rfc822: string): string {
|
|
44
|
+
return Buffer.from(rfc822, "utf-8").toString("base64url");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const server = new McpServer({ name: "nimbus-gmail", version: "0.1.0" });
|
|
48
|
+
|
|
49
|
+
const reg = createZodToolRegistrar(createRegisterSimpleTool(server));
|
|
50
|
+
|
|
51
|
+
/** Standard Gmail tool: token → gmailFetch(buildPath[, buildInit]) → mcpJsonResultIfOk("Gmail API", …, 200). */
|
|
52
|
+
import { createWriteToolRegistrar, type WriteToolConfig } from "../../../shared/consent-kit.ts";
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Every MUTATING gmail tool goes through here. Outside the gateway this adds the
|
|
56
|
+
* consent gate, the write-scope allow-list, the mutation budget and the audit record; inside
|
|
57
|
+
* the gateway it is a pass-through, because executor.ts (I2) is the gate there.
|
|
58
|
+
*/
|
|
59
|
+
const registerWriteTool = createWriteToolRegistrar(server, {
|
|
60
|
+
connector: "gmail",
|
|
61
|
+
scopeEnv: "NIMBUS_MCP_GMAIL_WRITE_SCOPE",
|
|
62
|
+
scopeKinds: ["recipient", "draft"],
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The write-tool equivalent of `registerGmailTool`: identical fetch and result handling, routed
|
|
67
|
+
* through the write registrar.
|
|
68
|
+
*/
|
|
69
|
+
function registerGmailWriteTool<T>(
|
|
70
|
+
name: string,
|
|
71
|
+
cfg: WriteToolConfig<T>,
|
|
72
|
+
description: string,
|
|
73
|
+
schema: ZodObjectSchema<T>,
|
|
74
|
+
buildPath: (p: T) => string,
|
|
75
|
+
buildInit?: (p: T) => RequestInit,
|
|
76
|
+
): void {
|
|
77
|
+
registerWriteTool(name, cfg, description, schema, async (parsed) => {
|
|
78
|
+
const token = requireProcessEnv("GOOGLE_OAUTH_ACCESS_TOKEN");
|
|
79
|
+
const res = await gmailFetch(token, buildPath(parsed), buildInit?.(parsed));
|
|
80
|
+
return mcpJsonResultIfOk("Gmail API", res, 200);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const registerGmailTool = makeRestToolRegistrar({
|
|
85
|
+
registrar: reg,
|
|
86
|
+
tokenEnv: "GOOGLE_OAUTH_ACCESS_TOKEN",
|
|
87
|
+
serviceLabel: "Gmail API",
|
|
88
|
+
fetch: gmailFetch,
|
|
89
|
+
snippetMax: 200,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const gmailMessageListArgs = z.object({
|
|
93
|
+
maxResults: z.number().int().min(1).max(100).optional(),
|
|
94
|
+
pageToken: z.string().optional(),
|
|
95
|
+
q: z.string().max(500).optional(),
|
|
96
|
+
labelIds: z.array(z.string()).optional(),
|
|
97
|
+
includeSpamTrash: z.boolean().optional(),
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
registerGmailTool(
|
|
101
|
+
"gmail_message_list",
|
|
102
|
+
"List Gmail message ids (metadata). Optional Gmail search query `q` (same syntax as Gmail UI).",
|
|
103
|
+
gmailMessageListArgs,
|
|
104
|
+
(data) => {
|
|
105
|
+
const u = new URL(`${GMAIL_BASE}/messages`);
|
|
106
|
+
u.searchParams.set("maxResults", String(data.maxResults ?? 25));
|
|
107
|
+
if (data.pageToken !== undefined && data.pageToken !== "") {
|
|
108
|
+
u.searchParams.set("pageToken", data.pageToken);
|
|
109
|
+
}
|
|
110
|
+
if (data.q !== undefined && data.q !== "") {
|
|
111
|
+
u.searchParams.set("q", data.q);
|
|
112
|
+
}
|
|
113
|
+
if (data.labelIds !== undefined) {
|
|
114
|
+
for (const lid of data.labelIds) {
|
|
115
|
+
u.searchParams.append("labelIds", lid);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (data.includeSpamTrash === true) {
|
|
119
|
+
u.searchParams.set("includeSpamTrash", "true");
|
|
120
|
+
}
|
|
121
|
+
return u.toString();
|
|
122
|
+
},
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const gmailMessageReadArgs = z.object({
|
|
126
|
+
messageId: z.string().min(1),
|
|
127
|
+
format: z.enum(["minimal", "full", "metadata", "raw"]).optional(),
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
registerGmailTool(
|
|
131
|
+
"gmail_message_read",
|
|
132
|
+
"Read a single Gmail message (format minimal | metadata | full | raw).",
|
|
133
|
+
gmailMessageReadArgs,
|
|
134
|
+
(data) => {
|
|
135
|
+
const fmt = data.format ?? "metadata";
|
|
136
|
+
const u = new URL(`${GMAIL_BASE}/messages/${encodeURIComponent(data.messageId)}`);
|
|
137
|
+
u.searchParams.set("format", fmt);
|
|
138
|
+
if (fmt === "metadata") {
|
|
139
|
+
u.searchParams.append("metadataHeaders", "Subject");
|
|
140
|
+
u.searchParams.append("metadataHeaders", "From");
|
|
141
|
+
u.searchParams.append("metadataHeaders", "To");
|
|
142
|
+
u.searchParams.append("metadataHeaders", "Date");
|
|
143
|
+
}
|
|
144
|
+
return u.toString();
|
|
145
|
+
},
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
const gmailThreadReadArgs = z.object({
|
|
149
|
+
threadId: z.string().min(1),
|
|
150
|
+
format: z.enum(["minimal", "full", "metadata"]).optional(),
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
registerGmailTool(
|
|
154
|
+
"gmail_thread_read",
|
|
155
|
+
"Read a Gmail thread and its messages.",
|
|
156
|
+
gmailThreadReadArgs,
|
|
157
|
+
(data) => {
|
|
158
|
+
const fmt = data.format ?? "metadata";
|
|
159
|
+
const u = new URL(`${GMAIL_BASE}/threads/${encodeURIComponent(data.threadId)}`);
|
|
160
|
+
u.searchParams.set("format", fmt);
|
|
161
|
+
return u.toString();
|
|
162
|
+
},
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
const gmailLabelListArgs = z.object({});
|
|
166
|
+
|
|
167
|
+
registerGmailTool(
|
|
168
|
+
"gmail_label_list",
|
|
169
|
+
"List all Gmail labels.",
|
|
170
|
+
gmailLabelListArgs,
|
|
171
|
+
() => `${GMAIL_BASE}/labels`,
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
const gmailDraftCreateArgs = z.object({
|
|
175
|
+
to: headerLine({ min: 1 }),
|
|
176
|
+
subject: headerLine({ min: 1, max: 998 }),
|
|
177
|
+
body: z.string().max(1_000_000),
|
|
178
|
+
cc: headerLine().optional(),
|
|
179
|
+
bcc: headerLine().optional(),
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
registerGmailWriteTool(
|
|
183
|
+
"gmail_draft_create",
|
|
184
|
+
{
|
|
185
|
+
mutates: "gmail.draft.create",
|
|
186
|
+
recoverable: true,
|
|
187
|
+
scopeTargetOf: (p) => ({ kind: "recipient", value: p.to }),
|
|
188
|
+
},
|
|
189
|
+
"Create a Gmail draft. Requires Gateway HITL email.draft.create.",
|
|
190
|
+
gmailDraftCreateArgs,
|
|
191
|
+
() => `${GMAIL_BASE}/drafts`,
|
|
192
|
+
(data) => {
|
|
193
|
+
const msgParams: { to: string; subject: string; body: string; cc?: string; bcc?: string } = {
|
|
194
|
+
to: data.to,
|
|
195
|
+
subject: data.subject,
|
|
196
|
+
body: data.body,
|
|
197
|
+
};
|
|
198
|
+
if (data.cc !== undefined && data.cc !== "") {
|
|
199
|
+
msgParams.cc = data.cc;
|
|
200
|
+
}
|
|
201
|
+
if (data.bcc !== undefined && data.bcc !== "") {
|
|
202
|
+
msgParams.bcc = data.bcc;
|
|
203
|
+
}
|
|
204
|
+
const raw = toRawBase64Url(buildRfc822Message(msgParams));
|
|
205
|
+
return {
|
|
206
|
+
method: "POST",
|
|
207
|
+
headers: { "Content-Type": "application/json" },
|
|
208
|
+
body: JSON.stringify({ message: { raw } }),
|
|
209
|
+
};
|
|
210
|
+
},
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
const gmailDraftSendArgs = z.object({
|
|
214
|
+
draftId: z.string().min(1),
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
registerGmailWriteTool(
|
|
218
|
+
"gmail_draft_send",
|
|
219
|
+
{
|
|
220
|
+
mutates: "gmail.draft.send",
|
|
221
|
+
recoverable: false,
|
|
222
|
+
capturePreState: (p) => Promise.resolve({ draftId: p.draftId }),
|
|
223
|
+
scopeTargetOf: (p) => ({ kind: "draft", value: p.draftId }),
|
|
224
|
+
},
|
|
225
|
+
"Send an existing Gmail draft by id. Requires Gateway HITL email.draft.send.",
|
|
226
|
+
gmailDraftSendArgs,
|
|
227
|
+
() => `${GMAIL_BASE}/drafts/send`,
|
|
228
|
+
(data) => ({
|
|
229
|
+
method: "POST",
|
|
230
|
+
headers: { "Content-Type": "application/json" },
|
|
231
|
+
body: JSON.stringify({ id: data.draftId }),
|
|
232
|
+
}),
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
const gmailMessageSendArgs = z.object({
|
|
236
|
+
to: headerLine({ min: 1 }),
|
|
237
|
+
subject: headerLine({ min: 1, max: 998 }),
|
|
238
|
+
body: z.string().max(1_000_000),
|
|
239
|
+
cc: headerLine().optional(),
|
|
240
|
+
bcc: headerLine().optional(),
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
registerGmailWriteTool(
|
|
244
|
+
"gmail_message_send",
|
|
245
|
+
{
|
|
246
|
+
mutates: "gmail.message.send",
|
|
247
|
+
recoverable: false,
|
|
248
|
+
// A sent mail cannot be recalled and nothing remains to query, so the recipient and subject
|
|
249
|
+
// ARE the pre-state.
|
|
250
|
+
capturePreState: (p) => Promise.resolve({ to: p.to, subject: p.subject }),
|
|
251
|
+
scopeTargetOf: (p) => ({ kind: "recipient", value: p.to }),
|
|
252
|
+
},
|
|
253
|
+
"Send a new Gmail message (not a draft). Requires Gateway HITL email.send.",
|
|
254
|
+
gmailMessageSendArgs,
|
|
255
|
+
() => `${GMAIL_BASE}/messages/send`,
|
|
256
|
+
(data) => {
|
|
257
|
+
const sendParams: { to: string; subject: string; body: string; cc?: string; bcc?: string } = {
|
|
258
|
+
to: data.to,
|
|
259
|
+
subject: data.subject,
|
|
260
|
+
body: data.body,
|
|
261
|
+
};
|
|
262
|
+
if (data.cc !== undefined && data.cc !== "") {
|
|
263
|
+
sendParams.cc = data.cc;
|
|
264
|
+
}
|
|
265
|
+
if (data.bcc !== undefined && data.bcc !== "") {
|
|
266
|
+
sendParams.bcc = data.bcc;
|
|
267
|
+
}
|
|
268
|
+
const raw = toRawBase64Url(buildRfc822Message(sendParams));
|
|
269
|
+
return {
|
|
270
|
+
method: "POST",
|
|
271
|
+
headers: { "Content-Type": "application/json" },
|
|
272
|
+
body: JSON.stringify({ raw }),
|
|
273
|
+
};
|
|
274
|
+
},
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
await server.connect(new StdioServerTransport());
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "com.nimbus.google-drive",
|
|
3
|
+
"displayName": "Google Drive",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Google Drive v3 connector (files, folders, search). Mutations require Gateway HITL.",
|
|
6
|
+
"author": "Nimbus",
|
|
7
|
+
"entrypoint": "dist/server.js",
|
|
8
|
+
"runtime": "bun",
|
|
9
|
+
"permissions": {
|
|
10
|
+
"network": ["www.googleapis.com", "oauth2.googleapis.com", "accounts.google.com"]
|
|
11
|
+
},
|
|
12
|
+
"hitlRequired": ["write", "delete"],
|
|
13
|
+
"syncInterval": 300,
|
|
14
|
+
"minNimbusVersion": "0.2.0"
|
|
15
|
+
}
|