MCPKit.Policy is evaluated on every request.
This makes it suitable for enterprise access control where capabilities may vary by user, account, tier, workspace, or request arguments.
Decisions
Policies return one of:
:allow{:deny, :not_found}{:deny, :forbidden}
Visibility vs direct access
Lists and completions are filtered item-by-item.
Direct access is enforced separately for:
tools/callprompts/getresources/readcompletion/complete
Example
defmodule MyApp.MCP.Policy do
@behaviour MCPKit.Policy
@impl true
def authorize({:tools, :call, "premium_report"}, %{auth_context: %{tier: :free}}) do
{:deny, :not_found}
end
def authorize({:completion, {:prompt_argument, "draft", "workspace_id"}}, %{auth_context: %{role: :viewer}}) do
{:deny, :forbidden}
end
def authorize(_action, _context), do: :allow
endRecommended practice
- use
{:deny, :not_found}when you want to hide feature existence - use
{:deny, :forbidden}when explicit denial is more appropriate - rely on request arguments in the context for fine-grained rules
- keep router declarations as the superset and let policy narrow what is visible