{"record":{"id":"8d3bef3dea18a893","repo":"siyuan-note/siyuan","slug":"oauth-protected-resource-metadata-has-no-authoriza","errorCode":null,"errorMessage":"OAuth protected resource metadata has no authorization server","messagePattern":"OAuth protected resource metadata has no authorization server","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/mcp/client/oauth.go","lineNumber":507,"sourceCode":"\t*oauthex.ProtectedResourceMetadata\n\tMetadataURL string\n}\n\nfunc discoverProtectedResource(ctx context.Context, challenges []oauthex.Challenge, resource string, client *http.Client) (*discoveredProtectedResource, error) {\n\tmetadataURL := \"\"\n\tfor _, challenge := range challenges {\n\t\tif strings.EqualFold(challenge.Scheme, \"bearer\") && challenge.Params[\"resource_metadata\"] != \"\" {\n\t\t\tmetadataURL = challenge.Params[\"resource_metadata\"]\n\t\t\tbreak\n\t\t}\n\t}\n\tfor _, candidate := range protectedResourceURLs(metadataURL, resource) {\n\t\tprm, err := oauthex.GetProtectedResourceMetadata(ctx, candidate.URL, candidate.Resource, client)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tif len(prm.AuthorizationServers) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"OAuth protected resource metadata has no authorization server\")\n\t\t}\n\t\treturn &discoveredProtectedResource{ProtectedResourceMetadata: prm, MetadataURL: candidate.URL}, nil\n\t}\n\treturn nil, fmt.Errorf(\"OAuth protected resource metadata not found\")\n}\n\nfunc (h *mcpOAuthHandler) validateCredentialIssuer(ctx context.Context, credential oauthCredential) (bool, error) {\n\tvar challenges []oauthex.Challenge\n\tresource := h.server.URL\n\tif credential.ResourceMetadataURL != \"\" {\n\t\tchallenges = []oauthex.Challenge{{Scheme: \"bearer\", Params: map[string]string{\"resource_metadata\": credential.ResourceMetadataURL}}}\n\t\tresource = credential.Resource\n\t}\n\tprm, err := discoverProtectedResource(ctx, challenges, resource, h.client)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"validate OAuth protected resource: %w\", err)\n\t}\n\tif prm.Resource != credential.Resource || len(prm.AuthorizationServers) == 0 {","sourceCodeStart":489,"sourceCodeEnd":525,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/mcp/client/oauth.go#L489-L525","documentation":"OAuth protected-resource discovery fetches the resource metadata (RFC 9728) and requires it to list at least one authorization server in the AuthorizationServers array, which is needed to find the AS metadata and run the flow. If the fetched metadata parses but lists no authorization servers, discovery fails with this error.","triggerScenarios":"discoverProtectedResource successfully called oauthex.GetProtectedResourceMetadata for a candidate URL, but prm.AuthorizationServers was empty (len == 0).","commonSituations":"MCP server publishes incomplete RFC 9728 metadata (missing authorization_servers array); stale/cached metadata after the server's auth setup changed; wrong metadata URL candidate returning a generic/incomplete document.","solutions":["Fix the MCP/protected-resource server to include at least one entry in authorization_servers in its RFC 9728 metadata","Verify you are hitting the correct protected resource metadata URL (check WWW-Authenticate challenge for the resource_metadata URL)","Clear any cached/stale metadata and re-run discovery","If you cannot change the server, supply the authorization server configuration manually or pre-register credentials"],"exampleFix":"// before (protected resource metadata)\n{\"resource\":\"https://mcp.example.com\",\"authorization_servers\":[]}\n// after\n{\"resource\":\"https://mcp.example.com\",\"authorization_servers\":[\"https://auth.example.com\"]}","handlingStrategy":"validation","validationCode":"// Fetch and validate the resource metadata before calling Authorize\nprm, err := oauthex.GetProtectedResourceMetadata(ctx, metadataURL, resource, client)\nif err == nil && len(prm.AuthorizationServers) == 0 {\n    return fmt.Errorf(\"metadata lacks authorization_servers; fix server config\")\n}","typeGuard":null,"tryCatchPattern":"if err := h.Authorize(ctx, false); err != nil {\n    if strings.Contains(err.Error(), \"no authorization server\") {\n        // fix the MCP server's RFC 9728 metadata before retrying\n    }\n}","preventionTips":["Validate the MCP server's RFC 9728 metadata (authorization_servers non-empty) during deployment","Read the WWW-Authenticate resource_metadata URL from the 401 challenge rather than guessing","Invalidate cached metadata after changing the server's auth configuration"],"tags":["oauth","mcp","discovery","rfc9728"],"backgroundTag":"empty-required-field","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}