Skip to main content

Add the Access Request MCP server to your agent

Install, configure, and run the Permit.io Access Request MCP server, then connect the server to an MCP client or add its tools to your own Model Context Protocol (MCP) server. This page is for AI agent builders who already have an agent and want the agent to request access and approvals that a human reviews.

To learn what the server does and when to use it, see the Access Request MCP overview. For a complete agent built step by step, see the food ordering demo.

Choose how to run the server

ModeWho uses the toolsWhere the server runs
LocalA reviewer with Permit credentials lists, approves, and denies requests from an AI assistant such as Claude Desktop.On the reviewer's machine, as a subprocess of the MCP client. Credentials come from a .env file.
HostedEnd users send access requests and approval requests from inside your AI application.In your backend, next to the LLM (large language model) and the agent framework, such as LangGraph or LangChain.

Both modes use the same server code and the same environment variables.

Prerequisites

The food ordering demo creates an example resource and all three elements.

Install the server

The editable install adds the server's dependencies from its pyproject.toml: mcp, permit (the Permit Python SDK), httpx, python-dotenv, and aiosqlite.

1

Clone the Access Request MCP server repository
Clone the repository from GitHub and change to the project directory:

git clone https://github.com/permitio/permit-mcp
cd permit-mcp
2

Create and activate a virtual environment
Create the environment with uv, then activate the environment for your operating system:

uv venv
source .venv/bin/activate # For Windows: .venv\Scripts\activate
Install uv first

If the uv command isn't found, install uv with the uv installation instructions.

3

Install the project
Install the project and its dependencies in editable mode:

uv pip install -e .

Configure the environment variables

The server reads its Permit connection and element IDs from a .env file in the project root.

Create the .env file

Copy the example file, then set the variables in the table:

cp .env.example .env

Environment variable reference

VariableDescription
PERMIT_API_KEYYour environment API key. See Get your API key.
PERMIT_PDP_URLThe URL of the policy decision point (PDP) the server sends permission checks to. Defaults to the Cloud PDP, https://cloudpdp.api.permit.io.
TENANTThe tenant key of the users and resource instances. Defaults to default.
PROJECT_IDYour Permit project ID or key. See Get project ID or key.
ENV_IDYour Permit environment ID or key. See Get environment ID or key.
RESOURCE_KEYThe key of the resource type the requests are for, for example restaurants.
ACCESS_ELEMENTS_CONFIG_IDThe config ID of the User Management element that stores access requests. The ID appears in the element's embed code after you click Get Code.
OPERATION_ELEMENTS_CONFIG_IDThe config ID of the Approval Management element that stores operation approvals. The ID appears in the element's embed code after you click Get Code.

Example .env file

In this example, the PDP runs on your machine, the resource is restaurants, and the element config IDs are restaurant-requests and dish-requests:

PERMIT_API_KEY=pk_123abc456
PERMIT_PDP_URL=http://localhost:7766
TENANT=default
PROJECT_ID=proj_abc123
ENV_ID=env_prod
RESOURCE_KEY=restaurants
ACCESS_ELEMENTS_CONFIG_ID=restaurant-requests
OPERATION_ELEMENTS_CONFIG_ID=dish-requests
Keep the .env file out of version control

The .env file holds your environment API key. Anyone with that key can change the environment's policy through the Permit API. Don't commit the file.

Choose a PDP for your policy model

The Cloud PDP evaluates role-based access control (RBAC) and relationship-based access control (ReBAC) policies. If your policy uses attribute-based access control (ABAC), the Cloud PDP can't evaluate the policy: run an Edge PDP container and point PERMIT_PDP_URL at the container:

PERMIT_PDP_URL=http://localhost:7766

To start the container on port 7766, see Run the PDP. For the policy models each PDP supports, see Cloud PDP capabilities.

Sync users with their names

The list tools return the requesting user's Permit profile with each request. Sync users with a first_name, so a reviewer sees who sent a request instead of a bare user key:

await permit.api.sync_user({
"key": user_id,
"first_name": firstname
})

Run the server

Run the server locally from an MCP client for development and review, or deploy the server with your AI application.

Connect the server to Claude Desktop

Claude Desktop starts the server as a subprocess and sends tool calls to the server over standard input and output (stdio). Add this entry to the mcpServers object in your Claude Desktop configuration file:

{
"mcpServers": {
"permit": {
"command": "uv",
"args": [
"--directory",
"/ABSOLUTE/PATH/TO/PARENT/FOLDER/src/permit_mcp",
"run",
"server.py"
]
}
}
}

Replace /ABSOLUTE/PATH/TO/PARENT/FOLDER with the absolute path of your cloned permit-mcp directory. Restart Claude Desktop. The permit server's tools, such as list_access_requests, appear in Claude Desktop's tool list.

Run the server from the command line

To check your configuration without an MCP client, change to the src/permit_mcp directory of the cloned repository and run:

uv run server.py

The server logs Starting Permit MCP server... and waits for MCP messages on standard input. The server loads the .env file from the project root. Press Ctrl+C to stop the server.

Deploy the server with your application

In a hosted deployment, your backend runs the MCP server next to the LLM application:

  • The MCP server holds the Permit API key and calls the Permit API and the PDP.
  • The agent, for example a LangGraph or LangChain agent, calls the MCP tools when a user asks for access or approval.
  • Reviewers approve or deny requests in the Permit Elements embedded in your application, through the Permit API, or through the approve and deny tools.

Authenticate your end users in the backend, and pass the authenticated user's key to the tools. The tools act as whichever user_id the agent passes, so an agent that takes the user ID from the prompt lets a user act as another user.

For a hosted example with a FastAPI backend, a WebSocket chat endpoint, and tools filtered by user role, see Serve the agent with FastAPI and a CLI.

Add the tools to your own MCP server

Import the PermitServer class into your own FastMCP server to use the access request tools next to your domain tools. You can:

  • Add domain tools, for example to list, update, or delete your application's entities.
  • Check permissions with permit.check() inside your tools, and send an access request when a check denies.
  • Remove Permit tools that a group of users must not call.

Register the built-in tools

PermitServer registers the Permit tools on the FastMCP instance you pass to it:

from mcp.server.fastmcp import FastMCP
from src.permit_mcp.server import PermitServer

mcp = FastMCP("custom_server_name")
permit_server = PermitServer(mcp)

The mcp instance exposes every tool in the tool reference. The src.permit_mcp.server import path works when you run your server from the root of the cloned repository.

Exclude tools

Pass tool names in exclude_tools to keep them off your server:

permit_server = PermitServer(
mcp,
exclude_tools=['create_access_request', 'create_operation_approval']
)

Your agent can call every other Permit tool. The excluded tools aren't registered.

Add custom tools

Register your own tools on the same instance with the @mcp.tool() decorator:

from typing import List

@mcp.tool()
async def list_dishes(user_id: str, restaurant_id: str) -> List[str]:
# Custom logic to fetch dish data from a database
...

The food ordering demo builds a complete list_dishes tool and an order_dish tool that check permissions with the Permit SDK. See Add food ordering tools to the MCP server.

Tool reference

PermitServer registers these tools. Any MCP client or agent framework that supports MCP tools can call them, for example Claude Desktop, or LangGraph through langchain-mcp-adapters. Each tool calls the Permit API with the PROJECT_ID, ENV_ID, TENANT, and RESOURCE_KEY from the .env file. A failed API call returns a tool error with the status code and response body.

Access request tools

ToolWhat it doesParameters
create_access_requestRequests a role for a user, through the User Management element set in ACCESS_ELEMENTS_CONFIG_ID.user_id (required): the user who requests access. role (required): the role key or ID requested. reason (required): why the user needs access. resource_instance (optional): the resource instance key or ID. Required for ReBAC policies.
list_access_requestsLists access requests that the user can see, with the requesting user's profile.user_id (required): the user who views the list. status (optional): pending, approved, denied, or canceled. role (optional). resource_instance (optional). page (default 1). per_page (default 30, maximum 100).
approve_access_requestApproves an access request. Permit assigns the requested role.user_id (required): the reviewer. access_request_id (required): from list_access_requests. reviewer_comment (optional).
deny_access_requestDenies an access request.user_id (required): the reviewer. access_request_id (required). reviewer_comment (optional).

Operation approval tools

ToolWhat it doesParameters
create_operation_approvalRequests one-time approval of an operation on a resource instance, through the element set in OPERATION_ELEMENTS_CONFIG_ID.user_id (required): the user who requests approval. reason (required). resource_instance (optional): the resource instance key or ID. Required for ReBAC policies.
list_operation_approvalsLists operation approval requests, with the requesting user's profile.user_id (required): the user who views the list. status (optional). resource_instance (optional). page (default 1). per_page (default 30, maximum 100).
approve_operation_approvalApproves an operation approval request. Permit assigns the _Approved_ role on the resource instance.user_id (required): the reviewer. operation_approval_id (required): from list_operation_approvals. reviewer_comment (optional).
deny_operation_approvalDenies an operation approval request.user_id (required): the reviewer. operation_approval_id (required). reviewer_comment (optional).

Resource tools

ToolWhat it doesParameters
list_resource_instancesLists the instances of RESOURCE_KEY in TENANT, with each instance's ID and key. Agents call this tool first to get the resource_instance value for the other tools.page (default 1). per_page (default 100, maximum 100).

Connect the tools to an agent

The food ordering demo shows two ways to connect the tools to an agent:

Best practices

Use named users

Sync each user with a first_name, as in Sync users with their names. A reviewer who sees only user keys can't tell who sent a request, and audit trails are harder to read.

await permit.api.sync_user({
"key": user_id,
"first_name": firstname
})

Give resource instances readable attributes

With relationship-based access control (ReBAC), the agent and the reviewer identify resource instances by key:

  • Give each resource instance, such as a restaurant, a unique key that doesn't change.
  • When the key isn't the instance's name, store the name and other facts the reviewer needs, such as allowed_for_children, as attributes.
await permit.api.resource_instances.create({
"resource": "restaurants",
"key": restaurant_id,
"tenant": TENANT,
"attributes": {
"name": restaurant_name,
"allowed_for_children": bool(allowed_for_children)
}
})

Enforce permissions in policy, not in code

Don't hardcode who can do what in your tools. Call await permit.check() in each tool, and define the rules in your Permit policy. You change a rule in the policy without redeploying the agent, and each decision appears in the Permit audit log.

Log tool calls

Log each tool call your agent makes, especially access requests and approvals, in your application's structured logs. Permit records permission checks in the audit log. Your own logs record the prompt and the tool arguments that led to each check.

Support asynchronous approvals

A reviewer doesn't always answer right away. Let reviewers approve in an embedded Approval Management element or User Management element, and have the agent tell the user that the request is pending instead of waiting.

Test with several roles and scenarios

Test that:

  • Users without permission can't perform sensitive actions.
  • Only users with the reviewer role can approve requests.
  • The agent handles a denied request and a failed tool call.

To generate permission-check test cases from your policy, use permit test generate e2e in the Permit CLI. See Policy testing commands in the Permit CLI.

Pause the agent for approval with LangGraph

LangGraph's interrupt() function pauses a graph before a tool runs and resumes the graph with a human decision. Use interrupt() before high-risk tools, such as approve_access_request. See Add human review with interrupt().

Next steps