Skip to content

feat(safety): annotate destructive tools and add a read-only mode #158

Description

@BryanFRD

Problem

The sub-MCPs expose irreversible / high-impact write tools with no machine-readable safety signal and no way to disable them:

  • delete_secret, rotate_secret, create_secret, update_secretpackages/mcp-vault/src/tools/secret-mutations.ts
  • delete_vault ("Delete a vault and ALL its secrets. Irreversible.") — packages/mcp-vault/src/tools/vaults.ts
  • trigger_agent_run (spends FerrFleet run quota / executes agents) — packages/mcp-fleet/src/tools/agents.ts
  • activate_release (switches the live serving release of a site) — packages/mcp-growth/src/tools/releases.ts
  • revoke_tokenpackages/mcp/src/tools/tokens.ts

None of these declare MCP tool annotations. Every server.tool(...) call passes only (name, description, schema, handler) — no annotations: { readOnlyHint, destructiveHint, idempotentHint }. There is also no FERRLABS_MCP_READONLY-style switch to register only read tools.

Why it matters

The danger lives entirely in a free-text description string. MCP clients increasingly use destructiveHint / readOnlyHint to gate auto-approval and to warn users before running a tool. Without them, an assistant can silently delete_secret or trigger_agent_run (which costs money and runs code) with no client-side guardrail. A read-only mode is the standard mitigation for users who want the MCP for inspection only.

Proposed approach

  • Add an annotations object to every server.tool registration: readOnlyHint: true for list/get/fetch tools, destructiveHint: true (and idempotentHint where applicable) for the mutating tools above.
  • Add a FERRLABS_MCP_READONLY=1 env flag that skips registration of all write/destructive tools, documented in the README.
  • Centralize the registration helper so the annotation is mandatory at the type level (hard to forget when adding a new tool).

Acceptance criteria

  • tools/list reports correct readOnlyHint / destructiveHint for every tool.
  • With FERRLABS_MCP_READONLY=1, no destructive tool is registered; a test asserts the registered set.
  • README documents the read-only flag and the annotation convention.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementImprovement to existing featuresecuritySecurity-related

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions