Skip to content

Commit 7452a26

Browse files
committed
feat(microsoft-excel): add clear, format, create-table, sort, and delete-worksheet tools
1 parent a759b5e commit 7452a26

10 files changed

Lines changed: 1713 additions & 63 deletions

File tree

apps/sim/blocks/blocks/microsoft_excel.ts

Lines changed: 682 additions & 63 deletions
Large diffs are not rendered by default.
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import { ErrorExtractorId } from '@/tools/error-extractors'
2+
import type {
3+
MicrosoftExcelClearRangeParams,
4+
MicrosoftExcelClearRangeResponse,
5+
} from '@/tools/microsoft_excel/types'
6+
import {
7+
buildWorksheetRangeUrl,
8+
getItemBasePath,
9+
getSpreadsheetWebUrl,
10+
} from '@/tools/microsoft_excel/utils'
11+
import type { ToolConfig } from '@/tools/types'
12+
13+
/**
14+
* Clears the contents and/or formatting of a worksheet range.
15+
* Uses Microsoft Graph: POST /workbook/worksheets/{name}/range(address='...')/clear
16+
*/
17+
export const clearRangeTool: ToolConfig<
18+
MicrosoftExcelClearRangeParams,
19+
MicrosoftExcelClearRangeResponse
20+
> = {
21+
id: 'microsoft_excel_clear_range',
22+
name: 'Clear Microsoft Excel Range',
23+
description: 'Clear the values and/or formatting of a range in a Microsoft Excel worksheet',
24+
version: '1.0',
25+
errorExtractor: ErrorExtractorId.MICROSOFT_GRAPH_ERRORS,
26+
27+
oauth: {
28+
required: true,
29+
provider: 'microsoft-excel',
30+
},
31+
32+
params: {
33+
accessToken: {
34+
type: 'string',
35+
required: true,
36+
visibility: 'hidden',
37+
description: 'The access token for the Microsoft Excel API',
38+
},
39+
spreadsheetId: {
40+
type: 'string',
41+
required: true,
42+
visibility: 'user-or-llm',
43+
description: 'The ID of the spreadsheet/workbook (e.g., "01ABC123DEF456")',
44+
},
45+
driveId: {
46+
type: 'string',
47+
required: false,
48+
visibility: 'user-or-llm',
49+
description:
50+
'The ID of the drive containing the spreadsheet. Required for SharePoint files. If omitted, uses personal OneDrive.',
51+
},
52+
sheetName: {
53+
type: 'string',
54+
required: false,
55+
visibility: 'user-or-llm',
56+
description:
57+
'The name of the worksheet (e.g., "Sheet1"). If omitted, the range must use the combined "Sheet1!A1:B2" format.',
58+
},
59+
range: {
60+
type: 'string',
61+
required: true,
62+
visibility: 'user-or-llm',
63+
description: 'The cell range to clear (e.g., "A1:D10" or "Sheet1!A1:D10")',
64+
},
65+
applyTo: {
66+
type: 'string',
67+
required: false,
68+
visibility: 'user-or-llm',
69+
description: 'What to clear: "All", "Formats", or "Contents". Defaults to "All".',
70+
},
71+
},
72+
73+
request: {
74+
url: (params) => {
75+
const spreadsheetId = params.spreadsheetId?.trim()
76+
if (!spreadsheetId) {
77+
throw new Error('Spreadsheet ID is required')
78+
}
79+
const basePath = getItemBasePath(spreadsheetId, params.driveId)
80+
return `${buildWorksheetRangeUrl(basePath, params.range, params.sheetName)}/clear`
81+
},
82+
method: 'POST',
83+
headers: (params) => {
84+
if (!params.accessToken) {
85+
throw new Error('Access token is required')
86+
}
87+
return {
88+
Authorization: `Bearer ${params.accessToken}`,
89+
'Content-Type': 'application/json',
90+
}
91+
},
92+
body: (params) => ({
93+
applyTo: params.applyTo || 'All',
94+
}),
95+
},
96+
97+
transformResponse: async (_response: Response, params?: MicrosoftExcelClearRangeParams) => {
98+
const spreadsheetId = params?.spreadsheetId?.trim() || ''
99+
const driveId = params?.driveId
100+
101+
const accessToken = params?.accessToken
102+
if (!accessToken) {
103+
throw new Error('Access token is required')
104+
}
105+
const webUrl = await getSpreadsheetWebUrl(spreadsheetId, accessToken, driveId)
106+
107+
return {
108+
success: true,
109+
output: {
110+
cleared: true,
111+
range: params?.range ?? '',
112+
applyTo: params?.applyTo || 'All',
113+
metadata: {
114+
spreadsheetId,
115+
spreadsheetUrl: webUrl,
116+
},
117+
},
118+
}
119+
},
120+
121+
outputs: {
122+
cleared: { type: 'boolean', description: 'Whether the range was cleared' },
123+
range: { type: 'string', description: 'The range that was cleared' },
124+
applyTo: { type: 'string', description: 'What was cleared (All, Formats, or Contents)' },
125+
metadata: {
126+
type: 'object',
127+
description: 'Spreadsheet metadata',
128+
properties: {
129+
spreadsheetId: { type: 'string', description: 'The ID of the spreadsheet' },
130+
spreadsheetUrl: { type: 'string', description: 'URL to access the spreadsheet' },
131+
},
132+
},
133+
},
134+
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import { ErrorExtractorId } from '@/tools/error-extractors'
2+
import type {
3+
MicrosoftExcelCreateTableParams,
4+
MicrosoftExcelCreateTableResponse,
5+
} from '@/tools/microsoft_excel/types'
6+
import { getItemBasePath, getSpreadsheetWebUrl } from '@/tools/microsoft_excel/utils'
7+
import type { ToolConfig } from '@/tools/types'
8+
9+
/**
10+
* Creates a new table over a range of cells.
11+
* Uses Microsoft Graph: POST /workbook/tables/add with { address, hasHeaders }.
12+
*/
13+
export const createTableTool: ToolConfig<
14+
MicrosoftExcelCreateTableParams,
15+
MicrosoftExcelCreateTableResponse
16+
> = {
17+
id: 'microsoft_excel_create_table',
18+
name: 'Create Microsoft Excel Table',
19+
description: 'Create a new table over a range of cells in a Microsoft Excel workbook',
20+
version: '1.0',
21+
errorExtractor: ErrorExtractorId.MICROSOFT_GRAPH_ERRORS,
22+
23+
oauth: {
24+
required: true,
25+
provider: 'microsoft-excel',
26+
},
27+
28+
params: {
29+
accessToken: {
30+
type: 'string',
31+
required: true,
32+
visibility: 'hidden',
33+
description: 'The access token for the Microsoft Excel API',
34+
},
35+
spreadsheetId: {
36+
type: 'string',
37+
required: true,
38+
visibility: 'user-or-llm',
39+
description: 'The ID of the spreadsheet/workbook (e.g., "01ABC123DEF456")',
40+
},
41+
driveId: {
42+
type: 'string',
43+
required: false,
44+
visibility: 'user-or-llm',
45+
description:
46+
'The ID of the drive containing the spreadsheet. Required for SharePoint files. If omitted, uses personal OneDrive.',
47+
},
48+
address: {
49+
type: 'string',
50+
required: true,
51+
visibility: 'user-or-llm',
52+
description:
53+
'The range address for the table data source (e.g., "Sheet1!A1:D5"). If no sheet name is included, the active sheet is used.',
54+
},
55+
hasHeaders: {
56+
type: 'boolean',
57+
required: false,
58+
visibility: 'user-or-llm',
59+
description: 'Whether the first row of the range contains column headers. Defaults to true.',
60+
},
61+
},
62+
63+
request: {
64+
url: (params) => {
65+
const spreadsheetId = params.spreadsheetId?.trim()
66+
if (!spreadsheetId) {
67+
throw new Error('Spreadsheet ID is required')
68+
}
69+
const basePath = getItemBasePath(spreadsheetId, params.driveId)
70+
return `${basePath}/workbook/tables/add`
71+
},
72+
method: 'POST',
73+
headers: (params) => {
74+
if (!params.accessToken) {
75+
throw new Error('Access token is required')
76+
}
77+
return {
78+
Authorization: `Bearer ${params.accessToken}`,
79+
'Content-Type': 'application/json',
80+
}
81+
},
82+
body: (params) => {
83+
const address = params.address?.trim()
84+
if (!address) {
85+
throw new Error('A range address is required (e.g., "Sheet1!A1:D5")')
86+
}
87+
return {
88+
address,
89+
hasHeaders: params.hasHeaders ?? true,
90+
}
91+
},
92+
},
93+
94+
transformResponse: async (response: Response, params?: MicrosoftExcelCreateTableParams) => {
95+
const data = await response.json()
96+
97+
const spreadsheetId = params?.spreadsheetId?.trim() || ''
98+
const driveId = params?.driveId
99+
100+
const accessToken = params?.accessToken
101+
if (!accessToken) {
102+
throw new Error('Access token is required')
103+
}
104+
const webUrl = await getSpreadsheetWebUrl(spreadsheetId, accessToken, driveId)
105+
106+
return {
107+
success: true,
108+
output: {
109+
table: {
110+
id: data.id ?? '',
111+
name: data.name ?? '',
112+
showHeaders: data.showHeaders ?? true,
113+
showTotals: data.showTotals ?? false,
114+
style: data.style ?? null,
115+
},
116+
metadata: {
117+
spreadsheetId,
118+
spreadsheetUrl: webUrl,
119+
},
120+
},
121+
}
122+
},
123+
124+
outputs: {
125+
table: {
126+
type: 'object',
127+
description: 'Details of the newly created table',
128+
properties: {
129+
id: { type: 'string', description: 'The unique ID of the table' },
130+
name: { type: 'string', description: 'The name of the table' },
131+
showHeaders: { type: 'boolean', description: 'Whether the header row is shown' },
132+
showTotals: { type: 'boolean', description: 'Whether the totals row is shown' },
133+
style: { type: 'string', description: 'The table style name' },
134+
},
135+
},
136+
metadata: {
137+
type: 'object',
138+
description: 'Spreadsheet metadata',
139+
properties: {
140+
spreadsheetId: { type: 'string', description: 'The ID of the spreadsheet' },
141+
spreadsheetUrl: { type: 'string', description: 'URL to access the spreadsheet' },
142+
},
143+
},
144+
},
145+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import { ErrorExtractorId } from '@/tools/error-extractors'
2+
import type {
3+
MicrosoftExcelDeleteWorksheetParams,
4+
MicrosoftExcelDeleteWorksheetResponse,
5+
} from '@/tools/microsoft_excel/types'
6+
import { getItemBasePath, getSpreadsheetWebUrl } from '@/tools/microsoft_excel/utils'
7+
import type { ToolConfig } from '@/tools/types'
8+
9+
/**
10+
* Deletes a worksheet from a workbook.
11+
* Uses Microsoft Graph: DELETE /workbook/worksheets/{name}
12+
*/
13+
export const deleteWorksheetTool: ToolConfig<
14+
MicrosoftExcelDeleteWorksheetParams,
15+
MicrosoftExcelDeleteWorksheetResponse
16+
> = {
17+
id: 'microsoft_excel_delete_worksheet',
18+
name: 'Delete Microsoft Excel Worksheet',
19+
description: 'Delete a worksheet (sheet) from a Microsoft Excel workbook',
20+
version: '1.0',
21+
errorExtractor: ErrorExtractorId.MICROSOFT_GRAPH_ERRORS,
22+
23+
oauth: {
24+
required: true,
25+
provider: 'microsoft-excel',
26+
},
27+
28+
params: {
29+
accessToken: {
30+
type: 'string',
31+
required: true,
32+
visibility: 'hidden',
33+
description: 'The access token for the Microsoft Excel API',
34+
},
35+
spreadsheetId: {
36+
type: 'string',
37+
required: true,
38+
visibility: 'user-or-llm',
39+
description: 'The ID of the spreadsheet/workbook (e.g., "01ABC123DEF456")',
40+
},
41+
driveId: {
42+
type: 'string',
43+
required: false,
44+
visibility: 'user-or-llm',
45+
description:
46+
'The ID of the drive containing the spreadsheet. Required for SharePoint files. If omitted, uses personal OneDrive.',
47+
},
48+
worksheetName: {
49+
type: 'string',
50+
required: true,
51+
visibility: 'user-or-llm',
52+
description: 'The name of the worksheet to delete (e.g., "Sheet1", "Old Data")',
53+
},
54+
},
55+
56+
request: {
57+
url: (params) => {
58+
const spreadsheetId = params.spreadsheetId?.trim()
59+
if (!spreadsheetId) {
60+
throw new Error('Spreadsheet ID is required')
61+
}
62+
const worksheetName = params.worksheetName?.trim()
63+
if (!worksheetName) {
64+
throw new Error('Worksheet name is required')
65+
}
66+
const basePath = getItemBasePath(spreadsheetId, params.driveId)
67+
return `${basePath}/workbook/worksheets('${encodeURIComponent(worksheetName)}')`
68+
},
69+
method: 'DELETE',
70+
headers: (params) => {
71+
if (!params.accessToken) {
72+
throw new Error('Access token is required')
73+
}
74+
return {
75+
Authorization: `Bearer ${params.accessToken}`,
76+
}
77+
},
78+
},
79+
80+
transformResponse: async (_response: Response, params?: MicrosoftExcelDeleteWorksheetParams) => {
81+
const spreadsheetId = params?.spreadsheetId?.trim() || ''
82+
const driveId = params?.driveId
83+
84+
const accessToken = params?.accessToken
85+
if (!accessToken) {
86+
throw new Error('Access token is required')
87+
}
88+
const webUrl = await getSpreadsheetWebUrl(spreadsheetId, accessToken, driveId)
89+
90+
return {
91+
success: true,
92+
output: {
93+
deleted: true,
94+
worksheetName: params?.worksheetName?.trim() ?? '',
95+
metadata: {
96+
spreadsheetId,
97+
spreadsheetUrl: webUrl,
98+
},
99+
},
100+
}
101+
},
102+
103+
outputs: {
104+
deleted: { type: 'boolean', description: 'Whether the worksheet was deleted' },
105+
worksheetName: { type: 'string', description: 'The name of the deleted worksheet' },
106+
metadata: {
107+
type: 'object',
108+
description: 'Spreadsheet metadata',
109+
properties: {
110+
spreadsheetId: { type: 'string', description: 'The ID of the spreadsheet' },
111+
spreadsheetUrl: { type: 'string', description: 'URL to access the spreadsheet' },
112+
},
113+
},
114+
},
115+
}

0 commit comments

Comments
 (0)