Use pz with an AI agent
pz mcp serves the current project to any Model Context Protocol
client — Claude Code, VS Code, GitHub Copilot CLI, opencode, or a custom agent — as a
set of typed tools: introspect the project, run pz validate/pz compile/pz plan
as a fix loop, author connections.yml/pipelines/*.sql with self-verifying edits,
and — only if you opt in — actually run the project. pz never calls an LLM itself;
the agent lives entirely on the other side of the protocol. Full contract:
MCP contract reference.
Quickstart: pz mcp init
Section titled “Quickstart: pz mcp init”From your project directory, wire up one or more clients in one step:
pz mcp init claude-codepz mcp init vscode claude-code copilot-cli opencode # several at oncepz mcp init --all # all fourThis does two things per client, merge-preservingly (existing config files keep
every other key and every other server entry — only the pz entry is written or
updated):
- Writes (or updates) that client’s MCP config file with a
pzserver entry pointing atpz mcp(see Manual setup below for the exact shape each client gets). - Installs the embedded
pz-pipelinesskill — aSKILL.mdplus theauthoring-for-agents.mdguide — into the locations that client’s ecosystem looks for skills in, so an agent that supports skills gets pz-specific authoring guidance without you writing any of it by hand.
Re-running is idempotent: the same inputs produce the same files, with any prior
pz entry replaced in place.
Pass --allow-run to bake the flag into the generated server entry for every
selected client (see below for what it unlocks). --skill-locations overrides
which skill directories get installed — see Skill install locations.
Add --project <dir> to target a project other than the current directory.
No client named and no --all is a PZ0605 error listing the four client names —
explicit over implicit, the same posture as a bare pz run on a multi-flow
project.
--allow-run: off by default, on purpose
Section titled “--allow-run: off by default, on purpose”Without --allow-run, the server exposes only introspection, verification, and
authoring tools — nothing that moves real data or advances a watermark. The three
execution tools (pz_run, pz_retry, pz_run_results) are absent from the tool
listing entirely when the flag is off, not present-but-refusing: the connected
agent never even sees them, so it can’t plan around a capability it doesn’t have.
This makes the server safe to point an agent at by default: it can read your
project, validate its own edits, and iterate on connections.yml/pipeline SQL —
but it cannot move data, hit a real database, or advance state — until an operator
(a human, deliberately) starts the server with --allow-run. That’s a server-start
flag, not something a model can toggle from inside a conversation.
pz mcp --allow-run # direct invocationpz mcp init claude-code --allow-run # baked into the generated client configA gated run reports once, when it finishes — the run’s events do not stream to the client as MCP progress notifications in this version (deliberately deferred; the final result carries the full node-by-node summary either way). Watch a long run live with the CLI’s own output or the NDJSON event stream instead.
What pz mcp init writes
Section titled “What pz mcp init writes”| Client | Config file | Top-level key |
|---|---|---|
vscode |
<project>/.vscode/mcp.json |
servers.pz |
claude-code |
<project>/.mcp.json |
mcpServers.pz |
copilot-cli |
~/.copilot/mcp-config.json (the one user-global target — every other client’s file is project-local) |
mcpServers.pz |
opencode |
<project>/opencode.json |
mcp.pz |
An existing config file that fails to parse as JSON is refused outright (PZ0605)
— left byte-untouched, never overwritten with a fresh empty file.
Skill install locations
Section titled “Skill install locations”--skill-locations token |
Directory | Installed by default for |
|---|---|---|
standard |
.agents/skills/pz-pipelines/ |
always |
claudecode |
.claude/skills/pz-pipelines/ |
claude-code |
github |
.github/skills/pz-pipelines/ |
vscode, copilot-cli |
opencode |
.opencode/skill/pz-pipelines/ (singular skill, matching that ecosystem’s own convention) |
opencode |
The default install set is standard plus whatever the clients you named imply;
pass --skill-locations all, --skill-locations none, or an explicit
comma-separated list to override. An unrecognized token is PZ0605, checked
before any file is written — a typo never partially installs.
Manual setup
Section titled “Manual setup”If your client isn’t one of the four pz mcp init covers, or you’d rather wire it
up by hand, every client needs the same two things: a command to launch (pz mcp,
optionally with --allow-run) and a working directory at the project root. The
four shapes pz mcp init itself generates, for reference:
VS Code (.vscode/mcp.json, servers.pz):
{ "type": "stdio", "command": "pz", "args": ["mcp"] }Claude Code (.mcp.json, mcpServers.pz) — equivalent to
claude mcp add pz -- pz mcp:
{ "command": "pz", "args": ["mcp"] }GitHub Copilot CLI (~/.copilot/mcp-config.json, mcpServers.pz):
{ "type": "local", "command": "pz", "args": ["mcp"], "tools": ["*"] }opencode (opencode.json, mcp.pz):
{ "type": "local", "command": ["pz", "mcp"], "enabled": true }For a generic client that isn’t any of these four: command: pz, args: [mcp]
(add --allow-run to the args if the client supports execution tools), and
cwd set to the project root — pz mcp serves the project in its current working
directory by default. A client that cannot set cwd can pass the directory
explicitly instead: args: [mcp, --project, /path/to/project] (--project works on
both pz mcp and pz mcp init).
Concurrency: two humans, two terminals
Section titled “Concurrency: two humans, two terminals”The server takes no long-lived project lock. Mutations (pz_add_connection,
pz_write_pipeline, …) are atomic per call — write to a temp file, rename over
the original — but there is no cross-call transaction, and gated runs take the
same RunDirLock a CLI-invoked pz run does. Practically: two agents (or an
agent and a human) pointed at one project behave exactly like two humans with two
terminals. Last write wins on files; a run and a concurrent run — from either
side, CLI or MCP — exclude each other (PZ0604 on the side that loses the race)
rather than corrupting shared state. This is good enough for a single project with
a single active author, which is the intended v1 shape; running two agents against
the same project concurrently and expecting them to merge cleanly is not
supported.
Secrets
Section titled “Secrets”The MCP surface extends the project’s existing secret-hygiene rule to authoring: connection config values never leave the server, and a mutation tool refuses to write one in as plaintext.
pz_project_overviewreturns connection names and connector types only — never a config value.pz_plan’sreasonstrings andpz_state’s values are the same way; neither can carry a credential.pz_add_connection/pz_update_connectionrequire any option whose key name containspassword,secret,token,key, orconnection_stringto be a${VAR}environment-variable reference — the whole value, nothing else, e.g."${DB_PASSWORD}". (That key-name heuristic is the whole check in v1: a connector schema marking a propertywriteOnly/format: passwordis not consulted, so a credential-shaped option under an unusual name is your responsibility.) A literal value in that shape is refused withPZ0601before any file is written or any connector is even resolved — the secret itself never transits the tool result, only the offending key name does. See Secure connection config for how to get the variable into the process environment in the first place.- A newly exported variable does not reach a running server.
pz mcpresolves${VAR}against the environment of the process it was launched in, which it inherited from the client at launch — so exporting the variable in some other shell changes nothing until the MCP server is restarted.PZ0103’snext_stepsays so explicitly underpz mcp, because an agent that cannot see the operator’s terminal has no other way to learn it: set the variable where the client will pick it up, then restart the server.
Paths stay inside the project
Section titled “Paths stay inside the project”Under pz mcp, a localfiles path:/root:/base_dir: that resolves outside the
project directory — ../ traversal or an absolute path elsewhere — is refused with
PZ0606, whether it sits in the existing config or in a block an authoring tool
proposes to write. This matches the posture the mutation tools already take for
../ in names (PZ0602): the agent surface operates only on files inside the
project. The plain pz CLI is unchanged — your config, your files — so a project
that legitimately reads outside its own directory still runs from the terminal;
it just isn’t drivable through an agent. The containment check is lexical, a guard
for steering agents, not a symlink-proof security boundary.
Learn more
Section titled “Learn more”- MCP contract reference — the full envelope shape, tool-by-tool inputs/results, and the PZ06xx error codes.
- Once connected, a resource-aware client can pull in the embedded authoring guide
and every concepts/how-to doc directly (
pz://docs/...resources) — ask it to readpz://docs/reference/authoring-for-agents.mdfirst if it needs a primer.