@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/README.md CHANGED
@@ -41,13 +41,14 @@ claude mcp add featureflip -e FEATUREFLIP_TOKEN=ffp_your_token -- npx -y @featur
41
41
  ## Tools
42
42
 
43
43
  CRUD: `list_projects`, `list_environments`, `list_flags`, `get_flag`, `create_flag`, `update_flag`,
44
- `delete_flag`, `archive_flag`, `restore_flag`, `set_flag_expiry`, `set_flag_owner`, `toggle_flag`,
45
- `update_flag_environment_config`, `get_targeting`, `update_targeting`, `manage_variation`, `list_segments`,
46
- `get_segment`
44
+ `delete_flag`, `archive_flag`, `restore_flag`, `set_flag_expiry`, `set_flag_owner`, `set_flag_permanent`,
45
+ `toggle_flag`, `update_flag_environment_config`, `get_targeting`, `update_targeting`, `manage_variation`,
46
+ `list_segments`, `get_segment`
47
47
 
48
48
  Webhooks (Admin token required): `list_webhooks`, `list_webhook_deliveries`, `manage_webhook`, `deliver_webhook`
49
49
 
50
50
  Workflows: `flag_status` (cross-environment view), `find_stale_flags` (cleanup candidates),
51
+ `list_removal_candidates` (flags safe to remove, with the prerequisite dependents to remove first),
51
52
  `wrap_feature` (create flag + get the SDK snippet for your language)
52
53
 
53
54
  Full reference: https://featureflip.io/docs/integrations/mcp/
package/dist/cli.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as e, i as t, n, o as r, s as i, t as a } from "./org-8tn1Su68.js";
1
+ import { a as e, i as t, n, o as r, s as i, t as a } from "./org-c4iWMSzU.js";
2
2
  import { serveStdio as o } from "@modelcontextprotocol/server/stdio";
3
3
  //#region src/unconfigured.ts
4
4
  var s = class extends i {
@@ -445,6 +445,7 @@ export interface paths {
445
445
  * Decode's "garbage input degrades gracefully" precedent)
446
446
  * rather than a 400, since it's a non-critical list filter, not a mutation. `owner` filters
447
447
  * by owner: an email (an active member, case-insensitive) or `none` for unowned flags.
448
+ * `permanent` filters to permanent (`true`) or temporary (`false`) flags.
448
449
  */
449
450
  get: {
450
451
  parameters: {
@@ -456,6 +457,7 @@ export interface paths {
456
457
  limit?: number;
457
458
  cursor?: string;
458
459
  owner?: string;
460
+ permanent?: boolean;
459
461
  };
460
462
  header?: never;
461
463
  path: {
@@ -637,9 +639,13 @@ export interface paths {
637
639
  /**
638
640
  * Lists confidently-dead (or, with `staleness=stale`, stale-or-dead) flags the Featureflip Flag Cleanup Action should remove, cursor-paginated, each with its kill `treatment` (true = keep the on-branch, false = keep the off-branch).
639
641
  * @description Only flags the staleness engine marks at or
640
- * above the requested tier via a decisive StuckRolledOut/StuckRolledBack reason are returned;
641
- * ZeroTraffic/CreatedUnused, cross-env-divergent, and archived flags are omitted (no confident code
642
- * treatment). The ONLY public projection of staleness.
642
+ * above the requested tier with a settled value are returned: a decisive StuckRolledOut/StuckRolledBack
643
+ * reason, or ZeroTraffic/CreatedUnused when no environment saw traffic and every environment's
644
+ * configuration serves one fixed variation (enabled, no rules, no prerequisites). Cross-env-divergent
645
+ * flags, flags without traffic whose configuration could serve more than one value, and archived
646
+ * flags are omitted (no confident code treatment). The ONLY public projection of staleness.
647
+ * Each item's `blockedBy` names the live flags that still list it as a prerequisite; while
648
+ * it is non-empty the flag cannot be archived (`FLAG_HAS_DEPENDENTS`), so remove those first.
643
649
  */
644
650
  get: {
645
651
  parameters: {
@@ -997,7 +1003,8 @@ export interface paths {
997
1003
  * date (`2026-12-01`), which means the end of that day in UTC, the same as picking it in the
998
1004
  * dashboard, or a full ISO-8601 timestamp, which is stored exactly. It must be in the future
999
1005
  * (400 `EXPIRY_IN_PAST`), so today's date is accepted, and the flag-expiration feature must
1000
- * be enabled (400 `FEATURE_NOT_ENABLED`).
1006
+ * be enabled (400 `FEATURE_NOT_ENABLED`). A permanent flag can't have a date
1007
+ * (400 `PERMANENT_FLAG_HAS_EXPIRY`).
1001
1008
  */
1002
1009
  put: {
1003
1010
  parameters: {
@@ -1371,6 +1378,193 @@ export interface paths {
1371
1378
  patch?: never;
1372
1379
  trace?: never;
1373
1380
  };
1381
+ "/api/v1/orgs/{org}/projects/{project}/flags/{flag}/permanent": {
1382
+ parameters: {
1383
+ query?: never;
1384
+ header?: never;
1385
+ path?: never;
1386
+ cookie?: never;
1387
+ };
1388
+ get?: never;
1389
+ /**
1390
+ * Marks the flag permanent: a kill switch, ops toggle or entitlement gate that is meant to stay.
1391
+ * @description A permanent flag is never offered as a removal candidate and never reported past expiry.
1392
+ * Requires at least Member. The flag can't have an expiry date (400 `PERMANENT_FLAG_HAS_EXPIRY`;
1393
+ * remove the date first) and the flag-permanence feature must be enabled (400 `FEATURE_NOT_ENABLED`).
1394
+ */
1395
+ put: {
1396
+ parameters: {
1397
+ query?: never;
1398
+ header?: never;
1399
+ path: {
1400
+ org: string;
1401
+ project: string;
1402
+ flag: string;
1403
+ };
1404
+ cookie?: never;
1405
+ };
1406
+ requestBody?: never;
1407
+ responses: {
1408
+ /** @description No Content */
1409
+ 204: {
1410
+ headers: {
1411
+ "X-RateLimit-Limit": components["headers"]["X-RateLimit-Limit"];
1412
+ "X-RateLimit-Remaining": components["headers"]["X-RateLimit-Remaining"];
1413
+ "X-RateLimit-Reset": components["headers"]["X-RateLimit-Reset"];
1414
+ [name: string]: unknown;
1415
+ };
1416
+ content?: never;
1417
+ };
1418
+ /** @description Bad Request */
1419
+ 400: {
1420
+ headers: {
1421
+ "X-RateLimit-Limit": components["headers"]["X-RateLimit-Limit"];
1422
+ "X-RateLimit-Remaining": components["headers"]["X-RateLimit-Remaining"];
1423
+ "X-RateLimit-Reset": components["headers"]["X-RateLimit-Reset"];
1424
+ [name: string]: unknown;
1425
+ };
1426
+ content: {
1427
+ "application/json": components["schemas"]["PublicApiErrorEnvelope"];
1428
+ };
1429
+ };
1430
+ /** @description Authentication is required, or the supplied API token is invalid or expired. */
1431
+ 401: {
1432
+ headers: {
1433
+ "X-RateLimit-Limit": components["headers"]["X-RateLimit-Limit"];
1434
+ "X-RateLimit-Remaining": components["headers"]["X-RateLimit-Remaining"];
1435
+ "X-RateLimit-Reset": components["headers"]["X-RateLimit-Reset"];
1436
+ [name: string]: unknown;
1437
+ };
1438
+ content: {
1439
+ "application/json": components["schemas"]["PublicApiErrorEnvelope"];
1440
+ };
1441
+ };
1442
+ /** @description Forbidden */
1443
+ 403: {
1444
+ headers: {
1445
+ "X-RateLimit-Limit": components["headers"]["X-RateLimit-Limit"];
1446
+ "X-RateLimit-Remaining": components["headers"]["X-RateLimit-Remaining"];
1447
+ "X-RateLimit-Reset": components["headers"]["X-RateLimit-Reset"];
1448
+ [name: string]: unknown;
1449
+ };
1450
+ content: {
1451
+ "application/json": components["schemas"]["PublicApiErrorEnvelope"];
1452
+ };
1453
+ };
1454
+ /** @description Not Found */
1455
+ 404: {
1456
+ headers: {
1457
+ "X-RateLimit-Limit": components["headers"]["X-RateLimit-Limit"];
1458
+ "X-RateLimit-Remaining": components["headers"]["X-RateLimit-Remaining"];
1459
+ "X-RateLimit-Reset": components["headers"]["X-RateLimit-Reset"];
1460
+ [name: string]: unknown;
1461
+ };
1462
+ content: {
1463
+ "application/json": components["schemas"]["PublicApiErrorEnvelope"];
1464
+ };
1465
+ };
1466
+ /** @description Rate limit exceeded. Retry after the interval indicated by the Retry-After header. */
1467
+ 429: {
1468
+ headers: {
1469
+ /** @description Number of seconds to wait before retrying the request. */
1470
+ "Retry-After"?: number;
1471
+ "X-RateLimit-Limit": components["headers"]["X-RateLimit-Limit"];
1472
+ "X-RateLimit-Remaining": components["headers"]["X-RateLimit-Remaining"];
1473
+ "X-RateLimit-Reset": components["headers"]["X-RateLimit-Reset"];
1474
+ [name: string]: unknown;
1475
+ };
1476
+ content: {
1477
+ "application/json": components["schemas"]["PublicApiErrorEnvelope"];
1478
+ };
1479
+ };
1480
+ };
1481
+ };
1482
+ post?: never;
1483
+ /**
1484
+ * Makes the flag temporary again.
1485
+ * @description Requires at least Member. Always allowed, even when the
1486
+ * flag-permanence feature is disabled.
1487
+ */
1488
+ delete: {
1489
+ parameters: {
1490
+ query?: never;
1491
+ header?: never;
1492
+ path: {
1493
+ org: string;
1494
+ project: string;
1495
+ flag: string;
1496
+ };
1497
+ cookie?: never;
1498
+ };
1499
+ requestBody?: never;
1500
+ responses: {
1501
+ /** @description No Content */
1502
+ 204: {
1503
+ headers: {
1504
+ "X-RateLimit-Limit": components["headers"]["X-RateLimit-Limit"];
1505
+ "X-RateLimit-Remaining": components["headers"]["X-RateLimit-Remaining"];
1506
+ "X-RateLimit-Reset": components["headers"]["X-RateLimit-Reset"];
1507
+ [name: string]: unknown;
1508
+ };
1509
+ content?: never;
1510
+ };
1511
+ /** @description Authentication is required, or the supplied API token is invalid or expired. */
1512
+ 401: {
1513
+ headers: {
1514
+ "X-RateLimit-Limit": components["headers"]["X-RateLimit-Limit"];
1515
+ "X-RateLimit-Remaining": components["headers"]["X-RateLimit-Remaining"];
1516
+ "X-RateLimit-Reset": components["headers"]["X-RateLimit-Reset"];
1517
+ [name: string]: unknown;
1518
+ };
1519
+ content: {
1520
+ "application/json": components["schemas"]["PublicApiErrorEnvelope"];
1521
+ };
1522
+ };
1523
+ /** @description Forbidden */
1524
+ 403: {
1525
+ headers: {
1526
+ "X-RateLimit-Limit": components["headers"]["X-RateLimit-Limit"];
1527
+ "X-RateLimit-Remaining": components["headers"]["X-RateLimit-Remaining"];
1528
+ "X-RateLimit-Reset": components["headers"]["X-RateLimit-Reset"];
1529
+ [name: string]: unknown;
1530
+ };
1531
+ content: {
1532
+ "application/json": components["schemas"]["PublicApiErrorEnvelope"];
1533
+ };
1534
+ };
1535
+ /** @description Not Found */
1536
+ 404: {
1537
+ headers: {
1538
+ "X-RateLimit-Limit": components["headers"]["X-RateLimit-Limit"];
1539
+ "X-RateLimit-Remaining": components["headers"]["X-RateLimit-Remaining"];
1540
+ "X-RateLimit-Reset": components["headers"]["X-RateLimit-Reset"];
1541
+ [name: string]: unknown;
1542
+ };
1543
+ content: {
1544
+ "application/json": components["schemas"]["PublicApiErrorEnvelope"];
1545
+ };
1546
+ };
1547
+ /** @description Rate limit exceeded. Retry after the interval indicated by the Retry-After header. */
1548
+ 429: {
1549
+ headers: {
1550
+ /** @description Number of seconds to wait before retrying the request. */
1551
+ "Retry-After"?: number;
1552
+ "X-RateLimit-Limit": components["headers"]["X-RateLimit-Limit"];
1553
+ "X-RateLimit-Remaining": components["headers"]["X-RateLimit-Remaining"];
1554
+ "X-RateLimit-Reset": components["headers"]["X-RateLimit-Reset"];
1555
+ [name: string]: unknown;
1556
+ };
1557
+ content: {
1558
+ "application/json": components["schemas"]["PublicApiErrorEnvelope"];
1559
+ };
1560
+ };
1561
+ };
1562
+ };
1563
+ options?: never;
1564
+ head?: never;
1565
+ patch?: never;
1566
+ trace?: never;
1567
+ };
1374
1568
  "/api/v1/orgs/{org}/projects/{project}/flags/{flag}/removal-pr": {
1375
1569
  parameters: {
1376
1570
  query?: never;
@@ -5960,7 +6154,10 @@ export interface components {
5960
6154
  * (400 `EXPIRY_IN_PAST` / `FEATURE_NOT_ENABLED`). OwnerEmail optionally names
5961
6155
  * the owner (an active member, matched case-insensitively; requires the flag-ownership feature —
5962
6156
  * 400 `OWNER_NOT_MEMBER` / `FEATURE_NOT_ENABLED`). Omitted, the owner is the caller when
5963
- * the caller is a member, so a service token creates an unowned flag.
6157
+ * the caller is a member, so a service token creates an unowned flag. IsPermanent marks
6158
+ * the flag permanent (a kill switch, ops toggle or entitlement that is meant to stay; never reported for
6159
+ * cleanup). It requires the flag-permanence feature and can't be combined with ExpiresAtUtc
6160
+ * (400 `FEATURE_NOT_ENABLED` / `PERMANENT_FLAG_HAS_EXPIRY`).
5964
6161
  * @example {
5965
6162
  * "key": "new-checkout-flow",
5966
6163
  * "name": "New Checkout Flow",
@@ -6002,6 +6199,7 @@ export interface components {
6002
6199
  /** Format: date-time */
6003
6200
  expiresAtUtc?: string | null;
6004
6201
  ownerEmail?: string | null;
6202
+ isPermanent?: boolean;
6005
6203
  };
6006
6204
  /**
6007
6205
  * @description Request body for `POST /api/v1/orgs/{org}/projects`. Named distinctly from the
@@ -6312,6 +6510,7 @@ export interface components {
6312
6510
  expiresAtUtc?: string | null;
6313
6511
  owner?: components["schemas"]["PublicFlagOwner"];
6314
6512
  removalPrUrl?: string | null;
6513
+ isPermanent?: boolean;
6315
6514
  };
6316
6515
  /**
6317
6516
  * @description A page of results plus an opaque cursor for the next page, or null when this is the last
@@ -6359,13 +6558,16 @@ export interface components {
6359
6558
  * (`"Stale"` or `"Dead"`, never `"Active"`) — lets a caller distinguish the two when
6360
6559
  * `?staleness=stale` widens the response to include both tiers. This is the ONLY public
6361
6560
  * projection of staleness — PublicFlagResponse and PublicFlagListItem
6362
- * still omit it by design.
6561
+ * still omit it by design. `BlockedBy` names the live flags that still list this one as a
6562
+ * prerequisite, sorted by key; while it is non-empty the flag cannot be archived
6563
+ * (`FLAG_HAS_DEPENDENTS`), so a caller should remove those dependents first.
6363
6564
  */
6364
6565
  PublicFlagRemovalCandidate: {
6365
6566
  key?: string | null;
6366
6567
  reason?: string | null;
6367
6568
  treatment?: boolean;
6368
6569
  status?: string | null;
6570
+ blockedBy?: string[] | null;
6369
6571
  };
6370
6572
  /**
6371
6573
  * @description A page of results plus an opaque cursor for the next page, or null when this is the last
@@ -6436,6 +6638,7 @@ export interface components {
6436
6638
  expiresAtUtc?: string | null;
6437
6639
  owner?: components["schemas"]["PublicFlagOwner"];
6438
6640
  removalPrUrl?: string | null;
6641
+ isPermanent?: boolean;
6439
6642
  /** @description Capability hints: which operations the authenticated caller may perform on this resource, each with allowed + optional reason. Computed from the caller's role and the resource's state. Injected at runtime; safe to ignore. Present only on top-level resource responses — nested occurrences (e.g. a rule inside a targeting-config response) do not carry it. */
6440
6643
  _actions?: {
6441
6644
  [key: string]: components["schemas"]["ActionCapability"];