MCP database tool error contracts: tell AI agents when retrying is safe
“Database error” is not enough information for an AI agent.
The failure could mean invalid input, missing authorization, a temporary pool timeout, a cancelled query, a broken schema contract, or a write whose outcome is unknown. If every error looks alike, the model may retry a permanent failure, rewrite a request that should be refused, or repeat a mutation that already succeeded.
An MCP database tool needs a stable error contract that guides recovery without exposing SQL, credentials, internal topology, or sensitive data.
Separate machine policy from human explanation
Return a compact machine-readable envelope plus a safe message:
{
"code": "DB_TIMEOUT",
"category": "transient",
"retryable": true,
"retry_after_ms": 800,
"outcome": "not_committed",
"trace_id": "...",
"message": "The query timed out before completion."
}
The code drives policy. The message helps the user. Neither should contain raw SQL, connection strings, stack traces, or database object names the caller is not authorized to know.
Use a small, stable taxonomy
- validation: inputs are malformed or outside the tool contract;
- clarification: required business scope is ambiguous;
- authorization: identity or policy does not permit the operation;
- not found: an authorized lookup has no matching resource;
- conflict: version or state no longer matches the request;
- transient: bounded retry may succeed;
- permanent: retrying the same operation will not help;
- unknown outcome: execution may have occurred, so blind retry is unsafe.
Keep codes stable across implementations. A database driver exception is evidence used to select a public code, not the public contract itself.
Make retryability explicit
Retry policy should combine error category, operation type, attempt count, deadline, idempotency, and outcome certainty. A read-only query that failed before execution can often be retried with bounded exponential backoff and jitter. A mutation whose connection dropped after commit requires reconciliation using an idempotency key or execution receipt.
Do not let the model infer retry safety from prose. Return retryable, a maximum or server-controlled delay where useful, and an outcome such as not_started, not_committed, committed, or unknown.
For mutation recovery, see idempotent MCP tools for database writes.
Do not turn authorization failures into hints
An authorization error should explain what the user can do next without revealing whether another tenant’s resource exists or which hidden role would unlock it. Return a policy reference or support path, not an inventory of protected objects.
Keep authentication, tenant, role, environment, and policy decisions outside model-controlled arguments. Related: scoped database access for AI agents.
Preserve correlation without leaking internals
A trace_id lets an operator find server-side diagnostics while the caller receives a sanitized response. Internal logs can include driver code, safe query fingerprint, connection state, cancellation result, policy version, and timing under access control.
Do not echo raw exception text. Driver messages can contain SQL fragments, table and column names, values, hostnames, or credentials.
Concrete example: report query versus status update
A report query hits a pool acquisition timeout before it reaches PostgreSQL. The tool returns POOL_BUSY, retryable=true, outcome=not_started, and a short randomized delay. The client retries twice inside the user deadline.
A status-update tool sends a mutation and loses the network response. The database may have committed it. The tool returns OUTCOME_UNKNOWN, retryable=false, and an operation key. The client checks the execution receipt before deciding whether another write is needed.
Both are “database errors” at the transport layer. They require opposite recovery behavior.
Test the contract under failure
- Send invalid types, missing fields, unsupported enums, and oversized ranges.
- Remove authorization without changing the prompt.
- Exhaust the pool and trigger statement and client timeouts.
- Cancel before execution, during execution, and during result delivery.
- Break a view or change a column under a pinned tool version.
- Drop the connection before and after a mutation commits.
- Verify that public errors contain no SQL or sensitive values.
- Confirm every trace ID resolves to complete restricted diagnostics.
Observe operations and attempts separately
One user operation may produce several attempts. Record both identifiers, plus error code, category, outcome, retry delay, tool version, policy version, database boundary, and final status. This prevents a retry storm from looking like user demand and makes duplicate execution visible.
Use MCP database observability for the wider metric and trace model.
Where Conexor fits
Conexor provides MCP infrastructure for connecting AI clients to databases and APIs through governed tools. Typed failures, bounded retries, idempotency, and auditable correlation turn database errors into controlled workflow decisions instead of model guesses.