Built-in tools
Every agent gets built-in tools from the platform (no integration to connect): memory, cross-run history, self-management, and more.
Do not put them in the tools array. That array is for 3rd-party integrations and custom tools; a built-in name like "memory" returns 422. Toggle the four configurable built-ins with enable_* fields; the rest follow account and agent config.
The catalog
"Multi-tenant" means the tool is available on end-user (user_id-scoped) runs. First-party only tools act on the account owner, so they are never mounted on a multi-tenant run.
The four configurable toggles
memory, history, task_setup_tools, and feedback can be set at three levels. They resolve run → task → agent → platform default (on); the most specific wins, and an omitted toggle inherits the next level up:
# agent default: applies to all its runs (scheduled, webhook, inbound)
bot = client.agents.create(
name="report bot",
instructions="Generate read-only reports",
enable_task_setup_tools=False, # this persona never manages tasks
user_id="customer_42",
)
# task default
task = client.tasks.create(agent_id=bot.id, instructions="weekly summary",
enable_history=False, user_id="customer_42") # this task ignores prior runs
# run override
run = client.runs.create(agent_id=bot.id, message="One-off analysis, don't save anything",
memory=False, stream=False, user_id="customer_42")
client.agents.update(bot.id, enable_task_setup_tools=None, user_id="customer_42") # None resets to platform defaultHow the non-configurable tools are gated
- email your owner is always available on first-party runs; Slack DM your owner turns on when a Slack workspace is connected. Both are off on multi-tenant (
user_id) runs. - company is mounted only for the Lead mate.
- read-only inbox follows the agent's fetchmail setting (
agents.enable_fetchmail/disable_fetchmail); SMS appears once you provision a Twilio number; computer use follows account-level sandbox execution.
Use discovery to see each tool's resolved enabled state for a given agent or end-user.
Run delivery (set_run_delivery)
On scheduled and webhook-triggered runs, agents call set_run_delivery before the closing message to declare channel: email (deep work + optional completion email), slack (quick nudge; email suppressed; first-party only for the Slack send), or none (all clear). Read the result from GET /runs/{id}/outcome (delivery_channel) or run.completed webhooks. See Runs: scheduled delivery.
Disable with disabled_builtin_tools: ["delivery"].
Task setup
Task setup tools let an agent manage work in its own scope mid-run: create tasks, set schedules, start and cancel runs, answer other runs' approval prompts, connect integrations, rotate webhooks, and write memories. On by default; disable per run with task_setup_tools=False (feedback=False works the same way):
# on by default: the agent can wire its own schedule
run = client.runs.create_and_wait(
agent_id=1,
message="set a schedule to run every weekday at 9am UTC, then stop",
user_id="customer_42",
)
# agent called set_task_schedule with cron="0 9 * * 1-5"
# off for runs that should not touch config
run = client.runs.create(agent_id=1, message="summarize my emails",
task_setup_tools=False, stream=False, user_id="customer_42")Task setup tools act within the same account and same end-user scope: an agent can manage other agents, tasks, and runs in that scope, but never cross into a different end-user scope (Users).
Attaching an app requires an active connection in the same account and end-user scope. Connecting new credentials has its own approval or consent flow. Schedule pause/resume is reversible, like setting the cadence.
The SDK's built-in Monitor tool also auto-approves like Bash; credential and protected-config blocks still apply. These setup and background-command tools count as writes for retry safety.
When a tool requires approval, unattended calls email the owner and pause. See Human-in-the-Loop.
API note:
teammate_idis the wire name for the agent id in JSON bodies, and tool names likelist_teammateskeep it too. The SDK acceptsagent_id(canonical) andteammate_idalike.
Tool reference
Older current-only tool names still work for compatibility; the canonical surface is below.
Read and inspect
These are agent-facing MCP calls. Lists return compact summaries; request a specific item when the full content is needed:
list_taskspreviews instructions at 200 characters and reportsinstructions_truncated. Passtask_idwithinclude_instructions=Truefor the complete instructions.list_custom_toolsreturns inventory metadata. Pass an exact slug or name astoolwithinclude_definitions=Trueto retrieve that tool's definitions.- On shared-mate history,
get_run_detailsdefaults to 10 messages and 2,000 characters per message. Follownext_before_sequenceusingbefore_sequencefor older messages. For a long message, pass itssequenceasmessage_sequenceand itsnext_content_offsetascontent_offsetto read the next chunk. - The Lead mate's
list_value_use_casesandlist_value_observationsreturnitems,has_more, andnext_cursor, with 20 rows by default. Passnext_cursorasstarting_afterfor another page. Full details requireinclude_details=Trueplus an exactuse_case_idorobservation_id, respectively.
Create and manage: tasks, schedules, agents, integrations
Status and access changes
These carry the irreversible or trust-sensitive operations. They ask in Auto or Plan mode unless the owner has granted Always-allow; autonomous/yolo supplies a standing grant. Explicit denials still apply.
App triggers (Composio)
Make a task reactive: run it when a connected app emits an event (a new GitHub issue, a Stripe payment, an inbound Gmail message). The app must already be connected as a Composio integration. See Webhook Triggers for the SDK/API path to the same triggers.
Examples: self-improving and self-wiring tasks
The agent completes work, then updates the task instructions to record its approach, so the next run skips discovery entirely:
run = client.runs.create_and_wait(
agent_id=1,
message="pull this week's mrr from stripe and post the delta to #revenue on slack",
user_id="customer_42",
)
# Agent completes the work, then calls update_task to save what it learned:
# update_task(instructions="... stripe mrr is in /v1/metrics?metric=mrr.
# compare current_period_end vs previous_period_end.
# #revenue channel id is C01ABC123.")Or the agent wires its own Composio app trigger, so the task runs automatically on a GitHub event:
run = client.runs.create_and_wait(
agent_id=1,
message="run this task whenever a new issue is opened in acme/app",
user_id="customer_42",
)
# Agent calls list_app_trigger_types("github") then create_app_trigger(
# app="github", trigger_name="GITHUB_ISSUE_ADDED_EVENT",
# trigger_config={"owner": "acme", "repo": "app"})Jev judgments
Jev is an advisory second-opinion tool. Jev judgments are included on every plan, Free included, at no charge: m8tes funds them by default. API customers can connect their own TypeSafe key for account-wide provider billing; the Platform always uses m8tes funding. Neither funding mode debits your m8tes balance. Availability follows the platform service setting and, for agent tools, the agent’s built-in tool configuration. The two tools use the same service as POST /api/v2/judgments and client.judgments.create(...) in the Python and TypeScript SDKs. Do not add them to the integrations tools array.
Use your own TypeSafe key
Configure your TypeSafe API key once from a trusted server with your m8tes account credentials. The connection covers every end user and agent in that account; these connection endpoints do not accept a user_id. Keep the key in server-side environment variables or a secret manager. Never put it in agent instructions, tool arguments, browser code, or judgment state/evidence.
import os
from m8tes import M8tes
client = M8tes() # Reads M8TES_API_KEY on your server.
connection = client.judgments.configure(api_key=os.environ["TYPESAFE_API_KEY"])
print(connection.funding_source) # customer; the key is never returned
status = client.judgments.connection()
# Explicitly return to platform funding, when available:
# client.judgments.disconnect()These methods use PUT, GET, and DELETE /api/v2/judgments/connection. Configure and inspect return only connected, enabled, funding_source, model, and updated_at; disconnect returns HTTP 204. Connected means a key is stored, not that TypeSafe has accepted it. Configure again to rotate the key. A key TypeSafe rejects returns HTTP 403 (jev_credential_rejected); a provider failure returns HTTP 503 (jev_unavailable). Neither silently falls back to platform funding; replace the key or disconnect explicitly.
With customer funding, TypeSafe charges your TypeSafe account directly. The per-minute rate limit and input limits still apply; the platform’s daily budgets and daily call caps do not. The platform service setting can still disable judgments. The estimated cost_usd remains available for monitoring, and funding_source identifies who funds the provider call; it is not a charge against your m8tes balance. API and agent usage automatically use the account connection without changing the judgment request.
Choose a judgment
jev_decidecompares bounded next steps, checks completion criteria, or supplies a narrow second opinion. Pass relevantstateplus named Choice, Noul, or Scorequestions.jev_verifychecks individual claims against supplied evidence. Passclaimswith IDs andevidence_ids, plusevidencewith IDs and original text or platform source references.
Before delivering a consequential report, use verification for claims that depend on source evidence. Batch independent checks with their original excerpts; ordinary chat messages do not need a check. Use bounded second opinions where they help resolve uncertainty, and avoid repeating unchanged checks or retrying until an answer agrees with the draft.
Disable the agent tools with disabled_builtin_tools: ["judgments"]. Existing run-scope and shared-mate permission boundaries still apply.
Verification returns a Choice for each claim: supported, contradicted, or insufficient_evidence. Supply original tool results or source passages, with the surrounding context needed to interpret them. A claim without adequate evidence should lead to gathering evidence or asking for review. A confident judgment over a fabricated or incomplete source is still not a fact check of the real world.
Evidence accepts either inline text or a source reference to an accessible platform document or stored tool result. The platform resolves references within the request’s account and end-user scope. Verification coverage.provenance records the origin, source ID when present, and SHA-256 of the resolved content. This traces what Jev evaluated; it does not authenticate the source’s claims. Every provenance item reports independently_authenticated: false. Neither tool independently researches the outside world. Keep exact arithmetic, dates, source-quote existence checks, and execution in code.
from m8tes import M8tes
client = M8tes()
judgment = client.judgments.create(
mode="verify",
claims=[{
"id": "savings",
"text": "The budget change saved money without reducing conversions.",
"evidence_ids": ["weekly_report"],
}],
evidence=[{
"id": "weekly_report",
"text": "Spend fell after the change. Conversion tracking was unavailable.",
}],
user_id="customer_42",
idempotency_key="weekly-report-2026-09-21-v1",
)
print(judgment.answers["savings"])
# Fetch the exact saved answer without another Jev call or charge.
saved = client.judgments.get(judgment.id, user_id="customer_42")
assert saved.id == judgment.id
# Inspect the advisory answer; the evidence does not establish the full claim.Use a source reference instead of inline text when the evidence already exists on the platform. For example, evidence=[{"id": "weekly_report", "source": {"type": "document", "id": document.id}}] reads that document’s stored content. Use type: "tool_result" for a persisted conversation message ID containing tool-result blocks; assistant prose and tool inputs are excluded. Supply the original user_id; inaccessible records are rejected. A source and inline text are mutually exclusive. Optional run_id attributes API usage to a run in the same account and end-user scope; agent tools receive their run context automatically.
The endpoint accepts up to 32 questions or claims per request, up to 32 evidence items, and a 128 KiB serialized input limit after source resolution. Each source is limited to 16,384 characters; oversized content is rejected rather than silently truncated. Question instructions and rubric descriptions are strings; state may be a string, object, or array. Choice accepts 2–255 named options. Noul returns P(yes), without separate confidence. Score uses an ordered array of 2–10 rubric levels and returns a probability-weighted level. Choice/Score confidence describes distribution concentration, not factual accuracy or authority to act. Evaluate any thresholds on representative cases before using them in a workflow; the tool does not automatically approve, block, or execute actions. Existing permission and approval rules remain authoritative.
Results include the provider funding_source (platform or customer), resolved model, typed answers, token usage, latency_ms, estimated provider cost_usd, and the verification rubric_version. The cost is an estimate, not a charge against your m8tes balance; customer-funded requests are billed by TypeSafe. Successful results are available through GET /api/v2/judgments/{id} or client.judgments.get(...) for 30 days. Use the same user_id as creation. After 30 days, retrieval returns HTTP 410 (result_not_retained). Settled usage metadata with known cost is retained for 90 days; unresolved or unknown-cost reservations remain for reconciliation. Accounts or runs configured for metadata-only retention do not store answers: retrieval or replay returns HTTP 410 (result_not_retained), while the same key still prevents another paid call during its 24-hour window. Raw claims, state, and evidence text are not retained in the judgment ledger.
Pass Idempotency-Key (Python idempotency_key, TypeScript idempotencyKey, or agent-tool idempotency_key) to protect a logical operation for 24 hours. Reuse the key and identical request after a lost response; a successful replay returns the same ID and original cost without another provider charge. A changed request, in-progress attempt, or unsuccessful attempt returns a conflict instead of executing again. After 24 hours, a key may start a new operation. SDK POST retries require a key. Do not repeatedly change keys to bypass failures or limits. Validation and service failures produce API/SDK errors, never a fabricated approving answer.
Discovery
List the built-in tools with their resolved state. Pass agent_id to reflect that agent's defaults, or user_id to check multi-tenant availability:
for tool in client.built_in_tools.list(agent_id=bot.id).data:
print(tool.name, tool.enabled, tool.multi_tenant_safe)Each entry reports name, server_name, display_name, description, enabled (resolved for the requested scope), multi_tenant_safe, and configurable.
Next: Tools · Users · Memories · Computer use