9. 连接远程 MCP 客户端
当服务器以 Streamable HTTP 或 SSE 方式运行时,您需要在 MCP 客户端配置中指定服务器 URL。
- Streamable HTTP :
http://<host>:<port>/mcp - SSE :
http://<host>:<port>/sse
其中 <host> 和 <port> 应与服务器启动参数一致。若服务器绑定 0.0.0.0,客户端需使用机器的实际 IP 或域名,并确保该主机名在 Host 白名单中。
示例(Claude Desktop 配置):
json
{
"mcpServers": {
"redisvl": {
"url": "http://192.168.1.10:8000/mcp",
"transport": "streamable-http"
}
}
}
10. 工具合同(Tool Contracts)
RedisVL MCP 向客户端暴露三个核心工具,以下是每个工具的请求/响应格式及行为说明。
10.1 list-indexes(发现可用索引)
无参数,返回所有配置的逻辑索引及其元数据。
响应示例:
json
{
"indexes": [
{
"id": "knowledge",
"description": "内部运行手册",
"upsert_available": true,
"fields": [
{ "name": "title", "type": "text" },
{ "name": "category", "type": "tag" },
{ "name": "rating", "type": "numeric" }
],
"limits": { "max_limit": 25 }
}
]
}
fields仅列出可用于过滤的字段(标签、数值、地理等),不含向量字段和用于嵌入的文本源字段。limits仅显示显式配置的运行时限制(默认值不输出)。- 底层的 Redis 索引名(
redis_name)不会暴露给客户端。
10.2 search-records(执行检索)
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
index |
string | 多索引时必填 | 逻辑索引 ID,单索引时可省略 |
query |
string | 是 | 检索词(向量搜索时为文本;纯向量也可为空,但一般使用查询文本) |
limit |
integer | 否 | 返回结果条数,默认使用配置的 default_limit |
offset |
integer | 否 | 分页偏移,默认为 0 |
filter |
string/object | 否 | 过滤条件,可为 Redis 原生过滤字符串,或 JSON DSL 对象 |
return_fields |
array | 否 | 指定返回的字段列表,默认返回所有非向量字段 |
请求示例(JSON DSL 过滤器):
json
{
"index": "knowledge",
"query": "incident response",
"limit": 2,
"filter": {
"and": [
{ "field": "category", "op": "eq", "value": "operations" },
{ "field": "rating", "op": "gte", "value": 4 }
]
},
"return_fields": ["title", "content"]
}
响应示例:
json
{
"index": "knowledge",
"search_type": "hybrid",
"offset": 0,
"limit": 2,
"results": [
{
"id": "knowledge:doc-123",
"score": 0.82,
"score_type": "hybrid_score",
"record": {
"title": "EU failover runbook",
"content": "Restore traffic after a regional failover.",
"category": "operations",
"rating": 5
}
}
]
}
search_type为响应元数据,反映配置的检索类型。- 返回的
record中不包含向量字段(为节约带宽和安全性)。 - 如果请求的
offset + limit超出max_result_window,请求会被拒绝。
10.3 upsert-records(写入或更新记录)
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
index |
string | 多索引时必填 | 逻辑索引 ID |
records |
array | 是 | 要写入的记录对象列表 |
id_field |
string | 否 | 记录中用作文档 ID 的字段名,默认使用随机 ID |
skip_embedding_if_present |
boolean | 否 | 是否跳过已有向量的重新生成,默认使用配置值 |
请求示例:
json
{
"index": "knowledge",
"records": [
{
"doc_id": "doc-42",
"content": "Updated operational guidance",
"category": "operations",
"rating": 5
}
],
"id_field": "doc_id"
}
响应:
json
{
"index": "knowledge",
"status": "success",
"keys_upserted": 1,
"keys": ["knowledge:doc-42"]
}
- 如果索引配置了
vectorizer,且记录中不包含向量字段,服务器会使用default_embed_text_field指定的源字段生成向量。 - 如果记录中已包含向量字段且
skip_embedding_if_present为true,则直接写入原向量。 - 若目标索引为只读(全局或单索引),写入请求会被拒绝。
11. 搜索与写入实战示例
11.1 多索引时的发现‑检索流程
在拥有多个索引的服务器上,客户端应首先调用 list-indexes 获取可用 ID,然后针对特定 ID 执行操作:
json
// 第一步:列出索引
// 请求:list-indexes 无参
// 响应中包含 knowledge 和 tickets
// 第二步:在 knowledge 中搜索
{
"index": "knowledge",
"query": "cache invalidation",
"limit": 3,
"return_fields": ["title", "content"]
}
11.2 纯向量检索
当 search.type 为 vector 时,只需提供查询文本,服务器会自动向量化并执行 KNN 搜索:
json
{
"query": "cache invalidation incident",
"limit": 3,
"return_fields": ["title", "content"]
}
11.3 混合检索(带权重配置)
在配置中设置 search.type: hybrid 并指定 combination_method: LINEAR 及 linear_text_weight: 0.3,则最终得分 = 0.3文本得分 + 0.7 向量得分。客户端请求与向量检索类似,但响应中的 score_type 会显示 hybrid_score。
11.4 过滤器使用
原始字符串过滤器(直接传递 Redis 查询语法):
json
{
"query": "science",
"filter": "@category:{science}",
"return_fields": ["content", "category"]
}
JSON DSL 过滤器 (更结构化,支持 and/or/not 及字段操作):
json
{
"query": "science",
"filter": {
"and": [
{ "field": "category", "op": "eq", "value": "science" },
{ "field": "rating", "op": "gte", "value": 4 }
]
}
}
11.5 分页与字段投影
通过 limit 和 offset 实现分页,return_fields 精确控制返回内容:
json
{
"query": "science",
"limit": 1,
"offset": 1,
"return_fields": ["content", "category"]
}
12. 写入(Upsert)进阶
12.1 自动生成向量(Server‑side Embedding)
当记录中缺少向量字段时,服务器自动调用配置的向量化器,将 default_embed_text_field 指定字段的文本转为向量并写入:
json
{
"records": [
{
"content": "First document",
"category": "science",
"rating": 5
}
]
}
12.2 使用 id_field 更新现有文档
若 id_field 指定的值在 Redis 中已存在,则该记录会被更新;否则创建新文档:
json
{
"records": [
{
"doc_id": "doc-1",
"content": "Updated content",
"category": "engineering"
}
],
"id_field": "doc_id"
}
12.3 控制向量重新生成
skip_embedding_if_present: true(默认):若记录中已包含vector_field_name字段,则直接使用该向量,不重新生成。skip_embedding_if_present: false:无论记录中是否有向量,都强制重新生成(覆盖原有向量)。
通常推荐让服务器管理向量,客户端只需提供源文本,避免传输大型向量。
12.4 纯文本索引的写入
如果索引只配置了全文检索(无 vectorizer 和 vector_field_name),则 upsert-records 仅写入文本字段,无需关心向量:
json
{
"records": [
{
"content": "New FAQ entry",
"category": "support"
}
]
}
13. 故障排查指南
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
rvl mcp 命令报缺少依赖 |
未安装 MCP 额外依赖 | 执行 pip install redisvl[mcp] |
| 启动失败,提示"Redis index does not exist" | 配置的 redis_name 在 Redis 中不存在 |
先在 Redis 中创建该索引(使用 RedisVL 或 FT.CREATE) |
| 启动失败,提示环境变量缺失 | YAML 中使用了 ${VAR} 但未定义 |
设置对应环境变量,或使用 ${VAR:-default} 提供默认值 |
| 启动失败,提示向量维度不匹配 | 向量化模型输出维度与 schema_overrides 或 Redis 索引中的维度不一致 |
检查模型维度(如 text-embedding-3-small 为 1536)并修正配置 |
| HTTP 请求被拒绝(403) | Host/Origin 校验未通过 | 检查是否添加了客户端实际使用的域名到 allowed_hosts 或 allowed_origins |
| 远程客户端连接不上 | 绑定了 127.0.0.1 或端口被防火墙拦截 |
使用 --host 0.0.0.0 并确保防火墙放行;同时正确配置 Host 白名单 |
upsert-records 返回 forbidden |
目标索引设置了 read_only: true 或全局 --read-only |
检查配置,若非必要请移除只读限制 |
| 混合检索结果不符合预期 | 可能缺少原生混合检索支持(旧版 Redis) | 升级 Redis 和 redis-py,或移除不支持的参数(如 knn_ef_runtime) |
14. 总结
- 配置先行:在启动服务器前,确保所有索引已在 Redis 中创建,且字段名称与配置完全匹配。
- 单索引 vs 多索引 :单索引配置最简洁,客户端无需指定
index;多索引适合为不同业务场景(如知识库、工单)提供统一接入,但客户端需先调用list-indexes进行发现。 - 安全 :生产环境绝对不要使用
--allow-unauthenticated绑定到公网接口。请启用 JWT 认证,并通过环境变量或配置文件严格限制 Host/Origin。 - 向量化成本 :若向量化服务(如 OpenAI)按量计费,请在
runtime中合理设置skip_embedding_if_present避免重复生成,同时限制max_upsert_records防止批量写入产生高昂费用。 - 分页与性能 :通过
max_result_window限制深度翻页,避免大偏移量查询拖垮 Redis。max_limit和max_upsert_records则防止单次请求数据量过大。
附录:服务器启动流程图
#mermaid-svg-7t7Db6ntItzc9oxR{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7t7Db6ntItzc9oxR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7t7Db6ntItzc9oxR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7t7Db6ntItzc9oxR .error-icon{fill:#552222;}#mermaid-svg-7t7Db6ntItzc9oxR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7t7Db6ntItzc9oxR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7t7Db6ntItzc9oxR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7t7Db6ntItzc9oxR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7t7Db6ntItzc9oxR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7t7Db6ntItzc9oxR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7t7Db6ntItzc9oxR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7t7Db6ntItzc9oxR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7t7Db6ntItzc9oxR .marker.cross{stroke:#333333;}#mermaid-svg-7t7Db6ntItzc9oxR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7t7Db6ntItzc9oxR p{margin:0;}#mermaid-svg-7t7Db6ntItzc9oxR .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7t7Db6ntItzc9oxR .cluster-label text{fill:#333;}#mermaid-svg-7t7Db6ntItzc9oxR .cluster-label span{color:#333;}#mermaid-svg-7t7Db6ntItzc9oxR .cluster-label span p{background-color:transparent;}#mermaid-svg-7t7Db6ntItzc9oxR .label text,#mermaid-svg-7t7Db6ntItzc9oxR span{fill:#333;color:#333;}#mermaid-svg-7t7Db6ntItzc9oxR .node rect,#mermaid-svg-7t7Db6ntItzc9oxR .node circle,#mermaid-svg-7t7Db6ntItzc9oxR .node ellipse,#mermaid-svg-7t7Db6ntItzc9oxR .node polygon,#mermaid-svg-7t7Db6ntItzc9oxR .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7t7Db6ntItzc9oxR .rough-node .label text,#mermaid-svg-7t7Db6ntItzc9oxR .node .label text,#mermaid-svg-7t7Db6ntItzc9oxR .image-shape .label,#mermaid-svg-7t7Db6ntItzc9oxR .icon-shape .label{text-anchor:middle;}#mermaid-svg-7t7Db6ntItzc9oxR .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7t7Db6ntItzc9oxR .rough-node .label,#mermaid-svg-7t7Db6ntItzc9oxR .node .label,#mermaid-svg-7t7Db6ntItzc9oxR .image-shape .label,#mermaid-svg-7t7Db6ntItzc9oxR .icon-shape .label{text-align:center;}#mermaid-svg-7t7Db6ntItzc9oxR .node.clickable{cursor:pointer;}#mermaid-svg-7t7Db6ntItzc9oxR .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7t7Db6ntItzc9oxR .arrowheadPath{fill:#333333;}#mermaid-svg-7t7Db6ntItzc9oxR .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7t7Db6ntItzc9oxR .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7t7Db6ntItzc9oxR .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7t7Db6ntItzc9oxR .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7t7Db6ntItzc9oxR .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7t7Db6ntItzc9oxR .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7t7Db6ntItzc9oxR .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7t7Db6ntItzc9oxR .cluster text{fill:#333;}#mermaid-svg-7t7Db6ntItzc9oxR .cluster span{color:#333;}#mermaid-svg-7t7Db6ntItzc9oxR div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7t7Db6ntItzc9oxR .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7t7Db6ntItzc9oxR rect.text{fill:none;stroke-width:0;}#mermaid-svg-7t7Db6ntItzc9oxR .icon-shape,#mermaid-svg-7t7Db6ntItzc9oxR .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7t7Db6ntItzc9oxR .icon-shape p,#mermaid-svg-7t7Db6ntItzc9oxR .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7t7Db6ntItzc9oxR .icon-shape .label rect,#mermaid-svg-7t7Db6ntItzc9oxR .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7t7Db6ntItzc9oxR .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7t7Db6ntItzc9oxR .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7t7Db6ntItzc9oxR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 任一失败
全部通过
启动 rvl mcp
解析 CLI 参数与环境变量
加载 YAML 配置文件
遍历 indexes 逐个验证
启动失败,记录错误
初始化索引管理器、向量化器、工具注册表
设置传输层(stdio/HTTP/SSE)
配置传输安全(Host/Origin/JWT)
绑定地址并开始监听
服务器就绪,等待 MCP 请求
通过以上步骤,能够顺利部署并运行 RedisVL MCP 服务器,将 Redis 的强大检索能力以标准化工具的形式提供给您的智能体或应用。