mcp_authorization v0.7.1

13. Invent your own predicate vocabulary

@requires (RBAC) and @feature (account flags) aren't enough. You want to gate on plan tier, beta enrollment, A/B bucket โ€” whatever your domain needs.

Problem. @requires (RBAC) and @feature (account flags) aren't enough. You want to gate on plan tier, beta enrollment, A/B bucket โ€” whatever your domain needs.

Solution. Any @name(value) that isn't a known constraint tag is a generic predicate. At compile time the gem calls server_context.{name}?(value). Define the predicates on your context; use the tags freely.

# Your server context โ€” one method per predicate you want to use:
class ServerContext
  def requires?(flag) = current_user.can?(flag.to_sym)
  def feature?(flag)  = account.feature_enabled?(flag.to_s)
  def tier?(name)     = account.plan_tier?(name.to_s)
  def beta?(flag)     = account.beta_enrolled?(flag.to_s)
end
# Use them on fields...
#: (
#:   ?status: "active" | "inactive" | "unlisted"  @feature(:opening_status_v2),
#:   ?bulk_limit: Integer                          @tier(:enterprise),
#:   ?experimental_ranking: bool                   @beta(:ranking_v2)
#: ) -> Hash[Symbol, untyped]

# ...and on whole tools, via gate:
class ExportEverythingTool < McpAuthorization::Tool
  gate :tier, :enterprise
  gate :beta, :bulk_export
end

Result. Multiple predicates on one field AND together โ€” all must pass for the field to appear. If the context doesn't respond to a predicate method, it's skipped (permissive at the field level; fail-open at the tool level with a dev warning). One pipeline, infinite vocabulary.

Collected from COOKBOOK.md in the repository. Edit it there, not here.