@frontmcp/plugin-feature-flags 1.8.6 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/esm/index.mjs CHANGED
@@ -1,3 +1,6 @@
1
+ var __frontmcpModule = typeof process !== "undefined" && typeof process.getBuiltinModule === "function" ? process.getBuiltinModule("module") : undefined;
2
+ const require = __frontmcpModule ? __frontmcpModule.createRequire(import.meta.url || 'file:///') : undefined;
3
+ // __frontmcpCreateRequire
1
4
  var __defProp = Object.defineProperty;
2
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -318,6 +321,27 @@ function buildFeatureFlagContext(ctx, config) {
318
321
  };
319
322
  }
320
323
 
324
+ // plugins/plugin-feature-flags/src/feature-flag.errors.ts
325
+ import { PublicMcpError } from "@frontmcp/sdk";
326
+ var FeatureFlagDisabledError = class extends PublicMcpError {
327
+ constructor(kind, entryName, flagKey) {
328
+ super(`${kind} "${entryName}" is disabled by feature flag "${flagKey}"`, "FEATURE_FLAG_DISABLED", 403);
329
+ this.kind = kind;
330
+ this.entryName = entryName;
331
+ this.flagKey = flagKey;
332
+ }
333
+ kind;
334
+ entryName;
335
+ flagKey;
336
+ name = "FeatureFlagDisabledError";
337
+ };
338
+ var FeatureFlagConfigurationError = class extends Error {
339
+ constructor(message) {
340
+ super(message);
341
+ this.name = "FeatureFlagConfigurationError";
342
+ }
343
+ };
344
+
321
345
  // plugins/plugin-feature-flags/src/feature-flag.symbols.ts
322
346
  var FeatureFlagAdapterToken = /* @__PURE__ */ Symbol(
323
347
  "plugin:feature-flags:adapter"
@@ -335,6 +359,7 @@ var FeatureFlagAccessor = class {
335
359
  adapter;
336
360
  ctx;
337
361
  config;
362
+ /** The adapter's answers by flag key: `undefined` when it has none (an unknown flag). */
338
363
  cache = /* @__PURE__ */ new Map();
339
364
  constructor(adapter, ctx, config) {
340
365
  this.adapter = adapter;
@@ -343,27 +368,41 @@ var FeatureFlagAccessor = class {
343
368
  }
344
369
  /**
345
370
  * Check if a feature flag is enabled.
371
+ *
372
+ * `defaultValue` (else the plugin's `defaultValue`, else `false`) is the answer when the adapter
373
+ * throws or has no answer for the flag: a key the static adapter was not given, or one a custom
374
+ * adapter's `evaluateFlags()` omits. A flag the adapter answers keeps its answer, `false` included.
375
+ * It used to apply only when the adapter threw, so `isEnabled('unknown', true)` was `false` (#678).
346
376
  */
347
377
  async isEnabled(flagKey, defaultValue) {
378
+ const answer = await this.evaluate(flagKey);
379
+ return answer ?? defaultValue ?? this.config.defaultValue ?? false;
380
+ }
381
+ /**
382
+ * The adapter's answer for one flag, or `undefined` when it has none or throws. Asked through
383
+ * `evaluateFlags()`, as the execution gates ask, which is how an adapter says it does not know a
384
+ * flag; `isEnabled()` answers `false` for it. A successful answer is cached per `cacheStrategy`.
385
+ */
386
+ async evaluate(flagKey) {
348
387
  const cacheStrategy = this.config.cacheStrategy ?? "none";
349
388
  const cacheTtlMs = this.config.cacheTtlMs ?? 3e4;
350
389
  if (cacheStrategy !== "none") {
351
390
  const cached = this.cache.get(flagKey);
352
391
  if (cached && Date.now() < cached.expiresAt) {
353
- return cached.value;
392
+ return cached.answer;
354
393
  }
355
394
  }
356
- const context = this.buildContext();
357
- let result;
395
+ let answer;
358
396
  try {
359
- result = await this.adapter.isEnabled(flagKey, context);
397
+ const results = await this.adapter.evaluateFlags([flagKey], this.buildContext());
398
+ answer = results.has(flagKey) ? results.get(flagKey) === true : void 0;
360
399
  } catch {
361
- result = defaultValue ?? this.config.defaultValue ?? false;
400
+ return void 0;
362
401
  }
363
402
  if (cacheStrategy !== "none") {
364
- this.cache.set(flagKey, { value: result, expiresAt: Date.now() + cacheTtlMs });
403
+ this.cache.set(flagKey, { answer, expiresAt: Date.now() + cacheTtlMs });
365
404
  }
366
- return result;
405
+ return answer;
367
406
  }
368
407
  /**
369
408
  * Get a feature flag variant (for multi-variate flags).
@@ -412,6 +451,22 @@ var ReadResourceHook = FlowHooksOf("resources:read-resource");
412
451
  var GetPromptHook = FlowHooksOf("prompts:get-prompt");
413
452
  var CompleteHook = FlowHooksOf("completion:complete");
414
453
  var FilterSkillsHook = FlowHooksOf("skills:filter");
454
+ var SUPPORTED_ADAPTERS = ["static", "splitio", "launchdarkly", "unleash", "custom"];
455
+ var REQUIRED_ADAPTER_METHODS = ["isEnabled", "getVariant", "evaluateFlags"];
456
+ function assertCustomAdapter(adapterInstance) {
457
+ let problem;
458
+ if (typeof adapterInstance !== "object" || adapterInstance === null) {
459
+ problem = `got ${adapterInstance === void 0 ? "undefined" : JSON.stringify(adapterInstance)}`;
460
+ } else {
461
+ const instance = adapterInstance;
462
+ const missing = REQUIRED_ADAPTER_METHODS.filter((method) => typeof instance[method] !== "function");
463
+ if (missing.length > 0) problem = `missing ${missing.map((m) => `${m}()`).join(", ")}`;
464
+ }
465
+ if (problem === void 0) return;
466
+ throw new FeatureFlagConfigurationError(
467
+ `FeatureFlagPlugin.init({ adapter: 'custom' }) requires an \`adapterInstance\` option: an object implementing FeatureFlagAdapter (${REQUIRED_ADAPTER_METHODS.map((m) => `${m}()`).join(", ")}); ${problem}.`
468
+ );
469
+ }
415
470
  var FeatureFlagPlugin = class extends DynamicPlugin {
416
471
  options;
417
472
  constructor(options) {
@@ -531,7 +586,7 @@ var FeatureFlagPlugin = class extends DynamicPlugin {
531
586
  enabled = defaultValue;
532
587
  }
533
588
  if (!enabled) {
534
- throw new Error(`${kind} "${metadata?.name}" is disabled by feature flag "${key}"`);
589
+ throw new FeatureFlagDisabledError(kind, metadata?.name, key);
535
590
  }
536
591
  }
537
592
  // ─────────────────────────────────────────────────────────────────────────
@@ -614,6 +669,11 @@ var FeatureFlagPlugin = class extends DynamicPlugin {
614
669
  */
615
670
  __publicField(FeatureFlagPlugin, "dynamicProviders", (options) => {
616
671
  const providers = [];
672
+ if (!SUPPORTED_ADAPTERS.includes(options?.adapter)) {
673
+ throw new FeatureFlagConfigurationError(
674
+ `FeatureFlagPlugin.init() requires an \`adapter\` option, got ${JSON.stringify(options?.adapter)}. Supported adapters: ${SUPPORTED_ADAPTERS.map((a) => `"${a}"`).join(", ")}.`
675
+ );
676
+ }
617
677
  switch (options.adapter) {
618
678
  case "static":
619
679
  providers.push({
@@ -662,6 +722,7 @@ __publicField(FeatureFlagPlugin, "dynamicProviders", (options) => {
662
722
  });
663
723
  break;
664
724
  case "custom":
725
+ assertCustomAdapter(options.adapterInstance);
665
726
  providers.push({
666
727
  name: "feature-flags:adapter:custom",
667
728
  provide: FeatureFlagAdapterToken,
@@ -747,6 +808,8 @@ export {
747
808
  FeatureFlagAccessorToken,
748
809
  FeatureFlagAdapterToken,
749
810
  FeatureFlagConfigToken,
811
+ FeatureFlagConfigurationError,
812
+ FeatureFlagDisabledError,
750
813
  FeatureFlagPlugin,
751
814
  LaunchDarklyFeatureFlagAdapter,
752
815
  SplitioFeatureFlagAdapter,
package/esm/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/plugin-feature-flags",
3
- "version": "1.8.6",
3
+ "version": "1.9.0",
4
4
  "description": "Feature flag plugin for FrontMCP - dynamically gate MCP capabilities behind feature flags",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "license": "Apache-2.0",
@@ -49,7 +49,7 @@
49
49
  }
50
50
  },
51
51
  "dependencies": {
52
- "@frontmcp/sdk": "1.8.6"
52
+ "@frontmcp/sdk": "1.9.0"
53
53
  },
54
54
  "peerDependencies": {
55
55
  "@launchdarkly/node-server-sdk": "^9.0.0 || ^10.0.0",
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Error classes for the feature-flag plugin.
3
+ *
4
+ * A refusal is an answer for the caller, so it is a public MCP error whose message reaches the
5
+ * client as written, in every environment, instead of being wrapped as an internal server error.
6
+ *
7
+ * @module @frontmcp/plugin-feature-flags
8
+ */
9
+ import { PublicMcpError } from '@frontmcp/sdk';
10
+ /**
11
+ * Raised when a tool, resource, prompt or agent is called while its feature flag is off.
12
+ */
13
+ export declare class FeatureFlagDisabledError extends PublicMcpError {
14
+ readonly kind: string;
15
+ readonly entryName: string | undefined;
16
+ readonly flagKey: string;
17
+ readonly name = "FeatureFlagDisabledError";
18
+ constructor(kind: string, entryName: string | undefined, flagKey: string);
19
+ }
20
+ /**
21
+ * Raised at startup when `FeatureFlagPlugin.init()` is given no usable adapter.
22
+ */
23
+ export declare class FeatureFlagConfigurationError extends Error {
24
+ constructor(message: string);
25
+ }
26
+ //# sourceMappingURL=feature-flag.errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-flag.errors.d.ts","sourceRoot":"","sources":["../src/feature-flag.errors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C;;GAEG;AACH,qBAAa,wBAAyB,SAAQ,cAAc;aAIxC,IAAI,EAAE,MAAM;aACZ,SAAS,EAAE,MAAM,GAAG,SAAS;aAC7B,OAAO,EAAE,MAAM;IALjC,SAAkB,IAAI,8BAA8B;gBAGlC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,OAAO,EAAE,MAAM;CAIlC;AAED;;GAEG;AACH,qBAAa,6BAA8B,SAAQ,KAAK;gBAC1C,OAAO,EAAE,MAAM;CAI5B"}
@@ -1 +1 @@
1
- {"version":3,"file":"feature-flag.plugin.d.ts","sourceRoot":"","sources":["../src/feature-flag.plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EAYb,KAAK,SAAS,EAGd,KAAK,YAAY,EAClB,MAAM,eAAe,CAAC;AAMvB,OAAO,KAAK,EAEV,wBAAwB,EACxB,6BAA6B,EAE9B,MAAM,sBAAsB,CAAC;AAa9B;;;;;;;;;;;;;;;;;;GAkBG;AAeH,MAAM,CAAC,OAAO,OAAO,iBAAkB,SAAQ,aAAa,CAAC,wBAAwB,EAAE,6BAA6B,CAAC;IACnH,OAAO,EAAE,wBAAwB,CAAC;gBAEtB,OAAO,EAAE,6BAA6B;IAKlD;;OAEG;IACH,OAAgB,gBAAgB,GAAI,SAAS,6BAA6B,KAAG,YAAY,EAAE,CA0FzF;IAMF;;;OAGG;IAEG,eAAe,CAAC,OAAO,EAAE,SAAS,CAAC,kBAAkB,CAAC;IAqB5D;;OAEG;IAEG,mBAAmB,CAAC,OAAO,EAAE,SAAS,CAAC,0BAA0B,CAAC;IAoBxE;;;;OAIG;IAEG,2BAA2B,CAAC,OAAO,EAAE,SAAS,CAAC,mCAAmC,CAAC;IAoBzF;;OAEG;IAEG,iBAAiB,CAAC,OAAO,EAAE,GAAG;IAsBpC;;;;;;;;OAQG;IAEG,YAAY,CAAC,OAAO,EAAE,SAAS,CAAC,eAAe,CAAC;IAsBtD;;;OAGG;IAEG,iBAAiB,CAAC,OAAO,EAAE,SAAS,CAAC,iBAAiB,CAAC;IAI7D;;;;;;OAMG;IAEG,gBAAgB,CAAC,OAAO,EAAE,GAAG;IAInC;;;;;OAKG;IAEG,aAAa,CAAC,OAAO,EAAE,GAAG;IAIhC;;;;;OAKG;IAEG,cAAc,CAAC,OAAO,EAAE,GAAG;IAMjC;;;;;OAKG;YACW,kBAAkB;IA+BhC;;;;;;;;OAQG;IACH,OAAO,CAAC,WAAW;IAYnB;;OAEG;IACH,OAAO,CAAC,eAAe;IAiBvB;;OAEG;YACW,iBAAiB;IAQ/B;;;;;;;OAOG;IACH,OAAO,CAAC,kBAAkB;IAW1B;;;;OAIG;IACH,OAAO,CAAC,YAAY;CAWrB"}
1
+ {"version":3,"file":"feature-flag.plugin.d.ts","sourceRoot":"","sources":["../src/feature-flag.plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EAYb,KAAK,SAAS,EAGd,KAAK,YAAY,EAClB,MAAM,eAAe,CAAC;AAOvB,OAAO,KAAK,EAEV,wBAAwB,EACxB,6BAA6B,EAE9B,MAAM,sBAAsB,CAAC;AAwC9B;;;;;;;;;;;;;;;;;;GAkBG;AAeH,MAAM,CAAC,OAAO,OAAO,iBAAkB,SAAQ,aAAa,CAAC,wBAAwB,EAAE,6BAA6B,CAAC;IACnH,OAAO,EAAE,wBAAwB,CAAC;gBAEtB,OAAO,EAAE,6BAA6B;IAKlD;;OAEG;IACH,OAAgB,gBAAgB,GAAI,SAAS,6BAA6B,KAAG,YAAY,EAAE,CAkGzF;IAMF;;;OAGG;IAEG,eAAe,CAAC,OAAO,EAAE,SAAS,CAAC,kBAAkB,CAAC;IAqB5D;;OAEG;IAEG,mBAAmB,CAAC,OAAO,EAAE,SAAS,CAAC,0BAA0B,CAAC;IAoBxE;;;;OAIG;IAEG,2BAA2B,CAAC,OAAO,EAAE,SAAS,CAAC,mCAAmC,CAAC;IAoBzF;;OAEG;IAEG,iBAAiB,CAAC,OAAO,EAAE,GAAG;IAsBpC;;;;;;;;OAQG;IAEG,YAAY,CAAC,OAAO,EAAE,SAAS,CAAC,eAAe,CAAC;IAsBtD;;;OAGG;IAEG,iBAAiB,CAAC,OAAO,EAAE,SAAS,CAAC,iBAAiB,CAAC;IAI7D;;;;;;OAMG;IAEG,gBAAgB,CAAC,OAAO,EAAE,GAAG;IAInC;;;;;OAKG;IAEG,aAAa,CAAC,OAAO,EAAE,GAAG;IAIhC;;;;;OAKG;IAEG,cAAc,CAAC,OAAO,EAAE,GAAG;IAMjC;;;;;OAKG;YACW,kBAAkB;IA+BhC;;;;;;;;OAQG;IACH,OAAO,CAAC,WAAW;IAYnB;;OAEG;IACH,OAAO,CAAC,eAAe;IAiBvB;;OAEG;YACW,iBAAiB;IAQ/B;;;;;;;OAOG;IACH,OAAO,CAAC,kBAAkB;IAW1B;;;;OAIG;IACH,OAAO,CAAC,YAAY;CAWrB"}
@@ -27,6 +27,11 @@ export type UserIdResolver = (ctx: FrontMcpContext) => string | undefined;
27
27
  export type AttributesResolver = (ctx: FrontMcpContext) => Record<string, unknown>;
28
28
  /** Shared base options for all adapter configurations. */
29
29
  interface FeatureFlagBaseOptions {
30
+ /**
31
+ * What `this.featureFlags.isEnabled()` answers when the adapter throws or has no answer for the
32
+ * flag and the call passes no `defaultValue` of its own. Gates use their ref's `defaultValue`.
33
+ * @default false
34
+ */
30
35
  defaultValue?: boolean;
31
36
  cacheStrategy?: FeatureFlagCacheStrategy;
32
37
  cacheTtlMs?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"feature-flag.types.d.ts","sourceRoot":"","sources":["../src/feature-flag.types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"feature-flag.types.d.ts","sourceRoot":"","sources":["../src/feature-flag.types.ts"],"names":[],"mappings":"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"}
package/index.d.ts CHANGED
@@ -22,6 +22,7 @@
22
22
  * @packageDocumentation
23
23
  */
24
24
  export { default, default as FeatureFlagPlugin } from './feature-flag.plugin';
25
+ export { FeatureFlagConfigurationError, FeatureFlagDisabledError } from './feature-flag.errors';
25
26
  export { FeatureFlagAdapterToken, FeatureFlagConfigToken, FeatureFlagAccessorToken } from './feature-flag.symbols';
26
27
  export type { FeatureFlagContext, FeatureFlagVariant, FeatureFlagRef, FeatureFlagCacheStrategy, UserIdResolver, AttributesResolver, FeatureFlagPluginOptions, FeatureFlagPluginOptionsInput, StaticFeatureFlagPluginOptions, SplitioFeatureFlagPluginOptions, LaunchDarklyFeatureFlagPluginOptions, UnleashFeatureFlagPluginOptions, CustomFeatureFlagPluginOptions, } from './feature-flag.types';
27
28
  export type { FeatureFlagAdapter } from './adapters';
package/index.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EAAE,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAG9E,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAGnH,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,wBAAwB,EACxB,cAAc,EACd,kBAAkB,EAClB,wBAAwB,EACxB,6BAA6B,EAC7B,8BAA8B,EAC9B,+BAA+B,EAC/B,oCAAoC,EACpC,+BAA+B,EAC/B,8BAA8B,GAC/B,MAAM,sBAAsB,CAAC;AAG9B,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,8BAA8B,EAAE,MAAM,iCAAiC,CAAC;AACjF,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAGvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC;AAKjF,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EAAE,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAG9E,OAAO,EAAE,6BAA6B,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAGhG,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAGnH,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,wBAAwB,EACxB,cAAc,EACd,kBAAkB,EAClB,wBAAwB,EACxB,6BAA6B,EAC7B,8BAA8B,EAC9B,+BAA+B,EAC/B,oCAAoC,EACpC,+BAA+B,EAC/B,8BAA8B,GAC/B,MAAM,sBAAsB,CAAC;AAG9B,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAE,8BAA8B,EAAE,MAAM,iCAAiC,CAAC;AACjF,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAGvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC;AAKjF,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC"}
package/index.js CHANGED
@@ -243,6 +243,8 @@ __export(index_exports, {
243
243
  FeatureFlagAccessorToken: () => FeatureFlagAccessorToken,
244
244
  FeatureFlagAdapterToken: () => FeatureFlagAdapterToken,
245
245
  FeatureFlagConfigToken: () => FeatureFlagConfigToken,
246
+ FeatureFlagConfigurationError: () => FeatureFlagConfigurationError,
247
+ FeatureFlagDisabledError: () => FeatureFlagDisabledError,
246
248
  FeatureFlagPlugin: () => FeatureFlagPlugin,
247
249
  LaunchDarklyFeatureFlagAdapter: () => LaunchDarklyFeatureFlagAdapter,
248
250
  SplitioFeatureFlagAdapter: () => SplitioFeatureFlagAdapter,
@@ -255,7 +257,7 @@ __export(index_exports, {
255
257
  module.exports = __toCommonJS(index_exports);
256
258
 
257
259
  // plugins/plugin-feature-flags/src/feature-flag.plugin.ts
258
- var import_sdk3 = require("@frontmcp/sdk");
260
+ var import_sdk4 = require("@frontmcp/sdk");
259
261
 
260
262
  // plugins/plugin-feature-flags/src/adapters/static.adapter.ts
261
263
  var StaticFeatureFlagAdapter = class {
@@ -318,6 +320,27 @@ function buildFeatureFlagContext(ctx, config) {
318
320
  };
319
321
  }
320
322
 
323
+ // plugins/plugin-feature-flags/src/feature-flag.errors.ts
324
+ var import_sdk2 = require("@frontmcp/sdk");
325
+ var FeatureFlagDisabledError = class extends import_sdk2.PublicMcpError {
326
+ constructor(kind, entryName, flagKey) {
327
+ super(`${kind} "${entryName}" is disabled by feature flag "${flagKey}"`, "FEATURE_FLAG_DISABLED", 403);
328
+ this.kind = kind;
329
+ this.entryName = entryName;
330
+ this.flagKey = flagKey;
331
+ }
332
+ kind;
333
+ entryName;
334
+ flagKey;
335
+ name = "FeatureFlagDisabledError";
336
+ };
337
+ var FeatureFlagConfigurationError = class extends Error {
338
+ constructor(message) {
339
+ super(message);
340
+ this.name = "FeatureFlagConfigurationError";
341
+ }
342
+ };
343
+
321
344
  // plugins/plugin-feature-flags/src/feature-flag.symbols.ts
322
345
  var FeatureFlagAdapterToken = /* @__PURE__ */ Symbol(
323
346
  "plugin:feature-flags:adapter"
@@ -330,11 +353,12 @@ var FeatureFlagAccessorToken = /* @__PURE__ */ Symbol(
330
353
  );
331
354
 
332
355
  // plugins/plugin-feature-flags/src/providers/feature-flag-accessor.provider.ts
333
- var import_sdk2 = require("@frontmcp/sdk");
356
+ var import_sdk3 = require("@frontmcp/sdk");
334
357
  var FeatureFlagAccessor = class {
335
358
  adapter;
336
359
  ctx;
337
360
  config;
361
+ /** The adapter's answers by flag key: `undefined` when it has none (an unknown flag). */
338
362
  cache = /* @__PURE__ */ new Map();
339
363
  constructor(adapter, ctx, config) {
340
364
  this.adapter = adapter;
@@ -343,27 +367,41 @@ var FeatureFlagAccessor = class {
343
367
  }
344
368
  /**
345
369
  * Check if a feature flag is enabled.
370
+ *
371
+ * `defaultValue` (else the plugin's `defaultValue`, else `false`) is the answer when the adapter
372
+ * throws or has no answer for the flag: a key the static adapter was not given, or one a custom
373
+ * adapter's `evaluateFlags()` omits. A flag the adapter answers keeps its answer, `false` included.
374
+ * It used to apply only when the adapter threw, so `isEnabled('unknown', true)` was `false` (#678).
346
375
  */
347
376
  async isEnabled(flagKey, defaultValue) {
377
+ const answer = await this.evaluate(flagKey);
378
+ return answer ?? defaultValue ?? this.config.defaultValue ?? false;
379
+ }
380
+ /**
381
+ * The adapter's answer for one flag, or `undefined` when it has none or throws. Asked through
382
+ * `evaluateFlags()`, as the execution gates ask, which is how an adapter says it does not know a
383
+ * flag; `isEnabled()` answers `false` for it. A successful answer is cached per `cacheStrategy`.
384
+ */
385
+ async evaluate(flagKey) {
348
386
  const cacheStrategy = this.config.cacheStrategy ?? "none";
349
387
  const cacheTtlMs = this.config.cacheTtlMs ?? 3e4;
350
388
  if (cacheStrategy !== "none") {
351
389
  const cached = this.cache.get(flagKey);
352
390
  if (cached && Date.now() < cached.expiresAt) {
353
- return cached.value;
391
+ return cached.answer;
354
392
  }
355
393
  }
356
- const context = this.buildContext();
357
- let result;
394
+ let answer;
358
395
  try {
359
- result = await this.adapter.isEnabled(flagKey, context);
396
+ const results = await this.adapter.evaluateFlags([flagKey], this.buildContext());
397
+ answer = results.has(flagKey) ? results.get(flagKey) === true : void 0;
360
398
  } catch {
361
- result = defaultValue ?? this.config.defaultValue ?? false;
399
+ return void 0;
362
400
  }
363
401
  if (cacheStrategy !== "none") {
364
- this.cache.set(flagKey, { value: result, expiresAt: Date.now() + cacheTtlMs });
402
+ this.cache.set(flagKey, { answer, expiresAt: Date.now() + cacheTtlMs });
365
403
  }
366
- return result;
404
+ return answer;
367
405
  }
368
406
  /**
369
407
  * Get a feature flag variant (for multi-variate flags).
@@ -396,10 +434,10 @@ var FeatureFlagAccessor = class {
396
434
  }
397
435
  };
398
436
  FeatureFlagAccessor = __decorateClass([
399
- (0, import_sdk2.Provider)({
437
+ (0, import_sdk3.Provider)({
400
438
  name: "provider:feature-flags:accessor",
401
439
  description: "Context-scoped accessor for feature flag evaluation",
402
- scope: import_sdk2.ProviderScope.CONTEXT
440
+ scope: import_sdk3.ProviderScope.CONTEXT
403
441
  })
404
442
  ], FeatureFlagAccessor);
405
443
  function createFeatureFlagAccessor(adapter, ctx, config) {
@@ -407,12 +445,28 @@ function createFeatureFlagAccessor(adapter, ctx, config) {
407
445
  }
408
446
 
409
447
  // plugins/plugin-feature-flags/src/feature-flag.plugin.ts
410
- var ListPromptsHook = (0, import_sdk3.FlowHooksOf)("prompts:list-prompts");
411
- var ReadResourceHook = (0, import_sdk3.FlowHooksOf)("resources:read-resource");
412
- var GetPromptHook = (0, import_sdk3.FlowHooksOf)("prompts:get-prompt");
413
- var CompleteHook = (0, import_sdk3.FlowHooksOf)("completion:complete");
414
- var FilterSkillsHook = (0, import_sdk3.FlowHooksOf)("skills:filter");
415
- var FeatureFlagPlugin = class extends import_sdk3.DynamicPlugin {
448
+ var ListPromptsHook = (0, import_sdk4.FlowHooksOf)("prompts:list-prompts");
449
+ var ReadResourceHook = (0, import_sdk4.FlowHooksOf)("resources:read-resource");
450
+ var GetPromptHook = (0, import_sdk4.FlowHooksOf)("prompts:get-prompt");
451
+ var CompleteHook = (0, import_sdk4.FlowHooksOf)("completion:complete");
452
+ var FilterSkillsHook = (0, import_sdk4.FlowHooksOf)("skills:filter");
453
+ var SUPPORTED_ADAPTERS = ["static", "splitio", "launchdarkly", "unleash", "custom"];
454
+ var REQUIRED_ADAPTER_METHODS = ["isEnabled", "getVariant", "evaluateFlags"];
455
+ function assertCustomAdapter(adapterInstance) {
456
+ let problem;
457
+ if (typeof adapterInstance !== "object" || adapterInstance === null) {
458
+ problem = `got ${adapterInstance === void 0 ? "undefined" : JSON.stringify(adapterInstance)}`;
459
+ } else {
460
+ const instance = adapterInstance;
461
+ const missing = REQUIRED_ADAPTER_METHODS.filter((method) => typeof instance[method] !== "function");
462
+ if (missing.length > 0) problem = `missing ${missing.map((m) => `${m}()`).join(", ")}`;
463
+ }
464
+ if (problem === void 0) return;
465
+ throw new FeatureFlagConfigurationError(
466
+ `FeatureFlagPlugin.init({ adapter: 'custom' }) requires an \`adapterInstance\` option: an object implementing FeatureFlagAdapter (${REQUIRED_ADAPTER_METHODS.map((m) => `${m}()`).join(", ")}); ${problem}.`
467
+ );
468
+ }
469
+ var FeatureFlagPlugin = class extends import_sdk4.DynamicPlugin {
416
470
  options;
417
471
  constructor(options) {
418
472
  super();
@@ -531,7 +585,7 @@ var FeatureFlagPlugin = class extends import_sdk3.DynamicPlugin {
531
585
  enabled = defaultValue;
532
586
  }
533
587
  if (!enabled) {
534
- throw new Error(`${kind} "${metadata?.name}" is disabled by feature flag "${key}"`);
588
+ throw new FeatureFlagDisabledError(kind, metadata?.name, key);
535
589
  }
536
590
  }
537
591
  // ─────────────────────────────────────────────────────────────────────────
@@ -549,13 +603,13 @@ var FeatureFlagPlugin = class extends import_sdk3.DynamicPlugin {
549
603
  judgedItems(items, entryOf) {
550
604
  let scope;
551
605
  try {
552
- scope = this.get(import_sdk3.ScopeEntry);
606
+ scope = this.get(import_sdk4.ScopeEntry);
553
607
  } catch {
554
608
  scope = void 0;
555
609
  }
556
610
  if (!scope?.hooks) return new Set(items);
557
611
  const judgingScope = scope;
558
- return new Set(items.filter((item) => (0, import_sdk3.isEntryGatedBy)(judgingScope, entryOf(item), this)));
612
+ return new Set(items.filter((item) => (0, import_sdk4.isEntryGatedBy)(judgingScope, entryOf(item), this)));
559
613
  }
560
614
  /**
561
615
  * Collect unique flag keys from items that have a featureFlag metadata.
@@ -590,7 +644,7 @@ var FeatureFlagPlugin = class extends import_sdk3.DynamicPlugin {
590
644
  */
591
645
  currentFlagContext() {
592
646
  try {
593
- const ctx = this.get(import_sdk3.FrontMcpContextStorage)?.getStore();
647
+ const ctx = this.get(import_sdk4.FrontMcpContextStorage)?.getStore();
594
648
  if (!ctx) return {};
595
649
  return buildFeatureFlagContext(ctx, this.options);
596
650
  } catch {
@@ -614,6 +668,11 @@ var FeatureFlagPlugin = class extends import_sdk3.DynamicPlugin {
614
668
  */
615
669
  __publicField(FeatureFlagPlugin, "dynamicProviders", (options) => {
616
670
  const providers = [];
671
+ if (!SUPPORTED_ADAPTERS.includes(options?.adapter)) {
672
+ throw new FeatureFlagConfigurationError(
673
+ `FeatureFlagPlugin.init() requires an \`adapter\` option, got ${JSON.stringify(options?.adapter)}. Supported adapters: ${SUPPORTED_ADAPTERS.map((a) => `"${a}"`).join(", ")}.`
674
+ );
675
+ }
617
676
  switch (options.adapter) {
618
677
  case "static":
619
678
  providers.push({
@@ -662,6 +721,7 @@ __publicField(FeatureFlagPlugin, "dynamicProviders", (options) => {
662
721
  });
663
722
  break;
664
723
  case "custom":
724
+ assertCustomAdapter(options.adapterInstance);
665
725
  providers.push({
666
726
  name: "feature-flags:adapter:custom",
667
727
  provide: FeatureFlagAdapterToken,
@@ -677,20 +737,20 @@ __publicField(FeatureFlagPlugin, "dynamicProviders", (options) => {
677
737
  providers.push({
678
738
  name: "feature-flags:accessor",
679
739
  provide: FeatureFlagAccessorToken,
680
- scope: import_sdk3.ProviderScope.CONTEXT,
681
- inject: () => [FeatureFlagAdapterToken, import_sdk3.FRONTMCP_CONTEXT, FeatureFlagConfigToken],
740
+ scope: import_sdk4.ProviderScope.CONTEXT,
741
+ inject: () => [FeatureFlagAdapterToken, import_sdk4.FRONTMCP_CONTEXT, FeatureFlagConfigToken],
682
742
  useFactory: (adapter, ctx, cfg) => createFeatureFlagAccessor(adapter, ctx, cfg)
683
743
  });
684
744
  return providers;
685
745
  });
686
746
  __decorateClass([
687
- import_sdk3.ListToolsHook.Did("findTools", { priority: 50 })
747
+ import_sdk4.ListToolsHook.Did("findTools", { priority: 50 })
688
748
  ], FeatureFlagPlugin.prototype, "filterListTools", 1);
689
749
  __decorateClass([
690
- import_sdk3.ListResourcesHook.Did("findResources", { priority: 50 })
750
+ import_sdk4.ListResourcesHook.Did("findResources", { priority: 50 })
691
751
  ], FeatureFlagPlugin.prototype, "filterListResources", 1);
692
752
  __decorateClass([
693
- import_sdk3.ListResourceTemplatesHook.Did("findTemplates", { priority: 50 })
753
+ import_sdk4.ListResourceTemplatesHook.Did("findTemplates", { priority: 50 })
694
754
  ], FeatureFlagPlugin.prototype, "filterListResourceTemplates", 1);
695
755
  __decorateClass([
696
756
  ListPromptsHook.Did("findPrompts", { priority: 50 })
@@ -699,7 +759,7 @@ __decorateClass([
699
759
  FilterSkillsHook.Did("filterSkills", { priority: 50, appliesTo: "uncovered-apps" })
700
760
  ], FeatureFlagPlugin.prototype, "filterSkills", 1);
701
761
  __decorateClass([
702
- import_sdk3.ToolHook.Will("execute", { priority: 50, appliesTo: "uncovered-apps" })
762
+ import_sdk4.ToolHook.Will("execute", { priority: 50, appliesTo: "uncovered-apps" })
703
763
  ], FeatureFlagPlugin.prototype, "gateToolExecution", 1);
704
764
  __decorateClass([
705
765
  ReadResourceHook.Will("execute", { priority: 50, appliesTo: "uncovered-apps" })
@@ -711,7 +771,7 @@ __decorateClass([
711
771
  CompleteHook.Will("complete", { priority: 50, appliesTo: "uncovered-apps" })
712
772
  ], FeatureFlagPlugin.prototype, "gateCompletion", 1);
713
773
  FeatureFlagPlugin = __decorateClass([
714
- (0, import_sdk3.Plugin)({
774
+ (0, import_sdk4.Plugin)({
715
775
  name: "feature-flags",
716
776
  description: "Feature flag-based capability filtering for MCP",
717
777
  // A server where an entry declares `featureFlag` and none of these gates reaches it refuses to start.
@@ -748,6 +808,8 @@ function tryGetFeatureFlags(ctx) {
748
808
  FeatureFlagAccessorToken,
749
809
  FeatureFlagAdapterToken,
750
810
  FeatureFlagConfigToken,
811
+ FeatureFlagConfigurationError,
812
+ FeatureFlagDisabledError,
751
813
  FeatureFlagPlugin,
752
814
  LaunchDarklyFeatureFlagAdapter,
753
815
  SplitioFeatureFlagAdapter,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/plugin-feature-flags",
3
- "version": "1.8.6",
3
+ "version": "1.9.0",
4
4
  "description": "Feature flag plugin for FrontMCP - dynamically gate MCP capabilities behind feature flags",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "license": "Apache-2.0",
@@ -49,7 +49,7 @@
49
49
  }
50
50
  },
51
51
  "dependencies": {
52
- "@frontmcp/sdk": "1.8.6"
52
+ "@frontmcp/sdk": "1.9.0"
53
53
  },
54
54
  "peerDependencies": {
55
55
  "@launchdarkly/node-server-sdk": "^9.0.0 || ^10.0.0",
@@ -9,12 +9,24 @@ export declare class FeatureFlagAccessor {
9
9
  private readonly adapter;
10
10
  private readonly ctx;
11
11
  private readonly config;
12
+ /** The adapter's answers by flag key: `undefined` when it has none (an unknown flag). */
12
13
  private readonly cache;
13
14
  constructor(adapter: FeatureFlagAdapter, ctx: FrontMcpContext, config: FeatureFlagPluginOptions);
14
15
  /**
15
16
  * Check if a feature flag is enabled.
17
+ *
18
+ * `defaultValue` (else the plugin's `defaultValue`, else `false`) is the answer when the adapter
19
+ * throws or has no answer for the flag: a key the static adapter was not given, or one a custom
20
+ * adapter's `evaluateFlags()` omits. A flag the adapter answers keeps its answer, `false` included.
21
+ * It used to apply only when the adapter threw, so `isEnabled('unknown', true)` was `false` (#678).
16
22
  */
17
23
  isEnabled(flagKey: string, defaultValue?: boolean): Promise<boolean>;
24
+ /**
25
+ * The adapter's answer for one flag, or `undefined` when it has none or throws. Asked through
26
+ * `evaluateFlags()`, as the execution gates ask, which is how an adapter says it does not know a
27
+ * flag; `isEnabled()` answers `false` for it. A successful answer is cached per `cacheStrategy`.
28
+ */
29
+ private evaluate;
18
30
  /**
19
31
  * Get a feature flag variant (for multi-variate flags).
20
32
  */
@@ -1 +1 @@
1
- {"version":3,"file":"feature-flag-accessor.provider.d.ts","sourceRoot":"","sources":["../../src/providers/feature-flag-accessor.provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAA2B,MAAM,eAAe,CAAC;AAEzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4CAA4C,CAAC;AAErF,OAAO,KAAK,EAEV,wBAAwB,EACxB,cAAc,EACd,kBAAkB,EACnB,MAAM,uBAAuB,CAAC;AAE/B;;;GAGG;AACH,qBAKa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkB;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA2B;IAClD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA4D;gBAEtE,OAAO,EAAE,kBAAkB,EAAE,GAAG,EAAE,eAAe,EAAE,MAAM,EAAE,wBAAwB;IAM/F;;OAEG;IACG,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IA4B1E;;OAEG;IACG,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAK9D;;OAEG;IACG,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAKtE;;OAEG;IACG,UAAU,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IAOvD;;OAEG;IACH,OAAO,CAAC,YAAY;CAGrB;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,kBAAkB,EAC3B,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE,wBAAwB,GAC/B,mBAAmB,CAErB"}
1
+ {"version":3,"file":"feature-flag-accessor.provider.d.ts","sourceRoot":"","sources":["../../src/providers/feature-flag-accessor.provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAA2B,MAAM,eAAe,CAAC;AAEzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4CAA4C,CAAC;AAErF,OAAO,KAAK,EAEV,wBAAwB,EACxB,cAAc,EACd,kBAAkB,EACnB,MAAM,uBAAuB,CAAC;AAE/B;;;GAGG;AACH,qBAKa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkB;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA2B;IAClD,yFAAyF;IACzF,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyE;gBAEnF,OAAO,EAAE,kBAAkB,EAAE,GAAG,EAAE,eAAe,EAAE,MAAM,EAAE,wBAAwB;IAM/F;;;;;;;OAOG;IACG,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAK1E;;;;OAIG;YACW,QAAQ;IA0BtB;;OAEG;IACG,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAK9D;;OAEG;IACG,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAKtE;;OAEG;IACG,UAAU,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IAOvD;;OAEG;IACH,OAAO,CAAC,YAAY;CAGrB;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,kBAAkB,EAC3B,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE,wBAAwB,GAC/B,mBAAmB,CAErB"}