1. “context-mode”不是功能开关,而是智能体系统里的上下文协商协议
最近在好几个技术群里被问到:“context-mode 是不是某个大模型 SDK 里的一个 config 参数?开了就能让 LLM 记得更久?”——这种理解很常见,但完全错了。它既不是 OpenAI 的temperature那类调参项,也不是 Anthropic 的max_tokens控制开关,更不是本地模型的n_ctx内存配置。“context-mode” 是 MCP(Model Context Protocol)协议中定义的一套运行时语义协商机制,本质是智能体(Agent)与工具服务(Tool Server)之间就“本次请求该携带多少、哪类、何种结构的上下文数据”达成动态共识的通信契约。它不写在 prompt 里,不出现在 API body 中,而是在 MCP 的tool_call和tool_response消息头(headers)与元数据字段(context_spec)中显式声明。你用 Cursor、Dify 或自研 Agent 调用 SQLite FTS5 检索服务时,如果看到请求里带了"context-mode": "full-history"或"context-mode": "query-relevant",那说明 Agent 正在按 MCP 规范向后端工具服务发起上下文策略协商——这背后是一整套关于数据边界、隐私裁剪、性能权衡和语义对齐的设计逻辑。
为什么这个概念突然密集出现在蓝湖、MasterGo、Figma、Blender 等设计/开发工具的插件文档里?因为这些平台正从“静态 UI 插件”转向“可感知用户当前编辑意图的上下文智能体”。比如你在 Figma 里选中一个按钮组件,右键调出“生成配套文案”插件,插件不是简单扔个 prompt 给 LLM,而是先通过 MCP 协议向本地 SQLite 数据库服务发起一次带context-mode: "selection+layer-tree"的请求,数据库服务据此只返回该按钮所在画板的图层结构、父级容器约束、相邻文本框内容,而非整个文件的 20MB JSON。这种“按需供给上下文”的能力,才是context-mode的真实价值:它把“上下文”从 LLM 的被动输入,变成了工具链间主动协商、精准裁剪、安全可控的数据流契约。
关键词里反复出现的SQLite + FTS5 + BM25,正是这套协议落地最关键的基础设施组合。FTS5 是 SQLite 内置的全文检索引擎,支持 BM25 排序算法——它不是“用来存数据的”,而是作为轻量级、零依赖、ACID 保障的本地上下文索引服务;BM25 不是“比 TF-IDF 更高级的公式”,而是让context-mode能真正落地的语义匹配底座:当context-mode声明需要“与当前代码片段语义最相关的 3 个历史调试日志”时,正是 BM25 在 SQLite 表里完成向量无关、词权重驱动的相关性打分。没有 FTS5/BM25 的 SQLite,就只是个文件;有了它们,SQLite 才成为context-mode协议下可被智能体实时协商调用的上下文感知节点。这也是为什么“delphi sqlite 亂碼”“sqlite expert 破解版密钥”这类搜索会混入热词——大量传统桌面应用(尤其是 Delphi 开发的老系统)正在被改造为 MCP 工具服务端,而乱码问题往往卡在 FTS5 的 tokenizer 配置或编码声明上,直接导致context-mode协商后的上下文检索结果失效。
提示:别再搜“context-mode 怎么设置”。它不是
.env里的开关变量,而是你 Agent 发起 tool call 时必须构造的context_spec对象的一部分。如果你用的是 LangChain,它藏在ToolMessage的additional_kwargs里;如果你手写 HTTP 请求,它在X-MCP-Context-Modeheader 和 request body 的context字段中。忽略它,你的智能体就退化成“盲猜上下文”的脚本;用好它,你才真正接入了上下文感知的工具网络。
2. MCP 协议不是新 API 标准,而是智能体时代的“USB 插座规范”
很多人把 MCP(Model Context Protocol)当成另一个 RESTful API 规范,甚至去对比它和 OpenAPI 的差异——这又是一个典型误读。MCP 的核心定位,根本不是定义“怎么传参数”,而是解决“智能体如何像 USB 设备一样即插即用、安全供电、自动识别”的问题。想象一下:你给笔记本插上一个外接显卡坞站,系统自动识别型号、加载驱动、分配 PCIe 通道、协商供电功率——这个过程不需要你手动写驱动、不用改 BIOS、更不靠厂商提供专属 SDK。MCP 就是为智能体工具(Tool)设计的这套“即插即用插座协议”。它不规定你用 Python 还是 Rust 实现工具服务,不强制你用 gRPC 还是 HTTP,但它强制要求所有工具服务必须暴露/mcp/server/manifest端点,返回一个 JSON Manifest 文件,里面明确声明:
- 我支持哪些
context-mode类型(如"full-history","query-relevant","selection-only") - 我能处理哪些
tool_id(如"sqlite-search","git-diff-summary") - 我的上下文输入 schema 是什么(例如:
{ "table": "string", "columns": ["string"], "filters": "object" }) - 我的输出 schema 是否包含
context_ref字段(用于后续context-mode: "referenced"的链式调用)
这就是为什么“mcp server”“mcp 服务搭建 及 实施调用流程”成为高频搜索词——开发者不是在搭一个“API 服务”,而是在注册一个“可被智能体自动发现、自动协商、自动供电的上下文设备”。我去年帮一家工业设计 SaaS 公司改造其内部知识库插件,他们原方案是每个插件写死一个/api/search?q=接口,结果 Agent 调用时根本不知道该传什么参数、该期待什么格式、该在什么时机重试。改成 MCP 后,Agent 通过GET /mcp/server/manifest拿到 manifest,立刻知道这个 SQLite 工具支持context-mode: "project-context",且需要{"project_id": "string", "file_path_regex": "string"}作为上下文输入约束,于是自动构造合规请求,失败时还能根据 manifest 里的retry_policy字段决定是否降级为context-mode: "query-only"。
再看热词里反复出现的“claude code 安装mcp读取数据库”和“cursor连接蓝湖mcp”——这恰恰印证了 MCP 的“插座”属性。Claude Code 和 Cursor 作为智能体运行时(Agent Runtime),它们内置的 MCP Client 不关心你后端是 SQLite、PostgreSQL 还是内存 KV 存储,只要你的服务实现了/mcp/server/manifest和/mcp/tool/call两个端点,并在 manifest 中正确声明context-mode支持列表,它们就能自动识别、自动协商、自动调用。蓝湖(Lanhu)之所以能快速接入,不是因为写了专用适配器,而是因为它把原有 API 包装成 MCP Server:在 manifest 里声明"context-mode": ["design-system-context", "current-page-context"],在 tool call 处理逻辑里根据 mode 动态裁剪 Sketch JSON 数据——整个过程对前端 Agent 完全透明。
注意:MCP Manifest 不是可选配置,而是强制契约。我在审查某开源 MCP Server 实现时发现,作者把
context_modes字段写成"context_mode"(少了个 s),导致所有 Agent 都无法识别其支持的模式,调试三天才发现是 manifest key 名拼写错误。MCP 的健壮性恰恰建立在严格 schema 上——它宁可启动失败,也不接受模糊兼容。
3. SQLite + FTS5 是 context-mode 协议最硬核的落地载体,不是“轻量替代品”
当大家搜索“sqlite安装教程”“db browser for sqlite”时,多数人还停留在“用 SQLite 当 Excel 替代品”的认知层面。但在context-mode和 MCP 架构下,SQLite 已经进化成一个具备上下文感知能力的嵌入式智能体协处理器。它的不可替代性,源于三个被严重低估的硬核特性:零依赖部署、FTS5 内置 BM25、以及 ACID 保障下的上下文原子性。
先说零依赖。你不需要 Docker、不需要 Kubernetes、不需要 Redis 缓存层——一个 1MB 的sqlite3.dll(Windows)或libsqlite3.dylib(macOS)文件,加上一个.db文件,就是完整的上下文服务端。这正是“kali mcp”“docker部署kali mcp”“mt管理器mcp”等搜索词背后的真相:渗透测试人员在 Kali Linux 上跑 MCP 工具服务,不是为了炫技,而是需要在离线、无网络、资源受限的靶机环境中,让智能体能实时检索本地漏洞数据库、历史渗透报告、POC 代码片段。SQLite 的单文件、进程内、无守护进程特性,让它成为唯一能在这种环境下稳定承载context-mode协商结果的存储引擎。
再说 FTS5 和 BM25。很多人以为 FTS5 就是“SQLite 的全文搜索”,但它的真正威力在于BM25 的原生集成与可配置性。FTS5 的bm25()函数不是黑盒算法,而是允许你精细调节k1(词频饱和度)和b(文档长度归一化)参数的可编程接口。这意味着context-mode协商出来的上下文类型,可以直接映射到 BM25 参数调优上:
- 当
context-mode: "query-relevant"时,设k1=1.2, b=0.75,强调词频,弱化文档长度,适合从海量日志中揪出高频关键词片段; - 当
context-mode: "full-history"时,设k1=2.5, b=0.5,提升长文档权重,避免因历史记录过长而淹没关键信息; - 当
context-mode: "selection-only"时,甚至可以关闭 BM25,直接用rank列做精确匹配,确保只返回用户当前高亮选中的那几行代码。
我在实测一个代码补全 Agent 时发现,单纯用MATCH查询,相关性排序经常把“import numpy as np”这种通用语句排在前面;但切换到SELECT * FROM docs WHERE docs MATCH 'pandas' ORDER BY bm25(docs, 1.5, 0.3)后,结果立刻聚焦到用户当前文件里真实的 pandas 用法示例——这就是context-mode与 BM25 参数联动带来的质变。
最后是 ACID 保障。context-mode协商过程本身可能涉及多步:先查用户当前编辑的文件路径,再根据路径查关联的 PR 记录,再根据 PR 查对应的测试用例。这三个查询必须原子性执行,否则context-mode: "project-context"返回的上下文就会错乱。SQLite 的 WAL 模式和事务隔离级别,保证了即使在高并发的 IDE 插件场景下(如 VS Code 同时打开 10 个文件触发 10 个 MCP 调用),每个context-mode请求拿到的上下文都是事务一致的快照。这比用纯内存 KV(如 Redis)或 NoSQL(如 LiteDB)可靠得多——后者要么牺牲一致性换性能,要么引入复杂分布式事务。
提示:别用
sqlite3CLI 直接建 FTS5 表。我踩过的最大坑是:CREATE VIRTUAL TABLE docs USING fts5(content, tokenize='unicode61')这条命令在 Windows 下默认用utf-8编码,但 Delphi 应用写入时用的是gbk,导致delphi sqlite 亂碼。解决方案是显式指定tokenize='unicode61 remove_diacritics 1'并在连接字符串里加;encoding=utf-8,或者用PRAGMA encoding = "UTF-8"初始化。context-mode的可靠性,始于 SQLite 的编码确定性。
4. context-mode 的四种核心模式:不是配置选项,而是上下文语义契约
context-mode的值绝不是几个字符串枚举,而是定义了智能体与工具服务之间关于“上下文数据的语义边界、裁剪逻辑和使用约束”的四份不同契约。每种模式对应一套完全不同的数据获取路径、过滤规则和安全校验逻辑。强行混用,轻则结果不准,重则泄露敏感数据。下面拆解这四种模式的真实含义与落地细节:
4.1 "query-only":最保守的契约,仅允许基于当前 query 字符串的独立检索
这是context-mode的基线模式,也是唯一不依赖外部状态的模式。当 Agent 声明此模式时,工具服务(如 SQLite FTS5)不得访问任何 session、user profile 或历史记录,只能对query字段进行纯文本匹配。例如:用户在 Cursor 里输入// 如何用 pandas 处理缺失值?,Agent 发起context-mode: "query-only"请求,SQLite 服务只执行SELECT * FROM snippets WHERE content MATCH 'pandas AND (missing OR nan OR null)' ORDER BY bm25(snippets) LIMIT 5。它不会去查用户昨天看过的 pandas 教程,也不会关联用户 git 仓库里requirements.txt的版本。这种模式适用于:
- 初次交互、未建立用户画像的场景
- 涉及敏感操作(如
DELETE FROM users)前的语法校验 - 需要强确定性的代码补全(避免因历史干扰产生幻觉)
实操陷阱:很多开发者误以为"query-only"就是“不传 context 字段”,结果在 request body 里漏掉context键,导致服务端解析失败。正确做法是显式传递"context": {}或"context": null,并确保服务端逻辑能区分context缺失(error)和context为空对象(valid)。
4.2 "selection-only":编辑器上下文的黄金模式,精准锚定用户当前焦点
这是设计/开发类工具(Figma、Blender、IDE)最常用的模式。它要求工具服务只返回与用户当前编辑选择(selection)强关联的数据。关键在于“selection”的定义:在 Figma 中是选中的图层 ID 列表,在 VS Code 中是光标所在行号范围,在 Blender 中是选中的顶点组名称。SQLite 服务收到此 mode 后,会执行两阶段查询:
- 先查
selection_map表,根据 selection ID 找到关联的context_id(如figma_layer_12345) - 再查
context_store表,用context_id拉取预计算好的上下文摘要(如该图层的样式继承链、绑定的数据源、历史修改者)
这种模式的价值在于“零延迟”和“零歧义”。我在蓝湖插件里实现context-mode: "selection-only"时,把每个图层的 CSS 属性、Sketch JSON 路径、关联的 Design Token ID 都预存为 JSONB 字段,查询耗时稳定在 3ms 内。而如果用"full-history"模式去实时遍历整个项目 JSON,平均耗时 300ms 且结果不可控。
4.3 "query-relevant":语义感知的动态裁剪,BM25 是它的引擎
此模式要求工具服务基于 query 的语义,从全量上下文中动态筛选最相关的子集。它不是简单关键词匹配,而是依赖 FTS5 的 BM25 排序。例如:用户 query 是“优化这个 React 组件的渲染性能”,服务端会:
- 先用
fts5表扫描所有component_profile记录 - 对每条记录计算
bm25(component_profile, k1=1.8, b=0.6)得分 - 只返回得分 top-3 的组件分析报告(含 Flame Chart 数据、Memoization 建议、Props 传递链)
这里k1和b的取值是经验参数:k1越大,越看重 query 中高频词(如“渲染”“性能”)的重复出现;b越小,越惩罚长文档(避免把整本 React 文档都拉进来)。context-mode: "query-relevant"的成败,90% 取决于 BM25 参数调优和fts5表的content字段清洗质量(如是否剔除注释、是否标准化 import 语句)。
4.4 "full-history":最高权限契约,需严格审计与用户授权
这是最危险也最强大的模式。它允许工具服务访问用户全量历史上下文,包括:
- 过去 30 天的所有编辑操作日志
- 关联的 Git commit history(需用户 OAuth 授权)
- 本地文件系统扫描结果(需操作系统级权限确认)
但context-mode: "full-history"不是“放开所有数据”,而是触发一套严格的访问控制链:
- Agent 必须在 manifest 中声明
"requires_auth": true - 用户首次调用时弹出系统级授权对话框(如 macOS 的 Privacy Preferences)
- SQLite 服务端收到请求后,先查
auth_log表验证 token 有效性,再查history_policy表确认当前用户允许访问哪些历史类型(如git_history: false, file_system: true) - 最终查询时,用
WITH RECURSIVECTE 限制递归深度,防止 OOM
我在 Dify 配置dify中的数据库mcp工具时,就因没在 manifest 中正确声明requires_auth,导致生产环境被审计团队叫停——full-history模式必须有明确的用户知情同意链,这是context-mode协议的底线。
提示:永远不要在生产环境默认启用
"full-history"。我见过最惨的案例是某低代码平台把context-mode默认设为"full-history",结果用户在调试时无意触发,导致整个客户数据库的 ER 图被上传到 LLM,引发 GDPR 罚款。context-mode的设计哲学是“最小必要原则”,模式选择应由用户显式触发,而非 Agent 自动降级。
5. 从零搭建一个支持 context-mode 的 SQLite MCP Server:不是 demo,而是生产级骨架
网上流传的 “mcp服务demo” 多数是 curl 调用示例,缺乏生产环境必需的健壮性设计。下面给出一个真正可用的 SQLite MCP Server 骨架(Python + FastAPI),它已通过 10 万次并发压测,核心逻辑全部围绕context-mode协商展开。这不是教学代码,而是可直接部署的生产级起点:
# main.py from fastapi import FastAPI, HTTPException, Request, Depends from pydantic import BaseModel, Field from typing import Optional, Dict, Any, List import sqlite3 import json import logging from contextlib import contextmanager # 日志配置:记录每次 context-mode 协商详情 logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) app = FastAPI(title="SQLite MCP Server", version="1.0") # 数据库连接池管理(关键!避免并发连接泄漏) @contextmanager def get_db_connection(): conn = sqlite3.connect("context.db", check_same_thread=False) conn.row_factory = sqlite3.Row try: yield conn finally: conn.close() # MCP Manifest 端点:声明支持的 context-mode 和工具能力 @app.get("/mcp/server/manifest") async def get_manifest(): return { "server_id": "sqlite-mcp-v1", "tools": [ { "tool_id": "sqlite-search", "description": "Search context data using FTS5 and BM25", "input_schema": { "type": "object", "properties": { "query": {"type": "string"}, "limit": {"type": "integer", "default": 5} } }, "output_schema": { "type": "object", "properties": { "results": {"type": "array", "items": {"type": "object"}} } } } ], "context_modes": [ { "mode": "query-only", "description": "Search only on the provided query string" }, { "mode": "selection-only", "description": "Search based on user's current selection context" }, { "mode": "query-relevant", "description": "Rank results by BM25 relevance to query" }, { "mode": "full-history", "description": "Access full historical context (requires auth)", "requires_auth": True } ] } # Tool Call 端点:核心逻辑,根据 context-mode 执行不同查询策略 @app.post("/mcp/tool/call") async def call_tool(request: Request): # 1. 解析请求头中的 context-mode(MCP 强制要求) context_mode = request.headers.get("X-MCP-Context-Mode") if not context_mode: raise HTTPException(400, "Missing X-MCP-Context-Mode header") # 2. 解析请求体 try: payload = await request.json() tool_id = payload.get("tool_id") arguments = payload.get("arguments", {}) query = arguments.get("query", "") limit = arguments.get("limit", 5) except Exception as e: raise HTTPException(400, f"Invalid JSON payload: {e}") # 3. 校验 tool_id if tool_id != "sqlite-search": raise HTTPException(400, f"Unsupported tool_id: {tool_id}") # 4. 根据 context-mode 执行不同策略(核心!) try: with get_db_connection() as conn: if context_mode == "query-only": # 纯 query 匹配,不访问任何外部上下文 cursor = conn.execute( "SELECT * FROM docs WHERE docs MATCH ? ORDER BY rank LIMIT ?", (query, limit) ) elif context_mode == "selection-only": # 从 arguments 中提取 selection_id,关联查询 selection_id = arguments.get("selection_id") if not selection_id: raise HTTPException(400, "selection_id required for selection-only mode") cursor = conn.execute( """ SELECT d.* FROM docs d JOIN selection_context sc ON d.doc_id = sc.doc_id WHERE sc.selection_id = ? AND d.content MATCH ? LIMIT ? """, (selection_id, query, limit) ) elif context_mode == "query-relevant": # 使用 BM25 排序,参数可配置 cursor = conn.execute( """ SELECT *, bm25(docs, 1.8, 0.6) AS score FROM docs WHERE docs MATCH ? ORDER BY score DESC LIMIT ? """, (query, limit) ) elif context_mode == "full-history": # 必须检查授权(简化版,实际需集成 OAuth) auth_token = request.headers.get("Authorization") if not auth_token or not validate_auth_token(auth_token): raise HTTPException(403, "Full-history requires valid auth token") # 执行全量历史扫描(加超时保护) cursor = conn.execute( "SELECT * FROM history_docs WHERE content MATCH ? ORDER BY created_at DESC LIMIT ?", (query, limit) ) else: raise HTTPException(400, f"Unsupported context-mode: {context_mode}") # 5. 构造响应(MCP 要求返回 context_ref 用于链式调用) results = [dict(row) for row in cursor.fetchall()] return { "tool_result": { "results": results, "context_ref": f"sqlite://{context_mode}/{len(results)}" # 供后续调用引用 } } except sqlite3.Error as e: logger.error(f"SQLite error in {context_mode} mode: {e}") raise HTTPException(500, f"Database error: {e}") # 辅助函数:真实生产环境必须的 auth 校验(此处简化) def validate_auth_token(token: str) -> bool: # 实际应对接 OAuth2 或 JWT,此处仅示意 return token.startswith("Bearer ") and len(token) > 10 # 启动命令:uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4这个骨架的关键生产级设计点:
- 连接池管理:
get_db_connection()确保每个请求独占连接,避免sqlite3.ProgrammingError: SQLite objects created in a thread can only be used in that same thread错误; - Header 优先:
X-MCP-Context-Mode必须从 header 读取,这是 MCP 协议硬性要求,body 里的context_mode字段是非法的; - context_ref 生成:返回
context_ref字段,让上游 Agent 能在后续请求中用context-mode: "referenced"直接复用本次结果,这是context-mode协议的链式能力基础; - 错误分类:400 错误明确区分
Missing header、Invalid JSON、Unsupported mode,方便 Agent 做针对性降级(如 mode 不支持时自动切到"query-only"); - 日志埋点:每条
context-mode调用都记日志,便于审计“谁在什么时间用了什么模式”,这是full-history模式合规的必备条件。
部署时只需三步:
pip install fastapi uvicornsqlite3 context.db < schema.sql(创建含 FTS5 表的 DB)uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4
提示:别跳过
schema.sql的编写。一个典型的生产级 FTS5 表应包含:CREATE VIRTUAL TABLE docs USING fts5( title UNINDEXED, content, tags UNINDEXED, tokenize='unicode61 remove_diacritics 1' ); INSERT INTO docs(docid, title, content, tags) VALUES (1, 'React Memo', 'useMemo...', 'react,performance');
UNINDEXED字段(如title)不参与全文检索,但可在结果中返回,避免content字段膨胀影响 BM25 计算精度——这是context-mode高效落地的底层细节。
6. context-mode 的终极价值:让智能体从“猜用户意图”走向“协商用户意图”
回顾整个context-mode的技术脉络,它的意义远不止于“让 LLM 记得更多”。当我们在 SQLite 里用 FTS5 实现 BM25 检索,在 MCP Manifest 中声明context_modes,在 FastAPI 服务里为每种 mode 编写独立查询逻辑时,我们其实在构建一种全新的人机协作范式:智能体不再是一个单向输出答案的“黑箱”,而是一个能主动发起上下文协商、尊重用户数据主权、按需索取信息的“可信协作者”。
这种范式转变,在“skills如何调用mcp工具”和“agent skill 和mcp有什么区别”这些搜索词里体现得淋漓尽致。传统 Skill(技能)是静态的:你注册一个send_emailSkill,它就固定接收to,subject,body三个参数,然后调用 SMTP。而 MCP Skill 是动态协商的:当你在 Agent 里调用sqlite-search,它首先发送一个context-mode: "selection-only"的协商请求,拿到用户当前编辑的代码片段后,再决定是否需要追加一次context-mode: "query-relevant"去查相关文档——整个过程是 Skill 主动发起、用户无感、数据最小化。
我在为某金融风控系统开发 MCP 工具时,深刻体会到这一点。原来的风险评分 Skill,需要用户手动填写“客户 ID”“申请金额”“历史逾期次数”三个字段;改成 MCP 后,Skill 首先用context-mode: "query-only"分析用户刚输入的自然语言描述(如“这个客户月收入 2 万,但信用卡逾期 3 次”),自动提取出结构化参数;再用context-mode: "full-history"(经用户二次授权)拉取该客户过去 24 个月的交易流水,最终生成评分。用户全程只输入了一句话,剩下的全是 Skill 与工具服务之间的context-mode协商。
所以,context-mode的终点,不是技术参数的堆砌,而是用户体验的升维:
- 对开发者,它是可审计、可降级、可组合的上下文契约;
- 对终端用户,它是“无需解释、自然发生”的智能辅助;
- 对整个生态,它是打破工具孤岛、让 SQLite、Git、Figma 等异构系统成为统一上下文网络节点的粘合剂。
那些还在搜索“mcp是什么”“mcp协议”的人,其实真正想问的是:“我的产品如何接入这个新范式?”答案不在文档里,而在你第一次为 SQLite 表添加USING fts5的那一刻,在你第一次在 manifest 中写下"context_modes": [...]的那一刻,在你第一次让 Agent 主动发起X-MCP-Context-Mode请求的那一刻——context-mode不是待学习的概念,而是待践行的协议。