Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions mcp/src/tools/permissions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,39 @@ describe("permission tools", () => {
action: "getResourcePermission",
resourceType: "noSqlDatabase",
resourceId: "todos",
AclTag: "READONLY",
aclTag: "READONLY",
acl_tag: "READONLY",
requestId: "req-resource-perm",
totalCount: 1,
},
});
expect(payload.data.permissionList).toEqual([
expect.objectContaining({
Resource: "todos",
Permission: "READONLY",
}),
]);
});

it("queryPermissions(action=listResourcePermissions) should expose compatibility aliases", async () => {
const result = await tools.queryPermissions.handler({
action: "listResourcePermissions",
resourceType: "noSqlDatabase",
resourceIds: ["todos"],
});
const payload = JSON.parse(result.content[0].text);

expect(payload.data).toMatchObject({
requestId: "req-resource-perm",
totalCount: 1,
});
expect(payload.data.permissionList).toEqual([
expect.objectContaining({
Resource: "todos",
Permission: "READONLY",
}),
]);
});

it("queryPermissions(action=getResourcePermission) should return a doc-id write hint for risky custom rules", async () => {
Expand Down
45 changes: 45 additions & 0 deletions mcp/src/tools/permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,19 @@ type ToolEnvelope = {
message: string;
};

type PermissionListItem = {
Resource?: string;
Permission?: string;
};

type DescribeResourcePermissionResult = {
RequestId?: string;
Data?: {
TotalCount?: number;
PermissionList?: PermissionListItem[];
};
};

function buildWriteVerificationHint(resourceId: string) {
return `对于 ${resourceId} 这类有后端权限控制的集合,前端调用 .doc(id).update() / .doc(id).remove() 后,不能只看是否没有抛异常。请显式检查返回结果中的 updated / deleted 是否大于 0;如果 result.code、result.message 存在,或 updated / deleted 为 0,要把它当作真实失败并向上抛错。`;
}
Expand Down Expand Up @@ -92,6 +105,35 @@ function buildEnvelope(data: Record<string, unknown>, message: string): ToolEnve
};
}

function buildPermissionResultCompatibilityFields(
result: DescribeResourcePermissionResult,
permissions: PermissionListItem[],
) {
return {
permissionList: permissions,
requestId: result.RequestId ?? null,
totalCount: result.Data?.TotalCount ?? permissions.length,
};
}

function buildAclTagCompatibilityFields(
permissions: PermissionListItem[],
resourceId?: string,
) {
const matchedPermission =
permissions.find((item) => item.Resource === resourceId)?.Permission ?? permissions[0]?.Permission;

if (!matchedPermission) {
return {};
}

return {
AclTag: matchedPermission,
aclTag: matchedPermission,
acl_tag: matchedPermission,
};
}

function buildErrorEnvelope(error: unknown): ToolEnvelope {
return {
success: false,
Expand Down Expand Up @@ -391,6 +433,8 @@ export function registerPermissionTools(server: ExtendedMcpServer) {
resourceType,
resourceId,
permissions,
...buildPermissionResultCompatibilityFields(result, permissions),
...buildAclTagCompatibilityFields(permissions, resourceId),
hints,
raw: result,
},
Expand Down Expand Up @@ -425,6 +469,7 @@ export function registerPermissionTools(server: ExtendedMcpServer) {
permissions,
resourceHints,
total: result.Data.TotalCount ?? 0,
...buildPermissionResultCompatibilityFields(result, permissions),
raw: result,
},
"资源权限列表查询成功",
Expand Down
Loading