{"record":{"id":"9fe5601664f1bf07","repo":"AlistGo/alist","slug":"mcp-manage-not-permitted","errorCode":null,"errorMessage":"MCP manage not permitted","messagePattern":"MCP manage not permitted","errorType":"http","errorClass":null,"httpStatus":403,"severity":"error","filePath":"server/mcp/auth.go","lineNumber":143,"sourceCode":"\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) {\n\t\treturn fmt.Errorf(\"permission denied for this operation\")\n\t}\n\treturn nil\n}\n\n// UserContextFunc returns an HTTPContextFunc that injects a specific user (for STDIO mode).\nfunc userContextMiddleware(user *model.User) func(ctx context.Context) context.Context {\n\treturn func(ctx context.Context) context.Context {\n\t\treturn context.WithValue(ctx, userKey, user)\n\t}\n}\n\n// resolveUserForStdio resolves a user by username for STDIO mode.\nfunc resolveUserForStdio(username string) (*model.User, error) {\n\tusername = strings.TrimSpace(username)\n\tif username == \"\" || username == \"admin\" {","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/server/mcp/auth.go#L125-L161","documentation":"Returned by checkManage (server/mcp/auth.go:143) when a write-oriented MCP operation is attempted by a non-admin whose merged permissions lack PermMCPManage. It fires after checkAccess has already passed, so path access and the MCP access bit are fine — the additional 'manage' (write-level) grant is missing.","triggerScenarios":"A non-admin user with mcp access but not mcp manage calls a mutating MCP tool (write, rename, or delete flows) that routes through checkManage.","commonSituations":"Read-only MCP roles being reused for maintenance scripts; permission sets cloned from a read role and never given the manage bit.","solutions":["Grant the MCP manage permission to the role performing write operations","Use separate read-only and read-write roles so the manage bit is deliberate","Verify with the permission matrix in the admin UI before scripting writes"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// before scripting write operations, assert the role carries both mcp access and mcp manage","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Separate read-only and read-write MCP roles","Add the manage bit only to roles that perform mutations"],"tags":["mcp","permissions","rbac","write-access"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}