Take actions in your product
An agent that operates your product for one end-user through your product's own REST API. A customer types "refund my last invoice" in your billing app; the agent looks the invoice up and issues the refund through your API, pausing for a human on the refund itself.
The mental model, in three moves:
- Your product becomes the agent's tools. Describe your REST endpoints once (custom tools); the agent sees each as a named tool. m8tes makes the call server-side, with your secret injected and never seen by the agent.
- It acts as one end-user. Pass a
user_idand the agent, its runs, memory, and permissions are scoped to that customer (Users). Isolation is strict, with no fallback to account data. - Writes wait for a human. A custom tool asks for approval before each use until you mark it trusted.
Wrap your REST API
Each endpoint you describe becomes a tool the agent can call by name:
Put {param} in a path to make it an argument (/invoices/{id}); any other arguments the agent passes go to the query string (GET/DELETE) or the JSON body (POST/PUT/PATCH). Auth methods and path rules: Custom tools.
Scope it to an end-user
If every customer hits your API with the same key, keep one account-level server and scope at the agent and run (next section). If each customer has their own credentials, create one server per customer:
For your customers' Gmail, Slack, and other catalog apps, use per-end-user OAuth instead (Tools).
Attach it and run it
Reference the server's slug in an agent's tools, then run for a customer:
The agent sees the tool as mcp__cmcp-<id>__create_refund; you only ever use the slug. Custom slugs attach to an agent — unlike catalog tools, they can't be passed per task or per run (a run-level tools list containing a custom slug is rejected). Give the agent its custom tools once, at create or update.
Keep a human in the loop
Read-only (GET) tools run without asking, in every permission mode — they can't change anything. Writes ask before each use. To let a write run unattended, mark its server trusted:
An untrusted write pauses for approval; in an unattended run with no one to approve, it is marked needs-approval and never silently fires. Handle approvals inline with runs.wait(...), or from your inbox or Slack. See Human-in-the-loop.
What's guaranteed (full detail): egress is server-side and IP-pinned (the agent never connects to your service directly; private and metadata addresses are blocked); your secret is encrypted at rest and never reaches the agent; a server is visible only to its owner and, when scoped, its user_id end-user.
End-to-end: refund an invoice with approval
A billing product lets its agent look up invoices on its own but requires a human to approve refunds, all for one customer:
The lookup ran on its own; the refund paused for on_approval. Surface req.tool_input in your UI so a human can decide before you return "allow" or "deny".
Next: Your API as MCP tools · Custom tools · Users · Human-in-the-loop · Going live
