{"record":{"id":"6df0fe969107b454","repo":"Tencent/WeKnora","slug":"mcp-service-url-is-required-for-oauth","errorCode":null,"errorMessage":"MCP service URL is required for OAuth","messagePattern":"MCP service URL is required for OAuth","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/mcp/oauth_manager.go","lineNumber":54,"sourceCode":"// in which case in-flight authorization states are kept in memory.\nfunc NewOAuthManager(\n\trepo interfaces.MCPOAuthRepository,\n\tserviceRepo interfaces.MCPServiceRepository,\n\trdb *redis.Client,\n) *OAuthManager {\n\treturn &OAuthManager{\n\t\trepo:        repo,\n\t\tserviceRepo: serviceRepo,\n\t\tstates:      newOAuthStateStore(rdb),\n\t}\n}\n\n// newHandler builds an OAuth handler bound to a service + per-principal token store.\nfunc (m *OAuthManager) newHandler(\n\tctx context.Context, service *types.MCPService, tenantID uint64, principal types.Principal, redirectURI string,\n) (*transport.OAuthHandler, error) {\n\tif service.URL == nil || *service.URL == \"\" {\n\t\treturn nil, fmt.Errorf(\"MCP service URL is required for OAuth\")\n\t}\n\tif err := ValidateServiceOutboundURLs(service); err != nil {\n\t\treturn nil, err\n\t}\n\thttpCfg := secutils.DefaultSSRFSafeHTTPClientConfig()\n\thttpCfg.Timeout = 30 * time.Second\n\tcfg := transport.OAuthConfig{\n\t\tRedirectURI:           redirectURI,\n\t\tScopes:                service.AuthConfig.Scopes,\n\t\tTokenStore:            newDBTokenStore(m.repo, tenantID, principal, service.ID),\n\t\tPKCEEnabled:           true,\n\t\tAuthServerMetadataURL: service.AuthConfig.AuthServerMetadataURL,\n\t\tHTTPClient:            secutils.NewSSRFSafeHTTPClient(httpCfg),\n\t}\n\tif existing, err := m.repo.GetClient(ctx, tenantID, service.ID); err == nil && existing != nil {\n\t\tcfg.ClientID = existing.ClientID\n\t\tcfg.ClientSecret = existing.ClientSecret\n\t}","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/mcp/oauth_manager.go#L36-L72","documentation":"A synchronous validation error raised by OAuthManager.newHandler when the MCP service record has no URL. OAuth flows need a server endpoint to discover metadata and exchange tokens, so a nil/empty service.URL makes OAuth impossible. StartAuthorization and CompleteAuthorization both route through newHandler.","triggerScenarios":"Calling StartAuthorization/StartAuthorizationForService/CompleteAuthorization with a types.MCPService whose URL field is nil or the empty string.","commonSituations":"Service created via import or script without a URL; URL field cleared during an update; config file omitted the service endpoint; migration left legacy rows with NULL url.","solutions":["Set the service URL before enabling OAuth (update the MCP service record with a valid https:// URL)","Add a pre-save validation on service creation requiring URL when AuthConfig is OAuth","Query the DB for services with NULL/empty url and backfill them","Fix the API caller to pass the fully populated service object"],"exampleFix":"// before\nsvc.AuthConfig = types.NewOAuthConfig()\nmanager.StartAuthorizationForService(ctx, svc, tenant, principal, redirect, \"\")\n// after\nif svc.URL == nil || *svc.URL == \"\" {\n    return fmt.Errorf(\"service %d needs a URL before OAuth setup\", svc.ID)\n}\nmanager.StartAuthorizationForService(ctx, svc, tenant, principal, redirect, \"\")","handlingStrategy":"validation","validationCode":"func oauthReady(svc *types.MCPService) error {\n    if svc == nil { return fmt.Errorf(\"service is nil\") }\n    if !svc.AuthConfig.IsOAuth() { return fmt.Errorf(\"service %s is not OAuth\", svc.ID) }\n    if svc.URL == nil || *svc.URL == \"\" { return fmt.Errorf(\"service %s has no URL\", svc.ID) }\n    return nil\n}","typeGuard":"func hasServiceURL(svc *types.MCPService) bool {\n    return svc != nil && svc.URL != nil && *svc.URL != \"\"\n}","tryCatchPattern":"if err := oauthReady(svc); err != nil { return err }\nif _, _, err := mgr.StartAuthorizationForService(ctx, svc, tenantID, principal, redirect, \"\"); err != nil {\n    if strings.Contains(err.Error(), \"URL is required\") { return fmt.Errorf(\"misconfigured service %s: %w\", svc.ID, err) }\n    return err\n}","preventionTips":["Validate service records (URL present when AuthConfig is OAuth) at creation/update time","Add a DB constraint or migration check for NULL urls on OAuth services","Test OAuth setup against seeded service fixtures with URLs","Surface the missing-URL state in admin UI before the connect button"],"tags":["oauth","configuration","validation"],"backgroundTag":"missing-required-config","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}