Skip to main content
An MCP server already describes its own tools. So instead of writing a tool, you name the server, and the agent offers what the server exposes. Reach for an MCP source when a whole catalogue of tools already exists behind one server, maintained by someone else, so attaching them one by one as separate tool files would just be repetition. For one tool of your own, write a webhook or a Python handler instead.
mcp: tools compile on the SLNG target only for now. Livekit and pipecat refuse them. The sections below that name LiveKit or Pipecat describe the mcp: block for when those targets compile it.
On this page: The file that does it holds one block and nothing else:
tools/web_search.yaml
Then the file name goes in an agent’s list, the same way any tool file does:
agent.yaml
This is a worked example rather than a quote, with auth: added to show the field. The real one ships in examples/hotel-concierge: its tools/web_search.yaml names the same server and the same two tools, and carries no url_env, transport or auth:. The platform reads the server by name from its own registration, so those fields matter only for a code target. That package deploys to slng only, agent name hotel-concierge, alongside four other tools, so there was nothing for auth: to reach.

The block

string
The server’s registered platform name. Omit to use this tool file’s name.
string
An UPPER_SNAKE environment variable name holding the server URL. Required on LiveKit and Pipecat. SLNG uses its registered server and does not read this field.
string
Accepts sse or streamable_http. If omitted, use the runtime’s URL-based transport selection; a URL ending in /mcp selects streamable HTTP.
object
type, token_env, and optional header, as described below. Omit for no authored authentication. SLNG uses its registered credentials and does not read this block.
list of strings
Unique, non-empty server tool names. Required on SLNG. If omitted on a code target, expose every tool the server offers.
A package that only targets slng needs none of url_env, transport or auth. SLNG already has the server registered, with its own connection settings and credential; those three fields exist for LiveKit and Pipecat, which dial the server themselves. Selecting a code target still requires them, with their existing rules and refusals. server exists because the two names live in different namespaces. A tool file name is lowercase snake_case, and a server’s name on the platform is whatever somebody typed in a dashboard: real ones carry dashes and spaces. Without server, a server called firecrawl-mcp-2 could not be named at all. Read the names your organisation has with voiceai mcp list. transport is optional because both platforms already have a rule for guessing it: a URL whose path ends in /mcp is streamable HTTP, anything else is SSE. Write it when you want the choice visible instead of inferred. auth is the same shape webhook tools use, so there is nothing new to learn and no new code in the generated project. Its own three keys:
string
required
Accepts bearer or api_key. Required when auth is present; no scheme is inferred.
string
required
An UPPER_SNAKE environment variable name holding the token, never the token itself. There is no default.
string
An HTTP header name, legal only with type: api_key. Omitted means X-API-Key. Bearer authentication uses Authorization: Bearer.

The file is the block, and nothing else

Six fields that describe one tool to the model are illegal on an mcp: file. Each is refused on its own, with the file, the line, and the reason:
The other four say the same thing about output, with, interruption, and effect. In one place, so you can see the whole rule at once: This is the whole point of the shape. One tool file describes one tool the model can call. An mcp: file is not that: it is a source of tools, and how many there are, what they take, and what they return is only known once the server is running.

Choosing which tools to offer

A selection filter, not a contract. Firecrawl also exposes scraping and crawling tools; naming one keeps the rest out of a conversation that has to stay fast. An empty list entry or a name repeated twice is still an authoring error and is refused before the server is contacted. On SLNG, unmute deploy checks every selected name against the registered server’s discovery snapshot. A missing tool blocks deployment. A real deploy can refresh an unusable snapshot once; a dry run never refreshes it. url_env, transport, and auth reach no further than that same offline compile on SLNG. The platform already has the server registered, under server, with whatever credential it needs. Nothing in this block reaches it at run time except the tool list. Those three fields matter for the code targets only.

Where the source can be listed

On SLNG, the source can go on an agent or on a task. Livekit and pipecat do not compile it yet.

What each target needs

An mcp: tool is SLNG-only for now. Livekit and pipecat do not compile mcp: tools yet, and core_spec.go refuses them. SLNG resolves the server by name from its own registration, so it needs none of url_env, transport or auth.

Advanced

What the compile does with it

On SLNG, the compile writes one reference per name in tools into agent.json. Nothing else about the server is written, and unmute deploy checks each name against the registered server’s discovery snapshot.

Troubleshooting

The slng target asks for an explicit tool list

SLNG has no “whole server” attachment, so tools: is required there. SLNG writes one reference per tool up front, and an offline compile cannot ask the server what those are:
Fix: name the tools you want under mcp.tools. Read what the server offers with voiceai mcp list.

Where to go next

Prebuilt tools

The last way a tool can run: the ones the runtime already has.

Secrets

Where url_env and token_env values come from.