1. Before you begin
What is Vertex AI?
Vertex AI is Google Cloud's unified development platform for building, deploying, and scaling enterprise-grade AI agents and applications. It provides developers and data scientists with the sophisticated tools needed to architect custom agentic workflows that are deeply integrated with global-scale infrastructure.
- Access the Model Garden: Choose from over 150+ foundation models, including the full Gemini family, third-party models, and specialized open-source models to find the right fit for specific agent tasks.
- Architect complex orchestration: Vertex AI provides the framework to design autonomous agents that use reasoning to plan, execute multi-step tasks, and call external APIs.
- Enterprise-grade grounding: Connect agents to live business data including high-performance RAG (Retrieval-Augmented Generation) to eliminate hallucinations and ensure factual accuracy.
- DevOps: Seamlessly integrate agent development into existing CI/CD pipelines with robust SDKs, APIs, and evaluation tools to measure agent performance and safety at scale.
- Industrial-strength security: Vertex AI ensures that customer data used for training or grounding remains private, encrypted, and compliant with global residency requirements.
- Optimized infrastructure: Scale agentic workloads effortlessly across Google's world-class TPU and GPU clusters, ensuring low-latency performance even for the most demanding global applications.
What is Google Workspace?
Google Workspace is a collection of cloud-based productivity and collaboration solutions designed for individuals, schools, and businesses:
- Communication: Professional email services (Gmail), video conferencing (Meet), and team messaging (Chat).
- Content Creation: Tools for writing documents (Docs), building spreadsheet (Sheets), and designing presentations (Slides).
- Organization: Shared calendars (Calendars) and digital note-taking (Keep).
- Storage: Centralized cloud space for saving and sharing files securely (Drive).
- Management: Administrative controls to manage users and security settings (Workspace Admin Console).
What kind of custom integrations?
Google Workspace and Vertex AI create a powerful feedback loop where Workspace provides real-time data and collaboration context, while Vertex AI offers the models, agentic reasoning, and orchestration required to automate intelligent workflows.
- Smart connectivity: Google-managed data stores, APIs, and MCP servers (Google-managed and custom) allows agents to securely and seamlessly access Workspace data and take actions on behalf of users.
- Custom agents: Using no-code designers or pro-code frameworks, teams can build specialized agents grounded in admin-governed Workspace data and actions.
- Native integration: Workspace add-ons bridge the gap between AI systems and applications like Chat and Gmail, whether through dedicated UI components or background processes. This allows agents to meet users exactly where they are for instantaneous, context-aware assistance.
By combining the robust productivity ecosystem of Google Workspace with the advanced agentic power of Vertex AI, organizations can transform their operations through custom, data-grounded AI agents that automate complex workflows directly within the tools their teams already use every day.
Prerequisites
If you want to follow all the steps on your own environment, you'll need:
- A basic knowledge of Google Cloud and Python.
- A Google Cloud project you are an owner of with billing. To check that an existing project has billing enabled, see Verify the billing status of your projects. To create a project and set up billing, see Create a Google Cloud project. To change project ownership, see Manage project members or change project ownership.
- A Business or Enterprise Google Workspace account with access to Google Chat and smart features turned on.
- The Google Cloud CLI installed and initialized for your Google Cloud project.
- Python 3.11+ installed, see instructions on the official Python website.
What you will build
In this codelab, we build three solutions with Vertex AI agents tightly integrated with Google Workspace. They'll demonstrate architectural patterns that can be used to interact with data, actions, and UIs.
Vertex AI Search app
This agent allows users to search data and take actions for Workspace in their natural language. It relies on the following elements:
- Model: Gemini.
- Data & actions: Vertex AI data stores for Google Workspace (Calendar, Gmail, Drive).
- Agent host: Vertex AI Search.
- UI: Vertex AI Search Web Widget.

Custom agent
This agent allows users to search data and take actions for Workspace in their natural language using custom tools and rules. It relies on the following elements:
- Model: Gemini.
- Data & actions: Vertex AI data stores for Google Workspace (Calendar, Gmail, Drive), Google-managed Vertex AI Search Model Context Protocol (MCP) server, custom tool function to send Google Chat messages (via Google Chat API).
- Agent building tools: Agent Development Kit (ADK).
- Agent host: Vertex AI Agent Engine.
- UI: ADK Web.


Agent as Google Workspace add-on
This agent allows users to search data for Workspace in their natural language within the context of Workspace app UIs. It relies on the following elements:
- Model: Gemini.
- Data & actions: Vertex AI data stores for Google Workspace (Calendar, Gmail, Drive), Google-managed Vertex AI Search Model Context Protocol (MCP) server, custom tool function to send Google Chat messages (via Google Chat API).
- Agent building tools: Agent Development Kit (ADK).
- Agent host: Vertex AI Agent Engine.
- UI: Google Workspace add on for Chat and Gmail (easily extendable to Calendar, Drive, Docs, Sheets, and Slides).
- Google Workspace add on: Apps Script, Vertex AI Agent Engine APIs, contextual (selected Gmail message).


What you will learn
- The integration points between Vertex AI Search and Google Workspace that enable data and actions.
- The options to build custom agents hosted in Vertex AI.
- The ways users can access agents such as Vertex AI Search Web Widget and Google Workspace applications.
2. Set up
Before building solutions, we have to initialize the project's Vertex AI Applications settings, enable the required APIs, and create the Vertex AI Workspace data stores.
Review concepts
Vertex AI application
A Vertex AI application is a managed, end-to-end solution on Google Cloud that integrates machine learning models (such as generative AI agents or search engines) with enterprise data and specialized tools to perform complex tasks like semantic search, content generation, or automated customer interaction.
Vertex AI data store
A Vertex AI data store is an entity that contains the data ingested from a first-party data source such as Google Workspace or third-party applications such as Jira or Shopify. Data stores that contain data from third-party applications are also called data connectors.
Initiate Vertex AI Applications settings
Initialize the Vertex AI Applications settings to enable agent creation.
Open the Google Cloud console in a new tab, then follow these steps:
- Select your project.
- In the Google Cloud search field, go to AI Applications
- Click Continue and activate the API after reviewing and agreeing to the terms.
- Navigate to Settings.
- In the Authentication tab, edit global.

- Select Google Identity, then click Save.

Enable APIs
The Vertex AI Workspace data stores require the enablement of APIs:
- In the Google Cloud console, enable the Calendar, Gmail, and People APIs:

- Click Menu ☰ > APIs & Services > Enabled APIs & Services and then confirm that Google Calendar API, Gmail API, and People API are in the list.
Create data stores
Create the Google Drive data store:
- In the Google Cloud console, go to AI Applications, then navigate to Data Stores.
- Click + Create data store.
- In Source, under Google Drive, click Select.

- In Data, select All, and click Continue.

- In Configuration, set the Data connector name to
drive, and click Continue after reviewing and agreeing to the charges that may apply.

- In Pricing, select the pricing model you prefer and click Create. General pricing is recommended in the context of this codelab.
- You are automatically redirected to Data Stores where you can see the newly added data store.
Create the Google Calendar data store:
- Click + Create data store.
- In the Source, search for Google Calendar, and click Select.
- In the Actions section, click Skip.
- In the Configuration section, set the Data connector name to
calendar. - Click Create.
- You are automatically redirected to Data Stores where you can see the newly added data store.
Create the Google Gmail data store:
- Click + New data store.
- In the Source, search for Google Gmail, and click Select.
- In the Actions section, click Skip.
- In the Configuration section, set the Data connector name to
gmail. - Click Create.
- You are automatically redirected to Data Stores where you can see the newly added data store.
3. Vertex AI Search app
This agent allows users to search data and take actions for Workspace in their natural language. It relies on the following elements:
- Model: Gemini.
- Data & actions: Vertex AI data stores for Google Workspace (Calendar, Gmail, Drive).
- Agent host: Vertex AI Search.
- UI: Vertex AI Search Web Widget.
Review concepts
Vertex AI Search app
A Vertex AI Search app provides search results, actions, and agents to your end users. The term app can be used interchangeably with the term engine in the context of APIs. An app must be connected to a data store in order to use the data from it to serve search results, answers, or actions.
Vertex AI Search Web Widget
The Vertex AI Search Web Widget is a pre-built, customizable UI component that allows developers to embed an AI-powered search bar and results interface directly into a website with minimal coding.
Vertex AI Search Preview
Vertex AI Search Preview is a built-in testing environment within the Google Cloud Console that allows developers to validate search configurations and generative answers before seamlessly deploying those same settings to a production-ready Vertex AI Search web widget.
Review solution architecture

Create app
Create a new search app to anchor your data stores.
Open AI Applications > Apps from the Cloud console, then follow these steps:
- Click + Create app.
- In Type, under Custom search (general), click Create.

- In Configuration, check Enterprise edition features and Generative Responses after reviewing and agreeing to the prices.
- Set the App name to
codelab. - An ID is generated based on the name and is displayed under the field, copy it.
- Set Company name to
Codelab. - Set the Multi-region to
global (Global). - Click Continue.

- In Data, select the data stores drive, gmail, and calendar, then click Continue.

- In Pricing, select the pricing model you prefer and click Create. General pricing is recommended in the context of this codelab.
- The app is created and you are automatically redirected to AI Applications > Apps > codelab > App overview.
- Navigate to Connected data stores.
- After a few minutes, all connected data store statuses should be Active.

Configure Web Widget
Configure the visual appearance and behavior of the search widget.
- Navigate to Configurations.
- In the tab UI, set the Search type to Search with follow-ups, then click Save and publish.

Try app
Test the search app directly in the Google Cloud console.
- Navigate to Preview, the Web Widget is displayed.
- In the chat, type
Do I have any meetings today?and pressenter. - In the chat, type
Did I receive an email on March 1st 2026?and pressenter. - In the chat, type
Give me the title of the latest Drive file I createdand pressenter.

4. Custom agent
This agent allows users to search data and take actions for Workspace in their natural language using custom tools and rules. It relies on the following elements:
- Model: Gemini.
- Data & actions: Vertex AI data stores for Google Workspace (Calendar, Gmail, Drive), Google-managed Vertex AI Search Model Context Protocol (MCP) server, custom tool function to send Google Chat messages (via Google Chat API).
- Agent building tools: Agent Development Kit (ADK).
- Agent host: Vertex AI Agent Engine.
- UI: ADK Web.
Review concepts
Agent Development Kit (ADK)
The Agent Development Kit (ADK) is a specialized suite of tools and frameworks designed to simplify the creation of autonomous AI agents by providing pre-built modules for reasoning, memory management, and tool integration.
Model Context Protocol (MCP)
The Model Context Protocol (MCP) is an open standard designed to enable seamless, secure integration between AI applications and various data sources or tools through a universal, "plug-and-play" interface.
Function Tool
A Function tool is a predefined executable routine that an AI model can trigger to perform specific actions or retrieve real-time data from external systems, extending its capabilities beyond simple text generation.
ADK Web
ADK web is the built-in dev UI that comes along with the ADK SDK for easier development and debugging.
Review solution architecture

Review source code
agent.py
The following code authenticates with Vertex AI, initializes the Vertex AI Search MCP and Chat API tools, and defines the agent's behavior.
- Authentication: It retrieves the
ACCESS_TOKENfrom the environment variables to authenticate MCP and API calls. - Tools Setup: It initializes
vertexai_mcp, a toolset that connects to the Vertex AI Search Model Context Protocol (MCP) server, and thesend_direct_messagetool. This gives the agent the ability to search through your connected data stores and send Google Chat messages. - Agent Definition: It defines the
root_agentusing thegemini-2.5-flashmodel. The instructions tell the agent to prioritize using the search tool for information retrieval and thesend_direct_messagetool for actions, effectively grounding the agent in your enterprise data.
...
MODEL = "gemini-2.5-flash"
# Access token for authentication
ACCESS_TOKEN = os.environ.get("ACCESS_TOKEN")
if not ACCESS_TOKEN:
raise ValueError("ACCESS_TOKEN environment variable must be set")
VERTEXAI_SEARCH_TIMEOUT = 15.0
def get_project_id():
"""Fetches the consumer project ID from the environment natively."""
_, project = google.auth.default()
if project:
return project
raise Exception(f"Failed to resolve GCP Project ID from environment.")
def find_serving_config_path():
"""Dynamically finds the default serving config in the engine."""
project_id = get_project_id()
engines = discoveryengine_v1.EngineServiceClient().list_engines(
parent=f"projects/{project_id}/locations/global/collections/default_collection"
)
for engine in engines:
# engine.name natively contains the numeric Project Number
return f"{engine.name}/servingConfigs/default_serving_config"
raise Exception(f"No Discovery Engines found in project {project_id}")
def send_direct_message(email: str, message: str) -> dict:
"""Sends a Google Chat Direct Message (DM) to a specific user by email address."""
chat_client = chat_v1.ChatServiceClient(
credentials=Credentials(token=ACCESS_TOKEN)
)
# 1. Setup the DM space or find existing one
person = chat_v1.User(
name=f"users/{email}",
type_=chat_v1.User.Type.HUMAN
)
membership = chat_v1.Membership(member=person)
space_req = chat_v1.Space(space_type=chat_v1.Space.SpaceType.DIRECT_MESSAGE)
setup_request = chat_v1.SetUpSpaceRequest(
space=space_req,
memberships=[membership]
)
space_response = chat_client.set_up_space(request=setup_request)
space_name = space_response.name
# 2. Send the message
msg = chat_v1.Message(text=message)
message_request = chat_v1.CreateMessageRequest(
parent=space_name,
message=msg
)
message_response = chat_client.create_message(request=message_request)
return {"status": "success", "message_id": message_response.name, "space": space_name}
vertexai_mcp = McpToolset(
connection_params=StreamableHTTPConnectionParams(
url="https://discoveryengine.googleapis.com/mcp",
timeout=VERTEXAI_SEARCH_TIMEOUT,
sse_read_timeout=VERTEXAI_SEARCH_TIMEOUT,
headers={"Authorization": f"Bearer {ACCESS_TOKEN}"}
),
tool_filter=['search']
)
# Answer nicely the following user queries:
# - Please find my meetings for today, I need their titles and links
# - What is the latest Drive file I created?
# - What is the latest Gmail message I received?
# - Please send the following message to someone@example.com: Hello, this is a test message.
root_agent = LlmAgent(
model=MODEL,
name='enterprise_ai',
instruction=f"""
You are a helpful assistant that always uses the Vertex AI MCP search tool to answer the user's message, unless the user asks you to send a message to someone.
If the user asks you to send a message to someone, use the send_direct_message tool to send the message.
You MUST unconditionally use the Vertex AI MCP search tool to find answer, even if you believe you already know the answer or believe the Vertex AI MCP search tool does not contain the data.
The Vertex AI MCP search tool accesses the user's data through datastores including Google Drive, Google Calendar, and Gmail.
Only use the Vertex AI MCP search tool with servingConfig and query parameters, do not use any other parameters.
Always use the servingConfig {find_serving_config_path()} while using the Vertex AI MCP search tool.
""",
tools=[vertexai_mcp, FunctionTool(send_direct_message)]
)
Download Source Code
Download the sample code to your local environment to get started.
- Download this GitHub repository.
- In a terminal, open the
solutions/enterprise-ai-agent-localdirectory.
Enable APIs
The solution requires the enablement of additional APIs:
- In the Google Cloud console, enable the Vertex AI, Cloud Resource Manager, and Google Chat APIs:

- Click Menu ☰ > APIs & Services > Enabled APIs & Services and then confirm that Vertex AI API, Cloud Resource Manager API, and Google Chat API are in the list.
Configure the OAuth consent screen
The solution requires a consent screen configuration:
- In the Google Cloud console, click to Menu ☰ > Google Auth platform > Branding.
- Click Get started.
- Under App Information, set the App name to
Codelab. - In User support email, choose a support email address where users can contact you if they have questions about their consent.
- Click Next.
- Under Audience, select Internal.
- Click Next.
- Under Contact Information, enter an Email address where you can be notified about any changes to your project.
- Click Next.
- Under Finish, review the Google API Services User Data Policy and if you agree, select I agree to the Google API Services: User Data Policy.
- Click Continue then Create.

- The configuration is saved and you are automatically redirected to Google Auth Platform > Overview.
To learn more, see the full Configure OAuth consent guide.
Create OAuth client credentials
Create a new Desktop app OAuth client to authenticate the user in a local environment:
- In the Google Cloud console, click to Menu ☰ > Google Auth platform > Clients.
- Click + Create client.
- For Application type, select Desktop app.
- Set the Name to
codelab. - Click Create. The newly created credential appears.
- Click Download JSON and save the file as client_secret.json in the
solutions/enterprise-ai-agent-localdirectory.

Enable Vertex AI Search MCP
- In a terminal, execute:
gcloud beta services mcp enable discoveryengine.googleapis.com \
--project=$(gcloud config get-value project)
Configure Chat app
Configure the Google Chat app with its basic informational details.
- In the Google Cloud console, search for
Google Chat APIin the Google Cloud search field, click Google Chat API, click Manage, and click Configuration.
- Set the App name and Description to
Vertex AI. - Set the Avatar URL to
https://developers.google.com/workspace/add-ons/images/quickstart-app-avatar.png. - Unselect Enable Interactive features then click Disable in the appearing modal dialog.
- Select Log errors to Logging.
- Click Save.

Run Agent in ADK Web
Launch the agent locally using the ADK Web interface.
- In a terminal, open the
solutions/enterprise-ai-agent-localdirectory then execute:
# 1. Authenticate with all the required scopes gcloud auth application-default login \ --client-id-file=client_secret.json \ --scopes=https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/chat.spaces,https://www.googleapis.com/auth/chat.messages # 2. Configure environment export ACCESS_TOKEN=$(gcloud auth application-default print-access-token) export GOOGLE_GENAI_USE_VERTEXAI=1 export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project) export GOOGLE_CLOUD_LOCATION=us-central1 # 3. Create and activate a new virtual environment python3 -m venv .venv source .venv/bin/activate # 4. Install poetry and project dependencies pip install poetry poetry install # 5. Start ADK Web adk web

Try agent
Verify the flow by chatting with your custom agent.
- In your Internet browser, open the ADK Web site.
- In the chat, type
Please find my meetings for today, I need their titles and linksand pressenter. - The agent answers with a list of Calendar events (depending on the user's account).
- In the chat, type
Please send a Chat message to someone@example.com with the following text: Hello!and pressenter. - The agent answers with a confirmation message.


5. Agent as Google Workspace add-on
This agent allows users to search data for Workspace in their natural language within the context of Workspace app UIs. It relies on the following elements:
- Model: Gemini.
- Data & actions: Vertex AI data stores for Google Workspace (Calendar, Gmail, Drive), Google-managed Vertex AI Search Model Context Protocol (MCP) server, custom tool function to send Google Chat messages (via Google Chat API).
- Agent building tools: Agent Development Kit (ADK).
- Agent host: Vertex AI Agent Engine.
- UI: Google Workspace add on for Chat and Gmail (easily extendable to Calendar, Drive, Docs, Sheets, and Slides).
- Google Workspace add on: Apps Script, Vertex AI Agent Engine APIs, contextual (selected Gmail message).
Review concepts
Google Workspace add on
A Google Workspace add on is a customized application that extends one or multiple Google Workspace applications (Gmail, Chat, Calendar, Docs, Drive, Meet, Sheets, and Slides).
Apps Script
Apps Script is a cloud-based JavaScript platform powered by Google Drive that lets you integrate with and automate tasks across Google products.
Google Workspace Card framework
The Card framework in Google Workspace enables developers to create rich, interactive user interfaces. It allows for the construction of organized and visually appealing cards that can include text, images, buttons, and other widgets. These cards enhance user experience by providing structured information and enabling quick actions directly within Workspace applications.
Review solution architecture

Review source code
Agent
agent.py
The following code authenticates with Vertex AI, initializes the Vertex AI Search MCP and Chat API tools, and defines the agent's behavior.
- Authentication: It uses a helper function
_get_access_token_from_contextto retrieve the authentication token (CLIENT_AUTH_NAME) that the client injects. This token is crucial for securely calling downstream services like Vertex AI Search MCP and Google Chat tools. - Tools Setup: It initializes
vertexai_mcp, a toolset that connects to the Vertex AI Search Model Context Protocol (MCP) server, and thesend_direct_messagetool. This gives the agent the ability to search through your connected data stores and send Google Chat messages. - Agent Definition: It defines the
root_agentusing thegemini-2.5-flashmodel. The instructions tell the agent to prioritize using the search tool for information retrieval and thesend_direct_messagetool for actions, effectively grounding the agent in your enterprise data.
...
MODEL = "gemini-2.5-flash"
# Client injects a bearer token into the ToolContext state.
# The key pattern is "CLIENT_AUTH_NAME_<random_digits>".
# We dynamically parse this token to authenticate our MCP and API calls.
CLIENT_AUTH_NAME = "enterprise-ai"
VERTEXAI_SEARCH_TIMEOUT = 15.0
def get_project_id():
"""Fetches the consumer project ID from the environment natively."""
_, project = google.auth.default()
if project:
return project
raise Exception(f"Failed to resolve GCP Project ID from environment.")
def find_serving_config_path():
"""Dynamically finds the default serving config in the engine."""
project_id = get_project_id()
engines = discoveryengine_v1.EngineServiceClient().list_engines(
parent=f"projects/{project_id}/locations/global/collections/default_collection"
)
for engine in engines:
# engine.name natively contains the numeric Project Number
return f"{engine.name}/servingConfigs/default_serving_config"
raise Exception(f"No Discovery Engines found in project {project_id}")
def _get_access_token_from_context(tool_context: ToolContext) -> str:
"""Helper method to dynamically parse the intercepted bearer token from the context state."""
escaped_name = re.escape(CLIENT_AUTH_NAME)
pattern = re.compile(fr"^{escaped_name}_\d+$")
# Handle ADK varying state object types (Raw Dict vs ADK State)
state_dict = tool_context.state.to_dict() if hasattr(tool_context.state, 'to_dict') else tool_context.state
matching_keys = [k for k in state_dict.keys() if pattern.match(k)]
if matching_keys:
return state_dict.get(matching_keys[0])
raise Exception(f"No bearer token found in ToolContext state matching pattern {pattern.pattern}")
def auth_header_provider(tool_context: ToolContext) -> dict[str, str]:
token = _get_access_token_from_context(tool_context)
return {"Authorization": f"Bearer {token}"}
def send_direct_message(email: str, message: str, tool_context: ToolContext) -> dict:
"""Sends a Google Chat Direct Message (DM) to a specific user by email address."""
chat_client = chat_v1.ChatServiceClient(
credentials=Credentials(token=_get_access_token_from_context(tool_context))
)
# 1. Setup the DM space or find existing one
person = chat_v1.User(
name=f"users/{email}",
type_=chat_v1.User.Type.HUMAN
)
membership = chat_v1.Membership(member=person)
space_req = chat_v1.Space(space_type=chat_v1.Space.SpaceType.DIRECT_MESSAGE)
setup_request = chat_v1.SetUpSpaceRequest(
space=space_req,
memberships=[membership]
)
space_response = chat_client.set_up_space(request=setup_request)
space_name = space_response.name
# 2. Send the message
msg = chat_v1.Message(text=message)
message_request = chat_v1.CreateMessageRequest(
parent=space_name,
message=msg
)
message_response = chat_client.create_message(request=message_request)
return {"status": "success", "message_id": message_response.name, "space": space_name}
vertexai_mcp = McpToolset(
connection_params=StreamableHTTPConnectionParams(
url="https://discoveryengine.googleapis.com/mcp",
timeout=VERTEXAI_SEARCH_TIMEOUT,
sse_read_timeout=VERTEXAI_SEARCH_TIMEOUT
),
tool_filter=['search'],
# The auth_header_provider dynamically injects the bearer token from the ToolContext
# into the MCP call for authentication.
header_provider=auth_header_provider
)
# Answer nicely the following user queries:
# - Please find my meetings for today, I need their titles and links
# - What is the latest Drive file I created?
# - What is the latest Gmail message I received?
# - Please send the following message to someone@example.com: Hello, this is a test message.
root_agent = LlmAgent(
model=MODEL,
name='enterprise_ai',
instruction=f"""
You are a helpful assistant that always uses the Vertex AI MCP search tool to answer the user's message, unless the user asks you to send a message to someone.
If the user asks you to send a message to someone, use the send_direct_message tool to send the message.
You MUST unconditionally use the Vertex AI MCP search tool to find answer, even if you believe you already know the answer or believe the Vertex AI MCP search tool does not contain the data.
The Vertex AI MCP search tool accesses the user's data through datastores including Google Drive, Google Calendar, and Gmail.
Only use the Vertex AI MCP search tool with servingConfig and query parameters, do not use any other parameters.
Always use the servingConfig {find_serving_config_path()} while using the Vertex AI MCP search tool.
""",
tools=[vertexai_mcp, FunctionTool(send_direct_message)]
)
Client
appsscript.json
The following configuration defines the add-on's triggers and permissions.
- Defines the add-on: It tells Workspace that this project is an add-on for both Chat and Gmail.
- Contextual Triggers: For Gmail, it sets up a
contextualTriggerthat firesonAddonEventwhenever a user opens an email message. This allows the add-on to "see" the email content. - Permissions: It lists the
oauthScopesrequired for the add-on to run, such as permissions to read the current email, execute the script, and connect to external services (like Vertex AI APIs).
...
"addOns": {
"common": {
"name": "Vertex AI",
"logoUrl": "https://developers.google.com/workspace/add-ons/images/quickstart-app-avatar.png"
},
"chat": {},
"gmail": {
"contextualTriggers": [
{
"unconditional": {},
"onTriggerFunction": "onAddonEvent"
}
]
}
},
"oauthScopes": [
"https://www.googleapis.com/auth/script.external_request",
"https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/gmail.addons.execute",
"https://www.googleapis.com/auth/gmail.addons.current.message.readonly"
]
...
Chat.gs
The following code handles incoming Google Chat messages.
- Receives Messages: The
onMessagefunction is the entry point for message interactions. - Manages Context: It saves the
space.name(the ID of the Chat space) to the user's properties. This ensures that when the agent is ready to reply, it knows exactly which conversation to post the message to. - Delegates to Agent: It calls
requestAgent, passing the user's message to the core logic that handles the API communication.
...
// Service that handles Google Chat operations.
// Handle incoming Google Chat message events, actions will be taken via Google Chat API calls
function onMessage(event) {
if (isInDebugMode()) {
console.log(`MESSAGE event received (Chat): ${JSON.stringify(event)}`);
}
// Extract data from the event.
const chatEvent = event.chat;
setChatConfig(chatEvent.messagePayload.space.name);
// Request AI agent to answer the message
requestAgent(chatEvent.messagePayload.message);
// Respond with an empty response to the Google Chat platform to acknowledge execution
return null;
}
// --- Utility functions ---
// The Chat direct message (DM) space associated with the user
const SPACE_NAME_PROPERTY = "DM_SPACE_NAME"
// Sets the Chat DM space name for subsequent operations.
function setChatConfig(spaceName) {
const userProperties = PropertiesService.getUserProperties();
userProperties.setProperty(SPACE_NAME_PROPERTY, spaceName);
console.log(`Space is set to ${spaceName}`);
}
// Retrieved the Chat DM space name to sent messages to.
function getConfiguredChat() {
const userProperties = PropertiesService.getUserProperties();
return userProperties.getProperty(SPACE_NAME_PROPERTY);
}
// Finds the Chat DM space name between the Chat app and the given user.
function findChatAppDm(userName) {
return Chat.Spaces.findDirectMessage(
{ 'name': userName },
{'Authorization': `Bearer ${getAddonCredentials().getAccessToken()}`}
).name;
}
// Creates a Chat message in the configured space.
function createMessage(message) {
const spaceName = getConfiguredChat();
console.log(`Creating message in space ${spaceName}...`);
return Chat.Spaces.Messages.create(
message,
spaceName,
{},
{'Authorization': `Bearer ${getAddonCredentials().getAccessToken()}`}
).name;
}
Sidebar.gs
The following code builds the Gmail sidebar and captures email context.
- Builds the UI:
createSidebarCardconstructs the visual interface using the Workspace Card Service. It creates a simple layout with a text input area and a "Send message" button. - Captures Email Context: In
handleSendMessage, the code checks if the user is currently looking at an email (event.gmail.messageId). If they are, it securely fetches the email's subject and body and appends it to the user's prompt. - Displays the Result: Once the agent responds, the code updates the sidebar card to show the reply.
...
// Service that handles Gmail operations.
// Triggered when the user opens the Gmail Add-on or selects an email.
function onAddonEvent(event) {
// If this was triggered by a button click, handle it
if (event.parameters && event.parameters.action === 'send') {
return handleSendMessage(event);
}
// Otherwise, just render the default initial sidebar
return createSidebarCard();
}
// Creates the standard Gmail sidebar card consisting of a text input and send button.
// Optionally includes an answer section if a response was generated.
function createSidebarCard(optionalAnswerSection) {
const card = CardService.newCardBuilder();
const actionSection = CardService.newCardSection();
// Create text input for the user's message
const messageInput = CardService.newTextInput()
.setFieldName("message")
.setTitle("Message")
.setMultiline(true);
// Create action for sending the message
const sendAction = CardService.newAction()
.setFunctionName('onAddonEvent')
.setParameters({ 'action': 'send' });
const sendButton = CardService.newTextButton()
.setText("Send message")
.setTextButtonStyle(CardService.TextButtonStyle.FILLED)
.setOnClickAction(sendAction);
actionSection.addWidget(messageInput);
actionSection.addWidget(CardService.newButtonSet().addButton(sendButton));
card.addSection(actionSection);
// Attach the response at the bottom if we have one
if (optionalAnswerSection) {
card.addSection(optionalAnswerSection);
}
return card.build();
}
// Handles clicks from the Send message button.
function handleSendMessage(event) {
const commonEventObject = event.commonEventObject || {};
const formInputs = commonEventObject.formInputs || {};
const messageInput = formInputs.message;
let userMessage = "";
if (messageInput && messageInput.stringInputs && messageInput.stringInputs.value.length > 0) {
userMessage = messageInput.stringInputs.value[0];
}
if (!userMessage || userMessage.trim().length === 0) {
return CardService.newActionResponseBuilder()
.setNotification(CardService.newNotification().setText("Please enter a message."))
.build();
}
let finalQueryText = `USER MESSAGE TO ANSWER: ${userMessage}`;
// If we have an email selected in Gmail, append its content as context
if (event.gmail && event.gmail.messageId) {
try {
GmailApp.setCurrentMessageAccessToken(event.gmail.accessToken);
const message = GmailApp.getMessageById(event.gmail.messageId);
const subject = message.getSubject();
const bodyText = message.getPlainBody() || message.getBody();
finalQueryText += `\n\nEMAIL THE USER HAS OPENED ON SCREEN:\nSubject: ${subject}\nBody:\n---\n${bodyText}\n---`;
} catch (e) {
console.error("Could not fetch Gmail context: " + e);
// Invalidate the token explicitly so the next prompt requests the missing scopes
ScriptApp.invalidateAuth();
CardService.newAuthorizationException()
.setResourceDisplayName("Enterprise AI")
.setAuthorizationUrl(ScriptApp.getAuthorizationUrl())
.throwException();
}
}
try {
const response = queryAgent({ text: finalQueryText });
// We leverage the 'showdown' library to parse the LLM's Markdown output into HTML
// We also substitute markdown listings with arrows and adjust newlines for clearer rendering in the sidebar
let displayedText = substituteListingsFromMarkdown(response.text);
displayedText = new showdown.Converter().makeHtml(displayedText).replace(/\n/g, '\n\n');
const textParagraph = CardService.newTextParagraph();
textParagraph.setText(displayedText);
const answerSection = CardService.newCardSection()
.addWidget(textParagraph);
const updatedCard = createSidebarCard(answerSection);
return CardService.newActionResponseBuilder()
.setNavigation(CardService.newNavigation().updateCard(updatedCard))
.build();
} catch (err) {
return CardService.newActionResponseBuilder()
.setNotification(CardService.newNotification().setText("Error fetching response: " + err.message))
.build();
}
}
...
AgentHandler.gs
The following code orchestrates the API call to Vertex AI.
- Orchestrates the API Call:
queryAgentis the bridge between your add-on and Vertex AI Agent Engine. It constructs a request that includes the user's query and the auth token in the state. - Streams the Response: Because agent responses can take a moment, it uses the
streamQueryAPI with Server-Sent Events (SSE). The code gathers the response in chunks and reconstructs the full answer.
...
// Service that handles Vertex AI Agent operations.
// Submits a query to the AI agent and returns the response string synchronously
function queryAgent(input) {
let systemPrompt = "SYSTEM PROMPT START Do not respond with tables but use bullet points instead." +
" Do not ask the user follow-up questions or converse with them as history is not kept in this interface." +
" SYSTEM PROMPT END\n\n";
const requestPayload = {
"class_method": "async_stream_query",
"input": {
"user_id": "vertex_ai_add_on",
"message": { "role": "user", "parts": [{ "text": systemPrompt + input.text }] },
"state_delta": {
"enterprise-ai_999": `${ScriptApp.getOAuthToken()}`
}
}
};
const responseContentText = UrlFetchApp.fetch(
`https://${getLocation()}-aiplatform.googleapis.com/v1/${getReasoningEngine()}:streamQuery?alt=sse`,
{
method: 'post',
headers: { 'Authorization': `Bearer ${ScriptApp.getOAuthToken()}` },
contentType: 'application/json',
payload: JSON.stringify(requestPayload),
muteHttpExceptions: true
}
).getContentText();
if (isInDebugMode()) {
console.log(`Response: ${responseContentText}`);
}
const events = responseContentText.split('\n').map(s => s.replace(/^data:\s*/, '')).filter(s => s.trim().length > 0);
console.log(`Received ${events.length} agent events.`);
let author = "default";
let answerText = "";
for (const eventJson of events) {
if (isInDebugMode()) {
console.log("Event: " + eventJson);
}
const event = JSON.parse(eventJson);
// Retrieve the agent responsible for generating the content
author = event.author;
// Ignore events that are not useful for the end-user
if (!event.content) {
console.log(`${author}: internal event`);
continue;
}
// Handle text answers
const parts = event.content.parts || [];
const textPart = parts.find(p => p.text);
if (textPart) {
answerText += textPart.text;
}
}
return { author: author, text: answerText };
}
...
Deploy Agent in Vertex AI Agent Engine
- In a terminal, open the
solutions/enterprise-ai-agentdirectory from the sources downloaded in previous steps then execute:
# 1. Create and activate a new virtual environment deactivate python3 -m venv .venv source .venv/bin/activate # 2. Install poetry and project dependencies pip install poetry poetry install # 3. Deploy the agent adk deploy agent_engine \ --project=$(gcloud config get-value project) \ --region=us-central1 \ --display_name="Enterprise AI" \ enterprise_ai

- When you see the line Deploying to agent engine... in the logs, open a new terminal and execute the following command to add required permissions to Vertex AI Reasoning Engine Service Agent:
# 1. Get the current Project ID
PROJECT_ID=$(gcloud config get-value project)
# 2. Extract the Project Number for that ID
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
# 3. Construct the Service Account name
SERVICE_ACCOUNT="service-${PROJECT_NUMBER}@gcp-sa-aiplatform-re.iam.gserviceaccount.com"
# 4. Apply the IAM policy binding
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT" \
--role="roles/discoveryengine.viewer"
- Wait for the adk deploy command to complete then copy the resource name of the newly deployed agent from the command output in green.

Initiate service account
Create a dedicated service account to authorize the add-on's server-side operations.
In the Google Cloud console, follow these steps:
- Click Menu ☰ > IAM & Admin > Service Accounts > + Create service account.
- Set Service account name to
vertexai-add-on.

- Click Done. You're redirected to the Service accounts page and can see the service account that you created.

- Select the newly created service account then the Keys tab.
- Click Add key then Create new key.
- Select JSON then click Create.

- The dialog closes and your newly created public/private key pair is automatically downloaded to your local environment as a JSON file.
Create and Configure Apps Script Project
Create a new Apps Script project to host the add-on code and configure its connection properties.
- Click the following button to open the Enterprise AI add on Apps Script project:
- Click Overview > Make a copy.
- In your Apps Script project, click Project Settings > Edit script properties > Add script property to add script properties.
- Set REASONING_ENGINE_RESOURCE_NAME to the Vertex AI agent resource name copied in previous steps. It has the following format:
projects/<PROJECT_NUMBER>/locations/us-central1/reasoningEngines/<AGENT_ID>
- Set APP_SERVICE_ACCOUNT_KEY to the JSON key from the service account file downloaded in previous steps.
- Click Save script properties
Deploy to Gmail and Chat
Deploy the add-on to test it directly within Gmail and Google Chat.
In your Apps Script project, follow these steps:
- Click Deploy > Test deployments, then Install. It's now available in Gmail.
- Click Copy under Head Deployment ID.

In the Google Cloud console, follow these steps:
- Search for
Google Chat APIin the Google Cloud search field, click Google Chat API, click Manage, and click Configuration.
- Select Enable Interactive features.
- Unselect Join spaces and group conversations.
- Under Connection settings, select Apps Script.
- Set Deployment ID to the Head Deployment ID copied in the previous steps.
- Under Visibility, select Make this Chat app available to specific people and groups in Your Workspace Domain and enter your email address.
- Click Save.

Try the Add On
Interact with your live add-on to verify it can fetch data and answer questions in context.
Open Google Chat in a new tab, then follow these steps:
- Open a direct message space with the Chat app Vertex AI.

- Click Configure and go through the authentication flow.
- Type
What are my meetings for today?and pressenter. Vertex AI Chat app should reply with the results.

Open Gmail in a new tab, then follow these steps:
- Send yourself an email with the Subject set to
We need to talkand the Body set toAre you available today between 8 and 9 AM? - Open the newly received email message.
- Open the Vertex AI add on sidebar.
- Set the Message to
Do I have any meeting conflicts? - Click Send message.
- The answer is displayed after the button.

6. Clean up
Delete Google Cloud project
To avoid incurring charges to your Google Cloud Account for the resources used in this codelab, we recommend that you delete the Google Cloud project.
In the Google Cloud console, follow these steps:
- Click Menu ☰ > IAM & Admin > Settings.
- Click Shut down.
- Enter the project ID.
- Click Shut down anyway.

7. Congratulations
Congratulations! You built solutions that harness the power of bringing Vertex AI and Google Workspace closer for workers!
What's next?
We only showcase the most typical use cases in this codelab, but there are plenty of expansion areas that you might want to consider in your solutions, such as the following:
- Use AI-empowered developer tools such as Gemini CLI and Antigravity.
- Integrate with other agent frameworks and tools such as custom MCPs, custom function calls, and generative UIs.
- Integrate with other AI models, including customs, hosted in dedicated platforms such as Vertex AI.
- Integrate with other agents, hosted in dedicated platforms such as Dialogflow or by third parties via the Cloud Marketplace.
- Publish agents on the Cloud Marketplace to empower teams, organizations or public users.
Learn more
There are plenty of resources available for developers such as YouTube videos, documentation websites, code samples, and tutorials:
- Google Cloud Developer Center
- Supported products | Google Cloud MCP servers
- A2UI
- Model Garden on Vertex AI | Google Cloud
- Vertex AI Agent Engine overview
- Get answers and follow-ups | Vertex AI Search | Google Cloud Documentation
- Offer AI agents through Google Cloud Marketplace
- Google Workspace Developers YouTube channel - Welcome Developers!
- Google Workspace Developers Website
- GitHub repository for all Google Workspace add on samples

