MCP server
@valv/mcp is a zero-config Model Context Protocol (MCP) server. Point it at a
connection string and it serves your database to a coding agent like Claude
Code, with the same validation and policy enforcement as the library. No code
required.
By default it serves the four read tools and is read-only across all tables. You narrow access with environment variables or a policy file.
Guided setup
The fastest way in probes your database, lets you choose access, and writes the config for you:
npx @valv/mcp init
It prompts for a connection string, confirms the dialect, connects and shows
your tables, offers read-only, pick-tables, or a policy-file stub, and writes
the .mcp.json entry.
Manual setup
Add the server to your .mcp.json (or your Claude Desktop config) and point it
at a connection string:
{
"mcpServers": {
"db": {
"command": "npx",
"args": ["-y", "@valv/mcp"],
"env": { "DATABASE_URL": "postgresql://user:pass@localhost:5432/app" }
}
}
}
For ClickHouse, use the HTTP URL and a database name:
"env": { "DATABASE_URL": "http://localhost:8123", "VALV_DATABASE": "analytics" }
Configuration
Configure the server with environment variables:
| Variable | Description |
|---|---|
DATABASE_URL |
Connection string (required; or pass as the first CLI argument). |
VALV_PROVIDER |
postgresql, mysql, sqlite, or clickhouse. Inferred when omitted. |
VALV_DATABASE |
Database name (ClickHouse). |
VALV_TABLES |
Comma-separated allowlist; only these tables are exposed. |
VALV_EXCLUDE |
Comma-separated denylist, applied after the allowlist. |
VALV_POLICY_FILE |
Path to a policy module for tenant scoping or hidden fields. |
VALV_CONTEXT |
JSON context the policy reads, such as {"tenant":{"id":"acme"}}. |
VALV_HTTP_PORT |
Serve over Streamable HTTP on this port instead of stdio. |
Policy file
Without a policy file, access is read-only across all tables. To restrict which
tables and columns the agent sees, point VALV_POLICY_FILE at a module that
receives the configured valv instance:
// valv.policy.cjs
module.exports = (valv) => {
valv.policy("orders", () => ({
read: true, // allow reads (or { column: value } to filter rows)
fields: { deny: ["internal_notes"] }, // hide columns from the agent
}))
// Tables without a policy are denied.
}
For per-request row scoping by real user or tenant identity, use MCP in your app, where your code supplies identity per request.
The valv skill
The /valv skill adds a slash command to a coding agent that queries your
database through the MCP server and turns the result into a chart, written as a
self-contained HTML file. Install it once:
npx skills add https://github.com/valv-dev --skill valv
With the MCP server configured, you can then ask the agent something like /valv revenue by plan this month. It runs a query through valv, scoped by your policy,
and renders the rows as a chart.
The skill also learns your database as you use it. The first time it
describes a table, works out the dialect’s time-bucket function, or maps a term
like “revenue” to sum(total) on orders, it records that in .valv/notes.md
in your working directory — so later sessions skip the rediscovery and start
warm. The notes hold schema and semantics only, never query results, and the
file is plain markdown you can read, edit, or pre-seed yourself.
Next steps
- MCP in your app: serve MCP from your own server with per-request context.
- Policies: the policy rules a policy file can use.