实战后台 MCP 02:增加 Resources 和权限控制

先理解:Resources 管数据,权限管边界

Tools 负责动作,Resources 更适合暴露状态、配置、说明文档等数据。后台 MCP 如果只有 Tools,AI 会缺少上下文;如果没有权限控制,AI 又可能看到不该看的东西。

本篇增加 Resources 和角色权限。这样不同运营角色能看到不同能力,后续也可以按角色开放写操作。

权限要靠 Server 执行

不要只在客户端隐藏工具。真正的权限判断必须在 MCP Server 里做,因为所有调用最终都经过 Server。Server 不允许,客户端再聪明也不能越权。

本篇扩展 MCP Server:暴露运营规则 Resources,并给 Tools 增加权限检查。

权限模块

src/auth.ts

ts
export type Role = "readonly" | "operator" | "admin"

export type Context = {
  userId: string
  role: Role
}

export function currentContext(): Context {
  return {
    userId: process.env.ADMIN_USER_ID || "local-dev",
    role: (process.env.ADMIN_ROLE as Role) || "readonly",
  }
}

export function requireRole(ctx: Context, allowed: Role[]) {
  if (!allowed.includes(ctx.role)) {
    throw new Error(`permission denied: role ${ctx.role}`)
  }
}

Resource

src/index.ts 中增加:

ts
server.resource(
  "refund-policy",
  "admin://policy/refund",
  async () => ({
    contents: [{
      uri: "admin://policy/refund",
      mimeType: "text/plain",
      text: "涉及退款金额、赔偿、封禁的操作必须人工二次确认。",
    }],
  }),
)

给 Tool 加权限

导入:

ts
import { currentContext, requireRole } from "./auth.js"

get_order handler 中加入:

ts
const ctx = currentContext()
requireRole(ctx, ["readonly", "operator", "admin"])

低风险写入 Tool

ts
server.tool(
  "update_order_note",
  "给订单添加运营备注,低风险写入",
  { orderId: z.string(), note: z.string() },
  async ({ orderId, note }) => {
    const ctx = currentContext()
    requireRole(ctx, ["operator", "admin"])
    return {
      content: [{ type: "text", text: `note updated for ${orderId}: ${note}` }],
    }
  },
)

验收

只读身份:

bash
ADMIN_ROLE=readonly npm run dev

预期:

  • get_order 可用。
  • update_order_note 报权限错误。

运营身份:

bash
ADMIN_ROLE=operator npm run dev

预期低风险写入可用。

权限必须在 Tool 内检查,不能只靠客户端隐藏按钮。

实战后台 MCP 03:审计日志和高风险二次确认
实战后台 MCP 01:封装订单查询和报表工具