Skip to content

Latest commit

 

History

History
315 lines (242 loc) · 11.4 KB

File metadata and controls

315 lines (242 loc) · 11.4 KB

Simple Calculator FastMCP - Protected

This example demonstrates how to set up an OAuth2-protected MCP server using the NVIDIA NeMo Agent Toolkit FastMCP server runtime. This complements the unprotected Simple Calculator FastMCP example to demonstrate both authenticated and unauthenticated MCP servers using the FastMCP server runtime.

This example uses per-user mode, enabling complete per-user isolation while accessing the same protected calculator tools.

Architecture Overview

This example consists of three main components:

graph TB
    subgraph Client["MCP Client (Calculator Client)"]
        direction TB
        CalculatorClient["Calculator Client Workflow"]
        MCPClientPlugin["MCP Client Plugin<br/>• Discovers tools<br/>• Handles OAuth2 flow<br/>• Manages JWT tokens"]
        CalculatorClient --> MCPClientPlugin
    end

    subgraph Server["MCP Server Using FastMCP Runtime (Calculator)"]
        direction TB
        Calculator["Calculator Workflow"]
        OAuthMiddleware["FastMCP OAuth2 Resource Server Middleware<br/>• Introspects tokens<br/>• Checks scopes<br/>• Validates activity"]
        Calculator --> OAuthMiddleware
    end

    subgraph Auth["Authorization Server (Keycloak)"]
        direction LR
        AuthCore["Keycloak OAuth2 Server<br/>• Authenticates users<br/>• Issues tokens<br/>• Provides introspection endpoint"]
    end

    MCPClientPlugin -->|"①  MCP initialize<br/>(triggers authentication flow)"| Calculator
    MCPClientPlugin -->|"②  OAuth2 Authorization Flow<br/>(Browser-based)"| AuthCore
    MCPClientPlugin -->|"③  MCP tool calls<br/>(Authorization: Bearer token)"| Calculator
    OAuthMiddleware -.->|"Introspect token<br/>(RFC 7662)"| AuthCore

    style Client fill:#e1f5ff
    style Server fill:#ffe1e1
    style Auth fill:#e1ffe1
Loading

Prerequisites

  • NVIDIA NeMo Agent Toolkit installed (see Installation Guide)
  • Keycloak server running locally (see setup instructions below)

Setup Instructions

Step 1: Start Keycloak

# Start Keycloak
docker run -d --name keycloak \
  -p 127.0.0.1:8080:8080 \
  -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
  -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
  quay.io/keycloak/keycloak:latest start-dev

Wait for Keycloak to start (about 30-60 seconds). Check logs:

docker logs -f keycloak

Look for: Listening on: http://0.0.0.0:8080

Access Keycloak: Open http://localhost:8080 in your browser

Step 2: Configure Keycloak Realm and Scopes

  1. Log in to Keycloak Admin Console:

    • Username: admin
    • Password: admin
  2. Verify you are in the master realm (top-left dropdown)

  3. Create the calculator_mcp_execute scope (for the calculator):

    • Go to Client scopes (left sidebar)
    • Click Create client scope
    • Fill in:
      • Name: calculator_mcp_execute
      • Description: Permission to execute calculator operations
      • Type: Optional
      • Protocol: openid-connect
      • Include in token scope: On
    • Click Save
  4. Verify OpenID discovery endpoint:

    curl http://localhost:8080/realms/master/.well-known/openid-configuration | python3 -m json.tool

    You should see the OAuth2 endpoints, including:

    • authorization_endpoint: http://localhost:8080/realms/master/protocol/openid-connect/auth
    • token_endpoint: http://localhost:8080/realms/master/protocol/openid-connect/token
    • introspection_endpoint: http://localhost:8080/realms/master/protocol/openid-connect/token/introspect

Step 3: Register MCP Client

You can register the client manually or use the dynamic client registration feature. For testing, manual registration is used.

  1. In Keycloak Admin Console, go to Clients (left sidebar)

  2. Click Create client

  3. General settings:

    • Client ID: nat-mcp-client
    • Client type: OpenID Connect
    • Click Next
  4. Capability config:

    • Client authentication: On (confidential client)
    • Authorization: Off
    • Authentication flow:
      • Standard flow (authorization code)
      • Direct access grants
    • Click Next
  5. Login settings:

    • Valid redirect URIs: http://localhost:8000/auth/redirect
    • Web origins: http://localhost:8000
    • Click Save
  6. Add client scope if not already added:

    • Go to Client scopes tab
    • Click Add client scope
    • Select calculator_mcp_execute
    • Choose Optional
    • Click Add
  7. Set Consent required:

    • Go to Settings tab
    • Toggle Consent required to On (scroll down to the bottom of the page to see the setting)
    • Click Save
  8. Get client credentials:

    • Go to Credentials tab
    • Copy the Client secret
    • Note the Client ID: nat-mcp-client

Step 4: Register Resource Server for Introspection

The FastMCP server runtime validates OAuth2 bearer tokens for this example. Register a resource server client so the MCP server can authenticate to Keycloak when introspecting opaque tokens, and so JWT access tokens can be audience-bound to the same resource server.

  1. In Keycloak Admin Console, go to Clients

  2. Click Create client

  3. General settings:

    • Client ID: nat-mcp-resource-server
    • Client type: OpenID Connect
    • Click Next
  4. Capability config:

    • Client authentication: On (confidential client)
    • Authorization: Off
    • Authentication flow:
      • Direct access grants
    • Click Next
    • Click Save
  5. Get resource server credentials:

    • Go to Credentials tab
    • Copy the Client secret
    • Note the Client ID: nat-mcp-resource-server
  6. Add the resource server audience to calculator tokens:

    • Go back to Client scopes (left sidebar)
    • Open the calculator_mcp_execute scope
    • Go to the Mappers tab
    • Click Configure a new mapper
    • Select Audience
    • Configure the mapper:
      • Name: mcp-resource-server-audience
      • Included Client Audience: nat-mcp-resource-server
      • Included Custom Audience: Leave blank
      • Add to ID token: Off
      • Add to access token: On
      • Add to token introspection: On (if available in your Keycloak version)
    • Click Save

    The server config expects nat-mcp-resource-server in the access token's aud claim. Without this mapper, browser consent can succeed but the protected FastMCP server still rejects MCP initialization.

Step 5: Start the Protected FastMCP Server

# Terminal 1
export NAT_CALCULATOR_RESOURCE_CLIENT_ID="nat-mcp-resource-server"  # Resource server client ID
export NAT_CALCULATOR_RESOURCE_CLIENT_SECRET="<your-resource-client-secret>"  # Resource server client secret from Step 4.5

nat fastmcp server run --config_file examples/MCP/simple_calculator_fastmcp_protected/configs/config-server.yml

Step 6: Run the MCP Calculator Client

The client workflow also starts the mcp_time function group with python -m mcp_server_time. If you installed this example package, the dependency is included automatically. To verify it is available before running the workflow:

uv run --project examples/MCP/simple_calculator_fastmcp_protected --locked python -m mcp_server_time --help

Set the client ID and client secret from Step 3 in the environment variables:

# Terminal 2
export NAT_CALCULATOR_CLIENT_ID="nat-mcp-client"  # OAuth client ID for the MCP client (auth code flow)
export NAT_CALCULATOR_CLIENT_SECRET="<your-client-secret>"  # OAuth client secret for the MCP client from Step 3.8

nat run --config_file examples/MCP/simple_calculator_fastmcp_protected/configs/config-client.yml \
  --input "Is the product of 2 and 3 greater than the current hour of the day?"

What should happen:

  1. Browser opens with Keycloak login page
  2. Log in with any user (or create one)
  3. Consent page appears requesting calculator_mcp_execute scope
  4. Browser redirects back to localhost:8000/auth/redirect
  5. Workflow continues and calls the calculator
  6. Response returned successfully

Cleanup

To stop and remove Keycloak:

docker stop keycloak
docker rm keycloak

To restart with clean state:

docker rm -f keycloak
# Then run the start command again

Configuration Files

Server Configuration (configs/config-server.yml)

This configures the protected MCP server frontend with OAuth2 resource server authentication using the FastMCP server runtime:

general:
  front_end:
    _type: fastmcp
    name: "Protected Calculator FastMCP"
    port: 9902
    host: localhost
    server_auth:
      issuer_url: http://localhost:8080/realms/master
      discovery_url: http://localhost:8080/realms/master/.well-known/openid-configuration
      introspection_endpoint: http://localhost:8080/realms/master/protocol/openid-connect/token/introspect
      audience: nat-mcp-resource-server
      client_id: ${NAT_CALCULATOR_RESOURCE_CLIENT_ID:-"nat-mcp-resource-server"}
      client_secret: ${NAT_CALCULATOR_RESOURCE_CLIENT_SECRET}
      scopes: [calculator_mcp_execute]

Client Configuration (configs/config-client.yml)

This configures an MCP client to connect to the protected MCP server in per-user mode:

function_groups:
  mcp_calculator_protected:
    _type: per_user_mcp_client
    server:
      transport: streamable-http
      url: http://localhost:9902/mcp
      auth_provider: mcp_oauth2_calculator

authentication:
  mcp_oauth2_calculator:
    _type: mcp_oauth2
    server_url: http://localhost:9902/mcp
    redirect_uri: http://localhost:8000/auth/redirect
    client_id: ${NAT_CALCULATOR_CLIENT_ID:-"nat-mcp-client"}
    client_secret: ${NAT_CALCULATOR_CLIENT_SECRET}
    scopes: [calculator_mcp_execute]

workflow:
  _type: per_user_react_agent
  tool_names: [mcp_calculator_protected]

Troubleshooting

If the server logs JWT audience ['master-realm', 'account'] does not contain required audience 'nat-mcp-resource-server', the access token does not include the resource server audience. Re-check the calculator_mcp_execute client-scope mapper from Step 4.6:

  • Included Client Audience must be nat-mcp-resource-server
  • Add to access token must be On
  • The calculator_mcp_execute scope must be assigned to the nat-mcp-client client and requested by the client workflow

Related Examples

  • examples/MCP/simple_calculator_fastmcp/: FastMCP calculator example without authentication

References

  • FastMCP Server - Learn about running the FastMCP server runtime
  • MCP Client - Learn about using the MCP client to interact with the MCP server