ToolProvider
The ToolProvider interface defines how the editor discovers and resolves integration tools from external platforms. Mastra includes two built-in implementations: ComposioToolProvider and ArcadeToolProvider.
See Editor tools for provider setup and the Studio workflow. See tool configuration for stored selections and resolution behavior.
ToolProvider interfaceDirect link to ToolProvider interface
Providers expose metadata and the legacy discovery and resolution methods. Agent Builder integrations can also implement the optional VNext catalog, connection, authorization, and health methods.
info:
displayName?:
capabilities?:
defaultScope?:
listToolkits()?:
listTools(params?):
getToolSchema(slug)?:
resolveTools(slugs, configs?, options?):
listToolkitsVNext()?:
listToolsVNext(options?)?:
resolveToolsVNext(options)?:
listConnectionFields(options)?:
getAuthStatus(authId)?:
getConnectionStatus(options)?:
listConnections(options)?:
getHealth()?:
revokeConnection(connectionId)?:
ComposioToolProviderDirect link to ComposioToolProvider
Connects to Composio for access to hundreds of integration tools.
Usage exampleDirect link to Usage example
import { MastraEditor } from '@mastra/editor'
import { ComposioToolProvider } from '@mastra/editor/composio'
const editor = new MastraEditor({
toolProviders: {
composio: new ComposioToolProvider({
apiKey: process.env.COMPOSIO_API_KEY!,
}),
},
})
Constructor parametersDirect link to Constructor parameters
apiKey:
allowedToolkits?:
allowedTools?:
defaultScope?:
userIdResolver?:
Tool slugsDirect link to Tool slugs
Composio tools use uppercase slug format: GITHUB_CREATE_ISSUE, SLACK_SEND_MESSAGE.
AuthenticationDirect link to Authentication
Connections use per-author scope by default. Set defaultScope: 'caller-supplied' to bucket authorization by the caller identity resolved from MASTRA_RESOURCE_ID_KEY in request context. Ensure each authenticated request provides a stable, unique resource ID. When using MastraAuthWorkos, configure mapUserToResourceId to set this value from the authenticated user.
How the provider resolves the Composio user for a tool call depends on the connection:
- Author-bound connections (
kind: 'author') execute as the agent author's user against the pinned connected account. - Invoker-bound connections (
kind: 'invoker') execute as the authenticated invoker against the exact pinned account, which may be an account another user shared with the invoker through Composio's access control list (ACL). The user ID comes fromuserIdResolverwhen configured, then the authenticated user, and never from the MemoryresourceId. Invoker resolution fails when no authenticated user or resolver result exists. - Caller-supplied scope (
scope: 'caller-supplied') usesuserIdResolverwhen configured. Otherwise, it falls back to the legacyresourceIdfrom request context for backward compatibility. When a specific connected account is pinned, execution routes to that exact account. Otherwise, Composio auto-resolves within the user's bucket.
Execute with a shared accountDirect link to Execute with a shared account
Bob needs to run a Salesforce tool with an account that Alice shared through Composio. Keep each identity separate:
| Identity | Value |
|---|---|
| Memory resource | project_123 |
| Composio user ID | bob |
| Connected account | ca_alice_salesforce |
Register the provider normally. Mastra server authentication writes the authenticated user to request context, so most applications don't need a userIdResolver:
import { Mastra } from '@mastra/core/mastra'
import { MastraEditor } from '@mastra/editor'
import { ComposioToolProvider } from '@mastra/editor/composio'
const editor = new MastraEditor({
toolProviders: {
composio: new ComposioToolProvider({
apiKey: process.env.COMPOSIO_API_KEY!,
}),
},
})
export const mastra = new Mastra({ editor })
Configure the agent with an invoker connection pinned to ca_alice_salesforce. When Bob invokes the agent, Mastra sends bob as the Composio user and the pinned account ID as the exact connected account. The Memory resource stays project_123. Composio then checks whether the account's ACL permits Bob to execute it.
Map application users to Composio usersDirect link to Map application users to Composio users
Use userIdResolver when your Composio user IDs differ from the IDs returned by Mastra authentication, or when your application must authorize the stored account pin before execution.
type ComposioUserIdResolver = (
input: ComposioUserIdResolverInput,
) => Promise<string | undefined> | string | undefined
requestContext?:
toolkit?:
connectedAccountId?:
The resolver returns the Composio user ID, or undefined to use the provider's default resolution. Returning an empty string throws instead of silently falling back. The resolver can't replace the connected account.
This example namespaces the Composio user by organization and asks the application's authorization layer to approve the exact account pin:
import { MASTRA_USER_KEY } from '@mastra/server/auth'
import { ComposioToolProvider } from '@mastra/editor/composio'
import { canUseConnectedAccount } from './integration-authorization'
type AuthenticatedUser = {
id: string
organizationId: string
}
function isAuthenticatedUser(value: unknown): value is AuthenticatedUser {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
typeof value.id === 'string' &&
'organizationId' in value &&
typeof value.organizationId === 'string'
)
}
const composio = new ComposioToolProvider({
apiKey: process.env.COMPOSIO_API_KEY!,
userIdResolver: async ({ requestContext, toolkit, connectedAccountId }) => {
const user = requestContext?.getRaw(MASTRA_USER_KEY)
if (!isAuthenticatedUser(user)) return undefined
if (connectedAccountId) {
const allowed = await canUseConnectedAccount({
actorId: user.id,
organizationId: user.organizationId,
provider: 'composio',
toolkit,
connectedAccountId,
})
if (!allowed) {
throw new Error('User cannot access this connected account')
}
}
return `${user.organizationId}:${user.id}`
},
})
Use the same namespaced ID when creating Composio connections and shared-account ACL entries. For example, Bob's Composio user ID in this setup is acme:bob.
Throw from userIdResolver to deny the request. During stored-agent resolution, Mastra logs the failure and omits tools associated with that connection, so no tool call reaches Composio. Other connections continue to resolve. When calling resolveToolsVNext() directly, the error is returned to the caller instead.
Connection management toolsDirect link to Connection management tools
Composio provides tools for starting and monitoring authorization from an agent chat. When allowedToolkits is set, include composio to make these tools available:
const editor = new MastraEditor({
toolProviders: {
composio: new ComposioToolProvider({
apiKey: process.env.COMPOSIO_API_KEY!,
allowedToolkits: ['composio', 'gmail'],
defaultScope: 'caller-supplied',
}),
},
})
Add only the connection management tools that the agent needs:
| Tool | Behavior |
|---|---|
COMPOSIO_MANAGE_CONNECTIONS | Creates an authorization link in chat through a session owned by the caller. |
COMPOSIO_WAIT_FOR_CONNECTIONS | Waits for the caller to finish authorization before the agent continues. |
COMPOSIO_WAIT_FOR_CONNECTIONS is optional. Without it, complete authorization and return to the chat. Then ask the agent to continue. The connected account remains associated with the caller resource ID for later requests.
ArcadeToolProviderDirect link to ArcadeToolProvider
Connects to Arcade for a curated tool catalog with built-in authentication.
Usage exampleDirect link to Usage example
import { MastraEditor } from '@mastra/editor'
import { ArcadeToolProvider } from '@mastra/editor/arcade'
const editor = new MastraEditor({
toolProviders: {
arcade: new ArcadeToolProvider({
apiKey: process.env.ARCADE_API_KEY!,
}),
},
})
Constructor parametersDirect link to Constructor parameters
apiKey:
baseURL?:
Tool slugsDirect link to Tool slugs
Arcade tools use Toolkit.ToolName format: Github.GetRepository, Slack.SendMessage.
AuthenticationDirect link to Authentication
The legacy Arcade resolver uses resourceId from request context when available. It otherwise falls back to the supplied userId, then to a shared default identity. Use default only for intentionally shared integrations. In tenant-isolated deployments, provide a trusted, stable resourceId or explicit userId. Omitting both doesn't isolate callers.