Agent Patterns

How to run Claude Code, Cursor, and Codex against PromptShip — prompt shape, file conventions, and permissions that make agents reliable.

This is the counterpart to the MCP reference. It's not "what the tools do" — that's already documented. It's "what a prompt has to look like, what CLAUDE.md has to say, and which permissions have to be scoped for an agent to ship to production without supervision."

Three patterns, in the order you'll need them:

Preview environments per deploy

Every app on PromptShip can have multiple environments. Treat one of them (prod) as the user-facing target and the rest (preview, staging, branch-scoped names) as the sandbox an agent ships to first. The agent deploys the branch to preview, hits the preview URL to verify the feature works, and only then promotes to prod. If anything goes wrong, the blast radius is the preview env — users see nothing.

The prompt shape

Tell the agent to use the preview branch as a staging step, not the final target. A prompt that reliably produces this shape:

Your goal is to ship <feature> to production.

Workflow:
1. Create a branch named feature/<slug>.
2. Implement the change. Run the tests locally (pytest, vitest, etc.).
3. Commit and push.
4. Call deploy_app with environment="preview" branch="feature/<slug>".
5. Poll get_deploy_status until status is "succeeded".
6. Hit the preview URL returned by get_app. Verify <concrete acceptance criteria>.
7. Only if verification passes, call deploy_app with environment="prod"
   branch="feature/<slug>".
8. Open a PR to main.

If any step fails, read get_logs, patch the code, and retry from step 3.
Do not skip step 6. Do not call deploy_app with environment="prod" before preview
has succeeded.

Why this shape

Each instruction corresponds to a guardrail the platform enforces. The deploy_appget_deploy_status loop is the only way the agent learns whether the build passed. The get_logs call is the only way it recovers from a failed deploy without a human. Skipping either step means the agent ships to prod blind.

What this implies for prompts. Always name the environment explicitly in the prompt. "Deploy my app" without an env name is ambiguous — an agent may default to prod. "Deploy to preview first, verify, then promote" is the phrase that keeps the guardrail.

Structuring CLAUDE.md for PromptShip

Claude Code reads CLAUDE.md at repo root as project context on every session. Treat it as the place where you encode the team's deploy conventions, not just coding style. A good PromptShip-aware CLAUDE.md has three sections: what the deploy surface is, which MCP tools are safe by default, and which require human approval.

Starter template

Drop this at the top of your repo's CLAUDE.md and edit the app names and environment list to match:

# Deployment

This project is deployed on PromptShip.

- App name: <my-app>
- Environments: prod (live), preview (sandbox for branch verification)
- Deploy tool: MCP via `promptship` server

## Safe-by-default MCP tools

Use these freely without asking:
- get_app, list_apps, get_app_config, get_deploy_status, get_logs
- get_db_attach_status (polling an in-flight attach)
- list_secrets (key names only; values are never returned)
- deploy_app with environment="preview"
- attach_postgres / attach_valkey / attach_clickhouse with tier="pg-1" / "valkey-1" / "ch-1" in non-prod environments only (async — follow each with get_db_attach_status until status is "succeeded")
- query_postgres / query_clickhouse (read-only)

## Tools that require explicit human approval

Ask before calling any of these:
- deploy_app with environment="prod"
- delete_app, delete_environment
- detach_postgres / detach_valkey / detach_clickhouse (data retention is 1 hour, not forever)
- reset_postgres / reset_valkey / reset_clickhouse (wipes data)
- change_app_tier, change_*_tier (affects billing at end of early access)
- set_secret / delete_secret (credential changes)
- change_team_plan, invite_member, remove_member, update_member_role
- set_domain, remove_domain

## Workflow conventions

- Branches: feature/<ticket-id>-<slug> (e.g. feature/ENG-421-oauth-refresh or feature/issue-142-oauth-refresh)
- Every feature ships to preview before prod. Verify the preview URL before promoting.
- If a deploy fails, read get_logs before patching — don't guess.
- Leave a comment on the ticket when you promote to prod.

What this implies for prompts. With CLAUDE.md in place, your per-task prompts get dramatically shorter — "ship <feature>, follow the workflow in CLAUDE.md" is enough. Without it, every prompt has to re-state the safety conventions, which agents forget under pressure.

Safe permissions for autonomous runs

When an agent runs unattended, the guardrails that matter aren't in the prompt — they're in the token. PromptShip has two levels: team roles (owner / admin / member) and app permissions (admin / member per app). Combine them so a stuck agent can't do lasting damage.

Create a scoped agent token

Use create_token to mint a token specifically for the agent, not your personal token. A per-agent token is auditable and revocable independently:

> promptship tokens create --name "claude-code-nightly"
Token: ps_live_<redacted>
Store this now — it won't be shown again.

Then narrow what that token can touch. Agent tokens should be issued to a service account user who is a member (not admin) of the team, with admin access only on the specific apps the agent is responsible for. That means:

Explicit lockdown list

For unattended runs (cron-triggered, long-running background agents), disable these tools at the MCP layer entirely. In Claude Code, this is a settings.json disabledTools entry. In Cursor/Codex, use the equivalent MCP config:

Supervised sessions (you're watching the IDE) can keep these enabled with "ask before calling" CLAUDE.md rules. Unattended sessions should remove them from the tool surface entirely — Claude can't call a tool it can't see.

What this implies for prompts. Don't rely on prompts to enforce safety. Prompts are advisory; token scope and disabled-tool lists are the actual guardrail. If a task genuinely needs a dangerous tool (e.g., change_app_tier for a scheduled scale-up), mint a separate one-task token with exactly that permission and revoke it after.