@featureflip/mcp 0.1.8 → 0.1.10

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,12 +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`, `toggle_flag`, `update_flag_environment_config`,
45
- `get_targeting`, `update_targeting`, `manage_variation`, `list_segments`, `get_segment`
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`
46
47
 
47
48
  Webhooks (Admin token required): `list_webhooks`, `list_webhook_deliveries`, `manage_webhook`, `deliver_webhook`
48
49
 
49
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),
50
52
  `wrap_feature` (create flag + get the SDK snippet for your language)
51
53
 
52
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-DZ1ge1Mx.js";
1
+ import { a as e, i as t, n, o as r, s as i, t as a } from "./org-C5dAa5In.js";
2
2
  import { serveStdio as o } from "@modelcontextprotocol/server/stdio";
3
3
  //#region src/unconfigured.ts
4
4
  var s = class extends i {
@@ -637,9 +637,13 @@ export interface paths {
637
637
  /**
638
638
  * 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
639
  * @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.
640
+ * above the requested tier with a settled value are returned: a decisive StuckRolledOut/StuckRolledBack
641
+ * reason, or ZeroTraffic/CreatedUnused when no environment saw traffic and every environment's
642
+ * configuration serves one fixed variation (enabled, no rules, no prerequisites). Cross-env-divergent
643
+ * flags, flags without traffic whose configuration could serve more than one value, and archived
644
+ * flags are omitted (no confident code treatment). The ONLY public projection of staleness.
645
+ * Each item's `blockedBy` names the live flags that still list it as a prerequisite; while
646
+ * it is non-empty the flag cannot be archived (`FLAG_HAS_DEPENDENTS`), so remove those first.
643
647
  */
644
648
  get: {
645
649
  parameters: {
@@ -993,9 +997,11 @@ export interface paths {
993
997
  /**
994
998
  * Sets the flag's advisory expiry date.
995
999
  * @description Evaluation never changes when it passes — the flag is
996
- * reported stale so it can be cleaned up. Requires at least Member. The date must be in the
997
- * future (400 `EXPIRY_IN_PAST`) and the flag-expiration feature must be enabled
998
- * (400 `FEATURE_NOT_ENABLED`).
1000
+ * reported stale so it can be cleaned up. Requires at least Member. `expiresAtUtc` takes a
1001
+ * date (`2026-12-01`), which means the end of that day in UTC, the same as picking it in the
1002
+ * dashboard, or a full ISO-8601 timestamp, which is stored exactly. It must be in the future
1003
+ * (400 `EXPIRY_IN_PAST`), so today's date is accepted, and the flag-expiration feature must
1004
+ * be enabled (400 `FEATURE_NOT_ENABLED`).
999
1005
  */
1000
1006
  put: {
1001
1007
  parameters: {
@@ -1369,6 +1375,197 @@ export interface paths {
1369
1375
  patch?: never;
1370
1376
  trace?: never;
1371
1377
  };
1378
+ "/api/v1/orgs/{org}/projects/{project}/flags/{flag}/removal-pr": {
1379
+ parameters: {
1380
+ query?: never;
1381
+ header?: never;
1382
+ path?: never;
1383
+ cookie?: never;
1384
+ };
1385
+ get?: never;
1386
+ /**
1387
+ * Records the pull request that removes this flag from code, so the flag's owner is sent a link to it.
1388
+ * @description Called by the Featureflip Flag Cleanup Action. Requires at least Member. The URL
1389
+ * must be an https pull request URL (400 `validation_failed`).
1390
+ */
1391
+ put: {
1392
+ parameters: {
1393
+ query?: never;
1394
+ header?: never;
1395
+ path: {
1396
+ org: string;
1397
+ project: string;
1398
+ flag: string;
1399
+ };
1400
+ cookie?: never;
1401
+ };
1402
+ requestBody?: {
1403
+ content: {
1404
+ "application/json": components["schemas"]["PublicSetFlagRemovalPrRequest"];
1405
+ "text/json": components["schemas"]["PublicSetFlagRemovalPrRequest"];
1406
+ "application/*+json": components["schemas"]["PublicSetFlagRemovalPrRequest"];
1407
+ };
1408
+ };
1409
+ responses: {
1410
+ /** @description No Content */
1411
+ 204: {
1412
+ headers: {
1413
+ "X-RateLimit-Limit": components["headers"]["X-RateLimit-Limit"];
1414
+ "X-RateLimit-Remaining": components["headers"]["X-RateLimit-Remaining"];
1415
+ "X-RateLimit-Reset": components["headers"]["X-RateLimit-Reset"];
1416
+ [name: string]: unknown;
1417
+ };
1418
+ content?: never;
1419
+ };
1420
+ /** @description Bad Request */
1421
+ 400: {
1422
+ headers: {
1423
+ "X-RateLimit-Limit": components["headers"]["X-RateLimit-Limit"];
1424
+ "X-RateLimit-Remaining": components["headers"]["X-RateLimit-Remaining"];
1425
+ "X-RateLimit-Reset": components["headers"]["X-RateLimit-Reset"];
1426
+ [name: string]: unknown;
1427
+ };
1428
+ content: {
1429
+ "application/json": components["schemas"]["PublicApiErrorEnvelope"];
1430
+ };
1431
+ };
1432
+ /** @description Authentication is required, or the supplied API token is invalid or expired. */
1433
+ 401: {
1434
+ headers: {
1435
+ "X-RateLimit-Limit": components["headers"]["X-RateLimit-Limit"];
1436
+ "X-RateLimit-Remaining": components["headers"]["X-RateLimit-Remaining"];
1437
+ "X-RateLimit-Reset": components["headers"]["X-RateLimit-Reset"];
1438
+ [name: string]: unknown;
1439
+ };
1440
+ content: {
1441
+ "application/json": components["schemas"]["PublicApiErrorEnvelope"];
1442
+ };
1443
+ };
1444
+ /** @description Forbidden */
1445
+ 403: {
1446
+ headers: {
1447
+ "X-RateLimit-Limit": components["headers"]["X-RateLimit-Limit"];
1448
+ "X-RateLimit-Remaining": components["headers"]["X-RateLimit-Remaining"];
1449
+ "X-RateLimit-Reset": components["headers"]["X-RateLimit-Reset"];
1450
+ [name: string]: unknown;
1451
+ };
1452
+ content: {
1453
+ "application/json": components["schemas"]["PublicApiErrorEnvelope"];
1454
+ };
1455
+ };
1456
+ /** @description Not Found */
1457
+ 404: {
1458
+ headers: {
1459
+ "X-RateLimit-Limit": components["headers"]["X-RateLimit-Limit"];
1460
+ "X-RateLimit-Remaining": components["headers"]["X-RateLimit-Remaining"];
1461
+ "X-RateLimit-Reset": components["headers"]["X-RateLimit-Reset"];
1462
+ [name: string]: unknown;
1463
+ };
1464
+ content: {
1465
+ "application/json": components["schemas"]["PublicApiErrorEnvelope"];
1466
+ };
1467
+ };
1468
+ /** @description Rate limit exceeded. Retry after the interval indicated by the Retry-After header. */
1469
+ 429: {
1470
+ headers: {
1471
+ /** @description Number of seconds to wait before retrying the request. */
1472
+ "Retry-After"?: number;
1473
+ "X-RateLimit-Limit": components["headers"]["X-RateLimit-Limit"];
1474
+ "X-RateLimit-Remaining": components["headers"]["X-RateLimit-Remaining"];
1475
+ "X-RateLimit-Reset": components["headers"]["X-RateLimit-Reset"];
1476
+ [name: string]: unknown;
1477
+ };
1478
+ content: {
1479
+ "application/json": components["schemas"]["PublicApiErrorEnvelope"];
1480
+ };
1481
+ };
1482
+ };
1483
+ };
1484
+ post?: never;
1485
+ /**
1486
+ * Forgets the flag's removal pull request.
1487
+ * @description Requires at least Member.
1488
+ */
1489
+ delete: {
1490
+ parameters: {
1491
+ query?: never;
1492
+ header?: never;
1493
+ path: {
1494
+ org: string;
1495
+ project: string;
1496
+ flag: string;
1497
+ };
1498
+ cookie?: never;
1499
+ };
1500
+ requestBody?: never;
1501
+ responses: {
1502
+ /** @description No Content */
1503
+ 204: {
1504
+ headers: {
1505
+ "X-RateLimit-Limit": components["headers"]["X-RateLimit-Limit"];
1506
+ "X-RateLimit-Remaining": components["headers"]["X-RateLimit-Remaining"];
1507
+ "X-RateLimit-Reset": components["headers"]["X-RateLimit-Reset"];
1508
+ [name: string]: unknown;
1509
+ };
1510
+ content?: never;
1511
+ };
1512
+ /** @description Authentication is required, or the supplied API token is invalid or expired. */
1513
+ 401: {
1514
+ headers: {
1515
+ "X-RateLimit-Limit": components["headers"]["X-RateLimit-Limit"];
1516
+ "X-RateLimit-Remaining": components["headers"]["X-RateLimit-Remaining"];
1517
+ "X-RateLimit-Reset": components["headers"]["X-RateLimit-Reset"];
1518
+ [name: string]: unknown;
1519
+ };
1520
+ content: {
1521
+ "application/json": components["schemas"]["PublicApiErrorEnvelope"];
1522
+ };
1523
+ };
1524
+ /** @description Forbidden */
1525
+ 403: {
1526
+ headers: {
1527
+ "X-RateLimit-Limit": components["headers"]["X-RateLimit-Limit"];
1528
+ "X-RateLimit-Remaining": components["headers"]["X-RateLimit-Remaining"];
1529
+ "X-RateLimit-Reset": components["headers"]["X-RateLimit-Reset"];
1530
+ [name: string]: unknown;
1531
+ };
1532
+ content: {
1533
+ "application/json": components["schemas"]["PublicApiErrorEnvelope"];
1534
+ };
1535
+ };
1536
+ /** @description Not Found */
1537
+ 404: {
1538
+ headers: {
1539
+ "X-RateLimit-Limit": components["headers"]["X-RateLimit-Limit"];
1540
+ "X-RateLimit-Remaining": components["headers"]["X-RateLimit-Remaining"];
1541
+ "X-RateLimit-Reset": components["headers"]["X-RateLimit-Reset"];
1542
+ [name: string]: unknown;
1543
+ };
1544
+ content: {
1545
+ "application/json": components["schemas"]["PublicApiErrorEnvelope"];
1546
+ };
1547
+ };
1548
+ /** @description Rate limit exceeded. Retry after the interval indicated by the Retry-After header. */
1549
+ 429: {
1550
+ headers: {
1551
+ /** @description Number of seconds to wait before retrying the request. */
1552
+ "Retry-After"?: number;
1553
+ "X-RateLimit-Limit": components["headers"]["X-RateLimit-Limit"];
1554
+ "X-RateLimit-Remaining": components["headers"]["X-RateLimit-Remaining"];
1555
+ "X-RateLimit-Reset": components["headers"]["X-RateLimit-Reset"];
1556
+ [name: string]: unknown;
1557
+ };
1558
+ content: {
1559
+ "application/json": components["schemas"]["PublicApiErrorEnvelope"];
1560
+ };
1561
+ };
1562
+ };
1563
+ };
1564
+ options?: never;
1565
+ head?: never;
1566
+ patch?: never;
1567
+ trace?: never;
1568
+ };
1372
1569
  "/api/v1/orgs/{org}/projects/{project}/flags/{flag}/archive": {
1373
1570
  parameters: {
1374
1571
  query?: never;
@@ -5414,8 +5611,8 @@ export interface paths {
5414
5611
  /**
5415
5612
  * Queues a finished delivery (succeeded or dead-lettered) to be sent again, restarting its retry schedule.
5416
5613
  * @description Requires Admin. Refused with a 400 when the delivery is still pending, when
5417
- * the subscription is disabled, or when the delivery's event belongs to a project the
5418
- * subscription no longer covers.
5614
+ * the subscription is disabled, or when the subscription's current project, environment or
5615
+ * event-type filters no longer cover the delivery's event.
5419
5616
  */
5420
5617
  post: {
5421
5618
  parameters: {
@@ -5557,10 +5754,10 @@ export interface components {
5557
5754
  name?: string | null;
5558
5755
  slug?: string | null;
5559
5756
  /**
5560
- * @description Allowed values: Solo, Pro, Business, Enterprise.
5757
+ * @description Allowed values: Solo, Team, Pro, Business, Enterprise.
5561
5758
  * @enum {string|null}
5562
5759
  */
5563
- plan?: "Solo" | "Pro" | "Business" | "Enterprise" | null;
5760
+ plan?: "Solo" | "Team" | "Pro" | "Business" | "Enterprise" | null;
5564
5761
  isActive?: boolean;
5565
5762
  /** Format: date-time */
5566
5763
  createdAt?: string;
@@ -5648,9 +5845,13 @@ export interface components {
5648
5845
  * `docsUrl`/`retryAfter`, breaking the published spec. !:JsonPropertyName
5649
5846
  * always wins over the policy, so these are pinned explicitly rather than relying on the
5650
5847
  * property names already being lowercase for the single-word ones.
5651
- * `did_you_mean` and `next_actions` are ADDITIVE optional keys (null → omitted via
5652
- * the global `DefaultIgnoreCondition = WhenWritingNull`), so pre-existing error bodies
5653
- * are byte-for-byte unchanged when they're absent.
5848
+ * `did_you_mean`, `next_actions` and `connection_id` are ADDITIVE optional keys
5849
+ * (null → omitted via the global `DefaultIgnoreCondition = WhenWritingNull`), so
5850
+ * pre-existing error bodies are byte-for-byte unchanged when they're absent.
5851
+ *
5852
+ * `connection_id` is present only on the `sso_required` error: the organization
5853
+ * requires single sign-on, and this credential didn't come through its identity provider.
5854
+ * `connection_id` identifies which SSO connection to sign in with.
5654
5855
  * @example {
5655
5856
  * "error": "not_found",
5656
5857
  * "message": "Flag 'new-checkout-flw' was not found in project 'checkout'.",
@@ -5677,6 +5878,8 @@ export interface components {
5677
5878
  retry_after?: number | null;
5678
5879
  did_you_mean?: string[] | null;
5679
5880
  next_actions?: components["schemas"]["NextAction"][] | null;
5881
+ /** Format: uuid */
5882
+ connection_id?: string | null;
5680
5883
  };
5681
5884
  /** @description Request-side condition group, symmetric with PublicConditionGroupResponse. */
5682
5885
  PublicConditionGroupRequest: {
@@ -5756,7 +5959,8 @@ export interface components {
5756
5959
  * is passed through verbatim to Type, which itself takes a
5757
5960
  * bare string (Boolean/String/Number/Json) rather than the domain enum — invalid values surface
5758
5961
  * as the handler's own validation failure, not a controller-level 400. ExpiresAtUtc is
5759
- * optional and advisory; it must be in the future and requires the flag-expiration feature
5962
+ * optional and advisory; a bare date (`2026-12-01`) means the end of that day in UTC, and a full
5963
+ * timestamp is stored exactly. It must be in the future and requires the flag-expiration feature
5760
5964
  * (400 `EXPIRY_IN_PAST` / `FEATURE_NOT_ENABLED`). OwnerEmail optionally names
5761
5965
  * the owner (an active member, matched case-insensitively; requires the flag-ownership feature —
5762
5966
  * 400 `OWNER_NOT_MEMBER` / `FEATURE_NOT_ENABLED`). Omitted, the owner is the caller when
@@ -6111,6 +6315,7 @@ export interface components {
6111
6315
  /** Format: date-time */
6112
6316
  expiresAtUtc?: string | null;
6113
6317
  owner?: components["schemas"]["PublicFlagOwner"];
6318
+ removalPrUrl?: string | null;
6114
6319
  };
6115
6320
  /**
6116
6321
  * @description A page of results plus an opaque cursor for the next page, or null when this is the last
@@ -6158,13 +6363,16 @@ export interface components {
6158
6363
  * (`"Stale"` or `"Dead"`, never `"Active"`) — lets a caller distinguish the two when
6159
6364
  * `?staleness=stale` widens the response to include both tiers. This is the ONLY public
6160
6365
  * projection of staleness — PublicFlagResponse and PublicFlagListItem
6161
- * still omit it by design.
6366
+ * still omit it by design. `BlockedBy` names the live flags that still list this one as a
6367
+ * prerequisite, sorted by key; while it is non-empty the flag cannot be archived
6368
+ * (`FLAG_HAS_DEPENDENTS`), so a caller should remove those dependents first.
6162
6369
  */
6163
6370
  PublicFlagRemovalCandidate: {
6164
6371
  key?: string | null;
6165
6372
  reason?: string | null;
6166
6373
  treatment?: boolean;
6167
6374
  status?: string | null;
6375
+ blockedBy?: string[] | null;
6168
6376
  };
6169
6377
  /**
6170
6378
  * @description A page of results plus an opaque cursor for the next page, or null when this is the last
@@ -6234,6 +6442,7 @@ export interface components {
6234
6442
  /** Format: date-time */
6235
6443
  expiresAtUtc?: string | null;
6236
6444
  owner?: components["schemas"]["PublicFlagOwner"];
6445
+ removalPrUrl?: string | null;
6237
6446
  /** @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. */
6238
6447
  _actions?: {
6239
6448
  [key: string]: components["schemas"]["ActionCapability"];
@@ -6406,7 +6615,8 @@ export interface components {
6406
6615
  * @description Request body for `PUT /api/v1/orgs/{org}/projects/{project}/flags/{flag}/expiry`. Expiry is a
6407
6616
  * dedicated sub-resource, deliberately NOT a field on PublicUpdateFeatureFlagRequest: that
6408
6617
  * PUT is a full replace, so a new nullable field there would let any client that doesn't know about
6409
- * expiry (the Terraform provider included) wipe it on every update.
6618
+ * expiry (the Terraform provider included) wipe it on every update. A bare date means the end of that
6619
+ * UTC day; a full timestamp is stored exactly.
6410
6620
  */
6411
6621
  PublicSetFlagExpiryRequest: {
6412
6622
  /** Format: date-time */
@@ -6421,6 +6631,15 @@ export interface components {
6421
6631
  PublicSetFlagOwnerRequest: {
6422
6632
  email?: string | null;
6423
6633
  };
6634
+ /**
6635
+ * @description Request body for `PUT /api/v1/orgs/{org}/projects/{project}/flags/{flag}/removal-pr`: the
6636
+ * pull request that removes this flag from code, as opened by the Featureflip Flag Cleanup Action.
6637
+ * A dedicated sub-resource for the same reason as expiry and owner. `url` must be an https
6638
+ * pull request URL (`https://{host}/{owner}/{repo}/pull/{number}`).
6639
+ */
6640
+ PublicSetFlagRemovalPrRequest: {
6641
+ url?: string | null;
6642
+ };
6424
6643
  /**
6425
6644
  * @description The full targeting configuration for a flag in a single environment, returned by
6426
6645
  * `GET .../targeting`. Maps 1:1 onto TargetingConfigurationDto — including
@@ -6696,6 +6915,10 @@ export interface components {
6696
6915
  /**
6697
6916
  * @description A webhook subscription. Never carries secret material: signing secrets are listed by id and
6698
6917
  * lifecycle only — the value itself is returned once, when it is created or rotated.
6918
+ * `overPlanLimit` is true when the organization has more subscriptions than its plan
6919
+ * allows, which happens after a downgrade. The oldest subscriptions up to the limit keep
6920
+ * delivering. The rest receive nothing and cannot be re-enabled, tested or redelivered to until
6921
+ * the plan is upgraded or older subscriptions are deleted.
6699
6922
  * @example {
6700
6923
  * "id": "0197b6a4-6f7a-7b8c-9d0e-1f2a3b4c5d6e",
6701
6924
  * "name": "Checkout flag changes to Slack relay",
@@ -6746,6 +6969,7 @@ export interface components {
6746
6969
  /** Format: date-time */
6747
6970
  updatedAt?: string;
6748
6971
  secrets?: components["schemas"]["PublicWebhookSecretResponse"][] | null;
6972
+ overPlanLimit?: boolean;
6749
6973
  /** @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. */
6750
6974
  _actions?: {
6751
6975
  [key: string]: components["schemas"]["ActionCapability"];