MCP tool declarations
The three parts
package.jsonsets the recipe boundary. It declares the MCP server ids the package may use and the maximum tools permitted from each server.- An endpoint source supplies the connection. A package MCP manifest,
<recipe-root>/.pi/mcp.local.json, or a host/platform binding maps a declared server id to its endpoint and credentials, without granting any additional tools. agent.yamlsets the agent policy. It selects the subset of package-permitted servers and tools available to that agent.
The available tools are the intersection of the package and agent policies for servers with a matching binding.
Example: resolve one tool for an agent
Assume a contacts MCP server exposes search_contacts, get_contact, and
delete_contact. This recipe permits the first two tools, while its agent
selects only search_contacts.
Package policy
{
"name": "contacts-agent",
"version": "0.1.0",
"type": "module",
"pi": {
"agents": ["agents/*.yaml"],
"mcp": {
"servers": [
{
"id": "contacts",
"required": true,
"tools": {
"include": ["search_contacts", "get_contact"]
}
}
]
}
}
}The three layers resolve like this:
| Layer | Result |
|---|---|
| Package policy | contacts.search_contacts and contacts.get_contact are permitted. delete_contact is outside the recipe boundary. |
| Agent selection | The agent narrows the package set to contacts.search_contacts. |
| Local binding | The contacts id gains a reachable endpoint and credential reference, but no additional tools. |
| Effective session | Only contacts.search_contacts is available. |
For an ordinary local run, <recipe-root> is the directory containing the
recipe’s package.json. Run introspection local from that directory so the
binding and recipe resolve together. A host may supply the same binding shape
explicitly instead.
If a required user-owned binding is missing, explain that exact recipe-relative path and let the user create it before retrying the real recipe. Do not move a machine-specific URL or credential into a package MCP manifest merely to make a local proof pass. Validate the binding through the runner without printing or repeating credential values.
Keep .pi/mcp.local.json untracked and ignored by Git when it contains local
endpoints or credential references. .recipeignore does not prevent Git from
tracking the file and does not exclude it from source validation. Managed
Runtimes receive their endpoint and credential configuration through
bindings, not this local file.
Two modes
Every resolved agent owns its mode independently; with no mode anywhere in a
from: chain, an agent gets CLI.
mode: cligives the agent onemcpcommand containing only its authorized tools. A large server costs one tool definition instead of forty: the agent discovers narrowly (mcp search), reads one schema (mcp list --schema), then calls. Sessions expose onlysearch,list,call, andrun— authentication, admin commands, and config overrides are rejected. Keep a shell tool such asbashintools, since the command runs there.mode: toolsregisters each authorized tool with Pi directly, for when the model should see real tool schemas.defer: ["*"]hides authorized tools at session start andeager:lists exceptions, which buys the token saving of deferred loading without implementing it; neither can authorize anythinginclude/excludedid not.
A declared agent mcp block replaces the inherited one whole, so a derived
agent must restate its mode and servers — a capability change is always
visible in the diff of the agent that made it.
After replacing the example URL with your server and exporting
CONTACTS_MCP_TOKEN, run the recipe. local validates it before Pi starts:
introspection localInside that Pi session, the resolved tool is exposed via the mcp CLI:
mcp list contacts
mcp list contacts.search_contacts --schema
mcp call contacts.search_contacts query="Ada Lovelace"See MCP and federation for managed bindings, credentials, and identity on the Introspection platform.