使用 spanner-list-graphs 工具查询 Cloud Spanner 属性图 Schema 元数据:MCP Toolbox 完整实战指南
【免费下载链接】mcp-toolboxMCP Toolbox for Databases is an open source MCP server for databases.项目地址: https://gitcode.com/GitHub_Trending/ge/mcp-toolbox
本文基于 MCP Toolbox 开源仓库(ge/mcp-toolbox)深入讲解spanner-list-graphs工具的完整用法:从 YAML 配置、参数语义、INFORMATION_SCHEMA.PROPERTY_GRAPHS底层查询实现,到 simple/detailed 两种输出格式的逐字段解析,以及将其接入 LLM Agent 的高级示例。读完本文,你将掌握如何通过 MCP 协议以只读方式获取 Spanner 数据库中属性图(Property Graph)的节点表、边表、标签与属性声明等完整元数据,并理解 GoogleSQL 方言限制背后的原因。
工具概览
spanner-list-graphs是 MCP Toolbox 中面向 Google Cloud Spanner 的内置工具,用于检索数据库中用户创建的属性图的完整 Schema 信息。它通过执行预定义的 SQL 查询,从INFORMATION_SCHEMA系统表中收集元数据,并返回结构化的 JSON,包含:
- 节点表(node tables):图中作为节点的基表及其关键列、标签与属性定义;
- 边表(edge tables):图中作为边的基表、源/目标节点表映射、键列与属性定义;
- 标签(labels):节点与边对应的标签名称及其属性声明列表;
- 属性声明(property declarations):图中各属性的名称与数据类型。
该工具由 spannerlistgraphs.go 实现。从源码结构看,它通过tools.Register("spanner-list-graphs", newConfig)注册到工具注册表(见resourceType常量与init()),属于标准的只读发现类工具,与同源族的spanner-list-tables、spanner-sql、spanner-search-catalog并列。
核心特性
- 全面 Schema 信息:一次查询即可返回节点表、边表、标签和属性声明,覆盖属性图元数据的绝大部分维度;
- 灵活过滤:支持列出全部图,也支持通过逗号分隔的图名列表精确过滤指定图;
- 输出格式可选:
simple模式只返回图名列表,detailed模式返回完整 Schema 信息(默认值)。
典型使用场景
- 数据库文档生成:基于真实 Schema 自动生成数据库结构文档;
- Schema 校验:验证期望的图、节点表与边表是否真实存在;
- 迁移规划:在进行结构变更前完整了解当前 Schema 状态;
- 开发工具:构建需要感知数据库结构的辅助工具(如代码生成器、数据探查器);
- 审计与合规:跟踪 Schema 变更,满足数据治理策略的合规要求。
使用前提与关键限制
仅支持 GoogleSQL 方言
重要警告:
spanner-list-graphs仅适用于GoogleSQL方言的 Spanner 数据库,因为 Spanner Graph(属性图)功能在 PostgreSQL 方言中不可用。
这一限制不仅是文档声明,也在源码中被强制执行:工具在Invoke中通过source.DatabaseDialect()在运行时检查方言,若返回值不等于googlesql,会直接返回 Agent 错误:
operation not supported: The 'spanner-list-graphs' tool is only available for GoogleSQL dialect databases. Your current database dialect is '...'对应代码位于 spannerlistgraphs.go。注释明确说明检查放在运行时而非启动时,因此你可以在同一个配置文件中声明多个 Spanner source,只要 GoogleSQL 方言的 source 才会被该工具兼容。
IAM 权限
工具以只读方式执行查询,运行 MCP Toolbox 的 IAM 身份需要具备以下权限(来自 spanner-googlesql-dialect.md):
- Cloud Spanner Database Reader(
roles/spanner.databaseReader):执行 DQL 查询与列出表/图所需的最小权限; - 若还需通过其他工具执行 DML,需要Cloud Spanner Database User(
roles/spanner.databaseUser)。
Toolbox 使用 Application Default Credentials (ADC) 进行认证与授权,配置服务器时需先完成 ADC 设置,并为身份授予相应 IAM 角色。
配置 Source 与 Tool
定义 Spanner 数据源
首先在 YAML 配置中定义一个type: spanner的 source。字段说明(完整参考见 source.md):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| type | string | 是 | 必须为"spanner" |
| project | string | 是 | GCP 项目 ID(如"my-project-id") |
| instance | string | 是 | Spanner 实例名称 |
| database | string | 是 | 实例上的数据库名称 |
| dialect | string | 否 | 必须为googlesql或postgresql,默认googlesql |
在 spanner.go 中可以看到,newConfig会在解码前把Dialect默认值预置为googlesql,因此省略dialect字段的 source 天然满足本工具的要求。
基础用法——列出所有图
kind: source name: my-spanner-db type: spanner project: ${SPANNER_PROJECT} instance: ${SPANNER_INSTANCE} database: ${SPANNER_DATABASE} dialect: googlesql # wont work for postgresql --- kind: tool name: list_all_graphs type: spanner-list-graphs source: my-spanner-db description: Lists all graphs with their complete schema information列出指定图
kind: tool name: list_specific_graphs type: spanner-list-graphs source: my-spanner-db description: | Lists schema information for specific graphs. Example usage: { "graph_names": "FinGraph,SocialGraph", "output_format": "detailed" }工具字段参考(Reference)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| type | string | 是 | 必须为"spanner-list-graphs" |
| source | string | 是 | 要查询的 Spanner source 名称(方言必须为 GoogleSQL) |
| description | string | 否 | 传给 LLM 的工具描述 |
| authRequired | string[] | 否 | 调用该工具所需的认证服务列表 |
spannerlistgraphs_test.go中的TestParseFromYamlListGraphs用例验证了三种配置形态的解析结果:最小配置(仅 name/type/source)、带description的常规配置,以及带authRequired的认证配置——这为上述字段提供了测试级佐证(见 spannerlistgraphs_test.go)。
参数详解
工具接受两个可选参数(定义见 spannerlistgraphs.go):
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| graph_names | string | "" | 逗号分隔的图名列表,用于过滤;为空时列出用户可访问 Schema 中的所有图 |
| output_format | string | "detailed" | 输出格式:simple仅返回图名,detailed返回完整 Schema 信息 |
源码层面的默认值处理逻辑:
graph_names通过parameters.NewStringParameter声明,默认空字符串;output_format默认"detailed",在Invoke中还会再次兜底:if outputFormat == "" { outputFormat = "detailed" }(spannerlistgraphs.go),即使 LLM 传入空值也不会出现未定义格式。
两个参数连同查询语句一起作为参数绑定传入source.RunSQL:
stmtParams := map[string]interface{}{ "graph_names": graphNames, "output_format": outputFormat, } resp, err := source.RunSQL(ctx, true, googleSQLStatement, stmtParams)注意这里的readOnly=true,结合 spanner.go 中RunSQL的实现:只读路径使用s.SpannerClient().Single().Query(ctx, stmt)(单次只读快照事务)执行查询,从机制上保证该工具不会修改任何数据。
底层实现:INFORMATION_SCHEMA 查询剖析
工具的查询逻辑集中在一个 GoogleSQL 语句中(spannerlistgraphs.go),核心分为三部分:
1. 图名过滤 CTE
WITH FilterGraphNames AS ( SELECT DISTINCT TRIM(name) AS GRAPH_NAME FROM UNNEST(IF(@graph_names = '' OR @graph_names IS NULL, ['%'], SPLIT(@graph_names, ','))) AS name )当graph_names为空时,CTE 生成单个通配符'%';否则按逗号拆分并去除首尾空白,得到目标图名集合。
2. 从系统表读取元数据
FROM INFORMATION_SCHEMA.PROPERTY_GRAPHS PGINFORMATION_SCHEMA.PROPERTY_GRAPHS是 Spanner 内置的属性图元数据视图,每一行代表一个属性图,PROPERTY_GRAPH_METADATA_JSON列保存了节点表(nodeTables)、边表(edgeTables)、标签(labels)与属性声明(propertyDeclarations)的 JSON 结构。
3. 按输出格式拼装 JSON
CASE WHEN @output_format = 'simple' THEN CONCAT('{"name":"', IFNULL(REPLACE(PG.PROPERTY_GRAPH_NAME, '"', '\"'), ''), '"}') ELSE CONCAT( '{', '"schema_name":"', IFNULL(PG.PROPERTY_GRAPH_SCHEMA, ''), '",', '"object_name":"', IFNULL(PG.PROPERTY_GRAPH_NAME, ''), '",', '"catalog":"', IFNULL(JSON_VALUE(PG.PROPERTY_GRAPH_METADATA_JSON,"$.catalog"), ''), '",', '"node_tables":', TO_JSON_STRING(PG.PROPERTY_GRAPH_METADATA_JSON.nodeTables), ',', '"edge_tables":', TO_JSON_STRING(PG.PROPERTY_GRAPH_METADATA_JSON.edgeTables), ',', '"labels":', TO_JSON_STRING(PG.PROPERTY_GRAPH_METADATA_JSON.labels), ',', '"property_declarations":', TO_JSON_STRING(PG.PROPERTY_GRAPH_METADATA_JSON.propertyDeclarations), '}' ) END AS object_detailsschema_name/object_name直接取自系统表的PROPERTY_GRAPH_SCHEMA与PROPERTY_GRAPH_NAME列;catalog通过JSON_VALUE从元数据 JSON 提取;而节点表、边表、标签与属性声明则通过TO_JSON_STRING序列化为 JSON 字符串。
4. 结果行的 JSON 反序列化
查询结果经RunSQL返回前,会经过 spanner.go 中processRows的特殊处理:当列名是object_details时,将字符串内容json.Unmarshal成map[string]any后再加入结果行。这样最终返回给调用方(LLM/Agent)的就是可直接消费的嵌套 JSON 对象,而不是一段需要二次解析的字符串。
输出格式详解
Simple 格式
当output_format为"simple"时,仅返回图的名称,JSON 结构最小化:
[ { "object_details": { "name": "FinGraph" }, "object_name": "FinGraph", "schema_name": "" }, { "object_details": { "name": "SocialGraph" }, "object_name": "SocialGraph", "schema_name": "" } ]Detailed 格式(默认)
当output_format为"detailed"(默认值)时,返回完整 Schema 信息。以下示例展示了一个名为SocialGraph的属性图,包含一个节点表Person与一条边表Knows:
[ { "object_details": { "catalog": "", "edge_tables": [ { "baseCatalogName": "", "baseSchemaName": "", "baseTableName": "Knows", "destinationNodeTable": { "edgeTableColumns": [ "DstId" ], "nodeTableColumns": [ "Id" ], "nodeTableName": "Person" }, "keyColumns": [ "SrcId", "DstId" ], "kind": "EDGE", "labelNames": [ "Knows" ], "name": "Knows", "propertyDefinitions": [ { "propertyDeclarationName": "DstId", "valueExpressionSql": "DstId" }, { "propertyDeclarationName": "SrcId", "valueExpressionSql": "SrcId" } ], "sourceNodeTable": { "edgeTableColumns": [ "SrcId" ], "nodeTableColumns": [ "Id" ], "nodeTableName": "Person" } } ], "labels": [ { "name": "Knows", "propertyDeclarationNames": [ "DstId", "SrcId" ] }, { "name": "Person", "propertyDeclarationNames": [ "Id", "Name" ] } ], "node_tables": [ { "baseCatalogName": "", "baseSchemaName": "", "baseTableName": "Person", "keyColumns": [ "Id" ], "kind": "NODE", "labelNames": [ "Person" ], "name": "Person", "propertyDefinitions": [ { "propertyDeclarationName": "Id", "valueExpressionSql": "Id" }, { "propertyDeclarationName": "Name", "valueExpressionSql": "Name" } ] } ], "object_name": "SocialGraph", "property_declarations": [ { "name": "DstId", "type": "INT64" }, { "name": "Id", "type": "INT64" }, { "name": "Name", "type": "STRING" }, { "name": "SrcId", "type": "INT64" } ], "schema_name": "" }, "object_name": "SocialGraph", "schema_name": "" } ]对返回结构的关键字段解读:
| 字段 | 含义 |
|---|---|
object_name/schema_name | 属性图名称及其所属 Schema(用户 Schema 时通常为空字符串) |
node_tables[].kind | 表角色,节点表为NODE,边表为EDGE |
node_tables[].keyColumns | 表的键列(如Person的Id) |
edge_tables[].sourceNodeTable/destinationNodeTable | 边的源/目标节点表,包含节点表名及对应的节点表列与边表列映射(如Knows用SrcId→Person.Id表示起点) |
edge_tables[].keyColumns | 边的键列组合(如["SrcId", "DstId"]) |
labels[].propertyDeclarationNames | 该标签关联的属性声明列表 |
property_declarations[] | 图中所有属性的名称与类型(如INT64、STRING) |
高级用法:接入 LLM Agent
将spanner-list-graphs接入 Agent 时,description字段会作为 LLM 的工具说明被传递,因此编写清晰、带示例的 description 能显著提升模型正确调用工具的准确率:
kind: source name: spanner-db type: spanner project: my-project instance: my-instance database: my-database dialect: googlesql --- kind: tool name: schema_inspector type: spanner-list-graphs source: spanner-db description: | Use this tool to inspect database schema information. You can: - List all graphs by leaving graph_names empty - Get specific graph schemas by providing comma-separated graph names - Choose between simple (names only) or detailed (full schema) output Examples: 1. List all graphs with details: {"output_format": "detailed"} 2. Get specific graphs: {"graph_names": "FinGraph,SocialGraph", "output_format": "detailed"} 3. Just get graph names: {"output_format": "simple"}若description为空,源码会注入一段默认描述(spannerlistgraphs.go),确保 LLM 始终能理解工具用途:
"Lists detailed graph schema information (node tables, edge tables, labels and property declarations) as JSON for user-created graphs. Filters by a comma-separated list of graph names. If names are omitted, lists all graphs. The output can be 'simple' (graph names only) or 'detailed' (full schema)."
使用预构建配置快速接入
MCP Toolbox 提供了现成的预构建配置(--prebuilt spanner),其中已包含list_graphs工具(见 spanner.yaml),并声明了所需环境变量与权限:
- 环境变量:
SPANNER_PROJECT(GCP 项目 ID)、SPANNER_INSTANCE(实例 ID)、SPANNER_DATABASE(数据库 ID)、SPANNER_DIALECT(默认googlesql); - 权限:
roles/spanner.databaseReader(执行 DQL 与列出表/图); - 工具集:
list_graphs与其他工具一同打包在data工具组与data_with_discovery工具集中,适合与execute_sql_readonly、list_tables组合实现"探索 + 查询"的完整 Agent 工作流。
完整说明见 spanner-googlesql-dialect.md。
验证与测试
仓库中针对该工具提供了解析级单元测试TestParseFromYamlListGraphs(spannerlistgraphs_test.go),覆盖三种配置输入:
- 基础示例:
kind: tool+type: spanner-list-graphs+source+description,验证解析出的Config各字段; - 带认证:
authRequired列表被正确解析; - 最小配置:仅
name/type/source,description为空、AuthRequired为空切片。
这些用例可作为自定义配置时的回归参考,也可用于理解server.UnmarshalPrimitiveConfig的解析链路。
故障排查
- 只读保证:该工具是只读的,通过单次只读快照事务(
Single().Query)执行,不会修改任何数据; - 方言限制:仅支持 GoogleSQL 方言,PostgreSQL 方言数据库会在运行时返回明确的操作不支持错误;
- 查询性能:大型数据库中图数量较多时,查询耗时可能更长——
detailed模式需要为每个图序列化完整的元数据 JSON,若仅需图名列表,优先使用simple模式以减少返回体量。
【免费下载链接】mcp-toolboxMCP Toolbox for Databases is an open source MCP server for databases.项目地址: https://gitcode.com/GitHub_Trending/ge/mcp-toolbox
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考