谈谈 API 接口自描述与权限配置系统
2026年05月16日0 次阅读0 人喜欢
API权限系统MCPTypeScript
起因
说实话,之前项目的 API 权限管理挺乱的。接口描述散落在 api-registry.ts 里,每次新增接口都得去那个文件手动注册,漏了就出问题。MCP 工具的开关也是硬编码的,想动态调整根本没戏。
最近花时间搞了一套 API 接口自描述系统,把这些问题一并解决了。
方案设计
核心思路很简单:每个 route.ts 文件自己声明自己。
typescript
// src/app/api/post/create/route.ts
import type { ApiDescriptor } from '@/types/api-descriptor';
import { POST_CREATE } from '@/constants/permissions';
export const descriptor: ApiDescriptor = {
code: 'post_create',
name: '创建文章',
module: 'post',
method: 'POST',
permissionCode: POST_CREATE,
inputSchema: {
type: 'object',
properties: {
title: { type: 'string', description: '文章标题' },
content: { type: 'string', description: '文章内容' },
},
required: ['title', 'content'],
},
};
export async function POST(request: NextRequest) {
// 原有逻辑不变
}
一个文件有多个接口?用 xxxDescriptor 命名就行:
typescript
export const getDescriptor: ApiDescriptor = { /* GET */ };
export const updateDescriptor: ApiDescriptor = { /* PUT */ };
export const deleteDescriptor: ApiDescriptor = { /* DELETE */ };
扫描脚本
写了个脚本 pnpm sync:api-registry,扫描所有 route.ts 文件,自动同步到数据库。
一开始想用字符串解析的方式提取 descriptor,结果发现根本没法处理导入的权限码常量。后来改成动态 import,直接读取模块导出,代码从 238 行缩减到 155 行,还省了一堆重复定义。
typescript
// 直接 import 模块,读取导出
const module = await import(filePath);
if (module.descriptor?.code) {
results.push(module.descriptor);
}
后台配置
在现有的角色管理页面加了 MCP 工具配置区域。现在可以:
- 给角色分配接口权限
- 设置数据权限范围(全部/仅自己)
- 动态开关哪些接口暴露给 MCP
不用改代码,后台勾选就行。
数据库变更
tb_api_registry 表加了两个字段:
auto_discovered:标记是自动扫描还是手动添加synced_at:最后同步时间
效果
现在新增接口只需要:
- 在 route.ts 里写好 descriptor
- 跑一下
pnpm sync:api-registry - 后台勾选权限和 MCP 配置
不用再去 api-registry.ts 手动注册了,也不会漏掉。
这套系统其实不复杂,但解决了实际痛点。有时候就是这样,一个小改动能让维护成本降不少。