【免费下载链接】mcp-server-neon
MCP server for interacting with Neon Management API and databases
Neon MCP Server 是连接自然语言与 Neon 云数据库的桥梁。而 Grant Context 与工具过滤机制正是它的权限引擎:通过几个 URL 查询参数,即可把 AI 智能体可用的一百多个数据库工具,精确裁剪到只读、单项目、指定角色甚至只剩几个文档工具——这就是细粒度权限控制的完整实现。
Grant Context 是什么:权限上下文的核心定义
Grant Context 是一个轻量的"权限凭证"结构体,记录了本次连接被允许做什么。它的完整定义只有四个字段:
export type GrantContext = {
projectId: string | null; // 锁定单个项目,null 表示全部项目
scopes: ScopeCategory[] | null; // 允许的工具分类,null 表示全部
unknownCategories?: string[]; // 无法识别的分类(用于提示用户)
roleName?: string; // 固定 SQL 执行所用的 Postgres 角色
};
(源码:grant-context.ts)
两个 null 语义是理解整个机制的钥匙:
scopes: null表示不限制,全部 13 个工具分类都开放;projectId: null表示不限定项目,可访问账号下所有项目。
13 个工具分类(scope categories)覆盖了 Neon 的主要能力域:
projects、branches、endpoints、snapshots、schema、querying、neon_auth、data_api、observability、docs、functions、storage、realtime
完整定义见 grant-context.ts,所有工具与其分类的对应关系可以查阅 hosted-tools-catalog.md。
💡 当没有任何配置时,系统回退到全量放行的默认值:
export const DEFAULT_GRANT: GrantContext = {
projectId: null,
scopes: null,
};
通过 URL 查询参数配置 Grant Context
Grant Context 的入口是 MCP 服务器 URL 上的 4 个查询参数,新手只需记住一张表:
| 参数 | 作用 | 示例 |
|---|---|---|
readonly | 启用只读模式(true/false) | ?readonly=true |
category | 限定工具分类,可重复或逗号分隔 | ?category=querying&category=schema |
projectId | 将全部操作限定到单个项目 | ?projectId=proj-123 |
roleName | 所有 SQL 固定以该 Postgres 角色执行 | ?roleName=app_reader |
参数解析统一由 resolveGrantFromSearchParams 完成,同时支持 ?category=a&category=b 和 ?category=a,b 两种写法(grant-context.ts)。两个容易踩坑的细节:
- 无效分类不报错而是被静默过滤,并记入
unknownCategories,最终会以一条提示告知用户哪些值被忽略、合法值有哪些; roleName的校验则是反向的——固定角色是一种"收窄",所以空值、重复或非法字符会直接抛出InvalidRoleNameError返回400,绝不悄悄降级为"不固定"(grant-context.ts)。
roleName 的完整规则(合法字符、LOGIN 要求、OAuth 与 API key 两种认证下的行为差异)在 README.md 中有权威说明。
🔐 值得注意的是:category=docs 且不带 projectId 是一个特殊组合,会触发无需登录的匿名文档端点,由 isDocsOnlyRequest 判定(grant-context.ts)。
工具过滤三层管线:从 100+ 工具到最小可用集
拿到 Grant Context 后,核心的过滤逻辑集中在 grant-filter.ts 中,由 filterToolsForGrant 编排三层管线:
let filtered = applyScopeCategoryFilter(tools, grant.scopes);
filtered = applyProjectScopeFilter(filtered, grant);
if (grant.roleName !== undefined) {
filtered = filtered.filter(
(tool) => !PINNED_ROLE_WITHHELD_TOOLS.has(tool.name),
);
}
第一层:按 category 过滤
scopes 为 null 时全量放行;为指定列表时,工具的 scope 字段必须命中列表;空数组则只剩三个"永远可用"工具:
export const ALWAYS_AVAILABLE_TOOLS: ReadonlySet<string> = new Set([
'search',
'fetch',
'send_feedback',
]);
这三个是智能体导航与用户反馈工具,任何收窄配置下都保留,避免用户被完全锁死(grant-filter.ts)。
第二层:按 projectId 过滤
一旦 grant 锁定了项目,只有 projectScoped: true 的工具才能存活——list_projects、create_project 这类跨项目管理工具会整体消失,project_id 参数则被服务端自动注入:配置了项目的连接直接省略它;显式传了但与授权项目不匹配则抛错,杜绝越权(grant-filter.ts)。
第三层:只读过滤
getFilteredTools 在此之上再叠一层:readonly 生效时,只保留 readOnlySafe: true 的工具。create_branch、delete_project、run_sql_transaction、get_connection_string 等写入类工具全部被剔除;而 run_sql 保留,但每条查询都包裹在 READ ONLY 事务中执行。
固定 Postgres 角色的额外保护
roleName 的作用不仅是"换个身份跑 SQL"——它决定了 Postgres 授权(GRANT)本身成为第二道闸门。为了配合这一限制,源码主动隐藏了 get_connection_string 工具:
export const PINNED_ROLE_WITHHELD_TOOLS: ReadonlySet<string> = new Set([
'get_connection_string',
]);
原因写得很直白:该工具存在就是为了把角色凭证交出去,若暴露它,调用方就能绕过服务器的只读事务、以固定角色身份在服务器之外执行任意 SQL(grant-filter.ts)。
两种授权页面:可编辑模式与固定授权
📄 OAuth 授权页面有两种形态,与 URL 是否带参数直接对应:
- 默认 URL(可编辑模式):授权页提供"全部项目 / 单个项目"单选、Postgres 角色输入框、13 个工具分类勾选(实时显示"13/13 selected · 126 tools available")以及 Read only / Read and write 权限选择;
- 参数化 URL(固定模式):页面只展示 URL 中已定好的项目、分类与权限供确认,并提示"To change this access, update the connection URL and authorize again."。
授权页面的工具清单预览同样走 grant 过滤逻辑(consent-tools.ts),所以用户看到的"126 tools available"与实际生效的工具集完全一致。
⚠️ 安全设计要点:token 签发后,修改 URL 不会扩大或改变已有 token 的权限——想变更授权必须重新授权。在 route.ts 中,OAuth token 的 grant 只从令牌存储读取(resolveGrantFromToken),若存储的 grant 无法解析,认证直接失败,而不是回退到 URL 参数或 API key 路径(route.ts)。
无认证预览:用 list-tools 端点验证你的配置
改完 URL 参数后,不必连接智能体就能验证工具可见性——/api/list-tools 是一个无需认证的无状态预览端点:
curl "https://mcp.neon.tech/api/list-tools?readonly=true&category=querying"
响应包含 grant(实际生效的权限上下文)、readOnly、notices(服务端级别的安全提示,如只读模式声明)、warnings(边界情况警告,例如"未设置任何有效分类,仅剩 search/fetch/send_feedback")以及完整的 tools 数组(route.ts)。
设计亮点小结
- 默认全开,显式收窄:
null即"全部",每个参数都在做减法,符合最小惊讶原则; - 收窄失败必须显式:非法
roleName直接 400、token grant 损坏直接拒绝认证,"宁可报错也不悄悄放宽"; - 权限提示集中下发:只读声明、破坏性工具警告等 Notices 作为顶层字段一次性返回,而不是复制进每个工具描述,为智能体省下了大量 token(route.ts);
- 过滤逻辑单点维护:MCP 协议路径、
list-tools预览端点、OAuth 授权页面三处共用同一套 grant 过滤代码,权限语义天然一致。
延伸阅读:关键模块路径
- 权限上下文解析:mcp/utils/grant-context.ts
- 工具过滤与权限提示:mcp/tools/grant-filter.ts
- 只读模式优先级判定:mcp/utils/read-only.ts
- 授权页面审批逻辑:mcp/oauth/consent-approval.ts
- MCP 传输路由(OAuth token 与 grant 的绑定):app/api/[transport]/route.ts
- 工具可见性预览端点:app/api/list-tools/route.ts
【免费下载链接】mcp-server-neon
MCP server for interacting with Neon Management API and databases
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考






