Let coding agents query any SQL database safely. Read-only by default and scoped by your policies.
Copy the AI prompt to install this server into Claude Code, Cursor, or another agent β or use 1-click editor setup below.
π‘ Paste the JSON block into your client's configuration file under mcpServers, then restart the application.
Let agents query your database. Just not all of it.
valv gives an agent structured tools to read your database β and, opt-in, to write to it. The model emits a structured query (or insert/update/delete) β never a native database command β and valv validates it against your schema, scopes it to the current user with policies you write in code, compiles it for your database, and runs it.
The model's query is treated as fully untrusted. It can't read a column you hid, a row the user isn't allowed to see, call a function you didn't allow, write a column you didn't permit, or escape its tenant on a write. Valv rebuilds and checks the query on the server before it reaches the database adapter.
@valv/mcp-sdk, scoped per request.@valv/mcp at a database and a tool like Claude Code queries it safely β no code required.Install an adapter for your database (it pulls in @valv/core):
Wire it up β connect, write a policy, hand the tools to an agent:
The agent gets four tools β list_resources, search_resources, describe_resource, and query β discovers your schema, and runs a query. valv scopes it to acme, compiles it to ClickHouse SQL, runs it, and hands back rows.
One query tool covers the whole read surface. The grammar is Prisma-idiomatic β a shape models already know cold β and desugars server-side into a checked query:
That's enough for real analytics β filters ({ field: value } equality, operator objects like { gte, lt, in, contains }, and AND/OR/NOT trees), aggregates, time-series (bucket with a function and group by the alias), top-N (order by an aggregate), and conditional aggregation (countIf, sumIf). ClickHouse adds dialect functions like quantileTiming and toStartOfInterval; every function is type-checked and its literals parameterized.
To read a related resource, reference its column with a dotted path from the root. The model can only follow relations declared in your schema; valv derives the joins, picks the keys, and composes the policy of every table it touches β each joined table is scoped by its own policy and field allowlist, so a join can never reach a hidden column or another tenant's rows.
belongsTo and hasMany relations are supported; join depth, table count, and fan-out are capped, and every query runs under a statement timeout. Relations are auto-introspected on Prisma and declared in the schema on ClickHouse.
createValv is async β it loads the schema on construction, so the instance is ready to use. Call it once at startup.
defaultPolicy: "deny-all" (recommended) makes a resource invisible until you write a policy for it.
A policy is a function of your context. It decides what the caller may read, per resource:
read value | Meaning |
|---|---|
true / false | allow / deny outright |
{ field: value } | a row filter, AND-ed into the query server-side |
The model can't widen or override the row filter. Valv injects it after parsing
the model's query and before handing the query to the database adapter. Fields
are denied two ways: fields.deny (a blacklist) or fields.allow (a
whitelist). Denied and unknown columns fail with the same message, so the model
can't probe for hidden columns. Use "*" as the resource name for a default
policy.
The same policy object carries the write axes β create, update, delete (and write as a shorthand for create+update) β which default to denied. See Writes.
valv.tools.<format>(ctx, options) returns provider-ready tools, bound to that context. Discovery is policy-filtered β list/search/describe only surface what the caller may read.
The aisdk format returns self-executing tools (the SDK runs them). The provider formats (anthropic/openai/gemini) return tool definitions for the API request; you dispatch a tool call with runTool:
The discovery tools (list/search/describe) are on by default; the write tools (create/update/delete) are off by default β turn them on per call:
await valv.instructions(ctx) returns a drop-in system-prompt block: how to drive the tools (discover β describe β query, filters are scoped server-side) plus the resources this caller may read β so the model can skip the opening list_resources round-trip. Put it in your system prompt alongside the tools. The static text is also exported as AGENT_INSTRUCTIONS if you'd rather compose the resource list yourself.
No reviews yet β be the first to share how this listing worked for you.
Showcase your server listing on GitHub or your project documentation. Embed this dynamic SVG badge to highlight official listing status and live engagement.
[](https://allmcps.com/mcp/valv)<a href="https://allmcps.com/mcp/valv"><img src="https://allmcps.com/api/badge/valv?style=directory" alt="Valv on AllMCPs" /></a>