关于 Lagaam
Lagaam 是面向 Trino 和 Apache Pinot 的 MCP 服务,为共享数据集群中的查询设置执行前关卡。它会检查表访问授权、估算查询成本并记录调用。
主要功能
- 01执行前检查查询成本
- 02按表设置访问授权
- 03验证只读 SQL
- 04验证结果并限制返回量
- 05记录 JSONL 审计日志
适用场景
- 01阻止高成本扫描和连接
- 02限制 Agent 访问获准表
- 03通过审计日志检查查询决策
MCP 相关
向 MCP 客户端提供 list_catalogs、describe_table 和 query_data 工具,并限制 Agent 只能通过这些工具访问数据库。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
uvx lagaamLAGAAM_ALLOWED_TABLESComma list of catalog.schema.table grants; the server will not start without itLAGAAM_ALLOW_ALL_TABLEStrue to run with no grant at all: an explicit opt-out, off by defaultLAGAAM_ENGINEWhich adapter to runTRINO_HOSTTrino coordinator hostTRINO_PORTTrino coordinator portTRINO_USERTrino userPINOT_CONTROLLER_URLPinot controller URLPINOT_BROKER_URLPinot broker URLPINOT_USERPinot user; unset means no authPINOT_PASSWORD密钥Pinot password; unset means no authLAGAAM_AGENT_NAMEIdentity stamped on the audit trailLAGAAM_MAX_SCAN_BYTESScan-bytes budget per query, checked before it runs (default 50 GiB)LAGAAM_MAX_ROWSScanned-row estimate budget per query; unset means ungatedLAGAAM_MAX_INTERMEDIATE_ROWSRows the engine would build at its widest step, not rows returned, so a LIMIT doesn't lower itLAGAAM_MAX_RETURNED_ROWSRows returned to the agent per query, capped at 100000; a bigger LIMIT in the query is lowered to itLAGAAM_QUERY_TIMEOUTWall-clock seconds per queryLAGAAM_METADATA_TTLMetadata cache TTL, secondsLAGAAM_AUDIT_LOGAudit JSONL file path; unset means stderr本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍

一次 Agent 查询不该拖慢所有人的 Trino。 在共享集群上,缺少分区筛选条件或意外的交叉连接带来的代价,不只是你的账单——还会影响其他人的查询。Lagaam 是一个 MCP 服务,位于 AI Agent 与数据湖仓(Trino 和 Apache Pinot)之间:每个查询都会根据架构信息生成,在运行前根据引擎自身的计划估算成本,通过预算检查,并记录审计日志;有问题的查询会在运行前被拒绝,并附上 Agent 可以采取行动的修正建议。

真实会话、真实 Trino,没有任何模拟内容——运行
uv run --project server python examples/demo.py 即可复现。

真实会话、真实的 Pinot 1.5.1 实时表,没有任何模拟内容——运行
uv run --project server python examples/demo_pinot.py 即可复现。
问题所在
Agent 会写出语法有效却后果严重的 SQL,而在共享集群上,受影响的不只是它们自己。缺少分区筛选条件会导致全表扫描,占用所有工作节点;意外的交叉连接会耗尽工作节点内存,让其他人的查询只能排队等待;重试循环还会再次运行同一查询。SELECT * 会把 40 列数据拖进一个只需要 2 列的上下文窗口。通常的解决办法是完全不给 Agent 数据库访问权限。
Lagaam 则是在设定约束的前提下授予它们访问权限:
- 成本是报价,不是账单。 每个查询在执行前都会根据引擎自身的计划估算成本——用
EXPLAIN (TYPE IO)估算可能扫描的字节数,用EXPLAIN (TYPE LOGICAL)估算任一运算符可能生成的最大行数(交叉连接会让这个数字暴增,而LIMIT无法掩盖)。超出预算 → 拦截,并说明具体数值和修正方法。 - 无法估算就拒绝。 表统计信息缺失、破坏估算的自连接、规划器无法查看的透传操作——这些情况都会触发安全拒绝,而不是寄希望于侥幸。
- 通过 AST 强制只读。 只允许单条
SELECT。禁止 DDL/DML、多语句注入、SELECT *和表函数透传;如果缺少LIMIT,则会自动添加。验证后的 SQL 会重新渲染,因此实际运行的内容与检查过的内容完全一致。 - Agent 可以自行获取准确信息。
list_catalogs和describe_table会返回准确的名称、类型和行数估算值——范围仅限该 Agent 获准访问的表,因此 Agent 永远无法获知无权访问的名称。 - 结果经过验证后才会被信任。 如果结果为零行、页面被截断或列全为 NULL,Agent 会收到包含后续操作建议的警告,而不是容易误导人的无提示结果。
- 每次调用都会记录审计日志。 每次工具调用写入一行 JSONL:调用者、操作内容、获准或拒绝,以及原因。
Trino 不已经有这些限制了吗?
Trino 会在查询运行后限制它。Lagaam 则会在查询开始前拒绝它。 两者都保留。
| 作用 | Agent 看到的内容 | 能否拦截读取量少的交叉连接 | |
|---|---|---|---|
query.max-scan-physical-bytes | 执行期间:扫描到相应字节数后终止 | 查询失败 | 不能——它统计读取的字节数,而不是构建的行数 |
query.max-execution-time | 执行期间:达到相应时长后终止 | 查询失败 | 只能等到查询占用集群这么久后 |
Resource groups(hardPhysicalDataScanLimit、softMemoryLimit,……) | 下一条查询:组超出可用份额后,新查询会排队 | 后续查询会等待 | 不能——正在运行的查询会继续 |
| Lagaam | 执行前,根据 EXPLAIN 进行判断 | 拒绝请求,并说明数值和解决办法 | 能——它会估算最宽步骤将构建的行数 |
在 Trino 476 上的测量结果:SELECT o.orderkey, l.partkey FROM tpch.tiny.orders o CROSS JOIN tpch.tiny.lineitem l LIMIT 10 根据 Trino 自己的计划会读取 676,575 字节——任何高于 0.7 MB 的扫描上限都会允许它运行——但它会构建 902,625,000 行。Lagaam 会在查询运行前拒绝它:“此查询最宽的步骤将构建 902,625,000 行,超过了 50,000,000 的预算……LIMIT 无济于事——请使用不同值更多的列进行联接,或在联接前分别筛选两侧的数据。”
Resource groups 仍是所有进入集群的查询的最后一道防线;Lagaam 则是面向 Agent 流量的前置关卡。
其他数据库 MCP 服务怎么样?
它们都是不错的服务,只是职责不同:确保 Agent 只能读取数据,并限制返回的内容。Lagaam 也能做到这些,还会在执行查询前对其进行评估。
| 只读 | 行数上限 | 超时 | 执行前检查计划 | |
|---|---|---|---|---|
| tuannvm/mcp-trino | 默认开启 | 获取结果时截断(TRINO_MAX_ROWS) | 执行期间(TRINO_QUERY_TIMEOUT) | 不能——explain_query 会向 Agent 显示计划,但不会拦截 execute_query |
| startreedata/mcp-pinot | 始终开启,执行前解析 | 执行前改写 LIMIT,分页返回结果 | 执行期间(PINOT_QUERY_TIMEOUT,60 s) | 不能 |
| bytebase/dbhub | 可选启用(readonly):关键字检查,加上数据库自身的只读事务 | 可选启用(max_rows),注入为 LIMIT/TOP | 可选启用(query_timeout),执行期间 | 不能——可选启用的 explain_sql 会显示计划,但不会拦截 execute_sql |
| Lagaam | 始终开启,执行前解析 | 默认 1,000;更大的 LIMIT 会在运行前调低 | 执行期间(LAGAAM_QUERY_TIMEOUT,300 s) | 能——根据 EXPLAIN 检查扫描字节数和最宽步骤的行数;超出预算即拒绝 |
拦截率
11 条 LLM Agent 可能会编写的查询——全表扫描、SELECT *、DDL、注入尝试、过大的联接以及超出授权范围的读取。原始 MCP 封装会将它们全部提交给引擎。Lagaam 会在执行前拦截 11/11 条,而范围合理的对照查询则不受影响。
复现方法:benchmarks/catch_rate.py →
结果。
快速开始
连接到你已有的 Trino(如果你的 TRINO_PORT / TRINO_USER 不是
8080 / lagaam):
TRINO_HOST=trino.internal LAGAAM_ALLOWED_TABLES=hive.sales.orders uvx lagaam # MCP server on stdio
也可以在演示数据仓库上试用:
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
对于 Pinot,--profile pinot 会启动批处理快速入门环境,而
--profile pinot-realtime up -d 会启动一个由 Kafka 提供数据的流式环境 —
之后运行 examples/pinot-realtime/bootstrap.sh 来创建主题、
表和数据流。然后通过 LAGAAM_ENGINE=pinot 启动服务。
将其接入任意 MCP 客户端(Claude Code、Claude Desktop 或你自己的 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"
}
}
}
}
在 Claude Code 中,也可以将其作为插件安装:
/plugin marketplace add lagaam-ai/lagaam
/plugin install lagaam@lagaam
它会询问要允许访问哪些表(LAGAAM_ALLOWED_TABLES),以及你的 Trino
主机、端口和用户(如果不填写,则为 localhost / 8080 / lagaam)。
在 shell 中,可以通过标志传入这些值:
claude plugin marketplace add lagaam-ai/lagaam
claude plugin install lagaam@lagaam \
--config allowed_tables=hive.sales.orders --config trino_host=trino.internal
对于 Pinot,选择 engine=pinot 并设置控制器和代理 URL
(如果不填写,则为 http://localhost:9000 / http://localhost:8000):
claude plugin install lagaam@lagaam --config engine=pinot \
--config allowed_tables=pinot.default.baseballStats \
--config pinot_controller_url=http://pinot.internal:9000 \
--config pinot_broker_url=http://pinot.internal:8099
其他 LAGAAM_* 设置,以及启用了身份验证的 Pinot 所需的 PINOT_USER / PINOT_PASSWORD,都会从启动 claude 时所在的环境中读取。
Agent 可使用三个工具 — list_catalogs、describe_table、
query_data — 且无法通过其他方式访问引擎。
配置
| 环境变量 | 含义 | 默认值 |
|---|---|---|
LAGAAM_ALLOWED_TABLES | 以逗号分隔的 catalog.schema.table 授权列表 | 必需 |
LAGAAM_ALLOW_ALL_TABLES | 设为 true 可在完全不设置授权列表的情况下运行 | 关闭 |
LAGAAM_AGENT_NAME | 记录在审计日志中的身份标识 | anonymous |
LAGAAM_MAX_SCAN_BYTES | 每次查询执行前的扫描字节数预算 | 50 GiB |
LAGAAM_MAX_ROWS | 每次查询的扫描行数估算预算 | 不设限制 |
LAGAAM_MAX_INTERMEDIATE_ROWS | 引擎在最宽步骤中会构建的行数,而非返回的行数,因此 LIMIT 不会降低该值 | 50,000,000 |
LAGAAM_MAX_RETURNED_ROWS | 每次查询返回给 Agent 的行数 — 未设置时,服务会应用自身的 1000 行上限,并在查询执行前将更大的 LIMIT 调低至该上限 | 1000(最大 100000) |
LAGAAM_QUERY_TIMEOUT | 每次查询的实际耗时上限,单位为秒 | 300 |
LAGAAM_METADATA_TTL | 元数据缓存的 TTL,单位为秒 | 300 |
LAGAAM_AUDIT_LOG | 审计 JSONL 文件路径 | stderr |
TRINO_HOST / TRINO_PORT / TRINO_USER | Trino 协调器 | localhost / 8080 / lagaam |
未设置 LAGAAM_ALLOWED_TABLES 时,服务不会启动。 防止 Agent
访问每个目录中的所有表,正是此工具存在的目的,因此必须明确授权 — 如果确实要这样做,请设置 LAGAAM_ALLOW_ALL_TABLES=true。
无论是否设置,上述预算维度都会生效。 如果不设置
LAGAAM_MAX_SCAN_BYTES 和 LAGAAM_MAX_INTERMEDIATE_ROWS,它们会采用默认值,而不是放开限制 — 无法承担的查询会被未配置的服务拒绝,而不会直接放行。如果查询遭到拒绝,而你原本以为没有限制,原因就在这里;请提高你想调整的预算维度。
成本估算来自引擎自身的计划估算,而这些估算需要表统计信息:没有统计信息的表无法估算成本,因此其查询会被拒绝,而不是凭空猜测。将 Agent 接入之前,请先对表运行 ANALYZE — 完全没有统计信息的连接器实际上无法通过此道闸使用。这是有意为之:无法估算成本的查询,正是会让共享集群陷入停滞的那类查询。
工作原理
一个 QueryEngine 接口,配有 Trino 适配器和原生 Pinot 适配器——为 OFFLINE 和 REALTIME 表提供基于依据的查询、执行和综合成本报价。每次调用 query_data 都会经过同一条流水线:校验(sqlglot AST)→ 表允许列表 → 成本报价 → 预算门控 → 执行(行数上限 + 超时)→ 验证 → 审计。
在 Trino 上,报价依据的是引擎自身的执行计划,而非对 SQL 文本的猜测:EXPLAIN (TYPE IO) 会为查询可能扫描的字节数计价;EXPLAIN (TYPE LOGICAL) 会为任意算子可能构建的最大行数计价——这个数字会因交叉连接而暴增,也无法被 LIMIT 掩盖。
Pinot 不提供此类执行计划——无论表中实际有多少数据,每次表扫描报告的都是相同的占位行数,也没有任何端点会报告字节数——因此其报价改为综合估算:依据静态分段元数据、Broker 自身的裁剪预判器(谓词筛选后剩余的分段数),以及执行计划的连接/合并形状。Controller 报告为空的消费中(REALTIME)分段,会按流自身的刷新阈值计价,而不是按零计价。只要目录能够证明连接键,就按连接的上限计价,而非乘积。参见 ADR 0008 和 ADR 0009。
详情见 docs/architecture.md;更完整的介绍见 docs/vision.md。
状态
v0.2.5——已发布到 PyPI,包名为 lagaam;已列入 MCP Registry,名称为 io.github.lagaam-ai/lagaam;可作为 Claude Code 插件安装(lagaam@lagaam)。包含 Trino 适配器、架构工具、基于执行计划的成本防护、查询预算、只读强制执行、按 Agent 划分的允许列表、结果验证和审计日志。共 1,189 个单元测试和 212 个集成测试(使用实时 Trino 476 和 Pinot 1.5.1,涵盖批处理和实时场景),通过 mypy strict 检查。设置 LAGAAM_ENGINE=pinot 即可启动原生 Pinot 适配器:为 OFFLINE 和 REALTIME 表提供基于依据的查询、执行,以及根据分段元数据和 Broker 自身的裁剪预判器综合生成的报价——消费中分段按流的刷新阈值计价;只要目录能够证明连接键,就按连接的上限而非乘积计价。后续计划(路线图):在审计记录中加入执行后的实际用量,然后构建 Kubernetes 控制平面——将 Agent 作为 CRD,配备 Token/美元预算和紧急终止开关。
Apache 2.0。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。