Authorize tools before the agent runs
With Just-in-time authentication, the agent will request the user to authorize each tool before running it. In many cases, this experience is not what you want to provide to your users. For example, you may expect the users to be able to list their emails, as well as send emails immediately after. With JIT auth, the user would have to authorize each tool separately, which may result in too much friction.
Overview
In this guide, you’ll learn how to use Arcade to authorize multiple tools before the agent runs.
This involves the following steps:
- Get a list of all the tools that the agent will use
- Authorize all the tools (grouped by their Auth provider)
- Run the agent only after all the tools are authorized
This guide will rely on Arcade’s built-in user verification system. If you want to run this in production, you’ll need to implement a custom user verifier. Learn more about user verification in production.
Prerequisites
Setup your environment
Export your Arcade API key as an environment variable:
export ARCADE_API_KEY='{arcade_api_key}'Initialize the client
from arcadepy import Arcade
client = Arcade()
user_id = "{arcade_user_id}"Write a helper function to get the tools
This guide is more useful when we are dealing with tools from multiple toolkits. We’ll write a helper function to get the tools from the Gmail and GitHub toolkits.
from typing import Optional
from arcadepy import NOT_GIVEN, AsyncArcade
from arcadepy.types import ToolDefinition
async def get_tool_definitions(
client: Optional[AsyncArcade] = None,
tools: Optional[list[str]] = None,
toolkits: Optional[list[str]] = None,
raise_on_empty: bool = True,
limit: Optional[int] = None,
offset: Optional[int] = None,
) -> list[ToolDefinition]:
"""
Retrieve tool definitions asynchronously from the Arcade client, accounting for pagination.
Args:
tools: Optional list of specific tool names to include.
toolkits: Optional list of toolkit names to include all tools from.
raise_on_empty: Whether to raise an error if no tools or toolkits are provided.
limit: Optional limit on the number of tools to retrieve per request.
offset: Optional offset for paginated requests.
Returns:
List of ToolDefinition instances.
Raises:
ValueError: If no tools or toolkits are provided and raise_on_empty is True.
"""
if client is None:
client = AsyncArcade()
all_tools: list[ToolDefinition] = []
# If no specific tools or toolkits are requested, raise an error.
if not tools and not toolkits:
if raise_on_empty:
raise ValueError("No tools or toolkits provided to retrieve tool definitions.")
return []
# First, gather single tools if the user specifically requested them.
if tools:
for tool_id in tools:
# ToolsResource.get(...) returns a single ToolDefinition.
single_tool = await client.tools.get(name=tool_id)
all_tools.append(single_tool)
# Next, gather tool definitions from any requested toolkits.
if toolkits:
for tk in toolkits:
paginated_tools = await client.tools.list(
toolkit=tk,
limit=NOT_GIVEN if limit is None else limit,
offset=NOT_GIVEN if offset is None else offset,
)
async for tool in paginated_tools:
all_tools.append(tool)
return all_toolsDefine the tools that the agent will use
To make this example representative, we’ll use tools from two different toolkits: Gmail and GitHub.
tools = await get_arcade_tools(client, toolkits=["Gmail", "GitHub"])