Agent Workflows

Use these workflows when an agent needs to validate Postgres behavior without assuming a framework.

ttlMinutes values in these examples are positive minutes. Omit the field to use the profile default; 0 and negative values are rejected with invalid_ttl.

Direct SQL Schema Change

  1. Create a sandbox.
{
  "tool": "create_database",
  "arguments": {
    "nameHint": "billing status check",
    "ttlMinutes": 45,
    "owner": "agent-session"
  }
}
  1. Apply SQL directly.
{
  "tool": "run_sql",
  "arguments": {
    "databaseId": "<databaseId>",
    "sql": "CREATE TABLE accounts(id serial PRIMARY KEY, email text UNIQUE);"
  }
}
  1. Inspect the schema.
{
  "tool": "describe_schema",
  "arguments": {
    "databaseId": "<databaseId>"
  }
}
  1. Delete the sandbox when finished.
{
  "tool": "delete_database",
  "arguments": {
    "databaseId": "<databaseId>"
  }
}

Before/After Diff

Use the full schema_digest response as baseDigest; a checksum string alone cannot compute a diff.

{
  "tool": "schema_digest",
  "arguments": {
    "databaseId": "<databaseId>"
  }
}
{
  "tool": "schema_diff",
  "arguments": {
    "databaseId": "<databaseId>",
    "baseDigest": {
      "databaseId": "<databaseId>",
      "databaseName": "<databaseName>",
      "digestVersion": 3,
      "checksum": "<checksum>",
      "relationCounts": {
        "tables": 0,
        "partitionedTables": 0,
        "views": 0,
        "materializedViews": 0,
        "foreignTables": 0,
        "other": 0
      },
      "columnCount": 0,
      "constraintCount": 0,
      "indexCount": 0,
      "extensionCount": 0,
      "tables": [],
      "extensions": []
    }
  }
}

For a compact stored baseline, prefer snapshots:

{
  "tool": "create_schema_snapshot",
  "arguments": {
    "databaseId": "<databaseId>",
    "snapshotName": "before_change",
    "notes": "baseline before repo command"
  }
}
{
  "tool": "diff_schema_snapshot",
  "arguments": {
    "databaseId": "<databaseId>",
    "snapshotName": "before_change"
  }
}

Row-Limited Reads

run_sql returns explicit row metadata:

{
  "tool": "run_sql",
  "arguments": {
    "databaseId": "<databaseId>",
    "readonly": true,
    "rowLimit": 2,
    "sql": "SELECT * FROM accounts ORDER BY id"
  }
}

Use returnedRowCount, affectedRowCount, totalRowCountKnown, and truncated for result-size and mutation checks. For multi-statement SQL, read the ordered resultSets array when you need each statement’s result; top-level rows mirrors the last row-returning statement. resultSets uses 1-based statementIndex values, applies rowLimit independently to each row-returning result set, and serializes each row-returning statement with the same typed rules as a single-statement query.

Repo Command Validation

Use run_repo_command when the repo already has a script. Use validate_schema_change when the agent needs the schema diff.

{
  "tool": "validate_schema_change",
  "arguments": {
    "repoPath": "/absolute/path/to/repo",
    "nameHint": "validate repo schema change",
    "ttlMinutes": 45,
    "owner": "agent-session",
    "command": ["npm", "run", "migrate"]
  }
}

Commands run with repoPath as current directory and receive DATABASE_URL, PGSANDBOX_DATABASE_URL, and libpq PG* variables for the sandbox. PGSandbox does not add an implicit shell, and it rejects shell wrappers and indirect launchers such as ["bash", "-lc", "..."], ["sh", "-c", "..."], env, and sudo. Pass direct argv such as ["npm", "run", "migrate"], ["psql", "-v", "ON_ERROR_STOP=1", "-f", "migrations/schema.sql"], or ["psql", "-Atc", "SELECT current_database(), current_user"]. For multi-step workflows, prefer a repo/package script that can be invoked directly, or split the work into separate tool calls.

Templates And Clone

Create a reusable local template from a sandbox:

{
  "tool": "create_template_from_sandbox",
  "arguments": {
    "databaseId": "<databaseId>",
    "templateName": "seeded_accounts",
    "createdBy": "agent-session"
  }
}

Restore it into a new sandbox:

{
  "tool": "create_sandbox_from_template",
  "arguments": {
    "templateName": "seeded_accounts",
    "nameHint": "replay bug",
    "ttlMinutes": 45,
    "owner": "agent-session"
  }
}

For external sources, use clone_database with schemaOnly when data is not needed. Do not paste production URLs into issue trackers, Rowset rows, or logs.

Cleanup And Diagnostics

List or clean up one profile by default:

{
  "tool": "list_databases",
  "arguments": {
    "owner": "agent-session"
  }
}

Use all-version scope deliberately:

{
  "tool": "cleanup_expired",
  "arguments": {
    "includeAllVersions": true,
    "dryRun": true
  }
}

Use MCP doctor when shell access is unavailable:

{
  "tool": "doctor",
  "arguments": {}
}

Error Handling

Branch on stable error.code and error.category, not prose. Common SQL repair cases include:

Creation tools return connectionStringRedacted and connectionStringsRedacted for summaries, task trackers, and logs. Use connectionStringsRedacted.localContainer only as a safe hint that a Dockerized local app should use the container variant.

get_connection_string also returns only redacted values by default. Pass includeCredentials: true only when a command or database client needs the actual credential-bearing URL. For Dockerized app services running on the same machine as PGSandbox, pass connectionStrings.localContainer as DATABASE_URL. Docker Desktop supports host.docker.internal automatically; on Linux Docker, add extra_hosts: ["host.docker.internal:host-gateway"] to the service. Do not echo raw connection values into chat, logs, PR comments, issues, or durable datasets.