ExMCP.Authorization.ScopeValidator (ex_mcp v0.9.2)

View Source

Maps MCP operations to required OAuth scopes and validates them.

This module provides a centralized way to manage scope requirements for different MCP methods. It defines a standard set of scopes following the mcp:<domain>:<action> convention and offers functions to retrieve required scopes for a method and validate them against the scopes granted in a token.

Standard Scopes

The following standard scopes are defined. More specific scopes can be satisfied by granting a less specific, "wildcard" scope. For example, a token with the mcp:tools:execute scope can access any tool, satisfying the more specific mcp:tools:execute:<tool_name> requirement.

Tools

  • mcp:tools:list: Allows listing available tools (tools/list).
  • mcp:tools:get: Allows retrieving the definition of a specific tool (tools/get).
  • mcp:tools:execute: Allows executing any tool. This is a wildcard scope.
  • mcp:tools:execute:<tool_name>: Allows executing a specific tool (tools/execute).

Resources

  • mcp:resources:list: Allows listing available resources (resources/list).
  • mcp:resources:get: Allows retrieving any specific resource (resources/get).
  • mcp:resources:create: Allows creating any new resource (resources/create).
  • mcp:resources:update: Allows updating any existing resource (resources/update).
  • mcp:resources:delete: Allows deleting any resource (resources/delete).
  • More granular resource scopes (e.g., mcp:resources:get:<resource_id>) can be implemented with a custom mapper.

Prompts

  • mcp:prompts:execute: Allows executing a prompt (prompts/execute).

Usage

To get the required scopes for an MCP request:

request = %{"method" => "tools/list"}
ExMCP.Authorization.ScopeValidator.get_required_scopes(request)
#=> ["mcp:tools:list"]

execute_request = %{
  "method" => "tools/execute",
  "params" => %{"tool_name" => "my_calculator"}
}
ExMCP.Authorization.ScopeValidator.get_required_scopes(execute_request)
#=> ["mcp:tools:execute:my_calculator"]

To validate scopes from a token:

token_scopes = ["mcp:tools:execute"]
required_scopes = ["mcp:tools:execute:my_calculator"]

ExMCP.Authorization.ScopeValidator.validate(token_scopes, required_scopes)
#=> :ok

token_scopes = ["mcp:tools:list", "mcp:tools:get"]
required_scopes = ["mcp:tools:execute:my_calculator"]
ExMCP.Authorization.ScopeValidator.validate(token_scopes, required_scopes)
#=> {:error, :insufficient_scope}

Extensibility

The scope mapping can be extended by passing a custom mapping function to get_required_scopes/2. This function receives the request map and should return a list of required scopes. If it returns nil, the standard mapping is used as a fallback.

my_mapper = fn
  %{"method" => "custom/op"} -> ["my:custom:scope"]
  %{"method" => "resources/get", "params" => %{"id" => resource_id}} ->
    ["mcp:resources:get:" <> resource_id]
  _ ->
    nil # Fallback to default for other methods
end

ExMCP.Authorization.ScopeValidator.get_required_scopes(request, my_mapper)

Summary

Functions

Returns a list of all standard, statically-defined MCP scopes.

Retrieves the list of required OAuth scopes for a given MCP request.

Validates that a set of token scopes satisfies a set of required scopes.

Types

custom_mapper()

@type custom_mapper() :: (request() -> required_scopes() | nil)

request()

@type request() :: map()

required_scopes()

@type required_scopes() :: [String.t()]

token_scopes()

@type token_scopes() :: [String.t()]

Functions

get_all_static_scopes()

@spec get_all_static_scopes() :: [String.t()]

Returns a list of all standard, statically-defined MCP scopes.

This is useful for advertising supported scopes, for example in a .well-known/oauth-protected-resource endpoint. Note that this list does not include dynamically generated scopes like mcp:tools:execute:<tool_name>.

get_required_scopes(request, custom_mapper \\ fn _ -> nil end)

@spec get_required_scopes(request(), custom_mapper()) :: required_scopes()

Retrieves the list of required OAuth scopes for a given MCP request.

This function inspects the method and params of the request to determine the necessary scopes. It can be extended with a custom mapping function.

validate(token_scopes, required_scopes)

@spec validate(token_scopes(), required_scopes()) ::
  :ok | {:error, :insufficient_scope}

Validates that a set of token scopes satisfies a set of required scopes.

This function checks if for every required scope, either the scope itself or a more general "wildcard" scope exists in the token scopes.

For example, if required_scopes is ["mcp:tools:execute:calculator"], this function will return :ok if token_scopes contains either "mcp:tools:execute:calculator" or "mcp:tools:execute".