Integrate Gemini Enterprise Agents with Google Workspace

1. Before you begin

83e1c1629d14fb31.png

What is Gemini Enterprise?

Gemini Enterprise is an advanced agentic platform that brings the best of Google AI to every employee, for every workflow. It empowers teams to discover, create, share, and run AI agents in one secure environment.

  • Access advanced models: Users get immediate access to Google's most powerful multimodal AI, including Gemini, to tackle complex business challenges.
  • Utilize specialized agents: The suite includes ready-to-use Google agents for research, coding, and note-taking to provide immediate value.
  • Empower every employee: No-code and pro-code options allow staff across all departments to build and manage their own custom agents for workflow automation.
  • Ground agents in data: Agents can be securely connected to internal company data and third-party applications to ensure their responses are contextually accurate.
  • Centralized governance: Administrators can visualize and audit all agent activity to ensure the organization meets strict security and compliance standards.
  • Expand with ecosystems: The platform integrates with a wide network of partner applications and service providers to extend automation across different systems.

127f2ed7d484722c.png

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 Gemini Enterprise create a powerful feedback loop where Workspace provides real-time data and collaboration context, while Gemini Enterprise 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 Gemini Enterprise, 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:

What you will build

In this codelab, we build three solutions with Gemini Enterprise AI agents tightly integrated with Google Workspace. They'll demonstrate architectural patterns that can be used to interact with data, actions, and UIs.

No-code custom agent

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: Gemini Enterprise data stores for Google Workspace (Calendar, Gmail, Drive, NotebookLM), Google Search.
  • Agent building tools: Gemini Enterprise Agent Designer.
  • Agent host: Gemini Enterprise.
  • UI: Gemini Enterprise Web app.

90e42539e5959634.png

60e62437ce29a818.png

Pro-code 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: Gemini Enterprise data stores for Google Workspace (Calendar, Gmail, Drive, NotebookLM), Google Search, 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: Gemini Enterprise Web app.

1647ebff031c42e7.png

a8087d2351e77fb4.png

Default 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: Gemini Enterprise data stores for Google Workspace (Calendar, Gmail, Drive, NotebookLM), Google Search.
  • Agent host: Gemini Enterprise.
  • UI: Google Workspace add on for Chat and Gmail (easily extendable to Calendar, Drive, Docs, Sheets, and Slides).
  • Google Workspace add on: Apps Script, Gemini Enterprise & Vertex AI APIs, contextual (user metadata, selected Gmail message).

c8c63fb3f324fecf.png

d33b8cb50ee251b7.png

What you will learn

  • The integration points between Gemini Enterprise and Google Workspace that enable data and actions.
  • The no-code and pro-code options to build custom agents hosted in Gemini Enterprise.
  • The ways users can access agents from the Gemini Enterprise web app and Google Workspace applications.

2. Get set up

Review concepts

Gemini Enterprise app

A Gemini Enterprise 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.

Gemini Enterprise web app

A Gemini Enterprise web app is associated with a Gemini Enterprise app. It functions as a centralized AI home base where employees use a single chat interface to search across siloed company data, run specialized AI agents for complex workflows, and generate professional-grade content with enterprise-level privacy.

Initialize and access resources

In this section, you access and configure the following resources from your preferred web browser.

Gemini Enterprise app

Open the Google Cloud console in a new tab, then follow these steps:

  1. Select your project.
  2. In the Google Cloud search field, search and select Gemini Enterprise, then click + Create app. If you do not have a license for Gemini Enterprise, you will be prompted to activate a 30-day free trial license.

  1. Set the App name to codelab.
  2. An ID is generated based on the name and is displayed under the field, copy it.
  3. Set the Multi-region to global (Global).
  4. Click Create.

8712ada39377205e.png

  1. The app is created and you are automatically redirected to Gemini Enterprise > Overview.
  2. Under Get Full access, click Set up identity.
  3. In the new screen, select Use Google Identity and click Confirm Workforce Identity.

3209c156eff4ba43.png

  1. The configuration is saved and you are automatically redirected to Gemini Enterprise > Overview.
  2. Navigate to Configurations.
  3. In the tab Feature Management, turn Enable agent designer on, click Save.

f0cd9da419b41cb6.png

Gemini Enterprise web app

Open Gemini Enterprise from the Cloud console in a new tab, then follow these steps:

  1. Click the app named codelab.
  2. Copy the URL that shows up as we will use it to navigate it to the Gemini Enterprise web app in next steps.

b46ee6176744565d.png

3. No-code custom agent

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: Gemini Enterprise data stores for Google Workspace (Calendar, Gmail, Drive, NotebookLM), Google Search.
  • Agent building tools: Gemini Enterprise Agent Designer.
  • Agent host: Gemini Enterprise.
  • UI: Gemini Enterprise Web app.

Review concepts

Gemini

Gemini is a multimodal LLM from Google. It helps people unlock their human potential so that they can augment their imagination, expand their curiosity, and enhance their productivity.

Gemini Enterprise data store

A Gemini Enterprise 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 Salesforce. Data stores that contain data from third-party applications are also called data connectors.

Gemini Enterprise Agent Designer

The Gemini Enterprise Agent Designer is an interactive no-code, low-code platform for creating, managing, and launching single and multi-step agents in Gemini Enterprise.

Review solution architecture

e77aafb772502aaf.png

Enable APIs

The Gemini Enterprise Workspace data stores require the enablement of APIs:

  1. In the Google Cloud console, enable the Calendar, Gmail, and People APIs:

573322606b715a69.png

  1. Click Menu ☰ > APIs & Services > Enabled APIs & Services and then confirm that Google Calendar API, Gmail API, and People API are in the list.

The Gemini Enterprise Workspace Calendar and Gmail actions require a consent screen configuration:

  1. In the Google Cloud console, click to Menu ☰ > Google Auth platform > Branding.

  1. Click Get started.
  2. Under App Information, set the App name to Codelab .
  3. In User support email, choose a support email address where users can contact you if they have questions about their consent.
  4. Click Next.
  5. Under Audience, select Internal.
  6. Click Next.
  7. Under Contact Information, enter an Email address where you can be notified about any changes to your project.
  8. Click Next.
  9. 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.
  10. Click Continue then Create.

578c2b38219b2f7b.png

  1. The configuration is saved and you are automatically redirected to Google Auth Platform > Overview.
  2. Navigate to Data Access.
  3. Click Add or remove scopes.
  4. Copy the following scopes and paste them into the Manually add scopes field.
https://www.googleapis.com/auth/calendar.readonly
https://www.googleapis.com/auth/calendar.events
https://www.googleapis.com/auth/calendar.calendars
https://www.googleapis.com/auth/gmail.send
https://www.googleapis.com/auth/gmail.readonly
  1. Click Add to table, then Update, then Save.

874b1dda14e8f379.png

To learn more, see the full Configure OAuth consent guide.

Create OAuth client credentials

Create a new OAuth client for Gemini Enterprise to authenticate users:

  1. In the Google Cloud console, click to Menu ☰ > Google Auth platform > Clients.

  1. Click + Create client.
  2. For Application type, select Web application.
  3. Set the Name to codelab.
  4. Skip Authorized JavaScript origins.
  5. In the Authorized redirect URIs section, click Add URI and enter https://vertexaisearch.cloud.google.com/oauth-redirect.
  6. Click Create.
  7. A dialog will appear with your newly created OAuth client ID and secret. Save this information in a safe place.

a46e5ebfb851aea5.png

Create data stores

Open Gemini Enterprise from the Cloud console in a new tab, then follow these steps:

  1. Click the app named codelab.
  2. In the navigation menu, click Connected data stores.
  3. Click + New data store.
  4. In the Source, search for Google Calendar, and click Select.
  5. In the Actions section, enter the Client ID and Client Secret saved from previous steps, and then click Verify Auth and follow the steps to authenticate and authorize the OAuth client.
  6. Enable the actions Create calendar event and Update calendar event.
  7. Click Continue.

a1d76e70edec0cf.png

  1. In the Configuration section, set the Data connector name to calendar.
  2. Click Create.
  3. You are automatically redirected to Connected data stores where you can see the newly added data store.

Create the Google Gmail data store:

  1. Click + New data store.
  2. In the Source, search for Google Gmail, and click Select.
  3. In the Actions section, enter the Client ID and Client Secret saved from previous steps, and then click Verify Auth.
  4. Enable the action Send email.
  5. Click Continue.
  6. In the Configuration section, set the Data connector name to gmail.
  7. Click Create.
  8. You are automatically redirected to Connected data stores where you can see the newly added data store.

Create the Google Drive data store:

  1. Click + New data store.
  2. In the Source, search for Google Drive, and click Select.
  3. In the Data section, select All, then click Continue.
  4. In the Configuration section, set the Data connector name to drive.
  5. Click Create.
  6. You are automatically redirected to Connected data stores where you can see the newly added data store.

Create the NotebookLM data store:

  1. Click + New data store.
  2. In the Source, search for NotebookLM, and click Select.
  3. In the Configuration section, set the Data connector name to notebooklm.
  4. Click Create.
  5. You are automatically redirected to Connected data stores where you can see the newly added data store.

After a few minutes, the status of all connected data store statuses (except for NotebookLM) will be Active. If you see any errors, you can click on the data source to view the error details.

ceba9eb2480a2696.png

Test data stores

Open the Gemini Enterprise web app URL that we copied earlier:

  1. Click Menu ☰ > New chat.
  2. In the footer of the new chat message field, click the Connectors icon and enable all connectors.
  3. You can now experiment with prompts related to the connectors. For example, in the chat, type Do I have any meetings today? and press enter.
  4. Next, try typing How many emails did I receive today? and press enter.
  5. Finally, type Give me the title of the last Drive file I created and press enter.

90e42539e5959634.png

Create custom agent

In the Gemini Enterprise web app, create a new agent using the Agent Designer:

  1. Click Menu ☰ > + New agent.
  2. In the chat, type An agent that always sends pirate-themed emails but use normal English otherwise and press enter.

2803c1dedd20433e.png

  1. The Agent Designer drafts the agent based on the prompt and opens it in the editor.
  2. Click Create

Try custom agent

  1. In the Gemini Enterprise web app, chat with the newly created agent:
  2. Click Menu ☰ > Agents.
  3. Select the agent under Your agents.
  4. In the footer of the new chat message field, click the Connectors icon, then click Enable actions for Mail and follow the instructions to authorize the agent
  5. In the chat, type Send an email to someone@example.com saying I'll see them at Cloud Next, generate some subject and body yourself and press enter. You can replace the sample email with your email address.
  6. Click ✔️ to send the email.

60e62437ce29a818.png

d4fb65d14fdf27da.png

4. Pro-code 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: Gemini Enterprise data stores for Google Workspace (Calendar, Gmail, Drive, NotebookLM), Google Search, 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: Gemini Enterprise Web app.

It will be integrated in Gemini Enterprise using the bring-your-own feature so we need to go through deployment, registration, and configuration steps.

Review concepts

Vertex AI

Vertex AI offers everything you need to build and use generative AI, including AI solutions, search and conversation, more than 130 foundation models, and a unified AI platform.

4670fcf7a826af4d.png

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.

Review solution architecture

43df337e0f3d64e8.png

Review source code

agent.py

...
MODEL = "gemini-2.5-flash"

# Gemini Enterprise authentication injects a bearer token into the ToolContext state.
# The key pattern is "GE_AUTH_NAME_<random_digits>".
# We dynamically parse this token to authenticate our MCP and API calls.
GE_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(GE_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)]
)

Enable APIs

The solution requires the enablement of additional APIs:

  1. In the Google Cloud console, enable the Vertex AI, Cloud Resource Manager, and Google Chat APIs:

4f02a36b050bab00.png

  1. 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.

The solution requires additional data access:

  1. In the Google Cloud console, click to Menu ☰ > Google Auth platform > Data Access.

  1. Click Add or remove scopes.
  2. Copy the following scopes and paste them into the Manually add scopes field.
  3. Click Add to table, then Update, then Save.
https://www.googleapis.com/auth/cloud-platform
https://www.googleapis.com/auth/chat.messages.create
https://www.googleapis.com/auth/chat.spaces.create
  1. Click Add to table, then Update, then Save.

56fbba733139acfe.png

Update OAuth client credentials

The solution requires an additional authorized redirect URI:

  1. In the Google Cloud console, click to Menu ☰ > Google Auth platform > Clients.

  1. Click on the client name codelab.
  2. In the Authorized redirect URIs section, click Add URI and enter https://vertexaisearch.cloud.google.com/static/oauth/oauth.html.
  3. Click Save.

deed597aa54fec91.png

Enable Vertex AI Search MCP

  1. In a terminal, execute:
gcloud beta services mcp enable discoveryengine.googleapis.com \
     --project=$(gcloud config get-value project)

Configure Chat app

  1. In the Google Cloud console, search for Google Chat API in the Google Cloud search field, click Google Chat API, click Manage, and click Configuration.

  1. Set the App name and Description to Gemini Enterprise.
  2. Set the Avatar URL to https://developers.google.com/workspace/add-ons/images/quickstart-app-avatar.png.
  3. Unselect Enable Interactive features then click Disable in the appearing modal dialog.
  4. Select Log errors to Logging.
  5. Click Save.

90cb612e51bce4e6.png

Deploy agent In Vertex AI Agent Engine

  1. Download this GitHub repository.

  1. In a terminal, open the solutions/enterprise-ai-agent directory then execute:
# 1. Create and activate a new virtual environment
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

eafd2f9c4fbf305.png

  1. 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"
  1. Wait for the adk deploy command to complete then copy the resource name of the newly deployed agent from the command output in green.

d098fe1347d6581b.png

Register agent in Gemini Enterprise

Open Gemini Enterprise from the Cloud console in a new tab, then follow these steps:

  1. Click the app named codelab.
  2. In the navigation menu, click Agents.
  3. Click + Add agent.
  4. Click Add for Custom agent via Agent Engine. The Authorizations section displays.
  5. Click Add authorization.
  6. Set the Authorization name to enterprise-ai. An ID is generated based on the name and is displayed under the field, copy it.
  7. Set the Client ID to the same value as the OAuth client created and updated in previous steps.
  8. Set the Client secret to the same value as the OAuth client created and updated in previous steps.
  9. Set the Token URI to https://oauth2.googleapis.com/token.
  10. Set the Authorization URI to the following value after replacing <CLIENT_ID> with the OAuth client ID created and updated in previous steps.
https://accounts.google.com/o/oauth2/v2/auth?client_id=<CLIENT_ID>&redirect_uri=https%3A%2F%2Fvertexaisearch.cloud.google.com%2Fstatic%2Foauth%2Foauth.html&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcalendar.readonly%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcalendar.calendars%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcalendar.events%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.send%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.readonly%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fchat.messages.create%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fchat.spaces.create&include_granted_scopes=true&response_type=code&access_type=offline&prompt=consent
  1. Click Done then Next. The Configuration section displays.
  2. Set the Agent name and Agent description to Enterprise AI.
  3. Set the Agent Engine reasoning engine to the reasoning engine resource name copied in previous steps. It has the following format:
projects/<PROJECT_ID>/locations/<LOCATION>/reasoningEngines/<REASONING_ENGINE_ID>
  1. Click Create. The newly added agent is now listed under Agents.

Try agent

  1. In the Gemini Enterprise web app, chat with the newly registered agent:
  2. Click Menu ☰ > Agents.
  3. Select the agent under From your organization.
  4. In the chat, type Please find my meetings for today, I need their titles and links and press enter.
  5. Click Authorize, then follow the authorization flow.

ed61cf654cbcd76c.png

  1. The agent answers with a list of Calendar events (depending on the user's account).
  2. In the chat, type Please send a Chat message to someone@example.com with the following text: Hello! and press enter.
  3. The agent answers with a confirmation message.

1647ebff031c42e7.png

a8087d2351e77fb4.png

5. Default agent as Google Workspace add-on

This agent allows users to search data for Workspace in their natural language in the context of Workspace app UIs. It relies on the following elements:

  • Model: Gemini.
  • Data: Gemini Enterprise data stores for Google Workspace (Calendar, Gmail, Drive, NotebookLM), Google Search.
  • Agent host: Gemini Enterprise.
  • UI: Google Workspace add on for Chat and Gmail (easily extendable to Calendar, Drive, Docs, Sheets, and Slides).
  • Google Workspace add on: Apps Script, Gemini Enterprise & Vertex AI APIs, contextual (user metadata, selected Gmail message).

The Google Workspace add on will be connected to Gemini Enterprise using the StreamAssist API.

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

1798c39f7aaed8fc.png

Review source code

appsscript.json

...
"addOns": {
    "common": {
      "name": "Enterprise 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/discoveryengine.assist.readwrite",
    "https://www.googleapis.com/auth/gmail.addons.execute",
    "https://www.googleapis.com/auth/gmail.addons.current.message.readonly"
  ]
...

Chat.gs

...
// 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

...
// 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 responseText = queryAgent({ text: finalQueryText, forceNewSession: true });

    // 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(responseText);
    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

...
// Service that handles Gemini Enterprise AI Agent operations.

// Submits a query to the AI agent and returns the response string synchronously
function queryAgent(input) {
  const isNewSession = input.forceNewSession || !PropertiesService.getUserProperties().getProperty(AGENT_SESSION_NAME);
  const sessionName = input.forceNewSession ? createAgentSession() : getOrCreateAgentSession();

  let systemPrompt = "SYSTEM PROMPT START Do not respond with tables but use bullet points instead.";
  if (input.forceNewSession) {
    systemPrompt += " Do not ask the user follow-up questions or converse with them as history is not kept in this interface.";
  }
  systemPrompt += " SYSTEM PROMPT END\n\n";

  const queryText = isNewSession ? systemPrompt + input.text : input.text;

  const requestPayload = {
    "session": sessionName,
    "userMetadata": { "timeZone": Session.getScriptTimeZone() },
    "query": { "text": queryText },
    "toolsSpec": { "vertexAiSearchSpec": { "dataStoreSpecs": getAgentDataStores().map(ds => { dataStore: ds }) } },
    "agentsSpec": { "agentSpecs": [{ "agentId": getAgentId() }] }
  };

  const responseContentText = UrlFetchApp.fetch(
    `https://${getLocation()}-discoveryengine.googleapis.com/v1alpha/${getReasoningEngine()}/assistants/default_assistant:streamAssist?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 answerText = "";
  for (const eventJson of events) {
    if (isInDebugMode()) {
      console.log("Event: " + eventJson);
    }
    const event = JSON.parse(eventJson);

    // Ignore internal events
    if (!event.answer) {
      console.log(`Ignored: internal event`);
      continue;
    }

    // Handle text replies
    const replies = event.answer.replies || [];
    for (const reply of replies) {
      const content = reply.groundedContent.content;
      if (content) {
        if (isInDebugMode()) {
          console.log(`Processing content: ${JSON.stringify(content)}`);
        }
        if (content.thought) {
          console.log(`Ignored: thought event`);
          continue;
        }
        answerText += content.text;
      }
    }

    if (event.answer.state === "SUCCEEDED") {
      console.log(`Answer text: ${answerText}`);
      return answerText;
    } else if (event.answer.state !== "IN_PROGRESS") {
      throw new Error("Something went wrong, check the Apps Script logs for more info.");
    }
  }
  return answerText;
}

// Gets the list of data stores configured for the agent to include in the request.
function getAgentDataStores() {
  const responseContentText = UrlFetchApp.fetch(
    `https://${getLocation()}-discoveryengine.googleapis.com/v1/${getReasoningEngine().split('/').slice(0, 6).join('/')}/dataStores`,
    {
      method: 'get',
      // Use the add on service account credentials for data store listing access
      headers: { 'Authorization': `Bearer ${getAddonCredentials().getAccessToken()}` },
      contentType: 'application/json',
      muteHttpExceptions: true
    }
  ).getContentText();
  if (isInDebugMode()) {
    console.log(`Response: ${responseContentText}`);
  }
  const dataStores = JSON.parse(responseContentText).dataStores.map(ds => ds.name);
  if (isInDebugMode()) {
    console.log(`Data stores: ${dataStores}`);
  }
  return dataStores;
}
...

Initiate service account

In the Google Cloud console, follow these steps:

  1. Click Menu ☰ > IAM & Admin > Service Accounts > + Create service account.

  1. Set Service account name to ge-add-on.

d44d6aae29e2464c.png

  1. Click Create and continue.
  2. Add the role Discovery Engine Viewer in permissions.

f1374efa4f326ef5.png

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

b9496085f1404c5c.png

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

f4280f5533a08821.png

  1. 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

  1. Click the following button to open the Enterprise AI add on Apps Script project:

  1. Click Overview > Make a copy.
  2. In your Apps Script project, click Project Settings > Edit script properties > Add script property to add script properties.
  3. Set REASONING_ENGINE_RESOURCE_NAME to the Gemini Enterprise application resource name. It has the following format:
# 1. Replace PROJECT_ID with the Google Cloud project ID.
# 2. Replace GE_APP_ID with the codelab app ID found in Google Cloud console > Gemini Enterprise > Apps.

projects/<PROJECT_ID>/locations/global/collections/default_collection/engines/<GE_APP_ID>
  1. Set APP_SERVICE_ACCOUNT_KEY to the JSON key from the service account file downloaded in previous steps.
  2. Click Save script properties

Deploy to Gmail and Chat

In your Apps Script project, follow these steps:

  1. Click Deploy > Test deployments, then Install. It's now available in Gmail.
  2. Click Copy under Head Deployment ID.

2ed2df972ad92715.png

In the Google Cloud console, follow these steps:

  1. Search for Google Chat API in the Google Cloud search field, click Google Chat API, click Manage, and click Configuration.

  1. Select Enable Interactive features.
  2. Unselect Join spaces and group conversations.
  3. Under Connection settings, select Apps Script.
  4. Set Deployment ID to the Head Deployment ID copied in the previous steps.
  5. Under Visibility, select Make this Chat app available to specific people and groups in Your Workspace Domain and enter your email address.
  6. Click Save.

3b7d461c423f7c51.png

Try the Add On

Open Google Chat in a new tab, then follow these steps:

  1. Open a direct message space with the Chat app Gemini Enterprise.

3da8690d19baf2d0.png

  1. Click Configure and go through the authentication flow.
  2. Type What are my meetings for today? and press enter. Gemini Enterprise Chat app should reply with the results.

c8c63fb3f324fecf.png

Open Gmail in a new tab, then follow these steps:

  1. Send yourself an email with the Subject set to We need to talk and the Body set to Are you available today between 8 and 9 AM?
  2. Open the newly received email message.
  3. Open the Enterprise AI add on sidebar.
  4. Set the Message to Am I?
  5. Click Send message.
  6. The answer is displayed after the button.

d33b8cb50ee251b7.png

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:

  1. Click Menu ☰ > IAM & Admin > Settings.

  1. Click Shut down.
  2. Enter the project ID.
  3. Click Shut down anyway.

3b9492d97f771b2c.png

7. Congratulations

Congratulations! You built solutions that harness the power of bringing Gemini Enterprise 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: