Edit

Deploy Azure MCP Server with on-behalf-of authentication

Deploy Azure MCP Server as a self-hosted remote server over HTTPS on Azure Container Apps. This article uses the on-behalf-of (OBO) authentication model, which lets the server call Azure services by using the identity of the signed-in user rather than the server's own managed identity. Agents in Microsoft Foundry and Microsoft Copilot Studio can connect to the deployed server and invoke Azure MCP tools that operate with the user's own permissions and access.

How the OBO flow works

The on-behalf-of flow is distinct from the managed identity approach used in other Azure MCP Server templates:

  • Managed identity approach: The server authenticates to downstream Azure services by using its own managed identity. All users share the permissions granted to that identity. For a Microsoft Foundry example that uses this model, see Deploy a remote Azure MCP Server and connect using Microsoft Foundry.
  • OBO approach: When a user authenticates with the server, the server exchanges the user's token for a new token scoped to a downstream Azure service. The server calls Azure services on behalf of the user, so each user's own Azure permissions determine what they can do.

The template provisions two Microsoft Entra app registrations to enable this flow:

  • Server app registration: Exposed to clients as the OAuth 2.0 resource. When a user's token arrives, the server uses a federated identity credential (backed by a managed identity) to perform the OBO token exchange to access downstream APIs like Azure Resource Manager and Azure Storage.
  • Client app registration: Used by external clients (Foundry agents, Copilot Studio custom connectors) to authenticate against the server. The client app is pre-authorized on the server app so users don't need to consent separately.

Prerequisites

  • Azure subscription with Owner or User Access Administrator permissions
  • Azure Developer CLI (azd) installed
  • Azure CLI installed
  • The list of Azure MCP Server tool namespaces you want to enable. See azmcp-commands.md. The template in this article enables the storage namespace by default.

Deploy the Azure MCP Server

This article shows how to use the azmcp-obo-aca azd template to deploy the Azure MCP Server to Azure Container Apps with OBO authentication. Deploy the server:

  1. Initialize the azmcp-obo-template template by using the azd init command.

    azd init -t azmcp-obo-template
    

    When prompted, enter an environment name.

  2. Run the template by using the azd up command.

    azd up
    

    azd prompts you for the following values:

    • Subscription: Select the subscription for the provisioned resources.
    • Resource group: Create or select a resource group to hold the resources.

azd uses the template files to provision the following resources and configurations:

  • Azure Container App: Runs the Azure MCP Server with the storage namespace enabled.
  • User-assigned managed identity: Provides a client credential for the server app registration through a federated identity credential. The server uses this identity to perform the OBO token exchange.
  • Entra app registration (server): The OAuth 2.0 resource exposed to clients. Has the Mcp.Tools.ReadWrite scope, and carries Azure Resource Manager and Azure Storage API permissions for the OBO exchange.
  • Entra app registration (client): Used by clients such as Foundry agents and Power Apps custom connectors to authenticate with the server. Pre-authorized on the server app to eliminate the need for user consent.
  • Application Insights: Provides telemetry and monitoring.

Retrieve deployment outputs

After the deployment finishes, use the azd env get-values command to get the azd environment variables.

azd env get-values

Example output:

AZURE_RESOURCE_GROUP="<your-resource-group-name>"
AZURE_SUBSCRIPTION_ID="<your-subscription-id>"
AZURE_TENANT_ID="<your-tenant-id>"
CONTAINER_APP_NAME="<your-container-app-name>"
CONTAINER_APP_URL="https://<your-container-app-name>.<region>.azurecontainerapps.io"
ENTRA_APP_CLIENT_CLIENT_ID="<client-app-registration-id>"
ENTRA_APP_SERVER_CLIENT_ID="<server-app-registration-id>"

Keep this output available. You need these values in the sections that follow.

After deployment, complete two required configuration steps before clients can connect.

Add the API scope to the client app registration

The client app registration needs permission to call the server app's Mcp.Tools.ReadWrite scope.

  1. In the Azure portal, search for the client app registration by using the ENTRA_APP_CLIENT_CLIENT_ID value.
  2. Go to API permissionsAdd a permissionMy APIs tab.
  3. Select the server app registration and add the Mcp.Tools.ReadWrite scope.
  4. Select Grant admin consent to apply the permission to all users.

Note

If the server app registration doesn't appear under My APIs, the app might still be propagating. Wait a few minutes and refresh, or see the Troubleshooting section.

The server app registration has Azure Resource Manager and Azure Storage API permissions configured, but these permissions require admin consent before the OBO token exchange can succeed.

  1. In the Azure portal, search for the server app registration by using the ENTRA_APP_SERVER_CLIENT_ID value.
  2. Go to API permissions.
  3. Select Grant admin consent for <your tenant> and confirm.

Note

If the Grant admin consent button is unavailable, your account lacks sufficient permissions. This template requires an Azure subscription with Owner or User Access Administrator access.

Alternatively, use the Azure CLI:

az ad app permission admin-consent --id <ENTRA_APP_SERVER_CLIENT_ID>

Connect to the server

After deploying and completing the post-deployment configuration, you can connect clients to the server. Select the option that fits your scenario.

The template includes a .NET console app in the client/ folder that you can use to verify the deployment locally. The app authenticates interactively through the browser by using the client app registration, connects to the MCP server, lists the available tools, and optionally calls the storage_account_get tool.

Prerequisites: .NET 10 SDK

  1. In the client/ folder, open appsettings.json and set the following values by using the azd env get-values output:

    {
      "McpServer": {
        "Url": "<CONTAINER_APP_URL>"
      },
      "EntraClientClientId": "<ENTRA_APP_CLIENT_CLIENT_ID>",
      "SubscriptionId": "<AZURE_SUBSCRIPTION_ID>"
    }
    

    Tip

    You can also create an appsettings.Development.json file with your local values and set the DOTNET_ENVIRONMENT environment variable to Development to load it without modifying the committed file.

  2. From the client/ folder, run the app:

    dotnet run
    

    The app opens a browser window for you to sign in. After sign-in, it connects to the MCP server and prints the list of available tools.

  3. To also call the storage_account_get tool and list storage accounts in your subscription, pass the --list-accounts flag:

    dotnet run -- --list-accounts true
    

If you encounter authentication errors such as MsalUiRequiredException, see the Troubleshooting section.

Add more Azure tools

The template enables the storage namespace by default. To enable additional tool namespaces:

  1. Identify the API permissions required for the tools you want. See the API permissions reference.

  2. Add the permissions to the server app registration by using the Azure CLI:

    az ad app permission add \
      --id <ENTRA_APP_SERVER_CLIENT_ID> \
      --api <downstream-api-id> \
      --api-permissions <permission-id>=Scope
    
  3. Grant admin consent for the new permissions:

    az ad app permission admin-consent --id <ENTRA_APP_SERVER_CLIENT_ID>
    
  4. Update the Container App environment variables to pass the additional namespace flags to the server startup command.

Clean up resources

Run azd down to delete the Azure resources created by this template.

azd down

Note

azd down doesn't delete the Entra app registrations. After running azd down, manually delete them in the Azure portal by searching for the ENTRA_APP_CLIENT_CLIENT_ID and ENTRA_APP_SERVER_CLIENT_ID values.

Troubleshooting

The following sections provide details on common errors you might encounter and how to resolve them.

IDW10502: MsalUiRequiredException

{"status":500,"message":"IDW10502: An MsalUiRequiredException was thrown due to a challenge for the user..."}

The server's OBO token exchange failed because admin consent isn't granted for the downstream API permissions on the server app registration. In the Azure portal, find the server app registration (using ENTRA_APP_SERVER_CLIENT_ID) → API permissionsGrant admin consent.

OBO token exchange failures

Check the Entra sign-in logs for details. In the Azure portal, go to Microsoft Entra IDMonitoringSign-in logsUser sign-ins (non-interactive). Look for entries where the application matches your server app registration and the resource matches the downstream Azure API.

Container App errors

Open the Azure portal, go to your Container App → MonitoringLog stream to view real-time application logs. Application Insights telemetry is available under Investigate → Search or via Log Analytics queries on the requests and traces tables.

ServiceManagementReference error on redeploy

{"error":{"code":"BadRequest","message":"ServiceManagementReference field is required for Update..."}}

This error occurs when running azd up on an existing deployment that was originally created without a serviceManagementReference value. Add the parameter to infra/main.parameters.json:

{
  "parameters": {
    "serviceManagementReference": {
      "value": "<your-guid>"
    }
  }
}

Note

For a list of other known issues, see KnownIssues.md in the template repository.