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.md
source 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

TermDescription
ProviderBackend that evaluates flags (flagd, LaunchDarkly, etc.)
ClientSDK instance for evaluating flags
ContextUser/request attributes for targeting
HookLifecycle callbacks for logging, metrics
FlagFeature 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

  1. Set default values defensively - Always provide sensible defaults
  2. Use targeting keys - Enable user-level targeting and consistency
  3. Add evaluation context - Include relevant attributes for targeting
  4. Implement hooks - Add logging and metrics for observability
  5. Clean up flags - Remove flags after features are fully rolled out
  6. Use typed values - Prefer specific value types over strings
  7. Test both paths - Test feature on and off states

References