Agents openfeature-eng
Implement OpenFeature feature flags in software projects. Use when adding feature flags with OpenFeature SDKs, configuring providers, setting up evaluation context, or integrating the OpenFeature MCP Server.
install
source · Clone the upstream repo
git clone https://github.com/aRustyDev/agents
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/aRustyDev/agents "$T" && mkdir -p ~/.claude/skills && cp -r "$T/content/skills/openfeature-eng" ~/.claude/skills/arustydev-agents-openfeature-eng && rm -rf "$T"
manifest:
content/skills/openfeature-eng/SKILL.mdsource content
OpenFeature Implementation
Guide for implementing OpenFeature - the open standard for feature flag management - across server and client applications.
When to Use This Skill
- Adding feature flags to a new or existing project
- Implementing OpenFeature Server SDKs (Go, Java, .NET, Node.js, PHP, Python, Ruby, Rust, Dart)
- Implementing OpenFeature Client SDKs (JavaScript, React, Angular, Kotlin, iOS/Swift)
- Configuring feature flag providers (flagd, LaunchDarkly, Split, etc.)
- Setting up evaluation context for targeting
- Using the OpenFeature MCP Server
This skill does NOT cover:
- Creating custom OpenFeature providers (see provider development docs)
- Vendor-specific flag management UIs
- Feature flag strategy/design patterns
Core Concepts
OpenFeature Architecture
┌─────────────────────────────────────────────────────────────┐ │ Application Code │ │ client.getBooleanValue("feature-x", false, context) │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ OpenFeature SDK │ │ - Evaluation API │ │ - Hooks (before/after/error/finally) │ │ - Event handling │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Provider │ │ flagd | LaunchDarkly | Split | CloudBees | In-Memory │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Flag Management │ │ Flag definitions, rules, segments, rollouts │ └─────────────────────────────────────────────────────────────┘
Key Terms
| Term | Description |
|---|---|
| Provider | Backend that evaluates flags (flagd, LaunchDarkly, etc.) |
| Client | SDK instance for evaluating flags |
| Context | User/request attributes for targeting |
| Hook | Lifecycle callbacks for logging, metrics |
| Flag | Feature toggle with key, type, and default value |
Server SDK Implementations
Go
go get github.com/open-feature/go-sdk
package main import ( "context" "github.com/open-feature/go-sdk/openfeature" "github.com/open-feature/go-sdk-contrib/providers/flagd/pkg" ) func main() { // Set up provider provider := flagd.NewProvider() openfeature.SetProvider(provider) // Get client client := openfeature.NewClient("my-app") // Evaluation context ctx := openfeature.NewEvaluationContext( "user-123", map[string]interface{}{ "email": "user@example.com", "tier": "premium", }, ) // Evaluate flags enabled, _ := client.BooleanValue( context.Background(), "new-feature", false, ctx, ) if enabled { // New feature code } }
Java
<dependency> <groupId>dev.openfeature</groupId> <artifactId>sdk</artifactId> <version>1.7.0</version> </dependency>
import dev.openfeature.sdk.*; import dev.openfeature.contrib.providers.flagd.FlagdProvider; public class FeatureFlags { public static void main(String[] args) { // Set provider OpenFeatureAPI api = OpenFeatureAPI.getInstance(); api.setProvider(new FlagdProvider()); // Get client Client client = api.getClient("my-app"); // Evaluation context EvaluationContext ctx = new ImmutableContext("user-123", Map.of( "email", new Value("user@example.com"), "tier", new Value("premium") ) ); // Evaluate flags boolean enabled = client.getBooleanValue("new-feature", false, ctx); if (enabled) { // New feature code } } }
.NET
dotnet add package OpenFeature dotnet add package OpenFeature.Contrib.Providers.Flagd
using OpenFeature; using OpenFeature.Contrib.Providers.Flagd; // Set provider var provider = new FlagdProvider(); await Api.Instance.SetProviderAsync(provider); // Get client var client = Api.Instance.GetClient("my-app"); // Evaluation context var context = EvaluationContext.Builder() .SetTargetingKey("user-123") .Set("email", "user@example.com") .Set("tier", "premium") .Build(); // Evaluate flags var enabled = await client.GetBooleanValueAsync("new-feature", false, context); if (enabled) { // New feature code }
Node.js / TypeScript
npm install @openfeature/server-sdk @openfeature/flagd-provider
import { OpenFeature } from '@openfeature/server-sdk'; import { FlagdProvider } from '@openfeature/flagd-provider'; // Set provider await OpenFeature.setProviderAndWait(new FlagdProvider()); // Get client const client = OpenFeature.getClient('my-app'); // Evaluation context const context = { targetingKey: 'user-123', email: 'user@example.com', tier: 'premium', }; // Evaluate flags const enabled = await client.getBooleanValue('new-feature', false, context); if (enabled) { // New feature code }
NestJS Integration
npm install @openfeature/server-sdk @openfeature/nestjs-sdk @openfeature/flagd-provider
// app.module.ts import { Module } from '@nestjs/common'; import { OpenFeatureModule } from '@openfeature/nestjs-sdk'; import { FlagdProvider } from '@openfeature/flagd-provider'; @Module({ imports: [ OpenFeatureModule.forRoot({ defaultProvider: new FlagdProvider(), }), ], }) export class AppModule {}
// feature.service.ts import { Injectable } from '@nestjs/common'; import { OpenFeatureClient, BooleanFeatureFlag } from '@openfeature/nestjs-sdk'; import { Client } from '@openfeature/server-sdk'; @Injectable() export class FeatureService { constructor( @OpenFeatureClient() private client: Client, ) {} @BooleanFeatureFlag({ flagKey: 'new-feature', defaultValue: false, }) async isNewFeatureEnabled(): Promise<boolean> { return this.client.getBooleanValue('new-feature', false); } }
Python
pip install openfeature-sdk openfeature-provider-flagd
from openfeature import api from openfeature.evaluation_context import EvaluationContext from openfeature.contrib.provider.flagd import FlagdProvider # Set provider api.set_provider(FlagdProvider()) # Get client client = api.get_client("my-app") # Evaluation context context = EvaluationContext( targeting_key="user-123", attributes={ "email": "user@example.com", "tier": "premium", } ) # Evaluate flags enabled = client.get_boolean_value("new-feature", False, context) if enabled: # New feature code pass
Ruby
gem install openfeature-sdk openfeature-flagd-provider
require 'openfeature/sdk' require 'openfeature/flagd/provider' # Set provider OpenFeature::SDK.configure do |config| config.set_provider(OpenFeature::Flagd::Provider.new) end # Get client client = OpenFeature::SDK.build_client(name: 'my-app') # Evaluation context context = OpenFeature::SDK::EvaluationContext.new( targeting_key: 'user-123', email: 'user@example.com', tier: 'premium' ) # Evaluate flags enabled = client.fetch_boolean_value( flag_key: 'new-feature', default_value: false, evaluation_context: context ) if enabled # New feature code end
Rust
[dependencies] open-feature = "0.2"
use open_feature::{ provider::NoOpProvider, EvaluationContext, OpenFeature, }; #[tokio::main] async fn main() { // Set provider let mut api = OpenFeature::singleton_mut().await; api.set_provider(NoOpProvider::default()).await; // Get client let client = api.create_client(); // Evaluation context let context = EvaluationContext::default() .with_targeting_key("user-123") .with_custom_field("email", "user@example.com") .with_custom_field("tier", "premium"); // Evaluate flags let enabled = client .get_bool_value("new-feature", Some(&context), None) .await .unwrap_or(false); if enabled { // New feature code } }
Dart
dependencies: openfeature_dart_sdk: ^0.1.0
import 'package:openfeature_dart_sdk/openfeature_dart_sdk.dart'; void main() async { // Set provider final api = OpenFeatureAPI.instance; await api.setProvider(InMemoryProvider()); // Get client final client = api.getClient('my-app'); // Evaluation context final context = EvaluationContext( targetingKey: 'user-123', attributes: { 'email': 'user@example.com', 'tier': 'premium', }, ); // Evaluate flags final enabled = await client.getBooleanValue( 'new-feature', defaultValue: false, context: context, ); if (enabled) { // New feature code } }
PHP
composer require open-feature/sdk open-feature/flagd-provider
<?php use OpenFeature\OpenFeatureAPI; use OpenFeature\Providers\Flagd\FlagdProvider; use OpenFeature\implementation\flags\EvaluationContext; // Set provider $api = OpenFeatureAPI::getInstance(); $api->setProvider(new FlagdProvider()); // Get client $client = $api->getClient('my-app'); // Evaluation context $context = new EvaluationContext( 'user-123', [ 'email' => 'user@example.com', 'tier' => 'premium', ] ); // Evaluate flags $enabled = $client->getBooleanValue('new-feature', false, $context); if ($enabled) { // New feature code }
Client SDK Implementations
React
npm install @openfeature/react-sdk @openfeature/web-sdk @openfeature/flagd-web-provider
// App.tsx import { OpenFeatureProvider, useBooleanFlagValue } from '@openfeature/react-sdk'; import { FlagdWebProvider } from '@openfeature/flagd-web-provider'; const provider = new FlagdWebProvider({ host: 'localhost', port: 8013, }); function App() { return ( <OpenFeatureProvider provider={provider}> <FeatureComponent /> </OpenFeatureProvider> ); } function FeatureComponent() { const enabled = useBooleanFlagValue('new-feature', false); return enabled ? <NewFeature /> : <OldFeature />; }
// With evaluation context import { useOpenFeatureClient } from '@openfeature/react-sdk'; function UserFeature({ userId }: { userId: string }) { const client = useOpenFeatureClient(); const enabled = useBooleanFlagValue('premium-feature', false, { targetingKey: userId, tier: 'premium', }); return enabled ? <PremiumFeature /> : <StandardFeature />; }
Angular
npm install @openfeature/angular-sdk @openfeature/web-sdk
// app.module.ts import { NgModule } from '@angular/core'; import { OpenFeatureModule } from '@openfeature/angular-sdk'; import { InMemoryProvider } from '@openfeature/web-sdk'; @NgModule({ imports: [ OpenFeatureModule.forRoot({ provider: new InMemoryProvider({ 'new-feature': { disabled: false, variants: { on: true, off: false }, defaultVariant: 'on' }, }), }), ], }) export class AppModule {}
// feature.component.ts import { Component } from '@angular/core'; import { BooleanFeatureFlag } from '@openfeature/angular-sdk'; @Component({ selector: 'app-feature', template: ` <div *ngIf="isEnabled$ | async"> New Feature Content </div> `, }) export class FeatureComponent { @BooleanFeatureFlag({ flagKey: 'new-feature', defaultValue: false }) isEnabled$!: Observable<boolean>; }
Kotlin (Android)
// build.gradle.kts dependencies { implementation("dev.openfeature:android-sdk:0.3.0") }
import dev.openfeature.sdk.OpenFeatureAPI import dev.openfeature.sdk.EvaluationContext import dev.openfeature.contrib.providers.flagd.FlagdProvider class FeatureFlags(context: Context) { private val client: Client init { // Set provider val api = OpenFeatureAPI.getInstance() api.setProvider(FlagdProvider()) client = api.getClient("my-app") } suspend fun isNewFeatureEnabled(userId: String): Boolean { val context = EvaluationContext( targetingKey = userId, attributes = mapOf( "platform" to "android", "version" to BuildConfig.VERSION_NAME ) ) return client.getBooleanValue("new-feature", false, context) } }
iOS / Swift
// Package.swift dependencies: [ .package(url: "https://github.com/open-feature/swift-sdk.git", from: "0.1.0") ]
import OpenFeature class FeatureFlags { private let client: Client init() { // Set provider let api = OpenFeatureAPI.shared api.setProvider(provider: InMemoryProvider()) client = api.getClient(name: "my-app") } func isNewFeatureEnabled(userId: String) async -> Bool { let context = MutableContext( targetingKey: userId, structure: MutableStructure( attributes: [ "platform": .string("ios"), "version": .string(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "") ] ) ) return await client.getBooleanValue( key: "new-feature", defaultValue: false, context: context ) } }
Provider Configuration
flagd (Open Source)
# flagd-config.yaml flags: new-feature: state: ENABLED variants: "on": true "off": false defaultVariant: "off" targeting: if: - in: - var: tier - ["premium", "enterprise"] - "on" - "off"
# Run flagd docker run -p 8013:8013 -v ./flagd-config.yaml:/config.yaml \ ghcr.io/open-feature/flagd:latest start --uri file:/config.yaml
In-Memory Provider (Testing)
import { InMemoryProvider } from '@openfeature/server-sdk'; const flags = { 'new-feature': { disabled: false, variants: { on: true, off: false }, defaultVariant: 'on', }, 'feature-limit': { disabled: false, variants: { low: 10, medium: 50, high: 100 }, defaultVariant: 'medium', }, }; await OpenFeature.setProviderAndWait(new InMemoryProvider(flags));
OpenFeature MCP Server
The OpenFeature MCP Server enables flag evaluation through Claude.
Installation
npx @openfeature/mcp-server
Configuration
{ "mcpServers": { "openfeature": { "command": "npx", "args": ["@openfeature/mcp-server"], "env": { "FLAGD_HOST": "localhost", "FLAGD_PORT": "8013" } } } }
Usage
The MCP server provides tools for:
- Evaluating feature flags
- Listing available flags
- Getting flag metadata
Hooks
Logging Hook
import { Hook, HookContext, EvaluationDetails } from '@openfeature/server-sdk'; const loggingHook: Hook = { before: (hookContext: HookContext) => { console.log(`Evaluating flag: ${hookContext.flagKey}`); }, after: (hookContext: HookContext, details: EvaluationDetails<any>) => { console.log(`Flag ${hookContext.flagKey} = ${details.value}`); }, error: (hookContext: HookContext, error: Error) => { console.error(`Error evaluating ${hookContext.flagKey}:`, error); }, }; client.addHooks(loggingHook);
Metrics Hook
const metricsHook: Hook = { after: (hookContext, details) => { metrics.increment('feature_flag.evaluation', { flag: hookContext.flagKey, variant: details.variant, reason: details.reason, }); }, };
Testing
Unit Testing with Mocks
import { OpenFeature, InMemoryProvider } from '@openfeature/server-sdk'; describe('Feature Tests', () => { beforeEach(async () => { await OpenFeature.setProviderAndWait( new InMemoryProvider({ 'new-feature': { disabled: false, variants: { on: true, off: false }, defaultVariant: 'on', }, }) ); }); it('should show new feature when enabled', async () => { const client = OpenFeature.getClient(); const enabled = await client.getBooleanValue('new-feature', false); expect(enabled).toBe(true); }); });
Best Practices
- Set default values defensively - Always provide sensible defaults
- Use targeting keys - Enable user-level targeting and consistency
- Add evaluation context - Include relevant attributes for targeting
- Implement hooks - Add logging and metrics for observability
- Clean up flags - Remove flags after features are fully rolled out
- Use typed values - Prefer specific value types over strings
- Test both paths - Test feature on and off states