Skip to main content
Glama
canopy-labs

Featureflip

Official

Find stale flags

find_stale_flags
Read-only

Find stale feature flags that are candidates for removal based on age, rollout state, or expired dates, so teams can plan cleanup and archiving.

Instructions

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. A project can give its temporary flags a default lifetime, so a flag with no date of its own can still be past expiry. Every result carries expiresAtUtc (the date set on the flag), effectiveExpiresAtUtc (the date in effect, from the flag or its project default), expirySource (explicit, project-default, or null), 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 50 candidates per call, expired flags first.

Input Schema

NameRequiredDescriptionDefault
daysNoMinimum age in days since last update
ownerNoOnly flags with this owner: an email, "me" for the token's own user, or "none" for unowned flags
projectYesProject key

Output Schema

NameRequiredDescriptionDefault
noteNo
ownerNo
staleYes
checkedYes
projectYes
truncatedYes
blockedByNoteNo
olderThanDaysYes

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed2 schema fields changedv0.3.0
    • addedInput schema / properties / project / description
      Added value: +"Project key"
    • changedOutput schema / (root)
      Previous value: -nullNew value: +{
      +  "$defs": {
      +    "PublicFlagOwner": {
      +      "description": "A flag's owner as returned on flag reads. Null means the flag is unowned.",
      +      "properties": {
      +        "email": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "id": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        },
      +        "name": {
      +          "type": [
      +            "string",
      +            "null"
      +          ]
      +        }
      +      },
      +      "type": [
      +        "object",
      +        "null"
      +      ]
      +    }
      +  },
      +  "properties": {
      +    "blockedByNote": {
      +      "type": "string"
      +    },
      +    "checked": {
      +      "type": "integer"
      +    },
      +    "note": {
      +      "type": "string"
      +    },
      +    "olderThanDays": {
      +      "type": "integer"
      +    },
      +    "owner": {
      +      "type": "string"
      +    },
      +    "project": {
      +      "type": "string"
      +    },
      +    "stale": {
      +      "items": {
      +        "properties": {
      +          "blockedBy": {
      +            "description": "null means unknown, not unblocked",
      +            "items": {
      +              "type": "string"
      +            },
      +            "type": [
      +              "array",
      +              "null"
      +            ]
      +          },
      +          "effectiveExpiresAtUtc": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "environments": {
      +            "type": "integer"
      +          },
      +          "expired": {
      +            "type": "boolean"
      +          },
      +          "expiresAtUtc": {
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "expirySource": {
      +            "description": "explicit, project-default, or null",
      +            "type": [
      +              "string",
      +              "null"
      +            ]
      +          },
      +          "key": {
      +            "type": "string"
      +          },
      +          "name": {
      +            "type": "string"
      +          },
      +          "owner": {
      +            "anyOf": [
      +              {
      +                "$ref": "#/$defs/PublicFlagOwner"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ]
      +          },
      +          "reason": {
      +            "description": "enabled-everywhere, disabled-everywhere or past-expiry",
      +            "type": "string"
      +          },
      +          "updatedAt": {
      +            "type": "string"
      +          }
      +        },
      +        "type": "object"
      +      },
      +      "type": "array"
      +    },
      +    "truncated": {
      +      "type": "boolean"
      +    }
      +  },
      +  "required": [
      +    "project",
      +    "olderThanDays",
      +    "checked",
      +    "truncated",
      +    "stale"
      +  ],
      +  "type": "object"
      +}
  2. Changed1 schema field changedv0.1.9
    • addedInput schema / properties / owner
      Added value: +{
      +  "description": "Only flags with this owner: an email, \"me\" for the token's own user, or \"none\" for unowned flags",
      +  "type": "string"
      +}
  3. First observedv0.1.6

TDQS

A4.3/5.0
Behavior5/5

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

Annotations only declare readOnlyHint=true, but the description adds substantial behavior: permanent flags (kill switches, ops toggles) are never listed, at most 50 candidates are checked per call with expired flags first, and blockedBy=null means 'unknown, not unblocked'. It also discloses the downstream failure mode (archive_flag fails with FLAG_HAS_DEPENDENTS if a dependent still names the flag), which is exactly the kind of cross-tool behavior an agent needs.

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

Conciseness3/5

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

The purpose and core criteria are front-loaded, which is good, but the body is a dense single paragraph, and a sizeable chunk enumerates result fields (expiresAtUtc, effectiveExpiresAtUtc, expirySource, expired, owner) that the output schema already defines. Those sentences do not earn their place as strongly as the criteria, cap, and dependency-ordering content.

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?

For a complex triage tool, the description covers the stale predicate, expiry and project-default lifetime rules, permanent-flag exclusion, owner filtering, the 50-candidate cap and ordering, and the prerequisite/dependency constraint that governs follow-up archival. With an output schema handling return values and annotations covering safety, nothing an agent needs to call it correctly is missing.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already documents all three parameters. The description restates them at the same level of detail ('not updated in N days', owner 'me'/'none', project key) without adding syntax, format, or edge-case meaning. Baseline 3 is appropriate when the schema carries the parameter burden.

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 opening sentence names a specific verb and resource ('Find flags that look ready for code cleanup') and then defines the exact predicate that makes a flag stale: N days without update AND either on in every environment or off in every environment, plus the overrides for expired flags. An agent can tell precisely what this tool returns without opening the schema, though it does not explicitly contrast itself with the nearby list_removal_candidates sibling.

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

Usage Guidelines4/5

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

It gives clear operative context: run this to identify cleanup candidates, filter with owner ('me'/'none'), and it warns to verify rollout is complete before removing. It also explains the removal workflow ordering versus archive_flag (dependents first, FLAG_HAS_DEPENDENTS otherwise), which steers correct usage. However, it never states when NOT to use it or names an alternative tool for overlapping needs.

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