Tools and MCP
Your systems, as tools the agent can call.
Amy speaks the Model Context Protocol (MCP) as a client. Point it at an MCP server you run and its tools become tools the agent can use mid-run — so "how many open tickets does this account have" is answered from your database rather than guessed. This is how your product gives Amy its data and actions.
Amy connects to your server over HTTPS using MCP's Streamable HTTP transport, so your server must serve that transport at a URL Amy can reach.
The three things every run needs
For an agent to use one of your tools on a run, three things must be true — and Amy is quiet about the one that is missing:
- The server is registered (below) and enabled, with its tools bound to handles.
- The run's
toolslist names the handle. A registered server's tools are not in an agent's default set, unless an Admin adds the handle to a capability the agent holds. - If the server needs a credential, the run carries it in
mcpAuth, matched to the server byserverSlug.
tools omits a handle, or
the server needs a token and mcpAuth has none, the run still returns 200 and completes — without those tools, and with no error and no warning event. The agent simply
cannot see them and answers without your data. Test with a question only your server can answer.Register a server
A server you always want available is registered once, by an organisation Admin, in Console → MCP servers → Register server. Registration is a Console action rather
than part of /v1: a standing server changes what every future run on the organisation
can reach.
| You provide | Rules |
|---|---|
| Slug | Lowercase letters, digits and dashes, up to 50 characters, starting with a letter or
digit. This is the serverSlug you use in mcpAuth. |
| URL | Up to 500 characters, https, with no credentials embedded in it. |
| Auth header name | Optional, up to 100 characters (for example Authorization). Leave it empty
for a server that needs no credential. When set, the run's token is sent as the whole value of this header, so include a Bearer prefix yourself if your
server expects one. |
| Tools | Up to 64 bindings, each a handle and a remoteName. The handle is
the stable name Amy and the model use: it must start with mcp_, be at most
100 characters, be unique across all your servers, and not reuse one of Amy's built-in
tool names. The remote name is what the tool is called on your server (up to 200
characters). |
You list the tools yourself: Amy does not enumerate your server and expose whatever it finds, and
the Console has no "discover" or "test" button. Amy lists the server's tools when a run starts,
and a bound remoteName your server does not expose fails the run with mcp_unavailable. A server you disable is skipped, and naming one of its handles in tools is a 400.
Servers on a private network. Amy refuses to connect to internal and private addresses. If your MCP server is not on the public internet, we set that up with you during onboarding.
Use it on a run
curl -X POST https://api.askamy.co.za/v1/agents/amy/runs/await \
-H "X-Api-Key: $AMY_API_KEY" -H "Content-Type: application/json" \
--data-binary @- <<EOF
{
"message": "How many open tickets does account 4182 have?",
"endUserRef": "user_8841",
"tools": ["recall", "remember", "mcp_list_tickets"],
"mcpAuth": [ { "serverSlug": "crm", "token": "Bearer $CRM_TOKEN" } ]
}
EOFconst response = await fetch('https://api.askamy.co.za/v1/agents/amy/runs/await', {
method: 'POST',
headers: {
'X-Api-Key': process.env.AMY_API_KEY!,
'Content-Type': 'application/json'
},
body: JSON.stringify({
message: 'How many open tickets does account 4182 have?',
endUserRef: 'user_8841',
// The complete list of tools for this run. Name your MCP handles and also the
// agent's own tools you want to keep (here its memory); leave one out and the run
// still succeeds, just without that tool.
tools: ['recall', 'remember', 'mcp_list_tickets'],
// The credential for this one run, minted by your back end for this end user.
mcpAuth: [{ serverSlug: 'crm', token: `Bearer ${crmToken}` }]
})
});tools names handles, not servers, and it is the complete list of tools for this run.
Leave it out and the run gets the agent's own tools and none of your MCP tools. Send it and the
run gets exactly the names you list, so to add your MCP tools and keep the agent's own (memory's recall and remember, web search, and so on), name those too; each tool's
name is on the agent's page in the Console, under Tool approvals. An empty list means no tools at
all. A name the agent does not have, or a handle no enabled server binds, is a 400 before
the run starts.
A run that goes ahead without some of your tools says so. Every answer carries toolWarnings, a list that is empty when nothing was left out, and a stream sends a tool_warnings event straight after run_started. The run is never refused
for it: a call that works today keeps working. Each warning has a code, the tools concerned, a fixed message, and the server when one needs
a token:
mcp_tools_not_requested— you sent notoolsand your organisation has MCP tools registered, so the run used the agent's own tools only.default_tools_left_out— you senttoolsand it left out some of the agent's own tools. An empty list sent on purpose does not warn.mcp_server_not_connected— a tool you named is on a server that needs a token and the request carried nomcpAuthfor it, so its tools were left out.
mcpAuth is a short-lived grant for this run: Amy holds the token for the run, never
stores it and never logs it. Mint it on your back end, scoped to the end user the run is for, so
that your server can enforce that user's permissions when the tool is called. Up to 16
entries, each token up to 8192 characters. An entry whose serverSlug matches no server
is ignored.
Resuming a paused run
A run that pauses for an approval reconnects to your
servers when you resume it. The tool list is remembered from the original run and is not re-sent, but the credential is never persisted, so send mcpAuth again:
{
"decision": "approve_once",
"mcpAuth": [ { "serverSlug": "crm", "token": "Bearer …" } ]
}Omit it and a server that needs a token goes dormant for the rest of the run, and the continuation
opens with a tool_warnings event naming it.
Per-request servers
Instead of registering a server, a run can declare it inline in mcpServers. Nothing
is stored: the declaration and its credential live for that run only. It suits a product where the
credential belongs to your end customer rather than to your Amy organisation.
curl -N -X POST https://api.askamy.co.za/v1/agents/amy/runs \
-H "X-Api-Key: $AMY_API_KEY" -H "Content-Type: application/json" \
--data-binary @- <<EOF
{
"message": "How many open tickets does account 4182 have?",
"endUserRef": "user_8841",
"tools": ["recall", "remember", "mcp_list_tickets"],
"mcpServers": [
{
"slug": "crm",
"url": "https://crm.example.com/mcp",
"authHeaderName": "Authorization",
"tools": [ { "handle": "mcp_list_tickets", "remoteName": "list_tickets" } ]
}
],
"mcpAuth": [ { "serverSlug": "crm", "token": "Bearer $CRM_TOKEN" } ]
}
EOFInline declarations are treated as untrusted input, and the gate is narrow:
- The agent must opt in. The agent's Inline MCP servers setting is Not allowed by default; a declaration sent to an agent that does not accept them is a
400onmcpServers. See Agents. - The host must be on your organisation's allowlist. With no allowlist entry matching the host, the declaration is refused before the run starts. We set up your hosts with you during onboarding; there is no self-service page for it.
httpson port 443, no credentials in the URL, and never an internal address. An inline server cannot reach a private network; register it instead.- Shape: up to 16 servers per run; each needs a slug (same rules as above), a
URL, and between 1 and 64 tool bindings with
mcp_handles. A handle may not appear on two servers of one run. - Every inline tool needs approval, always. No setting turns that off and no remembered grant bypasses it. A compromised server can at most propose a call; a person decides. See Approvals.
- Not for
/runs/submit. A submitted (queued) run carries neither inline servers normcpAuth: inline tools are refused, and a registered server that needs a token runs without one, so its tools are left out. The202names them intoolWarnings(mcp_server_not_connected). - Re-declare on resume. The declaration is fingerprinted onto the run. Resume
with the same
mcpServers(andmcpAuth) or the continuation ends with aninline_mcp_mismatcherror. A different URL or tool list cannot be swapped in under a decision a person already made.
When it is unreachable
Admission is fail-closed, and this is the behaviour most worth knowing: Amy connects to every server a run will use and lists its tools before the model is called. If a server is down, refuses the credential, or does not expose a bound tool, the whole run fails rather than quietly answering without your data:
mcp_unauthorized— your server answered401or403to the token.mcp_unavailable— anything else: it could not be reached, a bound tool is not there, or the address is blocked.
The failure arrives after the stream has opened, so on a streamed run it is an error event on a 200 response; on an awaited run the result has status: "failed" and an errorCode. Neither code is retried
automatically: fix the server or the token and start a new run.
event: error
data: {"code":"mcp_unauthorized","message":"A connected MCP tool server refused the credential this run sent, so the run was stopped. …","traceId":"…"}That is deliberate. An agent that has silently lost access to your systems still produces a
confident answer, and you would have no way to tell it from a good one. It is different from a
token that was never sent: that run goes ahead without those tools and says so in toolWarnings, so check that list in your integration.
When a tool call fails
Once the run is under way, an individual tool call that goes wrong does not end it. If your MCP
server returns an error result (isError: true), Amy passes it to the model unchanged
— write error messages you would be happy for the model to read. If the call itself throws, the
model is told only that the tool failed. Either way the stream carries a tool_result with ok: false and the run continues.
Design tools to be quick and safe to call twice: the agent may call one more than once. A retry of a failed run replays results already committed rather than calling the tool again (see Errors).
Checklist
- Server registered and enabled, handles bound, and every
remoteNameexists on your server. - Each run names the handles it needs in
tools. - Each run — and each resume — carries a fresh
mcpAuthif the server needs a credential. - Your server enforces the end user's permissions from that credential.
- A host on a private network? We set that up with you during onboarding.