Skip to content

Commit c5727ef

Browse files
aadamgoughAdam Gough
andauthored
feat(tools): added microsoft excel tools/block (#441)
* got the read excel tool working * added read manual ID insertion * write working * tool finished * removed old logic * removed logic that was causing credential error * cleaned up * ran bun lint * removed logger * added back merging logic * fix: removed comments * fix: added greptile changes * ran bun lint * removed function * added microsoft excel docs * fix: icon format * fix: docs * ran lint --------- Co-authored-by: Adam Gough <adamgough@Adams-MacBook-Pro.local>
1 parent e17cdc9 commit c5727ef

20 files changed

Lines changed: 1911 additions & 6 deletions

File tree

apps/docs/content/docs/tools/linear.mdx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,19 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
99
type="linear"
1010
color="#5E6AD2"
1111
icon={true}
12-
iconSvg={`<svg className="block-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 100 100"><path fill="#fff" d="M1.22541 61.5228c-.2225-.9485.90748-1.5459 1.59638-.857L39.3342 97.1782c.6889.6889.0915 1.8189-.857 1.5964C20.0515 94.4522 5.54779 79.9485 1.22541 61.5228ZM.00189135 46.8891c-.01764375.2833.08887215.5599.28957165.7606L52.3503 99.7085c.2007.2007.4773.3075.7606.2896 2.3692-.1476 4.6938-.46 6.9624-.9259.7645-.157 1.0301-1.0963.4782-1.6481L2.57595 39.4485c-.55186-.5519-1.49117-.2863-1.648174.4782-.465915 2.2686-.77832 4.5932-.92588465 6.9624ZM4.21093 29.7054c-.16649.3738-.08169.8106.20765 1.1l64.77602 64.776c.2894.2894.7262.3742 1.1.2077 1.7861-.7956 3.5171-1.6927 5.1855-2.684.5521-.328.6373-1.0867.1832-1.5407L8.43566 24.3367c-.45409-.4541-1.21271-.3689-1.54074.1832-.99132 1.6684-1.88843 3.3994-2.68399 5.1855ZM12.6587 18.074c-.3701-.3701-.393-.9637-.0443-1.3541C21.7795 6.45931 35.1114 0 49.9519 0 77.5927 0 100 22.4073 100 50.0481c0 14.8405-6.4593 28.1724-16.7199 37.3375-.3903.3487-.984.3258-1.3542-.0443L12.6587 18.074Z"/></svg>`}
12+
iconSvg={`<svg className="block-icon"
13+
14+
xmlns='http://www.w3.org/2000/svg'
15+
fill='currentColor'
16+
17+
18+
viewBox='0 0 100 100'
19+
>
20+
<path
21+
fill='currentColor'
22+
d='M1.22541 61.5228c-.2225-.9485.90748-1.5459 1.59638-.857L39.3342 97.1782c.6889.6889.0915 1.8189-.857 1.5964C20.0515 94.4522 5.54779 79.9485 1.22541 61.5228ZM.00189135 46.8891c-.01764375.2833.08887215.5599.28957165.7606L52.3503 99.7085c.2007.2007.4773.3075.7606.2896 2.3692-.1476 4.6938-.46 6.9624-.9259.7645-.157 1.0301-1.0963.4782-1.6481L2.57595 39.4485c-.55186-.5519-1.49117-.2863-1.648174.4782-.465915 2.2686-.77832 4.5932-.92588465 6.9624ZM4.21093 29.7054c-.16649.3738-.08169.8106.20765 1.1l64.77602 64.776c.2894.2894.7262.3742 1.1.2077 1.7861-.7956 3.5171-1.6927 5.1855-2.684.5521-.328.6373-1.0867.1832-1.5407L8.43566 24.3367c-.45409-.4541-1.21271-.3689-1.54074.1832-.99132 1.6684-1.88843 3.3994-2.68399 5.1855ZM12.6587 18.074c-.3701-.3701-.393-.9637-.0443-1.3541C21.7795 6.45931 35.1114 0 49.9519 0 77.5927 0 100 22.4073 100 50.0481c0 14.8405-6.4593 28.1724-16.7199 37.3375-.3903.3487-.984.3258-1.3542-.0443L12.6587 18.074Z'
23+
/>
24+
</svg>`}
1325
/>
1426

1527
{/* MANUAL-CONTENT-START:intro */}

apps/docs/content/docs/tools/meta.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"linkup",
2626
"mem0",
2727
"memory",
28+
"microsoft_excel",
2829
"microsoft_teams",
2930
"mistral_parse",
3031
"notion",
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
---
2+
title: Microsoft Excel
3+
description: Read, write, and update data
4+
---
5+
6+
import { BlockInfoCard } from "@/components/ui/block-info-card"
7+
8+
<BlockInfoCard
9+
type="microsoft_excel"
10+
color="#E0E0E0"
11+
icon={true}
12+
iconSvg={`<svg className="block-icon"
13+
14+
xmlns='http://www.w3.org/2000/svg'
15+
version='1.1'
16+
id='Livello_1'
17+
x='0px'
18+
y='0px'
19+
viewBox='0 0 2289.75 2130'
20+
enableBackground='new 0 0 2289.75 2130'
21+
>
22+
<path
23+
fill='#185C37'
24+
d='M1437.75,1011.75L532.5,852v1180.393c0,53.907,43.7,97.607,97.607,97.607l0,0h1562.036 c53.907,0,97.607-43.7,97.607-97.607l0,0V1597.5L1437.75,1011.75z'
25+
/>
26+
<path
27+
fill='#21A366'
28+
d='M1437.75,0H630.107C576.2,0,532.5,43.7,532.5,97.607c0,0,0,0,0,0V532.5l905.25,532.5L1917,1224.75 L2289.75,1065V532.5L1437.75,0z'
29+
/>
30+
<path fill='#107C41' d='M532.5,532.5h905.25V1065H532.5V532.5z' />
31+
<path
32+
opacity='0.1'
33+
enableBackground='new'
34+
d='M1180.393,426H532.5v1331.25h647.893c53.834-0.175,97.432-43.773,97.607-97.607 V523.607C1277.825,469.773,1234.227,426.175,1180.393,426z'
35+
/>
36+
<path
37+
opacity='0.2'
38+
enableBackground='new'
39+
d='M1127.143,479.25H532.5V1810.5h594.643 c53.834-0.175,97.432-43.773,97.607-97.607V576.857C1224.575,523.023,1180.977,479.425,1127.143,479.25z'
40+
/>
41+
<path
42+
opacity='0.2'
43+
enableBackground='new'
44+
d='M1127.143,479.25H532.5V1704h594.643c53.834-0.175,97.432-43.773,97.607-97.607 V576.857C1224.575,523.023,1180.977,479.425,1127.143,479.25z'
45+
/>
46+
<path
47+
opacity='0.2'
48+
enableBackground='new'
49+
d='M1073.893,479.25H532.5V1704h541.393c53.834-0.175,97.432-43.773,97.607-97.607 V576.857C1171.325,523.023,1127.727,479.425,1073.893,479.25z'
50+
/>
51+
<linearGradient
52+
id='SVGID_1_'
53+
gradientUnits='userSpaceOnUse'
54+
x1='203.5132'
55+
y1='1729.0183'
56+
x2='967.9868'
57+
y2='404.9817'
58+
gradientTransform='matrix(1 0 0 -1 0 2132)'
59+
>
60+
<stop offset='0' style={{ stopColor: '#18884F' }} />
61+
<stop offset='0.5' style={{ stopColor: '#117E43' }} />
62+
<stop offset='1' style={{ stopColor: '#0B6631' }} />
63+
</linearGradient>
64+
<path
65+
fill='url(#SVGID_1_)'
66+
d='M97.607,479.25h976.285c53.907,0,97.607,43.7,97.607,97.607v976.285 c0,53.907-43.7,97.607-97.607,97.607H97.607C43.7,1650.75,0,1607.05,0,1553.143V576.857C0,522.95,43.7,479.25,97.607,479.25z'
67+
/>
68+
<path
69+
fill='#FFFFFF'
70+
d='M302.3,1382.264l205.332-318.169L319.5,747.683h151.336l102.666,202.35 c9.479,19.223,15.975,33.494,19.49,42.919h1.331c6.745-15.336,13.845-30.228,21.3-44.677L725.371,747.79h138.929l-192.925,314.548 L869.2,1382.263H721.378L602.79,1160.158c-5.586-9.45-10.326-19.376-14.164-29.66h-1.757c-3.474,10.075-8.083,19.722-13.739,28.755 l-122.102,223.011H302.3z'
71+
/>
72+
<path
73+
fill='#33C481'
74+
d='M2192.143,0H1437.75v532.5h852V97.607C2289.75,43.7,2246.05,0,2192.143,0L2192.143,0z'
75+
/>
76+
<path fill='#107C41' d='M1437.75,1065h852v532.5h-852V1065z' />
77+
</svg>`}
78+
/>
79+
80+
{/* MANUAL-CONTENT-START:intro */}
81+
[Microsoft Teams](https://www.microsoft.com/en-us/microsoft-365/excel) is a powerful spreadsheet application that enables data management, analysis, and visualization. Through the Microsoft Excel integration in Sim Studio, you can programmatically read, write, and manipulate spreadsheet data to support your workflow automation needs.
82+
83+
With Microsoft Excel integration, you can:
84+
85+
- **Read Spreadsheet Data**: Access data from specific ranges, sheets, and cells
86+
- **Write and Update Data**: Add new data or modify existing spreadsheet content
87+
- **Manage Tables**: Create and manipulate tabular data structures
88+
- **Handle Multiple Sheets**: Work with multiple worksheets in a workbook
89+
- **Process Data**: Import, export, and transform spreadsheet data
90+
91+
In Sim Studio, the Microsoft Excel integration provides seamless access to spreadsheet functionality through OAuth authentication. You can read data from specific ranges, write new information, update existing cells, and handle various data formats. The integration supports both reading and writing operations with flexible input and output options. This enables you to build workflows that can effectively manage spreadsheet data, whether you're extracting information for analysis, updating records automatically, or maintaining data consistency across your applications.
92+
{/* MANUAL-CONTENT-END */}
93+
94+
95+
## Usage Instructions
96+
97+
Integrate Microsoft Excel functionality to manage spreadsheet data. Read data from specific ranges, write new data, update existing cells, and manipulate table data using OAuth authentication. Supports various input and output formats for flexible data handling.
98+
99+
100+
101+
## Tools
102+
103+
### `microsoft_excel_read`
104+
105+
Read data from a Microsoft Excel spreadsheet
106+
107+
#### Input
108+
109+
| Parameter | Type | Required | Description |
110+
| --------- | ---- | -------- | ----------- |
111+
| `accessToken` | string | Yes | The access token for the Microsoft Excel API |
112+
| `spreadsheetId` | string | Yes | The ID of the spreadsheet to read from |
113+
| `range` | string | No | The range of cells to read from |
114+
115+
#### Output
116+
117+
| Parameter | Type |
118+
| --------- | ---- |
119+
| `data` | json |
120+
121+
### `microsoft_excel_write`
122+
123+
Write data to a Microsoft Excel spreadsheet
124+
125+
#### Input
126+
127+
| Parameter | Type | Required | Description |
128+
| --------- | ---- | -------- | ----------- |
129+
| `accessToken` | string | Yes | The access token for the Microsoft Excel API |
130+
| `spreadsheetId` | string | Yes | The ID of the spreadsheet to write to |
131+
| `range` | string | No | The range of cells to write to |
132+
| `values` | array | Yes | The data to write to the spreadsheet |
133+
| `valueInputOption` | string | No | The format of the data to write |
134+
| `includeValuesInResponse` | boolean | No | Whether to include the written values in the response |
135+
136+
#### Output
137+
138+
| Parameter | Type |
139+
| --------- | ---- |
140+
| `updatedRange` | string |
141+
| `updatedRows` | string |
142+
| `updatedColumns` | string |
143+
| `updatedCells` | string |
144+
| `metadata` | string |
145+
| `spreadsheetId` | string |
146+
| `spreadsheetUrl` | string |
147+
148+
### `microsoft_excel_table_add`
149+
150+
Add new rows to a Microsoft Excel table
151+
152+
#### Input
153+
154+
| Parameter | Type | Required | Description |
155+
| --------- | ---- | -------- | ----------- |
156+
| `accessToken` | string | Yes | The access token for the Microsoft Excel API |
157+
| `spreadsheetId` | string | Yes | The ID of the spreadsheet containing the table |
158+
| `tableName` | string | Yes | The name of the table to add rows to |
159+
| `values` | array | Yes | The data to add to the table \(array of arrays or array of objects\) |
160+
161+
#### Output
162+
163+
| Parameter | Type |
164+
| --------- | ---- |
165+
| `data` | json |
166+
167+
168+
169+
## Block Configuration
170+
171+
### Input
172+
173+
| Parameter | Type | Required | Description |
174+
| --------- | ---- | -------- | ----------- |
175+
| `operation` | string | Yes | Operation |
176+
177+
178+
179+
### Outputs
180+
181+
| Output | Type | Description |
182+
| ------ | ---- | ----------- |
183+
| `response` | object | Output from response |
184+
|`data` | json | data of the response |
185+
|`metadata` | json | metadata of the response |
186+
|`updatedRange` | string | updatedRange of the response |
187+
|`updatedRows` | number | updatedRows of the response |
188+
|`updatedColumns` | number | updatedColumns of the response |
189+
|`updatedCells` | number | updatedCells of the response |
190+
|`index` | number | index of the response |
191+
|`values` | json | values of the response |
192+
193+
194+
## Notes
195+
196+
- Category: `tools`
197+
- Type: `microsoft_excel`
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { eq } from 'drizzle-orm'
2+
import { type NextRequest, NextResponse } from 'next/server'
3+
import { getSession } from '@/lib/auth'
4+
import { createLogger } from '@/lib/logs/console-logger'
5+
import { db } from '@/db'
6+
import { account } from '@/db/schema'
7+
import { refreshAccessTokenIfNeeded } from '../../utils'
8+
9+
export const dynamic = 'force-dynamic'
10+
11+
const logger = createLogger('MicrosoftFileAPI')
12+
13+
/**
14+
* Get a single file from Microsoft OneDrive
15+
*/
16+
export async function GET(request: NextRequest) {
17+
const requestId = crypto.randomUUID().slice(0, 8)
18+
try {
19+
// Get the session
20+
const session = await getSession()
21+
22+
// Check if the user is authenticated
23+
if (!session?.user?.id) {
24+
return NextResponse.json({ error: 'User not authenticated' }, { status: 401 })
25+
}
26+
27+
// Get the credential ID and file ID from the query params
28+
const { searchParams } = new URL(request.url)
29+
const credentialId = searchParams.get('credentialId')
30+
const fileId = searchParams.get('fileId')
31+
32+
if (!credentialId || !fileId) {
33+
return NextResponse.json({ error: 'Credential ID and File ID are required' }, { status: 400 })
34+
}
35+
36+
// Get the credential from the database
37+
const credentials = await db.select().from(account).where(eq(account.id, credentialId)).limit(1)
38+
39+
if (!credentials.length) {
40+
return NextResponse.json({ error: 'Credential not found' }, { status: 404 })
41+
}
42+
43+
const credential = credentials[0]
44+
45+
// Check if the credential belongs to the user
46+
if (credential.userId !== session.user.id) {
47+
return NextResponse.json({ error: 'Unauthorized' }, { status: 403 })
48+
}
49+
50+
// Refresh access token if needed using the utility function
51+
const accessToken = await refreshAccessTokenIfNeeded(credentialId, session.user.id, requestId)
52+
53+
if (!accessToken) {
54+
return NextResponse.json({ error: 'Failed to obtain valid access token' }, { status: 401 })
55+
}
56+
57+
const response = await fetch(
58+
`https://graph.microsoft.com/v1.0/me/drive/items/${fileId}?$select=id,name,mimeType,webUrl,thumbnails,createdDateTime,lastModifiedDateTime,size,createdBy`,
59+
{
60+
headers: {
61+
Authorization: `Bearer ${accessToken}`,
62+
},
63+
}
64+
)
65+
66+
if (!response.ok) {
67+
const errorData = await response.json().catch(() => ({ error: { message: 'Unknown error' } }))
68+
logger.error(`[${requestId}] Microsoft Graph API error`, {
69+
status: response.status,
70+
error: errorData.error?.message || 'Failed to fetch file from Microsoft OneDrive',
71+
})
72+
return NextResponse.json(
73+
{
74+
error: errorData.error?.message || 'Failed to fetch file from Microsoft OneDrive',
75+
},
76+
{ status: response.status }
77+
)
78+
}
79+
80+
const file = await response.json()
81+
82+
// Transform the response to match expected format
83+
const transformedFile = {
84+
id: file.id,
85+
name: file.name,
86+
mimeType:
87+
file.mimeType || 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
88+
iconLink: file.thumbnails?.[0]?.small?.url,
89+
webViewLink: file.webUrl,
90+
thumbnailLink: file.thumbnails?.[0]?.medium?.url,
91+
createdTime: file.createdDateTime,
92+
modifiedTime: file.lastModifiedDateTime,
93+
size: file.size?.toString(),
94+
owners: file.createdBy
95+
? [
96+
{
97+
displayName: file.createdBy.user?.displayName || 'Unknown',
98+
emailAddress: file.createdBy.user?.email || '',
99+
},
100+
]
101+
: [],
102+
downloadUrl: `https://graph.microsoft.com/v1.0/me/drive/items/${file.id}/content`,
103+
}
104+
105+
return NextResponse.json({ file: transformedFile }, { status: 200 })
106+
} catch (error) {
107+
logger.error(`[${requestId}] Error fetching file from Microsoft OneDrive`, error)
108+
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
109+
}
110+
}

0 commit comments

Comments
 (0)