@frontmcp/plugin-feature-flags 1.8.6 → 1.8.7
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 +30 -1
- package/esm/package.json +2 -2
- package/feature-flag.errors.d.ts +26 -0
- package/feature-flag.errors.d.ts.map +1 -0
- package/feature-flag.plugin.d.ts.map +1 -1
- package/index.d.ts +1 -0
- package/index.d.ts.map +1 -1
- package/index.js +52 -21
- package/package.json +2 -2
package/esm/index.mjs
CHANGED
|
@@ -318,6 +318,27 @@ function buildFeatureFlagContext(ctx, config) {
|
|
|
318
318
|
};
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
+
// plugins/plugin-feature-flags/src/feature-flag.errors.ts
|
|
322
|
+
import { PublicMcpError } from "@frontmcp/sdk";
|
|
323
|
+
var FeatureFlagDisabledError = class extends PublicMcpError {
|
|
324
|
+
constructor(kind, entryName, flagKey) {
|
|
325
|
+
super(`${kind} "${entryName}" is disabled by feature flag "${flagKey}"`, "FEATURE_FLAG_DISABLED", 403);
|
|
326
|
+
this.kind = kind;
|
|
327
|
+
this.entryName = entryName;
|
|
328
|
+
this.flagKey = flagKey;
|
|
329
|
+
}
|
|
330
|
+
kind;
|
|
331
|
+
entryName;
|
|
332
|
+
flagKey;
|
|
333
|
+
name = "FeatureFlagDisabledError";
|
|
334
|
+
};
|
|
335
|
+
var FeatureFlagConfigurationError = class extends Error {
|
|
336
|
+
constructor(message) {
|
|
337
|
+
super(message);
|
|
338
|
+
this.name = "FeatureFlagConfigurationError";
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
|
|
321
342
|
// plugins/plugin-feature-flags/src/feature-flag.symbols.ts
|
|
322
343
|
var FeatureFlagAdapterToken = /* @__PURE__ */ Symbol(
|
|
323
344
|
"plugin:feature-flags:adapter"
|
|
@@ -412,6 +433,7 @@ var ReadResourceHook = FlowHooksOf("resources:read-resource");
|
|
|
412
433
|
var GetPromptHook = FlowHooksOf("prompts:get-prompt");
|
|
413
434
|
var CompleteHook = FlowHooksOf("completion:complete");
|
|
414
435
|
var FilterSkillsHook = FlowHooksOf("skills:filter");
|
|
436
|
+
var SUPPORTED_ADAPTERS = ["static", "splitio", "launchdarkly", "unleash", "custom"];
|
|
415
437
|
var FeatureFlagPlugin = class extends DynamicPlugin {
|
|
416
438
|
options;
|
|
417
439
|
constructor(options) {
|
|
@@ -531,7 +553,7 @@ var FeatureFlagPlugin = class extends DynamicPlugin {
|
|
|
531
553
|
enabled = defaultValue;
|
|
532
554
|
}
|
|
533
555
|
if (!enabled) {
|
|
534
|
-
throw new
|
|
556
|
+
throw new FeatureFlagDisabledError(kind, metadata?.name, key);
|
|
535
557
|
}
|
|
536
558
|
}
|
|
537
559
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -614,6 +636,11 @@ var FeatureFlagPlugin = class extends DynamicPlugin {
|
|
|
614
636
|
*/
|
|
615
637
|
__publicField(FeatureFlagPlugin, "dynamicProviders", (options) => {
|
|
616
638
|
const providers = [];
|
|
639
|
+
if (!SUPPORTED_ADAPTERS.includes(options?.adapter)) {
|
|
640
|
+
throw new FeatureFlagConfigurationError(
|
|
641
|
+
`FeatureFlagPlugin.init() requires an \`adapter\` option, got ${JSON.stringify(options?.adapter)}. Supported adapters: ${SUPPORTED_ADAPTERS.map((a) => `"${a}"`).join(", ")}.`
|
|
642
|
+
);
|
|
643
|
+
}
|
|
617
644
|
switch (options.adapter) {
|
|
618
645
|
case "static":
|
|
619
646
|
providers.push({
|
|
@@ -747,6 +774,8 @@ export {
|
|
|
747
774
|
FeatureFlagAccessorToken,
|
|
748
775
|
FeatureFlagAdapterToken,
|
|
749
776
|
FeatureFlagConfigToken,
|
|
777
|
+
FeatureFlagConfigurationError,
|
|
778
|
+
FeatureFlagDisabledError,
|
|
750
779
|
FeatureFlagPlugin,
|
|
751
780
|
LaunchDarklyFeatureFlagAdapter,
|
|
752
781
|
SplitioFeatureFlagAdapter,
|
package/esm/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontmcp/plugin-feature-flags",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.7",
|
|
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.
|
|
52
|
+
"@frontmcp/sdk": "1.8.7"
|
|
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;
|
|
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;AAe9B;;;;;;;;;;;;;;;;;;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,CAiGzF;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"}
|
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
|
|
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,7 +353,7 @@ var FeatureFlagAccessorToken = /* @__PURE__ */ Symbol(
|
|
|
330
353
|
);
|
|
331
354
|
|
|
332
355
|
// plugins/plugin-feature-flags/src/providers/feature-flag-accessor.provider.ts
|
|
333
|
-
var
|
|
356
|
+
var import_sdk3 = require("@frontmcp/sdk");
|
|
334
357
|
var FeatureFlagAccessor = class {
|
|
335
358
|
adapter;
|
|
336
359
|
ctx;
|
|
@@ -396,10 +419,10 @@ var FeatureFlagAccessor = class {
|
|
|
396
419
|
}
|
|
397
420
|
};
|
|
398
421
|
FeatureFlagAccessor = __decorateClass([
|
|
399
|
-
(0,
|
|
422
|
+
(0, import_sdk3.Provider)({
|
|
400
423
|
name: "provider:feature-flags:accessor",
|
|
401
424
|
description: "Context-scoped accessor for feature flag evaluation",
|
|
402
|
-
scope:
|
|
425
|
+
scope: import_sdk3.ProviderScope.CONTEXT
|
|
403
426
|
})
|
|
404
427
|
], FeatureFlagAccessor);
|
|
405
428
|
function createFeatureFlagAccessor(adapter, ctx, config) {
|
|
@@ -407,12 +430,13 @@ function createFeatureFlagAccessor(adapter, ctx, config) {
|
|
|
407
430
|
}
|
|
408
431
|
|
|
409
432
|
// plugins/plugin-feature-flags/src/feature-flag.plugin.ts
|
|
410
|
-
var ListPromptsHook = (0,
|
|
411
|
-
var ReadResourceHook = (0,
|
|
412
|
-
var GetPromptHook = (0,
|
|
413
|
-
var CompleteHook = (0,
|
|
414
|
-
var FilterSkillsHook = (0,
|
|
415
|
-
var
|
|
433
|
+
var ListPromptsHook = (0, import_sdk4.FlowHooksOf)("prompts:list-prompts");
|
|
434
|
+
var ReadResourceHook = (0, import_sdk4.FlowHooksOf)("resources:read-resource");
|
|
435
|
+
var GetPromptHook = (0, import_sdk4.FlowHooksOf)("prompts:get-prompt");
|
|
436
|
+
var CompleteHook = (0, import_sdk4.FlowHooksOf)("completion:complete");
|
|
437
|
+
var FilterSkillsHook = (0, import_sdk4.FlowHooksOf)("skills:filter");
|
|
438
|
+
var SUPPORTED_ADAPTERS = ["static", "splitio", "launchdarkly", "unleash", "custom"];
|
|
439
|
+
var FeatureFlagPlugin = class extends import_sdk4.DynamicPlugin {
|
|
416
440
|
options;
|
|
417
441
|
constructor(options) {
|
|
418
442
|
super();
|
|
@@ -531,7 +555,7 @@ var FeatureFlagPlugin = class extends import_sdk3.DynamicPlugin {
|
|
|
531
555
|
enabled = defaultValue;
|
|
532
556
|
}
|
|
533
557
|
if (!enabled) {
|
|
534
|
-
throw new
|
|
558
|
+
throw new FeatureFlagDisabledError(kind, metadata?.name, key);
|
|
535
559
|
}
|
|
536
560
|
}
|
|
537
561
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -549,13 +573,13 @@ var FeatureFlagPlugin = class extends import_sdk3.DynamicPlugin {
|
|
|
549
573
|
judgedItems(items, entryOf) {
|
|
550
574
|
let scope;
|
|
551
575
|
try {
|
|
552
|
-
scope = this.get(
|
|
576
|
+
scope = this.get(import_sdk4.ScopeEntry);
|
|
553
577
|
} catch {
|
|
554
578
|
scope = void 0;
|
|
555
579
|
}
|
|
556
580
|
if (!scope?.hooks) return new Set(items);
|
|
557
581
|
const judgingScope = scope;
|
|
558
|
-
return new Set(items.filter((item) => (0,
|
|
582
|
+
return new Set(items.filter((item) => (0, import_sdk4.isEntryGatedBy)(judgingScope, entryOf(item), this)));
|
|
559
583
|
}
|
|
560
584
|
/**
|
|
561
585
|
* Collect unique flag keys from items that have a featureFlag metadata.
|
|
@@ -590,7 +614,7 @@ var FeatureFlagPlugin = class extends import_sdk3.DynamicPlugin {
|
|
|
590
614
|
*/
|
|
591
615
|
currentFlagContext() {
|
|
592
616
|
try {
|
|
593
|
-
const ctx = this.get(
|
|
617
|
+
const ctx = this.get(import_sdk4.FrontMcpContextStorage)?.getStore();
|
|
594
618
|
if (!ctx) return {};
|
|
595
619
|
return buildFeatureFlagContext(ctx, this.options);
|
|
596
620
|
} catch {
|
|
@@ -614,6 +638,11 @@ var FeatureFlagPlugin = class extends import_sdk3.DynamicPlugin {
|
|
|
614
638
|
*/
|
|
615
639
|
__publicField(FeatureFlagPlugin, "dynamicProviders", (options) => {
|
|
616
640
|
const providers = [];
|
|
641
|
+
if (!SUPPORTED_ADAPTERS.includes(options?.adapter)) {
|
|
642
|
+
throw new FeatureFlagConfigurationError(
|
|
643
|
+
`FeatureFlagPlugin.init() requires an \`adapter\` option, got ${JSON.stringify(options?.adapter)}. Supported adapters: ${SUPPORTED_ADAPTERS.map((a) => `"${a}"`).join(", ")}.`
|
|
644
|
+
);
|
|
645
|
+
}
|
|
617
646
|
switch (options.adapter) {
|
|
618
647
|
case "static":
|
|
619
648
|
providers.push({
|
|
@@ -677,20 +706,20 @@ __publicField(FeatureFlagPlugin, "dynamicProviders", (options) => {
|
|
|
677
706
|
providers.push({
|
|
678
707
|
name: "feature-flags:accessor",
|
|
679
708
|
provide: FeatureFlagAccessorToken,
|
|
680
|
-
scope:
|
|
681
|
-
inject: () => [FeatureFlagAdapterToken,
|
|
709
|
+
scope: import_sdk4.ProviderScope.CONTEXT,
|
|
710
|
+
inject: () => [FeatureFlagAdapterToken, import_sdk4.FRONTMCP_CONTEXT, FeatureFlagConfigToken],
|
|
682
711
|
useFactory: (adapter, ctx, cfg) => createFeatureFlagAccessor(adapter, ctx, cfg)
|
|
683
712
|
});
|
|
684
713
|
return providers;
|
|
685
714
|
});
|
|
686
715
|
__decorateClass([
|
|
687
|
-
|
|
716
|
+
import_sdk4.ListToolsHook.Did("findTools", { priority: 50 })
|
|
688
717
|
], FeatureFlagPlugin.prototype, "filterListTools", 1);
|
|
689
718
|
__decorateClass([
|
|
690
|
-
|
|
719
|
+
import_sdk4.ListResourcesHook.Did("findResources", { priority: 50 })
|
|
691
720
|
], FeatureFlagPlugin.prototype, "filterListResources", 1);
|
|
692
721
|
__decorateClass([
|
|
693
|
-
|
|
722
|
+
import_sdk4.ListResourceTemplatesHook.Did("findTemplates", { priority: 50 })
|
|
694
723
|
], FeatureFlagPlugin.prototype, "filterListResourceTemplates", 1);
|
|
695
724
|
__decorateClass([
|
|
696
725
|
ListPromptsHook.Did("findPrompts", { priority: 50 })
|
|
@@ -699,7 +728,7 @@ __decorateClass([
|
|
|
699
728
|
FilterSkillsHook.Did("filterSkills", { priority: 50, appliesTo: "uncovered-apps" })
|
|
700
729
|
], FeatureFlagPlugin.prototype, "filterSkills", 1);
|
|
701
730
|
__decorateClass([
|
|
702
|
-
|
|
731
|
+
import_sdk4.ToolHook.Will("execute", { priority: 50, appliesTo: "uncovered-apps" })
|
|
703
732
|
], FeatureFlagPlugin.prototype, "gateToolExecution", 1);
|
|
704
733
|
__decorateClass([
|
|
705
734
|
ReadResourceHook.Will("execute", { priority: 50, appliesTo: "uncovered-apps" })
|
|
@@ -711,7 +740,7 @@ __decorateClass([
|
|
|
711
740
|
CompleteHook.Will("complete", { priority: 50, appliesTo: "uncovered-apps" })
|
|
712
741
|
], FeatureFlagPlugin.prototype, "gateCompletion", 1);
|
|
713
742
|
FeatureFlagPlugin = __decorateClass([
|
|
714
|
-
(0,
|
|
743
|
+
(0, import_sdk4.Plugin)({
|
|
715
744
|
name: "feature-flags",
|
|
716
745
|
description: "Feature flag-based capability filtering for MCP",
|
|
717
746
|
// A server where an entry declares `featureFlag` and none of these gates reaches it refuses to start.
|
|
@@ -748,6 +777,8 @@ function tryGetFeatureFlags(ctx) {
|
|
|
748
777
|
FeatureFlagAccessorToken,
|
|
749
778
|
FeatureFlagAdapterToken,
|
|
750
779
|
FeatureFlagConfigToken,
|
|
780
|
+
FeatureFlagConfigurationError,
|
|
781
|
+
FeatureFlagDisabledError,
|
|
751
782
|
FeatureFlagPlugin,
|
|
752
783
|
LaunchDarklyFeatureFlagAdapter,
|
|
753
784
|
SplitioFeatureFlagAdapter,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontmcp/plugin-feature-flags",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.7",
|
|
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.
|
|
52
|
+
"@frontmcp/sdk": "1.8.7"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
55
|
"@launchdarkly/node-server-sdk": "^9.0.0 || ^10.0.0",
|