@featureflip/mcp 0.1.9 → 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/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-8tn1Su68.js";
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.9",
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.0",
46
+ "@modelcontextprotocol/server": "2.3.1",
47
47
  zod: "^4.6.5"
48
48
  },
49
49
  devDependencies: {
50
- "@modelcontextprotocol/client": "2.1.0",
50
+ "@modelcontextprotocol/client": "2.3.1",
51
51
  "@typescript/typescript6": "^6.0.2",
52
- "@types/node": "^26.6.2",
52
+ "@types/node": "^26.6.4",
53
53
  typescript: "^7.0.2",
54
- vite: "^8.3.1",
55
- "vite-plugin-dts": "^5.1.1",
56
- vitest: "^5.0.1"
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), and with FEATURE_NOT_ENABLED while flag expiration is not enabled for the organization. Clearing is always 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.",
@@ -360,7 +380,7 @@ function y(e, n) {
360
380
  annotations: { destructiveHint: !0 }
361
381
  }, async ({ project: e, flag: t }) => d(async () => (await n.api.request("DELETE", `${r(e)}/${i(t)}`), l({ deleted: t })))), e.registerTool("archive_flag", {
362
382
  title: "Archive feature flag",
363
- description: "Archive a flag (soft-hide, evaluation stops serving it). Reversible with restore_flag. Refused with FLAG_RECENTLY_EVALUATED while live traffic is still evaluating the flag, because archiving makes every caller fall back to its own hardcoded default — normally that means the code removal has merged but not deployed yet, and the refusal clears itself once it has.",
383
+ description: "Archive a flag (soft-hide, evaluation stops serving it). Reversible with restore_flag. Archive dependents first: refused with FLAG_HAS_DEPENDENTS while another live flag lists this one as a prerequisite, so archive those flags (blockedBy in find_stale_flags / list_removal_candidates) first. Refused with FLAG_RECENTLY_EVALUATED while live traffic is still evaluating the flag, because archiving makes every caller fall back to its own hardcoded default — normally that means the code removal has merged but not deployed yet, and the refusal clears itself once it has.",
364
384
  inputSchema: t.object({
365
385
  project: t.string(),
366
386
  flag: t.string(),
@@ -677,59 +697,89 @@ function k(e, n) {
677
697
  }
678
698
  //#endregion
679
699
  //#region src/tools/workflows/stale-flags.ts
680
- var A = 50;
681
- function j(e, n) {
700
+ var A = t.enum(["dead", "stale"]), j = 50;
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. Checks at most ${A} 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"),
688
708
  owner: g
689
709
  }),
690
710
  annotations: { readOnlyHint: !0 }
691
- }, async ({ project: e, days: t, owner: r }) => d(async () => {
692
- let a = await _(n, r), o = `/api/v1/orgs/${i(n.org)}/projects/${i(e)}/flags`, s = Date.now(), c = s - t * 24 * 60 * 60 * 1e3, u = (e) => !!e.expiresAtUtc && new Date(e.expiresAtUtc).getTime() <= s, d = [], f;
711
+ }, async ({ project: e, days: t, owner: a }) => d(async () => {
712
+ let o = await _(n, a), s = `/api/v1/orgs/${i(n.org)}/projects/${i(e)}/flags`, c = Date.now(), u = c - t * 24 * 60 * 60 * 1e3, d = (e) => !!e.expiresAtUtc && new Date(e.expiresAtUtc).getTime() <= c, f = [], p;
693
713
  do {
694
- let e = await n.api.request("GET", o, { query: {
714
+ let e = await n.api.request("GET", s, { query: {
695
715
  archived: !1,
696
716
  limit: 100,
697
- cursor: f,
698
- owner: a
717
+ cursor: p,
718
+ owner: o,
719
+ permanent: !1
699
720
  } });
700
- d.push(...(e.items ?? []).filter((e) => !!e.key && !!e.name && !!e.updatedAt && (new Date(e.updatedAt).getTime() < c || u(e)))), f = e.next_cursor ?? void 0;
701
- } while (f);
702
- d.sort((e, t) => Number(u(t)) - Number(u(e)));
703
- let p = d.length > A, m = d.slice(0, A), h = [];
704
- for (let e of m) {
705
- let t = await n.api.request("GET", `${o}/${i(e.key)}/environments`);
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;
722
+ } while (p);
723
+ f.sort((e, t) => Number(d(t)) - Number(d(e)));
724
+ let m = f.length > j, h = f.slice(0, j), g = [];
725
+ for (let e of h) {
726
+ let t = await n.api.request("GET", `${s}/${i(e.key)}/environments`);
706
727
  if (t.length === 0) continue;
707
- let r = u(e), a = t.every((e) => e.isEnabled), s = t.every((e) => !e.isEnabled);
708
- (a || s || r) && h.push({
728
+ let r = d(e), a = t.every((e) => e.isEnabled), o = t.every((e) => !e.isEnabled);
729
+ (a || o || r) && g.push({
709
730
  key: e.key,
710
731
  name: e.name,
711
732
  updatedAt: e.updatedAt,
712
733
  expiresAtUtc: e.expiresAtUtc ?? null,
713
734
  expired: r,
714
735
  owner: e.owner ?? null,
715
- reason: a ? "enabled-everywhere" : s ? "disabled-everywhere" : "past-expiry",
716
- environments: t.length
736
+ reason: a ? "enabled-everywhere" : o ? "disabled-everywhere" : "past-expiry",
737
+ environments: t.length,
738
+ blockedBy: null
717
739
  });
718
740
  }
741
+ let v;
742
+ if (g.length > 0) try {
743
+ let e = /* @__PURE__ */ new Map(), t;
744
+ do {
745
+ let r = await n.api.request("GET", `${s}/removal-candidates`, { query: {
746
+ staleness: "stale",
747
+ limit: 100,
748
+ cursor: t
749
+ } });
750
+ for (let t of r.items ?? []) t.key && t.blockedBy && e.set(t.key, t.blockedBy);
751
+ t = r.next_cursor ?? void 0;
752
+ } while (t);
753
+ for (let t of g) t.blockedBy = e.get(t.key) ?? null;
754
+ } catch (e) {
755
+ if (!(e instanceof r)) throw e;
756
+ v = `Could not read removal-candidates (${e.status} ${e.envelope.error}), so blockedBy is unknown for every flag. archive_flag still refuses any flag that a live flag depends on (FLAG_HAS_DEPENDENTS).`;
757
+ }
719
758
  return l({
720
759
  project: e,
721
760
  olderThanDays: t,
722
- ...a ? { owner: a } : {},
723
- checked: m.length,
724
- truncated: p,
725
- ...p ? { note: `Only the first ${A} of ${d.length} candidates were checked.` } : {},
726
- stale: h
761
+ ...o ? { owner: o } : {},
762
+ checked: h.length,
763
+ truncated: m,
764
+ ...m ? { note: `Only the first ${j} of ${f.length} candidates were checked.` } : {},
765
+ ...v ? { blockedByNote: v } : {},
766
+ stale: g
727
767
  });
728
- }));
768
+ })), e.registerTool("list_removal_candidates", {
769
+ title: "List removal candidates",
770
+ description: "List the flags the server is confident can be removed from code, the same list the Featureflip flag cleanup GitHub Action works from. Each item has key, reason, status (Dead or Stale), treatment (true = keep the on-branch, false = keep the off-branch) and blockedBy: the live flags that still list it as a prerequisite. Only remove a flag whose blockedBy is empty. 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. staleness \"dead\" (the default) returns only dead flags, \"stale\" adds stale ones. Paginated via cursor.",
771
+ inputSchema: t.object({
772
+ project: t.string().describe("Project key"),
773
+ staleness: A.optional().describe("Minimum staleness tier: dead (default) or stale"),
774
+ limit: t.number().int().min(1).max(100).optional(),
775
+ cursor: t.string().optional()
776
+ }),
777
+ annotations: { readOnlyHint: !0 }
778
+ }, async ({ project: e, ...t }) => d(async () => l(await n.api.request("GET", `/api/v1/orgs/${i(n.org)}/projects/${i(e)}/flags/removal-candidates`, { query: t }))));
729
779
  }
730
780
  //#endregion
731
781
  //#region src/tools/workflows/snippets.ts
732
- var M = [
782
+ var N = [
733
783
  "js",
734
784
  "node",
735
785
  "browser",
@@ -741,16 +791,16 @@ var M = [
741
791
  "php",
742
792
  "ruby",
743
793
  "swift"
744
- ], N = [
794
+ ], P = [
745
795
  "browser",
746
796
  "react",
747
797
  "swift"
748
798
  ];
749
- function P(e) {
750
- return N.includes(e);
799
+ function F(e) {
800
+ return P.includes(e);
751
801
  }
752
- var F = "Uses a SERVER-side SDK key (Featureflip dashboard → environment → SDK keys).", I = "Uses a CLIENT-side SDK key (safe to ship to browsers/apps).";
753
- function L(e, t) {
802
+ var I = "Uses a SERVER-side SDK key (Featureflip dashboard → environment → SDK keys).", L = "Uses a CLIENT-side SDK key (safe to ship to browsers/apps).";
803
+ function R(e, t) {
754
804
  switch (e) {
755
805
  case "js": return {
756
806
  install: "npm install @featureflip/js",
@@ -765,7 +815,7 @@ const enabled = client.boolVariation('${t}', { user_id: userId }, false);
765
815
  if (enabled) {
766
816
  // new code path
767
817
  }`,
768
- notes: F
818
+ notes: I
769
819
  };
770
820
  case "node": return {
771
821
  install: "npm install @featureflip/node",
@@ -776,7 +826,7 @@ const enabled = client.boolVariation('${t}', { user_id: userId }, false);
776
826
  if (enabled) {
777
827
  // new code path
778
828
  }`,
779
- notes: F
829
+ notes: I
780
830
  };
781
831
  case "browser": return {
782
832
  install: "npm install @featureflip/browser",
@@ -788,7 +838,7 @@ const enabled = client.boolVariation('${t}', false);
788
838
  if (enabled) {
789
839
  // new code path
790
840
  }`,
791
- notes: I
841
+ notes: L
792
842
  };
793
843
  case "react": return {
794
844
  install: "npm install @featureflip/react @featureflip/browser",
@@ -801,7 +851,7 @@ function MyComponent() {
801
851
  const enabled = useFeatureFlag('${t}', false);
802
852
  return enabled ? <NewExperience /> : <CurrentExperience />;
803
853
  }`,
804
- notes: I
854
+ notes: L
805
855
  };
806
856
  case "python": return {
807
857
  install: "pip install featureflip",
@@ -812,7 +862,7 @@ enabled = client.variation("${t}", {"user_id": user_id}, default=False)
812
862
  if enabled:
813
863
  # new code path
814
864
  ...`,
815
- notes: F
865
+ notes: I
816
866
  };
817
867
  case "csharp": return {
818
868
  install: "dotnet add package Featureflip.Client",
@@ -825,7 +875,7 @@ if (enabled)
825
875
  {
826
876
  // new code path
827
877
  }`,
828
- notes: F
878
+ notes: I
829
879
  };
830
880
  case "java": return {
831
881
  install: "implementation 'io.featureflip:featureflip-java:2.0.0'",
@@ -839,7 +889,7 @@ boolean enabled = client.boolVariation("${t}",
839
889
  if (enabled) {
840
890
  // new code path
841
891
  }`,
842
- notes: F
892
+ notes: I
843
893
  };
844
894
  case "go": return {
845
895
  install: "go get github.com/canopy-labs/featureflip-go/v2",
@@ -855,7 +905,7 @@ ctx := featureflip.EvaluationContext{UserID: userID}
855
905
  if client.BoolVariation("${t}", ctx, false) {
856
906
  // new code path
857
907
  }`,
858
- notes: F
908
+ notes: I
859
909
  };
860
910
  case "php": return {
861
911
  install: "composer require featureflip/featureflip-php",
@@ -870,7 +920,7 @@ $enabled = $client->boolVariation('${t}', ['user_id' => $userId], false);
870
920
  if ($enabled) {
871
921
  // new code path
872
922
  }`,
873
- notes: F + " Requires PSR-16 cache and PSR-18 HTTP implementations."
923
+ notes: I + " Requires PSR-16 cache and PSR-18 HTTP implementations."
874
924
  };
875
925
  case "ruby": return {
876
926
  install: "gem \"featureflip\"",
@@ -881,7 +931,7 @@ enabled = client.bool_variation("${t}", { "user_id" => user_id }, false)
881
931
  if enabled
882
932
  # new code path
883
933
  end`,
884
- notes: F
934
+ notes: I
885
935
  };
886
936
  case "swift": return {
887
937
  install: ".package(url: \"https://github.com/canopy-labs/featureflip-swift\", from: \"2.0.0\")",
@@ -894,13 +944,13 @@ let enabled = client.boolVariation("${t}", default: false)
894
944
  if enabled {
895
945
  // new code path
896
946
  }`,
897
- notes: I
947
+ notes: L
898
948
  };
899
949
  }
900
950
  }
901
951
  //#endregion
902
952
  //#region src/tools/workflows/wrap-feature.ts
903
- function R(e, n) {
953
+ function z(e, n) {
904
954
  e.registerTool("wrap_feature", {
905
955
  title: "Wrap a feature in a new flag",
906
956
  description: "Create a Boolean feature flag and get back the SDK code snippet to guard the new code path with it. Returns the snippet only — apply the edit yourself. The flag starts DISABLED in every environment; enable it with toggle_flag when ready.",
@@ -910,7 +960,7 @@ function R(e, n) {
910
960
  name: t.string().optional().describe("Display name; defaults to the key"),
911
961
  description: t.string().optional().describe("What this flag guards"),
912
962
  tags: t.array(t.string()).optional(),
913
- language: t.enum(M).describe("SDK language of the codebase being edited"),
963
+ language: t.enum(N).describe("SDK language of the codebase being edited"),
914
964
  idempotency_key: t.string().optional()
915
965
  }),
916
966
  annotations: { destructiveHint: !1 }
@@ -922,10 +972,10 @@ function R(e, n) {
922
972
  type: "Boolean",
923
973
  description: a,
924
974
  tags: o,
925
- ...P(s) ? { clientSideVisible: !0 } : {}
975
+ ...F(s) ? { clientSideVisible: !0 } : {}
926
976
  },
927
977
  idempotencyKey: c
928
- }), d = L(s, t);
978
+ }), d = R(s, t);
929
979
  return l({
930
980
  flag: u,
931
981
  integration: {
@@ -944,17 +994,17 @@ function R(e, n) {
944
994
  }
945
995
  //#endregion
946
996
  //#region src/server.ts
947
- function z(t) {
997
+ function B(t) {
948
998
  let r = new e({
949
999
  name: "featureflip",
950
1000
  version: n
951
1001
  });
952
- return p(r, t), m(r, t), h(r, t), y(r, t), x(r, t), w(r, t), T(r, t), k(r, t), j(r, t), R(r, t), r;
1002
+ return p(r, t), m(r, t), h(r, t), y(r, t), x(r, t), w(r, t), T(r, t), k(r, t), M(r, t), z(r, t), r;
953
1003
  }
954
1004
  //#endregion
955
1005
  //#region src/config.ts
956
- var B = "FEATUREFLIP_TOKEN is required. Create one in the Featureflip dashboard — Settings → API Tokens (personal token, ffp_...) or Organization Settings → Service Tokens (ffs_...) — and set it in your MCP client config.";
957
- function V(e = process.env) {
1006
+ var V = "FEATUREFLIP_TOKEN is required. Create one in the Featureflip dashboard — Settings → API Tokens (personal token, ffp_...) or Organization Settings → Service Tokens (ffs_...) — and set it in your MCP client config.";
1007
+ function H(e = process.env) {
958
1008
  let t = e.FEATUREFLIP_TOKEN?.trim();
959
1009
  return t ? {
960
1010
  token: t,
@@ -962,14 +1012,14 @@ function V(e = process.env) {
962
1012
  org: e.FEATUREFLIP_ORG?.trim() || void 0
963
1013
  } : null;
964
1014
  }
965
- function H(e = process.env) {
966
- let t = V(e);
967
- if (!t) throw Error(B);
1015
+ function U(e = process.env) {
1016
+ let t = H(e);
1017
+ if (!t) throw Error(V);
968
1018
  return t;
969
1019
  }
970
1020
  //#endregion
971
1021
  //#region src/org.ts
972
- async function U(e, t) {
1022
+ async function W(e, t) {
973
1023
  if (t) return t;
974
1024
  let n = (await e.request("GET", "/api/v1/orgs", { query: { limit: 100 } })).items ?? [];
975
1025
  if (n.length === 1) {
@@ -980,4 +1030,4 @@ async function U(e, t) {
980
1030
  throw n.length === 0 ? Error("The token can see no organizations. Check the token in the Featureflip dashboard.") : Error(`The token can see ${n.length} organizations — set FEATUREFLIP_ORG to one of: ` + n.map((e) => e.slug ?? "?").join(", "));
981
1031
  }
982
1032
  //#endregion
983
- export { z as a, n as c, V as i, B as n, r as o, H as r, c as s, U as t };
1033
+ export { B as a, n as c, H as i, V as n, r as o, U as r, c as s, W as t };
@@ -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,CAgQ3E"}
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;AAajD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CAoGhF"}
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.9",
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.0",
51
+ "@modelcontextprotocol/server": "2.3.1",
52
52
  "zod": "^4.6.5"
53
53
  },
54
54
  "devDependencies": {
55
- "@modelcontextprotocol/client": "2.1.0",
55
+ "@modelcontextprotocol/client": "2.3.1",
56
56
  "@typescript/typescript6": "^6.0.2",
57
- "@types/node": "^26.6.2",
57
+ "@types/node": "^26.6.4",
58
58
  "typescript": "^7.0.2",
59
- "vite": "^8.3.1",
60
- "vite-plugin-dts": "^5.1.1",
61
- "vitest": "^5.0.1"
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"