mcp_authorization v0.7.1

Three layers of authorization

The gem gives you three independent controls over what each user sees:

The gem gives you three independent controls over what each user sees:

LayerMechanismEffect
Tool visibilityauthorization :manage_workflows (RBAC) or gate :feature, :sms (any predicate) on the tool classTool hidden entirely from users who fail any check
Input fields@requires(:backward_routing) or @feature(:sms) on a param in #: annotationField excluded from the input schema and stripped from inbound params at call time
Output variants@requires(:backward_routing) or @feature(:sms) on a variant in @rbs type outputVariant excluded from the oneOf and fields projected out of the handler's return value before it crosses the wire

Tool-level authorization :perm is RBAC (calls current_user.can?). Tool-level gate :predicate, :value and the field-level annotations are generic โ€” any predicate name works, as long as the server context implements {predicate}?(value). See Generic predicate tags below.

Enforcement, not just shaping๐Ÿ”—

@requires is a security boundary, not a hint. At tool-call time the gem:

  • Filters inbound params against the user's compiled input schema. Gated fields, and any keys not declared in the schema at all, are dropped before the handler's #call is invoked. A handler that takes force: gated behind @requires(:admin) will see force: false (its default) for non-admins even if the MCP client sends force: true in the raw JSON-RPC payload.
  • Projects the handler's return value onto the user's compiled output schema. A variant hidden by @requires has its shape unavailable, so if the handler erroneously emits that variant's extra fields, they are stripped before serialization. A handler bug or refactor accident cannot leak admin-only fields to a non-admin.

This means handler authors don't have to remember to re-check can? at every branch -- the schema is the boundary. can? inside #call is still useful for logic that changes behavior (not just field visibility), but it is no longer load-bearing for security.

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