@featureflip/mcp 0.1.10 → 0.1.11
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/README.md +3 -3
- package/dist/cli.mjs +1 -1
- package/dist/generated/api-types.d.ts +198 -2
- package/dist/generated/api-types.d.ts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{org-C5dAa5In.js → org-c4iWMSzU.js} +31 -10
- package/dist/tools/flags.d.ts.map +1 -1
- package/dist/tools/workflows/stale-flags.d.ts.map +1 -1
- package/package.json +7 -7
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as e, c as t, o as n, r, s as i, t as a } from "./org-
|
|
1
|
+
import { a as e, c as t, o as n, r, s as i, t as a } from "./org-c4iWMSzU.js";
|
|
2
2
|
export { n as ApiError, i as FeatureflipApi, t as PKG_VERSION, e as createServer, r as loadConfig, a as resolveOrg };
|
|
@@ -4,7 +4,7 @@ import * as t from "zod/v4";
|
|
|
4
4
|
//#region src/version.ts
|
|
5
5
|
var n = {
|
|
6
6
|
name: "@featureflip/mcp",
|
|
7
|
-
version: "0.1.
|
|
7
|
+
version: "0.1.11",
|
|
8
8
|
mcpName: "io.github.canopy-labs/featureflip",
|
|
9
9
|
description: "MCP server for managing Featureflip feature flags from AI agents and editors",
|
|
10
10
|
keywords: [
|
|
@@ -43,17 +43,17 @@ var n = {
|
|
|
43
43
|
"smoke:docker": "node scripts/docker-smoke.mjs"
|
|
44
44
|
},
|
|
45
45
|
dependencies: {
|
|
46
|
-
"@modelcontextprotocol/server": "2.1
|
|
46
|
+
"@modelcontextprotocol/server": "2.3.1",
|
|
47
47
|
zod: "^4.6.5"
|
|
48
48
|
},
|
|
49
49
|
devDependencies: {
|
|
50
|
-
"@modelcontextprotocol/client": "2.1
|
|
50
|
+
"@modelcontextprotocol/client": "2.3.1",
|
|
51
51
|
"@typescript/typescript6": "^6.0.2",
|
|
52
|
-
"@types/node": "^26.6.
|
|
52
|
+
"@types/node": "^26.6.4",
|
|
53
53
|
typescript: "^7.0.2",
|
|
54
|
-
vite: "^8.3.
|
|
55
|
-
"vite-plugin-dts": "^5.1.
|
|
56
|
-
vitest: "^5.0.
|
|
54
|
+
vite: "^8.3.3",
|
|
55
|
+
"vite-plugin-dts": "^5.1.2",
|
|
56
|
+
vitest: "^5.0.3"
|
|
57
57
|
},
|
|
58
58
|
engines: { node: ">=20.19.0" }
|
|
59
59
|
}.version, r = class extends Error {
|
|
@@ -282,6 +282,7 @@ function y(e, n) {
|
|
|
282
282
|
})).optional(),
|
|
283
283
|
expiresAtUtc: t.string().optional().describe("Optional date (YYYY-MM-DD, end of that UTC day) or ISO-8601 timestamp the flag is expected to be removed by (see set_flag_expiry)"),
|
|
284
284
|
ownerEmail: t.string().optional().describe("Optional owner: the email of an active member (Pro plan and above, see set_flag_owner). Omitted, a personal access token makes its own user the owner and a service token leaves the flag unowned"),
|
|
285
|
+
isPermanent: t.boolean().optional().describe("Mark the flag permanent: a kill switch, ops toggle or entitlement gate that is meant to stay. Permanent flags are never listed as removal candidates or by find_stale_flags. Cannot be combined with expiresAtUtc"),
|
|
285
286
|
idempotency_key: t.string().optional().describe("Idempotency-Key header for safe retries")
|
|
286
287
|
}),
|
|
287
288
|
annotations: { destructiveHint: !1 }
|
|
@@ -314,7 +315,7 @@ function y(e, n) {
|
|
|
314
315
|
});
|
|
315
316
|
})), e.registerTool("set_flag_expiry", {
|
|
316
317
|
title: "Set or clear flag expiry",
|
|
317
|
-
description: "Set the date a flag is expected to be removed by, or pass expiresAtUtc: null to clear it. Expiry is advisory: evaluation never changes. Once the date passes, find_stale_flags reports the flag as expired. expiresAtUtc takes a date (2026-12-31), which means the end of that day in UTC, the same as picking it in the dashboard, or a full ISO-8601 timestamp, which is stored exactly. Refused with EXPIRY_IN_PAST for a time that has already passed (today's date is allowed),
|
|
318
|
+
description: "Set the date a flag is expected to be removed by, or pass expiresAtUtc: null to clear it. Expiry is advisory: evaluation never changes. Once the date passes, find_stale_flags reports the flag as expired. expiresAtUtc takes a date (2026-12-31), which means the end of that day in UTC, the same as picking it in the dashboard, or a full ISO-8601 timestamp, which is stored exactly. Refused with EXPIRY_IN_PAST for a time that has already passed (today's date is allowed), with FEATURE_NOT_ENABLED while flag expiration is not enabled for the organization, and with PERMANENT_FLAG_HAS_EXPIRY on a permanent flag (make it temporary first with set_flag_permanent). Clearing is always allowed.",
|
|
318
319
|
inputSchema: t.object({
|
|
319
320
|
project: t.string(),
|
|
320
321
|
flag: t.string().describe("Flag key or id"),
|
|
@@ -331,6 +332,25 @@ function y(e, n) {
|
|
|
331
332
|
flag: t,
|
|
332
333
|
expiresAtUtc: a
|
|
333
334
|
});
|
|
335
|
+
})), e.registerTool("set_flag_permanent", {
|
|
336
|
+
title: "Mark a flag permanent or temporary",
|
|
337
|
+
description: "Mark a flag permanent (a kill switch, ops toggle or entitlement gate that is meant to stay), or pass permanent: false to make it temporary again. Permanent flags are never listed as removal candidates or by find_stale_flags; evaluation never changes. Marking is refused with PERMANENT_FLAG_HAS_EXPIRY while the flag has an expiry date (clear it first with set_flag_expiry and expiresAtUtc: null), with FEATURE_NOT_ENABLED while permanent flags are not enabled for the organization, and on an archived flag. Making a flag temporary is always allowed.",
|
|
338
|
+
inputSchema: t.object({
|
|
339
|
+
project: t.string(),
|
|
340
|
+
flag: t.string().describe("Flag key or id"),
|
|
341
|
+
permanent: t.boolean().describe("true to mark the flag permanent, false to make it temporary")
|
|
342
|
+
}),
|
|
343
|
+
annotations: {
|
|
344
|
+
destructiveHint: !1,
|
|
345
|
+
idempotentHint: !0
|
|
346
|
+
}
|
|
347
|
+
}, async ({ project: e, flag: t, permanent: a }) => d(async () => {
|
|
348
|
+
let o = `${r(e)}/${i(t)}/permanent`;
|
|
349
|
+
return await n.api.request(a ? "PUT" : "DELETE", o), l({
|
|
350
|
+
project: e,
|
|
351
|
+
flag: t,
|
|
352
|
+
permanent: a
|
|
353
|
+
});
|
|
334
354
|
})), e.registerTool("set_flag_owner", {
|
|
335
355
|
title: "Set or clear flag owner",
|
|
336
356
|
description: "Name the person responsible for a flag, or pass owner: null to leave it unowned. owner is the email of an active member of the organization, or \"me\" for the token's own user (needs a personal access token). The owner gets the flag's cleanup notices; evaluation never changes. Refused with OWNER_NOT_MEMBER for an email that is not an active member, and with PLAN_FEATURE_UNAVAILABLE below the Pro plan. Clearing is always allowed.",
|
|
@@ -681,7 +701,7 @@ var A = t.enum(["dead", "stale"]), j = 50;
|
|
|
681
701
|
function M(e, n) {
|
|
682
702
|
e.registerTool("find_stale_flags", {
|
|
683
703
|
title: "Find stale flags",
|
|
684
|
-
description: `Find flags that look ready for code cleanup: not updated in N days AND either enabled in every environment (verify rollout is complete before removing — per-rule percentage ramps are not inspected) or disabled in every environment (dead — remove flag and code path). A flag whose expiry date (set_flag_expiry) has passed is always a candidate, however recently it was edited; if it is on in some environments and off in others its reason is past-expiry, meaning the owner has to decide which way to fold it. Every result carries expiresAtUtc, expired and owner (null when unowned). Pass owner to see one person's stale flags ("me" for your own) or "none" for the unowned ones. Every result also carries blockedBy: the live flags that still list it as a prerequisite, which have to be removed and archived before it ([] when none). blockedBy is null when the server did not classify the flag as a removal candidate, which means unknown, not unblocked. A flag that other live flags list as a prerequisite has to be removed LAST, dependents first: archiving it while a dependent still names it makes that dependent fail its prerequisite check and serve its off variation, so archive_flag refuses it with FLAG_HAS_DEPENDENTS. Checks at most ${j} candidates per call, expired flags first.`,
|
|
704
|
+
description: `Find flags that look ready for code cleanup: not updated in N days AND either enabled in every environment (verify rollout is complete before removing — per-rule percentage ramps are not inspected) or disabled in every environment (dead — remove flag and code path). A flag whose expiry date (set_flag_expiry) has passed is always a candidate, however recently it was edited; if it is on in some environments and off in others its reason is past-expiry, meaning the owner has to decide which way to fold it. Every result carries expiresAtUtc, expired and owner (null when unowned). Flags marked permanent (kill switches, ops toggles) are never listed. Pass owner to see one person's stale flags ("me" for your own) or "none" for the unowned ones. Every result also carries blockedBy: the live flags that still list it as a prerequisite, which have to be removed and archived before it ([] when none). blockedBy is null when the server did not classify the flag as a removal candidate, which means unknown, not unblocked. A flag that other live flags list as a prerequisite has to be removed LAST, dependents first: archiving it while a dependent still names it makes that dependent fail its prerequisite check and serve its off variation, so archive_flag refuses it with FLAG_HAS_DEPENDENTS. Checks at most ${j} candidates per call, expired flags first.`,
|
|
685
705
|
inputSchema: t.object({
|
|
686
706
|
project: t.string(),
|
|
687
707
|
days: t.number().int().min(1).optional().default(30).describe("Minimum age in days since last update"),
|
|
@@ -695,7 +715,8 @@ function M(e, n) {
|
|
|
695
715
|
archived: !1,
|
|
696
716
|
limit: 100,
|
|
697
717
|
cursor: p,
|
|
698
|
-
owner: o
|
|
718
|
+
owner: o,
|
|
719
|
+
permanent: !1
|
|
699
720
|
} });
|
|
700
721
|
f.push(...(e.items ?? []).filter((e) => !!e.key && !!e.name && !!e.updatedAt && (new Date(e.updatedAt).getTime() < u || d(e)))), p = e.next_cursor ?? void 0;
|
|
701
722
|
} while (p);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flags.d.ts","sourceRoot":"","sources":["../../src/tools/flags.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAI9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAKhD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"flags.d.ts","sourceRoot":"","sources":["../../src/tools/flags.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAI9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAKhD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CAqS3E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stale-flags.d.ts","sourceRoot":"","sources":["../../../src/tools/workflows/stale-flags.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAI9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAsBjD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"stale-flags.d.ts","sourceRoot":"","sources":["../../../src/tools/workflows/stale-flags.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAI9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAsBjD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CAsKhF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@featureflip/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"mcpName": "io.github.canopy-labs/featureflip",
|
|
5
5
|
"description": "MCP server for managing Featureflip feature flags from AI agents and editors",
|
|
6
6
|
"keywords": [
|
|
@@ -48,17 +48,17 @@
|
|
|
48
48
|
"smoke:docker": "node scripts/docker-smoke.mjs"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@modelcontextprotocol/server": "2.1
|
|
51
|
+
"@modelcontextprotocol/server": "2.3.1",
|
|
52
52
|
"zod": "^4.6.5"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@modelcontextprotocol/client": "2.1
|
|
55
|
+
"@modelcontextprotocol/client": "2.3.1",
|
|
56
56
|
"@typescript/typescript6": "^6.0.2",
|
|
57
|
-
"@types/node": "^26.6.
|
|
57
|
+
"@types/node": "^26.6.4",
|
|
58
58
|
"typescript": "^7.0.2",
|
|
59
|
-
"vite": "^8.3.
|
|
60
|
-
"vite-plugin-dts": "^5.1.
|
|
61
|
-
"vitest": "^5.0.
|
|
59
|
+
"vite": "^8.3.3",
|
|
60
|
+
"vite-plugin-dts": "^5.1.2",
|
|
61
|
+
"vitest": "^5.0.3"
|
|
62
62
|
},
|
|
63
63
|
"engines": {
|
|
64
64
|
"node": ">=20.19.0"
|