Third-party MCP servers and secrets
Naming a server you do not host, and the credential it authenticates with.
An agent may call an MCP server Plane does not host and cannot sign for — Linear, Notion, whatever your team already runs. Declare it inline in the definition.
definePlane({
agent: { /* … */ },
mcpServers: [
{
name: "linear",
url: "https://mcp.linear.app/mcp",
auth: { type: "bearer", secret: "linear-token" },
allow: ["list_issues", "create_issue"]
}
]
})Equivalently, in a raw definition:
{ "type": "mcp", "server": { "name": "linear", "url": "https://mcp.linear.app/mcp",
"auth": { "type": "bearer", "secret": "linear-token" } },
"allow": ["list_issues"] }auth.secret is a name, never a value
The agent definition is versioned, immutable and committed to your repository. A credential has no business being in one. Register the value once, out of band:
await client.secrets.upsert({
secret: {
name: "linear-token",
value: process.env.LINEAR_TOKEN!,
description: "Linear MCP, workspace-level token"
}
})
await client.secrets.list() // names, descriptions and two timestamps — never a value
await client.secrets.delete({ name: "linear-token" }) // refused while an agent names itThe API has no endpoint that returns a secret's value, so the control plane's Secrets
page is a table of names. Rotating one is another upsert.
plane sync verifies that every named secret exists and refuses to write a version
when one does not, naming the missing ones. It never creates them, and a secret value
never passes through a definition or through the SDK.
A name that does not resolve at connect time is a ConnectionError, which the turn
records as a session.error and continues without those tools — rather than failing
the turn.
An inline server is never signed
Signing needs a stable sub and an audience Plane controls, and somebody else's
endpoint has neither. So an inline server gets its stored bearer token and nothing
else; your own tools keep going through the signed connection sync registers.
Per-user credentials
auth: { type: "bearer" } is one credential for the whole environment. When the
server should act as the person the session is for instead, register it as a
connection with auth: { type: "oauth" } — see
per-user OAuth.