@featureflip/mcp 0.1.7 → 0.1.8

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.
@@ -4,9 +4,21 @@ 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",
7
+ version: "0.1.8",
8
8
  mcpName: "io.github.canopy-labs/featureflip",
9
9
  description: "MCP server for managing Featureflip feature flags from AI agents and editors",
10
+ keywords: [
11
+ "feature-flags",
12
+ "feature-toggles",
13
+ "feature-management",
14
+ "featureflip",
15
+ "mcp",
16
+ "mcp-server",
17
+ "model-context-protocol",
18
+ "ai-agents",
19
+ "claude",
20
+ "cursor"
21
+ ],
10
22
  license: "Apache-2.0",
11
23
  repository: {
12
24
  type: "git",
@@ -37,11 +49,11 @@ var n = {
37
49
  devDependencies: {
38
50
  "@modelcontextprotocol/client": "2.0.0",
39
51
  "@typescript/typescript6": "^6.0.2",
40
- "@types/node": "^26.5.1",
52
+ "@types/node": "^26.6.2",
41
53
  typescript: "^7.0.2",
42
54
  vite: "^8.3.0",
43
- "vite-plugin-dts": "^5.1.0",
44
- vitest: "^5.0.0"
55
+ "vite-plugin-dts": "^5.1.1",
56
+ vitest: "^5.0.1"
45
57
  },
46
58
  engines: { node: ">=20.19.0" }
47
59
  }.version, r = class extends Error {
@@ -54,51 +66,73 @@ var n = {
54
66
  function i(e) {
55
67
  return encodeURIComponent(e);
56
68
  }
57
- var a = class {
69
+ var a = /* @__PURE__ */ new Set([
70
+ 502,
71
+ 503,
72
+ 504
73
+ ]), o = /* @__PURE__ */ new Set([
74
+ "GET",
75
+ "HEAD",
76
+ "PUT",
77
+ "DELETE"
78
+ ]), s = [250, 1e3], c = class {
58
79
  opts;
59
80
  fetchImpl;
81
+ sleep;
60
82
  constructor(e) {
61
- this.opts = e, this.fetchImpl = e.fetchImpl ?? fetch;
83
+ this.opts = e, this.fetchImpl = e.fetchImpl ?? fetch, this.sleep = e.sleep ?? ((e) => new Promise((t) => setTimeout(t, e)));
62
84
  }
63
85
  async request(e, t, n = {}) {
64
86
  let i = new URL(this.opts.baseUrl + t);
65
87
  for (let [e, t] of Object.entries(n.query ?? {})) t !== void 0 && i.searchParams.set(e, String(t));
66
- let a = {
88
+ let c = {
67
89
  Authorization: `Bearer ${this.opts.token}`,
68
90
  Accept: "application/json"
69
- }, o;
70
- n.body !== void 0 && (a["Content-Type"] = "application/json", o = JSON.stringify(n.body)), n.idempotencyKey && (a["Idempotency-Key"] = n.idempotencyKey);
71
- let s = await this.fetchImpl(i.toString(), {
72
- method: e,
73
- headers: a,
74
- body: o
75
- });
76
- if (s.status !== 204) {
77
- if (!s.ok) {
91
+ }, l;
92
+ n.body !== void 0 && (c["Content-Type"] = "application/json", l = JSON.stringify(n.body)), n.idempotencyKey && (c["Idempotency-Key"] = n.idempotencyKey);
93
+ let u = o.has(e) || n.idempotencyKey !== void 0, d;
94
+ for (let t = 0;; t++) {
95
+ let n = t < s.length;
96
+ try {
97
+ d = await this.fetchImpl(i.toString(), {
98
+ method: e,
99
+ headers: c,
100
+ body: l
101
+ });
102
+ } catch (e) {
103
+ if (!u || !n) throw e;
104
+ await this.sleep(s[t]);
105
+ continue;
106
+ }
107
+ if (!(u ? a.has(d.status) : d.status === 503) || !n) break;
108
+ await d.body?.cancel(), await this.sleep(s[t]);
109
+ }
110
+ if (d.status !== 204) {
111
+ if (!d.ok) {
78
112
  let e;
79
113
  try {
80
- if (e = await s.json(), typeof e?.error != "string") throw Error("not an envelope");
114
+ if (e = await d.json(), typeof e?.error != "string") throw Error("not an envelope");
81
115
  } catch {
82
116
  e = {
83
- error: `http_${s.status}`,
84
- message: `HTTP ${s.status} from ${i.pathname}`
117
+ error: `http_${d.status}`,
118
+ message: `HTTP ${d.status} from ${i.pathname}`
85
119
  };
86
120
  }
87
- throw new r(s.status, e);
121
+ throw new r(d.status, e);
88
122
  }
89
- return await s.json();
123
+ return await d.json();
90
124
  }
91
125
  }
92
126
  };
93
127
  //#endregion
94
128
  //#region src/errors.ts
95
- function o(e) {
129
+ function l(e) {
96
130
  return { content: [{
97
131
  type: "text",
98
132
  text: JSON.stringify(e, null, 2)
99
133
  }] };
100
134
  }
101
- function s(e) {
135
+ function u(e) {
102
136
  if (e instanceof r) {
103
137
  let t = e.envelope, n = [`Featureflip API error ${e.status} (${t.error}): ${t.message}`];
104
138
  return t.fields && n.push(`Field errors: ${JSON.stringify(t.fields)}`), t.did_you_mean?.length && n.push(`Did you mean: ${t.did_you_mean.join(", ")}`), t.next_actions?.length && n.push(`Next actions: ${t.next_actions.map((e) => `${e.method} ${e.path}`).join("; ")}`), t.retry_after != null && n.push(`Rate limited — retry after ${t.retry_after}s.`), e.status === 404 && n.push("Note: 404 can also mean the public Management API is not enabled for this organization yet, or a service token does not include this project."), t.docs_url && n.push(`Docs: ${t.docs_url}`), {
@@ -117,33 +151,33 @@ function s(e) {
117
151
  isError: !0
118
152
  };
119
153
  }
120
- async function c(e) {
154
+ async function d(e) {
121
155
  try {
122
156
  return await e();
123
157
  } catch (e) {
124
- return s(e);
158
+ return u(e);
125
159
  }
126
160
  }
127
161
  //#endregion
128
162
  //#region src/tools/projects.ts
129
- var l = {
163
+ var f = {
130
164
  limit: t.number().int().min(1).max(100).optional().describe("Page size (default 20)"),
131
165
  cursor: t.string().optional().describe("next_cursor from a previous response")
132
166
  };
133
- function u(e, n) {
167
+ function p(e, n) {
134
168
  e.registerTool("list_projects", {
135
169
  title: "List projects",
136
170
  description: "List projects in the Featureflip organization. Paginated: pass cursor from next_cursor to continue.",
137
- inputSchema: t.object({ ...l }),
171
+ inputSchema: t.object({ ...f }),
138
172
  annotations: { readOnlyHint: !0 }
139
- }, async ({ limit: e, cursor: t }) => c(async () => o(await n.api.request("GET", `/api/v1/orgs/${i(n.org)}/projects`, { query: {
173
+ }, async ({ limit: e, cursor: t }) => d(async () => l(await n.api.request("GET", `/api/v1/orgs/${i(n.org)}/projects`, { query: {
140
174
  limit: e,
141
175
  cursor: t
142
176
  } }))));
143
177
  }
144
178
  //#endregion
145
179
  //#region src/tools/environments.ts
146
- function d(e, n) {
180
+ function m(e, n) {
147
181
  e.registerTool("list_environments", {
148
182
  title: "List environments",
149
183
  description: "List the environments of a project (e.g. development, staging, production).",
@@ -153,14 +187,14 @@ function d(e, n) {
153
187
  cursor: t.string().optional()
154
188
  }),
155
189
  annotations: { readOnlyHint: !0 }
156
- }, async ({ project: e, limit: t, cursor: r }) => c(async () => o(await n.api.request("GET", `/api/v1/orgs/${i(n.org)}/projects/${i(e)}/environments`, { query: {
190
+ }, async ({ project: e, limit: t, cursor: r }) => d(async () => l(await n.api.request("GET", `/api/v1/orgs/${i(n.org)}/projects/${i(e)}/environments`, { query: {
157
191
  limit: t,
158
192
  cursor: r
159
193
  } }))));
160
194
  }
161
195
  //#endregion
162
196
  //#region src/tools/segments.ts
163
- function f(e, n) {
197
+ function h(e, n) {
164
198
  e.registerTool("list_segments", {
165
199
  title: "List user segments",
166
200
  description: "List reusable user segments in a project. Segments are referenced from targeting rules via userSegmentId.",
@@ -170,7 +204,7 @@ function f(e, n) {
170
204
  cursor: t.string().optional()
171
205
  }),
172
206
  annotations: { readOnlyHint: !0 }
173
- }, async ({ project: e, limit: t, cursor: r }) => c(async () => o(await n.api.request("GET", `/api/v1/orgs/${i(n.org)}/projects/${i(e)}/segments`, { query: {
207
+ }, async ({ project: e, limit: t, cursor: r }) => d(async () => l(await n.api.request("GET", `/api/v1/orgs/${i(n.org)}/projects/${i(e)}/segments`, { query: {
174
208
  limit: t,
175
209
  cursor: r
176
210
  } })))), e.registerTool("get_segment", {
@@ -181,17 +215,17 @@ function f(e, n) {
181
215
  segment: t.string().describe("Segment key or id")
182
216
  }),
183
217
  annotations: { readOnlyHint: !0 }
184
- }, async ({ project: e, segment: t }) => c(async () => o(await n.api.request("GET", `/api/v1/orgs/${i(n.org)}/projects/${i(e)}/segments/${i(t)}`))));
218
+ }, async ({ project: e, segment: t }) => d(async () => l(await n.api.request("GET", `/api/v1/orgs/${i(n.org)}/projects/${i(e)}/segments/${i(t)}`))));
185
219
  }
186
220
  //#endregion
187
221
  //#region src/tools/flags.ts
188
- var p = t.enum([
222
+ var g = t.enum([
189
223
  "Boolean",
190
224
  "String",
191
225
  "Number",
192
226
  "Json"
193
227
  ]);
194
- function m(e, n) {
228
+ function _(e, n) {
195
229
  let r = (e) => `/api/v1/orgs/${i(n.org)}/projects/${i(e)}/flags`;
196
230
  e.registerTool("list_flags", {
197
231
  title: "List feature flags",
@@ -200,13 +234,13 @@ function m(e, n) {
200
234
  project: t.string().describe("Project key"),
201
235
  search: t.string().optional(),
202
236
  tag: t.string().optional(),
203
- type: p.optional(),
237
+ type: g.optional(),
204
238
  archived: t.boolean().optional().describe("true = only archived, false = only active"),
205
239
  limit: t.number().int().min(1).max(100).optional(),
206
240
  cursor: t.string().optional()
207
241
  }),
208
242
  annotations: { readOnlyHint: !0 }
209
- }, async ({ project: e, ...t }) => c(async () => o(await n.api.request("GET", r(e), { query: t })))), e.registerTool("get_flag", {
243
+ }, async ({ project: e, ...t }) => d(async () => l(await n.api.request("GET", r(e), { query: t })))), e.registerTool("get_flag", {
210
244
  title: "Get feature flag",
211
245
  description: "Get one feature flag with its variations and metadata. Address by flag key or id.",
212
246
  inputSchema: t.object({
@@ -214,14 +248,14 @@ function m(e, n) {
214
248
  flag: t.string().describe("Flag key or id")
215
249
  }),
216
250
  annotations: { readOnlyHint: !0 }
217
- }, async ({ project: e, flag: t }) => c(async () => o(await n.api.request("GET", `${r(e)}/${i(t)}`)))), e.registerTool("create_flag", {
251
+ }, async ({ project: e, flag: t }) => d(async () => l(await n.api.request("GET", `${r(e)}/${i(t)}`)))), e.registerTool("create_flag", {
218
252
  title: "Create feature flag",
219
253
  description: "Create a feature flag. type is one of Boolean|String|Number|Json. Boolean flags get true/false variations automatically; for other types pass initialVariations. The flag is created in every environment of the project (disabled).",
220
254
  inputSchema: t.object({
221
255
  project: t.string().describe("Project key"),
222
256
  key: t.string().describe("Unique flag key, e.g. checkout-v2"),
223
257
  name: t.string(),
224
- type: p,
258
+ type: g,
225
259
  description: t.string().optional(),
226
260
  tags: t.array(t.string()).optional(),
227
261
  clientSideVisible: t.boolean().optional().describe("Expose to client-side SDKs (browser/mobile)"),
@@ -231,10 +265,11 @@ function m(e, n) {
231
265
  value: t.string().describe("Variation value serialized as a string"),
232
266
  description: t.string().optional()
233
267
  })).optional(),
268
+ expiresAtUtc: t.string().optional().describe("Optional ISO-8601 UTC instant the flag is expected to be removed by (see set_flag_expiry)"),
234
269
  idempotency_key: t.string().optional().describe("Idempotency-Key header for safe retries")
235
270
  }),
236
271
  annotations: { destructiveHint: !1 }
237
- }, async ({ project: e, idempotency_key: t, ...i }) => c(async () => o(await n.api.request("POST", r(e), {
272
+ }, async ({ project: e, idempotency_key: t, ...i }) => d(async () => l(await n.api.request("POST", r(e), {
238
273
  body: i,
239
274
  idempotencyKey: t
240
275
  })))), e.registerTool("update_flag", {
@@ -249,11 +284,30 @@ function m(e, n) {
249
284
  clientSideVisible: t.boolean().optional()
250
285
  }),
251
286
  annotations: { destructiveHint: !0 }
252
- }, async ({ project: e, flag: t, ...a }) => c(async () => (await n.api.request("PUT", `${r(e)}/${i(t)}`, { body: a }), o({
287
+ }, async ({ project: e, flag: t, ...a }) => d(async () => (await n.api.request("PUT", `${r(e)}/${i(t)}`, { body: a }), l({
253
288
  project: e,
254
289
  flag: t,
255
290
  updated: !0
256
- })))), e.registerTool("delete_flag", {
291
+ })))), e.registerTool("set_flag_expiry", {
292
+ title: "Set or clear flag expiry",
293
+ 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 is an exact ISO-8601 UTC instant; a bare date means 00:00 UTC that day, so pass e.g. 2026-12-31T23:59:59Z for the end of a day. Refused with EXPIRY_IN_PAST for a time that has already passed, and with FEATURE_NOT_ENABLED while flag expiration is not enabled for the organization. Clearing is always allowed.",
294
+ inputSchema: t.object({
295
+ project: t.string(),
296
+ flag: t.string().describe("Flag key or id"),
297
+ expiresAtUtc: t.string().nullable().describe("ISO-8601 UTC instant, or null to clear the expiry")
298
+ }),
299
+ annotations: {
300
+ destructiveHint: !1,
301
+ idempotentHint: !0
302
+ }
303
+ }, async ({ project: e, flag: t, expiresAtUtc: a }) => d(async () => {
304
+ let o = `${r(e)}/${i(t)}/expiry`;
305
+ return a === null ? await n.api.request("DELETE", o) : await n.api.request("PUT", o, { body: { expiresAtUtc: a } }), l({
306
+ project: e,
307
+ flag: t,
308
+ expiresAtUtc: a
309
+ });
310
+ })), e.registerTool("delete_flag", {
257
311
  title: "Delete feature flag",
258
312
  description: "PERMANENTLY delete a flag across all environments. Fails with FLAG_HAS_DEPENDENTS if other flags use it as a prerequisite. Prefer archive_flag unless the flag must be fully removed.",
259
313
  inputSchema: t.object({
@@ -261,7 +315,7 @@ function m(e, n) {
261
315
  flag: t.string().describe("Flag key or id")
262
316
  }),
263
317
  annotations: { destructiveHint: !0 }
264
- }, async ({ project: e, flag: t }) => c(async () => (await n.api.request("DELETE", `${r(e)}/${i(t)}`), o({ deleted: t })))), e.registerTool("archive_flag", {
318
+ }, async ({ project: e, flag: t }) => d(async () => (await n.api.request("DELETE", `${r(e)}/${i(t)}`), l({ deleted: t })))), e.registerTool("archive_flag", {
265
319
  title: "Archive feature flag",
266
320
  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.",
267
321
  inputSchema: t.object({
@@ -270,7 +324,7 @@ function m(e, n) {
270
324
  force: t.boolean().optional().describe("Archive even though traffic is still evaluating the flag. Only for clients that can never be updated (old mobile app versions), where traffic will never drain on its own.")
271
325
  }),
272
326
  annotations: { destructiveHint: !0 }
273
- }, async ({ project: e, flag: t, force: a }) => c(async () => (await n.api.request("POST", `${r(e)}/${i(t)}/archive`, { query: a ? { force: !0 } : {} }), o({
327
+ }, async ({ project: e, flag: t, force: a }) => d(async () => (await n.api.request("POST", `${r(e)}/${i(t)}/archive`, { query: a ? { force: !0 } : {} }), l({
274
328
  flag: t,
275
329
  archived: !0
276
330
  })))), e.registerTool("restore_flag", {
@@ -281,19 +335,19 @@ function m(e, n) {
281
335
  flag: t.string()
282
336
  }),
283
337
  annotations: { destructiveHint: !1 }
284
- }, async ({ project: e, flag: t }) => c(async () => (await n.api.request("POST", `${r(e)}/${i(t)}/restore`), o({
338
+ }, async ({ project: e, flag: t }) => d(async () => (await n.api.request("POST", `${r(e)}/${i(t)}/restore`), l({
285
339
  flag: t,
286
340
  archived: !1
287
341
  }))));
288
342
  }
289
343
  //#endregion
290
344
  //#region src/tools/flag-environments.ts
291
- var h = t.enum([
345
+ var v = t.enum([
292
346
  "SingleVariation",
293
347
  "PercentageRollout",
294
348
  "TargetedRollout"
295
349
  ]);
296
- function g(e, n) {
350
+ function y(e, n) {
297
351
  let r = (e, t) => `/api/v1/orgs/${i(n.org)}/projects/${i(e)}/flags/${i(t)}`;
298
352
  e.registerTool("toggle_flag", {
299
353
  title: "Toggle flag in environment",
@@ -305,11 +359,11 @@ function g(e, n) {
305
359
  enabled: t.boolean()
306
360
  }),
307
361
  annotations: { destructiveHint: !0 }
308
- }, async ({ project: e, flag: t, environment: a, enabled: s }) => c(async () => (await n.api.request("POST", `${r(e, t)}/environments/${i(a)}/toggle`, { body: { enabled: s } }), o({
362
+ }, async ({ project: e, flag: t, environment: a, enabled: o }) => d(async () => (await n.api.request("POST", `${r(e, t)}/environments/${i(a)}/toggle`, { body: { enabled: o } }), l({
309
363
  project: e,
310
364
  flag: t,
311
365
  environment: a,
312
- enabled: s
366
+ enabled: o
313
367
  })))), e.registerTool("update_flag_environment_config", {
314
368
  title: "Update flag environment config",
315
369
  description: "Update a flag's per-environment serving config: defaultVariationId (served on fallthrough, required), strategy (SingleVariation | PercentageRollout | TargetedRollout — only SingleVariation is currently supported at the environment level), and prerequisites. Percentage rollouts are configured on targeting rules via update_targeting.",
@@ -318,18 +372,18 @@ function g(e, n) {
318
372
  flag: t.string(),
319
373
  environment: t.string(),
320
374
  defaultVariationId: t.string().describe("Variation id to serve on fallthrough — required by the API"),
321
- strategy: h.optional(),
375
+ strategy: v.optional(),
322
376
  prerequisites: t.array(t.object({
323
377
  prerequisiteFlagKey: t.string(),
324
378
  expectedVariationKey: t.string()
325
379
  })).optional().describe("Flags that must evaluate to the expected variation before this flag serves. Omit to leave existing prerequisites untouched; pass a list (possibly empty) to replace them wholesale.")
326
380
  }),
327
381
  annotations: { destructiveHint: !0 }
328
- }, async ({ project: e, flag: t, environment: a, ...s }) => c(async () => (await n.api.request("PUT", `${r(e, t)}/environments/${i(a)}`, { body: s }), o({
382
+ }, async ({ project: e, flag: t, environment: a, ...o }) => d(async () => (await n.api.request("PUT", `${r(e, t)}/environments/${i(a)}`, { body: o }), l({
329
383
  project: e,
330
384
  flag: t,
331
385
  environment: a,
332
- ...s
386
+ ...o
333
387
  })))), e.registerTool("flag_status", {
334
388
  title: "Flag status across environments",
335
389
  description: "Compact cross-environment view of one flag: enabled state, strategy, default variation, and prerequisites per environment.",
@@ -338,9 +392,9 @@ function g(e, n) {
338
392
  flag: t.string()
339
393
  }),
340
394
  annotations: { readOnlyHint: !0 }
341
- }, async ({ project: e, flag: t }) => c(async () => {
342
- let [i, a] = await Promise.all([n.api.request("GET", r(e, t)), n.api.request("GET", `${r(e, t)}/environments`)]), s = (e) => (i.variations ?? []).find((t) => t.id === e)?.key ?? e ?? null;
343
- return o({
395
+ }, async ({ project: e, flag: t }) => d(async () => {
396
+ let [i, a] = await Promise.all([n.api.request("GET", r(e, t)), n.api.request("GET", `${r(e, t)}/environments`)]), o = (e) => (i.variations ?? []).find((t) => t.id === e)?.key ?? e ?? null;
397
+ return l({
344
398
  key: i.key ?? null,
345
399
  name: i.name ?? null,
346
400
  type: i.type ?? null,
@@ -350,7 +404,7 @@ function g(e, n) {
350
404
  environment: e.environmentKey ?? null,
351
405
  enabled: e.isEnabled ?? !1,
352
406
  strategy: e.strategy ?? null,
353
- defaultVariation: s(e.defaultVariationId),
407
+ defaultVariation: o(e.defaultVariationId),
354
408
  prerequisites: e.prerequisites ?? []
355
409
  }))
356
410
  });
@@ -358,22 +412,22 @@ function g(e, n) {
358
412
  }
359
413
  //#endregion
360
414
  //#region src/tools/targeting.ts
361
- var _ = t.object({
415
+ var b = t.object({
362
416
  attribute: t.string().describe("Context attribute name, e.g. plan, country, user_id"),
363
417
  operator: t.string().describe("OperatorType member name, case-sensitive — e.g. Equals, NotEquals, Contains, In, GreaterThan"),
364
418
  values: t.array(t.string()),
365
419
  negate: t.boolean().optional().default(!1)
366
- }), v = t.object({
420
+ }), x = t.object({
367
421
  description: t.string().optional(),
368
422
  variationId: t.string().optional().describe("Variation served when the rule matches"),
369
423
  rolloutPercentage: t.number().int().min(0).max(100).optional().describe("Percentage rollout for this rule"),
370
424
  userSegmentId: t.string().optional().describe("Match users in this segment (see list_segments)"),
371
425
  conditionGroups: t.array(t.object({
372
426
  operator: t.enum(["And", "Or"]).describe("How conditions inside this group combine"),
373
- conditions: t.array(_)
427
+ conditions: t.array(b)
374
428
  })).optional().describe("Groups are ANDed together; conditions inside a group use the group operator")
375
429
  });
376
- function y(e, n) {
430
+ function S(e, n) {
377
431
  let r = (e, t, r) => `/api/v1/orgs/${i(n.org)}/projects/${i(e)}/flags/${i(t)}/environments/${i(r)}/targeting`;
378
432
  e.registerTool("get_targeting", {
379
433
  title: "Get targeting rules",
@@ -384,17 +438,17 @@ function y(e, n) {
384
438
  environment: t.string()
385
439
  }),
386
440
  annotations: { readOnlyHint: !0 }
387
- }, async ({ project: e, flag: t, environment: i }) => c(async () => o(await n.api.request("GET", r(e, t, i))))), e.registerTool("update_targeting", {
441
+ }, async ({ project: e, flag: t, environment: i }) => d(async () => l(await n.api.request("GET", r(e, t, i))))), e.registerTool("update_targeting", {
388
442
  title: "Replace targeting rules",
389
443
  description: "REPLACE all targeting rules of a flag in one environment (full PUT — rules not included are removed). Rules are evaluated in order. Get the current rules first with get_targeting.",
390
444
  inputSchema: t.object({
391
445
  project: t.string(),
392
446
  flag: t.string(),
393
447
  environment: t.string(),
394
- rules: t.array(v)
448
+ rules: t.array(x)
395
449
  }),
396
450
  annotations: { destructiveHint: !0 }
397
- }, async ({ project: e, flag: t, environment: i, rules: a }) => c(async () => (await n.api.request("PUT", r(e, t, i), { body: { rules: a } }), o({
451
+ }, async ({ project: e, flag: t, environment: i, rules: a }) => d(async () => (await n.api.request("PUT", r(e, t, i), { body: { rules: a } }), l({
398
452
  project: e,
399
453
  flag: t,
400
454
  environment: i,
@@ -403,7 +457,7 @@ function y(e, n) {
403
457
  }
404
458
  //#endregion
405
459
  //#region src/tools/variations.ts
406
- function b(e, n) {
460
+ function C(e, n) {
407
461
  e.registerTool("manage_variation", {
408
462
  title: "Add, update, or remove a variation",
409
463
  description: "Manage a flag's variations. action=add requires key + value; action=update/remove require variationId (get ids via get_flag). Removing a variation fails with VARIATION_HAS_DEPENDENTS if other flags depend on it.",
@@ -422,68 +476,213 @@ function b(e, n) {
422
476
  description: t.string().optional()
423
477
  }),
424
478
  annotations: { destructiveHint: !0 }
425
- }, async ({ project: e, flag: t, action: r, variationId: a, key: l, name: u, value: d, description: f }) => c(async () => {
426
- let c = `/api/v1/orgs/${i(n.org)}/projects/${i(e)}/flags/${i(t)}/variations`;
427
- return r === "add" ? !l || d === void 0 ? s(/* @__PURE__ */ Error("action=add requires key and value")) : o(await n.api.request("POST", c, { body: {
428
- key: l,
429
- name: u,
430
- value: d,
479
+ }, async ({ project: e, flag: t, action: r, variationId: a, key: o, name: s, value: c, description: f }) => d(async () => {
480
+ let d = `/api/v1/orgs/${i(n.org)}/projects/${i(e)}/flags/${i(t)}/variations`;
481
+ return r === "add" ? !o || c === void 0 ? u(/* @__PURE__ */ Error("action=add requires key and value")) : l(await n.api.request("POST", d, { body: {
482
+ key: o,
483
+ name: s,
484
+ value: c,
431
485
  description: f
432
- } })) : a ? r === "update" ? (await n.api.request("PUT", `${c}/${i(a)}`, { body: {
433
- name: u,
434
- value: d,
486
+ } })) : a ? r === "update" ? (await n.api.request("PUT", `${d}/${i(a)}`, { body: {
487
+ name: s,
488
+ value: c,
435
489
  description: f
436
- } }), o({ updated: a })) : (await n.api.request("DELETE", `${c}/${i(a)}`), o({ removed: a })) : s(/* @__PURE__ */ Error(`action=${r} requires variationId (see get_flag for ids)`));
490
+ } }), l({ updated: a })) : (await n.api.request("DELETE", `${d}/${i(a)}`), l({ removed: a })) : u(/* @__PURE__ */ Error(`action=${r} requires variationId (see get_flag for ids)`));
437
491
  }));
438
492
  }
439
493
  //#endregion
494
+ //#region src/tools/webhooks.ts
495
+ var w = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, T = "Requires an Admin token. A 404 on every webhook call means webhooks are not enabled for the organization.", E = {
496
+ limit: t.number().int().min(1).max(100).optional().describe("Page size (default 20)"),
497
+ cursor: t.string().optional().describe("next_cursor from a previous response")
498
+ };
499
+ function D(e, n) {
500
+ let r = `/api/v1/orgs/${i(n.org)}/webhooks`, a = (e) => `${r}/${i(e)}`;
501
+ async function o(e) {
502
+ let t = [];
503
+ for (let r of e) {
504
+ if (w.test(r)) {
505
+ t.push(r);
506
+ continue;
507
+ }
508
+ let e = await n.api.request("GET", `/api/v1/orgs/${i(n.org)}/projects/${i(r)}`);
509
+ if (!e?.id) throw Error(`Project "${r}" resolved without an id`);
510
+ t.push(e.id);
511
+ }
512
+ return t;
513
+ }
514
+ async function s(e) {
515
+ let t = e.map((e) => {
516
+ if (w.test(e)) return { id: e };
517
+ let t = e.indexOf("/"), n = e.slice(0, t), r = e.slice(t + 1);
518
+ if (t < 0 || !n || !r) throw Error(`Environment "${e}" needs its project: pass "<project>/<environment>", e.g. "web/production", or the environment's id.`);
519
+ return {
520
+ project: n,
521
+ env: r
522
+ };
523
+ }), r = [];
524
+ for (let e of t) {
525
+ if ("id" in e) {
526
+ r.push(e.id);
527
+ continue;
528
+ }
529
+ let t = await n.api.request("GET", `/api/v1/orgs/${i(n.org)}/projects/${i(e.project)}/environments/${i(e.env)}`);
530
+ if (!t?.id) throw Error(`Environment "${e.project}/${e.env}" resolved without an id`);
531
+ r.push(t.id);
532
+ }
533
+ return r;
534
+ }
535
+ e.registerTool("list_webhooks", {
536
+ title: "List webhook subscriptions",
537
+ description: "List the organization's outbound webhook subscriptions (url, event/project/environment filters, enabled state, failure count, signing secret ids — never secret values), plus availableEventTypes: the event types a subscription can filter on. Empty filter lists mean \"all\". Paginated via cursor. " + T,
538
+ inputSchema: t.object({ ...E }),
539
+ annotations: { readOnlyHint: !0 }
540
+ }, async ({ limit: e, cursor: t }) => d(async () => {
541
+ let [i, a] = await Promise.all([n.api.request("GET", r, { query: {
542
+ limit: e,
543
+ cursor: t
544
+ } }), n.api.request("GET", `${r}/event-types`)]);
545
+ return l({
546
+ ...i,
547
+ availableEventTypes: a
548
+ });
549
+ })), e.registerTool("list_webhook_deliveries", {
550
+ title: "List webhook deliveries",
551
+ description: "List one subscription's delivery attempts, most recent first. status is Pending (awaiting an attempt, backing off, or in flight), Succeeded or DeadLettered; lastResponseStatusCode and lastError explain a failure. Paginated via cursor. " + T,
552
+ inputSchema: t.object({
553
+ id: t.string().describe("Subscription id (from list_webhooks)"),
554
+ ...E
555
+ }),
556
+ annotations: { readOnlyHint: !0 }
557
+ }, async ({ id: e, limit: t, cursor: r }) => d(async () => l(await n.api.request("GET", `${a(e)}/deliveries`, { query: {
558
+ limit: t,
559
+ cursor: r
560
+ } })))), e.registerTool("manage_webhook", {
561
+ title: "Create, update, or delete a webhook subscription, or rotate its secret",
562
+ description: "action=create requires name + url and returns the signing secret. action=update/delete/rotate_secret/retire_secret require id. update changes only the fields you pass and keeps the rest; pass an empty list to widen that filter back to \"all\". projects take project keys or ids; environments take \"<project>/<environment>\" keys (e.g. \"web/production\") or ids. eventTypes come from list_webhooks → availableEventTypes. create and rotate_secret return a secret that is NEVER shown again: relay it to the user verbatim so they can store it. rotate_secret adds a new active secret while the old one keeps signing; retire_secret (secretId from list_webhooks) removes one and is refused for the last active secret. A project-restricted service token must list at least one project, all in its scope. " + T,
563
+ inputSchema: t.object({
564
+ action: t.enum([
565
+ "create",
566
+ "update",
567
+ "delete",
568
+ "rotate_secret",
569
+ "retire_secret"
570
+ ]),
571
+ id: t.string().optional().describe("Subscription id; required for everything except create"),
572
+ name: t.string().optional().describe("Required for create"),
573
+ url: t.string().optional().describe("Receiver URL; required for create"),
574
+ eventTypes: t.array(t.string()).optional().describe("Event types to send; empty or omitted = all"),
575
+ projects: t.array(t.string()).optional().describe("Project keys or ids to cover; empty or omitted on create = all"),
576
+ environments: t.array(t.string()).optional().describe("\"<project>/<environment>\" keys or environment ids; empty or omitted on create = all"),
577
+ enabled: t.boolean().optional().describe("update only; subscriptions are created enabled"),
578
+ secretId: t.string().optional().describe("Required for retire_secret"),
579
+ idempotency_key: t.string().optional().describe("Idempotency-Key header for create / rotate_secret")
580
+ }),
581
+ annotations: { destructiveHint: !0 }
582
+ }, async ({ action: e, id: t, name: c, url: f, eventTypes: p, projects: m, environments: h, enabled: g, secretId: _, idempotency_key: v }) => d(async () => {
583
+ if (e === "create") {
584
+ if (!c || !f) return u(/* @__PURE__ */ Error("action=create requires name and url"));
585
+ let e = h && await s(h), t = m && await o(m);
586
+ return l(await n.api.request("POST", r, {
587
+ body: {
588
+ name: c,
589
+ url: f,
590
+ provider: "GenericHttp",
591
+ eventTypes: p,
592
+ projectIds: t,
593
+ environmentIds: e
594
+ },
595
+ idempotencyKey: v
596
+ }));
597
+ }
598
+ if (!t) return u(/* @__PURE__ */ Error(`action=${e} requires id (see list_webhooks)`));
599
+ if (e === "update") {
600
+ let e = h && await s(h), r = m && await o(m), i = await n.api.request("GET", a(t)), u = {
601
+ name: c ?? i.name,
602
+ url: f ?? i.url,
603
+ eventTypes: p ?? i.eventTypes,
604
+ projectIds: r ?? i.projectIds,
605
+ environmentIds: e ?? i.environmentIds,
606
+ isEnabled: g ?? i.isEnabled
607
+ };
608
+ return await n.api.request("PUT", a(t), { body: u }), l({
609
+ id: t,
610
+ updated: !0,
611
+ ...u
612
+ });
613
+ }
614
+ return e === "delete" ? (await n.api.request("DELETE", a(t)), l({ deleted: t })) : e === "rotate_secret" ? l(await n.api.request("POST", `${a(t)}/secrets`, { idempotencyKey: v })) : _ ? (await n.api.request("DELETE", `${a(t)}/secrets/${i(_)}`), l({
615
+ id: t,
616
+ retiredSecretId: _
617
+ })) : u(/* @__PURE__ */ Error("action=retire_secret requires secretId (see list_webhooks)"));
618
+ })), e.registerTool("deliver_webhook", {
619
+ title: "Send a test event or redeliver",
620
+ description: "action=test queues one synthetic flag.toggled event to this subscription alone; action=redeliver (deliveryId from list_webhook_deliveries) re-sends a finished delivery, restarting its retry schedule. Delivery is asynchronous: read list_webhook_deliveries for the outcome. Both are refused for a disabled subscription, and redeliver for a delivery that is still Pending. " + T,
621
+ inputSchema: t.object({
622
+ action: t.enum(["test", "redeliver"]),
623
+ id: t.string().describe("Subscription id (from list_webhooks)"),
624
+ deliveryId: t.string().optional().describe("Required for redeliver")
625
+ }),
626
+ annotations: {
627
+ destructiveHint: !1,
628
+ openWorldHint: !0
629
+ }
630
+ }, async ({ action: e, id: t, deliveryId: r }) => d(async () => e === "test" ? l(await n.api.request("POST", `${a(t)}/test`)) : r ? (await n.api.request("POST", `${a(t)}/deliveries/${i(r)}/redeliver`), l({
631
+ id: t,
632
+ redelivered: r
633
+ })) : u(/* @__PURE__ */ Error("action=redeliver requires deliveryId (see list_webhook_deliveries)"))));
634
+ }
635
+ //#endregion
440
636
  //#region src/tools/workflows/stale-flags.ts
441
- var x = 50;
442
- function S(e, n) {
637
+ var O = 50;
638
+ function k(e, n) {
443
639
  e.registerTool("find_stale_flags", {
444
640
  title: "Find stale flags",
445
- 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). Checks at most ${x} candidates per call.`,
641
+ 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 and expired. Checks at most ${O} candidates per call, expired flags first.`,
446
642
  inputSchema: t.object({
447
643
  project: t.string(),
448
644
  days: t.number().int().min(1).optional().default(30).describe("Minimum age in days since last update")
449
645
  }),
450
646
  annotations: { readOnlyHint: !0 }
451
- }, async ({ project: e, days: t }) => c(async () => {
452
- let r = `/api/v1/orgs/${i(n.org)}/projects/${i(e)}/flags`, a = Date.now() - t * 24 * 60 * 60 * 1e3, s = [], c;
647
+ }, async ({ project: e, days: t }) => d(async () => {
648
+ let r = `/api/v1/orgs/${i(n.org)}/projects/${i(e)}/flags`, a = Date.now(), o = a - t * 24 * 60 * 60 * 1e3, s = (e) => !!e.expiresAtUtc && new Date(e.expiresAtUtc).getTime() <= a, c = [], u;
453
649
  do {
454
650
  let e = await n.api.request("GET", r, { query: {
455
651
  archived: !1,
456
652
  limit: 100,
457
- cursor: c
653
+ cursor: u
458
654
  } });
459
- s.push(...(e.items ?? []).filter((e) => !!e.key && !!e.name && !!e.updatedAt && new Date(e.updatedAt).getTime() < a)), c = e.next_cursor ?? void 0;
460
- } while (c);
461
- let l = s.length > x, u = s.slice(0, x), d = [];
462
- for (let e of u) {
655
+ c.push(...(e.items ?? []).filter((e) => !!e.key && !!e.name && !!e.updatedAt && (new Date(e.updatedAt).getTime() < o || s(e)))), u = e.next_cursor ?? void 0;
656
+ } while (u);
657
+ c.sort((e, t) => Number(s(t)) - Number(s(e)));
658
+ let d = c.length > O, f = c.slice(0, O), p = [];
659
+ for (let e of f) {
463
660
  let t = await n.api.request("GET", `${r}/${i(e.key)}/environments`);
464
661
  if (t.length === 0) continue;
465
- let a = t.every((e) => e.isEnabled), o = t.every((e) => !e.isEnabled);
466
- (a || o) && d.push({
662
+ let a = s(e), o = t.every((e) => e.isEnabled), c = t.every((e) => !e.isEnabled);
663
+ (o || c || a) && p.push({
467
664
  key: e.key,
468
665
  name: e.name,
469
666
  updatedAt: e.updatedAt,
470
- reason: a ? "enabled-everywhere" : "disabled-everywhere",
667
+ expiresAtUtc: e.expiresAtUtc ?? null,
668
+ expired: a,
669
+ reason: o ? "enabled-everywhere" : c ? "disabled-everywhere" : "past-expiry",
471
670
  environments: t.length
472
671
  });
473
672
  }
474
- return o({
673
+ return l({
475
674
  project: e,
476
675
  olderThanDays: t,
477
- checked: u.length,
478
- truncated: l,
479
- ...l ? { note: `Only the first ${x} of ${s.length} candidates were checked.` } : {},
480
- stale: d
676
+ checked: f.length,
677
+ truncated: d,
678
+ ...d ? { note: `Only the first ${O} of ${c.length} candidates were checked.` } : {},
679
+ stale: p
481
680
  });
482
681
  }));
483
682
  }
484
683
  //#endregion
485
684
  //#region src/tools/workflows/snippets.ts
486
- var C = [
685
+ var A = [
487
686
  "js",
488
687
  "node",
489
688
  "browser",
@@ -495,16 +694,16 @@ var C = [
495
694
  "php",
496
695
  "ruby",
497
696
  "swift"
498
- ], w = [
697
+ ], j = [
499
698
  "browser",
500
699
  "react",
501
700
  "swift"
502
701
  ];
503
- function T(e) {
504
- return w.includes(e);
702
+ function M(e) {
703
+ return j.includes(e);
505
704
  }
506
- var E = "Uses a SERVER-side SDK key (Featureflip dashboard → environment → SDK keys).", D = "Uses a CLIENT-side SDK key (safe to ship to browsers/apps).";
507
- function O(e, t) {
705
+ var N = "Uses a SERVER-side SDK key (Featureflip dashboard → environment → SDK keys).", P = "Uses a CLIENT-side SDK key (safe to ship to browsers/apps).";
706
+ function F(e, t) {
508
707
  switch (e) {
509
708
  case "js": return {
510
709
  install: "npm install @featureflip/js",
@@ -519,7 +718,7 @@ const enabled = client.boolVariation('${t}', { user_id: userId }, false);
519
718
  if (enabled) {
520
719
  // new code path
521
720
  }`,
522
- notes: E
721
+ notes: N
523
722
  };
524
723
  case "node": return {
525
724
  install: "npm install @featureflip/node",
@@ -530,7 +729,7 @@ const enabled = client.boolVariation('${t}', { user_id: userId }, false);
530
729
  if (enabled) {
531
730
  // new code path
532
731
  }`,
533
- notes: E
732
+ notes: N
534
733
  };
535
734
  case "browser": return {
536
735
  install: "npm install @featureflip/browser",
@@ -542,7 +741,7 @@ const enabled = client.boolVariation('${t}', false);
542
741
  if (enabled) {
543
742
  // new code path
544
743
  }`,
545
- notes: D
744
+ notes: P
546
745
  };
547
746
  case "react": return {
548
747
  install: "npm install @featureflip/react @featureflip/browser",
@@ -555,7 +754,7 @@ function MyComponent() {
555
754
  const enabled = useFeatureFlag('${t}', false);
556
755
  return enabled ? <NewExperience /> : <CurrentExperience />;
557
756
  }`,
558
- notes: D
757
+ notes: P
559
758
  };
560
759
  case "python": return {
561
760
  install: "pip install featureflip",
@@ -566,7 +765,7 @@ enabled = client.variation("${t}", {"user_id": user_id}, default=False)
566
765
  if enabled:
567
766
  # new code path
568
767
  ...`,
569
- notes: E
768
+ notes: N
570
769
  };
571
770
  case "csharp": return {
572
771
  install: "dotnet add package Featureflip.Client",
@@ -579,7 +778,7 @@ if (enabled)
579
778
  {
580
779
  // new code path
581
780
  }`,
582
- notes: E
781
+ notes: N
583
782
  };
584
783
  case "java": return {
585
784
  install: "implementation 'io.featureflip:featureflip-java:2.0.0'",
@@ -593,7 +792,7 @@ boolean enabled = client.boolVariation("${t}",
593
792
  if (enabled) {
594
793
  // new code path
595
794
  }`,
596
- notes: E
795
+ notes: N
597
796
  };
598
797
  case "go": return {
599
798
  install: "go get github.com/canopy-labs/featureflip-go/v2",
@@ -609,7 +808,7 @@ ctx := featureflip.EvaluationContext{UserID: userID}
609
808
  if client.BoolVariation("${t}", ctx, false) {
610
809
  // new code path
611
810
  }`,
612
- notes: E
811
+ notes: N
613
812
  };
614
813
  case "php": return {
615
814
  install: "composer require featureflip/featureflip-php",
@@ -624,7 +823,7 @@ $enabled = $client->boolVariation('${t}', ['user_id' => $userId], false);
624
823
  if ($enabled) {
625
824
  // new code path
626
825
  }`,
627
- notes: E + " Requires PSR-16 cache and PSR-18 HTTP implementations."
826
+ notes: N + " Requires PSR-16 cache and PSR-18 HTTP implementations."
628
827
  };
629
828
  case "ruby": return {
630
829
  install: "gem \"featureflip\"",
@@ -635,7 +834,7 @@ enabled = client.bool_variation("${t}", { "user_id" => user_id }, false)
635
834
  if enabled
636
835
  # new code path
637
836
  end`,
638
- notes: E
837
+ notes: N
639
838
  };
640
839
  case "swift": return {
641
840
  install: ".package(url: \"https://github.com/canopy-labs/featureflip-swift\", from: \"2.0.0\")",
@@ -648,13 +847,13 @@ let enabled = client.boolVariation("${t}", default: false)
648
847
  if enabled {
649
848
  // new code path
650
849
  }`,
651
- notes: D
850
+ notes: P
652
851
  };
653
852
  }
654
853
  }
655
854
  //#endregion
656
855
  //#region src/tools/workflows/wrap-feature.ts
657
- function k(e, n) {
856
+ function I(e, n) {
658
857
  e.registerTool("wrap_feature", {
659
858
  title: "Wrap a feature in a new flag",
660
859
  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.",
@@ -664,26 +863,26 @@ function k(e, n) {
664
863
  name: t.string().optional().describe("Display name; defaults to the key"),
665
864
  description: t.string().optional().describe("What this flag guards"),
666
865
  tags: t.array(t.string()).optional(),
667
- language: t.enum(C).describe("SDK language of the codebase being edited"),
866
+ language: t.enum(A).describe("SDK language of the codebase being edited"),
668
867
  idempotency_key: t.string().optional()
669
868
  }),
670
869
  annotations: { destructiveHint: !1 }
671
- }, async ({ project: e, key: t, name: r, description: a, tags: s, language: l, idempotency_key: u }) => c(async () => {
672
- let c = await n.api.request("POST", `/api/v1/orgs/${i(n.org)}/projects/${i(e)}/flags`, {
870
+ }, async ({ project: e, key: t, name: r, description: a, tags: o, language: s, idempotency_key: c }) => d(async () => {
871
+ let u = await n.api.request("POST", `/api/v1/orgs/${i(n.org)}/projects/${i(e)}/flags`, {
673
872
  body: {
674
873
  key: t,
675
874
  name: r ?? t,
676
875
  type: "Boolean",
677
876
  description: a,
678
- tags: s,
679
- ...T(l) ? { clientSideVisible: !0 } : {}
877
+ tags: o,
878
+ ...M(s) ? { clientSideVisible: !0 } : {}
680
879
  },
681
- idempotencyKey: u
682
- }), d = O(l, t);
683
- return o({
684
- flag: c,
880
+ idempotencyKey: c
881
+ }), d = F(s, t);
882
+ return l({
883
+ flag: u,
685
884
  integration: {
686
- language: l,
885
+ language: s,
687
886
  install: d.install,
688
887
  code: d.code,
689
888
  notes: d.notes
@@ -698,17 +897,17 @@ function k(e, n) {
698
897
  }
699
898
  //#endregion
700
899
  //#region src/server.ts
701
- function A(t) {
900
+ function L(t) {
702
901
  let r = new e({
703
902
  name: "featureflip",
704
903
  version: n
705
904
  });
706
- return u(r, t), d(r, t), f(r, t), m(r, t), g(r, t), y(r, t), b(r, t), S(r, t), k(r, t), r;
905
+ return p(r, t), m(r, t), h(r, t), _(r, t), y(r, t), S(r, t), C(r, t), D(r, t), k(r, t), I(r, t), r;
707
906
  }
708
907
  //#endregion
709
908
  //#region src/config.ts
710
- var j = "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.";
711
- function M(e = process.env) {
909
+ var R = "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.";
910
+ function z(e = process.env) {
712
911
  let t = e.FEATUREFLIP_TOKEN?.trim();
713
912
  return t ? {
714
913
  token: t,
@@ -716,14 +915,14 @@ function M(e = process.env) {
716
915
  org: e.FEATUREFLIP_ORG?.trim() || void 0
717
916
  } : null;
718
917
  }
719
- function N(e = process.env) {
720
- let t = M(e);
721
- if (!t) throw Error(j);
918
+ function B(e = process.env) {
919
+ let t = z(e);
920
+ if (!t) throw Error(R);
722
921
  return t;
723
922
  }
724
923
  //#endregion
725
924
  //#region src/org.ts
726
- async function P(e, t) {
925
+ async function V(e, t) {
727
926
  if (t) return t;
728
927
  let n = (await e.request("GET", "/api/v1/orgs", { query: { limit: 100 } })).items ?? [];
729
928
  if (n.length === 1) {
@@ -734,4 +933,4 @@ async function P(e, t) {
734
933
  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(", "));
735
934
  }
736
935
  //#endregion
737
- export { A as a, n as c, M as i, j as n, r as o, N as r, a as s, P as t };
936
+ export { L as a, n as c, z as i, R as n, r as o, B as r, c as s, V as t };