Skip to main content

Authorization policy reference

This page is the complete reference for everything you can use when writing authorization policies for ToolHive MCP servers. It covers Cedar entity types, actions, resource attributes, tool annotations, group membership, and the HTTP PDP model for external policy decision points.

For conceptual guidance and practical examples, see Cedar policies.

Cedar entity types

Every Cedar authorization request involves three entity types: a principal, an action, and a resource. ToolHive maps MCP concepts to these Cedar entities automatically.

Entity typeFormatDescription
ClientClient::"<sub_claim>"The authenticated user, identified by the sub claim from the access token
ActionAction::"<action_id>"The MCP operation being performed
ToolTool::"<tool_name>"A tool resource (used for tools/call)
PromptPrompt::"<prompt_name>"A prompt resource (used for prompts/get)
ResourceResource::"<sanitized_uri>"A data resource (used for resources/read). The URI is sanitized for Cedar compatibility
FeatureTypeFeatureType::"<feature>"A feature category (used for list operations). Values: tool, prompt, resource
THVGroupTHVGroup::"<group_name>"A group membership entity. Used with Cedar's in operator for group-based policies

Cedar actions

ToolHive maps MCP methods to Cedar actions. Each action corresponds to a specific MCP operation.

Actions that require authorization

These actions are evaluated against your Cedar policies:

ActionMCP methodDescription
Action::"call_tool"tools/callCall a specific tool
Action::"get_prompt"prompts/getRetrieve a specific prompt
Action::"read_resource"resources/readRead a specific data resource
Action::"list_tools"tools/listList available tools (response is filtered)
Action::"list_prompts"prompts/listList available prompts (response is filtered)
Action::"list_resources"resources/listList available resources (response is filtered)

Always-allowed MCP methods

These MCP methods bypass authorization entirely. You cannot write policies to restrict them:

MCP methodPurpose
initializeProtocol initialization handshake
pingHealth check
features/listCapability discovery
roots/listRoot directory discovery
logging/setLevelClient logging preference
completion/completeArgument auto-completion
notifications/*All server-to-client notifications

Denied-by-default MCP methods

These MCP methods are not in the authorization map and are always denied. They require new authorization features before they can be enabled:

  • elicitation/create -- User input prompting
  • sampling/createMessage -- LLM text generation
  • tasks/list, tasks/get, tasks/cancel, tasks/result -- Task management

Principal attributes

The principal entity (Client::) receives all JWT claims from the access token with a claim_ prefix. Any claim in the token becomes an attribute you can reference in policies.

Common principal attributes

AttributeSourceCedar typeDescription
claim_subJWT subStringSubject identifier (also used as the entity ID)
claim_nameJWT nameStringDisplay name
claim_emailJWT emailStringEmail address
claim_rolesJWT rolesSet of StringsRole memberships
claim_groupsJWT groupsSet of StringsGroup memberships
claim_roleJWT roleStringSingle role (some identity providers use this instead of roles)
claim_<key>JWT <key>VariesAny other JWT claim
tip

The exact attributes available depend on your identity provider and token configuration. Check your access token's claims to see what's available. Every claim becomes a claim_-prefixed attribute automatically.

Claim type mapping

JWT claim typeCedar type
StringString
BooleanBool
IntegerLong
FloatDecimal
Array of stringsSet of String values
Array of mixed typesSet (each element converted individually)

Resource attributes

Resource attributes vary depending on the type of MCP operation. Each operation type provides a different set of attributes on the resource entity.

Tool call attributes (tools/call)

When a client calls a tool, the resource entity (Tool::) has these attributes:

AttributeTypeDescription
nameStringThe tool name
operationStringAlways "call"
featureStringAlways "tool"
readOnlyHintBoolFrom tool annotations, if the MCP server sets it
destructiveHintBoolFrom tool annotations, if set
idempotentHintBoolFrom tool annotations, if set
openWorldHintBoolFrom tool annotations, if set
arg_<key>VariesTool argument values (see argument preprocessing)

Prompt get attributes (prompts/get)

When a client retrieves a prompt, the resource entity (Prompt::) has these attributes:

AttributeTypeDescription
nameStringThe prompt name
operationStringAlways "get"
featureStringAlways "prompt"
arg_<key>VariesPrompt argument values

Resource read attributes (resources/read)

When a client reads a data resource, the resource entity (Resource::) has these attributes:

AttributeTypeDescription
nameStringThe sanitized URI (same as the entity ID)
uriStringThe original, unsanitized resource URI
operationStringAlways "read"
featureStringAlways "resource"
arg_<key>VariesRequest argument values

Feature list attributes (list operations)

When a client lists tools, prompts, or resources, the resource entity (FeatureType::) has these attributes:

AttributeTypeDescription
nameStringThe feature type (same as the entity ID)
typeStringThe feature type: "tool", "prompt", or "resource"
operationStringAlways "list"
featureStringThe feature type

Tool annotation attributes

MCP servers can declare behavioral hints on their tools through annotations. ToolHive caches these annotations from tools/list responses and makes them available as resource attributes during tools/call authorization.

AttributeTypeMeaning when trueMeaning when false
readOnlyHintBoolThe tool only reads data; it does not modify anythingThe tool may modify data
destructiveHintBoolThe tool may perform destructive or irreversible operationsThe tool's modifications are non-destructive or reversible
idempotentHintBoolCalling the tool multiple times with the same arguments produces the same resultRepeated calls may have different effects
openWorldHintBoolThe tool interacts with external systems outside the MCP server's controlThe tool operates only within a closed, controlled environment
Annotations may be absent

Not all MCP servers set all annotation fields. An annotation attribute is only present on the resource entity when the MCP server explicitly sets it. If a tool omits an annotation, that attribute does not exist on the entity.

Always use Cedar's has operator to check for the presence of an annotation before accessing its value. Without has, accessing a missing attribute causes a Cedar evaluation error, which ToolHive treats as a deny.

The has operator

The has operator is essential for writing safe annotation-based policies. It checks whether an attribute exists on an entity before you try to read it:

// Safe: checks existence before access
permit(
principal,
action == Action::"call_tool",
resource
) when {
resource has readOnlyHint && resource.readOnlyHint == true
};
// Unsafe: fails with an evaluation error if readOnlyHint is absent
permit(
principal,
action == Action::"call_tool",
resource
) when {
resource.readOnlyHint == true
};

Trust boundary

Annotations are sourced exclusively from the MCP server's tools/list response, not from the client's tools/call request. This prevents a malicious client from setting readOnlyHint: true on a destructive tool to bypass annotation-based policies.

Context attributes

The Cedar context record contains a merged copy of all JWT claims and tool arguments. This gives you an alternative way to reference these values in policies. Context attributes use the same prefixes as entity attributes:

PrefixSourceExample
claim_<key>JWT claimscontext.claim_email == "admin@example.com"
arg_<key>Tool/prompt argumentscontext.arg_location == "New York"

You can use either entity attributes or context attributes in your policies. Both contain the same values:

// These are equivalent:
principal.claim_roles.contains("admin")
context.claim_roles.contains("admin")

// These are also equivalent:
resource.arg_location == "New York"
context.arg_location == "New York"

Group membership

ToolHive automatically extracts group claims from JWT tokens and creates THVGroup parent entities for the principal. This lets you write group-based policies using Cedar's in operator.

How groups are resolved

ToolHive checks the following JWT claim names in order and uses the first one found:

  1. Custom claim name (if configured via group_claim_name in Cedar config)
  2. groups -- Microsoft Entra ID, Okta, Auth0, PingIdentity
  3. roles -- Keycloak
  4. cognito:groups -- AWS Cognito

The claim value must be an array of strings. Each string becomes a THVGroup entity, and the principal is added as a child of each group.

Group policy examples

// Allow members of the "engineering" group to call any tool
permit(
principal in THVGroup::"engineering",
action == Action::"call_tool",
resource
);
// Allow only the "platform" group to read infrastructure resources
permit(
principal in THVGroup::"platform",
action == Action::"read_resource",
resource
);

Configuring a custom group claim

If your identity provider uses a non-standard claim name for groups (for example, Auth0 namespaced claims), configure it in the Cedar authorization config:

authz-config.yaml
version: '1.0'
type: cedarv1
cedar:
group_claim_name: 'https://example.com/groups'
policies:
- 'permit(principal in THVGroup::"admins", action, resource);'
entities_json: '[]'

Argument preprocessing

Tool and prompt arguments are converted to Cedar-compatible types with an arg_ prefix. The conversion rules depend on the argument's Go type:

Argument typeCedar attributeCedar typeExample
Stringarg_<key>Stringresource.arg_location == "NYC"
Booleanarg_<key>Boolresource.arg_verbose == true
Integerarg_<key>Longresource.arg_limit == 10
Floatarg_<key>Decimalresource.arg_threshold == 0.95
Complex (object, array)arg_<key>_presentBool (always true)resource.arg_config_present == true

Complex argument types (objects, nested arrays) cannot be represented directly in Cedar. Instead, ToolHive creates a boolean arg_<key>_present attribute set to true, which lets you check whether the argument was provided without inspecting its value.

Resource URI sanitization

For resources/read operations, the resource URI is sanitized to create a valid Cedar entity ID. The following characters are replaced with underscores (_):

CharacterReplaced by
:_
/_
\_
?_
&_
=_
#_
(space)_
._

For example, the URI file:///data/config.json becomes the entity ID Resource::"file____data_config_json".

To write policies against resource URIs, use the unsanitized uri attribute instead of matching the entity ID directly:

// Use the uri attribute for readable policies
permit(
principal,
action == Action::"read_resource",
resource
) when {
resource.uri == "file:///data/config.json"
};

List operation filtering

List operations (tools/list, prompts/list, resources/list) work differently from other operations. ToolHive always allows the list request itself, but filters the response to include only items the caller is authorized to access.

For each item in the list response, ToolHive runs a policy check using the corresponding action:

List methodPer-item check uses
tools/listAction::"call_tool" against each Tool::"<name>"
prompts/listAction::"get_prompt" against each Prompt::"<name>"
resources/listAction::"read_resource" against each Resource::"<uri>"

This means you don't need separate list policies. Your call_tool, get_prompt, and read_resource policies automatically control what appears in list responses.

note

If you want explicit control over list operations (for example, to allow listing but deny individual access), you can write policies using the list_tools, list_prompts, or list_resources actions against FeatureType:: entities.

Custom static entities

You can define custom entities with arbitrary attributes using the entities_json field in your Cedar configuration. These entities are merged with the dynamically created entities at evaluation time. This lets you attach metadata to tools, prompts, or resources that you can reference in policies.

authz-config.yaml
version: '1.0'
type: cedarv1
cedar:
policies:
- |
permit(
principal,
action == Action::"call_tool",
resource
) when {
resource.owner == principal.claim_sub
};
entities_json: |
[
{
"uid": "Tool::weather",
"attrs": {
"owner": "user123",
"department": "engineering"
}
},
{
"uid": "Tool::billing",
"attrs": {
"owner": "finance-bot",
"department": "finance"
}
}
]
warning

Static entity attributes are merged with dynamic attributes at evaluation time. The dynamic attributes (name, operation, feature, and any arg_ or annotation attributes) always take precedence over static ones. Don't define static attributes using reserved names.

HTTP PDP PORC mapping

The HTTP PDP authorizer (httpv1) maps MCP requests to a PORC (Principal-Operation-Resource-Context) model for external policy decision points.

PORC fields

FieldFormatExample
principal.subJWT sub claim"user@example.com"
principal.roles or principal.mrolesDepends on claim mapper["developer"]
principal.groups or principal.mgroupsDepends on claim mapper["engineering"]
principal.scopesJWT scope or scopes["read", "write"]
operationmcp:<feature>:<operation>"mcp:tool:call"
resourcemrn:mcp:<server>:<feature>:<id>"mrn:mcp:myserver:tool:weather"

PORC context fields

Context fields are optional and controlled by the context configuration:

FieldConfig requiredDescription
context.mcp.featureinclude_operation: trueMCP feature type
context.mcp.operationinclude_operation: trueMCP operation type
context.mcp.resource_idinclude_operation: trueResource identifier
context.mcp.args.<key>include_args: trueTool/prompt arguments
context.mcp.annotations.readOnlyHintAutomatic for tool operationsTool annotation hint
context.mcp.annotations.destructiveHintAutomatic for tool operationsTool annotation hint
context.mcp.annotations.idempotentHintAutomatic for tool operationsTool annotation hint
context.mcp.annotations.openWorldHintAutomatic for tool operationsTool annotation hint

HTTP PDP claim mappers

MapperConfig valuePrincipal fieldsCompatible with
MPEclaim_mapping: "mpe"sub, mroles, mgroups, scopes, mclearance, mannotationsManetu PolicyEngine
Standard OIDCclaim_mapping: "standard"sub, roles, groups, scopesGeneric PDPs expecting standard OIDC claims

Next steps