全部 Agent 工程

规范文件

Cursor mcp.json:本地与远程服务的字段和密钥写法自查

区分 Cursor mcp.json 里 stdio 与远程服务的字段,用自写脚本检查插值写法与密钥是否明文。

AI Nav City撰写 / 更新

Cursor 用 mcp.json 配置 MCP 服务器,项目里放 .cursor/mcp.json,全局放 ~/.cursor/mcp.json。本地命令服务和远程地址服务用的字段不同,密钥应当通过 ${env:名称} 引用而不是写进文件。本文依据官方文档整理字段与插值规则,并附一个自写的 Node.js 检查脚本,用来在提交配置前发现 envFile 用错位置和明文密钥。脚本不是官方校验,没有启动 Cursor,也没有连接任何 MCP 服务。

核验日期
· 未实测
适用于
  • Cursor 适用于 Cursor 的 MCP 配置;官方页面没有给出统一版本门槛,字段以 2026-11-05 的官方页面为准。[1]
涉及工具

文件位置

路径适用范围读取方式
./.cursor/mcp.json项目级 MCP 配置,只对该项目生效。放在项目里时会随仓库流转,密钥应使用 ${env:名称} 引用。[1]
~/.cursor/mcp.json全局 MCP 配置,所有项目可用。放在用户目录下,不随仓库提交。[1]

什么时候用

  • 要手写或审阅 mcp.json,并想在启用前检查字段位置与密钥写法。[1]

什么时候不用

  • 需要团队级的允许名单、托管分发或网络管控,那属于管理员设置,不在 mcp.json 里。[1]

字段分组与插值规则

官方说明 Cursor 支持三种传输方式:stdio、SSE 和 Streamable HTTP。stdio 由 Cursor 管理本地进程;SSE 与 Streamable HTTP 是按地址访问的服务,认证方式为 OAuth。[1]

表:mcp.json 字段分组(依据官方说明)
类型字段说明
stdiotype、command、args、env、envFilecommand 必须在系统路径中或写完整路径;envFile 只适用于 stdio
远程url、headers、auth远程服务不支持 envFile,应改用环境变量插值
静态 OAuthauth.CLIENT_ID(必填)、CLIENT_SECRET、scopes提供商要求固定客户端或不支持动态注册时使用;省略 scopes 时 Cursor 通过 /.well-known/oauth-authorization-server 发现

[1]

插值方面,官方写明 Cursor 会解析 command、args、env、url 和 headers 里的变量,语法包括 ${env:NAME}、${userHome}、${workspaceFolder}、${workspaceFolderBasename}、${pathSeparator} 和 ${/};auth 的值也支持同样的插值,官方建议用环境变量存放 Client ID 与 Client Secret,而不是写死。[1]

自写检查脚本

这个 Node.js 脚本读取一份 mcp.json,逐个服务器检查:command 与 url 是否恰好出现一个、envFile 是否出现在远程服务上、env 和 headers 里名字像密钥(含 key、token、secret、auth)的项是否是未使用 ${env:...} 的明文、auth.CLIENT_SECRET 是否明文,并对缺少 type 字段的服务给出提示。判断规则是我按官方字段表自己写的,不是官方校验。[1]

示例:原创检查脚本;离线运行,不是官方校验器
import fs from "node:fs";

const config = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
const secretName = /key|token|secret|auth/i;
const literal = value => typeof value === "string" && value !== "" && !/\$\{env:[\w-]+\}/.test(value);

for (const [name, server] of Object.entries(config.mcpServers ?? {})) {
  const notes = [];
  const local = "command" in server, remote = "url" in server;
  if (local === remote) notes.push("need exactly one of command / url");
  if (remote && "envFile" in server) notes.push("envFile is stdio-only");
  for (const [key, value] of Object.entries(server.env ?? {}))
    if (secretName.test(key) && literal(value)) notes.push(`literal value for env.${key}`);
  for (const [key, value] of Object.entries(server.headers ?? {}))
    if (secretName.test(key) && literal(value)) notes.push(`literal value for headers.${key}`);
  if (literal(server.auth?.CLIENT_SECRET)) notes.push("literal auth.CLIENT_SECRET");
  if (!("type" in server)) notes.push("info: no type field");
  console.log(name, "->", notes.length ? notes : ["no findings"]);
}

[1]

样例里 docs-local 是 stdio 服务,tickets-remote 是用环境变量引用凭据的远程服务,bad-remote 故意把 envFile、明文头和明文 CLIENT_SECRET 放在远程服务上。样例是原创的,其中的地址与值均为占位。[1]

示例:原创检查样例;未让 Cursor 读取
{
  "mcpServers": {
    "docs-local": {
      "type": "stdio",
      "command": "node",
      "args": ["${workspaceFolder}/tools/docs-server.mjs"],
      "env": { "DOCS_API_KEY": "${env:DOCS_API_KEY}" },
      "envFile": "${workspaceFolder}/.env.mcp"
    },
    "tickets-remote": {
      "url": "https://mcp.example.com/tickets",
      "headers": { "Authorization": "Bearer ${env:TICKETS_TOKEN}" },
      "auth": { "CLIENT_ID": "${env:TICKETS_CLIENT_ID}", "CLIENT_SECRET": "${env:TICKETS_CLIENT_SECRET}", "scopes": ["read"] }
    },
    "bad-remote": {
      "url": "https://mcp.example.com/bad",
      "envFile": ".env",
      "headers": { "Authorization": "Bearer demo-literal" },
      "auth": { "CLIENT_ID": "demo", "CLIENT_SECRET": "demo-literal" }
    }
  }
}

[1]

示例:脚本输出(离线运行)
docs-local -> [ 'no findings' ]
tickets-remote -> [ 'info: no type field' ]
bad-remote -> [
  'envFile is stdio-only',
  'literal value for headers.Authorization',
  'literal auth.CLIENT_SECRET',
  'info: no type field'
]

[1]

文档里不一致的地方与没有覆盖的部分

  • type 字段:官方 stdio 字段表把 type 标为必填,但同页的 mcp.json 示例并没有写 type。脚本对缺少 type 的服务只给出提示而不判错,是否必填以你本机 Cursor 的实际表现为准。
  • 插值字段:官方说明列出的可解析字段不含 auth,但另一段又说 auth 的值支持同样的插值。两处说法并存,脚本按「auth 也可以用 ${env:...}」处理。
  • 脚本不处理 SSE 与 Streamable HTTP 的差别、静态 OAuth 的回调地址(官方给出固定的网页与桌面回调)、扩展 API 注册,也不检查值本身是否真的是密钥。
  • 管理员的 MCP 允许名单、网络模式和团队分发在 mcp.json 之外,官方说明它们会限制哪些服务器可以运行。

[1]

官方对安全的提醒是:只安装来自可信开发者和仓库的服务器,核对服务器会访问的数据与 API,使用权限最小的密钥,必要时审阅服务器源码;Cursor 默认在使用 MCP 工具前请求批准。出现脚本提示的明文密钥,先轮换该密钥再改成 ${env:...} 引用,因为它可能已经进入过版本历史。本文只核验官方字段说明,没有启动 Cursor、没有连接 MCP 服务,也不认证任何服务器的安全性或费用。[1]

来源

以下均为官方页面,核验日期见各条;链接指向核验时的版本。

  1. Cursor · Model Context Protocol (MCP)

发现错误?告诉我们 →