Skip to Content
Platform
BuildConnect an MCP server

Connect an MCP server

The recipe declares what MCP access it may have, each agent narrows it, and a binding makes it reachable.

You need: a recipe (Create a recipe) and the server’s URL and token. For the cloud steps, a deployed runtime (Deploy your agent).

A session gets a tool only when all three gates agree:

GateWhereWhat it does
Package grantpi.mcp in package.jsonServers the recipe may use, and the most tools from each.
Agent selectionmcp: in agent YAMLThe subset this agent uses. It cannot add anything.
Binding.pi/mcp.local.json locally, a binding in the cloudEndpoint and credentials for a declared server id. It never adds tools.

The examples use a contacts server.

1. Grant the server

package.json
{ "pi": { "mcp": { "servers": [ { "id": "contacts", "required": true, "tools": { "include": ["search_contacts", "get_contact"] } } ] } } }
FieldMeaning
idThe name agents and bindings use.
requiredDefault false. With mode: tools, a task stops at startup when a required server is down; with mode: cli, it starts and that server’s mcp commands fail. A required server with no binding adds no tools in the cloud, and stops the session locally. An optional server that is missing or down adds no tools.
discovery"require" (default) or "defer", for a required server. With mode: tools, a build fails when a required server is down. "defer" lets the build finish without this server, for one that is reachable only where tasks run; a task still needs it.
tools.includeExact names, or ["*"] for all. Not a glob: search_* is invalid. No include means no tools.
tools.excludeExact names to remove. Always wins.

2. Select tools per agent

agents/agent.yaml
tools: [bash] mcp: mode: cli servers: contacts: include: [search_contacts]
ModeThe agent getsUse it when
cli (default)One mcp shell command (search, list, call, run). Needs bash.The server has many tools.
toolsEach tool registered with Pi. defer: ["*"] hides tools until needed; eager: lists exceptions.The model should see real schemas, or the agent has no bash.

A child agent’s mcp: block replaces the parent’s whole block; restate mode and servers.

3. Bind it locally

Keep this file out of Git:

.pi/mcp.local.json
{ "servers": [ { "id": "contacts", "transport": "streamable_http", "url": "https://contacts.example.com/mcp", "headers": { "Authorization": "Bearer ${CONTACTS_MCP_TOKEN}" } } ] }

4. Bind it in the cloud

Store the token, then connect the endpoint. Single-quote the header so your shell leaves ${CONTACTS_TOKEN} alone.

introspection bindings credentials create --name CONTACTS_TOKEN --from-env CONTACTS_TOKEN -r support-agent introspection bindings mcp connect --mcp-server-id contacts \ --endpoint-url https://contacts.example.com/mcp \ --header 'Authorization=Bearer ${CONTACTS_TOKEN}' \ -r support-agent -e staging

Leave out -e to cover every environment, including development (Connect services and secrets).

Use OAuth instead of a token

Create a custom connector from the server URL; discovery finds the OAuth endpoints. Pass --scope for each scope the server’s scopes_supported metadata lists, since the connector otherwise requests none. authorize opens your browser and stores one org-owned connection every task uses.

introspection connectors create --name "Contacts" --slug contacts-oauth --provider custom \ --auth-mode oauth-stored --issuer https://contacts.example.com/mcp --scope mcp introspection bindings mcp connect --mcp-server-id contacts \ --endpoint-url https://contacts.example.com/mcp --connector contacts-oauth -r support-agent introspection connectors authorize contacts-oauth

For each end user’s own sign-in, see Per-user tools.

Verify

export CONTACTS_MCP_TOKEN=... introspection local -p "Run mcp list contacts and tell me which tools you have." introspection bindings mcp list -r support-agent -e staging introspection tasks create -r support-agent -e staging --prompt "List the contacts tools you can use."

Expect search_contacts only, locally and in staging, and a contacts row in mcp list.

Limits

Symptom or limitFix
Agent has no tools from the server--mcp-server-id differs from pi.mcp.servers[].id, the agent’s mcp: omits it, or the server has no binding for this runtime and environment. Check bindings mcp list.
Build fails on a required MCP serverA required server was down during the build. Bring it up and push again, or set "discovery": "defer" on a server that is reachable only where tasks run.
Required server stops the taskThe server was down when the task started, with mode: tools. Check the endpoint is up, then start a new task. Locally, a required server missing from .pi/mcp.local.json stops the session as well (Bind it locally).
endpoint_credential_missing, or 401 from the serverThe header names a credential not stored for this runtime and environment (first), or the shell expanded ${...} (second).
Endpoint refusedMust be HTTPS on a public DNS name, not an IP address or localhost (The sandbox).
Connector create returns 422 “client_id is required”The provider has no automatic client registration. Register an OAuth app there and put client_id and client_secret in a connectors create @connector.yaml document.

Or ask your agent: “Give my main agent read-only search access to our contacts MCP server and bind it in staging.”

Last updated on