How to Build an Open Agents SDK Gmail Agent with Arcade (MCP)

How to Build an Open Agents SDK Gmail Agent with Arcade (MCP)

Arcade.dev Team's avatar
Arcade.dev Team
OCTOBER 21, 2025
6 MIN READ
TUTORIALS
Rays decoration image
Ghost Icon

This guide shows you how to build a Gmail agent using LangGraph with Arcade's MCP servers and authentication infrastructure. The agent will handle email operations through OAuth 2.0 with proper user-specific permissions.

What You'll Build

An agent that can:

  • List and read Gmail messages
  • Send emails from user accounts
  • Search email threads
  • Manage labels and folders
  • Handle multi-user scenarios with isolated credentials

Prerequisites

Before starting, set up your development environment with these requirements:

  • Python 3.11+ installed
  • An Arcade API key
  • Google Cloud Console project with OAuth 2.0 credentials
  • Basic Python async/await knowledge

Step 1: Install Required Packages

Install the Arcade integration for agents and the necessary dependencies:

pip install langchain-arcade langchain-openai langgraph

Configure your API keys in your environment:

export ARCADE_API_KEY="your_arcade_api_key"
export OPENAI_API_KEY="your_openai_api_key"

Get an Arcade API key from the Arcade dashboard.

Step 2: Configure Google OAuth

Set up Google authentication to allow your agent to access Gmail on behalf of users.

Create Google OAuth Credentials

Follow these steps to create OAuth credentials:

  1. Go to Google Cloud Console
  2. Create a new project or select an existing one
  3. Enable the Gmail API for your project
  4. Navigate to "APIs & Services" > "Credentials"
  5. Click "Create Credentials" > "OAuth client ID"
  6. Choose "Web application" as the application type
  7. Add authorized redirect URIs (Arcade will generate this)
  8. Download the client ID and secret

Add Google Provider to Arcade

Configure your Google OAuth provider in Arcade's dashboard:

  1. Navigate to OAuth > Providers
  2. Click "Add OAuth Provider"
  3. Select "Google" from the dropdown
  4. Enter your Client ID and Client Secret
  5. Note the generated redirect URL
  6. Add this redirect URL to your Google Cloud Console authorized URIs

The minimum required scopes are:

https://www.googleapis.com/auth/userinfo.email
https://www.googleapis.com/auth/userinfo.profile
https://www.googleapis.com/auth/gmail.modify

For detailed configuration steps, see Arcade's Google auth documentation.

Step 3: Load Gmail Tools from Arcade

Use Arcade's tool manager to fetch the Gmail MCP server:

from langchain_arcade import ArcadeToolManager
import os

# Initialize Arcade tool manager
arcade_api_key = os.environ.get("ARCADE_API_KEY")
manager = ArcadeToolManager(api_key=arcade_api_key)

# Get all Gmail tools
gmail_tools = manager.get_tools(toolkits=["Gmail"])

# View available tools
print(f"Loaded {len(manager.tools)} Gmail tools:")
for tool in manager.tools:
    print(f"  - {tool.name}")

This loads Arcade's pre-built Gmail MCP server with tools for:

  • Gmail.ListEmails - Retrieve messages from inbox
  • Gmail.SendEmail - Send emails with attachments
  • Gmail.SearchThreads - Search by keywords
  • Gmail.WriteDraftEmail - Create draft messages
  • Gmail.DeleteEmail - Remove messages
  • Gmail.TrashEmail - Move messages to trash

View all available tools in the Arcade MCP servers documentation.

Step 4: Create the LangGraph Agent

Build a ReAct-style agent that can use Gmail tools:

from langchain_openai import ChatOpenAI
from langgraph.checkpoint.memory import MemorySaver
from langgraph.prebuilt import create_react_agent

# Configure the language model
openai_api_key = os.environ.get("OPENAI_API_KEY")
model = ChatOpenAI(model="gpt-4o", api_key=openai_api_key)

# Bind tools to the model
bound_model = model.bind_tools(gmail_tools)

# Add memory for conversation context
memory = MemorySaver()

# Create the agent
agent = create_react_agent(
    model=bound_model,
    tools=gmail_tools,
    checkpointer=memory
)

The agent uses a reasoning loop to select appropriate tools based on user requests.

Step 5: Handle User Authorization

When a user invokes Gmail tools for the first time, Arcade triggers an OAuth authorization flow. Handle this with proper error catching:

from langgraph.errors import NodeInterrupt

def run_gmail_agent(user_id: str, query: str):
    """Execute agent with authorization handling."""

    config = {
        "configurable": {
            "thread_id": f"gmail_thread_{user_id}",
            "user_id": user_id
        }
    }

    user_input = {
        "messages": [("user", query)]
    }

    try:
        # Stream agent responses
        for chunk in agent.stream(user_input, config, stream_mode="values"):
            chunk["messages"][-1].pretty_print()

    except NodeInterrupt as exc:
        # Authorization required
        print(f"\n⚠️  Authorization Required")
        print(f"Visit this URL to grant Gmail access:")
        print(exc.args[0].get("url"))
        print("\nRun the agent again after authorizing.")

The user_id parameter ensures each user maintains separate OAuth credentials. Arcade handles token storage, refresh, and rotation automatically.

See the complete example in Arcade's repository.

Step 6: Build Agent Capabilities

List Recent Emails

# User query
query = "List my 10 most recent emails and summarize the important ones."

# Agent automatically selects Gmail.ListEmails tool
run_gmail_agent(user_id="user@example.com", query=query)

The agent retrieves messages and formats them for readability.

Send Emails

query = """
Send an email to team@example.com with:
- Subject: Weekly Update
- Body: Project completed successfully. Details attached.
"""

run_gmail_agent(user_id="user@example.com", query=query)

The agent uses Gmail.SendEmail with the authenticated user's credentials.

Search and Filter

query = "Find all emails from john@company.com in the last week about the project."

run_gmail_agent(user_id="user@example.com", query=query)

The agent applies Gmail.SearchThreads with appropriate filters.

Step 7: Manage Multiple Users

Each user needs isolated OAuth credentials. Arcade handles this through the user_id parameter:

# User A's session
run_gmail_agent(
    user_id="alice@company.com",
    query="Check my inbox"
)

# User B's session (completely separate credentials)
run_gmail_agent(
    user_id="bob@company.com",
    query="Send an email to the team"
)

Tokens never mix between users. See Arcade's authorization documentation for details on the security model.

Step 8: Custom Authorization Flows

For web applications, implement custom authorization handling:

from arcadepy import Arcade

async def handle_gmail_authorization(user_id: str):
    """Initiate OAuth flow for a user."""

    client = Arcade(api_key=arcade_api_key)

    # Start authorization
    auth_response = await client.auth.start(
        user_id=user_id,
        provider="google",
        scopes=["https://www.googleapis.com/auth/gmail.modify"]
    )

    if auth_response.status != "completed":
        # Return URL to user for OAuth consent
        return {
            "authorization_required": True,
            "url": auth_response.url
        }

    # Wait for completion
    await client.auth.wait_for_completion(auth_response)

    return {"authorized": True}

Display the authorization URL in your web interface and retry the tool execution after the user completes OAuth.

Step 9: Extend with Multiple Toolkits

Combine Gmail with other Arcade MCP servers:

# Load multiple toolkits
tools = manager.get_tools(toolkits=[
    "Gmail",
    "Google Calendar",
    "Slack",
    "GitHub"
])

# Create agent with all tools
multi_toolkit_agent = create_react_agent(
    model=bound_model,
    tools=tools,
    checkpointer=memory
)

# Agent can now coordinate across tools
query = """
Check my calendar for tomorrow's meetings,
then email the attendees a reminder about the 2pm standup.
"""

run_agent(user_id="user@example.com", query=query)

Browse all available MCP servers in the Arcade toolkits directory.

Step 10: Build Custom Gmail Tools

Create specialized tools using Arcade's Tool Development Kit:

from arcade_tdk import ToolContext, tool
from arcade_tdk.auth import Google
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build

@tool(
    requires_auth=Google(
        scopes=["https://www.googleapis.com/auth/gmail.send"]
    )
)
async def send_weekly_report(
    context: ToolContext,
    recipient: str,
    report_data: dict
) -> str:
    """Send a formatted weekly report via Gmail."""

    # Access user's Gmail with their token
    credentials = Credentials(context.authorization.token)
    gmail = build("gmail", "v1", credentials=credentials)

    # Generate email body
    html_body = format_report_html(report_data)

    # Create and send message
    message = create_html_message(
        to=recipient,
        subject="Weekly Report",
        html_body=html_body
    )

    result = gmail.users().messages().send(
        userId="me",
        body=message
    ).execute()

    return f"Report sent: {result['id']}"

Deploy custom tools to the Arcade Registry for reuse across projects.

Production Deployment

Self-Host Arcade Engine

For production environments, deploy Arcade Engine on your infrastructure:

# Install Arcade Engine
pip install arcade-ai

# Configure engine
arcade-engine init

# Start engine and workers
arcade-engine start
arcade-worker start

The engine serves as your MCP server and handles tool execution. Configure it with your OAuth providers in engine.yaml:

auth:
  providers:
    - id: google-prod
      type: oauth2
      provider_id: google
      client_id: ${GOOGLE_CLIENT_ID}
      client_secret: ${GOOGLE_CLIENT_SECRET}

api:
  host: 0.0.0.0
  port: 9099

See Arcade's deployment documentation for Kubernetes and Docker configurations.

Monitor Agent Performance

Track tool execution and authorization flows:

import logging

logging.basicConfig(level=logging.INFO)

# Arcade tools automatically log execution
run_gmail_agent(
    user_id="user@example.com",
    query="Check my emails"
)

# View logs:
# INFO:arcade:Tool execution: Gmail.ListEmails
# INFO:arcade:Authorization: completed
# INFO:arcade:Response: 10 messages retrieved

Use these logs to monitor authorization success rates and tool performance.

Best Practices

Follow these guidelines when building Gmail agents:

Security

  • Never hardcode API keys in source code
  • Use environment variables for all credentials
  • Implement custom user verifiers for production
  • Each user maintains separate OAuth tokens through Arcade

Performance

  • Cache tool definitions to reduce initialization time
  • Use streaming responses for long-running operations
  • Limit tool counts per agent (5-10 tools maximum)
  • Select specific tools rather than entire toolkits when possible

Authorization

  • Handle NodeInterrupt exceptions gracefully
  • Display clear instructions for OAuth consent
  • Implement retry logic after authorization completion
  • Test authorization flows with multiple users

Tool Selection

  • Choose relevant tools only - avoid loading unnecessary toolkits
  • Be aware of duplicate functionality across tools
  • Customize agent instructions to guide tool selection

TypeScript Implementation

Build the same agent in TypeScript:

import { Arcade } from "@arcadeai/arcadejs";
import { executeOrAuthorizeZodTool, toZod } from "@arcadeai/arcadejs/lib";
import { ChatOpenAI } from "@langchain/openai";
import { MemorySaver } from "@langchain/langgraph";
import { createReactAgent } from "@langchain/langgraph/prebuilt";

// Initialize Arcade
const arcade = new Arcade();

// Get Gmail tools
const gmailToolkit = await arcade.tools.list({
  toolkit: "Gmail",
  limit: 30
});

// Convert to Zod format for LangGraph
const tools = gmailToolkit.items.map(tool =>
  toZod({
    tool,
    client: arcade,
    userId: "user@example.com",
    executeFactory: executeOrAuthorizeZodTool
  })
);

// Create agent
const model = new ChatOpenAI({ model: "gpt-4o" });
const boundModel = model.bindTools(tools);
const memory = new MemorySaver();

const agent = createReactAgent({
  llm: boundModel,
  tools,
  checkpointer: memory
});

// Run agent
const result = await agent.invoke({
  messages: [{ role: "user", content: "List my recent emails" }]
}, {
  configurable: {
    thread_id: "gmail_thread_1",
    user_id: "user@example.com"
  }
});

See the complete TypeScript example in Arcade's repository.

Additional Resources

Continue learning about Arcade and agent development:

Next Steps

Expand your agent's capabilities:

This architecture scales from prototype to production while maintaining proper security boundaries. Arcade handles the OAuth complexity, allowing you to focus on building agent capabilities.

SHARE THIS POST

RECENT ARTICLES

Rays decoration image
THOUGHT LEADERSHIP

How to Query Postgres from GPT-5 via Arcade (MCP)

Large language models need structured data access to provide accurate, data-driven insights. This guide demonstrates how to connect GPT-5 to PostgreSQL databases through Arcade's Model Context Protocol implementation, enabling secure database queries without exposing credentials directly to language models. Prerequisites Before implementing database connectivity, ensure you have: * Python 3.8 or higher installed * PostgreSQL database with connection credentials * Arcade API key (free t

Rays decoration image
THOUGHT LEADERSHIP

How to Connect GPT-5 to Slack with Arcade (MCP)

Building AI agents that interact with Slack requires secure OAuth authentication, proper token management, and reliable tool execution. This guide shows you how to connect GPT-5 to Slack using Arcade's Model Context Protocol (MCP) implementation, enabling your agents to send messages, read conversations, and manage channels with production-grade security. Prerequisites Before starting, ensure you have: * Arcade.dev account with API key * Python 3.10+ or Node.js 18+ installed * OpenAI A

Rays decoration image
THOUGHT LEADERSHIP

How to Build a GPT-5 Gmail Agent with Arcade (MCP)

Building AI agents that can access and act on Gmail data represents a significant challenge in production environments. This guide demonstrates how to build a fully functional Gmail agent using OpenAI's latest models through Arcade's Model Context Protocol implementation, enabling secure OAuth-based authentication and real-world email operations. Prerequisites Before starting, ensure you have: * Active Arcade.dev account with API key * Python 3.10 or higher installed * OpenAI API key w

Blog CTA Icon

Get early access to Arcade, and start building now.