> ## Documentation Index
> Fetch the complete documentation index at: https://auth0.com/ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Control Access to MCP Tools with FGA

> Learn how to implement fine-grained authorization for your MCP server tools using Auth0 FGA with roles, groups, and temporal access.

export const DownloadQuickstartButton = ({category, framework, label = "Download Sample"}) => {
  const [isDownloading, setIsDownloading] = useState(false);
  const githubRepo = 'auth0-samples/auth0-ai-samples';
  const filename = `${category}-${framework}-sample.zip`;
  const downloadSample = () => {
    setIsDownloading(true);
    const downloadUrl = `https://github.com/${githubRepo}/releases/latest/download/${filename}`;
    window.open(downloadUrl, '_blank');
    setTimeout(() => setIsDownloading(false), 1000);
  };
  return <div className="download-button-container">
      <button onClick={downloadSample} disabled={isDownloading} className={`download-button ${isDownloading ? 'downloading' : ''}`} aria-label={`Download ${category} ${framework} sample code`}>
        {isDownloading ? <>
            <svg className="download-spinner" width="16" height="16" viewBox="0 0 24 24" fill="none">
              <circle cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="2" fill="none" strokeDasharray="31.416" strokeDashoffset="31.416">
                <animate attributeName="stroke-dasharray" dur="2s" values="0 31.416;15.708 15.708;0 31.416" repeatCount="indefinite" />
                <animate attributeName="stroke-dashoffset" dur="2s" values="0;-15.708;-31.416" repeatCount="indefinite" />
              </circle>
            </svg>
            Downloading...
          </> : <>
            <Icon icon="download" color="white" />
            {label}
          </>}
      </button>
    </div>;
};

Fine-grained authorization goes beyond simple scope-based access control by enabling sophisticated permission models based on relationships between users, roles, groups, and resources. This quickstart demonstrates how to use [Auth0 FGA](https://auth0.com/fine-grained-authorization) to implement [Relationship-Based Access Control (ReBAC)](https://docs.fga.dev/authorization-concepts#what-is-relationship-based-access-control) for your MCP server tools.

The sample application showcases five authorization patterns:

1. **Public Tools** - Accessible to all authenticated users without additional permissions
2. **Role-Based Access** - Tools assigned to specific roles (`admin`, `content_editor`)
3. **Group Membership** - Users inherit permissions through group membership (managers, marketing)
4. **Temporal Access** - Time-limited tool access with automatic expiration
5. **Resource-Specific Permissions** - Fine-grained control over tool features (e.g., viewing private documents)

By the end of this quickstart, you will have an MCP server that:

* Authenticates users with Auth0 OAuth 2.0
* Uses Auth0 FGA to determine which tools each user can access
* Dynamically filters tool lists based on user permissions
* Supports time-limited access grants that automatically expire
* Provides different data based on user roles (public vs private documents)

## Prerequisites

<AccordionGroup>
  <Accordion title="1. Create or log in to your Auth0 account">
    To continue with this quickstart, you need to have an [Auth0 account](https://auth0.com/signup).
  </Accordion>

  <Accordion title="2. Enable the Resource Parameter Compatibility Profile and Include Issuer in Authorization Responses">
    To use the `resource` parameter in your access tokens and the `iss` claim to identify your authorization server, you need to enable two toggles in the [Auth0 Dashboard](https://manage.auth0.com/dashboard/):

    1. Navigate to **Settings** on the left sidebar.
    2. Select [**Advanced**](https://manage.auth0.com/dashboard/#/tenant/advanced) in the top right corner.
    3. Scroll down to the **Settings** section and enable the following toggles:
       * **Resource Parameter Compatibility Profile**: Enables support for the `resource` parameter in authorization requests, allowing access tokens to be scoped to a specific API.
       * **Include Issuer in Authorization Responses**: Includes the `iss` parameter identifying the authorization server in authorization responses, helping clients defend against mix-up attacks.
  </Accordion>

  <Accordion title="3. Install the Auth0 CLI">
    This guide uses [Auth0 CLI](https://auth0.github.io/auth0-cli/) to configure an Auth0 tenant for secure MCP tool access.

    1. Follow the [Auth0 CLI installation instructions](https://auth0.github.io/auth0-cli/).
    2. Log in to your account with the Auth0 CLI:

    ```shell wrap lines theme={null}
    auth0 login --scopes "read:client_grants,create:client_grants,delete:client_grants,read:clients,create:clients,update:clients,read:resource_servers,create:resource_servers,update:resource_servers,read:roles,create:roles,update:roles,update:tenant_settings,read:connections,update:connections"
    ```

    Confirm you are in the correct tenant by double-checking the tenant domain in the terminal, or run the command below:

    ```shell theme={null}
    auth0 tenants list
    ```

    If more than one tenant is configured, the default tenant will be indicated by an arrow.
  </Accordion>

  <Accordion title="4. Install jq">
    To simplify the process of interacting with the Auth0 CLI, we recommend installing [jq](https://jqlang.org/download/). This will allow you to easily parse JSON responses from the CLI.

    <Tabs>
      <Tab title="macOS">
        <CodeBlock language="shell">brew install jq</CodeBlock>
      </Tab>

      <Tab title="Linux">
        <CodeBlock language="shell">sudo apt-get install jq</CodeBlock>
      </Tab>

      <Tab title="Windows">
        <CodeBlock language="shell">choco install jq</CodeBlock>
      </Tab>
    </Tabs>
  </Accordion>
</AccordionGroup>

Now install and setup the dependencies for FGA:

<AccordionGroup>
  <Accordion title="1. Install the FGA CLI">
    This guide uses the [OpenFGA CLI](https://github.com/openfga/cli?tab=readme-ov-file#installation) to manage authorization models and permissions.

    1. Install the FGA CLI:

    <Tabs>
      <Tab title="macOS">
        ```shell theme={null}
        brew install openfga/tap/fga
        ```
      </Tab>

      <Tab title="Linux">
        Download the latest release from the [OpenFGA CLI releases page](https://github.com/openfga/cli/releases) and add it to your PATH.

        ```shell theme={null}
        # Example for Linux (replace version with latest)
        wget https://github.com/openfga/cli/releases/download/v0.6.3/fga_0.6.3_linux_amd64.tar.gz
        tar -xzf fga_0.6.3_linux_amd64.tar.gz
        sudo mv fga /usr/local/bin/
        ```
      </Tab>

      <Tab title="Windows">
        Download the latest release from the [OpenFGA CLI releases page](https://github.com/openfga/cli/releases) and add it to your PATH.
      </Tab>
    </Tabs>

    2. Verify the installation:

    ```shell theme={null}
    fga version
    ```
  </Accordion>

  <Accordion title="2. Set up Auth0 FGA or OpenFGA">
    You'll need an Auth0 FGA or OpenFGA store to manage fine-grained authorization for your MCP tools.

    **Choose your FGA provider:**

    * **Auth0 FGA**: Fully managed service at [fga.dev](https://fga.dev). Best for production use.
    * **OpenFGA**: Self-hosted open-source option. Best for local development and testing.

    <Note>
      Both options use the same authorization model and API. You can start with OpenFGA locally and migrate to Auth0 FGA for production.
    </Note>

    The sample application includes a complete authorization model (`fga/model.fga`) and initial permission tuples (`fga/tuples.yaml`) that will be imported using the FGA CLI.
  </Accordion>
</AccordionGroup>

## Configure tenant settings

<Accordion title="1. Promote the connection to a domain-level connection">
  To allow third-party clients like MCP Inspector to use a connection such as a username-and-password database or a social connection, you need to promote the connection to a domain-level connection. [Learn more about configuring third-party applications](https://auth0.com/docs/get-started/applications/third-party-applications/configure-third-party-applications).

  <Steps>
    <Step title="List Your Connection">
      List your connections to get their IDs

      ```shell theme={null}
      auth0 api get connections
      ```
    </Step>

    <Step title="Choose Your Connections">
      From the list, identify which connections you want to use for the MCP server and copy the ID.

      * For the username-and-password database, look for the connection with the strategy `auth0`.
      * For the Google social connection, look for the connection with the strategy `google-oauth2`.
    </Step>

    <Step title="Upgrade the Connection to Domain-Level">
      For each of those specific connection IDs, run the following command to mark it as a domain-level connection. Replace `YOUR_CONNECTION_ID` with the actual ID (e.g., `con_XXXXXXXXXXXXXXXX`).

      ```shell wrap lines theme={null}
      auth0 api patch connections/YOUR_CONNECTION_ID --data '{"is_domain_connection": true}'
      ```
    </Step>
  </Steps>
</Accordion>

## Create an API to represent your MCP server

An MCP server is treated just like any other [API](https://auth0.com/docs/get-started/apis) in your Auth0 tenant, which means you can use the same access control, scoping, and authorization features. The API (also known as a Resource Server) represents your protected MCP Server.

Run the following command to [create an API](https://auth0.com/docs/get-started/auth0-overview/set-up-apis) in Auth0:

```shell wrap lines theme={null}
auth0 api post resource-servers --data '{
  "identifier": "http://localhost:3001/",
  "name": "MCP Tools API",
  "signing_alg": "RS256",
  "token_dialect": "rfc9068_profile_authz",
  "enforce_policies": true,
  "scopes": [
    {"value": "tool:whoami", "description": "Access the WhoAmI tool"},
    {"value": "tool:greet", "description": "Access the Greeting tool"}
  ]
}'
```

Note that `rfc9068_profile_authz` is used as the [token dialect](https://auth0.com/docs/get-started/apis/enable-role-based-access-control-for-apis#token-dialect-options) to include the `permissions` claim in the access token, which can be useful for the API to check user permissions without making additional calls.

For more details on protecting APIs with Auth0, check our [quickstarts](https://auth0.com/docs/quickstarts#backend%2Fapi).

<Info>
  This quickstart uses two OAuth scopes for demonstration: `tool:greet` and `tool:whoami`. The `get_datetime` tool is public and doesn't require scopes, while `get_documents` uses FGA for authorization.
</Info>

## Set up your FGA store

<Tabs>
  <Tab title="Auth0 FGA">
    <Steps>
      <Step title="Create an Auth0 FGA account">
        Sign up for a free account at [fga.dev](https://fga.dev).
      </Step>

      <Step title="Create a store">
        In the [Auth0 FGA dashboard](https://dashboard.fga.dev/), create a new store for your MCP server.
      </Step>

      <Step title="Generate API credentials">
        1. Navigate to **Settings** in your store.
        2. In the **Authorized Clients** section, click **+ Create Client**.
        3. Give your client a name and select the following permissions:
           * **Read/Write model, changes, and assertions** - To query authorization decisions
           * **Write and delete tuples** - To import tuples (user permissions)
           * **Read and query** - To import the authorization model
        4. Click **Create**.
        5. Save the credentials shown in the confirmation modal.
      </Step>

      <Step title="Configure environment variables">
        Set the following environment variables in your terminal. These will be used by the FGA CLI to connect to your store.

        ```shell wrap lines theme={null}
        export FGA_API_URL='https://api.us1.fga.dev'
        export FGA_STORE_ID='<your-store-id>'
        export FGA_API_TOKEN_ISSUER='auth.fga.dev'
        export FGA_API_AUDIENCE='https://api.us1.fga.dev/'
        export FGA_CLIENT_ID='<your-client-id>'
        export FGA_CLIENT_SECRET='<your-client-secret>'
        ```

        Replace the placeholders with the credentials from the previous step. Note that `FGA_API_URL` may vary based on your region (e.g., `api.eu1.fga.dev` for Europe).
      </Step>
    </Steps>
  </Tab>

  <Tab title="OpenFGA (Local)">
    <Steps>
      <Step title="Install OpenFGA">
        Install OpenFGA using Homebrew or Docker:

        <Tabs>
          <Tab title="Homebrew">
            ```shell theme={null}
            brew install openfga
            ```
          </Tab>

          <Tab title="Docker">
            Docker installation will be used in the next step to run the server.
          </Tab>
        </Tabs>

        For other installation methods, see the [OpenFGA releases page](https://github.com/openfga/openfga/releases).
      </Step>

      <Step title="Start OpenFGA">
        Start the OpenFGA server:

        <Tabs>
          <Tab title="Homebrew">
            ```shell theme={null}
            openfga run
            ```
          </Tab>

          <Tab title="Docker">
            ```shell theme={null}
            docker run -p 8080:8080 -p 8081:8081 -p 3000:3000 openfga/openfga run
            ```
          </Tab>
        </Tabs>

        The server will start on port 8080 by default. Leave this running in a separate terminal window.
      </Step>

      <Step title="Configure environment variables">
        Set the following environment variables in your terminal (in a different terminal from where OpenFGA is running):

        ```shell theme={null}
        export FGA_API_URL='http://localhost:8080'
        ```

        <Note>
          The `FGA_STORE_ID` will be set after importing the model in the next section.
        </Note>
      </Step>
    </Steps>
  </Tab>
</Tabs>

## Sample app

Start by downloading or cloning the sample app for this quickstart. The sample includes a FastMCP MCP server with Auth0 OAuth and Auth0 FGA integration in JavaScript/TypeScript.

<Tabs>
  <Tab title="Use sample app (recommended)">
    <DownloadQuickstartButton category="auth-for-mcp" framework="fastmcp-mcp-fga-js" />

    Once downloaded, extract the files and open the project in your preferred IDE.
  </Tab>

  <Tab title="Clone GitHub repository">
    Clone the repository and navigate to the sample app folder:

    ```shell wrap lines theme={null}
    git clone https://github.com/auth0-samples/auth0-ai-samples.git
    cd auth0-ai-samples/auth-for-mcp/fastmcp-mcp-fga-js
    ```

    Once cloned, open the project in your preferred IDE.
  </Tab>
</Tabs>

## Install packages

Install the required dependencies:

```shell theme={null}
npm install
```

## Create your environment file

From the root of your sample app directory, run the following command to create a new `.env` file with your Auth0 configuration:

```shell wrap lines expandable theme={null}
DOMAIN=$(auth0 tenants list --json | jq -r '.[] | select(.active == true) | .name') \
&& touch .env \
&& echo "AUTH0_DOMAIN=${DOMAIN}" > .env \
&& echo "AUTH0_AUDIENCE=http://localhost:3001/" >> .env \
&& echo "PORT=3001" >> .env \
&& echo "MCP_SERVER_URL=http://localhost:3001" >> .env \
&& echo "" >> .env \
&& echo ".env file created with your Auth0 configuration:" \
&& cat .env
```

Next, add your FGA configuration to the `.env` file. The variables you need depend on which FGA provider you're using:

<Tabs>
  <Tab title="Auth0 FGA">
    Add the following to your `.env` file using the credentials from your Auth0 FGA store:

    ```shell .env wrap lines theme={null}
    # Auth0 FGA Configuration
    FGA_API_URL=https://api.us1.fga.dev
    FGA_STORE_ID=YOUR_STORE_ID
    FGA_API_TOKEN_ISSUER=auth.fga.dev
    FGA_API_AUDIENCE=https://api.us1.fga.dev/
    FGA_CLIENT_ID=YOUR_CLIENT_ID
    FGA_CLIENT_SECRET=YOUR_CLIENT_SECRET
    ```

    Replace:

    * `YOUR_STORE_ID` with your store ID from the Auth0 FGA dashboard
    * `YOUR_CLIENT_ID` with your client ID
    * `YOUR_CLIENT_SECRET` with your client secret
    * Update `FGA_API_URL` if your store is in a different region (e.g., `https://api.eu1.fga.dev` for Europe)
  </Tab>

  <Tab title="OpenFGA (Local)">
    Add the following to your `.env` file:

    ```shell .env wrap lines theme={null}
    # OpenFGA Configuration
    FGA_API_URL=http://localhost:8080
    FGA_STORE_ID=YOUR_STORE_ID
    ```

    Replace `YOUR_STORE_ID` with the store ID that will be generated when you import the authorization model in the next step.

    <Note>
      If you haven't run `fga store import` yet, you can leave `FGA_STORE_ID` empty for now and add it after the import completes.
    </Note>
  </Tab>
</Tabs>

Your complete `.env` file should look like this:

<Tabs>
  <Tab title="Auth0 FGA">
    ```shell .env wrap lines theme={null}
    AUTH0_DOMAIN=YOUR_TENANT.us.auth0.com
    AUTH0_AUDIENCE=http://localhost:3001/
    PORT=3001
    MCP_SERVER_URL=http://localhost:3001

    # Auth0 FGA Configuration
    FGA_API_URL=https://api.us1.fga.dev
    FGA_STORE_ID=YOUR_STORE_ID
    FGA_API_TOKEN_ISSUER=auth.fga.dev
    FGA_API_AUDIENCE=https://api.us1.fga.dev/
    FGA_CLIENT_ID=YOUR_CLIENT_ID
    FGA_CLIENT_SECRET=YOUR_CLIENT_SECRET
    ```
  </Tab>

  <Tab title="OpenFGA (Local)">
    ```shell .env wrap lines theme={null}
    AUTH0_DOMAIN=YOUR_TENANT.us.auth0.com
    AUTH0_AUDIENCE=http://localhost:3001/
    PORT=3001
    MCP_SERVER_URL=http://localhost:3001

    # OpenFGA Configuration
    FGA_API_URL=http://localhost:8080
    FGA_STORE_ID=YOUR_STORE_ID
    ```
  </Tab>
</Tabs>

## Understanding the authorization model

The authorization model in this sample demonstrates how [Auth0 FGA](https://auth0.com/fine-grained-authorization) provides fine-grained access control using [Relationship-Based Access Control (ReBAC)](https://docs.fga.dev/authorization-concepts#what-is-relationship-based-access-control).

### Authorization Model Structure

The complete model is defined in `fga/model.fga`:

```typescript theme={null}
model
  schema 1.1

  type user

  # User groups
  type group
    relations
      define member : [user]

  # Roles can be linked to users or members of a user group
  type role
    relations
      define assignee : [user, group#member]

  type tool
    relations
      # Public access, role-based, and temporal access
      define can_use : [user:*, user, role#assignee, user with temporal_access, role#assignee with temporal_access]

      # Resource-specific permission for get_documents tool
      define can_view_private_documents : [role#assignee]

  # Temporal access condition
  condition temporal_access(grant_time: timestamp, grant_duration: duration, current_time: timestamp) {
    current_time < grant_time + grant_duration
  }
```

The model defines four authorization patterns:

<AccordionGroup>
  <Accordion title="1. Public Tools">
    Tools that are accessible to all authenticated users without requiring specific permissions.

    ```yaml theme={null}
    # In fga/tuples.yaml
    - user: user:*
      relation: can_use
      object: tool:get_datetime
    ```

    The wildcard `user:*` means any authenticated user can use the `get_datetime` tool.
  </Accordion>

  <Accordion title="2. Role-Based Access">
    Tools are assigned to specific roles, and users with those roles can access the tools.

    ```yaml theme={null}
    # In fga/tuples.yaml
    # Admin role can use all tools
    - user: role:admin#assignee
      relation: can_use
      object: tool:greet

    - user: role:admin#assignee
      relation: can_use
      object: tool:whoami

    - user: role:admin#assignee
      relation: can_use
      object: tool:get_documents
    ```

    Users assigned the `admin` role automatically gain access to these tools.
  </Accordion>

  <Accordion title="3. Group Membership">
    Users inherit permissions through group membership. Groups are assigned to roles, creating a permission chain.

    ```yaml theme={null}
    # In fga/tuples.yaml
    # Managers group is assigned the admin role
    - user: group:managers#member
      relation: assignee
      object: role:admin

    # Marketing group is assigned the content_editor role
    - user: group:marketing#member
      relation: assignee
      object: role:content_editor
    ```

    **Permission Chain**: User → Group → Role → Tool

    When you add a user to the `managers` group, they automatically inherit all permissions of the `admin` role, including access to all tools.
  </Accordion>

  <Accordion title="4. Temporal Access">
    Time-limited access to tools that automatically expires after a specified duration.

    ```typescript theme={null}
    // In fga/model.fga
    condition temporal_access(grant_time: timestamp, grant_duration: duration, current_time: timestamp) {
      current_time < grant_time + grant_duration
    }

    // Tools can be assigned with temporal conditions
    define can_use : [user:*, user, role#assignee, user with temporal_access, role#assignee with temporal_access]
    ```

    When you grant temporal access (e.g., "20 seconds to use the greet tool"), FGA evaluates the condition on each request. Access is automatically revoked when the time expires.
  </Accordion>

  <Accordion title="5. Resource-Specific Permissions">
    Fine-grained permissions for specific features or data within tools.

    ```yaml theme={null}
    # In fga/tuples.yaml
    # Admin role can view private documents
    - user: role:admin#assignee
      relation: can_view_private_documents
      object: tool:get_documents
    ```

    The `get_documents` tool checks this permission separately to determine whether to return private documents. This allows the same tool to provide different data based on user permissions.

    **Example**:

    * Admin role: Sees all documents (public + private)
    * Content editor role: Sees only public documents
  </Accordion>
</AccordionGroup>

### How Authorization Works

When a user connects to the MCP server:

1. The server authenticates the user with Auth0 (validates JWT token)
2. The server queries FGA to get all tools the user can access:
   * Checks `can_use` relation for each tool
   * Evaluates group memberships, role assignments, and temporal conditions
3. The server returns only authorized tools to the MCP client
4. When the user calls a tool like `get_documents`, the server checks `can_view_private_documents` to determine what data to return

This approach ensures that authorization decisions are centralized in FGA and can be updated dynamically without changing code.

## Import the authorization model and tuples

The sample includes a complete authorization model and initial permission tuples that define:

* Public access to `get_datetime` for all users
* Role assignments (`admin`, `content_editor`)
* Group-to-role mappings (`managers` → `admin`, `marketing` → `content_editor`)
* Tool access permissions for each role

<Steps>
  <Step title="Review the authorization model">
    The authorization model is defined in `fga/model.fga`. It includes types for users, groups, roles, and tools, plus a temporal access condition.

    The initial tuples are defined in `fga/tuples.yaml` and establish the baseline permissions.
  </Step>

  <Step title="Import the model and tuples">
    From the root of your sample app directory, run:

    ```shell wrap lines theme={null}
    fga store import --file fga/store.fga.yaml
    ```

    This command will:

    1. Create a new FGA store (if using OpenFGA locally)
    2. Upload the authorization model from `fga/model.fga`
    3. Import the initial tuples from `fga/tuples.yaml`
    4. Run tests defined in `fga/store.fga.yaml` to verify the setup

    <Note>
      If using OpenFGA, the command output will include a store ID. Copy this ID and add it to your `.env` file as `FGA_STORE_ID`. If using Auth0 FGA with existing store, the command will update your store with the new model and tuples.
    </Note>
  </Step>

  <Step title="Verify the import">
    If the import is successful, you should see output similar to:

    ```json theme={null}
    {
      "store": {
        "id": "01KB0NZZFAV9AX7SAPWY23KJAF",
        "name": "fastmcp-fga-example",
        "created_at": "2025-01-22T10:30:00Z",
        "updated_at": "2025-01-22T10:30:00Z"
      },
      "model": {
        "authorization_model_id": "01KED54TEMBDE753YBK7NT3DRZ"
      }
    }
    ```

    The tests at the end should show all checks passing, confirming that the authorization model is working correctly.
  </Step>
</Steps>

<Info>
  You can view and manage your authorization model in the [Auth0 FGA Dashboard](https://dashboard.fga.dev) if you're using Auth0 FGA. The dashboard provides a visual Model Explorer and allows you to view, add, and delete tuples in real-time.
</Info>

## Run the MCP server

Start the MCP server:

```shell theme={null}
npm run start
```

The server will start on port 3001 (or the port specified in your `.env` file). You should see console output indicating:

* The server has started successfully
* FGA client has been initialized
* The server is listening for connections at `http://localhost:3001/mcp`

```
FastMCP Auth0 Example Server started
FGA client initialized for store: 01KB0NZZFAV9AX7SAPWY23KJAF
Server listening on http://localhost:3001/mcp
```

Leave the server running for testing in the next sections.

## Manage user permissions

The sample application includes helper scripts in the `fga/` directory to make permission management easier. These scripts use the FGA CLI to add or remove authorization tuples dynamically.

### Available Scripts

<AccordionGroup>
  <Accordion title="Add User to Group">
    Grant a user all permissions associated with a group (and its assigned role).

    **Usage**:

    ```shell theme={null}
    ./fga/add-user-to-group.sh <user-email> <group-name>
    ```

    **Examples**:

    ```shell theme={null}
    # Add user to managers group (grants admin role with full access)
    ./fga/add-user-to-group.sh user@example.com managers

    # Add user to marketing group (grants content_editor role)
    ./fga/add-user-to-group.sh user@example.com marketing
    ```

    **What it does**: Creates an FGA tuple that makes the user a member of the specified group. Since groups are assigned to roles, the user automatically inherits all role permissions.

    **Available groups**:

    * `managers` - Assigned to `admin` role (all tools + private documents)
    * `marketing` - Assigned to `content_editor` role (all tools, public documents only)
  </Accordion>

  <Accordion title="Remove User from Group">
    Revoke a user's group membership and all associated permissions.

    **Usage**:

    ```shell theme={null}
    ./fga/remove-user-from-group.sh <user-email> <group-name>
    ```

    **Example**:

    ```shell theme={null}
    # Remove user from managers group
    ./fga/remove-user-from-group.sh user@example.com managers
    ```

    **What it does**: Deletes the FGA tuple that grants group membership. The user immediately loses all permissions from that group's role.
  </Accordion>

  <Accordion title="Grant Temporal Access">
    Provide time-limited access to a specific tool that automatically expires.

    **Usage**:

    ```shell theme={null}
    ./fga/add-temporal-access.sh <user-email> <tool-name> <duration>
    ```

    **Examples**:

    ```shell theme={null}
    # Grant 20-second access to the greet tool
    ./fga/add-temporal-access.sh user@example.com greet 20s

    # Grant 1-hour access to the whoami tool
    ./fga/add-temporal-access.sh user@example.com whoami 1h

    # Grant 5-minute access
    ./fga/add-temporal-access.sh user@example.com greet 5m
    ```

    **What it does**: Creates an FGA tuple with a temporal condition. FGA evaluates the condition on each request and automatically denies access when the duration expires. No cleanup is needed.

    **Supported durations**: Use standard time notation (`s` for seconds, `m` for minutes, `h` for hours).

    **Available tools**: `greet`, `whoami`, `get_documents`

    <Note>
      Public tools like `get_datetime` don't need temporal access grants since they're already accessible to all authenticated users.
    </Note>
  </Accordion>

  <Accordion title="Reset Tuples">
    Reset all tuples to the initial state defined in `fga/tuples.yaml`.

    **Usage**:

    ```shell theme={null}
    ./fga/reset-tuples.sh
    ```

    **What it does**: Deletes all existing tuples and reimports the default tuples from `fga/tuples.yaml`. Useful for returning to a clean state during testing.

    <Warning>
      This script removes all user assignments, including any custom permissions you've added. Use with caution.
    </Warning>
  </Accordion>
</AccordionGroup>

### Testing Permission Changes

After running any of these scripts, you need to reload the tools:

* for MCP inspector: click 'Clear' in the MCP inspector tools panel and then 'List Tools'
* for other MCP clients: you might need to reconnect the MCP Client to see the updated tool list.

The server queries FGA on each connection to determine available tools.

<Tip>
  You can also manage tuples directly in the [Auth0 FGA Dashboard](https://dashboard.fga.dev) if you're using Auth0 FGA. The dashboard provides a visual interface for viewing, adding, and removing authorization tuples in real-time.
</Tip>

## Testing your MCP server

Now that your MCP server is up and running, you can test it by using tools like [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector).

<Card title="Learn how to test your Auth0-powered MCP server" href="/ai/docs/mcp/guides/test-your-mcp-server-with-mcp-inspector" icon="book" iconType="solid" horizontal />

### Test authorization scenarios

Now test different authorization scenarios to see how FGA controls tool access. You'll need to use an MCP client like [MCP Inspector](https://github.com/modelcontextprotocol/inspector) to connect to your server.

<Steps>
  <Step title="Test baseline access">
    Connect to your MCP server using your user credentials. Initially, you should only see the public tool.

    **Expected tools**: `get_datetime`

    This demonstrates that without any role or group assignments, authenticated users only have access to public tools.
  </Step>

  <Step title="Grant admin access via managers group">
    Add your user to the managers group to grant admin role permissions:

    ```shell wrap lines theme={null}
    ./fga/add-user-to-group.sh YOUR_EMAIL managers
    ```

    Replace `YOUR_EMAIL` with the email address you use to authenticate.

    Reconnect in your MCP client and verify you now see all tools.

    **Expected tools**: `get_datetime`, `greet`, `whoami`, `get_documents`

    Call the `get_documents` tool and verify that you see both public and private documents in the response. This demonstrates resource-specific permissions based on the admin role.

    <Tip>
      In MCP Inspector, you'll need to disconnect and reconnect (or refresh the connection) after changing permissions. The server queries FGA on each new connection to determine available tools.
    </Tip>
  </Step>

  <Step title="Test content editor role">
    Switch from admin to content editor by changing group membership:

    ```shell wrap lines theme={null}
    ./fga/remove-user-from-group.sh YOUR_EMAIL managers
    ./fga/add-user-to-group.sh YOUR_EMAIL marketing
    ```

    Reconnect in your MCP client.

    **Expected tools**: `get_datetime`, `greet`, `whoami`, `get_documents`

    Call the `get_documents` tool and verify that you now see only public documents (no private documents). This demonstrates how the same tool can provide different data based on user roles.

    **Key difference**: Content editors have access to the same tools as admins but don't have the `can_view_private_documents` permission.
  </Step>

  <Step title="Test temporal access">
    Remove all group memberships and grant time-limited access to a specific tool:

    ```shell wrap lines theme={null}
    ./fga/remove-user-from-group.sh YOUR_EMAIL marketing
    ./fga/add-temporal-access.sh YOUR_EMAIL greet 20s
    ```

    This grants 20 seconds of access to the `greet` tool.

    **Immediately reconnect** in your MCP client.

    **Expected tools**: `get_datetime`, `greet`

    Call the `greet` tool to verify it works.

    **Wait 25 seconds** and reconnect again.

    **Expected tools**: `get_datetime` (only)

    The `greet` tool should no longer appear. This demonstrates automatic expiration of temporal access without any cleanup required.

    <Warning>
      Temporal access is evaluated in real-time on each request. Once the duration expires, FGA automatically denies access even if the tuple still exists in the database.
    </Warning>
  </Step>
</Steps>

## Next steps

* Learn about the simpler scope-based approach in [Authorization for Your MCP Server](./authorization-for-your-mcp-server)
* Explore [Auth0 FGA documentation](https://auth0.com/fine-grained-authorization) to learn more about designing authorization models
* Read about [OpenFGA](https://openfga.dev/docs) for advanced modeling patterns and best practices
* Learn how to [test your MCP server with MCP Inspector](/ai/docs/mcp/guides/test-your-mcp-server-with-mcp-inspector)
