接一个 MCP 服务并不难,官方 SDK 几十行代码就能连上。接进一个真正会动用户电脑的 Agent 里,问题就多了:
- 服务有几百个工具,全部塞进上下文,模型的工具上限和 token 都扛不住;
- 运行到一半服务更新了工具目录,新出现的工具算不算已授权?
- 服务返回 500,或者参数类型错了,Agent 该停下来还是继续?
- 模型想执行
npm run build,这条命令到底安不安全?
这篇讲 Tokeny 是怎么处理这几件事的。先交代背景:Tokeny 直接使用官方 @modelcontextprotocol/sdk,工具经过 Native binding、权限门(Permission Gate),进入我们自研的 Agent Kernel 执行,生产路径里没有 LangChain。
一、工具太多:四种可见方式
在「插件 → 已安装」里打开一个服务的工具列表,可以设置"工具可见方式":
| 方式 | 配置值 | 行为 |
|---|---|---|
| 直接可用 | direct |
在模型请求里直接声明工具,仍受工具数量上限和权限约束 |
| 按需网关 | gateway |
模型先通过 list_mcp_tools 查目录,再用 call_mcp_tool 调用 |
| 搜索后加载 | deferred |
模型用 search_mcp_tools 搜索,下一轮获得匹配工具的 Schema 并直接调用 |
| 隐藏 | hidden |
不提供该服务或工具的调用入口 |
还可以用 toolExposure 按工具原名单独覆盖服务级别的设置,比如把查询类工具设为 direct,把删除类工具设为 hidden。工具名必须精确匹配,不支持通配符。
{
"exposure": "deferred",
"toolExposure": {
"lookup_record": "direct",
"delete_record": "hidden"
},
"connectionScope": "workspace"
}
几个实现上的细节:
search_mcp_tools对名称、描述和服务说明做本地确定性排序,一次最多返回 8 项。模型的工具容量在装配时预留,超出容量时移除最早加载的工具,需要时再搜索一次就能重新加载。- 网关模式查询大服务时,如果完整列表超过工具输出预算,返回名称索引和精确查询方法,分页用
offset继续读。 - 隐藏和搜索加载只控制"模型能看到什么",不替代权限设置,实际调用仍要通过工作空间和 Agent 的策略检查。
二、运行中目录变了:三个集合 + 每轮冻结
这是整个集成里最需要想清楚的一点。我们区分三个集合:
- 服务当前的工具目录;
- 本次 run 已授权的工具集合,在准入时冻结工具身份、Schema 和绑定;
- 本轮模型可见的工具子集。
flowchart LR
A[服务当前目录] -->|run 准入时冻结| B[本次 run 已授权集合]
B -->|搜索 / 直接声明| C[本轮模型可见子集<br/>+ hash 落盘]
C --> D[实际执行前<br/>再次复核启用状态/策略/权限]
搜索只能从第 2 个集合里选,运行期间新安装的服务或新出现的工具,不会被加进权限范围。每个新的模型轮,可见子集和它的 hash 跟输入一起冻结并写入 journal;重试、接管和恢复都使用原来冻结的视图,恢复同一个 run 时还会恢复工具的加载顺序,不会因为重新搜索而改变选择。新的用户回合才重新搜索。
目录刷新本身也有约束:分页读取完整 tools 目录,最多 100 页、10,000 项;遇到重复 cursor、任一页失败或超限,整次刷新失败,不发布部分结果。收到 tools/list_changed 通知时合并并发刷新,客户端 generation 校验防止旧连接覆盖新目录。已经失效的目录,不能靠 SQLite 里存的旧目录重新获得执行权限。
三、连接:每个工作空间可以独立
外部服务的"连接范围"默认是全局共享。选择"每个工作空间独立"后,每个工作空间建立自己的连接,服务看到的 root 固定为当前工作空间,stdio 进程也在该目录启动。
连接池的键包含服务、工作空间、配置身份和 root,两个工作空间不共用带 roots 状态的客户端或工具目录。闲置 5 分钟回收,最多保留 16 个工作空间连接。配置变更、服务禁用、工作空间删除和应用退出都会关闭对应连接。
需要特别说明:roots 只是给服务的目录提示,不是操作系统沙箱,它并不限制外部进程的系统权限。所以第三方 MCP 服务仍然应该先审查来源,再安装。
四、调用失败:交给 Agent 决定,不中断整轮
调用前,Tokeny 按目标工具当前的 inputSchema 校验参数,支持 draft-07、2019-09 和 2020-12。缺参数或类型错误时,返回一个"未发起调用"的工具错误,并附上顶层必填项、允许的属性和类型提示,模型补齐参数后可以继续。参数保留原值,不做类型转换、不填默认值、不删除多余字段,避免"帮模型改了参数"这种隐蔽行为。
服务端 JSON-RPC 错误、HTTP 4xx/5xx、发送前失败、结果校验错误、断线和超时,统一作为工具失败返回给模型。错误诊断使用本次请求 ID 对应的实际响应,而不是只看异常类型。失败结果正常持久化,但这不代表服务端一定没有产生部分副作用。之后是换方法、重试还是向用户报告,由 Agent 决定;MCP 层不会因为结果未知就终止整轮。
长调用使用真实的 notifications/progress,最多每 150 ms 投影一次进度;空闲超时和调用总时长都可以配置。用户点停止时,取消会传播到调用。
结果方面,isError、有序的文本和图片、structuredContent 都会保留下来,经过 receipt、历史记录一直到发给模型的请求,重启之后读取历史,失败卡片仍然显示失败。二进制资源保存为工作空间/会话范围内的附件,单个最多 32 MiB,至少保留 7 天。PDF 在这一层只保证原始字节被保存,不代表已经解析出文字。
五、权限:deny 永远优先
MCP 工具可见,不代表调用一定获准。Tokeny 的权限评估顺序保证 deny 优先:
- 系统保护路径和不可绕过的危险命令,硬阻断;
- guardrail deny 和用户规则 deny;
- 安全审核的 deny / ask / allow;
- 工作空间权限模式;
- 规则匹配,需要时人工审批。
工作空间有三种模式:
- 默认权限:按规则处理,需要时人工审批命令,不调用审核模型;
- 自动审批:保留默认权限边界,由审核模型自动批准普通命令,高风险操作转人工确认;
- 完全权限:确认风险后开启,跳过命令审核和授权等待,但硬性限制仍然生效。
文件删除进入审批时只提供"允许本次"和"拒绝",不保存会话或工作空间级别的删除授权。
六、命令安全审核:让模型读脚本,但不让它跑脚本
自动审批模式下,硬性权限检查之后,Tokeny 会把原始命令、实际的外层 Shell、工作目录、工作空间根目录和风险提示交给审核模型。审核模型只有三种合法回应:
{
"action":"read_files","paths":["package.json"],"reason":"检查构建入口和前后钩子"}
{
"verdict":"safe","risk":"medium","reason":"仅构建工作空间内可重建产物"}
{
"action":"unavailable","reason":"必要执行内容依赖未知的运行时输入"}
以 npm run build 为例:模型先请求读取 package.json,再请求其中引用的构建脚本,最后给出结论。应用本身不解析命令、不推导变量、不展开通配符、不运行脚本、不下载远程代码。发现脚本依赖是审核模型的工作,应用只负责校验响应结构和读取边界。
预算和边界:
- 一次审核最多 4 轮文件取证,格式解析失败可以额外重试 3 次,所以最多 7 次模型调用;所有调用共用一个超时,并受命令工具 30 秒的外层截止时间约束;
- 最多读取 12 个普通本地文本文件,单个 64 KiB,合计 192 KiB;文件的真实路径必须在工作目录或工作空间内,网络路径和设备路径不读取;
- JSON 解析很严格:多个对象、重复字段、截断的 JSON 都不采纳,也不会从模型自己续写的多个对象里挑出一个"放行"结论;
- 已读取的文件记录真实路径和 SHA-256 指纹,执行前再校验一次,文件内容变了、被删了或者链接改了指向,都拒绝这次执行,要求重新提交。
重试用完或者审核不可用时,要求用户对这次命令做审批决定,以前保存的授权不能绕过这次审批。
七、目前没做的
为了避免读者误会,列一下当前还没有实现的部分:MCP Apps、Sampling、Elicitation、Prompts、Tasks,以及 codemode(单独的未来提案)。真实 OAuth 刷新、并发 401、scope 升级、发行包和更多操作系统的验证也还没有覆盖。100/500/1000 个工具规模的目录测试只记录了 Schema 字节数和本地发现耗时,不能作为跨模型成功率或固定 token 节省的承诺。
小结
- 可见性和权限是两件事:前者省上下文,后者保安全,两者不能互相替代;
- 工具集合在 run 准入时冻结,每一轮的可见子集落盘,恢复时不重新搜索;
- 工具失败交还给 Agent,参数不做"好心"修改;
- 命令审核允许模型读脚本、要求模型说明理由,执行前再校验文件指纹。
Tokeny 是面向 Windows、macOS 和 Linux 的桌面 AI Agent,免费下载,可以在「插件」页安装和配置 MCP 服务;模型需要使用你自己的 API Key。官网:https://tokeny-ai.com/?utm_source=aliyun&utm_medium=article&utm_campaign=g1115-tech-mcp