MCP Toolbox for Databases 中 bigtable-list-schemas 工具详解:一次获取 Bigtable 表、列族与 SQL 视图的完整 Schema
【免费下载链接】mcp-toolboxMCP Toolbox for Databases is an open source MCP server for databases.项目地址: https://gitcode.com/GitHub_Trending/ge/mcp-toolbox
bigtable-list-schemas是 MCP Toolbox for Databases(mcp-toolbox)中面向 Google Cloud Bigtable 的数据发现工具:它一次调用即可返回实例内所有表的列族定义、逻辑视图与物化视图的定义及其动态解析出的列级 Schema(列名 + 类型)。本文以官方文档 bigtable-list-schemas 说明 为主体,结合 工具实现源码 与 测试用例,完整讲解其配置方式、参数语义、输出结构,以及底层“从 GoogleSQL 视图查询中启发式解析列类型”的机制,帮助你在 Agent 工作流中让 LLM 无需逐个查询即可建立对 Bigtable 数据模型的全局认知。
工具定位:给 LLM 提供 Bigtable 实例的“全景 Schema 视图”
Bigtable 是典型的宽列 NoSQL 数据库,其表结构(列族)、GoogleSQL 逻辑视图与物化视图分散在不同的管理面 API 中。bigtable-list-schemas将这三类元数据聚合为单一的结构化 JSON 负载,使 LLM 能够一次性获得:
- Tables(表):表名及其已配置的列族定义(
FamilyList)。 - Logical Views & Materialized Views(逻辑视图与物化视图):视图定义(
LogicalViewID、MaterializedViewID与 GoogleSQLQuery),以及从视图查询中动态解析出的列级 Schema(name与type)。
动态解析列 Schema 是该工具的核心价值:Bigtable 的 SQL 视图只保存一段 GoogleSQL 查询文本,SDK 并不会直接暴露“该视图输出哪些列、各列什么类型”。bigtable-list-schemas通过在服务端对查询文本做启发式分析,把SELECT子句翻译成 LLM 可直接消费的列清单,这一点在源码parseColumnsFromQuery中有完整实现(后文展开)。
前置条件:配置 Bigtable Source
该工具通过source字段绑定一个type: bigtable的数据源。根据 Bigtable Source 文档,source 配置示例如下:
kind: source name: my-bigtable-source type: "bigtable" project: "my-project-id" instance: "test-instance"| field | type | required | description |
|---|---|---|---|
type | string | true | Must bebigtable。 |
project | string | true | Bigtable 实例所在的 GCP 项目 ID(如my-project-id)。 |
instance | string | true | Bigtable 实例名称。 |
从源码结构看,Bigtable source 在初始化时会创建三个客户端(见 bigtable.go):数据面bigtable.Client(用于执行 SQL)、项目级InstanceAdminClient(管理实例、逻辑/物化视图)以及实例级AdminClient(管理表与列族)。bigtable-list-schemas只用到后两个管理面客户端,这正是它无需写入权限、适合只读发现场景的原因。
运行环境上需要注意:
- 工具通过你的 Application Default Credentials (ADC) 完成鉴权,需确保对应的 IAM 身份具备读取表信息与视图列表的权限(如
bigtable.tables.get、bigtable.tables.list、bigtable.views.get等,具体以 Bigtable 官方 IAM 文档为准)。 - source 的
Config结构体在源码中用validate:"required"约束了name、type、project、instance四个必填字段(见 bigtable.go#L49-L54)。
工具配置:YAML 示例与 Reference 字段表
在工具配置中声明该工具的最小完整示例(继承自官方文档):
kind: tool name: bigtable_list_schemas type: bigtable-list-schemas source: my-bigtable-source description: List all Bigtable tables, column families, and SQL view column schemas.Reference 字段说明(完整继承自文档):
| field | type | required | description |
|---|---|---|---|
type | string | true | 必须为bigtable-list-schemas。 |
source | string | true | 要执行其上的 Bigtable source 名称。 |
description | string | false | 传递给 LLM 的自定义工具描述。 |
结合源码可以补充两个文档未显式写出的细节:
description缺省值:若未提供description,Initialize 会写入默认描述List all Bigtable schemas, including tables with column family definitions, logical views, and materialized views.(L71-L73)。- 默认只读注解:未显式指定
annotations时,工具使用tools.NewReadOnlyAnnotations作为默认注解(L82),即 MCP 客户端默认将其标记为只读工具。
单元测试 验证了 YAML 解析行为,包括基础示例与带authRequired列表(如my-google-auth-service)的变体,确认type与source被正确写入Config。
调用参数:limit 控制表数量
bigtable-list-schemas接受一个可选参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
limit | integer | 否 | 20 | 返回的最大表数量。 |
源码中的参数定义与取值逻辑值得注意(bigtablelistschemas.go#L75-L77、L286-L293):
allParameters := parameters.Parameters{ parameters.NewIntParameter("limit", "Optional: The maximum number of tables to return. Default is 20", parameters.WithIntDefault(20)), } ... limit := 20 if val, ok := paramsMap["limit"].(int); ok && val > 0 { limit = val } if len(tableNames) > limit { tableNames = tableNames[:limit] }可以确认三个实现细节:
limit只作用于表列表(截断tableNames),逻辑视图与物化视图始终全量返回,不受limit影响;- 传入
0或负数时会被忽略,回落到默认值 20; - 截断取的是列表的前
limit个(BigtableTablesAPI 返回顺序)。
bigtablelistschemas_test.go 中的success_with_limit用例即以limit=1验证了只返回第一张表、视图保持完整的截断行为。
输出格式:tables / logical_views / materialized_views 三段式 JSON
工具返回结构化 JSON 负载,包含tables、logical_views、materialized_views三个键。文档给出的示例输出:
{ "tables": [ { "table_name": "users", "info": { "FamilyList": ["profile", "activity"] } } ], "logical_views": [ { "LogicalViewID": "active_users_view", "Query": "SELECT _key, CAST(profile['name'] AS STRING) AS name FROM users", "columns": [ { "name": "_key", "type": "BYTES" }, { "name": "name", "type": "STRING" } ] } ], "materialized_views": [] }对照源码中的响应结构体(bigtablelistschemas.go#L111-L135)可以精确理解每个字段的来源:
type TableSchema struct { TableName string `json:"table_name"` Info *bigtable.TableInfo `json:"info,omitempty"` } type Column struct { Name string `json:"name"` Type string `json:"type,omitempty"` } type LogicalViewSchema struct { bigtable.LogicalViewInfo // LogicalViewID + Query Columns []Column `json:"columns"` } type MaterializedViewSchema struct { bigtable.MaterializedViewInfo // MaterializedViewID + Query Columns []Column `json:"columns"` } type SchemaList struct { Tables []TableSchema `json:"tables"` LogicalViews []LogicalViewSchema `json:"logical_views"` MaterializedViews []MaterializedViewSchema `json:"materialized_views"` }由此可补充文档示例之外的三点事实:
info是 SDK 的完整bigtable.TableInfo:文档示例中简化的FamilyList只是示意;实际序列化的是cloud.google.com/go/bigtableSDK 的TableInfo,包含列族名称、GC 规则等全部元数据(Info字段为omitempty,获取失败时整个字段缺省)。columns中的type可缺省:Type带omitempty标签,当启发式解析无法判定列类型时(如NULLIF(...)或普通列族列引用),type字段不会出现在 JSON 中。Invoke始终初始化三个空切片(L273-L277),因此即使实例没有任何表或视图,输出也是[]而非缺失键,便于 LLM 稳定解析。
底层执行流程:四步聚合 + 容错降级
Invoke方法(bigtablelistschemas.go#L267-L339)的完整调用链如下:
- 列出所有表:调用 source 的
ListTables。该错误是致命的——一旦失败立即返回ProcessGcpError包装后的错误。 - 应用
limit并逐表获取详细信息:对每张表调用GetTable拿TableInfo。这一步是容错降级的:单表GetTable失败不会中断整个调用,该表仍以TableName形式进入结果(不带info),保证部分可见性。 - 列出逻辑视图:调用
ListLogicalViews(ctx, source.InstanceID()),并对每个视图的Query执行parseColumnsFromQuery填充Columns。 - 列出物化视图:调用
ListMaterializedViews(ctx, source.InstanceID()),同样动态解析列。
第 3、4 步的列表错误同样致命(返回错误而非空结果),这一“列表必全、单表可缺”的容错策略在 TestInvoke 的error用例中得到验证:mock source 报错时Invoke必须返回错误。
数据面来源:Source 的 admin 封装
工具并不直接调用 Bigtable SDK,而是依赖一个最小接口(bigtablelistschemas.go#L49-L55):
type compatibleSource interface { ListTables(context.Context) (any, error) GetTable(context.Context, string) (any, error) ListLogicalViews(context.Context, string) (any, error) ListMaterializedViews(context.Context, string) (any, error) InstanceID() string }ValidateSource会在工具初始化阶段用类型断言检查 source 是否满足该接口;Invoke入口再断言一次,不兼容则返回 500 语义的ClientServerError(incompatible_source测试用例覆盖了此路径)。Bigtable source 对这些方法的实际实现位于 admin_wrappers.go:
ListTables→Admin.Tables(ctx)(实例级 AdminClient)GetTable→Admin.TableInfo(ctx, tableId)ListLogicalViews→InstanceAdmin.LogicalViews(ctx, instanceId)(项目级 InstanceAdminClient)ListMaterializedViews→InstanceAdmin.MaterializedViews(ctx, instanceId)
这意味着该工具的执行只依赖读操作(list/get),不需要bigtable.data.access数据面权限,只需相应管理面的读取权限。
深度解析:从 GoogleSQL 视图查询中启发式解析列 Schema
由于 Bigtable 视图只保存查询文本,columns字段是靠纯文本分析得到的。parseColumnsFromQuery的算法分为三步:
1. 提取 SELECT 子句
selectRe = regexp.MustCompile(`(?is)\bSELECT\s+(.+?)(?:\s+FROM\b|;|$)`)以正则捕获SELECT与FROM(或分号/行尾)之间的内容;支持跨行(s标志)。
2. 按“顶层逗号”切分列表达式
切分时维护括号深度((/[加 1,)/]减 1)与单/双引号状态,只在depth == 0且不在字符串字面量内时才视为列分隔符。这使得MAP_KEYS(cell_plan)、嵌套的address['street'][0].value、字符串中的逗号(如'apple, orange')都能被正确切分——parse_test.go 中comma in single quotes、deeply nested arrays AS等用例逐一验证了这些边界。
3. 逐列提取列名与类型
列名(extractColumn,L232-L265):从右向左扫描,找到最外层(不在括号内)的最后一个AS作为别名;若没有别名,则回退取“最后一个词元”作为近似列名(如cf['my_col']→my_col,(CAST(...)).product_name→product_name)。
类型(extractType,L191-L230)是一个基于“最外层表达式”的映射表:
| 表达式形态 | 推断类型 |
|---|---|
CAST(x AS T)/SAFE_CAST(x AS T) | T的大写形式(STRING、INT64、BOOL、TIMESTAMP等) |
TO_INT64、UNIX_MILLIS、UNIX_MICROS、UNIX_SECONDS | INT64 |
TO_FLOAT64/TO_FLOAT32/TO_VECTOR32 | FLOAT64/FLOAT32/VECTOR32 |
TO_HEX、TO_BASE64、SAFE_CONVERT_BYTES_TO_STRING、FORMAT_TIMESTAMP、ARRAY_TO_STRING、CODE_POINTS_TO_STRING、TO_JSON_STRING | STRING |
TIMESTAMP_MILLIS、TIMESTAMP_MICROS、TIMESTAMP_SECONDS、PARSE_TIMESTAMP | TIMESTAMP |
PARSE_DATE | DATE |
FROM_BASE64、FROM_HEX | BYTES |
MAP_KEYS | ARRAY<BYTES> |
以_key开头的裸引用 | BYTES |
NULLIF或未识别表达式 | 空(type字段在 JSON 中缺省) |
用官方文档示例验证:视图查询SELECT _key, CAST(profile['name'] AS STRING) AS name FROM users会被解析为[{"name":"_key","type":"BYTES"}, {"name":"name","type":"STRING"}],与文档 Output Format 完全一致,bigtablelistschemas_test.go 的success用例也断言了同样结果。
适用边界:这是一个启发式解析器而非 SQL 引擎——它不执行查询、不解析 proto 结构字段((CAST(cf['p'] AS pkg.Product)).price只能得到列名、类型缺省),SELECT *会得到单个名为*的占位列(见 parse_test.go 的select *用例)。对 LLM 而言,这种“列名必得、类型可得时给出”的输出已经足以支撑后续的bigtable-sql查询构造。
与 Bigtable 工具族的协作
bigtable-list-schemas通常作为 Agent 工作流的第一步:先拿到表/视图全景,再决定如何查询。同目录下的相关工具文档可作配套参考:
- bigtable-list-tables、bigtable-get-table:本工具在“表”维度使用的正是其底层能力。
- bigtable-list-logical-views、bigtable-list-materialized-views:本工具已聚合了它们的列表输出,并额外提供了列 Schema。
- bigtable-sql:拿到
columns后,LLM 可直接基于列名/类型构造 GoogleSQL 查询。 - Bigtable 集成总览:查看该集成下全部 27 个工具与 source 要求。
小结
bigtable-list-schemas用一个只读工具调用解决了 Bigtable 元数据分散的问题:表列族来自实例级 AdminClient,视图定义来自项目级 InstanceAdminClient,而视图列 Schema 则由内置的 GoogleSQL 启发式解析器补齐。配置上只需一个已正确声明project/instance的bigtablesource 与一行type: bigtable-list-schemas;行为上记住limit(默认 20)只截断表列表、单表获取失败会降级保留表名、无法判定的列类型会以字段缺省形式省略。理解这三点后,你可以放心把它接入 Agent 的“先发现、后查询”工作流。
【免费下载链接】mcp-toolboxMCP Toolbox for Databases is an open source MCP server for databases.项目地址: https://gitcode.com/GitHub_Trending/ge/mcp-toolbox
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考