AI agents and Operators are the next big things in software development. In a previous post, we discussed how AI agents are taking center stage by seamlessly integrating with multiple digital tools to be more efficient. In this post, we will be building a personal assistant that can use different types of tools. Both simple unauthenticated tools and more complex authenticated tools like Gmail, Calendar, and Google Drive.
If you haven't read the previous post, I recommend you do so before continuing to better understand tool calling in AI agents and how security is currently handled.

To keep this post focused on tool calling, we will only focus on the AI agent part and not the UI. Each step of the tutorial, including building the UI, can be found as a distinct commit in the GitHub repository.
Technology Stack
We will use a Next.js application called Assistant 0 as the base, Assistant 0 is an AI personal assistant that consolidates your digital life by dynamically accessing multiple tools to help you stay organized and efficient.
Apart from Next.js, we mainly use the following technologies:
- LangGraph.js and LangChain's JavaScript framework for building agentic workflows.
- LangChain community tools.
- Vercel's AI SDK to stream response tokens to the client and display the incoming messages.
- The Auth0 AI SDK and Auth0 Next.js SDK to secure the application and call third-party APIs.
Prerequisites
You will need the following tools and services to build this application:
- Bun v1.2 or NodeJS v20
- An Auth for GenAI account. Create one.
- An OpenAI account and API key. Create one or use any other LLM provider supported by LangChain.
- A SerpAPI account and API key. Create one.
- A Google account for Gmail, Calendar, and Drive. Preferably a new one for testing.
Getting Started
First clone the repository and install the dependencies:
git clone https://github.com/auth0-samples/auth0-assistant0.git cd auth0-assistant0 git switch step-1 # so that we skip building the UI and get straight to the AI agent bun install # or npm install
Next, you'll need to set up environment variables in your repo's
.env.local
file. Copy the .env.example
file to .env.local
. To start you'll just need to add your OpenAI API key.Now, start the development server:
bun dev # or npm run dev
Open
http://localhost:3000
with your browser to see the result! Ask the agent something, and you'll see a streamed response.A Peek into the Code
The application is structured as below:
: Contains the Next.js application routes, layout, and pages.src/app
: Home page with chat interface.src/app/page.tsx
: API route for handling chat requests. This is where the AI agent is defined.src/api/chat/route.ts
: UI components used in the application.src/components
: Services and configurations. This is where custom tools will be defined.src/lib
: Utility functionssrc/utils
Let's take a look at the
src/api/chat/route.ts
file where the AI agent is defined.// src/api/chat/route.ts // ... const AGENT_SYSTEM_TEMPLATE = `You are a personal assistant named Assistant0. You are a helpful assistant that can answer questions and help with tasks. You have access to a set of tools, use the tools as needed to answer the user's question.`; export async function POST(req: NextRequest) { // ... Process the request and get messages // Create a new OpenAI chat model const llm = new ChatOpenAI({ model: 'gpt-4', temperature: 0 }); const tools = []; // Use a prebuilt LangGraph agent. const agent = createReactAgent({ llm, tools, messageModifier: new SystemMessage(AGENT_SYSTEM_TEMPLATE), }); // Stream back all generated tokens and steps from their runs. const eventStream = agent.streamEvents({ messages }, { version: 'v2' }); // Log tool calling data. Only in development mode const transformedStream = logToolCallsInDevelopment(eventStream); // Adapt the LangChain stream to Vercel AI SDK Stream return LangChainAdapter.toDataStreamResponse(transformedStream); }
The route uses a prebuilt LangGraph agent and OpenAI chat model. No tools are defined yet; we will add them in the next section.
Here is a high-level architecture of the application with the tools we will be adding in this post.
Now that the base application is running, let's start building our personal assistant with tool-calling capabilities.
Add Tools to the Assistant
If you recall the previous post, we discussed authenticated and unauthenticated tools. Let's start with an unauthenticated tool and then proceed to add more complex tools like Gmail, Calendar, and Google Drive.
Add a Calculator Tool
We will start with a simple calculator tool from LangChain community tools to perform basic arithmetic operations.
Install the
@langchain/community
package.bun add @langchain/community # or npm i @langchain/community
Update the
src/api/chat/route.ts
file with the following code.// src/api/chat/route.ts import { Calculator } from '@langchain/community/tools/calculator'; // ... export async function POST(req: NextRequest) { // ... const tools = [new Calculator()]; // ... }
It's that simple! Now try asking the assistant to perform some calculations. For example,
what is (2+56) x 200
. You should see the answer in the UI. If you check the console where Next.js dev server (bun dev
) is running, you should see the tool calling data like below.Tool calls state: { "call_E3Za9klJcJ5TADWiMj8XfjdB": { "name": "calculator", "args": "{\n \"input\": \"(2+56) * 200\"\n}" } } POST /api/chat 200 in 8382ms
Add a Web Search Tool
Now let's add a web search tool to search the web for information. Let's use SerpAPI for this. It's an authenticated tool. But since this is going to be API usage and not a user impersonation, we can simply use an API key.
First, get an API key from SerpAPI and add that to your
.env.local
file.SERPAPI_API_KEY=your-api-key
Now, update the
src/api/chat/route.ts
file with the following code.// src/api/chat/route.ts import { SerpAPI } from '@langchain/community/tools/serpapi'; // ... export async function POST(req: NextRequest) { // ... const tools = [new Calculator(), new SerpAPI()]; // ... }
Again, quite simple! Now try asking the assistant to search the web for information. For example,
What is the top 10 news today?
. You will see the response in the UI.If you check the console, you should see the tool calling data like the one below.
Tool calls state: { "call_e8vqAPW45vMsUbFiIF9SW5Yn": { "name": "search", "args": "{\n \"input\": \"top 10 news today\"\n}" } } POST /api/chat 200 in 15443ms
This corresponds to the
step-2
branch in case you want to double-check your code.git switch step-2
Add Gmail Tools
Now, let's get into business. I know you are here for this part, not to learn about adding calculators and web search tools. But before we can add a Gmail tool, we need to add Auth0 authentication to the application since we will be using Auth0's Token Vault feature to get access tokens to call the Gmail API via a tool.
Add Auth0 Authentication
First, let's add Auth0 as the authentication provider to the application.
Follow the Auth0 Next.js quickstart guide to set up the application.
If you want to create the Auth0 application manually, create a new application (Type: Regular Web App, Callback URL:
) and get the client ID and client secret.http://localhost:3000/auth/callback
Update the
.env.local
file with credentials, and create the src/lib/auth0.ts
and src/middleware.ts
files.If you want to skip to the next step, check out the
step-3
branch. The full changelog for this step is on GitHub.git switch step-3
Configure Auth0 for Gmail
To use Gmail API with Auth0 via a Google Social Connection, you need to do the following:
- Create Google OAuth 2.0 credentials
- Configure Gmail social connection in Auth0
You can follow the Google Sign-in and Authorization guide from Auth0 to configure this.
Add Gmail Search Tool
We can add Gmail tools now that we have Auth0 authentication in place. Let's start with a Gmail search tool. This tool will allow the assistant to search for emails in the user's Gmail account.
First, we need to get an access token for the Gmail API. We can get this using the Auth0 Token Vault feature.
Update
src/lib/auth0.ts
with the following code.// src/lib/auth0.ts // ... export const auth0 = new Auth0Client({ // this is required to get federated access tokens from services like Google authorizationParameters: { access_type: 'offline', prompt: 'consent', }, }); export const getGoogleAccessToken = async () => { const { token } = await auth0.getAccessTokenForConnection({ connection: 'google-oauth2', }); return token; };
Install the
googleapis
package.bun add googleapis # or npm i googleapis
Import the
GmailSearch
tool from LangChain community tools and update the src/api/chat/route.ts
file with the following code.// src/api/chat/route.ts import { GmailSearch } from '@langchain/community/tools/gmail'; import { getGoogleAccessToken } from '@/lib/auth0'; // ... export async function POST(req: NextRequest) { // ... // Update the LLM model to gpt-4o-mini to support a larger context window const llm = new ChatOpenAI({ model: 'gpt-4o-mini', temperature: 0, }); // Get the access token via Auth0 const accessToken = await getGoogleAccessToken(); // Provide the access token to the Gmail tools const gmailParams = { credentials: { accessToken }, }; const tools = [new Calculator(), new SerpAPI(), new GmailSearch(gmailParams)]; // ... }
We are all set! Now try asking the assistant to search for emails. For example,
What is the latest unread email?
. You should see the response in the UI.If you check the console, you should see the tool calling data like the one below.
Tool calls state: { "call_rCzcZRjueyD8p2kzTmeZMiSK": { "name": "search_gmail", "args": "{\"query\":\"is:unread\",\"maxResults\":1,\"resource\":\"messages\"}" } } POST /api/chat 200 in 10892ms
Add Gmail Draft Tool
We will add a Gmail draft tool next. This tool will allow the assistant to draft emails on behalf of the user.
Update the
src/api/chat/route.ts
file with the following code.// src/api/chat/route.ts import { GmailDraft } from '@langchain/community/tools/gmail'; // ... export async function POST(req: NextRequest) { // ... const tools = [ new Calculator(), new SerpAPI(), new GmailSearch(gmailParams), new GmailDraft(gmailParams), ]; // ... }
Now, ask the assistant to draft an email. For example,
Draft an email to John Doe about the meeting tomorrow.
You should see the response in the UI.If you check the console, you should see the tool calling data like the one below.
Tool calls state: { "call_pPAk6rFYJSSyAuYkNWFf5KYX": { "name": "create_gmail_draft", "args": "{\"message\":\"Hi John,\\n\\nI hope this message finds you well. I wanted to … [Your Name]\",\"to\":[\"john.doe@example.com\"],\"subject\":\"Meeting Reminder\",\"cc\":[],\"bcc\":[]}" } } POST /api/chat 200 in 7594ms
You can find the full changelog of these two steps on GitHub.
This corresponds to the
step-4
branch in case you want to double-check your code.git switch step-4
Learn more about AI Agents and Auth for GenAI
You have successfully built an AI personal assistant that can search the web, search your emails, and draft emails. In the next chapter, we will add more tools like User Info, Google Calendar, Google Drive, and Slack to the assistant.
This tutorial is part of a series of posts on tool-calling Agents with Auth for GenAI, we learned how to add tools to an AI agent and how to secure them using Auth0.
Sign up for Auth for GenAI in Developer Preview.
Before you go, we have some great news to share: we are working on more content and sample apps in collaboration with amazing GenAI frameworks like LlamaIndex, LangChain, CrewAI, Vercel AI, and GenKit. Auth for GenAI is our new product to help you protect your user's information in GenAI-powered applications. Make sure to join the Auth0 Lab Discord server to hear more and ask questions.
About the author

Deepu K Sasidharan
Staff Developer Advocate