Using Claude Code on a multi-tenant app?Don’t give it every customer’s data.

ZenStack Studio turns your Postgres, MySQL, or SQLite database into an MCP server that scopes Claude Code, Cursor, or any agent to the models, operations, and users you choose — so it works on real data without seeing all of it.

The MCP server is free on localhost.

The problem

Three ways teams connect agents to data today. None of them work.

Your authorization — which tenant a row belongs to, what a support user may change, which fields are private — lives in your application. A connection string goes straight past it. So every option is a trade between access and safety.

A production connection string

Real data, and every table, every tenant, and every column that comes with it. One vague prompt away from a DELETE without a WHERE.

A read-only replica

Nothing gets written, but nothing is hidden either. Ask about one customer and the agent can read all of them, PII included.

A staging copy

Safe, and useless for the question you actually have. The bug is in production, in one customer’s data, and the agent is looking at seed rows.

The difference

Your app’s rules travel with the data.

Studio’s MCP server does not speak to the agent as a database role. It speaks as your application schema — the models you expose, the operations you allow, and the access policies you already wrote — so the agent gets real data without getting all of it.

AspectConnection-string MCP serversZenStack Studio MCP
What the agent can seeEverything the database role can reachOnly the models you expose
What it can changeRead-only or everything, for the whole databaseRead, insert, update, delete — set per model
Whose data it acts onEvery tenant at onceOne user, under your access policies
How it queriesHand-written SQL against guessed column namesPrisma-style queries, type-checked against your schema
Where credentials liveIn the MCP config on every machineIn the proxy you run; clients hold a signed access key

Control

Three layers, from coarse to exact.

Per model

Expose Orders. Hide Payments.

Turn read, insert, update, and delete on or off for each model. A model with nothing enabled is removed from the schema the agent sees, so it cannot ask about a table it does not know exists. A forbidden operation is rejected before it reaches your database.

Per user

One customer’s rows, and nothing else.

An access key names the user the agent acts as. Every query then runs under your ZenStack access policies: rows that user cannot read do not come back, fields they cannot see are left out, writes they are not allowed to make are rejected.

Per query

Checked before it runs.

The agent writes Prisma-style queries — findMany, create, groupBy — which it already knows from countless open-source projects. Each one is type-checked against your schema first, so a misspelled field comes back as an error to fix, not a failed query to retry.

The per-user rules are ZenStack access policies declared in your schema — the same ones Studio’s table and query editors enforce when you view the data as a user. One set of rules for people and agents.

What it makes possible

Real data, the right slice.

Debug a ticket on production

Hand Claude Code the ticket and a key for that customer. It reads their real orders and settings — and cannot wander into anyone else’s.

Ship an “ask your data” assistant

Issue one key per end user and put an assistant in your product that answers from live data, scoped to whoever is asking. No second authorization layer to build.

Give a teammate’s agent a safe slice

Let a support or ops teammate point their agent at the database with read access to the models they need and no way to touch the rest.

How it works

Three tools, in a fixed order.

The server gives the agent exactly three tools and tells it to use them in order: read the schema, check the operation, then run it.

  1. 1. schema

    Your schema, minus the models the agent may not touch, with the operations each remaining model allows.

  2. 2. check

    Validates the model, the operation, and the permission, and type-checks the arguments against your schema.

  3. 3. execute

    Runs the operation through your proxy. Permissions are enforced again here, so skipping check skips nothing.

Connect

Stop pasting DATABASE_URL into your agent’s config.

Paste this instead. Enable the MCP server on a project in Studio and it hands you the configuration with the URL and an access key filled in. Your database credentials stay in the proxy and never touch the agent’s config.

Your proxy is reachable from the internet. The client talks to Studio’s MCP endpoint directly.

{  "mcpServers": {    "zenstack": {      "url": "https://studio.zenstack.dev/api/mcp/<your-routing-token>",      "headers": {        "Authorization": "Bearer <access-key>"      }    }  }}

Issuing a key per user

Access keys are standard HS256 JWTs signed with your project’s MCP secret key, so your backend can mint one per user with any JWT library. The claim names the user your access policies read; { type: 'superUser' } bypasses them.

With authentication on, the server verifies the signature on every request and refuses one without a valid key.

Sign an access key (Node.js)
import crypto from 'node:crypto' const secret = '<your MCP secret key>'const header = Buffer.from(JSON.stringify({ alg: 'HS256', typ: 'JWT' })).toString('base64url') // The user the agent acts as. { type: 'superUser' } bypasses access policies.const payload = Buffer.from(  JSON.stringify({ type: 'user', data: { userId: '<user-id>' } })).toString('base64url') const signature = crypto  .createHmac('sha256', Buffer.from(secret, 'hex'))  .update(`${header}.${payload}`)  .digest('base64url') const accessKey = `${header}.${payload}.${signature}`

MCP server FAQ

Is it safe to connect an AI agent to my production database?

It is as safe as the access you give it. Studio lets you decide which models the agent can see, which operations it may run on each, and which user it acts as — so an agent debugging one customer’s ticket sees only that customer’s rows. Your database credentials stay in the proxy you run and never reach the agent or ZenStack.

How is this different from a Postgres MCP server?

A typical database MCP server connects with a database role and gives the agent a SQL tool, so the agent can reach whatever that role can — usually every table and every tenant. Studio’s MCP server works at the level of your application schema instead: models and operations you allow, access policies evaluated per user, and a check step that type-checks each query against your schema before the agent runs it.

Do I have to adopt the ZenStack ORM?

No. Studio introspects your existing database and your application code stays as it is. Per-user scoping reads access policies, which you add to the generated schema when you want them.

Which MCP clients work with it?

Any client that speaks the Model Context Protocol — Claude Code, Claude Desktop, Cursor, and others. Studio gives you a connection URL and a ready-made configuration block to paste into the client.

My database is only reachable on localhost. Can I still use it?

Yes. Run the local delegate, @zenstackhq/studio-mcp-remote, as the MCP command in your client. It runs on your machine and forwards requests between the agent and the proxy on your network.

Stop choosing between real data and a safe agent.

npx @zenstackhq/cli studio