Server data from the Official MCP Registry
Stops an agent's Trino or Pinot query before it runs if it costs too much or reads outside its grant
About
Stops an agent's Trino or Pinot query before it runs if it costs too much or reads outside its grant
Security Report
Lagaam is a well-architected governed MCP server with strong security controls for query execution and cost management. The codebase demonstrates excellent validation, authorization scoping, and read-only enforcement mechanisms. Minor quality concerns around exception handling and test coverage do not materially affect the security posture. Permissions are appropriate for the server's purpose as a analytics governance layer. Supply chain analysis found 3 known vulnerabilities in dependencies (1 critical, 1 high severity). Package verification found 1 issue.
3 files analyzed · 8 issues found
Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.
Permissions Required
This plugin requests these system permissions. Most are normal for its category.
What You'll Need
Set these up before or after installing:
Environment variable: LAGAAM_ALLOWED_TABLES
Environment variable: LAGAAM_ALLOW_ALL_TABLES
Environment variable: LAGAAM_ENGINE
Environment variable: TRINO_HOST
Environment variable: TRINO_PORT
Environment variable: TRINO_USER
Environment variable: PINOT_CONTROLLER_URL
Environment variable: PINOT_BROKER_URL
Environment variable: PINOT_USER
Environment variable: PINOT_PASSWORD
Environment variable: LAGAAM_AGENT_NAME
Environment variable: LAGAAM_MAX_SCAN_BYTES
Environment variable: LAGAAM_MAX_ROWS
Environment variable: LAGAAM_MAX_INTERMEDIATE_ROWS
Environment variable: LAGAAM_MAX_RETURNED_ROWS
Environment variable: LAGAAM_QUERY_TIMEOUT
Environment variable: LAGAAM_METADATA_TTL
Environment variable: LAGAAM_AUDIT_LOG
How to Install
Add this to your MCP configuration file:
{
"mcpServers": {
"io-github-lagaam-ai-lagaam": {
"env": {
"PINOT_USER": "your-pinot-user-here",
"TRINO_HOST": "your-trino-host-here",
"TRINO_PORT": "your-trino-port-here",
"TRINO_USER": "your-trino-user-here",
"LAGAAM_ENGINE": "your-lagaam-engine-here",
"PINOT_PASSWORD": "your-pinot-password-here",
"LAGAAM_MAX_ROWS": "your-lagaam-max-rows-here",
"LAGAAM_AUDIT_LOG": "your-lagaam-audit-log-here",
"PINOT_BROKER_URL": "your-pinot-broker-url-here",
"LAGAAM_AGENT_NAME": "your-lagaam-agent-name-here",
"LAGAAM_METADATA_TTL": "your-lagaam-metadata-ttl-here",
"LAGAAM_QUERY_TIMEOUT": "your-lagaam-query-timeout-here",
"PINOT_CONTROLLER_URL": "your-pinot-controller-url-here",
"LAGAAM_ALLOWED_TABLES": "your-lagaam-allowed-tables-here",
"LAGAAM_MAX_SCAN_BYTES": "your-lagaam-max-scan-bytes-here",
"LAGAAM_ALLOW_ALL_TABLES": "your-lagaam-allow-all-tables-here",
"LAGAAM_MAX_RETURNED_ROWS": "your-lagaam-max-returned-rows-here",
"LAGAAM_MAX_INTERMEDIATE_ROWS": "your-lagaam-max-intermediate-rows-here"
},
"args": [
"lagaam"
],
"command": "uvx"
}
}
}Documentation
View on GitHubFrom the project's GitHub README.

Stop your agent from running the $500 query. Lagaam is a governed MCP server that sits between your AI agents and your lakehouse (Trino and Apache Pinot). Every query is schema-grounded, priced before it runs, checked against a budget, and audited — and every rejection tells the agent exactly how to fix its SQL.

Real session, real Trino, nothing mocked — reproduce it with
uv run --project server python examples/demo.py.

Real session, real Pinot 1.5.1 realtime table, nothing mocked — reproduce it
with uv run --project server python examples/demo_pinot.py.
The problem
Agents write syntactically-valid, catastrophic SQL. A missing partition
filter turns into a full scan over a petabyte table; one retry loop burns a
day's warehouse budget in minutes; a SELECT * drags 40 columns into a
context window that needed 2. The usual fix is to not give agents database
access at all.
Lagaam gives them access with reins on:
- Cost is a quotation, not a bill. Every query is priced from the
engine's own plan before execution —
EXPLAIN (TYPE IO)for the bytes it would scan,EXPLAIN (TYPE LOGICAL)for the widest row count any operator would build (the number a cross join blows and aLIMITcannot hide). Over budget → blocked, with the number and the fix. - Un-estimable means no. No table statistics, a self-join that breaks the estimate, a passthrough the planner can't see — the gate fails safe instead of hoping.
- Read-only, enforced in the AST. Single
SELECTonly. No DDL/DML, no multi-statement injection, noSELECT *, no table-function passthrough, and aLIMITis injected when missing. Validated SQL is re-rendered, so what runs is exactly what was checked. - Agents ground themselves.
list_catalogsanddescribe_tablereturn exact names, types, and row estimates — scoped to the agent's table grant, so the agent never learns names it isn't allowed to touch. - Results are verified before they're trusted. Zero rows, truncated pages, all-NULL columns — the agent gets a warning with a next action, not a silently misleading answer.
- Every call is audited. One JSONL line per tool call: who, what, allowed or denied, and why.
Catch rate
11 queries an LLM agent plausibly writes — full scans, SELECT *, DDL,
injection attempts, oversized joins, out-of-grant reads. A raw MCP
wrapper submits all of them to the engine. Lagaam stops 11/11 before
execution while the well-scoped control query runs untouched.
Reproduce: benchmarks/catch_rate.py →
results.
Quickstart
Against the Trino you already have (TRINO_PORT / TRINO_USER if yours
aren't 8080 / lagaam):
TRINO_HOST=trino.internal LAGAAM_ALLOWED_TABLES=hive.sales.orders uvx lagaam # MCP server on stdio
Or try it on a demo warehouse:
git clone https://github.com/lagaam-ai/lagaam && cd lagaam
docker compose -f examples/docker-compose.yml --profile trino up -d # demo warehouse
cd server && uv sync
LAGAAM_ALLOWED_TABLES=tpch.tiny.orders,tpch.tiny.lineitem \
uv run python -m lagaam # MCP server on stdio
For Pinot, --profile pinot brings up the batch quickstart and
--profile pinot-realtime up -d brings up a Kafka-fed streaming one —
run examples/pinot-realtime/bootstrap.sh after it to create the topics,
tables and feed. Then start the server with LAGAAM_ENGINE=pinot.
Wire it into any MCP client (Claude Code, Claude Desktop, or your own agent):
{
"mcpServers": {
"lagaam": {
"command": "uvx",
"args": ["lagaam"],
"env": {
"TRINO_HOST": "localhost",
"LAGAAM_MAX_SCAN_BYTES": "5368709120",
"LAGAAM_ALLOWED_TABLES": "hive.sales.orders,hive.sales.customers"
}
}
}
}
The agent gets three tools — list_catalogs, describe_table,
query_data — and cannot reach the engine any other way.
Configuration
| Env var | Meaning | Default |
|---|---|---|
LAGAAM_ALLOWED_TABLES | Comma list of catalog.schema.table grants | required |
LAGAAM_ALLOW_ALL_TABLES | true to run with no grant at all | off |
LAGAAM_AGENT_NAME | Identity stamped on the audit trail | anonymous |
LAGAAM_MAX_SCAN_BYTES | Scan-bytes budget per query, pre-execution | 50 GiB |
LAGAAM_MAX_ROWS | Scanned-row estimate budget per query | ungated |
LAGAAM_MAX_INTERMEDIATE_ROWS | Rows the engine would build at its widest step — not rows returned, so a LIMIT doesn't lower it | 50,000,000 |
LAGAAM_MAX_RETURNED_ROWS | Rows returned to the agent per query — unset, the server applies its own 1000-row cap, and a bigger LIMIT in the query is lowered to it before it runs | 1000 (max 100000) |
LAGAAM_QUERY_TIMEOUT | Wall-clock seconds per query | 300 |
LAGAAM_METADATA_TTL | Metadata cache TTL, seconds | 300 |
LAGAAM_AUDIT_LOG | Audit JSONL file path | stderr |
TRINO_HOST / TRINO_PORT / TRINO_USER | Trino coordinator | localhost / 8080 / lagaam |
The server will not start without LAGAAM_ALLOWED_TABLES. An agent that
can reach every table in every catalog is the thing this exists to prevent, so
that has to be asked for — set LAGAAM_ALLOW_ALL_TABLES=true if you mean it.
The budget dimensions above apply whether or not you set them. Leaving
LAGAAM_MAX_SCAN_BYTES and LAGAAM_MAX_INTERMEDIATE_ROWS unset gives you
their defaults, not an open gate — an unconfigured server refuses what it
cannot afford rather than waving it through. If queries are being denied and
you expected no limits, that is why; raise the dimension you mean to raise.
Cost quotes come from the engine's own plan estimates, and those need table
statistics: a table without stats cannot be priced, so its queries are refused
rather than guessed at. Run ANALYZE on your tables before pointing an agent
at them — a connector that has no statistics at all is effectively unusable
through the gate. That is deliberate: a query nobody can size is exactly the
kind that runs for $500.
How it works
A QueryEngine port with a Trino adapter and a native Pinot adapter —
grounding, execution, and a synthesised cost quotation for both OFFLINE and
REALTIME tables. Every query_data call walks one pipeline: validate
(sqlglot AST) → table allowlist → cost quotation → budget gate → execute
(row cap + timeout) → verify → audit.
On Trino, the quote is the engine's own plan, not a guess from the SQL text:
EXPLAIN (TYPE IO) prices the bytes a query would scan, and
EXPLAIN (TYPE LOGICAL) prices the widest row count any operator would
build — the number a cross join blows and a LIMIT cannot hide.
Pinot gives no such plan — every table scan reports the same placeholder row count whatever the table holds, and no endpoint reports bytes at all — so its quote is synthesised instead: from static segment metadata, the broker's own pruning oracle (how many segments survive the predicate), and the plan's join/union shape. A consuming (REALTIME) segment, which the controller reports as empty mid-flight, is priced at the stream's own flush threshold instead of zero. A join is charged its bound rather than the product wherever the catalog can prove the join key. See ADR 0008 and ADR 0009.
Details in docs/architecture.md; the longer story in docs/vision.md.
Status
v0.2.4 — on PyPI as lagaam, listed in the MCP Registry as io.github.lagaam-ai/lagaam. Trino adapter, schema tools, plan-based cost guard, query
budgets, read-only enforcement, per-agent allowlists, result verification,
audit log. 1,176 unit + 212 integration tests (live Trino 476 and Pinot
1.5.1, batch and realtime), mypy strict. LAGAAM_ENGINE=pinot starts the
native Pinot adapter: grounding, execution and a quotation synthesised from
segment metadata and the broker's own pruning oracle, for OFFLINE and
REALTIME tables alike — a consuming segment is charged at the stream's
flush threshold, and a join is charged its bound rather than the product
wherever the catalog proves the key. On deck
(roadmap): post-execution actuals on the audit line,
then a Kubernetes control plane — agents as CRDs with token/dollar budgets
and kill switches.
Apache 2.0.
Reviews
No reviews yet
Be the first to review this server!
More Developer Tools MCP Servers
Fetch
Freeby Modelcontextprotocol · Developer Tools
Web content fetching and conversion for efficient LLM usage
Git
Freeby Modelcontextprotocol · Developer Tools
Read, search, and manipulate Git repositories programmatically
Paperclip
Freeby Paperclipai · Developer Tools
Trending hip-hop artist momentum scores across four cultural dimensions.
Toleno
Freeby Toleno · Developer Tools
Toleno Network MCP Server — Manage your Toleno mining account with Claude AI using natural language.
mcp-creator-python
Freeby mcp-marketplace · Developer Tools
Create, build, and publish Python MCP servers to PyPI — conversationally.
MCP Marketplace
Freeby mcp-marketplace · Developer Tools
Search and install MCP servers from inside your AI client.
