Tools
Tools are the capabilities agents can use. Three kinds:
- Built-in tools: capabilities the platform provides directly (memory, task history, task setup, feedback). Never passed in
tools; the configurable ones are toggled withenable_*fields. See Built-in tools. - 3rd-party integrations: external services like Gmail, Slack, and Google Ads, connected via OAuth.
- Desktop tools: when sandbox execution is enabled, every run also gets
computer,bash, and a file editor over a full Linux desktop, automatically. See Computer use.
The tools array below is only for 3rd-party integrations and custom tools. Passing a built-in name (e.g. "memory") returns a 422.
List available integrations
from m8tes import M8tes
client = M8tes()
page = client.apps.list()
for app in page.data:
status = "connected" if app.connected else "not connected"
print(f"{app.name} ({app.category}): {status}")Each app has a stable key such as google:ads or google:search_console. Use the key in app operations and tools arrays to select the exact integration. Existing provider aliases such as gmail remain supported. The catalog includes connection guidance, API-key labels, and region choices without exposing internal IDs or OAuth secrets.
# List every connection in this exact scope (omit user_id for your own account).
connections = client.apps.connections.list(user_id="cust_123")
for connection in connections.data:
print(connection.app_key, connection.status, connection.account_label)
# Provider-specific key options, such as a data region.
client.apps.connect_api_key("sentry:error_tracking", api_key="sntrys_...",
options={"region": "eu"})Connections expose selected account IDs and accessible Google Ads customer IDs as explicit fields. Credential metadata is never returned. API-key connects and Composio completion accept agent_id to grant an account-scoped agent access after connecting; an end-user connection cannot use this account assignment shortcut.
Native Google connections use the same app API. Start and complete OAuth with google:ads or google:search_console, then discover and select the resource in the exact scope:
customers = client.apps.list_customers("google:ads", user_id="cust_123")
client.apps.select_customer("google:ads", customers.data[0].id, user_id="cust_123")
sites = client.apps.list_sites("google:search_console", user_id="cust_123")
client.apps.select_site("google:search_console", sites.data[0].site_url, user_id="cust_123")Slack's account setup helpers expose native V2 response shapes:
workspaces = client.apps.list_workspaces("slack:messaging")
channels = client.apps.list_channels("slack:messaging")
members = client.apps.list_members("slack:messaging")Apps with auth_type="external_oauth" use connect_external_oauth(app.key, redirect_uri=...), followed by complete_external_oauth(app.key, code=..., state=..., redirect_uri=...). This flow supports account scope only; passing user_id is rejected. Pass the m8tes /apps page as redirect_uri (for example https://www.m8tes.ai/apps); the server adds its app marker. The authenticated browser that authorized completes the connection. Arbitrary external callbacks are rejected, matching the account-consent boundary of Composio.
Assign tools to an agent
Set the base tool set when creating an agent. These tools are available on every run:
bot = client.agents.create(
name="marketing bot",
tools=["slack", "googlesheets", "gmail"],
instructions="Manage marketing campaigns and report on metrics",
user_id="cust_123",
)Override per task or per run
Tasks and runs can override the agent's tool set:
task = client.tasks.create(
agent_id=bot.id,
instructions="Send the weekly newsletter via Slack",
tools=["slack"], # only Slack for this task
user_id="cust_123",
)
run = client.runs.create(
agent_id=bot.id,
message="Send the weekly newsletter",
tools=["slack"], # only Slack for this run
stream=False,
user_id="cust_123",
)The most specific level wins: run over task over agent. If a run omits tools, the task's tools apply, then the agent's.
Agents discover tool schemas as needed across model providers. Assigned integrations remain available without loading every schema into each model request; the agent searches for a tool by name and loads only that schema. Approvals apply to the individual tool being called, under its own name.
Discovery reduces the initial tool count, but a long conversation can accumulate selected schemas. If a provider rejects the count, the run fails with tool_limit_exceeded and auto_retryable=false. Repeating the same request or Continue without changing the tool set can fail again. Narrow the run's tools override where applicable; see Run errors and Detecting failures.
This cascade covers catalog tools. Custom tools attach to an agent only. A task- or run-level tools list containing a custom slug is rejected. For one agent per end-user acting on your own API, see Your API as MCP tools.
Account-level connections are made through the Developer Dashboard; OAuth flows are handled automatically, and client.apps.list() (above) shows what's connected.
End-user connections (multi-tenant OAuth)
Connect apps programmatically for your end-users. Each customer authorizes their own Gmail, Slack, etc.
1. Initiate connection
conn = client.apps.connect_oauth(
"gmail",
redirect_uri="https://yourapp.com/callback",
user_id="cust_123",
)
print(conn.authorization_url) # redirect your user here; keep conn.connection_id2. Complete connection
After the user completes OAuth and is redirected back:
result = client.apps.connect_complete("gmail", connection_id=conn.connection_id, user_id="cust_123")
print(result.status) # "connected"3. Check status and disconnect
connections = client.apps.connections.list("gmail", user_id="cust_123")
for connection in connections.data:
print(connection.status, connection.account_label, connection.scopes)
client.apps.disconnect("gmail", user_id="cust_123")Several accounts of one app
Connect a second Gmail inbox or Sentry organization next to the first with add_account=True. Runs get every account. The app keeps one tool set, and each of its tools takes a required connected_account argument. The agent picks an account per call, so it can read one inbox and reply from another in the same run.
client.apps.connect_api_key("sentry:error_tracking", api_key="sntrys_...", add_account=True)
for c in client.apps.connections.list("sentry:error_tracking").data:
print(c.id, c.name) # c.name is what the agent passes as connected_account
# A label and a one-line note tell the agent which account is for what.
client.apps.connections.update(42, label="EU org", notes="EU production errors only")
client.apps.connections.delete(42) # disconnect just this accountFor OAuth apps, pass add_account=True to both connect_oauth and connect_complete. apps.disconnect(app) returns 409 when the app has several accounts, so it never disconnects the wrong one.
Isolation
- Each end-user gets their own OAuth connection, scoped by
user_id - Runs for that user automatically use their connected accounts
- There is no fallback from
user_id-scoped runs to account-level connections
Next: Built-in tools · Computer use · Human-in-the-loop · Users