How to Build a Gmail Assistant

How to Build a Gmail Assistant

Welcome to the first post of OneDayBuild, a newsletter where I build small but useful AI applications sharing tips & tricks in the process.

I'm not trying to become a solo founder of a software product that I need to maintain and manage. Instead, I want to build simple and fun tools that solve one or more problems for me.

As an experienced programmer and AI engineer, my goal is to show you clever ways to use AI in your products and in doing so, help unlock your AI creativity. The applications themselves won't be perfect, but I succeed when you think,

"That's really clever - I can use the same idea or approach to solve this other problem".

You're probably aware that generative AI has changed what it means to be a developer. Today, you can go from idea to prototype in hours without writing a single line of code yourself.

Therefore, I'll focus on ideas and tips that can make you a better AI-powered developer rather than the code itself.

The real magic happens when you:

  • Code faster and smarter with AI
  • Know when to trust AI vs. when to think for yourself
  • Leverage technical knowledge to make your prompts precise
  • Avoid common pitfalls by learning from real builds
  • Design AI workflows that reliably solve user problems

I'll do my best to help you improve on each by sharing my own builds. Subscribe if you like the sound of that! ๐Ÿ˜Š


Blueprint ๐Ÿ“

I run a small machine learning company specialized in automated inspection of critical assets and infrastructure. Communicating with current and potential customers is one of my most frequent activities.

There are many AI tools that help you craft better sales emails, but most of them are too complicated for my taste. What I want instead is a lightweight Chrome extension that can instantly generate a reply based on the ongoing conversation and knowledge of my company.

I want to control the email generation by providing relevant information and suggestions to the AI. It won't change the world, but perhaps it can make me 1% more effective.

Features & Components

Hereโ€™s what the extension needs to do:

  • Work as a Chrome plugin for Gmail
  • Allow me to define reusable instructions for all emails
  • Support ad-hoc, email-specific instructions
  • Read messages in the current Gmail thread
  • Generate email drafts using AI
  • Copy the generated email to the clipboard

Tools & Resources

To build it, Iโ€™m using:

  • Cursor as my code editor
  • OpenAI to generate emails
  • Gmail API to access thread content

Access to code

You can access the full code base at: https://github.com/oscarleoo/gmail-assistant.

Feel free to use it or change it however you like. There's a guide on how to install and use the plugin. Note that you need to use your own API keys for OpenAI and Google authentication (see instructions).


Building โš’๏ธ

In this section, I will go through some specific aspects of building this plugin. Since AI does most of the coding these days, I'll focus on details that can help you become a better developer and where tools like Cursor may struggle.

Laying the foundation

Since I'm a programmer myself, I often edit or change the code generated by Cursor. That's why I ask the AI to generate code with good structure and familiar tools right from the start.

If you havenโ€™t built a Chrome extension before, a common pitfall with tools like Cursor is that they default to generating a bare-bones extension โ€” just a manifest and a couple of JavaScript files โ€” without any dev tooling or build setup.

It can still work, but you may run into problems later on when you want to import libraries or structure your code into logical folders and files. Luckily, handling configurations is one of the more rule-based programming tasks available and something the AI does with few errors.

Therefore, I often start my project by asking Cursor to generate a minimalistic starting point with the tooling I need. Here's my first prompt for this project to give you an example.

This is an empty folder where I want you to create a chrome extension that uses Webpack. First, Here's a list describing what I need. I will provide more details about the features later.

1. A chrome extension that uses Webpack
2. The extension should be used for Gmail
3. The extension requires Google authentication

Accessing Gmail messages - Part 1

It's surprisingly hard to access the content in a Gmail thread by parsing the DOM elements or by monitoring the network requests. Gmail, and many other websites loads content dynamically as the user interacts with the page, which makes scraping more difficult. This approach is also vulnerable to changes from the website owner.

Instead, I wanted to use the Gmail API which means I need to find the ID that allows me to access messages from the ongoing conversation. It was clear that both ChatGPT and Cursor struggled to find a good solution for that.

When you enter a thread in Gmail, you have a url that looks something like this: https://mail.google.com/mail/u/0/#inbox/FMfcgzQbfLcZCQFWzpbbKpPThlKQkgrP

The string at the end of the url is not the thread ID, but that's what Cursor tried using. When that didn't work, it started searching for the ID in network requests, but that didn't work either.

Instead, I looked at the DOM elements and found that the h2 element at the top of the email thread has an attribute called data-legacy-thread-id containing the correct ID.

const msgElements = document.querySelectorAll("h2[data-legacy-thread-id]");
if (msgElements.length > 0) {
  const threadId = msgElements[0].getAttribute("data-legacy-thread-id");

  if (threadId !== currentThreadId) {
    currentThreadId = threadId;
    chrome.runtime.sendMessage({
      action: "saveThreadId",
      threadId: threadId,
    });
  }
}

This is a good example of a programming task that's challenging for AI and where you need to do some manual work. Looking at network requests or DOM elements for an individual website is too specific.

๐Ÿ’ก
It could be useful with a tool that loads the DOM and show relevant elements to Cursor when it tries to perform tasks like this.

I send the thread ID to the background script, which stores it in a local variable. When the user wants to generate a new email, I fetch all the messages in the thread using the Gmail API.

Accessing Gmail messages - Part 2

Getting the message content from the API also involves a few tricks. The actual text appears as a custom base64 encoded string in a nested attribute called parts. Cursor can handle problems like that for Gmail because it's a well used, but custom implementations like that can be a challenge when working with a less common API.

Another challenge is that the message content contains more than just the message. Email providers like Gmail and Outlook include additional information such as previous messages.

In long email threads, irrelevant content outnumber the relevant parts, and you probably want to do some parsing to extract the stuff that matters.

Here are four possible approaches:

  1. Let the email generating AI see everything including irrelevant content
  2. Use another AI call to remove irrelevant content before generating the email
  3. Apply rules and regex to extract what's relevant programmatically.
  4. A combination of 1 and 3

I recommend the last one because it's easy to get rid of most appendage with rules but difficult to remove all of it.

When turning the messages into a prompt, I have the following format.

Generate a sales email (only the body of the email) based on the following information:

# Current conversation:

Email 1:

From: [sender email]
To: [receiver email]
CC: [additional emails]
Date: [message date]

[message content]

Email 2:
...

I include information about the sender, receiver, cc, and date since that gives the AI relevant information such as names and timeline.

User settings and instructions

I implemented two ways for the user to add relevant information and instruction to the email assistant AI.

The first is a settings panel that stores information long-term. I can write down everything that I think is relevant for my email assistant including things like:

  • Information about my company
  • Details about our offer and pricing
  • Success stories from customers
  • Guidelines on style and language

Here's what it looks like.

Since I'm building something that others can download and use, I've also added a field where you add your OpenAI key. If you want to use another AI provider, just edit generateEmail.js.

There's also a second way to add more ad-hoc instructions. The idea is that you might want to generate a specific type of email or present an idea that just came to mind.

In that case, you can use Alt+Shift+I command which opens a simple input field designed for email specific instructions.

It's often good to have both long-term and short-term instructions when you're building a tool that use AI to generate content.

Generating an email

There are many AI providers and APIs that you can use to generate the actual email. I'm using OpenAI and ChatGPT-4o for this extension.

The final prompt looks like this.

Generate a sales email (only the body of the email) based on the following information:

# Relevant information from the user

[Settings instructions]

# Current conversation:

Email 1:

From: [sender email]
To: [receiver email]
CC: [additional emails]
Date: [message date]

[message content]

Email 2:
...

# Email-specific instructions

[Ad-hoc instructions]

Since I wanted to build something simple, I just copy the generated email to the clipboard and paste it into the response box myself.

You can paste it into the response automatically but then you have to handle formatting, opening a response box programmatically, and other situations. I didn't want any headaches.

Here's a short demo of what it looks like:

0:00
/0:11

Summary ๐Ÿ‘จโ€๐Ÿซ

Today's project was to build a chrome extension that can help me write better emails using information about my company and by accessing emails from an ongoing thread.

Some tips to keep in mind for your own projects.

  • Donโ€™t rely on scraping. Monitoring the DOM or network requests is often too brittle and site-specific.
  • Ask for structure up front. Tell Cursor to generate clean code scaffolding so itโ€™s easier to edit later.
  • Communicate your edits. If you change code manually, tell Cursor โ€” otherwise it may overwrite your work.
  • Keep it simple. Sometimes copy-to-clipboard beats fighting with extra automation.

Thank you for reading! ๐Ÿ˜„