This guide will help you set up an Atlas MCP server that provides access to Microsoft Graph API using Entra ID authentication with On-Behalf-Of (OBO) flow. This sample uses Microsoft Graph API as the API layer for MCP to interface with, as an example.
You can use this Atlas MCP server to test the experience in Visual Studio Code:
"graph-mcp": {
"url": "https://graph-mcp.azurewebsites.net/"
}
⚠️ Warning: All tenant data accessed via Microsoft Graph API will pass through this Atlas MCP server. Ensure you trust the deployment and review security best practices before use. It is recommended to connect only non-production Entra ID tenants. For your own instance of Atlas, follow the instructions below.
- Prerequisites
- Entra ID Application Registration
- Configure the Application
- Local Development Setup
- Azure Deployment
- Testing the Deployment
- Available MCP Tools
- Troubleshooting
- Azure subscription
- Azure CLI installed (Download here)
- .NET 9.0 SDK installed
- PowerShell (Windows) or Bash (macOS/Linux)
- An MCP client for testing (VS Code Insiders edition)
- Sign in to Azure Portal: https://portal.azure.com
- Navigate to Azure Active Directory → App registrations
- Click "New registration"
- Fill in the details:
- Name:
Atlas MCP Server
- Supported account types:
Accounts in this organizational directory only
(Single tenant)
- Redirect URI: Leave blank for now
- Name:
- Click "Register"
- In your app registration, go to "API permissions"
- Click "Add a permission"
- Select "Microsoft Graph" → "Application permissions"
- Add these permissions:
Directory.Read.All
(Read directory data)
- Click "Add permissions"
- IMPORTANT: Click "Grant admin consent" for your organization
- Go to "Certificates & secrets"
- Click "New client secret"
- Add description:
MCP Server Secret
- Set expiration: Choose appropriate duration (recommended: 12-24 months)
- Click "Add"
⚠️ COPY THE SECRET VALUE IMMEDIATELY - you won't be able to see it again!
- Go to "Expose an API"
- Click "Set" next to Application ID URI
- Accept the default URI (api://your-client-id)
- Click "Add a scope":
- Scope name:
user.read
- Who can consent:
Admins and users
- Admin consent display name:
Access user data
- Admin consent description:
Allow the application to access user data on behalf of the signed-in user
- User consent display name:
Access your data
- User consent description:
Allow the application to access your data
- State:
Enabled
- Scope name:
- Click "Add scope"
Copy these values - you'll need them later:
- Tenant ID (Directory ID): Found in "Overview"
- Client ID (Application ID): Found in "Overview"
- Client Secret: The value you copied in Step 3
Open appsettings.json
and replace the placeholder values:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "YOUR_TENANT_ID_HERE",
"ClientId": "YOUR_CLIENT_ID_HERE",
"ClientSecret": "YOUR_CLIENT_SECRET_HERE"
}
}
Replace these values:
YOUR_TENANT_ID_HERE
→ Your Entra ID Tenant IDYOUR_CLIENT_ID_HERE
→ Your App Registration Client IDYOUR_CLIENT_SECRET_HERE
→ Your Client Secret
The fallback values in Program.cs
should also be updated. Find these lines around line 13-15:
var tenantId = builder.Configuration["AzureAd:TenantId"] ?? "YOUR_TENANT_ID_HERE";
var clientId = builder.Configuration["AzureAd:ClientId"] ?? "YOUR_CLIENT_ID_HERE";
Replace the fallback values with your actual Entra ID values.
-
Open terminal in the project directory
-
Build and run the application:
dotnet build dotnet run
-
Verify it's running:
- Open browser: http://localhost:7071/.well-known/oauth-protected-resource
- Should return JSON with OAuth configuration
Configure your MCP client to:
- Server URL:
http://localhost:7071
- Available tools:
GetCurrentUserFromGraph
,CallGraphApiEndpoint
, etc. - Test prompt: Using Agent mode in your client, ask the Agent "Get my user profile". It should return the users attributes.
az login
az group create --name "rg-mcp-server" --location "East US"
az appservice plan create --resource-group rg-mcp-server --name mcpServer --sku B1
az webapp create --resource-group "rg-mcp-server" --plan "mcpServer" --name "graph-mcp"
"graph-mcp"
with a globally unique name if this one is taken.
az webapp config appsettings set --resource-group "rg-mcp-server" --name "graph-mcp" --settings \
"AzureAd__TenantId=YOUR_TENANT_ID_HERE" \
"AzureAd__ClientId=YOUR_CLIENT_ID_HERE" \
"AzureAd__ClientSecret=YOUR_CLIENT_SECRET_HERE" \
"AzureAd__Instance=https://login.microsoftonline.com/" \
"ASPNETCORE_ENVIRONMENT=Production"
Replace the placeholder values with your actual Entra ID values.
dotnet publish -c Release -o ./publish
Compress-Archive -Path "./publish/*" -DestinationPath "./publish.zip" -Force
az webapp deployment source config-zip --resource-group "rg-mcp-server" --name "graph-mcp" --src "./publish.zip"
Open in browser: https://graph-mcp.azurewebsites.net/.well-known/oauth-protected-resource
Expected response:
{
"resource": "https://graph-mcp.azurewebsites.net/sse",
"authorization_servers": ["https://login.microsoftonline.com/YOUR_TENANT_ID/v2.0"],
"bearer_methods_supported": ["header"],
"resource_documentation": "https://docs.example.com/api/mcp"
}
Configure your MCP client:
- Server URL:
https://graph-mcp.azurewebsites.net
- Tools: Test the available MCP tools
- Description: Get current user information from Microsoft Graph /me endpoint
- Parameters: None
- Returns: User profile information
- Description: Universal tool to call any Microsoft Graph API endpoint
- Parameters:
endpoint
(required): Graph API endpoint (e.g.,/v1.0/me
,/v1.0/users
)method
(optional): HTTP method (GET, POST, PUT, PATCH, DELETE) - defaults to GET
- Returns: JSON response from Graph API
-
"Token validation failed"
- Check your Entra ID configuration
- Verify Tenant ID and Client ID are correct
- Ensure admin consent was granted for API permissions
-
"On-Behalf-Of flow failed"
- Check client secret is correct and not expired
- Verify the "Expose an API" configuration
- Ensure the incoming token has the right audience
-
"No Bearer token found"
- Make sure you're sending
Authorization: Bearer <token>
header - Verify the token is valid and not expired
- Make sure you're sending
-
"Web app not found" during deployment
- The app name must be globally unique
- Try a different name like
graph-mcp-yourname
-
Local development not working on localhost:7071
- Run:
dotnet run --urls=http://localhost:7071
- Check
Properties/launchSettings.json
has the correct URL
- Run:
Check web app status:
az webapp show --resource-group "rg-mcp-server" --name "graph-mcp" --query "{State:state, HostName:defaultHostName}"
View application logs:
az webapp log tail --resource-group "rg-mcp-server" --name "graph-mcp"
Test local server:
curl http://localhost:7071/.well-known/oauth-protected-resource
- Never commit secrets to source control
- Use Azure Key Vault for production secrets
- Regularly rotate client secrets
- Use least-privilege principle for API permissions
- Enable HTTPS only for production
- Monitor and log authentication attempts
- Entra ID issues: Check Azure Portal → Azure Active Directory → Sign-ins
- App Service issues: Check Azure Portal → App Service → Log stream
- MCP Protocol: Refer to Model Context Protocol documentation
🎉 Your MCP server with Microsoft Graph API is now ready!