Skip to main content
Glama
canopy-labs

Featureflip

Official

List removal candidates

list_removal_candidates
Read-only

List feature flags the server confirms can be removed, showing reason, status, treatment, and blocking live prerequisites. Remove dependents first; only flags with empty blockedBy are safe.

Instructions

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.

Input Schema

NameRequiredDescriptionDefault
limitNoPage size (default 20)
cursorNonext_cursor from a previous response
projectYesProject key
stalenessNoMinimum staleness tier: dead (default) or stale

Output Schema

NameRequiredDescriptionDefault
itemsNo
next_cursorNo

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed3 schema fields changedv0.3.0
    • addedInput schema / properties / cursor / description
      Added value: +"next_cursor from a previous response"
    • addedInput schema / properties / limit / description
      Added value: +"Page size (default 20)"
    • changedOutput schema / (root)
      Previous value: -nullNew value: +{
      +  "$defs": {
      +    "PublicFlagRemovalCandidate": {
      +      "description": "One dead flag the Featureflip Flag Cleanup Action should remove, returned by\n`GET .../flags/removal-candidates`. `Treatment` is the kill decision: true → keep the\nflag's on-branch, false → keep the off-branch. `Status` is the flag's staleness tier\n(`\"Stale\"` or `\"Dead\"`, never `\"Active\"`) — lets a caller distinguish the two when\n`?staleness=stale` widens the response to include both tiers. This is the ONLY public\nprojection of staleness — PublicFlagResponse and PublicFlagListItem\nstill omit it by design. `BlockedBy` names the live flags that still list this one as a\nprerequisite, sorted by key; while it is non-empty the flag cannot be archived\n(`FLAG_HAS_DEPENDENTS`), so a caller should remove those dependents first.",
      +      "properties": {
      +        "blockedBy": {
      +          "items": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "type": [
      +            "array",
      +            "null"
      +          ]
      +        },
      +        "key": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "reason": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "status": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "treatment": {
      +          "type": [
      +            "boolean",
      +            "null"
      +          ]
      +        }
      +      },
      +      "type": [
      +        "object",
      +        "null"
      +      ]
      +    }
      +  },
      +  "description": "A page of results plus an opaque cursor for the next page, or null when this is the last\npage. Wire keys are frozen: `items` (already lowercase under the camelCase policy) and\n`next_cursor` (pinned explicitly — the policy alone would emit `nextCursor`).",
      +  "properties": {
      +    "items": {
      +      "items": {
      +        "anyOf": [
      +          {
      +            "$ref": "#/$defs/PublicFlagRemovalCandidate"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ]
      +      },
      +      "type": [
      +        "array",
      +        "null"
      +      ]
      +    },
      +    "next_cursor": {
      +      "type": [
      +        "string",
      +        "null"
      +      ]
      +    }
      +  },
      +  "type": "object"
      +}
  2. Addedv0.1.10

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint=true; the description adds substantive behavior beyond that — the semantic contract of each returned field, the blockedBy dependency signal, and why archive_flag will refuse a still-depended-on flag. This is real context an agent cannot get from the safety hint alone.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well front-loaded: the purpose and alternative-context come first, then item shape, then the critical removal rule. It is somewhat long and the treatment-field gloss leans on output-schema territory, but no sentence is pure filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given a read-only, paginated list tool with a rich output schema and a non-obvious dependency-removal hazard, the description covers purpose, ordering, error behavior, and pagination. An agent has everything needed to list and safely act on results.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds meaning by clarifying the tiered semantics of staleness ("dead" returns only dead flags, "stale" adds stale ones) and noting cursor pagination. This nudges above the schema-only baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb+resource ("List the flags the server is confident can be removed from code") and pins the exact item shape (key, reason, status, treatment, blockedBy), clearly distinguishing this removal-oriented list from siblings like list_flags and find_stale_flags.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It states the operative rule ("Only remove a flag whose blockedBy is empty"), the ordering constraint (dependents first, prerequisites last), and the consequence of violating it, including the cross-tool interaction with archive_flag and its FLAG_HAS_DEPENDENTS error. This is explicit when/when-not guidance tied to a sibling tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.