Migrate from Arcade
Move an agent from Arcade to AgentKit: map Arcade users, auth providers and tools to AgentKit, move your users, and update your tool calls.
Use this page if your agent uses Arcade today. It covers what’s specific to Arcade: how its concepts map to AgentKit, how to move your users, and how your tool calls change. The steps every migration shares, such as setting up Scalekit and creating connections, are in Migrate to AgentKit.
How Arcade maps to AgentKit
Section titled “How Arcade maps to AgentKit”| Arcade | AgentKit |
|---|---|
user_id | Identifier: your app’s ID for the user, passed on each call |
| Auth provider | Connection: one per app, with its auth type, scopes and OAuth app |
| A user’s authorization for a provider | Connected account: one per user and connection |
| MCP server (toolkit) | Connector, such as Gmail or GitHub |
Tool, such as Gmail.ListEmails | Tool, such as gmail_fetch_mails |
client.tools.authorize() / client.auth.start() | Authorization link |
auth_response.context.token for direct API calls | actions.request() through the Scalekit proxy |
| MCP gateway | Virtual MCP server with a session token per agent run |
Move your users
Section titled “Move your users”Arcade gives your app a user’s current access token, but not the refresh token or its expiry; it refreshes tokens itself. An imported access token without a refresh token stops working when it expires, so plan for each user to authorize again: send them an authorization link for each app they use, as in Option A.
- Create a connection in Scalekit for each Arcade auth provider your agent uses. If you configured your own OAuth app in Arcade, you can use the same app in Scalekit, so users see the same consent screen. See Bring your own OAuth app.
- For each user, get an authorization link for each app they authorized in Arcade, and send it to them. Use the same user ID you passed to Arcade as the
identifier, unless it’s an email address: AgentKit identifiers should be your app’s opaque user ID.
# Your user IDs and the Scalekit connections each one needsusers = {"user_123": ["gmail", "github"]}
for identifier, connection_names in users.items(): for connection_name in connection_names: link = actions.get_authorization_link( connection_name=connection_name, identifier=identifier ).link print(identifier, connection_name, link) # send this to the user// Your user IDs and the Scalekit connections each one needsconst users: Record<string, string[]> = { user_123: ['gmail', 'github'] };
for (const [identifier, connectionNames] of Object.entries(users)) { for (const connectionName of connectionNames) { const { link } = await actions.getAuthorizationLink({ connectionName, identifier }); console.log(identifier, connectionName, link); // send this to the user }}These blocks reuse the client from Set up Scalekit. To find which apps each user authorized, list their connections in Arcade with client.admin.user_connections.list().
Move your agent code
Section titled “Move your agent code”Execute tools
Section titled “Execute tools”Before (Arcade):
response = client.tools.execute( tool_name="Gmail.ListEmails", user_id="user_123",)print(response.output.value)After (Scalekit):
result = actions.execute_tool( tool_name="gmail_fetch_mails", identifier="user_123", connection_name="gmail", tool_input={"max_results": 5},)print(result.data)Before (Arcade):
const response = await client.tools.execute({ tool_name: 'Gmail.ListEmails', user_id: 'user_123',});console.log(response.output.value);After (Scalekit):
const result = await actions.executeTool({ toolName: 'gmail_fetch_mails', identifier: 'user_123', connector: 'gmail', toolInput: { max_results: 5 },});console.log(result.data);You don’t authorize per tool. Once a user’s connected account is ACTIVE, every tool of that connection works for them. Pass identifier and the connection name on every call: connection_name in Python, connector in Node.js.
Arcade tool names are Toolkit.ToolName; AgentKit tool names are lower case, such as gmail_fetch_mails, and the inputs can differ. Find each tool’s exact name and input in the connector catalog.
Tool definitions for your LLM
Section titled “Tool definitions for your LLM”Where you fetched tool definitions with client.tools.formatted.list(...), list the user’s tools with list_scoped_tools. Each tool comes with its input schema:
tools_response, _ = scalekit_client.actions.tools.list_scoped_tools( identifier="user_123", # Required: list every connection whose tools the agent should see filter={"connection_names": ["gmail"]}, page_size=100, # up to 100 tools per page; pass next_page_token back as page_token for more)const { tools } = await scalekit.tools.listScopedTools('user_123', { // Required: list every connection whose tools the agent should see filter: { connectionNames: ['gmail'] }, pageSize: 100, // up to 100 tools per page; pass nextPageToken back as pageToken for more});To hand them to a framework such as LangChain or the OpenAI Agents SDK, see Choose a framework.
Direct API calls
Section titled “Direct API calls”Where you called an API yourself with auth_response.context.token, call it through the Scalekit proxy with actions.request(). It adds the user’s credentials, so your code never handles the token:
response = actions.request( connection_name="gmail", identifier="user_123", method="GET", path="/gmail/v1/users/me/messages",)const response = await actions.request({ connectionName: 'gmail', identifier: 'user_123', method: 'GET', path: '/gmail/v1/users/me/messages',});If your agent connected to an Arcade MCP gateway, create a Virtual MCP server with the same connections and tools. It has one static URL for all users; mint a session token for the user before each agent run and pass it as a bearer token.
Your own tools
Section titled “Your own tools”Tools you built and deployed to Arcade move into your app as custom tools that call the app’s API through the Scalekit proxy. For an app that isn’t in the catalog, add your own connector.
API reference
The endpoints this page's code calls, with every field and the SDK method for each: