@softeria/ms-365-mcp-server 0.87.1 → 0.88.1
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.
|
@@ -14,6 +14,46 @@ export function createAndSaveSimplifiedOpenAPI(endpointsFile, openapiFile, opena
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
// Synthesize operations that the Graph REST API supports but are missing from
|
|
18
|
+
// Microsoft's published OpenAPI metadata (e.g. PATCH on range(address='{address}')
|
|
19
|
+
// for cell-value writes — documented in Excel API but not in the OpenAPI spec).
|
|
20
|
+
for (const endpoint of endpoints) {
|
|
21
|
+
const pathSpec = openApiSpec.paths[endpoint.pathPattern];
|
|
22
|
+
const methodLower = endpoint.method.toLowerCase();
|
|
23
|
+
if (pathSpec && !pathSpec[methodLower]) {
|
|
24
|
+
pathSpec[methodLower] = {
|
|
25
|
+
tags: ['drives.driveItem'],
|
|
26
|
+
summary: endpoint.llmTip || `${endpoint.toolName} (synthesized)`,
|
|
27
|
+
description: endpoint.llmTip || `${endpoint.toolName} (synthesized)`,
|
|
28
|
+
operationId: endpoint.toolName,
|
|
29
|
+
requestBody:
|
|
30
|
+
methodLower === 'get' || methodLower === 'delete'
|
|
31
|
+
? undefined
|
|
32
|
+
: {
|
|
33
|
+
description: 'Operation payload',
|
|
34
|
+
required: true,
|
|
35
|
+
content: {
|
|
36
|
+
'application/json': {
|
|
37
|
+
schema: { type: 'object', additionalProperties: true },
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
responses: {
|
|
42
|
+
'2XX': {
|
|
43
|
+
description: 'Success',
|
|
44
|
+
content: {
|
|
45
|
+
'application/json': {
|
|
46
|
+
schema: { type: 'object', additionalProperties: true },
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
'4XX': { $ref: '#/components/responses/error' },
|
|
51
|
+
'5XX': { $ref: '#/components/responses/error' },
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
17
57
|
for (const [key, value] of Object.entries(openApiSpec.paths)) {
|
|
18
58
|
const e = endpoints.filter((ep) => ep.pathPattern === key);
|
|
19
59
|
if (e.length === 0) {
|
|
@@ -395,4 +395,50 @@ describe("graph-tools", () => {
|
|
|
395
395
|
expect(tool.schema["timezone"]).toBeUndefined();
|
|
396
396
|
});
|
|
397
397
|
});
|
|
398
|
+
describe("outlook.body-content-type Prefer header", () => {
|
|
399
|
+
it('should set Prefer: outlook.body-content-type="text" on GET requests', async () => {
|
|
400
|
+
const endpoint = makeEndpoint({ method: "get" });
|
|
401
|
+
const config = makeConfig({ method: "get" });
|
|
402
|
+
mockEndpoints.push(endpoint);
|
|
403
|
+
mockEndpointsJson = [config];
|
|
404
|
+
const graphClient = createMockGraphClient([
|
|
405
|
+
{ content: [{ type: "text", text: JSON.stringify({ value: [] }) }] }
|
|
406
|
+
]);
|
|
407
|
+
const server = createMockServer();
|
|
408
|
+
const { registerGraphTools } = await loadModule();
|
|
409
|
+
registerGraphTools(server, graphClient);
|
|
410
|
+
await server.tools.get("test-tool").handler({});
|
|
411
|
+
const [, options] = graphClient.graphRequest.mock.calls[0];
|
|
412
|
+
expect(options.headers["Prefer"]).toContain('outlook.body-content-type="text"');
|
|
413
|
+
});
|
|
414
|
+
it("should NOT set Prefer: outlook.body-content-type on POST requests", async () => {
|
|
415
|
+
const endpoint = makeEndpoint({
|
|
416
|
+
alias: "create-reply-draft",
|
|
417
|
+
method: "post",
|
|
418
|
+
path: "/me/messages/:messageId/createReply",
|
|
419
|
+
parameters: [
|
|
420
|
+
{ name: "messageId", type: "Path", schema: z.string() },
|
|
421
|
+
{ name: "body", type: "Body", schema: z.any() }
|
|
422
|
+
]
|
|
423
|
+
});
|
|
424
|
+
const config = makeConfig({
|
|
425
|
+
toolName: "create-reply-draft",
|
|
426
|
+
method: "post",
|
|
427
|
+
pathPattern: "/me/messages/{message-id}/createReply"
|
|
428
|
+
});
|
|
429
|
+
mockEndpoints.push(endpoint);
|
|
430
|
+
mockEndpointsJson = [config];
|
|
431
|
+
const graphClient = createMockGraphClient([{ content: [{ type: "text", text: "{}" }] }]);
|
|
432
|
+
const server = createMockServer();
|
|
433
|
+
const { registerGraphTools } = await loadModule();
|
|
434
|
+
registerGraphTools(server, graphClient);
|
|
435
|
+
await server.tools.get("create-reply-draft").handler({
|
|
436
|
+
messageId: "AAMk123",
|
|
437
|
+
body: { Message: { body: { contentType: "html", content: "<p>hi</p>" } } }
|
|
438
|
+
});
|
|
439
|
+
const [, options] = graphClient.graphRequest.mock.calls[0];
|
|
440
|
+
const prefer = options.headers["Prefer"];
|
|
441
|
+
expect(prefer === void 0 || !prefer.includes("outlook.body-content-type")).toBe(true);
|
|
442
|
+
});
|
|
443
|
+
});
|
|
398
444
|
});
|
package/dist/endpoints.json
CHANGED
|
@@ -190,13 +190,15 @@
|
|
|
190
190
|
"pathPattern": "/me/messages/{message-id}/createReply",
|
|
191
191
|
"method": "post",
|
|
192
192
|
"toolName": "create-reply-draft",
|
|
193
|
-
"scopes": ["Mail.ReadWrite"]
|
|
193
|
+
"scopes": ["Mail.ReadWrite"],
|
|
194
|
+
"llmTip": "For HTML replies pass Message.body.contentType: 'html' with Message.body.content as HTML. Note: supplying Message.body replaces the whole draft body, so the original quoted history is not included. Specifying both 'comment' and Message.body returns 400. Signatures are added by the Outlook client only, not via Graph."
|
|
194
195
|
},
|
|
195
196
|
{
|
|
196
197
|
"pathPattern": "/me/messages/{message-id}/createReplyAll",
|
|
197
198
|
"method": "post",
|
|
198
199
|
"toolName": "create-reply-all-draft",
|
|
199
|
-
"scopes": ["Mail.ReadWrite"]
|
|
200
|
+
"scopes": ["Mail.ReadWrite"],
|
|
201
|
+
"llmTip": "For HTML replies pass Message.body.contentType: 'html' with Message.body.content as HTML. Note: supplying Message.body replaces the whole draft body, so the original quoted history is not included. Specifying both 'comment' and Message.body returns 400. Signatures are added by the Outlook client only, not via Graph."
|
|
200
202
|
},
|
|
201
203
|
{
|
|
202
204
|
"pathPattern": "/me/messages/{message-id}/send",
|
|
@@ -556,6 +558,59 @@
|
|
|
556
558
|
"scopes": ["Files.Read"],
|
|
557
559
|
"skipEncoding": ["address"]
|
|
558
560
|
},
|
|
561
|
+
{
|
|
562
|
+
"pathPattern": "/drives/{drive-id}/items/{driveItem-id}/workbook/worksheets/{workbookWorksheet-id}/range(address='{address}')",
|
|
563
|
+
"method": "patch",
|
|
564
|
+
"toolName": "update-excel-range",
|
|
565
|
+
"isExcelOp": true,
|
|
566
|
+
"scopes": ["Files.ReadWrite"],
|
|
567
|
+
"skipEncoding": ["address"],
|
|
568
|
+
"llmTip": "Set cell values, formulas, or number format on any range — does NOT require the worksheet to be a formal Excel table. Body: { values: [['v1','v2','v3']] } for a single row, or [['a','b'],['c','d']] for multi-row. Use this for append (target the next empty row's address, e.g. 'A172:H172'), update (target a single cell like 'H42'), or prepend-style edits (read existing, concatenate, write back). Number of inner-array values must match the column count of the address."
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
"pathPattern": "/drives/{drive-id}/items/{driveItem-id}/workbook/worksheets/{workbookWorksheet-id}/range(address='{address}')/insert",
|
|
572
|
+
"method": "post",
|
|
573
|
+
"toolName": "insert-excel-range",
|
|
574
|
+
"isExcelOp": true,
|
|
575
|
+
"scopes": ["Files.ReadWrite"],
|
|
576
|
+
"skipEncoding": ["address"],
|
|
577
|
+
"llmTip": "Insert blank cells at the given range, shifting existing content. Body: { shift: 'Down' } or { shift: 'Right' }. Use 'Down' to insert blank rows above existing data."
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
"pathPattern": "/drives/{drive-id}/items/{driveItem-id}/workbook/worksheets/{workbookWorksheet-id}/range(address='{address}')/delete",
|
|
581
|
+
"method": "post",
|
|
582
|
+
"toolName": "delete-excel-range",
|
|
583
|
+
"isExcelOp": true,
|
|
584
|
+
"scopes": ["Files.ReadWrite"],
|
|
585
|
+
"skipEncoding": ["address"],
|
|
586
|
+
"llmTip": "Delete cells at the given range, shifting remaining content. Body: { shift: 'Up' } or { shift: 'Left' }. Use 'Up' to delete entire rows."
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
"pathPattern": "/drives/{drive-id}/items/{driveItem-id}/workbook/tables/{workbookTable-id}/rows/itemAt(index={index})",
|
|
590
|
+
"method": "patch",
|
|
591
|
+
"toolName": "update-excel-table-row",
|
|
592
|
+
"isExcelOp": true,
|
|
593
|
+
"scopes": ["Files.ReadWrite"],
|
|
594
|
+
"skipEncoding": ["index"],
|
|
595
|
+
"llmTip": "Update a single row in a formal Excel table by zero-based row index. Body: { values: [[...]] } with one inner array matching the column count."
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
"pathPattern": "/drives/{drive-id}/items/{driveItem-id}/workbook/tables/{workbookTable-id}/rows/itemAt(index={index})",
|
|
599
|
+
"method": "delete",
|
|
600
|
+
"toolName": "delete-excel-table-row",
|
|
601
|
+
"isExcelOp": true,
|
|
602
|
+
"scopes": ["Files.ReadWrite"],
|
|
603
|
+
"skipEncoding": ["index"],
|
|
604
|
+
"llmTip": "Delete a single row from a formal Excel table by zero-based row index."
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
"pathPattern": "/drives/{drive-id}/items/{driveItem-id}/workbook/worksheets/{workbookWorksheet-id}/tables/add",
|
|
608
|
+
"method": "post",
|
|
609
|
+
"toolName": "create-excel-table",
|
|
610
|
+
"isExcelOp": true,
|
|
611
|
+
"scopes": ["Files.ReadWrite"],
|
|
612
|
+
"llmTip": "Convert a worksheet range into a formal Excel table. Body: { address: 'A1:H171', hasHeaders: true }. Required before using add-excel-table-rows / update-excel-table-row / delete-excel-table-row on a plain-cells sheet."
|
|
613
|
+
},
|
|
559
614
|
{
|
|
560
615
|
"pathPattern": "/drives/{drive-id}/items/{driveItem-id}/workbook/worksheets",
|
|
561
616
|
"method": "get",
|
package/dist/generated/client.js
CHANGED
|
@@ -1358,6 +1358,7 @@ const microsoft_graph_workbookRange = z.object({
|
|
|
1358
1358
|
sort: microsoft_graph_workbookRangeSort.optional(),
|
|
1359
1359
|
worksheet: microsoft_graph_workbookWorksheet.optional()
|
|
1360
1360
|
}).passthrough();
|
|
1361
|
+
const create_excel_table_Body = z.object({ address: z.string().nullable(), hasHeaders: z.boolean().default(false) }).partial().passthrough();
|
|
1361
1362
|
const microsoft_graph_assignedLabel = z.object({
|
|
1362
1363
|
displayName: z.string().describe("The display name of the label. Read-only.").nullish(),
|
|
1363
1364
|
labelId: z.string().describe("The unique identifier of the label.").nullish()
|
|
@@ -4461,6 +4462,7 @@ const schemas = {
|
|
|
4461
4462
|
microsoft_graph_workbookRangeFormat,
|
|
4462
4463
|
microsoft_graph_workbookRangeSort,
|
|
4463
4464
|
microsoft_graph_workbookRange,
|
|
4465
|
+
create_excel_table_Body,
|
|
4464
4466
|
microsoft_graph_assignedLabel,
|
|
4465
4467
|
microsoft_graph_licenseProcessingState,
|
|
4466
4468
|
microsoft_graph_group,
|
|
@@ -5737,6 +5739,30 @@ Items with this property set should be removed from your local state.`,
|
|
|
5737
5739
|
],
|
|
5738
5740
|
response: z.void()
|
|
5739
5741
|
},
|
|
5742
|
+
{
|
|
5743
|
+
method: "patch",
|
|
5744
|
+
path: "/drives/:driveId/items/:driveItemId/workbook/tables/:workbookTableId/rows/itemAt(index=:index)",
|
|
5745
|
+
alias: "update-excel-table-row",
|
|
5746
|
+
description: `Update a single row in a formal Excel table by zero-based row index. Body: { values: [[...]] } with one inner array matching the column count.`,
|
|
5747
|
+
requestFormat: "json",
|
|
5748
|
+
parameters: [
|
|
5749
|
+
{
|
|
5750
|
+
name: "body",
|
|
5751
|
+
description: `Operation payload`,
|
|
5752
|
+
type: "Body",
|
|
5753
|
+
schema: z.object({}).partial().passthrough().passthrough()
|
|
5754
|
+
}
|
|
5755
|
+
],
|
|
5756
|
+
response: z.void()
|
|
5757
|
+
},
|
|
5758
|
+
{
|
|
5759
|
+
method: "delete",
|
|
5760
|
+
path: "/drives/:driveId/items/:driveItemId/workbook/tables/:workbookTableId/rows/itemAt(index=:index)",
|
|
5761
|
+
alias: "delete-excel-table-row",
|
|
5762
|
+
description: `Delete a single row from a formal Excel table by zero-based row index.`,
|
|
5763
|
+
requestFormat: "json",
|
|
5764
|
+
response: z.void()
|
|
5765
|
+
},
|
|
5740
5766
|
{
|
|
5741
5767
|
method: "get",
|
|
5742
5768
|
path: "/drives/:driveId/items/:driveItemId/workbook/worksheets",
|
|
@@ -5845,6 +5871,70 @@ Items with this property set should be removed from your local state.`,
|
|
|
5845
5871
|
requestFormat: "json",
|
|
5846
5872
|
response: z.void()
|
|
5847
5873
|
},
|
|
5874
|
+
{
|
|
5875
|
+
method: "patch",
|
|
5876
|
+
path: `/drives/:driveId/items/:driveItemId/workbook/worksheets/:workbookWorksheetId/range(address=':address')`,
|
|
5877
|
+
alias: "update-excel-range",
|
|
5878
|
+
description: `Set cell values, formulas, or number format on any range \u2014 does NOT require the worksheet to be a formal Excel table. Body: { values: [['v1','v2','v3']] } for a single row, or [['a','b'],['c','d']] for multi-row. Use this for append (target the next empty row's address, e.g. 'A172:H172'), update (target a single cell like 'H42'), or prepend-style edits (read existing, concatenate, write back). Number of inner-array values must match the column count of the address.`,
|
|
5879
|
+
requestFormat: "json",
|
|
5880
|
+
parameters: [
|
|
5881
|
+
{
|
|
5882
|
+
name: "body",
|
|
5883
|
+
description: `Operation payload`,
|
|
5884
|
+
type: "Body",
|
|
5885
|
+
schema: z.object({}).partial().passthrough().passthrough()
|
|
5886
|
+
}
|
|
5887
|
+
],
|
|
5888
|
+
response: z.void()
|
|
5889
|
+
},
|
|
5890
|
+
{
|
|
5891
|
+
method: "post",
|
|
5892
|
+
path: `/drives/:driveId/items/:driveItemId/workbook/worksheets/:workbookWorksheetId/range(address=':address')/delete`,
|
|
5893
|
+
alias: "delete-excel-range",
|
|
5894
|
+
description: `Invoke action delete`,
|
|
5895
|
+
requestFormat: "json",
|
|
5896
|
+
parameters: [
|
|
5897
|
+
{
|
|
5898
|
+
name: "body",
|
|
5899
|
+
description: `Action parameters`,
|
|
5900
|
+
type: "Body",
|
|
5901
|
+
schema: z.object({ shift: z.string() }).partial().passthrough()
|
|
5902
|
+
}
|
|
5903
|
+
],
|
|
5904
|
+
response: z.void()
|
|
5905
|
+
},
|
|
5906
|
+
{
|
|
5907
|
+
method: "post",
|
|
5908
|
+
path: `/drives/:driveId/items/:driveItemId/workbook/worksheets/:workbookWorksheetId/range(address=':address')/insert`,
|
|
5909
|
+
alias: "insert-excel-range",
|
|
5910
|
+
description: `Invoke action insert`,
|
|
5911
|
+
requestFormat: "json",
|
|
5912
|
+
parameters: [
|
|
5913
|
+
{
|
|
5914
|
+
name: "body",
|
|
5915
|
+
description: `Action parameters`,
|
|
5916
|
+
type: "Body",
|
|
5917
|
+
schema: z.object({ shift: z.string() }).partial().passthrough()
|
|
5918
|
+
}
|
|
5919
|
+
],
|
|
5920
|
+
response: z.void()
|
|
5921
|
+
},
|
|
5922
|
+
{
|
|
5923
|
+
method: "post",
|
|
5924
|
+
path: "/drives/:driveId/items/:driveItemId/workbook/worksheets/:workbookWorksheetId/tables/add",
|
|
5925
|
+
alias: "create-excel-table",
|
|
5926
|
+
description: `Create a new table. The range source address determines the worksheet under which the table will be added. If the table can't be added (for example, because the address is invalid, or the table would overlap with another table), an error is generated.`,
|
|
5927
|
+
requestFormat: "json",
|
|
5928
|
+
parameters: [
|
|
5929
|
+
{
|
|
5930
|
+
name: "body",
|
|
5931
|
+
description: `Action parameters`,
|
|
5932
|
+
type: "Body",
|
|
5933
|
+
schema: create_excel_table_Body
|
|
5934
|
+
}
|
|
5935
|
+
],
|
|
5936
|
+
response: z.void()
|
|
5937
|
+
},
|
|
5848
5938
|
{
|
|
5849
5939
|
method: "get",
|
|
5850
5940
|
path: "/drives/:driveId/root",
|
package/dist/graph-tools.js
CHANGED
|
@@ -142,7 +142,7 @@ async function executeGraphTool(tool, config, graphClient, params, authManager)
|
|
|
142
142
|
logger.info(`Setting timezone preference: outlook.timezone="${params.timezone}"`);
|
|
143
143
|
}
|
|
144
144
|
const bodyFormat = process.env.MS365_MCP_BODY_FORMAT || "text";
|
|
145
|
-
if (bodyFormat !== "html") {
|
|
145
|
+
if (bodyFormat !== "html" && tool.method.toUpperCase() === "GET") {
|
|
146
146
|
preferValues.push(`outlook.body-content-type="${bodyFormat}"`);
|
|
147
147
|
}
|
|
148
148
|
if (preferValues.length > 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softeria/ms-365-mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.88.1",
|
|
4
4
|
"description": " A Model Context Protocol (MCP) server for interacting with Microsoft 365 and Office services through the Graph API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
package/src/endpoints.json
CHANGED
|
@@ -190,13 +190,15 @@
|
|
|
190
190
|
"pathPattern": "/me/messages/{message-id}/createReply",
|
|
191
191
|
"method": "post",
|
|
192
192
|
"toolName": "create-reply-draft",
|
|
193
|
-
"scopes": ["Mail.ReadWrite"]
|
|
193
|
+
"scopes": ["Mail.ReadWrite"],
|
|
194
|
+
"llmTip": "For HTML replies pass Message.body.contentType: 'html' with Message.body.content as HTML. Note: supplying Message.body replaces the whole draft body, so the original quoted history is not included. Specifying both 'comment' and Message.body returns 400. Signatures are added by the Outlook client only, not via Graph."
|
|
194
195
|
},
|
|
195
196
|
{
|
|
196
197
|
"pathPattern": "/me/messages/{message-id}/createReplyAll",
|
|
197
198
|
"method": "post",
|
|
198
199
|
"toolName": "create-reply-all-draft",
|
|
199
|
-
"scopes": ["Mail.ReadWrite"]
|
|
200
|
+
"scopes": ["Mail.ReadWrite"],
|
|
201
|
+
"llmTip": "For HTML replies pass Message.body.contentType: 'html' with Message.body.content as HTML. Note: supplying Message.body replaces the whole draft body, so the original quoted history is not included. Specifying both 'comment' and Message.body returns 400. Signatures are added by the Outlook client only, not via Graph."
|
|
200
202
|
},
|
|
201
203
|
{
|
|
202
204
|
"pathPattern": "/me/messages/{message-id}/send",
|
|
@@ -556,6 +558,59 @@
|
|
|
556
558
|
"scopes": ["Files.Read"],
|
|
557
559
|
"skipEncoding": ["address"]
|
|
558
560
|
},
|
|
561
|
+
{
|
|
562
|
+
"pathPattern": "/drives/{drive-id}/items/{driveItem-id}/workbook/worksheets/{workbookWorksheet-id}/range(address='{address}')",
|
|
563
|
+
"method": "patch",
|
|
564
|
+
"toolName": "update-excel-range",
|
|
565
|
+
"isExcelOp": true,
|
|
566
|
+
"scopes": ["Files.ReadWrite"],
|
|
567
|
+
"skipEncoding": ["address"],
|
|
568
|
+
"llmTip": "Set cell values, formulas, or number format on any range — does NOT require the worksheet to be a formal Excel table. Body: { values: [['v1','v2','v3']] } for a single row, or [['a','b'],['c','d']] for multi-row. Use this for append (target the next empty row's address, e.g. 'A172:H172'), update (target a single cell like 'H42'), or prepend-style edits (read existing, concatenate, write back). Number of inner-array values must match the column count of the address."
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
"pathPattern": "/drives/{drive-id}/items/{driveItem-id}/workbook/worksheets/{workbookWorksheet-id}/range(address='{address}')/insert",
|
|
572
|
+
"method": "post",
|
|
573
|
+
"toolName": "insert-excel-range",
|
|
574
|
+
"isExcelOp": true,
|
|
575
|
+
"scopes": ["Files.ReadWrite"],
|
|
576
|
+
"skipEncoding": ["address"],
|
|
577
|
+
"llmTip": "Insert blank cells at the given range, shifting existing content. Body: { shift: 'Down' } or { shift: 'Right' }. Use 'Down' to insert blank rows above existing data."
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
"pathPattern": "/drives/{drive-id}/items/{driveItem-id}/workbook/worksheets/{workbookWorksheet-id}/range(address='{address}')/delete",
|
|
581
|
+
"method": "post",
|
|
582
|
+
"toolName": "delete-excel-range",
|
|
583
|
+
"isExcelOp": true,
|
|
584
|
+
"scopes": ["Files.ReadWrite"],
|
|
585
|
+
"skipEncoding": ["address"],
|
|
586
|
+
"llmTip": "Delete cells at the given range, shifting remaining content. Body: { shift: 'Up' } or { shift: 'Left' }. Use 'Up' to delete entire rows."
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
"pathPattern": "/drives/{drive-id}/items/{driveItem-id}/workbook/tables/{workbookTable-id}/rows/itemAt(index={index})",
|
|
590
|
+
"method": "patch",
|
|
591
|
+
"toolName": "update-excel-table-row",
|
|
592
|
+
"isExcelOp": true,
|
|
593
|
+
"scopes": ["Files.ReadWrite"],
|
|
594
|
+
"skipEncoding": ["index"],
|
|
595
|
+
"llmTip": "Update a single row in a formal Excel table by zero-based row index. Body: { values: [[...]] } with one inner array matching the column count."
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
"pathPattern": "/drives/{drive-id}/items/{driveItem-id}/workbook/tables/{workbookTable-id}/rows/itemAt(index={index})",
|
|
599
|
+
"method": "delete",
|
|
600
|
+
"toolName": "delete-excel-table-row",
|
|
601
|
+
"isExcelOp": true,
|
|
602
|
+
"scopes": ["Files.ReadWrite"],
|
|
603
|
+
"skipEncoding": ["index"],
|
|
604
|
+
"llmTip": "Delete a single row from a formal Excel table by zero-based row index."
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
"pathPattern": "/drives/{drive-id}/items/{driveItem-id}/workbook/worksheets/{workbookWorksheet-id}/tables/add",
|
|
608
|
+
"method": "post",
|
|
609
|
+
"toolName": "create-excel-table",
|
|
610
|
+
"isExcelOp": true,
|
|
611
|
+
"scopes": ["Files.ReadWrite"],
|
|
612
|
+
"llmTip": "Convert a worksheet range into a formal Excel table. Body: { address: 'A1:H171', hasHeaders: true }. Required before using add-excel-table-rows / update-excel-table-row / delete-excel-table-row on a plain-cells sheet."
|
|
613
|
+
},
|
|
559
614
|
{
|
|
560
615
|
"pathPattern": "/drives/{drive-id}/items/{driveItem-id}/workbook/worksheets",
|
|
561
616
|
"method": "get",
|