Connecting a corporate database to an AI agent starts with deciding which operations the agent is allowed to perform. MCP provides the interface between the agent and those operations. Database permissions, application authorization and operational controls still have to enforce the boundary.
Put a service boundary in front of the data
A useful starting architecture is: authenticated user → agent → MCP tool service → authorized data operation → database. Give the tool service a narrowly scoped database identity. Resolve the caller and tenant from trusted authentication context, never from a tenant identifier that the model supplies.
Begin with specific read tools such as get_order_status or list_overdue_invoices. Define their inputs, returned fields, pagination and failure behavior. A general SQL tool requires a much stronger isolation and query-control design; an instruction asking the model to behave is insufficient.
Keep credentials and permissions outside the prompt
The model should receive a tool definition and the minimum result needed to answer. It should not receive a database password. Store credentials in the runtime's secret facility, rotate them, and separate environments. For remote HTTP deployments, implement the applicable MCP authorization requirements and validate that a token is intended for the receiving service. Downstream database credentials belong to a separate boundary.
Database grants and, where applicable, row-level policies must support the application's restrictions. Parameterized queries address SQL injection; they do not decide whether the caller may read a customer's invoice. Both checks are needed.
Separate reading from changing state
A proposed write should pass schema validation, business rules and authorization before execution. For consequential operations, require an approval bound to the exact operation and arguments. Use idempotency keys for retryable writes and make approval expiry explicit. A second model call is not a substitute for permission enforcement.
Example contract, not a deployed benchmark:
get_order_status(order_id)
identity: authenticated caller and tenant from server context
authorization: caller may access this order
result: status, updated_at, source_reference
bounds: deadline, result-size limit, rate limit
audit: trace_id, tool version, decision, outcome
Test the boundary before adding autonomy
Test a valid request and its denied counterpart: another tenant, an expired credential, a forbidden field, an oversized result and an unavailable database. Treat retrieved text as untrusted data, including text that tells the agent to invoke another tool. Keep sensitive payloads out of general-purpose logs.
When selecting an engineer, ask to see the denial tests, permission model and incident trail alongside the working integration. The linked work below documents my integrations and their limits.