Skip to content

Commit 10ab1b4

Browse files
authored
feat(tools): added google calendar tools/block, and gcal selector subblock (#454)
* added google calendar picker, tools, & block * added attendees list to quick add for gcal * added docs for gcal * cleanup * consolidated utils, additional type safety
1 parent fc7171b commit 10ab1b4

File tree

21 files changed

+2197
-26
lines changed

21 files changed

+2197
-26
lines changed
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
---
2+
title: Google Calendar
3+
description: Manage Google Calendar events
4+
---
5+
6+
import { BlockInfoCard } from "@/components/ui/block-info-card"
7+
8+
<BlockInfoCard
9+
type="google_calendar"
10+
color="#E0E0E0"
11+
icon={true}
12+
iconSvg={`<svg className="block-icon"
13+
14+
version='1.1'
15+
xmlns='http://www.w3.org/2000/svg'
16+
xmlnsXlink='http://www.w3.org/1999/xlink'
17+
x='0px'
18+
y='0px'
19+
viewBox='0 0 200 200'
20+
enableBackground='new 0 0 200 200'
21+
xmlSpace='preserve'
22+
>
23+
<g>
24+
<g transform='translate(3.75 3.75)'>
25+
<path
26+
fill='#FFFFFF'
27+
d='M148.882,43.618l-47.368-5.263l-57.895,5.263L38.355,96.25l5.263,52.632l52.632,6.579l52.632-6.579
28+
l5.263-53.947L148.882,43.618z'
29+
/>
30+
<path
31+
fill='#1A73E8'
32+
d='M65.211,125.276c-3.934-2.658-6.658-6.539-8.145-11.671l9.132-3.763c0.829,3.158,2.276,5.605,4.342,7.342
33+
c2.053,1.737,4.553,2.592,7.474,2.592c2.987,0,5.553-0.908,7.697-2.724s3.224-4.132,3.224-6.934c0-2.868-1.132-5.211-3.395-7.026
34+
s-5.105-2.724-8.5-2.724h-5.276v-9.039H76.5c2.921,0,5.382-0.789,7.382-2.368c2-1.579,3-3.737,3-6.487
35+
c0-2.447-0.895-4.395-2.684-5.855s-4.053-2.197-6.803-2.197c-2.684,0-4.816,0.711-6.395,2.145s-2.724,3.197-3.447,5.276
36+
l-9.039-3.763c1.197-3.395,3.395-6.395,6.618-8.987c3.224-2.592,7.342-3.895,12.342-3.895c3.697,0,7.026,0.711,9.974,2.145
37+
c2.947,1.434,5.263,3.421,6.934,5.947c1.671,2.539,2.5,5.382,2.5,8.539c0,3.224-0.776,5.947-2.329,8.184
38+
c-1.553,2.237-3.461,3.947-5.724,5.145v0.539c2.987,1.25,5.421,3.158,7.342,5.724c1.908,2.566,2.868,5.632,2.868,9.211
39+
s-0.908,6.776-2.724,9.579c-1.816,2.803-4.329,5.013-7.513,6.618c-3.197,1.605-6.789,2.421-10.776,2.421
40+
C73.408,129.263,69.145,127.934,65.211,125.276z'
41+
/>
42+
<path
43+
fill='#1A73E8'
44+
d='M121.25,79.961l-9.974,7.25l-5.013-7.605l17.987-12.974h6.895v61.197h-9.895L121.25,79.961z'
45+
/>
46+
<path
47+
fill='#EA4335'
48+
d='M148.882,196.25l47.368-47.368l-23.684-10.526l-23.684,10.526l-10.526,23.684L148.882,196.25z'
49+
/>
50+
<path
51+
fill='#34A853'
52+
d='M33.092,172.566l10.526,23.684h105.263v-47.368H43.618L33.092,172.566z'
53+
/>
54+
<path
55+
fill='#4285F4'
56+
d='M12.039-3.75C3.316-3.75-3.75,3.316-3.75,12.039v136.842l23.684,10.526l23.684-10.526V43.618h105.263
57+
l10.526-23.684L148.882-3.75H12.039z'
58+
/>
59+
<path
60+
fill='#188038'
61+
d='M-3.75,148.882v31.579c0,8.724,7.066,15.789,15.789,15.789h31.579v-47.368H-3.75z'
62+
/>
63+
<path
64+
fill='#FBBC04'
65+
d='M148.882,43.618v105.263h47.368V43.618l-23.684-10.526L148.882,43.618z'
66+
/>
67+
<path
68+
fill='#1967D2'
69+
d='M196.25,43.618V12.039c0-8.724-7.066-15.789-15.789-15.789h-31.579v47.368H196.25z'
70+
/>
71+
</g>
72+
</g>
73+
</svg>`}
74+
/>
75+
76+
{/* MANUAL-CONTENT-START:intro */}
77+
[Google Calendar](https://calendar.google.com) is Google's powerful calendar and scheduling service that provides a comprehensive platform for managing events, meetings, and appointments. With seamless integration across Google's ecosystem and widespread adoption, Google Calendar offers robust features for both personal and professional scheduling needs.
78+
79+
With Google Calendar, you can:
80+
81+
- **Create and manage events**: Schedule meetings, appointments, and reminders with detailed information
82+
- **Send calendar invites**: Automatically notify and coordinate with attendees through email invitations
83+
- **Natural language event creation**: Quickly add events using conversational language like "Meeting with John tomorrow at 3pm"
84+
- **View and search events**: Easily find and access your scheduled events across multiple calendars
85+
- **Manage multiple calendars**: Organize different types of events across various calendars
86+
87+
In Sim Studio, the Google Calendar integration enables your agents to programmatically create, read, and manage calendar events. This allows for powerful automation scenarios such as scheduling meetings, sending calendar invites, checking availability, and managing event details. Your agents can create events with natural language input, send automated calendar invitations to attendees, retrieve event information, and list upcoming events. This integration bridges the gap between your AI workflows and calendar management, enabling seamless scheduling automation and coordination with one of the world's most widely used calendar platforms.
88+
{/* MANUAL-CONTENT-END */}
89+
90+
91+
## Usage Instructions
92+
93+
Integrate Google Calendar functionality to create, read, update, and list calendar events within your workflow. Automate scheduling, check availability, and manage events using OAuth authentication.
94+
95+
96+
97+
## Tools
98+
99+
### `google_calendar_create`
100+
101+
Create a new event in Google Calendar
102+
103+
#### Input
104+
105+
| Parameter | Type | Required | Description |
106+
| --------- | ---- | -------- | ----------- |
107+
| `accessToken` | string | Yes | Access token for Google Calendar API |
108+
| `calendarId` | string | No | Calendar ID \(defaults to primary\) |
109+
| `summary` | string | Yes | Event title/summary |
110+
| `description` | string | No | Event description |
111+
| `location` | string | No | Event location |
112+
| `startDateTime` | string | Yes | Start date and time \(RFC3339 format, e.g., 2025-06-03T10:00:00-08:00\) |
113+
| `endDateTime` | string | Yes | End date and time \(RFC3339 format, e.g., 2025-06-03T11:00:00-08:00\) |
114+
| `timeZone` | string | No | Time zone \(e.g., America/Los_Angeles\) |
115+
| `attendees` | array | No | Array of attendee email addresses |
116+
| `sendUpdates` | string | No | How to send updates to attendees: all, externalOnly, or none |
117+
118+
#### Output
119+
120+
| Parameter | Type |
121+
| --------- | ---- |
122+
| `content` | string |
123+
124+
### `google_calendar_list`
125+
126+
List events from Google Calendar
127+
128+
#### Input
129+
130+
| Parameter | Type | Required | Description |
131+
| --------- | ---- | -------- | ----------- |
132+
| `accessToken` | string | Yes | Access token for Google Calendar API |
133+
| `calendarId` | string | No | Calendar ID \(defaults to primary\) |
134+
| `timeMin` | string | No | Lower bound for events \(RFC3339 timestamp, e.g., 2025-06-03T00:00:00Z\) |
135+
| `timeMax` | string | No | Upper bound for events \(RFC3339 timestamp, e.g., 2025-06-04T00:00:00Z\) |
136+
| `orderBy` | string | No | Order of events returned \(startTime or updated\) |
137+
| `showDeleted` | boolean | No | Include deleted events |
138+
139+
#### Output
140+
141+
| Parameter | Type |
142+
| --------- | ---- |
143+
| `content` | string |
144+
145+
### `google_calendar_get`
146+
147+
Get a specific event from Google Calendar
148+
149+
#### Input
150+
151+
| Parameter | Type | Required | Description |
152+
| --------- | ---- | -------- | ----------- |
153+
| `accessToken` | string | Yes | Access token for Google Calendar API |
154+
| `calendarId` | string | No | Calendar ID \(defaults to primary\) |
155+
| `eventId` | string | Yes | Event ID to retrieve |
156+
157+
#### Output
158+
159+
| Parameter | Type |
160+
| --------- | ---- |
161+
| `content` | string |
162+
163+
### `google_calendar_quick_add`
164+
165+
Create events from natural language text
166+
167+
#### Input
168+
169+
| Parameter | Type | Required | Description |
170+
| --------- | ---- | -------- | ----------- |
171+
| `accessToken` | string | Yes | Access token for Google Calendar API |
172+
| `calendarId` | string | No | Calendar ID \(defaults to primary\) |
173+
| `text` | string | Yes | Natural language text describing the event \(e.g., |
174+
| `attendees` | array | No | Array of attendee email addresses \(comma-separated string also accepted\) |
175+
| `sendUpdates` | string | No | How to send updates to attendees: all, externalOnly, or none |
176+
177+
#### Output
178+
179+
| Parameter | Type |
180+
| --------- | ---- |
181+
| `content` | string |
182+
183+
184+
185+
## Block Configuration
186+
187+
### Input
188+
189+
| Parameter | Type | Required | Description |
190+
| --------- | ---- | -------- | ----------- |
191+
| `operation` | string | Yes | Operation |
192+
193+
194+
195+
### Outputs
196+
197+
| Output | Type | Description |
198+
| ------ | ---- | ----------- |
199+
| `response` | object | Output from response |
200+
|`content` | string | content of the response |
201+
|`metadata` | json | metadata of the response |
202+
203+
204+
## Notes
205+
206+
- Category: `tools`
207+
- Type: `google_calendar`

apps/docs/content/docs/tools/knowledge.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ Key features of the Knowledge Base include:
4646
In Sim Studio, the Knowledge Base block enables your agents to perform intelligent semantic searches across your custom knowledge bases. This creates opportunities for automated information retrieval, content recommendations, and knowledge discovery as part of your AI workflows. The integration allows agents to search and retrieve relevant information programmatically, facilitating automated knowledge management tasks and ensuring that important information is easily accessible. By leveraging the Knowledge Base block, you can build intelligent agents that enhance information discovery while automating routine knowledge management tasks, improving team efficiency and ensuring consistent access to organizational knowledge.
4747
{/* MANUAL-CONTENT-END */}
4848

49+
4950
## Usage Instructions
5051

5152
Perform semantic vector search across your knowledge base to find the most relevant content. Uses advanced AI embeddings to understand meaning and context, returning the most similar documents to your search query.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"firecrawl",
1414
"github",
1515
"gmail",
16+
"google_calendar",
1617
"google_docs",
1718
"google_drive",
1819
"google_search",
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
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 { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
6+
import { db } from '@/db'
7+
import { account } from '@/db/schema'
8+
9+
export const dynamic = 'force-dynamic'
10+
11+
const logger = createLogger('GoogleCalendarAPI')
12+
13+
interface CalendarListItem {
14+
id: string
15+
summary: string
16+
description?: string
17+
primary?: boolean
18+
accessRole: string
19+
backgroundColor?: string
20+
foregroundColor?: string
21+
}
22+
23+
/**
24+
* Get calendars from Google Calendar
25+
*/
26+
export async function GET(request: NextRequest) {
27+
const requestId = crypto.randomUUID().slice(0, 8) // Generate a short request ID for correlation
28+
logger.info(`[${requestId}] Google Calendar calendars request received`)
29+
30+
try {
31+
// Get the session
32+
const session = await getSession()
33+
34+
// Check if the user is authenticated
35+
if (!session?.user?.id) {
36+
logger.warn(`[${requestId}] Unauthenticated request rejected`)
37+
return NextResponse.json({ error: 'User not authenticated' }, { status: 401 })
38+
}
39+
40+
// Get the credential ID from the query params
41+
const { searchParams } = new URL(request.url)
42+
const credentialId = searchParams.get('credentialId')
43+
44+
if (!credentialId) {
45+
logger.warn(`[${requestId}] Missing credentialId parameter`)
46+
return NextResponse.json({ error: 'Credential ID is required' }, { status: 400 })
47+
}
48+
49+
// Get the credential from the database
50+
const credentials = await db.select().from(account).where(eq(account.id, credentialId)).limit(1)
51+
52+
if (!credentials.length) {
53+
logger.warn(`[${requestId}] Credential not found`, { credentialId })
54+
return NextResponse.json({ error: 'Credential not found' }, { status: 404 })
55+
}
56+
57+
const credential = credentials[0]
58+
59+
// Check if the credential belongs to the user
60+
if (credential.userId !== session.user.id) {
61+
logger.warn(`[${requestId}] Unauthorized credential access attempt`, {
62+
credentialUserId: credential.userId,
63+
requestUserId: session.user.id,
64+
})
65+
return NextResponse.json({ error: 'Unauthorized' }, { status: 403 })
66+
}
67+
68+
// Refresh access token if needed using the utility function
69+
const accessToken = await refreshAccessTokenIfNeeded(credentialId, session.user.id, requestId)
70+
71+
if (!accessToken) {
72+
return NextResponse.json({ error: 'Failed to obtain valid access token' }, { status: 401 })
73+
}
74+
75+
// Fetch calendars from Google Calendar API
76+
logger.info(`[${requestId}] Fetching calendars from Google Calendar API`)
77+
const calendarResponse = await fetch(
78+
'https://www.googleapis.com/calendar/v3/users/me/calendarList',
79+
{
80+
method: 'GET',
81+
headers: {
82+
Authorization: `Bearer ${accessToken}`,
83+
'Content-Type': 'application/json',
84+
},
85+
}
86+
)
87+
88+
if (!calendarResponse.ok) {
89+
const errorData = await calendarResponse
90+
.text()
91+
.then((text) => JSON.parse(text))
92+
.catch(() => ({ error: { message: 'Unknown error' } }))
93+
logger.error(`[${requestId}] Google Calendar API error`, {
94+
status: calendarResponse.status,
95+
error: errorData.error?.message || 'Failed to fetch calendars',
96+
})
97+
return NextResponse.json(
98+
{ error: errorData.error?.message || 'Failed to fetch calendars' },
99+
{ status: calendarResponse.status }
100+
)
101+
}
102+
103+
const data = await calendarResponse.json()
104+
const calendars: CalendarListItem[] = data.items || []
105+
106+
// Sort calendars with primary first, then alphabetically
107+
calendars.sort((a, b) => {
108+
if (a.primary && !b.primary) return -1
109+
if (!a.primary && b.primary) return 1
110+
return a.summary.localeCompare(b.summary)
111+
})
112+
113+
logger.info(`[${requestId}] Successfully fetched ${calendars.length} calendars`)
114+
115+
return NextResponse.json({
116+
calendars: calendars.map((calendar) => ({
117+
id: calendar.id,
118+
summary: calendar.summary,
119+
description: calendar.description,
120+
primary: calendar.primary || false,
121+
accessRole: calendar.accessRole,
122+
backgroundColor: calendar.backgroundColor,
123+
foregroundColor: calendar.foregroundColor,
124+
})),
125+
})
126+
} catch (error) {
127+
logger.error(`[${requestId}] Error fetching Google calendars`, error)
128+
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
129+
}
130+
}

0 commit comments

Comments
 (0)