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.
| Tool | Present when |
|---|---|
list_agents | Always |
describe_deployment | Always |
<channel>_send_message | One per JSON message door in a reachable deployment's manifest |
record_sent_reply | A reachable deployment advertises the record capability (kit 0.17.6 or later) |
| Telemetry tools | The key carries permissions.telemetry |
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.
list_agents
List the Subcore agents (cores) this API key can reach, with each one's deployment URL and connection status.
No arguments. Returns each agent's slug, name, description,
deploymentUrl and connectionStatus, plus a problem string on any agent that
cannot be reached — for example one whose deployment URL has not been connected
yet, or is not a public HTTPS address.
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
An agent deployment's self-description: the public manifest merged with the signed describe document.
| Argument | Type | Description |
|---|---|---|
core | string | Agent slug. Optional when the key reaches exactly one agent |
includePrompts | boolean | Include prompt file and compiled bundle bodies. Default false |
Returns the deployment's channels and doors, its kit version, model, tools per channel and per-door auth posture, plus prompt files by hash. It also lists which conversation tools were generated for that agent, and which doors were excluded and why — the fastest way to find out why a tool you expected is not there.
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, 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:
| Tool | From |
|---|---|
chat_send_message | POST /chat/message |
sms_send_message | POST /sms/message |
email_send_message | POST /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:
| Argument | Type | Description |
|---|---|---|
sessionId | string | Omit to start a new conversation. Pass an existing id only to continue that one. See session semantics |
core | string | Agent slug. Optional when the key reaches exactly one agent |
For a typical kit deployment, the channel-specific fields are:
| Tool | Fields |
|---|---|
chat_send_message | message (required), customerPhone |
sms_send_message | body (required), from |
email_send_message | body (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 deployment's playground door, not the customer traffic door, with that channel's real instructions and tools. 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
| Excluded | Why |
|---|---|
POST /outbound | Places a real phone call, on your bill |
POST /sms/send | Sends a real text, on your bill |
| Form-encoded doors | Composed by the vendor's webhook, not by a caller. A tool that wrote one would be a tool for forging a webhook |
Non-POST doors | Not 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.
| Argument | Type | Description |
|---|---|---|
sessionId | string | Required. Must come from a previous <channel>_send_message response |
text | string | Required. The exact text that was sent. At most 20,000 characters |
core | string | Agent 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.
The tool is only listed when a reachable deployment advertises the record capability, which the kit began doing in 0.17.6. On an older deployment it is absent, and calling it by name is refused with a message saying so.
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.
| Argument | Type | Description |
|---|---|---|
core | string | Agent slug. Omit to span every reachable agent |
channel | string | voice, sms, chat, email, … |
runType | string | production is customer traffic, test is playground and MCP turns, eval is evaluation runs |
from | string | ISO 8601. Only sessions still active at or after this instant |
to | string | ISO 8601. Only sessions that had started by this instant |
cursor | string | nextCursor from a previous page |
limit | integer | 1–100, default 25 |
get_session
Every event of one session in the order it happened — the transcript plus tool calls.
| Argument | Type | Description |
|---|---|---|
sid | string | Required. The session id |
cursor | string | nextCursor from a previous page |
limit | integer | 1–100, default 25 |
search_events
Search telemetry events by type, tool, agent and time window, newest first.
| Argument | Type | Description |
|---|---|---|
core | string | Agent slug. Omit to span every reachable agent |
eventType | string | e.g. tool.called, session.ended |
toolId | string | |
agentId | string | |
from | string | ISO 8601 lower bound on the event time, inclusive |
to | string | ISO 8601 upper bound on the event time, inclusive |
cursor | string | nextCursor from a previous page |
limit | integer | 1–100, default 25 |
get_eval_run
Every event of one evaluation run (
run_type"eval"), by run id.
| Argument | Type | Description |
|---|---|---|
runId | string | Required |
cursor | string | nextCursor from a previous page |
limit | integer | 1–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.