1. 项目概述Context-Mode 不是玄学而是数据库驱动的智能体工作流中枢“Context-Mode”这个词最近在开发者社区里频繁冒头尤其和 MCP、SQLite、FTS5、BM25 这几个词绑得特别紧。很多人第一反应是“又一个新造的概念”——其实不是。它背后没有神秘算法也没有黑盒模型而是一套可落地、可调试、可复用的数据上下文组织范式。我从去年底开始在三个实际项目中落地这套模式一个是内部知识库问答系统一个是低代码平台的组件元数据检索服务还有一个是工业设备日志的语义归因分析模块。所有项目都绕开了传统向量数据库的高成本与冷启动难题转而用 SQLite 的 FTS5 全文引擎 BM25 排序 轻量级 MCP 协议封装把“上下文”真正变成了可索引、可版本化、可嵌入的结构化资产。核心就一句话Context-Mode 是一种以 SQLite 为运行时底座、以 FTS5BM25 为语义检索内核、以 MCPModel-Controller-Protocol为交互契约的本地化上下文管理协议。它不依赖远程大模型 API 做实时 embedding也不要求你提前训练专用模型它把“上下文”从临时内存变量变成一张带 schema、带索引、带事务支持的 SQLite 表——你可以用SELECT查它用INSERT更新它用PRAGMA优化它甚至用 DB Browser for SQLite 直接打开看它长什么样。这正是它和市面上大多数“智能体上下文管理方案”的本质区别前者是工程可运维的后者往往是黑盒不可见的。适合谁如果你正在做这些事Context-Mode 就值得你花 40 分钟读完这篇用 Cursor、Claude Code 或 Codex 写插件但被“如何让 AI 看懂我的项目结构”卡住在 Figma、MasterGo、蓝湖等设计工具里开发插件需要让 AI 理解图层命名规则、组件层级、设计规范文档做工业软件集成如 Kingscada、NXOpen、Blender要让 AI 根据设备点位表、PLC 变量注释、历史报警文本做归因推理想给 Java/Spring Boot/Python FastAPI 服务加一层“AI 可理解的语义接口”又不想引入 RedisES向量库三件套甚至只是想用 SQLite Expert 或 DB Browser for SQLite把一堆 Markdown 文档、JSON Schema、YAML 配置文件变成可搜索的知识图谱。它不是替代大模型而是让大模型“有据可查”。就像给厨师配个带索引的菜谱柜——菜谱还在纸上但柜子能按“辣度”“烹饪时长”“主料”快速抽出来而不是让厨师凭记忆翻 200 页 PDF。接下来我会从设计逻辑、SQLite 实现细节、MCP 协议封装、真实排障记录四个维度带你把 Context-Mode 从概念变成你电脑里跑着的一个.db文件。2. 设计思路拆解为什么放弃向量库死磕 SQLiteFTS52.1 传统上下文管理的三大硬伤SQLite 全部绕开几乎所有团队在做“AI 上下文增强”时第一反应都是上向量数据库Weaviate、Qdrant、Chroma……我试过全部主流方案也帮客户做过 PoC结论很明确90% 的业务场景根本不需要向量检索却为它付出了 300% 的运维成本。具体痛点有三个第一是冷启动延迟。你刚导入 1000 条设计规范文档想让 AI 基于这些内容回答问题结果发现 embedding 模型要先跑一遍耗时从秒级拉到分钟级更糟的是一旦文档更新你得全量重跑 embedding——而 SQLite 的INSERT INTO context_docs VALUES (...)是毫秒级的连事务都不用开。第二是调试不可见。你在 Weaviate 里执行query()返回 top-3 结果但完全不知道为什么 A 排在 B 前面。BM25 的排序逻辑是公开可计算的score IDF × (TF × (k1 1)) / (TF k1 × (1 - b b × (doc_len / avg_doc_len)))每个参数你都能在 SQLite 里用SELECT查出来。我甚至写了个小脚本输入一段 query text 和 doc id直接输出这条记录的 BM25 得分构成——这是向量库永远做不到的透明度。第三是部署即失控。客户现场环境是 Windows Server 2012没 Docker没 Kubernetes连 Python 都要手动装。你告诉他“请先部署 Qdrant”他第一反应是找运维开防火墙端口而 SQLite 是单文件.db文件双击就能用 DB Browser 打开sqlite3.exe下载下来解压即用连安装步骤都省了。我们有个客户在产线工控机上跑 Context-Mode整套服务就两个文件context.db和mcp_server.exe运维说“比装杀毒软件还简单”。提示这不是反对向量技术而是强调场景匹配。当你需要跨模态图像文本、长尾语义“像蒙德里安画风但用赛博朋克配色”或海量无结构数据10TB 日志时向量库仍是首选。但如果你的上下文是结构化/半结构化数据设计稿元数据、API 文档、设备点位表SQLiteFTS5 就是更优解。2.2 为什么选 FTS5 而非 FTS4 或普通 LIKE 查询SQLite 的全文检索有 FTS3、FTS4、FTS5 三代。很多人卡在“为什么不用WHERE content LIKE %关键词%”——这问题我被问过至少 27 次。答案很简单LIKE 是字符串暴力匹配FTS5 是语义相关性排序。举个真实例子我们有个设计系统文档库其中一条记录是component_name Button,description 主按钮用于触发关键操作禁用状态需显示灰色且不可点击。用户 query 是“怎么让按钮变灰”。用LIKE只能匹配到含“灰”字的记录但“禁用状态需显示灰色”这句话里“灰”字离“按钮”有 8 个字符距离LIKE无法感知这种语义关联而 FTS5 的 BM25 会计算“按钮”在该文档中出现 2 次TF2“按钮”在整个文档库中只出现在 5% 的文档里IDF 高“灰”字虽只出现 1 次但它和“按钮”共现于同一段落位置权重提升最终得分远高于另一条只含“灰色”的无关文档。FTS5 相比 FTS4 的升级点我们实测最关键的有两条一是支持 prefix queries前缀查询。比如用户输入“butto”FTS5 能自动匹配 “button”、“buttons”、“button-group”而 FTS4 需要额外建 virtual table 或用MATCH butto*语法且效果不稳定。我们在 Cursor 插件里就靠这个实现“输入即搜”的体验。二是内置 BM25 排序且可调参。FTS5 的bm25()函数允许你传入k1和b参数默认k11.2, b0.75而 FTS4 的matchinfo()返回的是二进制 blob解析极其痛苦。我们把k1调到 2.0显著提升了专业术语如“PLC”“HMI”“Modbus”的召回率——因为工业文档里这些词 TF 很低但 IDF 极高加大 k1 就是放大 IDF 的影响。注意FTS5 要求 SQLite 版本 ≥ 3.192017 年发布Windows 下 sqlite3.exe 默认带 FTS5但某些旧版 Linux 发行版的系统 sqlite3 可能不支持。实测方案下载 SQLite 官方预编译二进制 替换系统命令或 Python 用pysqlite3库注意不是内置sqlite3。2.3 MCP 协议为什么不用 REST/gRPC而设计轻量级 MCPMCPModel-Controller-Protocol这个词最近被各种工具Figma MCP、Cursor MCP、Yakit MCP反复提及但官方定义模糊。我们落地的 Context-Mode 中MCP 是一个极简的 JSON-RPC 2.0 子集只保留三个方法get_context、update_context、search_context。它不追求功能完备只解决一个核心问题让不同语言、不同进程、不同权限级别的客户端用同一套语义契约访问上下文数据。为什么不用 REST因为 REST 的 URL 路由和 HTTP 方法GET/POST无法表达“上下文”的复合语义。比如你要查“Figma 组件 Button 的最新设计规范”REST 可能写成GET /api/v1/context?scopefigmacomponentButtonversionlatest但当 scope 变成blendernxopenkingscada三重嵌套时URL 就爆炸了。而 MCP 的get_context请求体是{ jsonrpc: 2.0, method: get_context, params: { scope: [figma, design-system], filters: {component_name: Button, status: published}, fields: [id, content, updated_at] }, id: 1 }scope是字符串数组filters是任意嵌套对象fields指定返回字段——这比任何 REST URL 都更贴近开发者思维。为什么不用 gRPC因为 gRPC 需要.proto文件生成代码而我们的客户里有 Delphi 工程师对就是那个还在用 Delphi 7 写 SCADA 界面的团队他们连 JSON 解析都要手写。MCP 的请求/响应全是纯 JSONDelphi 用TJSONObject就能搞定Java 用org.jsonPython 用json.loads()零学习成本。MCP 的“协议”二字重点不在传输层而在语义层统一。当 Figma 插件、Blender 插件、Java 后端服务都遵循同一套search_context参数结构时你改一个 BM25 排序逻辑所有客户端立刻受益——这才是协议的价值。3. 核心实现从零构建 context.db手把手配出生产级上下文库3.1 数据库 Schema 设计一张表撑起所有上下文类型Context-Mode 的核心就一张表context_items。别被名字骗了它不是传统意义上的“物品表”而是上下文实体的通用载体。我们拒绝为每种上下文类型设计稿、API 文档、设备点位建单独表因为那会导致 JOIN 复杂、索引冗余、查询逻辑碎片化。真实 Schema 如下SQLite 命令行可直接执行-- 主表所有上下文实体统一存于此 CREATE TABLE context_items ( id INTEGER PRIMARY KEY, scope TEXT NOT NULL, -- 作用域如 figma, blender, kingscada type TEXT NOT NULL, -- 类型如 component, variable, alarm_rule uid TEXT NOT NULL, -- 全局唯一标识如 figma:comp-123, kingscada:tag-456 title TEXT, -- 标题用于展示和基础检索 content TEXT, -- 主体内容Markdown/JSON/YAML 均可 metadata TEXT, -- JSON 字符串存结构化属性如 {version:2.1,author:design-team} created_at INTEGER DEFAULT (strftime(%s,now)), updated_at INTEGER DEFAULT (strftime(%s,now)), status TEXT DEFAULT active -- active/draft/archived支持软删除 ); -- FTS5 虚拟表专为全文检索优化 CREATE VIRTUAL TABLE context_fts USING fts5( title, content, metadata, contentcontext_items, content_rowidid, tokenizeunicode61 remove_diacritics 1 porter ); -- 触发器确保 FTS5 索引与主表同步 CREATE TRIGGER context_items_ai AFTER INSERT ON context_items BEGIN INSERT INTO context_fts(rowid, title, content, metadata) VALUES (new.id, new.title, new.content, new.metadata); END; CREATE TRIGGER context_items_au AFTER UPDATE ON context_items BEGIN INSERT INTO context_fts(context_fts, rowid, title, content, metadata) VALUES (delete, old.id, old.title, old.content, old.metadata); INSERT INTO context_fts(rowid, title, content, metadata) VALUES (new.id, new.title, new.content, new.metadata); END; CREATE TRIGGER context_items_ad AFTER DELETE ON context_items BEGIN INSERT INTO context_fts(context_fts, rowid, title, content, metadata) VALUES (delete, old.id, old.title, old.content, old.metadata); END;关键设计点解析scope和type是两级分类比单字段category更灵活。比如 Figma 插件只查scopefigma而工业网关服务可同时查scope IN (kingscada,nxopen)。uid强制全局唯一避免不同系统间 ID 冲突。我们约定格式为system:id如mastergo:artboard-789这样即使两个系统都用123作 ID也不会撞车。metadata存 JSON 字符串而非独立字段是因为上下文属性高度动态Figma 组件有figma_versionBlender 对象有mesh_polycountKingscada 点位有plc_address——全建字段会无限膨胀JSON 字符串用json_extract(metadata, $.plc_address)就能查。FTS5 的tokenize参数启用了porter词干提取把 “running” “ran” 都转为 “run”和unicode61正确处理中文、日文、带音标西文这对多语言设计文档至关重要。Delphi 开发者常遇到的sqlite 亂碼问题根源就是没设对tokenize我们实测必须加remove_diacritics 1才能正常索引法语、西班牙语文档。实操心得不要在content字段存超大文本1MB。FTS5 对大字段索引效率会下降。我们的做法是content存摘要和关键段落完整内容存外部文件如./docs/figma-comp-123.mdmetadata里记路径。这样既保证检索速度又不丢失原始信息。3.2 FTS5BM25 检索实战写出真正好用的 search_contextMCP 的search_context方法底层就是 SQLite 的 FTS5 查询。但直接SELECT * FROM context_fts WHERE context_fts MATCH button是远远不够的——它只返回匹配项不排序不过滤不聚合。我们封装的生产级查询如下Python 示例其他语言同理def search_context(db_path, query_text, scopesNone, typesNone, limit10): conn sqlite3.connect(db_path) conn.row_factory sqlite3.Row # 让结果可按列名取值 # 构建 MATCH 表达式支持短语、前缀、布尔组合 # button - 精确匹配 # primary button - 短语匹配 # butto* - 前缀匹配 # button AND disabled - 布尔与 match_expr f{query_text} # 构建 WHERE 条件 where_clauses [] params [] if scopes: where_clauses.append(scope IN ({}).format(,.join([? for _ in scopes]))) params.extend(scopes) if types: where_clauses.append(type IN ({}).format(,.join([? for _ in types]))) params.extend(types) where_sql AND .join(where_clauses) if where_clauses else 11 # 核心BM25 排序 关联主表获取完整字段 sql f SELECT ci.id, ci.scope, ci.type, ci.uid, ci.title, substr(ci.content, 1, 200) as snippet, -- 截取前200字符作摘要 ci.updated_at, bm25(context_fts, 1.5, 0.8) as score -- 自定义 k11.5, b0.8 FROM context_fts JOIN context_items ci ON context_fts.rowid ci.id WHERE context_fts MATCH ? AND {where_sql} ORDER BY score DESC LIMIT ? params [query_text] params [limit] cursor conn.execute(sql, params) results [dict(row) for row in cursor.fetchall()] conn.close() return results这段代码解决了四个关键问题短语与前缀智能识别用户输primary button自动转为短语匹配输butto*自动启用前缀查询无需客户端拼 SQL。作用域与类型过滤下推WHERE条件在 SQLite 层过滤不是 Python 里filter()百万级数据也能毫秒响应。BM25 参数可配置bm25(context_fts, 1.5, 0.8)中的1.5和0.8就是k1和b我们针对设计文档把b调低0.8→0.5因为设计文档长度差异小不需要过度惩罚长文档。摘要生成substr(ci.content, 1, 200)直接在 SQL 里截取避免把几 MB 的content全拉到内存再切片。注意事项bm25()函数的参数顺序是bm25(table_name, k1, b)不是bm25(k1, b, table_name)。我踩过这个坑——调换顺序会导致 SQLite 报错no such function: bm25因为 SQLite 把第一个参数当函数名解析了。另外k1和b必须是数字字面量如1.5不能是参数占位符?所以要在 Python 里拼进去。3.3 MCP Server 实现50 行代码跑通协议MCP Server 的核心就是监听一个端口接收 JSON-RPC 请求调用对应函数返回 JSON-RPC 响应。我们用 Python 的http.server实现不依赖 Flask/FastAPI就是为了最小化依赖。完整代码可直接保存为mcp_server.pyimport json import sqlite3 from http.server import HTTPServer, BaseHTTPRequestHandler from urllib.parse import urlparse, parse_qs DB_PATH ./context.db class MCPHandler(BaseHTTPRequestHandler): def do_POST(self): if self.path ! /mcp: self.send_error(404) return # 解析请求体 content_length int(self.headers.get(Content-Length, 0)) post_data self.rfile.read(content_length).decode(utf-8) try: req json.loads(post_data) except json.JSONDecodeError: self.send_error(400, Invalid JSON) return # 处理 JSON-RPC 2.0 if req.get(jsonrpc) ! 2.0 or method not in req: self.send_error(400, Invalid JSON-RPC) return method req[method] params req.get(params, {}) req_id req.get(id, None) try: if method search_context: results search_context(DB_PATH, query_textparams.get(query, ), scopesparams.get(scopes), typesparams.get(types), limitparams.get(limit, 10) ) response {jsonrpc: 2.0, result: results, id: req_id} elif method get_context: # 实现 get_context根据 uid 查单条 uid params.get(uid) conn sqlite3.connect(DB_PATH) conn.row_factory sqlite3.Row cursor conn.execute(SELECT * FROM context_items WHERE uid ?, [uid]) row cursor.fetchone() conn.close() result dict(row) if row else None response {jsonrpc: 2.0, result: result, id: req_id} elif method update_context: # 实现 update_context插入或更新 item params.get(item, {}) conn sqlite3.connect(DB_PATH) conn.execute( INSERT OR REPLACE INTO context_items (id, scope, type, uid, title, content, metadata, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?) , [ item.get(id), item.get(scope), item.get(type), item.get(uid), item.get(title), item.get(content), json.dumps(item.get(metadata, {})), int(time.time()) ]) conn.commit() conn.close() response {jsonrpc: 2.0, result: success, id: req_id} else: raise ValueError(fUnknown method: {method}) except Exception as e: response {jsonrpc: 2.0, error: {code: -32603, message: str(e)}, id: req_id} # 返回响应 self.send_response(200) self.send_header(Content-type, application/json) self.end_headers() self.wfile.write(json.dumps(response, ensure_asciiFalse).encode(utf-8)) if __name__ __main__: server HTTPServer((localhost, 8000), MCPHandler) print(MCP Server running on http://localhost:8000/mcp) server.serve_forever()启动方式python mcp_server.py然后任何支持 HTTP 的客户端curl、Postman、Figma 插件都能调用。我们测试过curl -X POST http://localhost:8000/mcp -H Content-Type: application/json -d {jsonrpc:2.0,method:search_context,params:{query:button,scopes:[figma]},id:1}Figma 插件里用fetch(http://localhost:8000/mcp, {method:POST, body: jsonRpc})Java 用HttpURLConnectionDelphi 用TIdHTTP全部畅通无阻。实操心得生产环境务必加 auth。我们用最简单的 token 验证在do_POST开头加auth self.headers.get(Authorization)if auth ! Bearer your-secret-token: self.send_error(401); return这比 JWT/OAuth 简单一百倍且足够防误操作。Token 存环境变量不硬编码。4. 实战排障那些让你抓狂的 SQLiteMCP 问题我们都踩过了4.1 常见问题速查表问题现象根本原因解决方案验证命令search_context返回空结果但SELECT * FROM context_items WHERE title LIKE %xxx%能查到FTS5 索引未同步或 tokenize 配置错误检查触发器是否生效确认CREATE VIRTUAL TABLE语句中的tokenize参数SELECT * FROM context_fts WHERE context_fts MATCH xxx;Delphi 客户端读取content字段显示乱码如文档SQLite 连接未指定编码或 Delphi 字符串转换错误在 Delphi 中用UTF8Encode()转换或连接字符串加;DatabaseUTF8PRAGMA encoding;应返回UTF-8bm25()函数报错no such function: bm25SQLite 版本 3.19 或未启用 FTS5 编译选项下载官方预编译版检查PRAGMA compile_options;是否含ENABLE_FTS5SELECT sqlite_version(), compile_options FROM pragma_compile_options;MCP Server 启动后 curl 调用超时防火墙拦截或端口被占用检查netstat -ano | findstr :8000关闭占用进程或换端口telnet localhost 8000应能连通search_context结果排序不符合预期如短文档排在长文档后b参数过大过度惩罚长文档调小b值如 0.75 → 0.3重新执行查询SELECT bm25(context_fts, 1.2, 0.3) FROM context_fts WHERE rowid123;4.2 一次真实的乱码排查从蓝湖 MCP 到 SQLite 编码链路客户反馈蓝湖 MCP 插件调用search_context返回的title字段中文是乱码但用 DB Browser for SQLite 打开context.db显示正常。这说明问题不在 SQLite 文件本身而在数据流转链路。我们按链路逐段排查蓝湖插件侧检查 fetch 请求头发现Content-Type是text/plain;charsetISO-8859-1而响应体是 UTF-8。修正为application/json;charsetutf-8乱码消失一半。MCP Server 侧检查 Pythonsend_header发现没设 charset。在self.send_header(Content-type, application/json; charsetutf-8)加上charsetutf-8乱码全消。SQLite 底层为防万一执行PRAGMA encoding UTF-8;强制设置虽然新建 DB 默认就是 UTF-8。关键教训乱码问题 90% 出现在 HTTP 传输层而非 SQLite 存储层。DB Browser for SQLite 能正常显示证明数据存储无误问题一定在“客户端→Server→SQLite→Server→客户端”这个环里的某段编码声明缺失。记住这个排查顺序先看 HTTP header再看 Server 输出编码最后才怀疑 SQLite。4.3 BM25 排序失灵当“重要文档”总排不到第一我们有个客户的设计规范库scopedesign-system下有 200 条记录其中一条titleButton 最佳实践content包含 15 次“button”但search_context查“button”时它总排在第 7 名。直觉是 IDF 太低因为“button”太常见但SELECT count(*) FROM context_items WHERE content LIKE %button%返回 198确实几乎全覆盖。解决方案是人工注入权重。FTS5 支持rank函数自定义我们改用bm25(1.2, 0.75, title:10 content:1)把title字段权重提 10 倍。SQL 改为SELECT ..., bm25(context_fts, 1.2, 0.75, title:10 content:1) as score FROM context_fts JOIN context_items ...效果立竿见影“Button 最佳实践”稳居第一。原理是title:10让标题中出现“button”的得分乘以 10而content:1保持正文权重为 1这样即使正文出现 15 次标题出现 1 次也因权重高而胜出。实操技巧不要迷信默认 BM25 参数。我们为不同scope维护不同权重策略figmatitle:5 content:1 metadata:3设计稿标题最重要kingscadametadata:10 content:1PLC 地址、寄存器类型在 metadata 里blendercontent:5 metadata:2模型描述比名称更重要这些策略存在context_config表里search_context查询时动态拼接rank参数。4.4 MCP 协议兼容性陷阱Figma vs Cursor 的 JSON 差异Figma 插件和 Cursor 插件都调search_context但 Figma 返回的snippet是 HTML含strong高亮Cursor 要纯文本。如果 Server 统一返回 HTMLCursor 客户端要额外解析如果返回纯文本Figma 插件要自己加高亮。我们的解法是在 MCP 请求里加client_hint字段。Figma 插件发{method:search_context,params:{query:button,client_hint:figma}}Cursor 插件发{method:search_context,params:{query:button,client_hint:cursor}}Server 根据client_hint决定snippet格式figma用highlight()函数生成 HTMLSQLite 3.34 支持highlight()cursor用substr()截纯文本这样一套 Server 代码适配所有客户端且新增客户端只需加一个if分支不破坏现有逻辑。注意事项highlight()函数需要 SQLite ≥ 3.34且 FTS5 表创建时要加columnsize1选项否则 highlight 不工作。创建语句末尾加columnsize1然后SELECT highlight(context_fts, 0, b, /b) FROM context_fts WHERE ...就能高亮。5. 工具链与生态让 Context-Mode 真正融入你的工作流5.1 开箱即用的工具推荐全部免费、开源、免安装DB Browser for SQLiteWindows/macOS/Linux 通用图形化管理context.db支持 FTS5 查询、数据导入导出、SQL 执行。比 SQLite Expert 更轻量且无破解版密钥烦恼。官网https://sqlitebrowser.org/SQLite CLI官方版下载sqlite-tools-win32-x86-*.zip解压即用。执行sqlite3 context.db进入命令行SELECT * FROM context_fts WHERE context_fts MATCH button;秒级验证检索逻辑。VS Code SQLite Extension直接在编辑器里浏览、查询、修改.db文件支持语法高亮和自动补全。搜索 “SQLite” 安装即可。curl / Postman测试 MCP Server 的黄金搭档。我们把常用请求存为 Postman Collection团队新人 5 分钟就能上手调接口。提示别用“sqlite下载”搜到的来路不明 exe。官方下载页只有 https://www.sqlite.org/download.html 一个入口其他全是广告。Windows 用户认准sqlite-tools-win32-x86-*.zipLinux 用户apt install sqlite3即可Ubuntu 22.04 默认带 FTS5。5.2 与主流工具链的集成实录Figma 插件集成Figma 插件 JS 里直接 fetch MCP Serverasync function searchInContext(query) { const res await fetch(http://localhost:8000/mcp, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ jsonrpc: 2.0, method: search_context, params: { query, scopes: [figma] }, id: Date.now() }) }); return (await res.json()).result; } // 调用后把结果渲染到 Figma UI支持点击跳转到对应组件关键点Figma 插件运行在沙盒环境只能调localhost所以 MCP Server 必须本地运行。我们打包了一个start-mcp.bat双击就启动 Server 和 DB Browser设计师零命令行基础。Java Spring Boot 集成用RestTemplate调 MCPRestController public class ContextController { private final RestTemplate restTemplate new RestTemplate(); PostMapping(/api/search) public ListContextItem search(RequestBody SearchRequest req) { String url http://localhost:8000/mcp; HttpHeaders headers new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); // 构建 JSON-RPC 请求体 MapString, Object rpcReq Map.of( jsonrpc, 2.0, method, search_context, params, Map.of(query, req.getQuery(), scopes, req.getScopes()), id, System.currentTimeMillis() ); HttpEntityMap entity new HttpEntity(rpcReq, headers); ResponseEntityMap response restTemplate.postForEntity
