@softeria/ms-365-mcp-server 0.145.0 → 0.145.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/dist/endpoints.json
CHANGED
|
@@ -212,7 +212,7 @@
|
|
|
212
212
|
"toolName": "add-mail-attachment",
|
|
213
213
|
"presets": ["mail", "outlook", "personal"],
|
|
214
214
|
"scopes": ["Mail.ReadWrite"],
|
|
215
|
-
"llmTip": "
|
|
215
|
+
"llmTip": "The only path for attachments under 3MB. contentBytes must carry the complete base64 verbatim; a truncated argument fails with 400 UnableToDeserializePostBody. At 3MB and above use create-mail-attachment-upload-session, which rejects anything smaller. Body requires @odata.type: {\"@odata.type\": \"#microsoft.graph.fileAttachment\", \"name\": \"file.pdf\", \"contentBytes\": \"<base64>\"}."
|
|
216
216
|
},
|
|
217
217
|
{
|
|
218
218
|
"pathPattern": "/me/messages/{message-id}/attachments/createUploadSession",
|
|
@@ -220,7 +220,7 @@
|
|
|
220
220
|
"toolName": "create-mail-attachment-upload-session",
|
|
221
221
|
"presets": ["mail", "outlook", "personal"],
|
|
222
222
|
"scopes": ["Mail.ReadWrite"],
|
|
223
|
-
"llmTip": "For
|
|
223
|
+
"llmTip": "For attachments 3MB to 150MB. Graph rejects smaller files with ErrorAttachmentSizeShouldNotBeLessThanMinimumSize, so under 3MB use add-mail-attachment instead. Body: { AttachmentItem: { attachmentType: 'file', name: 'report.pdf', size: 5000000 } }. Returns a pre-authenticated uploadUrl; the caller PUTs the bytes there itself in ranges up to 4MB. This server does not perform the PUT."
|
|
224
224
|
},
|
|
225
225
|
{
|
|
226
226
|
"pathPattern": "/me/messages/{message-id}/attachments",
|
|
@@ -607,7 +607,7 @@
|
|
|
607
607
|
"toolName": "upload-file-content",
|
|
608
608
|
"presets": ["files", "onedrive", "personal"],
|
|
609
609
|
"scopes": ["Files.ReadWrite"],
|
|
610
|
-
"llmTip": "Body is a base64-encoded string of the file bytes; the server decodes it before PUT.
|
|
610
|
+
"llmTip": "Body is a base64-encoded string of the file bytes; the server decodes it before PUT. Graph accepts up to 250MB here, but the whole string travels as a tool argument and a truncated one decodes to a truncated file with no error, so use create-upload-session rather than emitting a large base64 string. For new files use path format: /items/root:/path/to/file.txt:/content. Overwrites existing files without warning."
|
|
611
611
|
},
|
|
612
612
|
{
|
|
613
613
|
"pathPattern": "/drives/{drive-id}/items/{driveItem-id}/createUploadSession",
|
|
@@ -615,7 +615,7 @@
|
|
|
615
615
|
"toolName": "create-upload-session",
|
|
616
616
|
"presets": ["files", "onedrive", "personal"],
|
|
617
617
|
"scopes": ["Files.ReadWrite"],
|
|
618
|
-
"llmTip": "For large file uploads (no size limit). Returns a pre-authenticated uploadUrl
|
|
618
|
+
"llmTip": "For large file uploads (no size limit, and no minimum unlike the Outlook attachment session). Returns a pre-authenticated uploadUrl; the caller PUTs the bytes there itself. This server does not perform the PUT. For new files use path: /items/{parentId}:/{fileName}:/createUploadSession. Body (optional): { item: { '@microsoft.graph.conflictBehavior': 'rename' } }."
|
|
619
619
|
},
|
|
620
620
|
{
|
|
621
621
|
"pathPattern": "/drives/{drive-id}/items/{driveItem-id}",
|
|
@@ -2609,7 +2609,7 @@
|
|
|
2609
2609
|
"presets": ["personal"],
|
|
2610
2610
|
"scopes": ["User.ReadWrite"],
|
|
2611
2611
|
"contentType": "image/jpeg",
|
|
2612
|
-
"llmTip": "Uploads a new profile photo for the signed-in user. Body is a base64-encoded string of the image bytes (the server decodes before PUT). Photo must be JPEG, max 4 MB. Microsoft 365 generates HD downsized variants automatically (48x48, 64x64, 96x96, 120x120, 240x240, 360x360, 432x432, 504x504, 648x648). For work or school accounts, ProfilePhoto.ReadWrite.All is the more granular alternative permission. Use download-bytes with target=/me/photo/$value to retrieve the current photo."
|
|
2612
|
+
"llmTip": "Uploads a new profile photo for the signed-in user. Body is a base64-encoded string of the image bytes (the server decodes before PUT). Photo must be JPEG, max 4 MB; the base64 travels as a tool argument and a truncated one is written without error, so resize before encoding instead of emitting a large string. Microsoft 365 generates HD downsized variants automatically (48x48, 64x64, 96x96, 120x120, 240x240, 360x360, 432x432, 504x504, 648x648). For work or school accounts, ProfilePhoto.ReadWrite.All is the more granular alternative permission. Use download-bytes with target=/me/photo/$value to retrieve the current photo."
|
|
2613
2613
|
},
|
|
2614
2614
|
{
|
|
2615
2615
|
"pathPattern": "/me/todo/lists",
|
package/dist/generated/client.js
CHANGED
|
@@ -448,7 +448,9 @@ const microsoft_graph_chat = z.object({
|
|
|
448
448
|
permissionGrants: z.array(microsoft_graph_resourceSpecificPermissionGrant).describe("A collection of permissions granted to apps for the chat.").optional(),
|
|
449
449
|
pinnedMessages: z.array(microsoft_graph_pinnedChatMessageInfo).describe("A collection of all the pinned messages in the chat. Nullable.").optional(),
|
|
450
450
|
tabs: z.array(microsoft_graph_teamsTab).describe("A collection of all the tabs in the chat. Nullable.").optional(),
|
|
451
|
-
targetedMessages: z.array(microsoft_graph_targetedChatMessage).
|
|
451
|
+
targetedMessages: z.array(microsoft_graph_targetedChatMessage).describe(
|
|
452
|
+
"A collection of targeted messages in the chat that are visible only to specific users. Nullable. You can't expand this relationship using $expand. Targeted messages can also be retrieved via the userTeamwork: getAllTargetedMessages API."
|
|
453
|
+
).optional()
|
|
452
454
|
}).passthrough();
|
|
453
455
|
const microsoft_graph_ODataErrors_ErrorDetails = z.object({ code: z.string(), message: z.string(), target: z.string().nullish() }).passthrough();
|
|
454
456
|
const microsoft_graph_ODataErrors_InnerError = z.object({
|
package/dist/mcp-instructions.js
CHANGED
|
@@ -6,7 +6,7 @@ function buildGeneralMcpInstructions(opts) {
|
|
|
6
6
|
"When you need an organizational user or recipient address, resolve it with list-users (or another directory tool); do not invent SMTP addresses.",
|
|
7
7
|
"Directory $search on collections such as /users or /groups requires ConsistencyLevel: eventual when the tool exposes that header.",
|
|
8
8
|
"Teams chat and channel messages: prefer HTML contentType in the body; plain text is often mangled by Graph.",
|
|
9
|
-
"Files / binary content: for large drive/SharePoint file content, prefer get-download-url to resolve a pre-authenticated URL for out-of-band download. Use download-bytes for authenticated byte reads such as mail attachments, profile photos, Teams hosted content, and meeting recordings. In stdio mode, download-bytes-to-file writes those same authenticated bytes straight to a local absolute path instead of returning base64 \u2014 the only out-of-band option for large mail attachments and meeting recordings, which get-download-url cannot handle. These tools take relative Microsoft Graph paths, not absolute URLs. For uploads, upload-file-content takes a base64 string body
|
|
9
|
+
"Files / binary content: for large drive/SharePoint file content, prefer get-download-url to resolve a pre-authenticated URL for out-of-band download. Use download-bytes for authenticated byte reads such as mail attachments, profile photos, Teams hosted content, and meeting recordings. In stdio mode, download-bytes-to-file writes those same authenticated bytes straight to a local absolute path instead of returning base64 \u2014 the only out-of-band option for large mail attachments and meeting recordings, which get-download-url cannot handle. These tools take relative Microsoft Graph paths, not absolute URLs. For uploads, upload-file-content takes a base64 string body (Graph allows 250MB, but the whole string passes through the agent context and a truncated one is written without error); use create-upload-session for anything but small files."
|
|
10
10
|
];
|
|
11
11
|
if (opts.readOnly) parts.push("This server is read-only; write operations are disabled.");
|
|
12
12
|
if (opts.multiAccount)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
2
2
|
import logger from "./logger.js";
|
|
3
|
+
const MAX_INLINE_GROWTH = 2;
|
|
3
4
|
function unescapePointer(token) {
|
|
4
5
|
return token.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
5
6
|
}
|
|
@@ -51,6 +52,78 @@ function repointRefs(node, nameFor) {
|
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
54
|
}
|
|
55
|
+
function hasDef(defs, name) {
|
|
56
|
+
return Object.prototype.hasOwnProperty.call(defs, name);
|
|
57
|
+
}
|
|
58
|
+
function defRefName(value) {
|
|
59
|
+
const prefix = "#/$defs/";
|
|
60
|
+
return typeof value === "string" && value.startsWith(prefix) ? value.slice(prefix.length) : null;
|
|
61
|
+
}
|
|
62
|
+
function collectDefRefNames(node, out) {
|
|
63
|
+
if (!node || typeof node !== "object") return;
|
|
64
|
+
if (Array.isArray(node)) {
|
|
65
|
+
for (const item of node) collectDefRefNames(item, out);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
for (const [key, value] of Object.entries(node)) {
|
|
69
|
+
const name = key === "$ref" ? defRefName(value) : null;
|
|
70
|
+
if (name === null) collectDefRefNames(value, out);
|
|
71
|
+
else out.add(name);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function collectSiblingRefNames(node, out) {
|
|
75
|
+
if (!node || typeof node !== "object") return;
|
|
76
|
+
if (Array.isArray(node)) {
|
|
77
|
+
for (const item of node) collectSiblingRefNames(item, out);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const record = node;
|
|
81
|
+
const name = defRefName(record.$ref);
|
|
82
|
+
if (name !== null && Object.keys(record).length > 1) out.add(name);
|
|
83
|
+
for (const value of Object.values(record)) collectSiblingRefNames(value, out);
|
|
84
|
+
}
|
|
85
|
+
function findCyclicDefs(defs) {
|
|
86
|
+
const edges = /* @__PURE__ */ new Map();
|
|
87
|
+
for (const [name, value] of Object.entries(defs)) {
|
|
88
|
+
const out = /* @__PURE__ */ new Set();
|
|
89
|
+
collectDefRefNames(value, out);
|
|
90
|
+
edges.set(name, out);
|
|
91
|
+
}
|
|
92
|
+
const cyclic = /* @__PURE__ */ new Set();
|
|
93
|
+
for (const start of edges.keys()) {
|
|
94
|
+
const seen = /* @__PURE__ */ new Set();
|
|
95
|
+
const stack = [...edges.get(start) ?? []];
|
|
96
|
+
while (stack.length > 0) {
|
|
97
|
+
const next = stack.pop();
|
|
98
|
+
if (next === start) {
|
|
99
|
+
cyclic.add(start);
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
if (seen.has(next)) continue;
|
|
103
|
+
seen.add(next);
|
|
104
|
+
for (const onward of edges.get(next) ?? []) stack.push(onward);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return cyclic;
|
|
108
|
+
}
|
|
109
|
+
function inlineDefRefs(node, defs, pinned, expand) {
|
|
110
|
+
if (!node || typeof node !== "object") return node;
|
|
111
|
+
if (Array.isArray(node)) return node.map((item) => inlineDefRefs(item, defs, pinned, expand));
|
|
112
|
+
const record = node;
|
|
113
|
+
const name = defRefName(record.$ref);
|
|
114
|
+
if (name !== null && hasDef(defs, name) && !pinned.has(name)) {
|
|
115
|
+
const siblings = {};
|
|
116
|
+
for (const [key, value] of Object.entries(record)) {
|
|
117
|
+
if (key !== "$ref") siblings[key] = inlineDefRefs(value, defs, pinned, expand);
|
|
118
|
+
}
|
|
119
|
+
return { ...structuredClone(expand(name)), ...siblings };
|
|
120
|
+
}
|
|
121
|
+
const out = {};
|
|
122
|
+
for (const [key, value] of Object.entries(record)) {
|
|
123
|
+
out[key] = inlineDefRefs(value, defs, pinned, expand);
|
|
124
|
+
}
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
54
127
|
function normalizeToolSchemaRefs(schema) {
|
|
55
128
|
const targets = /* @__PURE__ */ new Set();
|
|
56
129
|
collectBadRefTargets(schema, targets);
|
|
@@ -85,8 +158,37 @@ function normalizeToolSchemaRefs(schema) {
|
|
|
85
158
|
repointRefs(clone, nameFor);
|
|
86
159
|
for (const name of Object.keys(defs)) repointRefs(defs[name], nameFor);
|
|
87
160
|
const existingDefs = clone.$defs ?? {};
|
|
161
|
+
delete clone.$defs;
|
|
162
|
+
const pinned = findCyclicDefs(defs);
|
|
163
|
+
collectSiblingRefNames(clone, pinned);
|
|
164
|
+
for (const value of Object.values(defs)) collectSiblingRefNames(value, pinned);
|
|
165
|
+
for (const value of Object.values(existingDefs)) collectSiblingRefNames(value, pinned);
|
|
166
|
+
for (const name of [...pinned]) {
|
|
167
|
+
if (!hasDef(defs, name)) pinned.delete(name);
|
|
168
|
+
}
|
|
169
|
+
if (pinned.size > 0) {
|
|
170
|
+
clone.$defs = { ...existingDefs, ...defs };
|
|
171
|
+
return clone;
|
|
172
|
+
}
|
|
173
|
+
const memo = /* @__PURE__ */ new Map();
|
|
174
|
+
const expand = (name) => {
|
|
175
|
+
if (memo.has(name)) return memo.get(name);
|
|
176
|
+
const value = inlineDefRefs(defs[name], defs, pinned, expand);
|
|
177
|
+
memo.set(name, value);
|
|
178
|
+
return value;
|
|
179
|
+
};
|
|
180
|
+
const inlined = inlineDefRefs(clone, defs, pinned, expand);
|
|
181
|
+
if (Object.keys(existingDefs).length > 0) {
|
|
182
|
+
inlined.$defs = Object.fromEntries(
|
|
183
|
+
Object.entries(existingDefs).map(([name, value]) => [
|
|
184
|
+
name,
|
|
185
|
+
inlineDefRefs(value, defs, pinned, expand)
|
|
186
|
+
])
|
|
187
|
+
);
|
|
188
|
+
}
|
|
88
189
|
clone.$defs = { ...existingDefs, ...defs };
|
|
89
|
-
|
|
190
|
+
const grewTooMuch = JSON.stringify(inlined).length > JSON.stringify(clone).length * MAX_INLINE_GROWTH;
|
|
191
|
+
return grewTooMuch ? clone : inlined;
|
|
90
192
|
}
|
|
91
193
|
function installToolSchemaRefNormalization(server) {
|
|
92
194
|
const lowLevel = server.server;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softeria/ms-365-mcp-server",
|
|
3
3
|
"mcpName": "io.github.Softeria/ms-365-mcp-server",
|
|
4
|
-
"version": "0.145.
|
|
4
|
+
"version": "0.145.2",
|
|
5
5
|
"description": " A Model Context Protocol (MCP) server for interacting with Microsoft 365 and Office services through the Graph API",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|
package/src/endpoints.json
CHANGED
|
@@ -212,7 +212,7 @@
|
|
|
212
212
|
"toolName": "add-mail-attachment",
|
|
213
213
|
"presets": ["mail", "outlook", "personal"],
|
|
214
214
|
"scopes": ["Mail.ReadWrite"],
|
|
215
|
-
"llmTip": "
|
|
215
|
+
"llmTip": "The only path for attachments under 3MB. contentBytes must carry the complete base64 verbatim; a truncated argument fails with 400 UnableToDeserializePostBody. At 3MB and above use create-mail-attachment-upload-session, which rejects anything smaller. Body requires @odata.type: {\"@odata.type\": \"#microsoft.graph.fileAttachment\", \"name\": \"file.pdf\", \"contentBytes\": \"<base64>\"}."
|
|
216
216
|
},
|
|
217
217
|
{
|
|
218
218
|
"pathPattern": "/me/messages/{message-id}/attachments/createUploadSession",
|
|
@@ -220,7 +220,7 @@
|
|
|
220
220
|
"toolName": "create-mail-attachment-upload-session",
|
|
221
221
|
"presets": ["mail", "outlook", "personal"],
|
|
222
222
|
"scopes": ["Mail.ReadWrite"],
|
|
223
|
-
"llmTip": "For
|
|
223
|
+
"llmTip": "For attachments 3MB to 150MB. Graph rejects smaller files with ErrorAttachmentSizeShouldNotBeLessThanMinimumSize, so under 3MB use add-mail-attachment instead. Body: { AttachmentItem: { attachmentType: 'file', name: 'report.pdf', size: 5000000 } }. Returns a pre-authenticated uploadUrl; the caller PUTs the bytes there itself in ranges up to 4MB. This server does not perform the PUT."
|
|
224
224
|
},
|
|
225
225
|
{
|
|
226
226
|
"pathPattern": "/me/messages/{message-id}/attachments",
|
|
@@ -607,7 +607,7 @@
|
|
|
607
607
|
"toolName": "upload-file-content",
|
|
608
608
|
"presets": ["files", "onedrive", "personal"],
|
|
609
609
|
"scopes": ["Files.ReadWrite"],
|
|
610
|
-
"llmTip": "Body is a base64-encoded string of the file bytes; the server decodes it before PUT.
|
|
610
|
+
"llmTip": "Body is a base64-encoded string of the file bytes; the server decodes it before PUT. Graph accepts up to 250MB here, but the whole string travels as a tool argument and a truncated one decodes to a truncated file with no error, so use create-upload-session rather than emitting a large base64 string. For new files use path format: /items/root:/path/to/file.txt:/content. Overwrites existing files without warning."
|
|
611
611
|
},
|
|
612
612
|
{
|
|
613
613
|
"pathPattern": "/drives/{drive-id}/items/{driveItem-id}/createUploadSession",
|
|
@@ -615,7 +615,7 @@
|
|
|
615
615
|
"toolName": "create-upload-session",
|
|
616
616
|
"presets": ["files", "onedrive", "personal"],
|
|
617
617
|
"scopes": ["Files.ReadWrite"],
|
|
618
|
-
"llmTip": "For large file uploads (no size limit). Returns a pre-authenticated uploadUrl
|
|
618
|
+
"llmTip": "For large file uploads (no size limit, and no minimum unlike the Outlook attachment session). Returns a pre-authenticated uploadUrl; the caller PUTs the bytes there itself. This server does not perform the PUT. For new files use path: /items/{parentId}:/{fileName}:/createUploadSession. Body (optional): { item: { '@microsoft.graph.conflictBehavior': 'rename' } }."
|
|
619
619
|
},
|
|
620
620
|
{
|
|
621
621
|
"pathPattern": "/drives/{drive-id}/items/{driveItem-id}",
|
|
@@ -2609,7 +2609,7 @@
|
|
|
2609
2609
|
"presets": ["personal"],
|
|
2610
2610
|
"scopes": ["User.ReadWrite"],
|
|
2611
2611
|
"contentType": "image/jpeg",
|
|
2612
|
-
"llmTip": "Uploads a new profile photo for the signed-in user. Body is a base64-encoded string of the image bytes (the server decodes before PUT). Photo must be JPEG, max 4 MB. Microsoft 365 generates HD downsized variants automatically (48x48, 64x64, 96x96, 120x120, 240x240, 360x360, 432x432, 504x504, 648x648). For work or school accounts, ProfilePhoto.ReadWrite.All is the more granular alternative permission. Use download-bytes with target=/me/photo/$value to retrieve the current photo."
|
|
2612
|
+
"llmTip": "Uploads a new profile photo for the signed-in user. Body is a base64-encoded string of the image bytes (the server decodes before PUT). Photo must be JPEG, max 4 MB; the base64 travels as a tool argument and a truncated one is written without error, so resize before encoding instead of emitting a large string. Microsoft 365 generates HD downsized variants automatically (48x48, 64x64, 96x96, 120x120, 240x240, 360x360, 432x432, 504x504, 648x648). For work or school accounts, ProfilePhoto.ReadWrite.All is the more granular alternative permission. Use download-bytes with target=/me/photo/$value to retrieve the current photo."
|
|
2613
2613
|
},
|
|
2614
2614
|
{
|
|
2615
2615
|
"pathPattern": "/me/todo/lists",
|