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:

  1. The server is registered (below) and enabled, with its tools bound to handles.
  2. The run's tools list 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.
  3. If the server needs a credential, the run carries it in mcpAuth, matched to the server by serverSlug.
A missing piece does not fail the run. If 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 provideRules
SlugLowercase letters, digits and dashes, up to 50 characters, starting with a letter or digit. This is the serverSlug you use in mcpAuth.
URLUp to 500 characters, https, with no credentials embedded in it.
Auth header nameOptional, 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.
ToolsUp 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

request
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" } ]
}
EOF
TypeScript
const 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 no tools and your organisation has MCP tools registered, so the run used the agent's own tools only.
  • default_tools_left_out — you sent tools and 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 no mcpAuth for 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:

resume request body
{
  "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.

request
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" } ]
}
EOF

Inline 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 400 on mcpServers. 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.
  • https on 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 nor mcpAuth: inline tools are refused, and a registered server that needs a token runs without one, so its tools are left out. The 202 names them in toolWarnings (mcp_server_not_connected).
  • Re-declare on resume. The declaration is fingerprinted onto the run. Resume with the same mcpServers (and mcpAuth) or the continuation ends with an inline_mcp_mismatch error. 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 answered 401 or 403 to 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.

text/event-stream
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 remoteName exists on your server.
  • Each run names the handles it needs in tools.
  • Each run — and each resume — carries a fresh mcpAuth if 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.