FastMCP ToolSecurityTrimming
A quick sample on how to implement tool security trimming based on user's role assignment with entraID using FastMCP Middleware
Ask AI about FastMCP ToolSecurityTrimming
Powered by Claude Β· Grounded in docs
I know everything about FastMCP ToolSecurityTrimming. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
Secure MCP Server with Azure AD and JWT-based Role Authorization
This project implements a secure Model Context Protocol (MCP) server using Azure AD authentication and JWT-based role authorization, as described in the Medium article by Saima Khan.
Features
- Azure AD Authentication: Secure login using Microsoft Azure Active Directory
- JWT Token Verification: FastMCP integration with JWT bearer authentication
- Role-Based Access Control (RBAC): Tools filtered by Azure AD App Roles
- Redis Token Storage: Secure token management with automatic expiration
- One-Time Auth Codes: Secure token exchange without browser storage
- Middleware-Based Authorization: Protocol-aware access control
Architecture
The solution implements a two-tier security approach:
Tier 1: App Role-Based Access Control (Implemented)
- Controls which tools users can see based on Azure AD App Roles
- Prevents access to dangerous operations before they're exposed
- Acts as a gatekeeper at the MCP Server level
Tier 2: API-Level Restrictions (Future Enhancement)
- Runtime permission checks using On-Behalf-Of (OBO) pattern
- Fine-grained access control for external API calls
- Delegated authentication for downstream services
Project Structure
secure-mcp-server/
βββ shared/
β βββ auth_context.py # Azure AD + MSAL integration
β βββ redis_token_store.py # Token + auth code management
β βββ middleware/
β βββ authorization_middleware.py # Custom FastMCP RBAC enforcement
βββ main.py # Main MCP server with weather tools
βββ client.py # Example client implementation
βββ requirements.txt # Python dependencies
βββ .env.example # Environment variables template
βββ README.md # This file
Azure AD Setup
1. Create App Registration
- Go to Azure Portal β Azure Active Directory β App registrations
- Click "New registration"
- Configure:
- Name: Your MCP Server name
- Redirect URI:
http://localhost:8000/auth/callback
2. Configure Authentication
- Go to Authentication blade
- Enable "ID tokens" under "Implicit grant and hybrid flows"
- Confirm redirect URI is correctly added
3. Create App Roles
- Go to App roles blade (or Manifest)
- Create roles:
{
"allowedMemberTypes": ["User"],
"displayName": "Task Read Access",
"id": "generate-unique-uuid",
"isEnabled": true,
"description": "Allows reading weather tools",
"value": "Task.Read"
}
Create additional roles:
Task.Write: Read + write accessTask.All: Read + write + admin accessAdmin: Full access including delete operations
4. Assign Users to Roles
- Go to Enterprise Applications β Your App β Users and groups
- Click "Add user/group"
- Select users and assign appropriate roles
5. Get Configuration Values
- Tenant ID: Azure AD β Overview β Tenant ID
- Client ID: App registrations β Your app β Application (client) ID
- Client Secret: App registrations β Your app β Certificates & secrets β New client secret
Redis Setup
Create an Azure Cache for Redis instance:
- Go to Azure Portal β Create resource β Azure Cache for Redis
- Configure basic settings (Standard tier recommended)
- Note the hostname and access keys
Installation & Setup
1. Clone and Install Dependencies
git clone <your-repo>
pip install -r requirements.txt
2. Configure Environment
cp .env.example .env
# Edit .env with your Azure AD and Redis configuration
Required environment variables:
# Azure AD Configuration
AZURE_TENANT_ID=""
AZURE_CLIENT_ID=""
AZURE_CLIENT_SECRET=""
# Redis Configuration
REDIS_HOST="<host>.australiaeast.redis.azure.net"
REDIS_PORT=10000
REDIS_USE_AAD=false
REDIS_USERNAME="default"
REDIS_PASSWORD="optional-if-not-using-aad"
REDIS_AAD_SCOPE="https://redis.azure.com/.default"
REDIS_AUTH_MODE=auto
REDIS_SSL=true
REDIS_DECODE=true
REDIS_USERNAME="default"
# Server Configuration
REDIRECT_URI=http://localhost:8000/auth/callback
DEBUG=true
# Optional: Custom port
PORT=8000
3. Start the Server
python main.py
The server will start on http://localhost:8000 with these endpoints:
- Authentication:
/auth/login - MCP Protocol:
/mcp- on port 9000!!! - Health Check:
/health
Usage
Authentication Flow
- Login: Navigate to
http://localhost:8000/auth/login - Azure AD: Complete Microsoft login
- Auth Code: Copy the one-time auth code from the callback page
- Token Exchange: Use the auth code to get access tokens
Using the Client
from client import SecureMCPClient
import asyncio
async def example():
client = SecureMCPClient()
# Authenticate with auth code
token_data = client.authenticate_with_auth_code("your-auth-code")
# List available tools (filtered by role)
tools = await client.list_tools()
print(f"Available tools: {[tool.name for tool in tools]}")
# Call tools based on permissions
weather = await client.get_weather("Perth")
print(weather)
asyncio.run(example())
Interactive Demo
python client.py
This will guide you through the authentication process and demonstrate different access levels.
Available Tools & Access Levels
| Tool | Description | Required Role | Tags |
|---|---|---|---|
get_weather | Get current weather | Task.Read+ | read |
get_forecast | Get weather forecast | Task.Read+ | read |
set_weather_alert | Set weather alerts | Task.Write+ | write |
get_weather_stats | System statistics | Task.All+ | admin |
clear_weather_data | Delete weather data | Admin | delete |
Role Hierarchy
- Task.Read: Can access tools tagged with
read - Task.Write: Can access tools tagged with
read,write - Task.All: Can access tools tagged with
read,write,admin - Admin: Can access all tools including
deleteoperations
API Endpoints
Authentication Endpoints
GET /auth/login- Initiate OAuth loginGET /auth/callback- OAuth callback handlerPOST /auth/exchange- Exchange auth code for tokensPOST /auth/refresh- Refresh access token
MCP Protocol
POST /mcp/*- FastMCP protocol endpoints (requires JWT authentication)
Utility
GET /health- Health checkGET /- Service information
Security Features
Token Management
- Redis Storage: Tokens stored in Redis with automatic expiration
- One-Time Auth Codes: Secure token exchange without browser storage
- Refresh Tokens: Automatic token renewal capability
Access Control
- JWT Verification: JWKS-based token validation
- Role-Based Filtering: Tools filtered by Azure AD App Roles
- Protocol-Aware Middleware: FastMCP middleware for fine-grained control
Production Considerations
- HTTPS Required: Use TLS in production
- Token Rotation: Implement regular token refresh
- Audit Logging: Add comprehensive audit trails
- Rate Limiting: Implement API rate limiting
- Error Handling: Enhanced error responses
Development
Running Tests
# Start server
python main.py
# In another terminal, run client demo
python client.py
Adding New Tools
- Define tool function with appropriate
tags:
@mcp.tool(tags=["read"]) # or ["write"], ["admin"], ["delete"]
def my_new_tool(param: str) -> str:
return f"Result for {param}"
- Update role mappings in
authorization_middleware.pyif needed
Debugging
Enable debug mode in middleware:
mcp.add_middleware(AuthorizationMiddleware(auth_context, redis_token_store, debug=True))
Troubleshooting
Common Issues
- Import Errors: Install dependencies with
pip install -r requirements.txt - Authentication Failed: Check Azure AD configuration and tenant ID
- Token Expired: Use refresh token or re-authenticate
- No Tools Visible: Verify Azure AD App Role assignments
- Redis Connection: Check Redis host, port, and password
Debugging Steps
- Check server logs for detailed error messages
- Verify environment variables are correctly set
- Test Azure AD configuration with a simple OAuth flow
- Confirm Redis connectivity with
redis-cli
References
License
This project is for educational and demonstration purposes based on the referenced Medium article.
