SCOPED CAPABILITIES

Show callers what they can use. Reject what they cannot.

Serve different capability sets from one MCP using application-resolved scopes, with authorization on discovery and direct calls.

Build this with Chumbo
ChumboSupabaseScoped capabilities
SAME MCP. DIFFERENT CALLERS.
Application-resolved scopes
Checked on discovery and call
Show what they can use. Reject the rest.

THE RECIPE Serve different capability sets from one MCP using application-resolved scopes, with authorization on discovery and direct calls.

View source pattern ↗
SOURCE REVIEW

Reviewed against the shipped OSS source. The acceptance checks are yours to run in your application.

Version and verification details

Source reviewed against published Chumbo 0.11.0 and its matching core source on 2026-09-06 (UTC). This is a source review, not an executed application walkthrough. Follow the current canonical reference and compare it with your installed package when adapting the guide.

01

Different callers can need different tools.

A catalog reader needs to find items. A publishing integration also needs to publish an approved item. Keeping both on one endpoint can be useful if your app already recognizes these caller relationships. Chumbo’s scopes let each request discover the capabilities its resolved permissions allow.

Two callers, one endpointIllustrative data

Read the catalog

Starting point
Caller resolves catalog:read.
  • list_catalog is discoverable.
  • publish_item is absent.
What to do
Reject a direct publish_item call without invoking its handler.

Omitting a tool from a UI is insufficient.

Read and publish

Starting point
Caller resolves catalog:read and catalog:publish.
  • Both capabilities are discoverable.
  • The publish operation still checks application data authority.
What to do
Allow an eligible publish through the application’s authorized write path.

A scope gate does not replace grants, RLS or domain checks.

A hidden tool must also reject a direct call by name.

02

Resolve permissions from your application.

Before you start
  • Two real caller types with different application-authorized capabilities.
  • An existing source of permissions or a narrow resolver that can supply scopes.
  • For application keys, a verifier with a stable subject, a unique routing prefix in multi-auth and a defined data-access path.

Chumbo does not define owner/admin roles, membership tables or an entitlement model. Choose your own scope vocabulary.

Start from the canonical privileged-capabilities reference. Its multi auth configuration combines a Supabase user strategy with an application-key strategy. Give verifier-backed keys a unique tokenPrefix so routing selects the intended verifier before authentication. A failed matching key must not fall through and become another identity.

Use access.resolveScopes when your existing application owns the permission decision. Keep it authoritative: accepting a scope string from a tool argument or user-editable profile field would let the caller choose their own permissions.

03

Apply the gate where capabilities are registered.

Verified identityApplication scopeswithScopes registrationDiscovery and invocation

Register restricted tools, Resources and prompts through server.withScopes. The gated registration participates in the request’s isolated server, so a missing scope affects both listing and calling. Keep request-aware instructions consistent with that surface: a reader should not be told to use a publisher-only action.

User requests retain their Supabase identity and caller-scoped client. Application-key requests receive the verifier’s stable subject and scopes with an anonymous Supabase client. Use your existing narrow RPC or application API for privileged work. The verifier-only administrative client is not a capability-handler escape hatch.

04

Try the name the reader was never shown.

The testing guide below includes an executable scoped-discovery fixture through chumbo/testing. Use that to test runtime composition with controlled identities. Then use your real auth, key verifier and database fixtures to prove the application permission boundary.

YOUR ACCEPTANCE CHECK

Denied capabilities never reach their handlers.

  • 01A reader’s tools/list omits the restricted tool and their direct tools/call is rejected.
  • 02A permitted caller discovers the tool and can perform an eligible operation.
  • 03Restricted Resources and prompts receive equivalent direct-access checks.
  • 04An invalid key with the application prefix does not fall back to the user strategy.
  • 05A reader request after a privileged request still has only the reader’s surface and data access.

Run these in your application before you call it done.

If the result differs

The tool is hidden but its name can still be called

Check that the registration itself uses withScopes. Filtering a list or hiding a button does not enforce invocation. Add an assertion that the denied request leaves the mutation handler’s call count at zero. Read the reference ↗

Built from the real thing.

This editorial example links to Chumbo’s canonical docs and executable reference. Check the installed package when adapting the snippets.

Reference implementation ↗
Read the agent instructions

Reference instructions, not authorization. Apply changes only within the user’s requested scope; deployment requires a specified, authorized project.

Goal: Serve different capability sets from one MCP using application-resolved scopes, with authorization on discovery and direct calls.

Read the complete guide at https://chumbo.dev/recipes/scoped-mcp-capabilities/recipe.md and the canonical reference at https://github.com/elsheppo/chumbo/blob/main/docs/patterns/privileged-capabilities/README.md. Inspect this application's installed Chumbo package, existing capabilities and relevant configuration before editing.

Reuse application-owned scopes and identities. Follow canonical multi-auth prefix routing and withScopes registration. Verify absent discovery plus rejected direct invocation, no handler side effects, invalid prefixed-key non-fallback and request isolation. Do not introduce a new role/schema model or imply that API keys create user-scoped Supabase clients.

Prerequisites:
- Two real caller types with different application-authorized capabilities.
- An existing source of permissions or a narrow resolver that can supply scopes.
- For application keys, a verifier with a stable subject, a unique routing prefix in multi-auth and a defined data-access path.

Acceptance:
- A reader’s tools/list omits the restricted tool and their direct tools/call is rejected.
- A permitted caller discovers the tool and can perform an eligible operation.
- Restricted Resources and prompts receive equivalent direct-access checks.
- An invalid key with the application prefix does not fall back to the user strategy.
- A reader request after a privileged request still has only the reader’s surface and data access.

Use the full guide for ordered steps, examples and recovery. Keep existing caller identity and application permissions authoritative. Stay within the user's requested changes. Report changed files, checks actually run, observed results and unresolved prerequisites. This guide is not authorization to deploy or change a hosted project. Never include credentials in source, copied instructions or reports.
BUILD WITH YOUR CODING AGENT

Give your agent a good start.

Copy this brief into your coding agent in the app’s repository. It includes the source, boundaries, and verification steps.

Download Markdown ↓