Skip to content
PtahPtah

Configure agent permissions

Every Ptah tool asks the capability policy before doing work. Authority comes from the operator who starts the process; repository content can narrow it but cannot grant more.

These configurations widen access one step at a time:

Terminal window
# Reading tools, with no configured source.
ptah mcp
# Read a declared schema.
ptah mcp --schema-source-root ./schema
# Read and preview migration artifacts.
ptah mcp --workspace . --migrations-dir ./migrations --dialect postgres
# Ask before applying each migration patch.
ptah mcp --workspace . --migrations-dir ./migrations --dialect postgres \
--allow-write migrations
# Apply without a prompt when the client cannot ask.
ptah mcp --workspace . --migrations-dir ./migrations --dialect postgres \
--allow-write migrations --auto-approve

--allow-write accepts migrations, schema, and tests. Each class also requires its directory flag. A class you do not name cannot be written, regardless of the model’s request.

The operator supplies the connection and class; the model never supplies or sees the URL:

Terminal window
ptah mcp \
--database-url "postgres://reader@db.internal:5432/app?sslmode=require" \
--database-class dev \
--allow-database-inspect ask

Without an explicit override, database classes resolve as follows:

Class Default verdict
ephemeral allow
dev ask for each read
target ask for each read
production deny; no flag on this surface widens it
unclassified deny

Ptah does not infer the class from a database or hostname. Approval binds to the exact configured target, not to every database with the same class. --auto-approve applies to artifact patches and grants no database access.

Verdict Result
allow The operation proceeds.
ask The call returns an input request; a capable client presents it and retries with the answer.
deny The call refuses and names the capability or configuration that would change the result.

A client that cannot present a prompt treats ask as a refusal. It never promotes the operation to allow. An approval prompt identifies the capability, artifact, paths, before and after digests, and patch content address.

A project may contain .ptah/agent-policy:

# No agent session may change declarative tests in this repository.
artifact.write:tests deny

Repository policy can only remove authority. A rule that tries to grant more than the process flags is ignored and reported by describe_session under ignored_policy_rules.

A CI job has nobody to ask. Grant only the artifact class the job needs and add --auto-approve deliberately:

Terminal window
ptah mcp --workspace . --migrations-dir ./migrations --dialect postgres \
--allow-write migrations --auto-approve

Without --auto-approve, a non-interactive client receives this refusal:

approval_unavailable: "artifact.write:migrations": operation requires approval
and this session cannot ask. This client cannot present an approval prompt. The
operator grants the capability outright when starting the server, for example:
ptah mcp --workspace . --allow-write=migrations --auto-approve. Naming a class
without --auto-approve asks for each patch, which is what could not be done here.

Auto-approval removes the prompt. Path containment, capability policy, digest checks, and verification gates still run.

ptah assist explain can drive the same tools in a one-shot job:

Terminal window
ptah assist explain "does the newest migration match the declared schema?" \
--workspace . --migrations-dir ./migrations --dialect postgres \
--non-interactive --ephemeral --format jsonl > agent-run.jsonl

Exit 0 means the run finished, 1 means a provider or execution limit stopped it, and 2 means configuration was invalid. The answer record’s verified field is true only when at least one Ptah tool answered.

Keep the audit log as a CI artifact. The server announces its path at startup; each JSON line records the schema version, decision, policy layer, approvals, paths, digests, and gates, including refusals.