{"record":{"id":"cfaf527efb468da3","repo":"Tencent/WeKnora","slug":"principal-context-is-required-to-connect-to-oauth","errorCode":null,"errorMessage":"principal context is required to connect to OAuth MCP service %s","messagePattern":"principal context is required to connect to OAuth MCP service (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/mcp/manager.go","lineNumber":76,"sourceCode":"func (m *MCPManager) GetOrCreateClient(ctx context.Context, service *types.MCPService) (MCPClient, error) {\n\t// Check if service is enabled\n\tif !service.Enabled {\n\t\treturn nil, fmt.Errorf(\"MCP service %s is not enabled\", service.Name)\n\t}\n\n\t// Stdio transport is disabled for security reasons\n\tif service.TransportType == types.MCPTransportStdio {\n\t\treturn nil, fmt.Errorf(\"stdio transport is disabled for security reasons; please use SSE or HTTP Streamable transport instead\")\n\t}\n\n\tvar tenantID uint64\n\tvar principal types.Principal\n\tif service.AuthConfig.IsOAuth() {\n\t\ttenantID, _ = types.TenantIDFromContext(ctx)\n\t\tprincipal, _ = types.PrincipalFromContext(ctx)\n\t\tprincipal = types.MCPOAuthPrincipalFromContext(ctx)\n\t\tif !principal.Valid() {\n\t\t\treturn nil, fmt.Errorf(\"principal context is required to connect to OAuth MCP service %s\", service.Name)\n\t\t}\n\t}\n\tkey := cacheKey(service, principal)\n\n\t// For SSE/HTTP Streamable, check if client already exists and reuse\n\tm.clientsMu.RLock()\n\tclient, exists := m.clients[key]\n\tm.clientsMu.RUnlock()\n\n\tif exists && client.IsConnected() {\n\t\treturn client, nil\n\t}\n\n\t// Create new client\n\tm.clientsMu.Lock()\n\tdefer m.clientsMu.Unlock()\n\n\t// Double check after acquiring write lock","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/mcp/manager.go#L58-L94","documentation":"For OAuth-enabled MCP services, GetOrCreateClient requires a valid principal in the request context (from types.PrincipalFromContext / MCPOAuthPrincipalFromContext) so each identity can use its own token. If the principal is missing or invalid (Principal.Valid() == false), it returns \"principal context is required to connect to OAuth MCP service %s\".","triggerScenarios":"Calling GetOrCreateClient (or GetMCPServiceTools/Resources) with a context lacking the principal when service.AuthConfig.IsOAuth() is true — e.g. background jobs, webhook handlers, or code paths that build a bare context.Background() without auth middleware.","commonSituations":"Calling MCP tools from a cron/worker goroutine where auth middleware never ran; forgetting to inject the principal after extracting a tenant ID; a test constructing ctx without the principal helper.","solutions":["Ensure the request context passes through the auth middleware that injects the principal before MCP calls","Use types.WithPrincipal (or the equivalent context helper) to attach a valid types.Principal to the context","If no user context exists (system job), connect as a designated service principal instead"],"exampleFix":"// before\nclient, err := manager.GetOrCreateClient(context.Background(), oauthSvc)\n// after\nctx := types.WithPrincipal(context.Background(), principal) // principal from auth/session\nclient, err := manager.GetOrCreateClient(ctx, oauthSvc)","handlingStrategy":"validation","validationCode":"principal, _ := types.PrincipalFromContext(ctx)\nif !principal.Valid() {\n    return errors.New(\"request context lacks a valid principal; required for OAuth MCP services\")\n}","typeGuard":"func hasPrincipal(ctx context.Context) bool {\n    p, _ := types.PrincipalFromContext(ctx)\n    return p.Valid()\n}","tryCatchPattern":"client, err := manager.GetOrCreateClient(ctx, svc)\nif err != nil && strings.Contains(err.Error(), \"principal context is required\") {\n    return fmt.Errorf(\"attach authenticated principal to ctx before using OAuth service %q\", svc.Name)\n}","preventionTips":["Route all MCP calls through middleware that injects the principal","For background jobs, mint a service principal and attach it to the context explicitly","Check service.AuthConfig.IsOAuth() before choosing which context a call needs"],"tags":["mcp","oauth","context","principal"],"backgroundTag":"missing-auth-context","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}