@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,256 +1,9 @@
|
|
|
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 { resolveUrlWithBase } from "../../../shared/fetch-bearer-json.ts";
|
|
6
|
-
import {
|
|
7
|
-
createRegisterSimpleTool,
|
|
8
|
-
createZodToolRegistrar,
|
|
9
|
-
mcpJsonResult,
|
|
10
|
-
mcpJsonResultIfOk,
|
|
11
|
-
requireProcessEnv,
|
|
12
|
-
type ZodObjectSchema,
|
|
13
|
-
} from "../../../shared/mcp-tool-kit.ts";
|
|
14
|
-
import { makeRestToolRegistrar } from "../../../shared/rest-tool-kit.ts";
|
|
15
|
-
|
|
16
|
-
const GRAPH = "https://graph.microsoft.com/v1.0";
|
|
17
|
-
|
|
18
|
-
async function graphRequest(
|
|
19
|
-
token: string,
|
|
20
|
-
pathOrUrl: string,
|
|
21
|
-
init?: RequestInit,
|
|
22
|
-
): Promise<{ ok: boolean; status: number; json: unknown; text: string; bytes: number }> {
|
|
23
|
-
const url = resolveUrlWithBase(GRAPH, pathOrUrl);
|
|
24
|
-
const mergedHeaders = new Headers({ Authorization: `Bearer ${token}` });
|
|
25
|
-
if (init?.headers !== undefined) {
|
|
26
|
-
const extra = new Headers(init.headers);
|
|
27
|
-
for (const [k, v] of extra) {
|
|
28
|
-
mergedHeaders.set(k, v);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
const res = await fetch(url, {
|
|
32
|
-
...init,
|
|
33
|
-
headers: mergedHeaders,
|
|
34
|
-
});
|
|
35
|
-
const buf = await res.arrayBuffer();
|
|
36
|
-
const text = new TextDecoder("utf-8", { fatal: false }).decode(buf);
|
|
37
|
-
let json: unknown;
|
|
38
|
-
try {
|
|
39
|
-
json = JSON.parse(text) as unknown;
|
|
40
|
-
} catch {
|
|
41
|
-
json = null;
|
|
42
|
-
}
|
|
43
|
-
return { ok: res.ok, status: res.status, json, text, bytes: buf.byteLength };
|
|
44
|
-
}
|
|
3
|
+
import { registerOnedriveTools } from "./tools.ts";
|
|
45
4
|
|
|
46
5
|
const server = new McpServer({ name: "nimbus-onedrive", version: "0.1.0" });
|
|
47
6
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const reg = createZodToolRegistrar(createRegisterSimpleTool(server));
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Every MUTATING onedrive tool goes through here. Outside the gateway this adds the
|
|
54
|
-
* consent gate, the write-scope allow-list, the mutation budget and the audit record; inside
|
|
55
|
-
* the gateway it is a pass-through, because executor.ts (I2) is the gate there.
|
|
56
|
-
*/
|
|
57
|
-
const registerWriteTool = createWriteToolRegistrar(server, {
|
|
58
|
-
connector: "onedrive",
|
|
59
|
-
scopeEnv: "NIMBUS_MCP_ONEDRIVE_WRITE_SCOPE",
|
|
60
|
-
scopeKinds: ["item"],
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
/** Standard Graph tool: token → graphRequest(buildPath[, buildInit]) → mcpJsonResultIfOk("Graph", …, 200). */
|
|
64
|
-
/**
|
|
65
|
-
* The write-tool equivalent of `registerOnedriveTool`: identical fetch and result handling, routed
|
|
66
|
-
* through the write registrar.
|
|
67
|
-
*/
|
|
68
|
-
function registerOnedriveWriteTool<T>(
|
|
69
|
-
name: string,
|
|
70
|
-
cfg: WriteToolConfig<T>,
|
|
71
|
-
description: string,
|
|
72
|
-
schema: ZodObjectSchema<T>,
|
|
73
|
-
buildPath: (p: T) => string,
|
|
74
|
-
buildInit?: (p: T) => RequestInit,
|
|
75
|
-
): void {
|
|
76
|
-
registerWriteTool(name, cfg, description, schema, async (parsed) => {
|
|
77
|
-
const token = requireProcessEnv("MICROSOFT_OAUTH_ACCESS_TOKEN");
|
|
78
|
-
const res = await graphRequest(token, buildPath(parsed), buildInit?.(parsed));
|
|
79
|
-
return mcpJsonResultIfOk("Graph", res, 200);
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const registerOnedriveTool = makeRestToolRegistrar({
|
|
84
|
-
registrar: reg,
|
|
85
|
-
tokenEnv: "MICROSOFT_OAUTH_ACCESS_TOKEN",
|
|
86
|
-
serviceLabel: "Graph",
|
|
87
|
-
fetch: graphRequest,
|
|
88
|
-
snippetMax: 200,
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
const onedriveItemListArgs = z.object({
|
|
92
|
-
parentId: z.string().min(1).optional(),
|
|
93
|
-
pageSize: z.number().int().min(1).max(200).optional(),
|
|
94
|
-
nextLink: z.url().optional(),
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
registerOnedriveTool(
|
|
98
|
-
"onedrive_item_list",
|
|
99
|
-
"List drive items under root or a folder (by parentId). Use nextLink from prior response for pagination.",
|
|
100
|
-
onedriveItemListArgs,
|
|
101
|
-
(data) => {
|
|
102
|
-
if (data.nextLink !== undefined && data.nextLink !== "") {
|
|
103
|
-
return data.nextLink;
|
|
104
|
-
}
|
|
105
|
-
const pageSize = data.pageSize ?? 50;
|
|
106
|
-
if (data.parentId !== undefined && data.parentId !== "") {
|
|
107
|
-
return `/me/drive/items/${encodeURIComponent(data.parentId)}/children?$top=${String(pageSize)}`;
|
|
108
|
-
}
|
|
109
|
-
return `/me/drive/root/children?$top=${String(pageSize)}`;
|
|
110
|
-
},
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
const onedriveItemGetArgs = z.object({
|
|
114
|
-
itemId: z.string().min(1),
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
registerOnedriveTool(
|
|
118
|
-
"onedrive_item_get",
|
|
119
|
-
"Get OneDrive item metadata by id (file or folder).",
|
|
120
|
-
onedriveItemGetArgs,
|
|
121
|
-
(data) => `/me/drive/items/${encodeURIComponent(data.itemId)}`,
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
const onedriveItemDownloadArgs = z.object({
|
|
125
|
-
itemId: z.string().min(1),
|
|
126
|
-
maxBytes: z
|
|
127
|
-
.number()
|
|
128
|
-
.int()
|
|
129
|
-
.min(1024)
|
|
130
|
-
.max(8 * 1024 * 1024)
|
|
131
|
-
.optional(),
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
reg(
|
|
135
|
-
"onedrive_item_download",
|
|
136
|
-
"Download file content as base64 (capped by maxBytes, default 256 KiB). Folders are rejected.",
|
|
137
|
-
onedriveItemDownloadArgs,
|
|
138
|
-
async (data) => {
|
|
139
|
-
const token = requireProcessEnv("MICROSOFT_OAUTH_ACCESS_TOKEN");
|
|
140
|
-
const maxBytes = data.maxBytes ?? 256 * 1024;
|
|
141
|
-
const meta = await graphRequest(
|
|
142
|
-
token,
|
|
143
|
-
`/me/drive/items/${encodeURIComponent(data.itemId)}?$select=id,name,folder,file`,
|
|
144
|
-
);
|
|
145
|
-
if (!meta.ok) {
|
|
146
|
-
throw new Error(`Graph ${String(meta.status)}: ${meta.text.slice(0, 200)}`);
|
|
147
|
-
}
|
|
148
|
-
const m = meta.json;
|
|
149
|
-
if (m === null || typeof m !== "object" || Array.isArray(m)) {
|
|
150
|
-
throw new Error("Invalid metadata response");
|
|
151
|
-
}
|
|
152
|
-
const folder = (m as Record<string, unknown>)["folder"];
|
|
153
|
-
if (folder !== undefined && folder !== null) {
|
|
154
|
-
throw new Error("Item is a folder; download applies to files only");
|
|
155
|
-
}
|
|
156
|
-
const res = await fetch(`${GRAPH}/me/drive/items/${encodeURIComponent(data.itemId)}/content`, {
|
|
157
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
158
|
-
});
|
|
159
|
-
const buf = await res.arrayBuffer();
|
|
160
|
-
if (!res.ok) {
|
|
161
|
-
const err = new TextDecoder().decode(buf);
|
|
162
|
-
throw new Error(`Graph ${String(res.status)}: ${err.slice(0, 200)}`);
|
|
163
|
-
}
|
|
164
|
-
const truncated = buf.byteLength > maxBytes;
|
|
165
|
-
const slice = truncated ? buf.slice(0, maxBytes) : buf;
|
|
166
|
-
const b64 = Buffer.from(slice).toString("base64");
|
|
167
|
-
const out = {
|
|
168
|
-
itemId: data.itemId,
|
|
169
|
-
encoding: "base64" as const,
|
|
170
|
-
truncated,
|
|
171
|
-
byteLength: buf.byteLength,
|
|
172
|
-
returnedBytes: slice.byteLength,
|
|
173
|
-
content: b64,
|
|
174
|
-
};
|
|
175
|
-
return mcpJsonResult(out);
|
|
176
|
-
},
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
const onedriveItemSearchArgs = z.object({
|
|
180
|
-
query: z.string().min(1).max(500),
|
|
181
|
-
pageSize: z.number().int().min(1).max(100).optional(),
|
|
182
|
-
nextLink: z.url().optional(),
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
registerOnedriveTool(
|
|
186
|
-
"onedrive_item_search",
|
|
187
|
-
"Search OneDrive under /me/drive/root/search.",
|
|
188
|
-
onedriveItemSearchArgs,
|
|
189
|
-
(data) => {
|
|
190
|
-
if (data.nextLink !== undefined && data.nextLink !== "") {
|
|
191
|
-
return data.nextLink;
|
|
192
|
-
}
|
|
193
|
-
const pageSize = data.pageSize ?? 25;
|
|
194
|
-
const escaped = data.query.replaceAll("'", "''");
|
|
195
|
-
return `/me/drive/root/search(q='${escaped}')?$top=${String(pageSize)}`;
|
|
196
|
-
},
|
|
197
|
-
);
|
|
198
|
-
|
|
199
|
-
const onedriveItemDeleteArgs = z.object({
|
|
200
|
-
itemId: z.string().min(1),
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
registerWriteTool(
|
|
204
|
-
"onedrive_item_delete",
|
|
205
|
-
{
|
|
206
|
-
mutates: "onedrive.item.delete",
|
|
207
|
-
recoverable: false,
|
|
208
|
-
capturePreState: (p) => Promise.resolve({ itemId: p.itemId }),
|
|
209
|
-
scopeTargetOf: (p) => ({ kind: "item", value: p.itemId }),
|
|
210
|
-
},
|
|
211
|
-
"Permanently delete a OneDrive item. Requires Gateway HITL onedrive.delete.",
|
|
212
|
-
onedriveItemDeleteArgs,
|
|
213
|
-
async (data) => {
|
|
214
|
-
const token = requireProcessEnv("MICROSOFT_OAUTH_ACCESS_TOKEN");
|
|
215
|
-
const r = await graphRequest(token, `/me/drive/items/${encodeURIComponent(data.itemId)}`, {
|
|
216
|
-
method: "DELETE",
|
|
217
|
-
});
|
|
218
|
-
if (!r.ok && r.status !== 204) {
|
|
219
|
-
throw new Error(`Graph ${String(r.status)}: ${r.text.slice(0, 200)}`);
|
|
220
|
-
}
|
|
221
|
-
return mcpJsonResult({ ok: true });
|
|
222
|
-
},
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
const onedriveItemMoveArgs = z.object({
|
|
226
|
-
itemId: z.string().min(1),
|
|
227
|
-
newParentId: z.string().min(1),
|
|
228
|
-
newName: z.string().min(1).max(500).optional(),
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
registerOnedriveWriteTool(
|
|
232
|
-
"onedrive_item_move",
|
|
233
|
-
{
|
|
234
|
-
mutates: "onedrive.item.move",
|
|
235
|
-
recoverable: true,
|
|
236
|
-
scopeTargetOf: (p) => ({ kind: "item", value: p.itemId }),
|
|
237
|
-
},
|
|
238
|
-
"Move (and optionally rename) a drive item. Requires Gateway HITL onedrive.move.",
|
|
239
|
-
onedriveItemMoveArgs,
|
|
240
|
-
(data) => `/me/drive/items/${encodeURIComponent(data.itemId)}`,
|
|
241
|
-
(data) => {
|
|
242
|
-
const body: Record<string, unknown> = {
|
|
243
|
-
parentReference: { id: data.newParentId },
|
|
244
|
-
};
|
|
245
|
-
if (data.newName !== undefined) {
|
|
246
|
-
body["name"] = data.newName;
|
|
247
|
-
}
|
|
248
|
-
return {
|
|
249
|
-
method: "PATCH",
|
|
250
|
-
headers: { "Content-Type": "application/json" },
|
|
251
|
-
body: JSON.stringify(body),
|
|
252
|
-
};
|
|
253
|
-
},
|
|
254
|
-
);
|
|
7
|
+
registerOnedriveTools(server);
|
|
255
8
|
|
|
256
9
|
await server.connect(new StdioServerTransport());
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type ConsentServer, createWriteToolRegistrar } from "../../../shared/consent-kit.ts";
|
|
3
|
+
import { resolveUrlWithBase } from "../../../shared/fetch-bearer-json.ts";
|
|
4
|
+
import {
|
|
5
|
+
createRegisterSimpleTool,
|
|
6
|
+
createZodToolRegistrar,
|
|
7
|
+
mcpJsonResult,
|
|
8
|
+
requireProcessEnv,
|
|
9
|
+
} from "../../../shared/mcp-tool-kit.ts";
|
|
10
|
+
import {
|
|
11
|
+
makeRestToolRegistrar,
|
|
12
|
+
makeRestWriteToolRegistrar,
|
|
13
|
+
} from "../../../shared/rest-tool-kit.ts";
|
|
14
|
+
|
|
15
|
+
const GRAPH = "https://graph.microsoft.com/v1.0";
|
|
16
|
+
|
|
17
|
+
async function graphRequest(
|
|
18
|
+
token: string,
|
|
19
|
+
pathOrUrl: string,
|
|
20
|
+
init?: RequestInit,
|
|
21
|
+
): Promise<{ ok: boolean; status: number; json: unknown; text: string; bytes: number }> {
|
|
22
|
+
const url = resolveUrlWithBase(GRAPH, pathOrUrl);
|
|
23
|
+
const mergedHeaders = new Headers({ Authorization: `Bearer ${token}` });
|
|
24
|
+
if (init?.headers !== undefined) {
|
|
25
|
+
const extra = new Headers(init.headers);
|
|
26
|
+
for (const [k, v] of extra) {
|
|
27
|
+
mergedHeaders.set(k, v);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const res = await fetch(url, {
|
|
31
|
+
...init,
|
|
32
|
+
headers: mergedHeaders,
|
|
33
|
+
});
|
|
34
|
+
const buf = await res.arrayBuffer();
|
|
35
|
+
const text = new TextDecoder("utf-8", { fatal: false }).decode(buf);
|
|
36
|
+
let json: unknown;
|
|
37
|
+
try {
|
|
38
|
+
json = JSON.parse(text) as unknown;
|
|
39
|
+
} catch {
|
|
40
|
+
json = null;
|
|
41
|
+
}
|
|
42
|
+
return { ok: res.ok, status: res.status, json, text, bytes: buf.byteLength };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Tool names exposed by this connector — for contract/introspection tests. */
|
|
46
|
+
export const ONEDRIVE_TOOL_NAMES = [
|
|
47
|
+
"onedrive_item_list",
|
|
48
|
+
"onedrive_item_get",
|
|
49
|
+
"onedrive_item_download",
|
|
50
|
+
"onedrive_item_search",
|
|
51
|
+
"onedrive_item_delete",
|
|
52
|
+
"onedrive_item_move",
|
|
53
|
+
] as const;
|
|
54
|
+
|
|
55
|
+
export function registerOnedriveTools(
|
|
56
|
+
server: ConsentServer & { tool: (...args: never) => unknown },
|
|
57
|
+
): void {
|
|
58
|
+
const reg = createZodToolRegistrar(createRegisterSimpleTool(server));
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Every MUTATING onedrive tool goes through here. Outside the gateway this adds the
|
|
62
|
+
* consent gate, the write-scope allow-list, the mutation budget and the audit record; inside
|
|
63
|
+
* the gateway it is a pass-through, because executor.ts (I2) is the gate there.
|
|
64
|
+
*/
|
|
65
|
+
const registerWriteTool = createWriteToolRegistrar(server, {
|
|
66
|
+
connector: "onedrive",
|
|
67
|
+
scopeEnv: "NIMBUS_MCP_ONEDRIVE_WRITE_SCOPE",
|
|
68
|
+
scopeKinds: ["item"],
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Standard Graph tool, read or write: token → graphRequest(buildPath[, buildInit]) →
|
|
73
|
+
* mcpJsonResultIfOk("Graph", …, 200).
|
|
74
|
+
*/
|
|
75
|
+
const graphRest = {
|
|
76
|
+
tokenEnv: "MICROSOFT_OAUTH_ACCESS_TOKEN",
|
|
77
|
+
serviceLabel: "Graph",
|
|
78
|
+
fetch: graphRequest,
|
|
79
|
+
snippetMax: 200,
|
|
80
|
+
} as const;
|
|
81
|
+
|
|
82
|
+
/** The write-tool equivalent of `registerOnedriveTool`, routed through the write registrar. */
|
|
83
|
+
const registerOnedriveWriteTool = makeRestWriteToolRegistrar({ registerWriteTool, ...graphRest });
|
|
84
|
+
const registerOnedriveTool = makeRestToolRegistrar({ registrar: reg, ...graphRest });
|
|
85
|
+
|
|
86
|
+
const onedriveItemListArgs = z.object({
|
|
87
|
+
parentId: z.string().min(1).optional(),
|
|
88
|
+
pageSize: z.number().int().min(1).max(200).optional(),
|
|
89
|
+
nextLink: z.url().optional(),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
registerOnedriveTool(
|
|
93
|
+
"onedrive_item_list",
|
|
94
|
+
"List drive items under root or a folder (by parentId). Use nextLink from prior response for pagination.",
|
|
95
|
+
onedriveItemListArgs,
|
|
96
|
+
(data) => {
|
|
97
|
+
if (data.nextLink !== undefined && data.nextLink !== "") {
|
|
98
|
+
return data.nextLink;
|
|
99
|
+
}
|
|
100
|
+
const pageSize = data.pageSize ?? 50;
|
|
101
|
+
if (data.parentId !== undefined && data.parentId !== "") {
|
|
102
|
+
return `/me/drive/items/${encodeURIComponent(data.parentId)}/children?$top=${String(pageSize)}`;
|
|
103
|
+
}
|
|
104
|
+
return `/me/drive/root/children?$top=${String(pageSize)}`;
|
|
105
|
+
},
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const onedriveItemGetArgs = z.object({
|
|
109
|
+
itemId: z.string().min(1),
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
registerOnedriveTool(
|
|
113
|
+
"onedrive_item_get",
|
|
114
|
+
"Get OneDrive item metadata by id (file or folder).",
|
|
115
|
+
onedriveItemGetArgs,
|
|
116
|
+
(data) => `/me/drive/items/${encodeURIComponent(data.itemId)}`,
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const onedriveItemDownloadArgs = z.object({
|
|
120
|
+
itemId: z.string().min(1),
|
|
121
|
+
maxBytes: z
|
|
122
|
+
.number()
|
|
123
|
+
.int()
|
|
124
|
+
.min(1024)
|
|
125
|
+
.max(8 * 1024 * 1024)
|
|
126
|
+
.optional(),
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
reg(
|
|
130
|
+
"onedrive_item_download",
|
|
131
|
+
"Download file content as base64 (capped by maxBytes, default 256 KiB). Folders are rejected.",
|
|
132
|
+
onedriveItemDownloadArgs,
|
|
133
|
+
async (data) => {
|
|
134
|
+
const token = requireProcessEnv("MICROSOFT_OAUTH_ACCESS_TOKEN");
|
|
135
|
+
const maxBytes = data.maxBytes ?? 256 * 1024;
|
|
136
|
+
const meta = await graphRequest(
|
|
137
|
+
token,
|
|
138
|
+
`/me/drive/items/${encodeURIComponent(data.itemId)}?$select=id,name,folder,file`,
|
|
139
|
+
);
|
|
140
|
+
if (!meta.ok) {
|
|
141
|
+
throw new Error(`Graph ${String(meta.status)}: ${meta.text.slice(0, 200)}`);
|
|
142
|
+
}
|
|
143
|
+
const m = meta.json;
|
|
144
|
+
if (m === null || typeof m !== "object" || Array.isArray(m)) {
|
|
145
|
+
throw new Error("Invalid metadata response");
|
|
146
|
+
}
|
|
147
|
+
const folder = (m as Record<string, unknown>)["folder"];
|
|
148
|
+
if (folder !== undefined && folder !== null) {
|
|
149
|
+
throw new Error("Item is a folder; download applies to files only");
|
|
150
|
+
}
|
|
151
|
+
const res = await fetch(
|
|
152
|
+
`${GRAPH}/me/drive/items/${encodeURIComponent(data.itemId)}/content`,
|
|
153
|
+
{
|
|
154
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
155
|
+
},
|
|
156
|
+
);
|
|
157
|
+
const buf = await res.arrayBuffer();
|
|
158
|
+
if (!res.ok) {
|
|
159
|
+
const err = new TextDecoder().decode(buf);
|
|
160
|
+
throw new Error(`Graph ${String(res.status)}: ${err.slice(0, 200)}`);
|
|
161
|
+
}
|
|
162
|
+
const truncated = buf.byteLength > maxBytes;
|
|
163
|
+
const slice = truncated ? buf.slice(0, maxBytes) : buf;
|
|
164
|
+
const b64 = Buffer.from(slice).toString("base64");
|
|
165
|
+
const out = {
|
|
166
|
+
itemId: data.itemId,
|
|
167
|
+
encoding: "base64" as const,
|
|
168
|
+
truncated,
|
|
169
|
+
byteLength: buf.byteLength,
|
|
170
|
+
returnedBytes: slice.byteLength,
|
|
171
|
+
content: b64,
|
|
172
|
+
};
|
|
173
|
+
return mcpJsonResult(out);
|
|
174
|
+
},
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
const onedriveItemSearchArgs = z.object({
|
|
178
|
+
query: z.string().min(1).max(500),
|
|
179
|
+
pageSize: z.number().int().min(1).max(100).optional(),
|
|
180
|
+
nextLink: z.url().optional(),
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
registerOnedriveTool(
|
|
184
|
+
"onedrive_item_search",
|
|
185
|
+
"Search OneDrive under /me/drive/root/search.",
|
|
186
|
+
onedriveItemSearchArgs,
|
|
187
|
+
(data) => {
|
|
188
|
+
if (data.nextLink !== undefined && data.nextLink !== "") {
|
|
189
|
+
return data.nextLink;
|
|
190
|
+
}
|
|
191
|
+
const pageSize = data.pageSize ?? 25;
|
|
192
|
+
const escaped = data.query.replaceAll("'", "''");
|
|
193
|
+
return `/me/drive/root/search(q='${escaped}')?$top=${String(pageSize)}`;
|
|
194
|
+
},
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
const onedriveItemDeleteArgs = z.object({
|
|
198
|
+
itemId: z.string().min(1),
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
registerWriteTool(
|
|
202
|
+
"onedrive_item_delete",
|
|
203
|
+
{
|
|
204
|
+
mutates: "onedrive.item.delete",
|
|
205
|
+
recoverable: false,
|
|
206
|
+
capturePreState: (p) => Promise.resolve({ itemId: p.itemId }),
|
|
207
|
+
scopeTargetOf: (p) => ({ kind: "item", value: p.itemId }),
|
|
208
|
+
},
|
|
209
|
+
"Permanently delete a OneDrive item. Requires Gateway HITL onedrive.delete.",
|
|
210
|
+
onedriveItemDeleteArgs,
|
|
211
|
+
async (data) => {
|
|
212
|
+
const token = requireProcessEnv("MICROSOFT_OAUTH_ACCESS_TOKEN");
|
|
213
|
+
const r = await graphRequest(token, `/me/drive/items/${encodeURIComponent(data.itemId)}`, {
|
|
214
|
+
method: "DELETE",
|
|
215
|
+
});
|
|
216
|
+
if (!r.ok && r.status !== 204) {
|
|
217
|
+
throw new Error(`Graph ${String(r.status)}: ${r.text.slice(0, 200)}`);
|
|
218
|
+
}
|
|
219
|
+
return mcpJsonResult({ ok: true });
|
|
220
|
+
},
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
const onedriveItemMoveArgs = z.object({
|
|
224
|
+
itemId: z.string().min(1),
|
|
225
|
+
newParentId: z.string().min(1),
|
|
226
|
+
newName: z.string().min(1).max(500).optional(),
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
registerOnedriveWriteTool(
|
|
230
|
+
"onedrive_item_move",
|
|
231
|
+
{
|
|
232
|
+
mutates: "onedrive.item.move",
|
|
233
|
+
recoverable: true,
|
|
234
|
+
scopeTargetOf: (p) => ({ kind: "item", value: p.itemId }),
|
|
235
|
+
},
|
|
236
|
+
"Move (and optionally rename) a drive item. Requires Gateway HITL onedrive.move.",
|
|
237
|
+
onedriveItemMoveArgs,
|
|
238
|
+
(data) => `/me/drive/items/${encodeURIComponent(data.itemId)}`,
|
|
239
|
+
(data) => {
|
|
240
|
+
const body: Record<string, unknown> = {
|
|
241
|
+
parentReference: { id: data.newParentId },
|
|
242
|
+
};
|
|
243
|
+
if (data.newName !== undefined) {
|
|
244
|
+
body["name"] = data.newName;
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
method: "PATCH",
|
|
248
|
+
headers: { "Content-Type": "application/json" },
|
|
249
|
+
body: JSON.stringify(body),
|
|
250
|
+
};
|
|
251
|
+
},
|
|
252
|
+
);
|
|
253
|
+
}
|