Connect AG2 Agents to 8,000+ Tools with Arcade.dev - AG2

Connect AG2 Agents to 8,000+ Tools with Arcade.dev

Overview

Arcade.dev is a tool execution runtime that provides 8,000+ pre-built integrations with just-in-time authorization. It sits between your agents and enterprise systems: your agent calls a tool, the user authenticates once through Arcade, and Arcade handles token management, scope negotiation, and credential storage from that point on. No service accounts, no secrets in your codebase.

This solves common problems when connecting agents to external services:

In this post, we'll build a Gmail assistant using AG2 and the Arcade SDK. The agent will list, search, read threads, send, reply, and archive emails using AG2's AssistantAgent + UserProxyAgent, pattern while Arcade handles all the authentication and API complexity behind the scenes.

How It Works

Your AG2 agent calls tools by name. Arcade routes each call to the right service with the right credentials:

AG2 handles agent orchestration. Arcade handles tool infrastructure. Each does what it's best at.

Building a Gmail Agent

You'll need an OpenAI API key, an Arcade API key, and the email you used to register at arcade.dev.

Installation

pip install "ag2[openai]" arcadepy

Step 1: Initialize Arcade and the tool helper

The call_arcade_tool helper handles authorization automatically: if a tool hasn't been authorized yet, it prints a URL for the user to visit, then waits for completion.

main.py

import os
from arcadepy import Arcade

arcade = Arcade(api_key=os.environ["ARCADE_API_KEY"])
ARCADE_USER_ID = os.environ["ARCADE_USER_ID"]

def call_arcade_tool(tool_name: str, inputs: dict) -> dict:
    """Authorize (if needed) and execute an Arcade tool, returning the output."""
    auth = arcade.tools.authorize(tool_name=tool_name, user_id=ARCADE_USER_ID)
    if auth.status != "completed":
        print(f"\n[Auth required] Visit this URL to authorize {tool_name}:\n  {auth.url}\n")
        arcade.auth.wait_for_completion(auth.id)

result = arcade.tools.execute(
        tool_name=tool_name,
        input=inputs,
        user_id=ARCADE_USER_ID,
    )
    if not result.output:
        return {}
    data = result.output.model_dump()
    return data.get("value") or {}

The first time a tool is called, Arcade sends the user an OAuth link. After that, tokens are cached automatically.

Step 2: Create tool wrappers for AG2

We create thin wrappers that call call_arcade_tool with the Arcade tool name and inputs. The full example includes 11 tools (search, archive, trash, reply, drafts, and more). Here are three of the core ones:

main.py

def list_emails(
    sender: str = None, max_results: int = 50, is_unread: bool = False
) -> dict:
    """List emails from Gmail inbox.
    - sender: filter by sender email address (optional)
    - max_results: maximum number of emails to return (default 50)
    - is_unread: if True, return only unread emails
    """
    inputs: dict = {"max_results": max_results, "is_unread": is_unread}
    if sender:
        inputs["sender"] = sender
    return call_arcade_tool("Gmail.ListEmails", inputs)

def get_thread(thread_id: str) -> dict:
    """Fetch the full conversation thread by thread ID."""
    return call_arcade_tool("Gmail.GetThread", {"thread_id": thread_id})

def send_email(to: str, subject: str, body: str) -> dict:
    """Send a new email.
    - to: recipient email address
    - subject: email subject line
    - body: plain-text email body
    """
    return call_arcade_tool(
        "Gmail.SendEmail", {"recipient": to, "subject": subject, "body": body}
    )

Step 3: Wire up the AG2 agents

The AssistantAgent decides which tools to call, and the ConversableAgent executes them. We register each function on both agents:

main.py

import datetime
from autogen import AssistantAgent, ConversableAgent, LLMConfig

llm_config = LLMConfig({"model": "gpt-4o", "api_key": os.environ["OPENAI_API_KEY"]})

_today = datetime.date.today().strftime("%Y-%m-%d")

assistant = AssistantAgent(
    name="GmailAssistant",
    llm_config=llm_config,
    system_message=(
        f"Today's date is {_today}.\n\n"
        "You are a helpful Gmail assistant that manages emails via Arcade tools. "
        "Use the provided tools to complete the user's requests.\n\n"
        "Always confirm with the user before sending any email. "
        "After completing a task, end your reply with TERMINATE."
    ),
    is_termination_msg=lambda msg: "TERMINATE" in (msg.get("content") or ""),
)

user_proxy = ConversableAgent(
    name="User",
    human_input_mode="NEVER",
    max_consecutive_auto_reply=20,
    is_termination_msg=lambda msg: "TERMINATE" in (msg.get("content") or ""),
)

# Register tools on both agents
for func, description in [\
    (list_emails, "List emails from Gmail. Filter by sender, unread status, and max results."),\
    (get_thread, "Get the full conversation thread by thread ID."),\
    (send_email, "Send a new email to a recipient with subject and body."),\
]:
    user_proxy.register_for_execution()(func)
    assistant.register_for_llm(description=description)(func)

Step 4: Conversation loop

main.py

if __name__ == "__main__":
    print("Gmail Assistant ready. Type your request (or 'exit' to quit).")
    while True:
        message = input("\nYou: ").strip()
        if not message or message.lower() in ("exit", "quit", "bye"):
            print("Goodbye!")
            break
        user_proxy.run(
            recipient=assistant,
            message=message,
            clear_history=False,
        ).process()
        print("\nAnything else? (type your next request or 'exit' to quit)")

Run it:

source .env && uv run python main.py

On the first run, Arcade will print an authorization URL. Open it in your browser, sign in with Google, and grant the requested Gmail permissions. This happens once. Subsequent runs skip this step since Arcade caches the token.

Gmail Assistant ready. Type your request (or 'exit' to quit).

You: Show my unread emails

GmailAssistant: Let me fetch your unread emails.
  [Calling Gmail.ListEmails with is_unread=True]

GmailAssistant: Here are your unread emails:

1. From: alice@company.com | Subject: Q2 Planning Doc | Date: 2026-03-18
   Snippet: Hey, I've attached the Q2 planning document...

2. From: bob@company.com | Subject: Re: API design review | Date: 2026-03-17
   Snippet: Looks good overall, just a few comments on the auth flow...

...

TERMINATE

Anything else? (type your next request or 'exit' to quit)

What Arcade Handles For You

Try It Yourself

The complete working code is available in the build-with-ag2 repository:

Where to Take It Next

The Gmail agent uses Arcade's Gmail toolkit, but Arcade exposes the entire Google Workspace suite (Calendar, Drive, Docs) plus Linear, Slack, GitHub, and thousands more. The pattern stays exactly the same: write a thin wrapper, register it with your AG2 agent, and Arcade handles the rest.

A few ideas:

Learn More