Subcore AI Docs
MCP server

Tools

Every tool the server can expose, what its arguments are, and why the list differs between organizations.

The tool list is generated per request, not maintained by hand. Two tools are always present; the rest depend on what your key reaches and what those deployments publish.

ToolPresent when
list_agentsAlways
describe_deploymentAlways
<channel>_send_messageOne per JSON message door in the manifest of a deployment a reachable agent names
record_sent_replySuch a deployment advertises the record capability and runs kit 0.31.0 or later
Telemetry toolsThe key carries permissions.telemetry
Outcome toolsThe key carries permissions.manageAgents
get_agent_modelsThe key carries permissions.manageAgents

Start with list_agents. Every other tool takes a core argument that must be one of the slugs it returns, and that argument may only be omitted when the key reaches exactly one agent. describe_deployment can take a deployment's name instead.

An agent is reached through its deployment: the one its deployment names, at the address an operator approved for it, under the agent's routing key (/a/{routingKey}/…). Requests are signed as the deployment's name, which is what the deployment verifies. An agent that names no deployment is listed, but it has no conversation tools until it is linked. The MCP server never falls back to your only deployment, as some agents API routes do.

list_agents

List the Subcore agents this API key can reach, each with its tenant path and the deployment that serves it.

No arguments. Returns each agent's slug, name, description and tenantPath, and its deployment:

{
  "slug": "clinic-one",
  "name": "Clinic One",
  "description": null,
  "tenantPath": "/a/k3x9q2m",
  "deployment": { "name": "clinics-production", "url": "https://clinics.example.com", "connectionStatus": "success" }
}

deployment is null for an agent that names none. Any agent that cannot be reached also carries a problem string that says why and what fixes it:

  • it names no deployment. Link it on its Configuration → Connection tab in the console, or with PUT /agents/{slug} and a deployment;
  • its deployment's address has not been approved, or is not a public HTTPS address;
  • its deployment serves no agents by routing key.

A deployment's former default agent is retired and not listed.

It also reports keyScope (whether the key spans the whole organization or is limited to named cores) and serverSigning (whether the server can sign requests to deployments at all).

describe_deployment

A deployment's self-description: the public manifest merged with the signed describe document.

ArgumentTypeDescription
deploymentstringA deployment name, as list_agents shows it. Optional when the key reaches exactly one deployment
corestringAn agent slug: describes the deployment that agent names
includePromptsbooleanInclude prompt file and compiled bundle bodies. Default false

Pass deployment or core, never both. An agent may share a deployment's name, so one string could otherwise name two different targets, and passing both is refused. An agent that names no deployment is refused with a message pointing at its Connection tab. A key limited to some agents reaches only the deployments they name.

Returns the deployment's name, approved URL and connection status, and, when asked by core, the agent's slug, name and tenant path. Then its channels and doors, its kit version, model, the tools it wires to each channel (an agent's own choices are on the Agent object) and per-door auth posture, plus prompt files by hash. It also lists which conversation tools were generated from it, and which doors were excluded and why. That is the fastest way to find out why a tool you expected is not there.

For a deployment on kit 0.21.3 or later, the signed description also lists the prompt variables it declares, under description.prompts.variables, and says which of them it requires. From kit 0.24.0 a deployment refuses an agent that is missing a required one, so this is where to read the list before you create an agent. See variables a deployment requires.

From kit 0.28.2, the entry for a variable the deployment's code reads, such as an agent's voice, carries setting: true. See settings a deployment reads.

From kit 0.32.0, the description says what an agent may choose: capabilities.agentChoices, deployment.modelOptions (the models an agent may switch to and the channels a model choice applies to) and locked: true on each tool an agent always has. From kit 0.33.0, capabilities.toolsOptIn says an agent may turn on any tool the deployment has that is not locked, for any channel. A locked tool stays on the channels the deployment puts it on. See models and tools per agent.

From kit 0.36.0, a deployment that lets each agent choose relay or realtime voice lists its realtime speech models under deployment.realtimeOptions, each with its provider, its voices and its default voice, and one marked default. An agent chooses one with its voice entry's model and voice. See relay or realtime on voice.

From kit 0.31.1, each tool under description.tools carries parameters: the JSON Schema of the arguments the model passes it. A field with a default is not in required. A tool on an older kit has no parameters, and neither does one whose schema the deployment could not convert, nor any tool when the description would pass 256 KB even without its prompt contents. None of these means the tool takes no arguments.

From kit 0.35.0, the description says which outcomes the deployment has. A tool under description.tools whose success names one carries outcome, such as "booked", and deployment.outcomes lists them all in order, shallowest first. escalate_to_human is listed on the text channels, locked, with outcome: "escalated", unless the project sets lifecycle.textHandoffToolId, which leaves it without one. An agent's default outcomes are worked out from these. See what describe announces.

From kit 0.38.0, deployment.sessionIdle lists the channels the deployment ends on silence and the minutes it falls back to on a channel where the agent stores neither that channel's minutes nor a default. See when a text conversation ends.

Prompt bodies are omitted unless you ask for them, because they are large. The whole result is capped at 200 KB; over that, prompts are dropped first, and the variable list goes with them, then the signed description, then the text is truncated.

<channel>_send_message

These are the conversation tools, and they vary per agent. There is no fixed list.

Every deployment publishes a manifest at /.well-known/subcore-agent.json describing each channel and the doors into it. The server reads that manifest and emits one tool per JSON message door, named after the channel. A typical kit deployment serving voice, SMS, chat and email produces three:

ToolFrom
chat_send_messagePOST /chat/message
sms_send_messagePOST /sms/message
email_send_messagePOST /email/message

The voice channel produces none, because its only door is the form-encoded Twilio webhook. Add a channel to a deployment and its tool appears here with no change to the MCP server.

When a channel publishes a second JSON door, the extra one keeps its door id as a suffix — chat_send_message_<doorId>. The primary door is the one with id api.

Arguments

The schema comes from the door's own payload documentation, so the fields differ by channel. Every one of these tools also has:

ArgumentTypeDescription
sessionIdstringOmit to start a new conversation. Pass an existing id only to continue that one. See session semantics
corestringAgent slug. Optional when the key reaches exactly one agent

For a typical kit deployment, the channel-specific fields are:

ToolFields
chat_send_messagemessage (required), customerPhone
sms_send_messagebody (required), from
email_send_messagebody (required), from, subject, customerPhone

The customer's text is capped at 4000 characters through the playground door, whatever the channel's own limit is. Any field beyond the ones the playground itself understands is passed to the deployment as prompt context, capped at 4000 bytes serialized.

Two conveniences for the CRM lookup, which keys on a phone number rather than an address: a from that looks like a phone number is used as customerPhone when none was given, and a from that is an email address is also published to the deployment as fromAddress.

What a turn actually does

The turn runs through the playground door of the agent's deployment, under the agent's routing key (/a/{routingKey}/api/playground/message), not the customer traffic door. A deployment on a kit before 0.31.0 refuses that path, because the key is 7 characters. It uses that agent's real instructions and tools for the channel. For chat those are the same pipeline. For voice and SMS the playground is a simulation, and the result says so.

Nothing is delivered to a customer. The conversation is stored in Subcore telemetry as a test session — run_type: "test" — so it never mixes into your production numbers.

The response carries the reply text plus the sessionId used, the channel, the agent, whether the session was newly created, the tool calls the agent made, and its lifecycle events.

Doors that never become tools

ExcludedWhy
POST /outboundPlaces a real phone call, on your bill
POST /sms/sendSends a real text, on your bill
Form-encoded doorsComposed by the vendor's webhook, not by a caller. A tool that wrote one would be a tool for forging a webhook
Non-POST doorsNot a message door

The two cost-incurring paths are refused by path, before any other rule. There is no configuration that turns them on.

record_sent_reply

Record the reply that was actually sent to the customer into an existing conversation, without running a turn.

ArgumentTypeDescription
sessionIdstringRequired. Must come from a previous <channel>_send_message response
textstringRequired. The exact text that was sent. At most 20,000 characters
corestringAgent slug. Optional when the key reaches exactly one agent

Use it after you have sent a possibly-edited agent reply through your own channel, so the agent's memory of the conversation matches what the customer actually received and telemetry carries a reply.recorded event. This is the last step of the agent-as-a-brain walkthrough.

It never creates a conversation. A sessionId that does not already exist on the deployment is an error, not an invitation to invent a session.

It records under the agent's routing key (/a/{routingKey}/api/playground/record), where the conversation belongs. The kit answers there from 0.31.0; older kits record only on the bare door, which does not know a conversation an agent's routing key opened. So the tool is only listed when a reachable deployment advertises the record capability on kit 0.31.0 or later. On an older deployment it is absent, and calling it by name is refused with a message naming the version.

Telemetry

Four tools over the same query layer the read API serves, so the semantics are identical. They appear only when the key carries permissions.telemetry.

list_sessions

Recent conversation sessions (sids) for this organization, most recently active first, with pagination.

ArgumentTypeDescription
corestringAgent slug. Omit to span every reachable agent
channelstringvoice, sms, chat, email, …
runTypestringproduction is customer traffic, test is playground and MCP turns, eval is evaluation runs
fromstringISO 8601. Only sessions still active at or after this instant
tostringISO 8601. Only sessions that had started by this instant
cursorstringnextCursor from a previous page
limitinteger1–100, default 25

get_session

Every event of one session in the order it happened — the transcript plus tool calls.

ArgumentTypeDescription
sidstringRequired. The session id
cursorstringnextCursor from a previous page
limitinteger1–100, default 25

search_events

Search telemetry events by type, tool, agent and time window, newest first.

ArgumentTypeDescription
corestringAgent slug. Omit to span every reachable agent
eventTypestringe.g. tool.called, session.ended
toolIdstring
agentIdstring
fromstringISO 8601 lower bound on the event time, inclusive
tostringISO 8601 upper bound on the event time, inclusive
cursorstringnextCursor from a previous page
limitinteger1–100, default 25

get_eval_run

Every event of one evaluation run (run_type "eval"), by run id.

ArgumentTypeDescription
runIdstringRequired
cursorstringnextCursor from a previous page
limitinteger1–100, default 25

All four return { items, nextCursor } and honour permissions.cores exactly as the read API does. get_session and get_eval_run have no core argument, so a key restricted to named cores is told the object does not exist rather than that it may not see it.

Outcomes

Three tools over the same handlers as the agents API's outcome routes, so the defaults, the whole-list replace and the refusals are the same through either. They read and change an agent's outcomes: the ordered rules that give each of its sessions one outcome in the console's Sessions list. They appear only when the key carries permissions.manageAgents, the permission those routes need, and a key without it calling one by name is refused.

Each answers as the route does, { outcomes: { fallback, rules }, source }, where source is "stored" for a list someone saved and "defaults" for the list worked out from the agent's deployment until then. A refusal comes back as an error result that starts with its code, such as INVALID_OUTCOMES:.

get_agent_outcomes

An agent's outcomes: the ordered rules that give each of its sessions one outcome in the console's Sessions list.

ArgumentTypeDescription
corestringAgent slug. Optional when the key reaches exactly one agent

Returns the list in force: the saved one, or the defaults.

set_agent_outcomes

Replace an agent's whole outcome list: pass every rule, in order, since any rule left out is removed.

ArgumentTypeDescription
fallbackstringRequired. What names a session no rule matches, such as "Answered"
rulesarrayRequired. At most 30 { name, when } rules, in order. The last one whose condition holds names a session, so the deeper outcomes go later. when is exactly one of { "tool": "<tool id>" } or { "event": "transferred" | "voicemail" | "hung_up" | "agent_ended" }
corestringAgent slug. Optional when the key reaches exactly one agent

Each name is 1 to 40 characters once trimmed, unique ignoring case, and stored trimmed. The change relabels the agent's past sessions too, since the console decides outcomes when it reads them. Call get_agent_outcomes first and send the list back with your change. See replace the outcomes for every refusal.

reset_agent_outcomes

Put an agent back on the default outcomes, removing the list someone saved.

ArgumentTypeDescription
corestringAgent slug. Optional when the key reaches exactly one agent

Answers with the defaults now in force, and the same for an agent already on them.

All three honour permissions.cores, as every tool with a core argument does.

get_agent_models

The models an agent may choose, per channel, from what its deployment offers.

ArgumentTypeDescription
corestringAgent slug. Optional when the key reaches exactly one agent
deploymentstringA deployment name, as list_agents shows it. Lists what that deployment offers instead of the agent's own, for an agent you are about to move. Optional

It calls the same handler as GET /agents/{slug}/deployment/models, so it answers { deployment, readAt, channels } from the copy of the deployment's description the console keeps. That copy is what a new model or voice for the agent is checked against. Without deployment, it lists the agent's own deployment, and refuses an agent that names none. Unlike describe_deployment, it never falls back to the only deployment the key reaches.

It appears only when the key carries permissions.manageAgents, as the outcome tools do. It honours permissions.cores, and deployment must be one the key reaches.

On this page