{"record":{"id":"a226a24f211a36ba","repo":"AlistGo/alist","slug":"permission-denied-a226a2","errorCode":null,"errorMessage":"permission denied","messagePattern":"permission denied","errorType":"http","errorClass":null,"httpStatus":403,"severity":"error","filePath":"server/mcp/auth.go","lineNumber":127,"sourceCode":"}\n\n// buildFsContext resolves path and sets meta in context for fs operations.\nfunc buildFsContext(ctx context.Context, user *model.User, path string) (context.Context, string, error) {\n\treqPath, err := user.JoinPath(path)\n\tif err != nil {\n\t\treturn ctx, \"\", err\n\t}\n\tmeta, _ := op.GetNearestMeta(reqPath)\n\tctx = context.WithValue(ctx, \"meta\", meta)\n\tctx = context.WithValue(ctx, \"user\", user)\n\treturn ctx, reqPath, nil\n}\n\n// checkAccess checks if user can access the path (read).\nfunc checkAccess(user *model.User, reqPath string) error {\n\tmeta, _ := op.GetNearestMeta(reqPath)\n\tif !common.CanAccessWithRoles(user, meta, reqPath, \"\") {\n\t\treturn fmt.Errorf(\"permission denied\")\n\t}\n\tperm := common.MergeRolePermissions(user, reqPath)\n\tif !user.IsAdmin() && !common.HasPermission(perm, common.PermMCPAccess) {\n\t\treturn fmt.Errorf(\"MCP access not permitted\")\n\t}\n\treturn nil\n}\n\n// checkManage checks if user can perform write operations via MCP.\nfunc checkManage(user *model.User, reqPath string, permBit uint) error {\n\tif err := checkAccess(user, reqPath); err != nil {\n\t\treturn err\n\t}\n\tperm := common.MergeRolePermissions(user, reqPath)\n\tif !user.IsAdmin() && !common.HasPermission(perm, common.PermMCPManage) {\n\t\treturn fmt.Errorf(\"MCP manage not permitted\")\n\t}\n\tif !user.IsAdmin() && !common.HasPermission(perm, permBit) {","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/server/mcp/auth.go#L109-L145","documentation":"Returned by checkAccess (server/mcp/auth.go:127) when common.CanAccessWithRoles rejects the user for reqPath based on the nearest meta's access rules. This is the read-level path gate evaluated before any MCP permission bit: the user's roles must satisfy the meta visibility and role restrictions attached to the path or a parent directory.","triggerScenarios":"A non-admin user whose role scope (base path) does not cover reqPath; a directory meta marked hidden or restricted to other roles; the guest account hitting a path with a restrictive meta.","commonSituations":"Roles scoped to /data/team-a while the tool requests /data/team-b; metas set to private on upload roots; newly added meta restrictions breaking previously working MCP flows.","solutions":["Check that the user's role base path covers the requested path","Inspect the nearest meta on the path and relax its visibility or role list if appropriate","Switch to an account whose roles cover the path","Remember the check walks up: a meta on any ancestor applies"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// before the call, confirm the path is inside one of the user's role base paths\nfunc covered(userRoles []Role, p string) bool {\n  for _, r := range userRoles { if isSub(r.BasePath, p) { return true } }\n  return false\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep role base paths aligned with the directories tools touch","Remember metas on ancestor directories apply to descendants","After adding restrictive metas, re-test MCP paths"],"tags":["mcp","permissions","meta","path-access"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}