improvement: added gmail draft operation

This commit is contained in:
Adam Gough
2025-07-11 15:47:14 -07:00
parent e216b176ac
commit 1afdeed244
4 changed files with 130 additions and 32 deletions
+25 -30
View File
@@ -14,17 +14,17 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
icon: GmailIcon,
subBlocks: [
// Operation selector
// {
// id: 'operation',
// title: 'Operation',
// type: 'dropdown',
// layout: 'full',
// options: [
// { label: 'Send Email', id: 'send_gmail' },
// // { label: 'Read Email', id: 'read_gmail' },
// // { label: 'Search Emails', id: 'search_gmail' },
// ],
// },
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
layout: 'full',
options: [
{ label: 'Send Email', id: 'send_gmail' },
// { label: 'Read Email', id: 'read_gmail' },
{ label: 'Draft Email', id: 'draft_gmail' },
],
},
// Gmail Credentials
{
id: 'credential',
@@ -48,7 +48,7 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
type: 'short-input',
layout: 'full',
placeholder: 'Recipient email address',
// condition: { field: 'operation', value: 'send_gmail' },
condition: { field: 'operation', value: ['send_gmail', 'draft_gmail'] },
},
{
id: 'subject',
@@ -56,7 +56,7 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
type: 'short-input',
layout: 'full',
placeholder: 'Email subject',
// condition: { field: 'operation', value: 'send_gmail' },
condition: { field: 'operation', value: ['send_gmail', 'draft_gmail'] },
},
{
id: 'body',
@@ -64,7 +64,7 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
type: 'long-input',
layout: 'full',
placeholder: 'Email content',
// condition: { field: 'operation', value: 'send_gmail' },
condition: { field: 'operation', value: ['send_gmail', 'draft_gmail'] },
},
// Read Email Fields - Add folder selector
// {
@@ -130,22 +130,17 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
// },
],
tools: {
access: ['gmail_send', 'gmail_read', 'gmail_search'],
access: ['gmail_send', 'gmail_draft'],
config: {
tool: (params) => {
// Since we only have send_gmail now, we can simplify this
return 'gmail_send'
// switch (params.operation) {
// case 'send_gmail':
// return 'gmail_send'
// case 'read_gmail':
// return 'gmail_read'
// case 'search_gmail':
// return 'gmail_search'
// default:
// throw new Error(`Invalid Gmail operation: ${params.operation}`)
// }
switch (params.operation) {
case 'send_gmail':
return 'gmail_send'
case 'draft_gmail':
return 'gmail_draft'
default:
throw new Error(`Invalid Gmail operation: ${params.operation}`)
}
},
params: (params) => {
// Pass the credential directly from the credential field
@@ -158,13 +153,13 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
return {
...rest,
credential, // Keep the credential parameter
credential,
}
},
},
},
inputs: {
// operation: { type: 'string', required: true },
operation: { type: 'string', required: true },
credential: { type: 'string', required: true },
// Send operation inputs
to: { type: 'string', required: false },
+101
View File
@@ -0,0 +1,101 @@
import type { ToolConfig } from '../types'
import type { GmailSendParams, GmailToolResponse } from './types'
const GMAIL_API_BASE = 'https://gmail.googleapis.com/gmail/v1/users/me'
export const gmailDraftTool: ToolConfig<GmailSendParams, GmailToolResponse> = {
id: 'gmail_draft',
name: 'Gmail Draft',
description: 'Draft emails using Gmail',
version: '1.0.0',
oauth: {
required: true,
provider: 'google-email',
additionalScopes: ['https://www.googleapis.com/auth/gmail.compose'],
},
params: {
accessToken: {
type: 'string',
required: true,
description: 'Access token for Gmail API',
},
to: {
type: 'string',
required: true,
description: 'Recipient email address',
},
subject: {
type: 'string',
required: true,
description: 'Email subject',
},
body: {
type: 'string',
required: true,
description: 'Email body content',
},
},
request: {
url: () => `${GMAIL_API_BASE}/drafts`,
method: 'POST',
headers: (params: GmailSendParams) => ({
Authorization: `Bearer ${params.accessToken}`,
'Content-Type': 'application/json',
}),
body: (params: GmailSendParams): Record<string, any> => {
const email = [
'Content-Type: text/plain; charset="UTF-8"',
'MIME-Version: 1.0',
`To: ${params.to}`,
`Subject: ${params.subject}`,
'',
params.body,
].join('\n')
return {
message: {
raw: Buffer.from(email).toString('base64url'),
},
}
},
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message || 'Failed to draft email')
}
return {
success: true,
output: {
content: 'Email drafted successfully',
metadata: {
id: data.id,
message: {
id: data.message?.id,
threadId: data.message?.threadId,
labelIds: data.message?.labelIds,
},
},
},
}
},
transformError: (error) => {
if (error.error?.message) {
if (error.error.message.includes('invalid authentication credentials')) {
return 'Invalid or expired access token. Please reauthenticate.'
}
if (error.error.message.includes('quota')) {
return 'Gmail API quota exceeded. Please try again later.'
}
return error.error.message
}
return error.message || 'An unexpected error occurred while drafting email'
},
}
+2 -1
View File
@@ -1,5 +1,6 @@
import { gmailDraftTool } from './draft'
import { gmailReadTool } from './read'
import { gmailSearchTool } from './search'
import { gmailSendTool } from './send'
export { gmailSendTool, gmailReadTool, gmailSearchTool }
export { gmailSendTool, gmailReadTool, gmailSearchTool, gmailDraftTool }
+2 -1
View File
@@ -25,7 +25,7 @@ import {
githubPrTool,
githubRepoInfoTool,
} from './github'
import { gmailReadTool, gmailSearchTool, gmailSendTool } from './gmail'
import { gmailDraftTool, gmailReadTool, gmailSearchTool, gmailSendTool } from './gmail'
import { searchTool as googleSearchTool } from './google'
import {
googleCalendarCreateTool,
@@ -142,6 +142,7 @@ export const tools: Record<string, ToolConfig> = {
gmail_send: gmailSendTool,
gmail_read: gmailReadTool,
gmail_search: gmailSearchTool,
gmail_draft: gmailDraftTool,
whatsapp_send_message: whatsappSendMessageTool,
x_write: xWriteTool,
x_read: xReadTool,