grane
Enables MCP-compatible agents to run governed, read-only analytics queries against Postgres using predefined metrics and dimensions, with safety checks and provenance on every result.
README
Grane
Governed analytics and controlled exploration for AI agents.
Connect your database, define the business metrics that matter, and give any MCP-compatible agent governed access to those definitions — plus permissioned exploration of everything else.
Self-hosted. Deterministic. Semantic-first, not semantic-only.
Your AI can write SQL. That doesn't mean it knows what Revenue means. Grane tells it which numbers are authoritative and which conclusions are exploratory.
What Grane does
AI agents can already write SQL. But your database does not know your company's approved definitions of Revenue, MRR, Active Customer or ARPU — and letting an LLM invent them produces plausible-looking, wrong numbers.
Grane sits between your database and your agents:
Claude / ChatGPT / Cursor / internal agents
|
| MCP
v
GRANE metrics, dimensions, relationships,
| deterministic compiler, validation,
| SQL join/grain safety, provenance
v
Your Postgres
- The agent reasons. Grane enforces truth — and labels exploration. Agents
send semantic requests ("revenue by country last month"); Grane resolves the
approved definitions, plans the joins, compiles the SQL, and executes it
read-only. Permitted raw warehouse columns can be requested as
raw_dimensions/raw_metricswithout writing SQL. - Fan-out safety. Grane knows relationship cardinality and metric grain.
Measures across
one_to_manyjoins are pre-aggregated deterministically; queries that would silently multiply rows are refused — including exploratory ones. - Refusal is a trust feature. Ask for a metric that isn't defined and
Grane returns a structured
undefined_metricresponse with suggestions — it never invents business logic. Raw columns are allowed only when exploration is enabled and the column is not excluded. - Three trust levels.
governed(approved definitions only),mixed(approved metrics plus raw fields),exploratory(raw warehouse data). Agents must not present exploration as approved business truth. - No LLM inside. Grane is deterministic infrastructure. No API keys, no hosted data plane, nothing leaves your environment.
Connect ChatGPT, Claude, Gemini, or any MCP agent
Grane does not need your OpenAI, Anthropic, or Google API keys. You use your own agent subscription or API key on the chat side; Grane sits in the middle and answers governed analytics queries over MCP.
Your agent (ChatGPT / Claude / Gemini / Cursor) — your LLM keys
|
| MCP
v
Grane — no LLM keys; metrics + SQL compiler
|
| read-only SQL
v
Your Postgres — DATABASE_URL
Setup in three steps:
- Database — point
grane.ymlat Postgres with a read-only user; define metrics in YAML; rungrane validate. - Grane MCP — run
grane serve(HTTP) or let the agent launchgrane serve --stdio(local desktop clients). - Agent — register Grane with
grane mcp connect <client>(Claude, Cursor, Gemini, VS Code, ChatGPT, Windsurf, Claude Code, or generic), then ask questions in chat.
| Agent | Typical setup | Grane transport |
|---|---|---|
| Claude Desktop | grane mcp connect claude |
stdio (local) or HTTPS (remote) |
| ChatGPT | grane mcp connect chatgpt (prints HTTPS steps) |
HTTPS only — deploy Grane publicly |
| Gemini CLI | grane mcp connect gemini |
stdio or HTTP |
| Cursor / VS Code | grane mcp connect cursor or vscode |
stdio or local HTTP |
Full walkthrough: docs/connect-an-agent.md
MCP tool reference: docs/mcp-setup.md
Warehouse connections: docs/warehouses.md
Quickstart (with the example database)
npm install -g grane-analytics @duckdb/node-api
git clone https://github.com/Nareik33L/grane.git
cd grane
# DuckDB (no Docker): seeded shop data in example/analytics-duckdb
grane -p example/analytics-duckdb validate
grane -p example/analytics-duckdb query revenue -d country --last 30d
# Or Postgres:
docker compose -f example/docker-compose.yml up -d --wait
grane -p example/analytics validate
grane -p example/analytics query revenue --dimension country --last last_month
grane -p example/analytics query revenue --raw-dimension customers.name --last 30d
grane -p example/analytics mcp doctor --offline --skip-mcp
grane -p example/analytics mcp print-config generic
grane -p example/analytics serve
# MCP http://localhost:8080/mcp
Install
npm install -g grane-analytics
# or: npx grane-analytics --help
The CLI command is still grane. Requires Node 20+. Warehouse drivers other
than Postgres are not installed with the CLI — add only the one you use
(see Warehouses below). That keeps a global install free of unrelated SDK
deprecation warnings.
Warehouses
Set connection.type in grane.yml. Postgres and Redshift use the bundled
pg driver. Other engines need one extra package:
| Type | Extra install |
|---|---|
postgres / redshift |
(bundled) |
mysql |
npm install mysql2 |
snowflake |
npm install snowflake-sdk |
bigquery |
npm install @google-cloud/bigquery |
duckdb |
npm install @duckdb/node-api |
clickhouse |
npm install @clickhouse/client |
databricks |
npm install @databricks/sql |
Connection examples: docs/warehouses.md
Quickstart (your own database)
grane init # scaffolds grane.yml, metrics.yml, dimensions.yml, relationships.yml
export DATABASE_URL=postgres://readonly_user:...@host:5432/db
grane discover # introspect tables, columns, FKs; infer relationships
# ... define entities, metrics, dimensions, relationships ...
grane validate # the "type checker for analytics"
grane query revenue -d country --last 30d
grane serve # or: grane serve --stdio
Use a read-only database user. Grane also wraps every query in a
READ ONLY transaction with a statement timeout, but the database remains
the final security boundary.
Defining metrics
Configuration is code: YAML files, reviewed in pull requests, versioned in Git, edited by you or your coding agent.
# entities: the business objects metrics are counted at (their grain)
entities:
order:
table: orders
primary_key: id
# metrics.yml
metrics:
revenue:
description: Net revenue from completed orders
owner: finance
entity: order
type: sum # sum | count | count_distinct | avg | min | max | ratio
sql: ${orders.net_amount}
time_dimension: ${orders.completed_at}
unit: GBP
status: approved # experimental | approved | deprecated
synonyms: [sales, net sales]
filters:
orders.status: completed
# dimensions.yml
dimensions:
country:
entity: customer
sql: ${customers.country}
# relationships.yml — cardinality powers the join-safety checks
relationships:
orders_to_customers:
from: orders.customer_id
to: customers.id
type: many_to_one
grane validate checks every reference against the live schema, verifies
types, and detects unsafe fan-out before an agent ever runs a query.
The MCP surface
Four tools, deliberately hard to misuse:
| Tool | Purpose |
|---|---|
catalog() |
Discover metrics, dimensions, entities, synonyms, and (when enabled) explorable warehouse columns |
query() |
Run a Query Model v1 request: resolve → validate → compile → execute → provenance |
validate() |
Dry-run a query without executing it |
explain() |
Inspect definitions, trust level, the join plan and the exact SQL |
Agents send analytical intent, not SQL:
{
"metrics": ["revenue"],
"dimensions": ["country"],
"raw_dimensions": ["orders.discount_code"],
"filters": [{ "field": "customer_type", "operator": "=", "value": "business" }],
"time": { "from": "2026-07-01", "to": "2026-07-31", "grain": "month" },
"order": [{ "field": "revenue", "direction": "desc" }],
"limit": 100
}
Every result carries a trust level and provenance:
{
"trust": "mixed",
"governed": ["revenue"],
"ungoverned": ["orders.discount_code"],
"warning": "orders.discount_code is not defined in the Grane semantic model",
"provenance": {
"query_id": "q_1faea438cc34",
"trust": "mixed",
"query_model": "v1",
"metrics": { "revenue": { "definition_version": "a82cf1d3" } },
"generated_sql": "SELECT ...",
"executed_at": "2026-08-25T12:00:00Z"
}
}
See docs/connect-an-agent.md for ChatGPT, Claude,
Gemini, Cursor, and grane mcp connect. See docs/mcp-setup.md
for MCP tool reference and config file formats.
The trust contract
Grane is semantic-first, not semantic-only. A company should not have to
model its entire warehouse before agents can investigate. Define Revenue, MRR,
Customers; let agents explore discount_code or device_type when policy
allows. Grane still compiles the SQL — agents never get unrestricted SQL by
default.
trust |
Meaning |
|---|---|
governed |
Every field came through an approved Grane definition. Present as business truth. |
mixed |
Approved metrics combined with permitted raw warehouse fields. A strong lead, not an approved conclusion. |
exploratory |
Raw warehouse data only. Investigation, not governed analytics. |
Enable exploration in grane.yml:
exploration:
enabled: true
schemas:
- public
exclude:
- users.password_hash
- customers.ssn
Set enabled: false to refuse every raw column. Excluded columns are never
queryable. The database credentials used by Grane should remain read-only.
When a raw field is repeatedly useful:
grane usage # orders.discount_code used in 47 analyses
grane promote orders.discount_code # writes a governed dimension to dimensions.yml
When Grane returns trust: governed, it guarantees that every metric and
dimension was explicitly defined in the semantic model, every join was known
and cardinality-safe, no business logic was invented by an LLM, the SQL is
inspectable, and the exact definition versions are identified. If Grane
cannot safely resolve the requested meaning, it refuses instead.
What Grane is not
No dashboards, no chart builder, no built-in chatbot, no hosted data plane, no required LLM API key. Agents own presentation; Grane owns analytics truth — and always says which numbers are governed and which are exploratory.
Development
npm install
npm run test:unit # no database needed
docker compose -f example/docker-compose.yml up -d --wait
npm test # unit + integration
V0.1 supports Postgres. The connector interface will open up to other databases (MySQL, ClickHouse, DuckDB, Snowflake, ...) as demand appears.
License
Apache-2.0
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.