MCP Toolbox 中 singlestore-sql 工具实战:从参数化查询到向量检索
【免费下载链接】mcp-toolboxMCP Toolbox for Databases is an open source MCP server for databases.项目地址: https://gitcode.com/GitHub_Trending/ge/mcp-toolbox
本指南以 MCP Toolbox for Databases(开源 MCP 服务器,用于为 Agent 提供数据库能力)中面向 SingleStore 的singlestore-sql工具为主线,系统讲解如何通过 YAML 配置把一条预定义 SQL 语句暴露为可供 LLM/Agent 调用的 MCP 工具,并覆盖参数化查询防注入、模板参数的取舍,以及结合embeddingModel与JSON_ARRAY_PACK()/DOT_PRODUCT()实现语义向量检索的完整方案。读完本文,你将能够独立配置基于 SingleStore 的只读查询工具与向量检索工具,并理解其底层执行链路。
singlestore-sql 工具是什么
singlestore-sql是 MCP Toolbox 注册的、面向 SingleStore 数据库的预定义 SQL 工具类型(resourceType为"singlestore-sql",见 singlestoresql.go)。它的工作方式与postgres-sql、mysql等同类工具一致:在配置文件中把一条预先写好的 SQL 语句和它的参数声明绑定在一起,运行 MCP 服务器后,该配置会自动转化为一个 MCP Tool,Agent 只需要提供参数值即可完成查询。
它和仓库中另一个面向 SingleStore 的工具singlestore-execute-sql有本质区别:
| 工具类型 | 输入 | 适用场景 |
|---|---|---|
singlestore-sql | 预定义语句 + 声明参数 | 生产级 Agent 工作流,语句受控、可防注入 |
singlestore-execute-sql | 任意sql字符串参数 | 开发者辅助(human-in-the-loop)场景,官方明确提示不应用于生产 Agent |
后者在 singlestoreexecutesql.go 中会注册一个名为sql的字符串参数,并把用户输入的语句直接交给数据源执行;而singlestore-sql的语句是静态配置的,Agent 只能按声明好的参数填值。本文聚焦singlestore-sql。
兼容的数据源(Compatible Sources)
singlestore-sql要求其source指向实现了以下接口的数据源(见 singlestoresql.go):
type compatibleSource interface { SingleStorePool() *sql.DB RunSQL(context.Context, string, []any) (any, error) }即必须是类型为singlestore的 source(source 配置文档)。在工具初始化时,ValidateSource会校验数据源类型,不兼容会返回invalid source for "singlestore-sql" tool错误(singlestoresql.go)。
前置准备:配置 singlestore source
在配置工具之前,需要先在配置文件中声明一个kind: source、type: singlestore的数据源。以下是文档中给出的最小可运行示例(完整字段见 source.md):
kind: source name: my-singlestore-source type: singlestore host: 127.0.0.1 port: 3306 database: my_db user: ${USER_NAME} password: ${PASSWORD} queryTimeout: 30s # Optional: query timeout duration从源码看,该 source 的连接建立在 go-sql-driver/mysql 之上(SingleStore 兼容 MySQL 协议)。initSingleStoreConnectionPool会默认设置tls=preferred(服务器支持则启用 SSL/TLS,否则回退明文),并追加vector_type_project_format=JSON参数,这正是向量功能能工作的关键前提之一(singlestore.go)。
如果需要强制加密,可通过connectionParams覆盖:
kind: source name: my-singlestore-source type: singlestore host: svc-abc123.svc.singlestore.com port: 3306 database: my_db user: ${USER_NAME} password: ${PASSWORD} connectionParams: tls: "true" # Require TLS and verify the server certificatetls还支持"skip-verify"(要求 TLS 但跳过证书校验)和"false"(完全禁用)。connectionParams支持任何 go-sql-driver/mysql 的 DSN 参数,例如压缩等。
提示:密码等敏感信息请使用
${ENV_NAME}环境变量替换语法,不要硬编码进配置文件。
基本用法:参数化查询与防注入
singlestore-sql的核心配置项是statement(预定义 SQL)与parameters(参数声明)。官方示例(singlestore-sql.md)给出一个航班查询工具:
kind: tool name: search_flights_by_number type: singlestore-sql source: my-s2-instance statement: | SELECT * FROM flights WHERE airline = ? AND flight_number = ? LIMIT 10 description: | Use this tool to get information for a specific flight. Takes an airline code and flight number and returns info on the flight. Do NOT use this tool with a flight id. Do NOT guess an airline code or flight number. A airline code is a code for an airline service consisting of two-character airline designator and followed by flight number, which is 1 to 4 digit number. For example, if given CY 0123, the airline is "CY", and flight_number is "123". Another example for this is DL 1234, the airline is "DL", and flight_number is "1234". If the tool returns more than one option choose the date closes to today. Example: {{ "airline": "CY", "flight_number": "888", }} Example: {{ "airline": "DL", "flight_number": "1234", }} parameters: - name: airline type: string description: Airline unique 2 letter identifier - name: flight_number type: string description: 1 to 4 digit number关键要点:
- 占位符语法:SQL 语句中的参数使用
?占位符,与parameters列表中声明的参数按顺序一一对应。 - 防 SQL 注入:该工具使用参数化查询(prepared statement)执行语句。
parameters声明的参数会作为绑定值传给数据源,Agent 提供的任何输入都不会被拼接进 SQL 文本。 description是给 LLM 看的:它会被完整传递给模型(对应源码中tools.Manifest{Description: cfg.Description, ...}的构建逻辑,见 singlestoresql.go)。因此描述应当写清楚:何时使用该工具、参数含义、示例 JSON 调用、禁止行为等,示例中甚至示范了如何给出多组{{ "airline": "CY", "flight_number": "888" }}式的输入样例来引导模型。- 参数覆盖范围限制:参数化绑定值只能替换表达式,不能替换标识符、列名、表名等 SQL 结构。如果需要在语句中动态改变表名/列名,必须使用下面的
templateParameters。
参数化执行的底层链路
在 singlestoresql.go 的Invoke中,执行流程为:
params.AsMap()收集 Agent 传入的全部参数值;ResolveTemplateParams先解析模板参数(若存在);GetParams按声明顺序提取标准参数值;source.RunSQL(ctx, newStatement, sliceParams)以绑定参数形式执行查询。
而数据源侧的RunSQL(singlestore.go)使用QueryContext(ctx, statement, params...)执行,逐行扫描并把每行结果转换为map[列名]值的结构返回,列类型转换复用mysqlcommon.ConvertToType。也就是说,Agent 最终拿到的是一组 JSON 对象数组。
模板参数(templateParameters):灵活但需警惕注入
当语句需要动态插入表名、列名等 SQL 结构时,可以使用templateParameters。示例:
kind: tool name: list_table type: singlestore-sql source: my-s2-instance statement: | SELECT * FROM {{.tableName}}; description: | Use this tool to list all information from a specific table. Example: {{ "tableName": "flights", }} templateParameters: - name: tableName type: string description: Table to select from模板参数在 SQL 中以{{.参数名}}的形式书写,执行前会被替换进语句文本。
⚠️ 安全警告:模板参数允许直接修改 SQL 语句(包括标识符、列名、表名),因此比基本参数更容易遭受 SQL 注入。官方明确建议:出于性能与安全考虑,优先使用基本
parameters;只有确有必要时才使用templateParameters。更详细的说明见 tools 配置文档的 Template Parameters 一节。
从源码看,模板参数的处理发生在标准参数之前(ResolveTemplateParams先于GetParams),且模板参数不参与prepared statement 的绑定——它是先完成字符串替换、再把替换后的整条语句作为查询文本执行,这正是其注入风险的来源。
参数声明字段速查
parameters与templateParameters中的每个参数项支持以下字段(详见 tools/_index.md):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
name | string | 是 | 参数名 |
type | string | 是 | "string"、"integer"、"float"、"boolean"、"array"之一 |
description | string | 是 | 给 Agent 看的自然语言描述 |
default | 参数类型 | 否 | 默认值,提供后参数变为可选 |
required | bool | 否 | 是否必填,默认true |
allowedValues | []string | 否 | 允许值白名单,支持正则 |
excludedValues | []string | 否 | 排除值黑名单,支持正则 |
escape | string | 否 | 仅 string 类型,配合 templateParameters 使用,取single-quotes/double-quotes/backticks/square-brackets |
minValue/maxValue | int/float | 否 | 数值上下限(integer/float) |
secure | bool | 否 | 标记为安全参数(需协议版本2026-07-28及扩展),默认false |
向量检索:Embedding 与 JSON_ARRAY_PACK 的配合
SingleStore 原生支持向量操作,singlestore-sql工具因此可以组合出完整的「向量入库 + 语义检索」能力。核心机制是:当一个参数声明了embeddedBy引用某个embeddingModel时,工具会自动把该参数的文本转换为向量字符串数组(JSON 格式字符串数组),随后在 SQL 中用 SingleStore 的JSON_ARRAY_PACK()把它打包成二进制向量(BLOB)参与存储与相似度计算。
在 singlestoresql.go 中,工具覆写了EmbedParams,使用parameters.EmbedParams(..., embeddingmodels.FormatVectorForPgvector)对声明了embeddedBy的参数执行嵌入转换;而 source 侧 DSN 中默认追加的vector_type_project_format=JSON参数(singlestore.go)正是为了保证向量以 JSON 数组形式往返,从而能被JSON_ARRAY_PACK()正确解析。
第一步:定义 Embedding 模型
kind: embeddingModel name: gemini-model type: gemini model: gemini-embedding-001 apiKey: ${GOOGLE_API_KEY} dimension: 768完整的embeddingModel配置说明见 embedding-models 文档。
第二步:向量入库工具
下面的工具向vector_table写入原始文本及其向量表示。注意text_to_embed参数使用了valueFromParam: content:它并不要求 Agent 额外传值,而是自动复制content参数的值,再经embeddedBy: gemini-model转成向量——Agent 只需提供一次内容,向量转换逻辑对模型完全透明:
kind: tool name: insert_doc_singlestore type: singlestore-sql source: my-s2-source statement: | INSERT INTO vector_table (id, content, embedding) VALUES (1, ?, JSON_ARRAY_PACK(?)) description: | Index new documents for semantic search in SingleStore. parameters: - name: content type: string description: The text content to store. - name: text_to_embed type: string # Automatically copies 'content' and converts it to a vector string array valueFromParam: content embeddedBy: gemini-model语句中第一个?绑定原始文本content,第二个?绑定向量数组,经JSON_ARRAY_PACK(?)转为 BLOB 写入embedding列。
第三步:自然语言检索工具
检索工具把 Agent 的自然语言查询转换为向量,用DOT_PRODUCT()计算余弦相似度并返回最相似的结果:
kind: tool name: search_docs_singlestore type: singlestore-sql source: my-s2-source statement: | SELECT id, content, DOT_PRODUCT(embedding, JSON_ARRAY_PACK(?)) AS score FROM vector_table ORDER BY score DESC LIMIT 1 description: | Search for documents in SingleStore using natural language. Returns the most semantically similar result. parameters: - name: query type: string description: The search query to be converted to a vector. embeddedBy: gemini-model这里只有一个?,即query参数:Agent 输入一句自然语言(例如「What is the refund policy?」),工具自动嵌入为向量数组,JSON_ARRAY_PACK(?)打包后与库中所有向量的DOT_PRODUCT排序,取LIMIT 1返回语义最接近的文档。
注意:以上两个示例中的
vector_table表及其embedding列需要提前在 SingleStore 中创建好(可参考 SingleStore 官方向量类型与DOT_PRODUCT文档),工具本身不负责建表。
参考字段表
singlestore-sql工具配置的完整字段如下(来自 singlestore-sql.md 的 Reference,与源码中Config结构体 singlestoresql.go 一一对应):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
type | string | 是 | 必须为"singlestore-sql" |
source | string | 是 | 要执行 SQL 的 source 名称(需为singlestore类型) |
description | string | 是 | 工具描述,会传递给 LLM |
statement | string | 是 | 要执行的 SQL 语句 |
parameters | parameters | 否 | 将被插入 SQL 语句(以绑定参数方式)的参数列表 |
templateParameters | templateParameters | 否 | 在执行 prepared statement 之前插入 SQL 语句的模板参数列表 |
其中source、statement、description在源码中均带validate:"required"校验,且description为空会在初始化时报错description is required for tool(singlestoresql.go)。
快速体验:使用预构建配置
如果不想从零写配置,仓库还提供了 SingleStore 的预构建配置(见 prebuilt-configs/singlestore.md),可通过--prebuilt singlestore启动,只需设置以下环境变量:
SINGLESTORE_HOST:SingleStore 服务器主机名或 IPSINGLESTORE_PORT:端口号SINGLESTORE_DATABASE:数据库名SINGLESTORE_USER:数据库用户名SINGLESTORE_PASSWORD:数据库用户密码
预构建配置默认暴露execute_sql(执行 SQL)与list_tables(列出用户建表的结构信息)两个工具。需要更精确、更安全的行为时,则按本文方式自定义singlestore-sql工具。
总结
singlestore-sql把「SQL 语句 + 参数声明 + 向量嵌入」三者以声明式 YAML 的方式绑定为一个安全的 MCP 工具:默认的参数化查询保证 Agent 输入不会破坏 SQL 结构;templateParameters在必要时提供结构级灵活性但需谨慎使用;embeddedBy+valueFromParam+JSON_ARRAY_PACK()/DOT_PRODUCT()的组合则让 SingleStore 的向量能力以「自然语言进、语义结果出」的形式无缝暴露给 LLM。结合 source 配置 与 tools 通用参数规范,开发者可以快速搭建出面向 SingleStore 的安全查询与语义检索 Agent 工作流。
【免费下载链接】mcp-toolboxMCP Toolbox for Databases is an open source MCP server for databases.项目地址: https://gitcode.com/GitHub_Trending/ge/mcp-toolbox
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考