feat(mcp): aggregate_records 工具 — GROUP BY 聚合走 engine 读路径(RLS/D10/FLS 全继承) - #2976
Merged
Conversation
…e engine read path
Adds GROUP BY aggregation to the MCP surface so agents can answer
quantitative questions in one call instead of paging raw rows through
query_records (capped + token-expensive).
- mcp: new data:read tool aggregate_records (count/sum/avg/min/max/
count_distinct, groupBy incl. {field,dateGranularity} date bucketing,
where, timezone). Optional McpDataBridge.aggregate seam — runtimes that
omit it simply don't register the tool. Output rows capped at the query
limit with a truncated flag.
- runtime: callData('aggregate') branch resolves the ObjectQL ENGINE
(never the raw per-env dataDriver) so the security middleware — RLS,
tenant scoping, ADR-0090 D10 delegator intersection — always runs;
requires >=1 aggregation (the engine's in-memory path would otherwise
degrade to raw rows the FLS masker does not cover). aggregate maps to
the 'list' ApiMethod in the exposure gate.
- plugin-security: FLS aggregate-INPUT gate (2.5b) — result masking never
runs for aggregate and aliases hide field provenance, so groupBy /
aggregation references to FLS-unreadable fields are rejected
fail-closed with the offending names, including the D10 delegator
field-mask intersection.
Tests: mcp 72 passed (6 new: registration/degradation, delegation,
sys_* guard, min(1) schema, truncation, FLS error surface); plugin-
security 426 passed (3 new FLS aggregate gate cases); runtime 507
passed (new aggregate exposure-mapping case).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012RE5oFswe4DUvExuAgvbg2
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckThis PR changes 3 package(s): 31 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
…te_records) natural-language-queries.mdx stated the open MCP surface has no server-side aggregate tool and a BYO agent must sum client-side — the aggregate_records tool added in this branch makes that false. Update the tool enumeration + the aggregation callout, and keep the distinction from the cloud dataset-level aggregate_data tool (cross-object / cubes). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012RE5oFswe4DUvExuAgvbg2
os-zhuang
marked this pull request as ready for review
July 15, 2026 15:21
68 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
目的
/mcp目前只有 CRUD 级工具——agent 回答「按月成交额」这类定量问题只能用query_records分页拉原始行自己算(每页封顶maxQueryLimit,token 成本高,大表不可行)。本 PR 新增aggregate_records工具(data:read家族):count/sum/avg/min/max/count_distinct + groupBy(含{field, dateGranularity}日期分桶)+ where + IANA timezone。关键设计:执行强制走 ObjectQL ENGINE(
callData('aggregate')分支刻意不用 MCP bridge 线程的裸 per-envdataDriver)——security middleware 把aggregate与find同列读操作(security-plugin.ts步骤 2.6),所以 RLS 注入、租户 scoping、ADR-0090 D10 delegator 交集全部自动继承,零新安全缝。与 analytics 服务(raw-SQL +getReadFilter路径,#2852 Gap 1 潜伏面)无关,不激活它。改动
McpDataBridge.aggregate?可选缝 +aggregate_records注册(bridge 未实现则不注册,优雅降级,与 action 工具同契约);输出行数按maxQueryLimit截断并带truncated/totalGroups。callData新增aggregate分支(仅经 engine);buildMcpBridge加aggregate方法;api-exposure把aggregate映射为list类 ApiMethod(白名单排除list的对象不能经 GROUP BY 泄漏行统计)。aggregate操作,且输出行只有别名,掩码无从判断sum(salary) AS total的来源字段;因此在输入侧 fail-closed:groupBy/度量引用 FLS 不可读字段即拒(含 D10 delegator 字段掩码交集),报错带字段名,镜像 2.5 写门风格。三个防御纵深点(评审重点)
context.dataDriver(多环境下是裸 db 驱动);aggregate 若走它会整体绕过 middleware。分支内显式只解析 engine,bridge 侧加了注释说明。callData强制aggregations.length >= 1(工具 zod 层同样min(1),双层)。验证
@objectstack/mcp:72 passed(新增 6:注册/降级、透传、sys_*fail-closed、min(1)、截断、FLS 错误面)@objectstack/plugin-security:426 passed(新增 3:不可读字段聚合被拒、结构化 groupBy 被拒、可读字段 + count(*) 放行)@objectstack/runtime:507 passed(新增 aggregate→list 映射用例)关联
#2852(analytics read-scope D10 缺口——本 PR 刻意不走那条路径,评估记录见 issue v2)· ADR-0090 D10 · #2920(AI-native 主轨)
🤖 Generated with Claude Code
https://claude.ai/code/session_012RE5oFswe4DUvExuAgvbg2
Generated by Claude Code