@juvantlabs/m365-graph-mcp-server 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/ARCHITECTURE.md +225 -0
- package/CHANGELOG.md +188 -0
- package/LICENSE +21 -0
- package/README.md +164 -0
- package/SECURITY.md +64 -0
- package/dist/auth/confirmation_tokens.d.ts +38 -0
- package/dist/auth/confirmation_tokens.d.ts.map +1 -0
- package/dist/auth/confirmation_tokens.js +85 -0
- package/dist/auth/confirmation_tokens.js.map +1 -0
- package/dist/auth/keyring.d.ts +20 -0
- package/dist/auth/keyring.d.ts.map +1 -0
- package/dist/auth/keyring.js +41 -0
- package/dist/auth/keyring.js.map +1 -0
- package/dist/auth/msal.d.ts +42 -0
- package/dist/auth/msal.d.ts.map +1 -0
- package/dist/auth/msal.js +96 -0
- package/dist/auth/msal.js.map +1 -0
- package/dist/auth/setup.d.ts +18 -0
- package/dist/auth/setup.d.ts.map +1 -0
- package/dist/auth/setup.js +110 -0
- package/dist/auth/setup.js.map +1 -0
- package/dist/client/graph.d.ts +30 -0
- package/dist/client/graph.d.ts.map +1 -0
- package/dist/client/graph.js +38 -0
- package/dist/client/graph.js.map +1 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +131 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/cancel_event.d.ts +18 -0
- package/dist/tools/cancel_event.d.ts.map +1 -0
- package/dist/tools/cancel_event.js +95 -0
- package/dist/tools/cancel_event.js.map +1 -0
- package/dist/tools/copy_file.d.ts +39 -0
- package/dist/tools/copy_file.d.ts.map +1 -0
- package/dist/tools/copy_file.js +168 -0
- package/dist/tools/copy_file.js.map +1 -0
- package/dist/tools/create_event.d.ts +29 -0
- package/dist/tools/create_event.d.ts.map +1 -0
- package/dist/tools/create_event.js +144 -0
- package/dist/tools/create_event.js.map +1 -0
- package/dist/tools/decline_event.d.ts +18 -0
- package/dist/tools/decline_event.d.ts.map +1 -0
- package/dist/tools/decline_event.js +105 -0
- package/dist/tools/decline_event.js.map +1 -0
- package/dist/tools/delete_file.d.ts +28 -0
- package/dist/tools/delete_file.d.ts.map +1 -0
- package/dist/tools/delete_file.js +103 -0
- package/dist/tools/delete_file.js.map +1 -0
- package/dist/tools/download_file.d.ts +43 -0
- package/dist/tools/download_file.d.ts.map +1 -0
- package/dist/tools/download_file.js +133 -0
- package/dist/tools/download_file.js.map +1 -0
- package/dist/tools/get_event.d.ts +27 -0
- package/dist/tools/get_event.d.ts.map +1 -0
- package/dist/tools/get_event.js +55 -0
- package/dist/tools/get_event.js.map +1 -0
- package/dist/tools/index.d.ts +13 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +61 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/list_calendars.d.ts +26 -0
- package/dist/tools/list_calendars.d.ts.map +1 -0
- package/dist/tools/list_calendars.js +60 -0
- package/dist/tools/list_calendars.js.map +1 -0
- package/dist/tools/list_drives.d.ts +27 -0
- package/dist/tools/list_drives.d.ts.map +1 -0
- package/dist/tools/list_drives.js +58 -0
- package/dist/tools/list_drives.js.map +1 -0
- package/dist/tools/list_events.d.ts +51 -0
- package/dist/tools/list_events.d.ts.map +1 -0
- package/dist/tools/list_events.js +119 -0
- package/dist/tools/list_events.js.map +1 -0
- package/dist/tools/list_items.d.ts +31 -0
- package/dist/tools/list_items.d.ts.map +1 -0
- package/dist/tools/list_items.js +81 -0
- package/dist/tools/list_items.js.map +1 -0
- package/dist/tools/move_file.d.ts +18 -0
- package/dist/tools/move_file.d.ts.map +1 -0
- package/dist/tools/move_file.js +60 -0
- package/dist/tools/move_file.js.map +1 -0
- package/dist/tools/search_events.d.ts +25 -0
- package/dist/tools/search_events.d.ts.map +1 -0
- package/dist/tools/search_events.js +71 -0
- package/dist/tools/search_events.js.map +1 -0
- package/dist/tools/search_events_content.d.ts +32 -0
- package/dist/tools/search_events_content.d.ts.map +1 -0
- package/dist/tools/search_events_content.js +106 -0
- package/dist/tools/search_events_content.js.map +1 -0
- package/dist/tools/search_files.d.ts +30 -0
- package/dist/tools/search_files.d.ts.map +1 -0
- package/dist/tools/search_files.js +82 -0
- package/dist/tools/search_files.js.map +1 -0
- package/dist/tools/update_event.d.ts +25 -0
- package/dist/tools/update_event.d.ts.map +1 -0
- package/dist/tools/update_event.js +123 -0
- package/dist/tools/update_event.js.map +1 -0
- package/dist/tools/upload_file.d.ts +38 -0
- package/dist/tools/upload_file.d.ts.map +1 -0
- package/dist/tools/upload_file.js +152 -0
- package/dist/tools/upload_file.js.map +1 -0
- package/dist/types/tool.d.ts +32 -0
- package/dist/types/tool.d.ts.map +1 -0
- package/dist/types/tool.js +10 -0
- package/dist/types/tool.js.map +1 -0
- package/dist/types/validators.d.ts +44 -0
- package/dist/types/validators.d.ts.map +1 -0
- package/dist/types/validators.js +78 -0
- package/dist/types/validators.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: m365-graph:upload_file
|
|
3
|
+
*
|
|
4
|
+
* Upload a local file to a drive. Auto-routes between two paths based
|
|
5
|
+
* on file size:
|
|
6
|
+
* - ≤ 4 MB → single PUT to `/items/{parent}:/{name}:/content`
|
|
7
|
+
* - > 4 MB → resumable upload session via OneDriveLargeFileUploadTask
|
|
8
|
+
* (10 MB chunks, automatic resume on transient errors)
|
|
9
|
+
*
|
|
10
|
+
* Required Graph scope: `Files.ReadWrite` (delegated). Writes to the
|
|
11
|
+
* cloud side. Reads from the caller-supplied `local_path` on the
|
|
12
|
+
* server's filesystem.
|
|
13
|
+
*
|
|
14
|
+
* Trust note: the agent supplies `local_path`. The MCP server runs as
|
|
15
|
+
* the user; the user trusts the agent. There's no server-side path
|
|
16
|
+
* sandbox for *uploads* (unlike downloads, which we sandbox to a
|
|
17
|
+
* known dir): an upload tool that refused arbitrary paths would be
|
|
18
|
+
* useless for the canonical "upload this file the user prepared"
|
|
19
|
+
* workflow. The absolute path is logged to stderr so operators can
|
|
20
|
+
* review via the MCP server logs.
|
|
21
|
+
*
|
|
22
|
+
* Size cap: 200 MB (handbook spec § Performance). Refused pre-read.
|
|
23
|
+
*
|
|
24
|
+
* Conflict behavior is parametric:
|
|
25
|
+
* - `fail` (default) — refuse if a file with that name exists
|
|
26
|
+
* - `replace` — overwrite the existing file (creates a new version)
|
|
27
|
+
* - `rename` — let Graph append a numeric suffix
|
|
28
|
+
*/
|
|
29
|
+
import type { Tool } from "../types/tool.js";
|
|
30
|
+
export declare const MAX_FILE_SIZE: number;
|
|
31
|
+
/**
|
|
32
|
+
* Throw if the file size exceeds the configured maximum. Extracted so
|
|
33
|
+
* this defense-in-depth check can be unit-tested independently of the
|
|
34
|
+
* actual fs + Graph integration in the handler.
|
|
35
|
+
*/
|
|
36
|
+
export declare function checkSizeCap(size: number): void;
|
|
37
|
+
export declare const uploadFileTool: Tool;
|
|
38
|
+
//# sourceMappingURL=upload_file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload_file.d.ts","sourceRoot":"","sources":["../../src/tools/upload_file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAmBH,OAAO,KAAK,EAAE,IAAI,EAA6C,MAAM,kBAAkB,CAAC;AAGxF,eAAO,MAAM,aAAa,QAAoB,CAAC;AAG/C;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAM/C;AAsJD,eAAO,MAAM,cAAc,EAAE,IAA8B,CAAC"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: m365-graph:upload_file
|
|
3
|
+
*
|
|
4
|
+
* Upload a local file to a drive. Auto-routes between two paths based
|
|
5
|
+
* on file size:
|
|
6
|
+
* - ≤ 4 MB → single PUT to `/items/{parent}:/{name}:/content`
|
|
7
|
+
* - > 4 MB → resumable upload session via OneDriveLargeFileUploadTask
|
|
8
|
+
* (10 MB chunks, automatic resume on transient errors)
|
|
9
|
+
*
|
|
10
|
+
* Required Graph scope: `Files.ReadWrite` (delegated). Writes to the
|
|
11
|
+
* cloud side. Reads from the caller-supplied `local_path` on the
|
|
12
|
+
* server's filesystem.
|
|
13
|
+
*
|
|
14
|
+
* Trust note: the agent supplies `local_path`. The MCP server runs as
|
|
15
|
+
* the user; the user trusts the agent. There's no server-side path
|
|
16
|
+
* sandbox for *uploads* (unlike downloads, which we sandbox to a
|
|
17
|
+
* known dir): an upload tool that refused arbitrary paths would be
|
|
18
|
+
* useless for the canonical "upload this file the user prepared"
|
|
19
|
+
* workflow. The absolute path is logged to stderr so operators can
|
|
20
|
+
* review via the MCP server logs.
|
|
21
|
+
*
|
|
22
|
+
* Size cap: 200 MB (handbook spec § Performance). Refused pre-read.
|
|
23
|
+
*
|
|
24
|
+
* Conflict behavior is parametric:
|
|
25
|
+
* - `fail` (default) — refuse if a file with that name exists
|
|
26
|
+
* - `replace` — overwrite the existing file (creates a new version)
|
|
27
|
+
* - `rename` — let Graph append a numeric suffix
|
|
28
|
+
*/
|
|
29
|
+
import fs from "node:fs/promises";
|
|
30
|
+
import { createReadStream } from "node:fs";
|
|
31
|
+
import path from "node:path";
|
|
32
|
+
import { OneDriveLargeFileUploadTask, StreamUpload, } from "@microsoft/microsoft-graph-client";
|
|
33
|
+
import { validateOptionalEnum, validateOptionalString, validateRequiredString, } from "../types/validators.js";
|
|
34
|
+
const SMALL_FILE_CAP = 4 * 1024 * 1024; // 4 MB — single-PUT threshold per Graph docs
|
|
35
|
+
export const MAX_FILE_SIZE = 200 * 1024 * 1024; // 200 MB hard cap (handbook spec)
|
|
36
|
+
const LARGE_FILE_RANGE = 10 * 1024 * 1024; // 10 MB chunks for resumable
|
|
37
|
+
/**
|
|
38
|
+
* Throw if the file size exceeds the configured maximum. Extracted so
|
|
39
|
+
* this defense-in-depth check can be unit-tested independently of the
|
|
40
|
+
* actual fs + Graph integration in the handler.
|
|
41
|
+
*/
|
|
42
|
+
export function checkSizeCap(size) {
|
|
43
|
+
if (size > MAX_FILE_SIZE) {
|
|
44
|
+
throw new Error(`File size (${size} bytes) exceeds the 200 MB cap. Refusing to upload.`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const CONFLICT_BEHAVIORS = ["fail", "replace", "rename"];
|
|
48
|
+
const definition = {
|
|
49
|
+
name: "m365-graph:upload_file",
|
|
50
|
+
description: "Upload a local file to a drive (OneDrive primary by default; SharePoint via drive_id). Auto-routes between single PUT (≤ 4 MB) and resumable upload (> 4 MB, 10 MB chunks) based on file size. Default conflict behavior is 'fail' — set to 'replace' or 'rename' to overwrite or auto-rename. 200 MB hard cap.",
|
|
51
|
+
inputSchema: {
|
|
52
|
+
type: "object",
|
|
53
|
+
properties: {
|
|
54
|
+
local_path: {
|
|
55
|
+
type: "string",
|
|
56
|
+
description: "Absolute or relative path to the local file to upload. Resolved to absolute before reading.",
|
|
57
|
+
},
|
|
58
|
+
drive_id: {
|
|
59
|
+
type: "string",
|
|
60
|
+
description: "Optional drive ID. Defaults to the user's primary OneDrive.",
|
|
61
|
+
},
|
|
62
|
+
parent_item_id: {
|
|
63
|
+
type: "string",
|
|
64
|
+
description: "Optional folder ID to upload INTO. Defaults to the drive root. Get from list_items.",
|
|
65
|
+
},
|
|
66
|
+
name: {
|
|
67
|
+
type: "string",
|
|
68
|
+
description: "Optional name for the uploaded file. Defaults to the basename of local_path.",
|
|
69
|
+
},
|
|
70
|
+
conflict_behavior: {
|
|
71
|
+
type: "string",
|
|
72
|
+
enum: ["fail", "replace", "rename"],
|
|
73
|
+
description: "What to do if a file with this name already exists. Default: 'fail'.",
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
required: ["local_path"],
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
function summarizeUploadedItem(item) {
|
|
80
|
+
return {
|
|
81
|
+
id: String(item.id ?? ""),
|
|
82
|
+
name: String(item.name ?? ""),
|
|
83
|
+
size: Number(item.size ?? 0),
|
|
84
|
+
webUrl: String(item.webUrl ?? ""),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const handler = async (graph, args) => {
|
|
88
|
+
const localPathRaw = validateRequiredString(args.local_path, "local_path");
|
|
89
|
+
const localPath = path.resolve(localPathRaw);
|
|
90
|
+
const driveId = validateOptionalString(args.drive_id, "drive_id");
|
|
91
|
+
const parentItemId = validateOptionalString(args.parent_item_id, "parent_item_id");
|
|
92
|
+
const customName = validateOptionalString(args.name, "name");
|
|
93
|
+
const conflictBehavior = validateOptionalEnum(args.conflict_behavior, "conflict_behavior", CONFLICT_BEHAVIORS, "fail");
|
|
94
|
+
const stat = await fs.stat(localPath);
|
|
95
|
+
if (!stat.isFile()) {
|
|
96
|
+
throw new Error(`local_path is not a regular file: ${localPath}`);
|
|
97
|
+
}
|
|
98
|
+
checkSizeCap(stat.size);
|
|
99
|
+
const filename = customName ?? path.basename(localPath);
|
|
100
|
+
const drivePath = driveId ? `/drives/${encodeURIComponent(driveId)}` : "/me/drive";
|
|
101
|
+
const parentApi = parentItemId
|
|
102
|
+
? `${drivePath}/items/${encodeURIComponent(parentItemId)}`
|
|
103
|
+
: `${drivePath}/root`;
|
|
104
|
+
console.error(`[m365-graph-mcp-server] upload_file: ${localPath} (${stat.size} bytes) → ${drivePath}/${parentItemId ?? "<root>"}/${filename}`);
|
|
105
|
+
let uploadedItem;
|
|
106
|
+
let uploadPath;
|
|
107
|
+
if (stat.size <= SMALL_FILE_CAP) {
|
|
108
|
+
// Small file: single PUT to /items/{parent}:/{name}:/content
|
|
109
|
+
const content = await fs.readFile(localPath);
|
|
110
|
+
uploadedItem = (await graph
|
|
111
|
+
.api(`${parentApi}:/${encodeURIComponent(filename)}:/content`)
|
|
112
|
+
.query({ "@microsoft.graph.conflictBehavior": conflictBehavior })
|
|
113
|
+
.put(content));
|
|
114
|
+
uploadPath = "single_put";
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
// Large file: resumable upload session, 10 MB chunks.
|
|
118
|
+
const stream = createReadStream(localPath);
|
|
119
|
+
const fileObject = new StreamUpload(stream, filename, stat.size);
|
|
120
|
+
// Graph upload-session URL pattern:
|
|
121
|
+
// /me/drive/root:/<name>:/createUploadSession (root)
|
|
122
|
+
// /me/drive/items/<parent-id>:/<name>:/createUploadSession (sub-folder)
|
|
123
|
+
// /drives/<drive-id>/... (non-default drive)
|
|
124
|
+
const sessionUrl = parentItemId
|
|
125
|
+
? `${parentApi}:/${encodeURIComponent(filename)}:/createUploadSession`
|
|
126
|
+
: `${parentApi}:/${encodeURIComponent(filename)}:/createUploadSession`;
|
|
127
|
+
const taskOptions = {
|
|
128
|
+
rangeSize: LARGE_FILE_RANGE,
|
|
129
|
+
};
|
|
130
|
+
const uploadSession = await OneDriveLargeFileUploadTask.createUploadSession(graph, sessionUrl, {
|
|
131
|
+
fileName: filename,
|
|
132
|
+
conflictBehavior,
|
|
133
|
+
});
|
|
134
|
+
const uploadTask = new OneDriveLargeFileUploadTask(graph, fileObject, uploadSession, taskOptions);
|
|
135
|
+
const taskResult = await uploadTask.upload();
|
|
136
|
+
uploadedItem = (taskResult.responseBody ?? {});
|
|
137
|
+
uploadPath = "resumable_session";
|
|
138
|
+
}
|
|
139
|
+
const result = {
|
|
140
|
+
drive_id: driveId ?? null,
|
|
141
|
+
parent_item_id: parentItemId ?? null,
|
|
142
|
+
uploaded: {
|
|
143
|
+
...summarizeUploadedItem(uploadedItem),
|
|
144
|
+
upload_path: uploadPath,
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
return {
|
|
148
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
export const uploadFileTool = { definition, handler };
|
|
152
|
+
//# sourceMappingURL=upload_file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload_file.js","sourceRoot":"","sources":["../../src/tools/upload_file.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for MCP tool definitions in this server.
|
|
3
|
+
*
|
|
4
|
+
* Each tool exports a `definition` (the static metadata returned by
|
|
5
|
+
* tools/list) and a `handler` (the function invoked by tools/call).
|
|
6
|
+
* The dispatcher in src/index.ts switches on the tool name to call
|
|
7
|
+
* the right handler.
|
|
8
|
+
*/
|
|
9
|
+
import type { Client } from "@microsoft/microsoft-graph-client";
|
|
10
|
+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
11
|
+
export interface ToolDefinition {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
inputSchema: {
|
|
15
|
+
type: "object";
|
|
16
|
+
properties: Record<string, unknown>;
|
|
17
|
+
required: string[];
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Return shape for a tool handler. We use the SDK's `CallToolResult`
|
|
22
|
+
* directly so the dispatcher in `src/index.ts` can return handler
|
|
23
|
+
* results to `setRequestHandler(CallToolRequestSchema, …)` without a
|
|
24
|
+
* type assertion.
|
|
25
|
+
*/
|
|
26
|
+
export type ToolResponse = CallToolResult;
|
|
27
|
+
export type ToolHandler = (graph: Client, args: Record<string, unknown>) => Promise<ToolResponse>;
|
|
28
|
+
export interface Tool {
|
|
29
|
+
definition: ToolDefinition;
|
|
30
|
+
handler: ToolHandler;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/types/tool.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ,CAAC;QACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACpC,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;CACH;AAED;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,cAAc,CAAC;AAE1C,MAAM,MAAM,WAAW,GAAG,CACxB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC1B,OAAO,CAAC,YAAY,CAAC,CAAC;AAE3B,MAAM,WAAW,IAAI;IACnB,UAAU,EAAE,cAAc,CAAC;IAC3B,OAAO,EAAE,WAAW,CAAC;CACtB"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for MCP tool definitions in this server.
|
|
3
|
+
*
|
|
4
|
+
* Each tool exports a `definition` (the static metadata returned by
|
|
5
|
+
* tools/list) and a `handler` (the function invoked by tools/call).
|
|
6
|
+
* The dispatcher in src/index.ts switches on the tool name to call
|
|
7
|
+
* the right handler.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool.js","sourceRoot":"","sources":["../../src/types/tool.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input validators for MCP tool handlers.
|
|
3
|
+
*
|
|
4
|
+
* Tool handlers receive `Record<string, unknown>` from the MCP SDK and
|
|
5
|
+
* must validate each field before passing it to Graph. These helpers
|
|
6
|
+
* centralize the validation logic + give consistent error messages
|
|
7
|
+
* the agent can act on.
|
|
8
|
+
*
|
|
9
|
+
* Naming convention: every exported helper starts with `validate*`,
|
|
10
|
+
* which the CI dead-code grep enforces is imported elsewhere in src/.
|
|
11
|
+
* Defense-in-depth code that's never wired into a real handler is a
|
|
12
|
+
* security smell (handbook anti-pattern S1).
|
|
13
|
+
*/
|
|
14
|
+
export declare function validateRequiredString(value: unknown, fieldName: string): string;
|
|
15
|
+
export declare function validateOptionalString(value: unknown, fieldName: string): string | undefined;
|
|
16
|
+
export declare function validateOptionalInteger(value: unknown, fieldName: string, options: {
|
|
17
|
+
min: number;
|
|
18
|
+
max: number;
|
|
19
|
+
default: number;
|
|
20
|
+
}): number;
|
|
21
|
+
export declare function validateOptionalEnum<T extends string>(value: unknown, fieldName: string, allowed: ReadonlyArray<T>, defaultValue: T): T;
|
|
22
|
+
/**
|
|
23
|
+
* Lightweight ISO 8601 date/datetime check. Accepts:
|
|
24
|
+
* - YYYY-MM-DD
|
|
25
|
+
* - YYYY-MM-DDTHH:MM
|
|
26
|
+
* - YYYY-MM-DDTHH:MM:SS
|
|
27
|
+
* - YYYY-MM-DDTHH:MM:SS.sss
|
|
28
|
+
* with optional Z or ±HH:MM timezone offset.
|
|
29
|
+
*
|
|
30
|
+
* Microsoft Graph does the strict parsing; we just guard against
|
|
31
|
+
* obviously-wrong inputs so the agent gets a near-the-input error
|
|
32
|
+
* rather than a cryptic Graph 400.
|
|
33
|
+
*/
|
|
34
|
+
export declare function validateRequiredISODate(value: unknown, fieldName: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Strip path-traversal-dangerous characters from a filename. Used
|
|
37
|
+
* before constructing a local FS path that includes a server-supplied
|
|
38
|
+
* filename (e.g. download_file caches the Graph item.name on disk).
|
|
39
|
+
*
|
|
40
|
+
* Defense-in-depth: combined with prefix check on path.resolve(), even
|
|
41
|
+
* a malicious filename ('../../etc/passwd') cannot escape the sandbox.
|
|
42
|
+
*/
|
|
43
|
+
export declare function sanitizeFilename(name: string): string;
|
|
44
|
+
//# sourceMappingURL=validators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../src/types/validators.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAKhF;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,SAAS,CAGpB;AAED,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACrD,MAAM,CAaR;AAID,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,MAAM,EACnD,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,EACzB,YAAY,EAAE,CAAC,GACd,CAAC,CAQH;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAQjF;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKrD"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input validators for MCP tool handlers.
|
|
3
|
+
*
|
|
4
|
+
* Tool handlers receive `Record<string, unknown>` from the MCP SDK and
|
|
5
|
+
* must validate each field before passing it to Graph. These helpers
|
|
6
|
+
* centralize the validation logic + give consistent error messages
|
|
7
|
+
* the agent can act on.
|
|
8
|
+
*
|
|
9
|
+
* Naming convention: every exported helper starts with `validate*`,
|
|
10
|
+
* which the CI dead-code grep enforces is imported elsewhere in src/.
|
|
11
|
+
* Defense-in-depth code that's never wired into a real handler is a
|
|
12
|
+
* security smell (handbook anti-pattern S1).
|
|
13
|
+
*/
|
|
14
|
+
export function validateRequiredString(value, fieldName) {
|
|
15
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
16
|
+
throw new Error(`'${fieldName}' must be a non-empty string`);
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
export function validateOptionalString(value, fieldName) {
|
|
21
|
+
if (value === undefined || value === null)
|
|
22
|
+
return undefined;
|
|
23
|
+
return validateRequiredString(value, fieldName);
|
|
24
|
+
}
|
|
25
|
+
export function validateOptionalInteger(value, fieldName, options) {
|
|
26
|
+
if (value === undefined || value === null)
|
|
27
|
+
return options.default;
|
|
28
|
+
if (typeof value !== "number" ||
|
|
29
|
+
!Number.isInteger(value) ||
|
|
30
|
+
value < options.min ||
|
|
31
|
+
value > options.max) {
|
|
32
|
+
throw new Error(`'${fieldName}' must be an integer between ${options.min} and ${options.max}`);
|
|
33
|
+
}
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
const ISO_DATE_LOOKS_LIKE = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:?\d{2})?)?$/;
|
|
37
|
+
export function validateOptionalEnum(value, fieldName, allowed, defaultValue) {
|
|
38
|
+
if (value === undefined || value === null)
|
|
39
|
+
return defaultValue;
|
|
40
|
+
if (typeof value !== "string" || !allowed.includes(value)) {
|
|
41
|
+
throw new Error(`'${fieldName}' must be one of ${JSON.stringify(allowed)}; got ${JSON.stringify(value)}`);
|
|
42
|
+
}
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Lightweight ISO 8601 date/datetime check. Accepts:
|
|
47
|
+
* - YYYY-MM-DD
|
|
48
|
+
* - YYYY-MM-DDTHH:MM
|
|
49
|
+
* - YYYY-MM-DDTHH:MM:SS
|
|
50
|
+
* - YYYY-MM-DDTHH:MM:SS.sss
|
|
51
|
+
* with optional Z or ±HH:MM timezone offset.
|
|
52
|
+
*
|
|
53
|
+
* Microsoft Graph does the strict parsing; we just guard against
|
|
54
|
+
* obviously-wrong inputs so the agent gets a near-the-input error
|
|
55
|
+
* rather than a cryptic Graph 400.
|
|
56
|
+
*/
|
|
57
|
+
export function validateRequiredISODate(value, fieldName) {
|
|
58
|
+
const s = validateRequiredString(value, fieldName);
|
|
59
|
+
if (!ISO_DATE_LOOKS_LIKE.test(s)) {
|
|
60
|
+
throw new Error(`'${fieldName}' must be an ISO 8601 date or datetime (e.g. '2026-05-04' or '2026-05-04T10:00:00Z'); got ${JSON.stringify(s)}`);
|
|
61
|
+
}
|
|
62
|
+
return s;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Strip path-traversal-dangerous characters from a filename. Used
|
|
66
|
+
* before constructing a local FS path that includes a server-supplied
|
|
67
|
+
* filename (e.g. download_file caches the Graph item.name on disk).
|
|
68
|
+
*
|
|
69
|
+
* Defense-in-depth: combined with prefix check on path.resolve(), even
|
|
70
|
+
* a malicious filename ('../../etc/passwd') cannot escape the sandbox.
|
|
71
|
+
*/
|
|
72
|
+
export function sanitizeFilename(name) {
|
|
73
|
+
return name
|
|
74
|
+
.replace(/[/\\\0]/g, "_")
|
|
75
|
+
.replace(/^\.+/, "_") // no leading dots
|
|
76
|
+
.slice(0, 200);
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=validators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validators.js","sourceRoot":"","sources":["../../src/types/validators.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,UAAU,sBAAsB,CAAC,KAAc,EAAE,SAAiB;IACtE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,IAAI,SAAS,8BAA8B,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,KAAc,EACd,SAAiB;IAEjB,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5D,OAAO,sBAAsB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,KAAc,EACd,SAAiB,EACjB,OAAsD;IAEtD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC;IAClE,IACE,OAAO,KAAK,KAAK,QAAQ;QACzB,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC;QACxB,KAAK,GAAG,OAAO,CAAC,GAAG;QACnB,KAAK,GAAG,OAAO,CAAC,GAAG,EACnB,CAAC;QACD,MAAM,IAAI,KAAK,CACb,IAAI,SAAS,gCAAgC,OAAO,CAAC,GAAG,QAAQ,OAAO,CAAC,GAAG,EAAE,CAC9E,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,mBAAmB,GAAG,0EAA0E,CAAC;AAEvG,MAAM,UAAU,oBAAoB,CAClC,KAAc,EACd,SAAiB,EACjB,OAAyB,EACzB,YAAe;IAEf,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,YAAY,CAAC;IAC/D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAE,OAAiC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACrF,MAAM,IAAI,KAAK,CACb,IAAI,SAAS,oBAAoB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CACzF,CAAC;IACJ,CAAC;IACD,OAAO,KAAU,CAAC;AACpB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAc,EAAE,SAAiB;IACvE,MAAM,CAAC,GAAG,sBAAsB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IACnD,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACb,IAAI,SAAS,6FAA6F,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAC9H,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,IAAI;SACR,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC;SACxB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,kBAAkB;SACvC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACnB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@juvantlabs/m365-graph-mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Microsoft Graph MCP server — OneDrive, SharePoint, and Calendar read+write for Juvant OS agents.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Juvant Srls",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"bin": {
|
|
10
|
+
"m365-graph-mcp-server": "dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist/",
|
|
14
|
+
"README.md",
|
|
15
|
+
"ARCHITECTURE.md",
|
|
16
|
+
"CHANGELOG.md",
|
|
17
|
+
"SECURITY.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=20"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc --pretty false",
|
|
25
|
+
"prepublishOnly": "npm run lint && npm run typecheck && npm run test:unit && npm run build",
|
|
26
|
+
"start": "node dist/index.js",
|
|
27
|
+
"dev": "tsx --env-file=.env.local src/index.ts",
|
|
28
|
+
"setup": "tsx --env-file=.env.local src/index.ts setup",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"test:unit": "vitest run tests/unit --passWithNoTests",
|
|
31
|
+
"test:integration": "vitest run tests/integration --passWithNoTests",
|
|
32
|
+
"test:watch": "vitest",
|
|
33
|
+
"lint": "eslint src tests --no-error-on-unmatched-pattern",
|
|
34
|
+
"typecheck": "tsc --noEmit",
|
|
35
|
+
"audit": "npm audit --audit-level=moderate"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@azure/msal-node": "^5.0.0",
|
|
39
|
+
"@microsoft/microsoft-graph-client": "^3.0.7",
|
|
40
|
+
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
41
|
+
"@napi-rs/keyring": "^1.3.0",
|
|
42
|
+
"isomorphic-fetch": "^3.0.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/isomorphic-fetch": "^0.0.39",
|
|
46
|
+
"@types/node": "^20.0.0",
|
|
47
|
+
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
48
|
+
"@typescript-eslint/parser": "^8.0.0",
|
|
49
|
+
"@vitest/coverage-v8": "^3.0.0",
|
|
50
|
+
"eslint": "^9.0.0",
|
|
51
|
+
"tsx": "^4.0.0",
|
|
52
|
+
"typescript": "^5.4.0",
|
|
53
|
+
"vitest": "^3.0.0"
|
|
54
|
+
},
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public"
|
|
57
|
+
},
|
|
58
|
+
"repository": {
|
|
59
|
+
"type": "git",
|
|
60
|
+
"url": "git+https://github.com/juvantlabs/m365-graph-mcp-server.git"
|
|
61
|
+
},
|
|
62
|
+
"homepage": "https://github.com/juvantlabs/m365-graph-mcp-server#readme",
|
|
63
|
+
"bugs": {
|
|
64
|
+
"url": "https://github.com/juvantlabs/m365-graph-mcp-server/issues"
|
|
65
|
+
},
|
|
66
|
+
"keywords": [
|
|
67
|
+
"mcp",
|
|
68
|
+
"model-context-protocol",
|
|
69
|
+
"m365-graph",
|
|
70
|
+
"juvant-os"
|
|
71
|
+
]
|
|
72
|
+
}
|