Everything Apps Script
This repository is a monorepo containing tools, projects, and more related to Apps Script.
About: I am a Senior Developer Relations Engineer at Google for Google Workspace.
I had the opportunity to attend the Winter Tech Forum (WTF) in Crested Butte, CO last week and I had a blast! As a remote worker, it was an excellent opportunity to engage and interact with an awesome group of 30ish attendees.
This unconf or open space conference is setup with sessions most mornings and workshops or free time in the afternoon, but on Wednesday it's a hack day with the sharing of results in the evening.
All of this wouldn't happen without Bruce Eckel, who also graciously opened his home to sessions and hacking on code.
While many of the groups sought out implementing layers over the Model Context Protocol, I decided to explore agents and Apps Script (aka tool calling in a loop given a prompt).
At the heart of my project is the agent loop, a process that enables AI to reason and act autonomously. Here’s a breakdown of the steps:
doNothing()
function is triggered, indicating the task is complete.I encountered a fascinating challenge during this process: occasional redundant tool calls. Specifically, the createDraftReply()
function was sometimes invoked multiple times within different loops. The shortcut solution was to simply remove this from the available function calls once it had been done. I also had success by asking Gemini to plan before doing anything else.
searchGmail
One of the key tools I exposed was the searchGmail
function, which leverages the Gmail API. This function allows the agent to search for related Gmail threads and messages using a flexible query syntax. Here's a glimpse of the JSON definition:
{
"name": "searchGmail",
"description": "Search for related Gmail threads and messages. The Gmail query syntax is described here: https://developers.google.com/gmail/api/guides/query. Here are some examples: 'in:inbox after:2025-01-01', 'subject:invoice', ...",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Gmail search query"
}
},
"required": ["query"]
}
}
This tool empowers the AI to find relevant information within Gmail, enabling more context-aware actions.
To manage the tool-calling process, I iterated through the Vertex AI response, identifying function calls and their arguments. Here’s a snippet of the code:
const functionReponsePart: FunctionResponsePart[] =
content.parts
.filter(isFunctionCallPart)
.map((part) => {
const name = part.functionCall.name;
const args = part.functionCall.args;
if (name === "doNothing") {
isDone = true;
}
// ...
I also used Zod for runtime type checking (and generating the tool schemas for Vertex), ensuring the arguments passed to the functions were valid. Interestingly, I never encountered a validation failure, which speaks to the reliability of Vertex AI's output.
const { fn, schema } = FUNCTIONS[name];
const content = fn(schema.parse(args), {
thread,
threadId: thread.getId(),
});
return {
functionResponse: {
name,
response: {
content,
},
},
};
To illustrate the agent’s capabilities, here’s an example of the tool calls made when processing an email:
Meeting Agenda - Calling applyLabels with {"labelNames":["Tech"]}
Meeting Agenda - Calling markAsImportant with {"important":false}
Meeting Agenda - Calling createDraftReply with {"reply":"Sounds good!"}
Meeting Agenda - Calling searchGmail with {"query":"from:Justin Poehnelt (Google Docs) subject:Meeting Agenda"}
Meeting Agenda - Calling doNothing with {}
This demonstrates how the agent can perform a sequence of actions, from labeling emails to creating draft replies, all driven by AI.
The code for this is available on GitHub at https://github.com/jpoehnelt/apps-script/tree/main/projects/apps-script-agent.
Some things to note:
This repository is a monorepo containing tools, projects, and more related to Apps Script.
Let me know if you have any questions on the code!
Attending this week long event was the best tech related experience I have had in what seems like forever. The atmosphere was one of support, vulnerability, and community growth. Bruce and the veterans of this event were incredibly welcoming to newcomers, no matter our background.
As I reflect back on this experience, I know that I will be doing everything I can to make this an annual retreat for my own well-being and personal growth!
[hidden by post author]