Documentation Index Fetch the complete documentation index at: https://docs.agentfront.dev/llms.txt
Use this file to discover all available pages before exploring further.
The Feature Flags Plugin gates tools, resources, prompts, and skills based on feature flag evaluation — enabling progressive rollouts, A/B testing, and dynamic capability management for FrontMCP servers.
Why Use Feature Flags?
Declarative Gating Annotate capabilities with featureFlag metadata — no conditional logic in your tool code
Multiple Providers Split.io, LaunchDarkly, Unleash, static flags, or your own custom adapter
Per-User Targeting Evaluate flags per user or session via auth context for targeted rollouts
Execution Gate Blocks direct tool/call invocations that bypass the list filter — no sneaking past disabled flags
Installation
npm install @frontmcp/plugin-feature-flags
How It Works
Hook Registration
The plugin registers hooks on list flows for tools, resources, prompts, and skills
Metadata Collection
When capabilities are listed, the plugin collects featureFlag refs from each entry’s metadata
Batch Evaluation
Flags are batch-evaluated via the configured adapter in a single call
Capability Filtering
Capabilities with disabled flags are filtered out before reaching the client. An execution gate also blocks direct tool/call for disabled flags.
Quick Start
Basic Setup
Class-based App
FrontMcp decorator
import { FrontMcp , App } from ' @frontmcp/sdk ' ;
import { FeatureFlagPlugin } from ' @frontmcp/plugin-feature-flags ' ;
@ App ({
id : ' my-app ' ,
name : ' My App ' ,
plugins : [
FeatureFlagPlugin . init ({
adapter : ' static ' ,
flags : { ' beta-search ' : true , ' experimental-agent ' : false },
}),
],
tools : [ /* your tools */ ],
})
class MyApp {}
@ FrontMcp ({
info : { name : ' My Server ' , version : ' 1.0.0 ' },
apps : [ MyApp ],
})
export default class Server {}
import { Tool , ToolContext } from ' @frontmcp/sdk ' ;
import { z } from ' @frontmcp/sdk ' ;
@ Tool ({
name : ' beta-search ' ,
description : ' Search with beta features enabled ' ,
inputSchema : { query : z . string () },
featureFlag : ' beta-search ' , // Hidden when flag is off
})
export default class BetaSearchTool extends ToolContext {
async execute ( input : { query : string }) {
return ` Beta results for: ${ input . query } ` ;
}
}
Programmatic Flag Checks
The plugin extends all execution contexts with this.featureFlags:
@ Tool ({
name : ' smart-tool ' ,
description : ' A tool that adapts based on flags ' ,
inputSchema : { query : z . string () },
})
export default class SmartTool extends ToolContext {
async execute ( input : { query : string }) {
const useBeta = await this . featureFlags . isEnabled ( ' beta-algorithm ' );
if ( useBeta ) {
return this . betaSearch ( input . query );
}
return this . standardSearch ( input . query );
}
}
Adapters
Static
Best for: Development, testing, and demos with fixed flag values.
FeatureFlagPlugin . init ({
adapter : ' static ' ,
flags : {
' beta-search ' : true ,
' experimental-agent ' : false ,
' premium-feature ' : true ,
},
})
Split.io
FeatureFlagPlugin . init ({
adapter : ' splitio ' ,
config : {
apiKey : process . env . SPLITIO_API_KEY !,
},
})
Requires the @splitsoftware/splitio peer dependency: npm install @splitsoftware/splitio
LaunchDarkly
FeatureFlagPlugin . init ({
adapter : ' launchdarkly ' ,
config : {
sdkKey : process . env . LAUNCHDARKLY_SDK_KEY !,
},
})
Requires the @launchdarkly/node-server-sdk peer dependency: npm install @launchdarkly/node-server-sdk
Unleash
FeatureFlagPlugin . init ({
adapter : ' unleash ' ,
config : {
url : process . env . UNLEASH_URL !,
appName : ' my-mcp-server ' ,
apiKey : process . env . UNLEASH_API_KEY ,
},
})
Requires the unleash-client peer dependency: npm install unleash-client
Custom
Provide your own adapter implementing the FeatureFlagAdapter interface:
import type { FeatureFlagAdapter } from ' @frontmcp/plugin-feature-flags ' ;
const myAdapter : FeatureFlagAdapter = {
async initialize () { /* connect to your service */ },
async isEnabled ( flagKey , context ) {
return await myService . checkFlag ( flagKey , context . userId );
},
async getVariant ( flagKey , context ) {
const variant = await myService . getVariant ( flagKey , context . userId );
return { name : variant . name , value : variant . value , enabled : variant . enabled };
},
async evaluateFlags ( flagKeys , context ) {
const results = new Map < string , boolean >();
for ( const key of flagKeys ) {
results . set ( key , await myService . checkFlag ( key , context . userId ));
}
return results ;
},
async destroy () { /* cleanup */ },
};
FeatureFlagPlugin . init ({
adapter : ' custom ' ,
adapterInstance : myAdapter ,
})
The FeatureFlagAdapter interface:
interface FeatureFlagAdapter {
initialize (): Promise < void >;
isEnabled ( flagKey : string , context : FeatureFlagContext ): Promise < boolean >;
getVariant ( flagKey : string , context : FeatureFlagContext ): Promise < FeatureFlagVariant >;
evaluateFlags ( flagKeys : string [], context : FeatureFlagContext ): Promise < Map < string , boolean >>;
destroy (): Promise < void >;
}
Annotating Capabilities
Add featureFlag to any tool, resource, prompt, or skill metadata. Capabilities with disabled flags are hidden from list responses and blocked from direct invocation.
String Shorthand
@ Tool ({ name : ' beta-search ' , featureFlag : ' beta-search ' })
@ Resource ({ uri : ' data://beta ' , featureFlag : ' beta-data ' })
@ Prompt ({ name : ' beta-prompt ' , featureFlag : ' beta-prompt ' })
@ Skill ({ name : ' beta-skill ' , featureFlag : ' beta-skill ' })
Use the object form to specify a defaultValue — the fallback when the adapter throws or the flag is unknown:
@ Tool ({
name : ' critical-tool ' ,
featureFlag : { key : ' critical-tool ' , defaultValue : true }, // fail-open
})
FeatureFlagRef Fields
Field Type Description keystringThe flag key to evaluate defaultValuebooleanFallback value when evaluation fails (default: false)
When using the string shorthand (featureFlag: 'key'), defaultValue is false.
Plugin Options
adapter
'static' | 'splitio' | 'launchdarkly' | 'unleash' | 'custom'
required
The feature flag provider to use
flags
Record<string, boolean | FeatureFlagVariant>
Static flag values (only used with adapter: 'static')
Provider-specific configuration (varies by adapter):
Split.io : { apiKey: string }
LaunchDarkly : { sdkKey: string }
Unleash : { url: string, appName: string, apiKey?: string }
Custom adapter instance (only used with adapter: 'custom')
Global fallback when the adapter throws an error during evaluation
cacheStrategy
'session' | 'request' | 'none'
default: "'none'"
How to cache flag evaluation results:
session — cache per session, expires after cacheTtlMs
request — cache per request lifecycle
none — no caching, evaluate every time
Cache TTL in milliseconds (used with cacheStrategy: 'session')
userIdResolver
(ctx: FrontMcpContext) => string | undefined
Custom function to extract the user ID from the request context. By default, the plugin reads authInfo.extra.sub, authInfo.extra.userId, or authInfo.clientId.
attributesResolver
(ctx: FrontMcpContext) => Record<string, unknown>
Custom function to extract targeting attributes from the request context. Attributes are passed to the adapter for per-user targeting rules.
API Reference
FeatureFlagAccessor Methods
Access via this.featureFlags in any execution context (ToolContext, AgentContext, etc.).
isEnabled(flagKey, defaultValue?)
Check if a feature flag is enabled. Uses caching if configured. const enabled = await this . featureFlags . isEnabled ( ' beta-feature ' );
const safe = await this . featureFlags . isEnabled ( ' critical-feature ' , true ); // fail-open
getVariant(flagKey)
Promise<FeatureFlagVariant>
Get the variant for a multi-variate flag const variant = await this . featureFlags . getVariant ( ' search-algorithm ' );
console . log ( variant . name ); // e.g., 'v2'
console . log ( variant . value ); // e.g., { boost: 1.5 }
console . log ( variant . enabled ); // true
evaluateFlags(flagKeys)
Promise<Map<string, boolean>>
Batch evaluate multiple flags at once const results = await this . featureFlags . evaluateFlags ([ ' flag-a ' , ' flag-b ' , ' flag-c ' ]);
if ( results . get ( ' flag-a ' )) {
// flag-a is enabled
}
Resolve a FeatureFlagRef (string or object) to a boolean const ref = { key : ' my-flag ' , defaultValue : true };
const enabled = await this . featureFlags . resolveRef ( ref );
Best Practices
Use static adapter for development
The static adapter requires no external dependencies and gives you instant, predictable flag values during development. Switch to a real adapter (Split.io, LaunchDarkly, Unleash) for staging and production. FeatureFlagPlugin . init ({
adapter : ' static ' ,
flags : { ' beta-tools ' : true , ' experimental-agent ' : true },
})
Set defaultValue to control failure behavior
When the adapter throws (network error, service down), defaultValue controls whether the flag is treated as enabled or disabled. Set it globally or per-flag: // Global default: treat unknown flags as disabled
FeatureFlagPlugin . init ({
adapter : ' launchdarkly ' ,
config : { sdkKey : ' ... ' },
defaultValue : false ,
})
// Per-flag: critical features should fail-open
@ Tool ({
name : ' core-tool ' ,
featureFlag : { key : ' core-tool ' , defaultValue : true },
})
Use caching with external adapters
External adapters involve network calls. Use cacheStrategy: 'session' to avoid repeated evaluations: FeatureFlagPlugin . init ({
adapter : ' splitio ' ,
config : { apiKey : ' ... ' },
cacheStrategy : ' session ' ,
cacheTtlMs : 30000 , // 30 seconds
})
Use object-form for critical features
Complete Example
import { FrontMcp , App , Tool , ToolContext , Resource , ResourceContext , Prompt , PromptContext } from ' @frontmcp/sdk ' ;
import { FeatureFlagPlugin } from ' @frontmcp/plugin-feature-flags ' ;
import { z } from ' @frontmcp/sdk ' ;
// Configure with static adapter
const featureFlagPlugin = FeatureFlagPlugin . init ({
adapter : ' static ' ,
flags : {
' beta-search ' : true ,
' experimental-agent ' : false ,
' premium-reports ' : true ,
},
defaultValue : false ,
});
// Tool gated by a feature flag
@ Tool ({
name : ' beta-search ' ,
description : ' Search with beta algorithm ' ,
inputSchema : { query : z . string () },
featureFlag : ' beta-search ' ,
})
class BetaSearchTool extends ToolContext {
async execute ( input : { query : string }) {
return ` Beta results for: ${ input . query } ` ;
}
}
// Tool with fail-open behavior
@ Tool ({
name : ' always-on-tool ' ,
description : ' A tool that defaults to enabled ' ,
inputSchema : { input : z . string () },
featureFlag : { key : ' always-on ' , defaultValue : true },
})
class AlwaysOnTool extends ToolContext {
async execute ( input : { input : string }) {
return ` Processed: ${ input . input } ` ;
}
}
// Tool with programmatic flag check
@ Tool ({
name : ' adaptive-tool ' ,
description : ' Adapts behavior based on flags ' ,
inputSchema : { data : z . string () },
})
class AdaptiveTool extends ToolContext {
async execute ( input : { data : string }) {
const usePremium = await this . featureFlags . isEnabled ( ' premium-reports ' );
if ( usePremium ) {
return ` Premium report for: ${ input . data } ` ;
}
return ` Standard report for: ${ input . data } ` ;
}
}
// Resource gated by feature flag
@ Resource ({
uri : ' report://premium-status ' ,
name : ' Premium Status Report ' ,
featureFlag : ' premium-reports ' ,
})
class PremiumStatusResource extends ResourceContext {
async read () {
return { contents : [{ uri : ' report://premium-status ' , text : ' Premium data... ' }] };
}
}
// Prompt gated by feature flag
@ Prompt ({
name : ' experimental-analysis ' ,
description : ' Experimental analysis prompt ' ,
featureFlag : ' experimental-agent ' ,
})
class ExperimentalPrompt extends PromptContext {
async execute () {
return { messages : [{ role : ' user ' as const , content : { type : ' text ' as const , text : ' Analyze... ' } }] };
}
}
@ App ({
id : ' flagged-app ' ,
name : ' Flagged App ' ,
plugins : [ featureFlagPlugin ],
tools : [ BetaSearchTool , AlwaysOnTool , AdaptiveTool ],
resources : [ PremiumStatusResource ],
prompts : [ ExperimentalPrompt ],
})
class FlaggedApp {}
@ FrontMcp ({
info : { name : ' Feature Flag Server ' , version : ' 1.0.0 ' },
apps : [ FlaggedApp ],
http : { port : 3000 },
})
export default class Server {}
Links & Resources
Source Code View the feature flags plugin source code
Plugin Guide Learn more about FrontMCP plugins
Remember Plugin For session memory storage
Approval Plugin For tool authorization workflows