@plane/sdk/dev
The outbound channel client, behind npx plane dev.
Your app opens a socket to a hosted Plane and serves its tools, its event hooks and its tool-call gate down it. No public URL, no tunnel.
Most people should use npx plane dev, which is this plus a file
watcher, a session stream and a terminal. Call it directly when you want to own the
process:
import { plane } from "./lib/plane"
const dev = await plane.dev({
planeUrl: process.env.PLANE_URL,
name: "jordan" // namespaces the agent slug and the connection
})
await dev.ready
// …
await dev.close()The socket code is loaded on demand, so importing @plane/sdk in a deployed app
does not pull it in.
What it registers
A connection with transport: "channel" and no url and no hooksUrl. There is
nothing to expose and nothing to rotate. MCP requests, hook deliveries and the
synchronous tool.call.before gate all arrive as frames on that one socket and are
answered by the same handlers your deployed app serves over HTTP.
Reconnect is the protocol's own rule: exponential backoff with full jitter, 250 ms to
30 s, reset once a channel has survived a minute, and terminal on 4401/4403 —
a token that will never verify must not become a hot loop against the control plane.
Verification is replaced, not skipped
Over HTTP a per-request JWS proves the caller is Plane. Over a channel the socket
proved it: your process presented a Plane-signed channel token at the upgrade and
everything since arrived on that one TLS stream. ctx.claims, ctx.connection and
ctx.session are the same shape either way — a tool handler cannot tell which
transport reached it, which is what makes this a development mode rather than a
second product.
Tokens
await client.channels.issueToken({ kind: "dev", name: "jordan" })kind is dev or worker, and deliberately not sandbox: Plane hands a sandbox
its own token, and a caller who could ask for one could name somebody else's sandbox
id. The channel id is derived from (kind, name) rather than generated, because
plane dev restarts on every save and a moving id would orphan a connection row each
time.