Skip to content

Commit 16f337f

Browse files
authored
feat(google): add missing tools for Gmail, Drive, Sheets, and Calendar (#3338)
* feat(google): add missing tools for Gmail, Drive, Sheets, and Calendar * fix(google-drive): remove dead transformResponse from move tool
1 parent 063ec87 commit 16f337f

23 files changed

+1956
-0
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import { GMAIL_API_BASE } from '@/tools/gmail/utils'
2+
import type { ToolConfig } from '@/tools/types'
3+
4+
interface GmailCreateLabelParams {
5+
accessToken: string
6+
name: string
7+
messageListVisibility?: string
8+
labelListVisibility?: string
9+
}
10+
11+
interface GmailCreateLabelResponse {
12+
success: boolean
13+
output: {
14+
id: string
15+
name: string
16+
messageListVisibility?: string
17+
labelListVisibility?: string
18+
type?: string
19+
}
20+
}
21+
22+
export const gmailCreateLabelV2Tool: ToolConfig<GmailCreateLabelParams, GmailCreateLabelResponse> =
23+
{
24+
id: 'gmail_create_label_v2',
25+
name: 'Gmail Create Label',
26+
description: 'Create a new label in Gmail',
27+
version: '2.0.0',
28+
29+
oauth: {
30+
required: true,
31+
provider: 'google-email',
32+
},
33+
34+
params: {
35+
accessToken: {
36+
type: 'string',
37+
required: true,
38+
visibility: 'hidden',
39+
description: 'Access token for Gmail API',
40+
},
41+
name: {
42+
type: 'string',
43+
required: true,
44+
visibility: 'user-or-llm',
45+
description: 'Display name for the new label',
46+
},
47+
messageListVisibility: {
48+
type: 'string',
49+
required: false,
50+
visibility: 'user-or-llm',
51+
description: 'Visibility of messages with this label in the message list (show or hide)',
52+
},
53+
labelListVisibility: {
54+
type: 'string',
55+
required: false,
56+
visibility: 'user-or-llm',
57+
description:
58+
'Visibility of the label in the label list (labelShow, labelShowIfUnread, or labelHide)',
59+
},
60+
},
61+
62+
request: {
63+
url: () => `${GMAIL_API_BASE}/labels`,
64+
method: 'POST',
65+
headers: (params: GmailCreateLabelParams) => ({
66+
Authorization: `Bearer ${params.accessToken}`,
67+
'Content-Type': 'application/json',
68+
}),
69+
body: (params: GmailCreateLabelParams) => {
70+
const body: Record<string, string> = { name: params.name }
71+
if (params.messageListVisibility) {
72+
body.messageListVisibility = params.messageListVisibility
73+
}
74+
if (params.labelListVisibility) {
75+
body.labelListVisibility = params.labelListVisibility
76+
}
77+
return body
78+
},
79+
},
80+
81+
transformResponse: async (response: Response) => {
82+
const data = await response.json()
83+
84+
if (!response.ok) {
85+
return {
86+
success: false,
87+
output: { id: '', name: '' },
88+
error: data.error?.message || 'Failed to create label',
89+
}
90+
}
91+
92+
return {
93+
success: true,
94+
output: {
95+
id: data.id,
96+
name: data.name,
97+
messageListVisibility: data.messageListVisibility ?? null,
98+
labelListVisibility: data.labelListVisibility ?? null,
99+
type: data.type ?? null,
100+
},
101+
}
102+
},
103+
104+
outputs: {
105+
id: { type: 'string', description: 'Label ID' },
106+
name: { type: 'string', description: 'Label display name' },
107+
messageListVisibility: {
108+
type: 'string',
109+
description: 'Visibility of messages with this label',
110+
optional: true,
111+
},
112+
labelListVisibility: {
113+
type: 'string',
114+
description: 'Visibility of the label in the label list',
115+
optional: true,
116+
},
117+
type: { type: 'string', description: 'Label type (system or user)', optional: true },
118+
},
119+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { GMAIL_API_BASE } from '@/tools/gmail/utils'
2+
import type { ToolConfig } from '@/tools/types'
3+
4+
interface GmailDeleteDraftParams {
5+
accessToken: string
6+
draftId: string
7+
}
8+
9+
interface GmailDeleteDraftResponse {
10+
success: boolean
11+
output: {
12+
deleted: boolean
13+
draftId: string
14+
}
15+
}
16+
17+
export const gmailDeleteDraftV2Tool: ToolConfig<GmailDeleteDraftParams, GmailDeleteDraftResponse> =
18+
{
19+
id: 'gmail_delete_draft_v2',
20+
name: 'Gmail Delete Draft',
21+
description: 'Delete a specific draft from Gmail',
22+
version: '2.0.0',
23+
24+
oauth: {
25+
required: true,
26+
provider: 'google-email',
27+
},
28+
29+
params: {
30+
accessToken: {
31+
type: 'string',
32+
required: true,
33+
visibility: 'hidden',
34+
description: 'Access token for Gmail API',
35+
},
36+
draftId: {
37+
type: 'string',
38+
required: true,
39+
visibility: 'user-or-llm',
40+
description: 'ID of the draft to delete',
41+
},
42+
},
43+
44+
request: {
45+
url: (params: GmailDeleteDraftParams) => `${GMAIL_API_BASE}/drafts/${params.draftId}`,
46+
method: 'DELETE',
47+
headers: (params: GmailDeleteDraftParams) => ({
48+
Authorization: `Bearer ${params.accessToken}`,
49+
'Content-Type': 'application/json',
50+
}),
51+
},
52+
53+
transformResponse: async (response: Response, params?: GmailDeleteDraftParams) => {
54+
if (!response.ok) {
55+
const data = await response.json()
56+
return {
57+
success: false,
58+
output: { deleted: false, draftId: params?.draftId ?? '' },
59+
error: data.error?.message || 'Failed to delete draft',
60+
}
61+
}
62+
63+
return {
64+
success: true,
65+
output: {
66+
deleted: true,
67+
draftId: params?.draftId ?? '',
68+
},
69+
}
70+
},
71+
72+
outputs: {
73+
deleted: { type: 'boolean', description: 'Whether the draft was successfully deleted' },
74+
draftId: { type: 'string', description: 'ID of the deleted draft' },
75+
},
76+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { GMAIL_API_BASE } from '@/tools/gmail/utils'
2+
import type { ToolConfig } from '@/tools/types'
3+
4+
interface GmailDeleteLabelParams {
5+
accessToken: string
6+
labelId: string
7+
}
8+
9+
interface GmailDeleteLabelResponse {
10+
success: boolean
11+
output: {
12+
deleted: boolean
13+
labelId: string
14+
}
15+
}
16+
17+
export const gmailDeleteLabelV2Tool: ToolConfig<GmailDeleteLabelParams, GmailDeleteLabelResponse> =
18+
{
19+
id: 'gmail_delete_label_v2',
20+
name: 'Gmail Delete Label',
21+
description: 'Delete a label from Gmail',
22+
version: '2.0.0',
23+
24+
oauth: {
25+
required: true,
26+
provider: 'google-email',
27+
},
28+
29+
params: {
30+
accessToken: {
31+
type: 'string',
32+
required: true,
33+
visibility: 'hidden',
34+
description: 'Access token for Gmail API',
35+
},
36+
labelId: {
37+
type: 'string',
38+
required: true,
39+
visibility: 'user-or-llm',
40+
description: 'ID of the label to delete',
41+
},
42+
},
43+
44+
request: {
45+
url: (params: GmailDeleteLabelParams) => `${GMAIL_API_BASE}/labels/${params.labelId}`,
46+
method: 'DELETE',
47+
headers: (params: GmailDeleteLabelParams) => ({
48+
Authorization: `Bearer ${params.accessToken}`,
49+
'Content-Type': 'application/json',
50+
}),
51+
},
52+
53+
transformResponse: async (response: Response, params?: GmailDeleteLabelParams) => {
54+
if (!response.ok) {
55+
const data = await response.json()
56+
return {
57+
success: false,
58+
output: { deleted: false, labelId: params?.labelId ?? '' },
59+
error: data.error?.message || 'Failed to delete label',
60+
}
61+
}
62+
63+
return {
64+
success: true,
65+
output: {
66+
deleted: true,
67+
labelId: params?.labelId ?? '',
68+
},
69+
}
70+
},
71+
72+
outputs: {
73+
deleted: { type: 'boolean', description: 'Whether the label was successfully deleted' },
74+
labelId: { type: 'string', description: 'ID of the deleted label' },
75+
},
76+
}

apps/sim/tools/gmail/get_draft.ts

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { GMAIL_API_BASE } from '@/tools/gmail/utils'
2+
import type { ToolConfig } from '@/tools/types'
3+
4+
interface GmailGetDraftParams {
5+
accessToken: string
6+
draftId: string
7+
}
8+
9+
interface GmailGetDraftResponse {
10+
success: boolean
11+
output: {
12+
id: string
13+
messageId?: string
14+
threadId?: string
15+
to?: string
16+
from?: string
17+
subject?: string
18+
body?: string
19+
labelIds?: string[]
20+
}
21+
}
22+
23+
export const gmailGetDraftV2Tool: ToolConfig<GmailGetDraftParams, GmailGetDraftResponse> = {
24+
id: 'gmail_get_draft_v2',
25+
name: 'Gmail Get Draft',
26+
description: 'Get a specific draft from Gmail by its ID',
27+
version: '2.0.0',
28+
29+
oauth: {
30+
required: true,
31+
provider: 'google-email',
32+
},
33+
34+
params: {
35+
accessToken: {
36+
type: 'string',
37+
required: true,
38+
visibility: 'hidden',
39+
description: 'Access token for Gmail API',
40+
},
41+
draftId: {
42+
type: 'string',
43+
required: true,
44+
visibility: 'user-or-llm',
45+
description: 'ID of the draft to retrieve',
46+
},
47+
},
48+
49+
request: {
50+
url: (params: GmailGetDraftParams) => `${GMAIL_API_BASE}/drafts/${params.draftId}?format=full`,
51+
method: 'GET',
52+
headers: (params: GmailGetDraftParams) => ({
53+
Authorization: `Bearer ${params.accessToken}`,
54+
'Content-Type': 'application/json',
55+
}),
56+
},
57+
58+
transformResponse: async (response: Response) => {
59+
const data = await response.json()
60+
61+
if (!response.ok) {
62+
return {
63+
success: false,
64+
output: { id: '' },
65+
error: data.error?.message || 'Failed to get draft',
66+
}
67+
}
68+
69+
const message = data.message || {}
70+
const headers = message.payload?.headers || []
71+
const getHeader = (name: string): string | undefined =>
72+
headers.find((h: Record<string, string>) => h.name.toLowerCase() === name.toLowerCase())
73+
?.value
74+
75+
let body = ''
76+
if (message.payload?.body?.data) {
77+
body = Buffer.from(message.payload.body.data, 'base64').toString()
78+
} else if (message.payload?.parts) {
79+
const textPart = message.payload.parts.find(
80+
(part: Record<string, unknown>) => part.mimeType === 'text/plain'
81+
)
82+
if (textPart?.body?.data) {
83+
body = Buffer.from(textPart.body.data, 'base64').toString()
84+
}
85+
}
86+
87+
return {
88+
success: true,
89+
output: {
90+
id: data.id,
91+
messageId: message.id ?? undefined,
92+
threadId: message.threadId ?? undefined,
93+
to: getHeader('To'),
94+
from: getHeader('From'),
95+
subject: getHeader('Subject'),
96+
body: body || undefined,
97+
labelIds: message.labelIds ?? undefined,
98+
},
99+
}
100+
},
101+
102+
outputs: {
103+
id: { type: 'string', description: 'Draft ID' },
104+
messageId: { type: 'string', description: 'Gmail message ID', optional: true },
105+
threadId: { type: 'string', description: 'Gmail thread ID', optional: true },
106+
to: { type: 'string', description: 'Recipient email address', optional: true },
107+
from: { type: 'string', description: 'Sender email address', optional: true },
108+
subject: { type: 'string', description: 'Draft subject', optional: true },
109+
body: { type: 'string', description: 'Draft body text', optional: true },
110+
labelIds: {
111+
type: 'array',
112+
items: { type: 'string' },
113+
description: 'Draft labels',
114+
optional: true,
115+
},
116+
},
117+
}

0 commit comments

Comments
 (0)