Skip to content

Commit d07a497

Browse files
authored
Merge pull request #312019 from craigshoemaker/aca/mcp-content
[Container Apps] New: MCP Content
2 parents 05868e8 + c895447 commit d07a497

11 files changed

Lines changed: 3279 additions & 298 deletions

articles/container-apps/TOC.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,26 @@ items:
5252
href: ai-integration.md
5353
- name: Compose for agents
5454
href: compose-agent.md
55+
- name: MCP servers
56+
items:
57+
- name: Overview
58+
href: mcp-overview.md
59+
- name: Choose a hosting option
60+
href: mcp-choosing-azure-service.md
61+
- name: Tutorials
62+
items:
63+
- name: Deploy with .NET
64+
href: tutorial-mcp-server-dotnet.md
65+
- name: Deploy with Python
66+
href: tutorial-mcp-server-python.md
67+
- name: Deploy with Node.js
68+
href: tutorial-mcp-server-nodejs.md
69+
- name: Deploy with Java
70+
href: tutorial-mcp-server-java.md
71+
- name: Authentication
72+
href: mcp-authentication.md
73+
- name: Troubleshooting
74+
href: mcp-troubleshooting.md
5575
- name: GPUs
5676
items:
5777
- name: Serverless GPUs
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
---
2+
title: Secure MCP servers on Azure Container Apps
3+
description: Learn how to authenticate and authorize MCP servers on Azure Container Apps using Microsoft Entra ID or API key authentication.
4+
#customer intent: As a developer, I want to secure my MCP server on Azure Container Apps so that only authorized clients can access my tools.
5+
ms.topic: how-to
6+
ms.service: azure-container-apps
7+
ms.collection: ce-skilling-ai-copilot
8+
ms.date: 02/19/2026
9+
author: craigshoemaker
10+
ms.author: cshoe
11+
ms.reviewer: cshoe
12+
---
13+
14+
# Secure MCP servers on Azure Container Apps
15+
16+
This article explains how to authenticate and secure MCP servers running on Azure Container Apps. The approach differs depending on whether you host a **standalone container app** or use the **platform-managed MCP server** in dynamic sessions.
17+
18+
## Prerequisites
19+
20+
- An Azure account with an active subscription. [Create one for free](https://azure.microsoft.com/free/).
21+
- [Azure CLI](/cli/azure/install-azure-cli) version 2.62.0 or later.
22+
- An existing container app or session pool. If you don't have one, see the [MCP server tutorials](mcp-overview.md).
23+
24+
## Authentication models overview
25+
26+
Azure Container Apps supports two authentication models for MCP servers. The following table summarizes the key differences.
27+
28+
| Aspect | Standalone container app | Dynamic sessions MCP |
29+
|--------|--------------------------|----------------------|
30+
| Auth mechanism | Container Apps built-in authentication with Microsoft Entra ID | API key via `x-ms-apikey` header |
31+
| Token type | OAuth 2.0 Bearer token | Opaque API key string |
32+
| Identity provider | Microsoft Entra ID | Azure Resource Manager |
33+
| Key/token rotation | Managed by Microsoft Entra ID | Regenerate via Azure Resource Manager API |
34+
| Authorization scope | Configurable per application | Session pool level |
35+
| Transport encryption | TLS (Container Apps ingress) | TLS (Container Apps sessions endpoint) |
36+
37+
## Standalone container app with Microsoft Entra ID authentication
38+
39+
When you deploy your own MCP server as a container app, you own the authentication layer. Use the Container Apps [built-in authentication](/azure/container-apps/authentication) feature backed by Microsoft Entra ID.
40+
41+
### Configure built-in authentication
42+
43+
The following steps register a Microsoft Entra ID application and enable built-in authentication on your container app.
44+
45+
1. Register an application in Microsoft Entra ID:
46+
47+
```azurecli
48+
APP_ID=$(az ad app create \
49+
--display-name "mcp-server-auth" \
50+
--sign-in-audience AzureADMyOrg \
51+
--query appId -o tsv)
52+
```
53+
54+
1. Create a service principal:
55+
56+
```azurecli
57+
az ad sp create --id $APP_ID
58+
```
59+
60+
1. Add a client secret:
61+
62+
```azurecli
63+
CLIENT_SECRET=$(az ad app credential reset --id $APP_ID --query password -o tsv)
64+
TENANT_ID=$(az account show --query tenantId -o tsv)
65+
```
66+
67+
1. Enable built-in auth on the container app:
68+
69+
```azurecli
70+
az containerapp auth microsoft update \
71+
--name <CONTAINER_APP_NAME> \
72+
--resource-group <RESOURCE_GROUP> \
73+
--client-id $APP_ID \
74+
--client-secret $CLIENT_SECRET \
75+
--tenant-id $TENANT_ID \
76+
--issuer "https://login.microsoftonline.com/$TENANT_ID/v2.0" \
77+
--yes
78+
```
79+
80+
1. Set the unauthenticated action to require login:
81+
82+
```azurecli
83+
az containerapp auth update \
84+
--name <CONTAINER_APP_NAME> \
85+
--resource-group <RESOURCE_GROUP> \
86+
--unauthenticated-client-action Return401
87+
```
88+
89+
### Connect from an MCP client with a bearer token
90+
91+
When your MCP server requires a bearer token, configure token retrieval in your MCP client. The following example shows a `.vscode/mcp.json` configuration for GitHub Copilot:
92+
93+
```json
94+
{
95+
"servers": {
96+
"my-mcp-server": {
97+
"type": "http",
98+
"url": "https://<CONTAINER_APP_NAME>.<REGION>.azurecontainerapps.io/mcp",
99+
"headers": {
100+
"Authorization": "Bearer ${input:mcpBearerToken}"
101+
}
102+
}
103+
},
104+
"inputs": [
105+
{
106+
"id": "mcpBearerToken",
107+
"type": "promptString",
108+
"description": "Enter your bearer token for the MCP server",
109+
"password": true
110+
}
111+
]
112+
}
113+
```
114+
115+
> [!TIP]
116+
> For development, get a token by using `az account get-access-token --resource $APP_ID --query accessToken -o tsv` and paste it when prompted. For automated workflows, integrate with your organization's token management system.
117+
118+
### Configure CORS
119+
120+
MCP clients that connect from web-based environments need CORS headers. Use the following command to configure CORS on your container app:
121+
122+
```azurecli
123+
az containerapp ingress cors update \
124+
--name <CONTAINER_APP_NAME> \
125+
--resource-group <RESOURCE_GROUP> \
126+
--allowed-origins "https://vscode.dev" "https://github.dev" \
127+
--allowed-methods "GET" "POST" "OPTIONS" \
128+
--allowed-headers "Content-Type" "Authorization" "Mcp-Session-Id" \
129+
--max-age 3600
130+
```
131+
132+
The following headers are key to allow:
133+
134+
- `Content-Type`: required for JSON-RPC requests
135+
- `Authorization`: required for bearer token auth
136+
- `Mcp-Session-Id`: used by MCP clients for stateful sessions
137+
138+
> [!NOTE]
139+
> GitHub Copilot connects to remote MCP servers from the VS Code desktop app, not from a browser. CORS is only needed if you intend to support browser-based MCP clients or VS Code for the Web. The standalone tutorials use wildcard CORS origins for simplicity; for production, restrict to specific trusted origins as shown here.
140+
141+
### Security recommendations for standalone MCP servers
142+
143+
Apply the following best practices to harden your standalone MCP server.
144+
145+
- **Network restrictions**: Use [IP restrictions](/azure/container-apps/ip-restrictions) or [virtual network integration](/azure/container-apps/vnet-custom) to limit access to known client IPs.
146+
- **Rate limiting**: Implement rate limiting in your application code or front the app with Azure API Management.
147+
- **Input validation**: Validate all tool arguments in your MCP server code. MCP tool inputs are arbitrary JSON. Treat them as untrusted.
148+
- **Stateless design**: Prefer stateless MCP servers to avoid session-hijacking risks. In most MCP SDKs, this means disabling server-side session ID generation (for example, `sessionIdGenerator: undefined` in TypeScript or `stateless_http=True` in Python).
149+
- **Health probes**: Configure health probes on a separate endpoint (such as `/healthz`), not on the MCP endpoint. MCP endpoints expect JSON-RPC POST requests and return errors for plain GET probes.
150+
151+
## Dynamic sessions with API key authentication
152+
153+
> [!IMPORTANT]
154+
> The platform-managed MCP server for dynamic sessions is in **preview**. The API version `2025-02-02-preview` and `mcpServerSettings` properties are subject to change.
155+
156+
The platform-managed MCP server in dynamic sessions uses API key authentication. The key is scoped to the session pool and grants access to all tools and sessions in the pool.
157+
158+
### API key authentication flow
159+
160+
The following steps describe how API key authentication works for dynamic sessions.
161+
162+
1. The client sends a JSON-RPC request with the `x-ms-apikey` header.
163+
1. The session pool proxy validates the key against the Azure control plane.
164+
1. If the key is valid, the request is forwarded to the session. If not, an authentication error is returned.
165+
166+
### Retrieve the API key
167+
168+
Use the following command to fetch the API key for your session pool.
169+
170+
```azurecli
171+
API_KEY=$(az rest --method POST \
172+
--uri "https://management.azure.com/subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<RESOURCE_GROUP>/providers/Microsoft.App/sessionPools/<SESSION_POOL_NAME>/fetchMCPServerCredentials" \
173+
--uri-parameters api-version=2025-02-02-preview \
174+
--query "apiKey" -o tsv)
175+
```
176+
177+
### Rotate and cache the API key
178+
179+
You can regenerate the API key at any time. The platform caches validation results for up to five minutes, so previously valid keys might continue to work after regeneration until the cache expires.
180+
181+
To rotate the API key, call the `regenerateCredentials` action on the session pool:
182+
183+
```azurecli
184+
az rest --method POST \
185+
--uri "https://management.azure.com/subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<RESOURCE_GROUP>/providers/Microsoft.App/sessionPools/<SESSION_POOL_NAME>/regenerateCredentials" \
186+
--uri-parameters api-version=2025-02-02-preview
187+
```
188+
189+
After regeneration, retrieve the new key by using `fetchMCPServerCredentials` as shown earlier.
190+
191+
### Security recommendations for dynamic sessions
192+
193+
Apply the following best practices to secure your dynamic sessions MCP deployment.
194+
195+
- **API key scope**: The API key grants access to the entire session pool. Any client with the key can create environments and execute code. Don't share the key with untrusted parties.
196+
- **Environment isolation**: Each session runs in a Hyper-V-isolated container. Code execution in one session can't access another session's data.
197+
- **Network egress**: Control whether sessions can access the internet by using `sessionNetworkConfiguration.status`. Set to `EgressDisabled` if the sessions don't need external network access.
198+
- **Session lifetime**: Configure `coolDownPeriodInSeconds` to automatically destroy idle sessions. This setting limits the window of exposure if a session is compromised.
199+
- **Secret storage**: Store the API key in [Azure Key Vault](/azure/key-vault/general/overview) or [Container Apps secrets](/azure/container-apps/manage-secrets) rather than in code or configuration files.
200+
201+
## Common authentication mismatches
202+
203+
A common mistake is using the API key header (`x-ms-apikey`) with a standalone container app, or using a bearer token with the sessions MCP endpoint. The following table shows what happens when you mix them up.
204+
205+
| Mismatch | Result |
206+
|----------|--------|
207+
| `x-ms-apikey` header sent to standalone app | Header is ignored; request hits built-in authentication and returns `401` if auth is enabled |
208+
| `Authorization: Bearer` sent to sessions MCP | Key validation fails and returns `401` |
209+
210+
Make sure your MCP client configuration matches the hosting model you deployed.
211+
212+
## Related content
213+
214+
- [MCP servers on Azure Container Apps overview](mcp-overview.md)
215+
- [Authentication and authorization in Azure Container Apps](/azure/container-apps/authentication)
216+
- [Microsoft Entra ID authentication](/azure/container-apps/authentication-azure-active-directory)
217+
- [Dynamic sessions overview](/azure/container-apps/sessions)
218+
- [Manage secrets in Container Apps](/azure/container-apps/manage-secrets)
219+
- [Configure CORS for Container Apps](/azure/container-apps/cors)
220+
- [Troubleshoot MCP servers on Container Apps](mcp-troubleshooting.md)
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
---
2+
title: Choose an Azure service for your MCP server
3+
description: Compare Azure Container Apps, App Service, and Azure Functions for hosting MCP servers and pick the best fit for your workload.
4+
#customer intent: As a developer, I want to understand how Azure hosting options compare for MCP servers so that I can pick the right service for my workload.
5+
ms.topic: conceptual
6+
ms.service: azure-container-apps
7+
ms.collection: ce-skilling-ai-copilot
8+
ms.custom: cross-service
9+
ms.date: 02/19/2026
10+
author: craigshoemaker
11+
ms.author: cshoe
12+
ms.reviewer: cshoe
13+
---
14+
15+
# Choose an Azure service for your MCP server
16+
17+
You can use several Azure services to host Model Context Protocol (MCP) servers. This guide helps you choose the right service based on your workload requirements, team expertise, and operational needs.
18+
19+
> [!NOTE]
20+
> This article compares multiple Azure services to help you make a hosting decision. For Container Apps-specific details, see [MCP servers on Azure Container Apps](mcp-overview.md).
21+
22+
## Hosting options overview
23+
24+
Azure provides four ways to host an MCP server. Each option targets a different mix of flexibility, simplicity, and isolation.
25+
26+
### Azure Container Apps (standalone)
27+
28+
Deploy any MCP server you build as a container with HTTP ingress. Container Apps gives you full control over the runtime, supports any language with an MCP SDK, and includes features like autoscaling with scale-to-zero, Dapr integration, and service-to-service networking.
29+
30+
### Azure Container Apps dynamic sessions
31+
32+
Use platform-managed session pools with built-in MCP tooling for sandboxed code execution. You don't write or deploy MCP server code. The platform provides predefined tools for Python and shell environments, with Hyper-V isolation between sessions.
33+
34+
### Azure App Service
35+
36+
Add an MCP endpoint to an existing or new web app. App Service supports code-based deployment without a Dockerfile and integrates with Microsoft Entra ID for authentication.
37+
38+
### Azure Functions
39+
40+
Map function triggers to MCP tools by using the [Azure Functions MCP extension](/azure/azure-functions/scenario-custom-remote-mcp-server). Azure Functions is optimized for stateless, event-driven tool execution with per-invocation pricing.
41+
42+
## Compare hosting options
43+
44+
The following table summarizes the key differences between hosting options.
45+
46+
| Consideration | Container Apps (standalone) | Container Apps (sessions) | App Service | Azure Functions |
47+
|---|---|---|---|---|
48+
| **Custom tools** | Yes | No (platform-defined only) | Yes | Yes |
49+
| **Language support** | Any (containerized) | Python and shell only | .NET, Python, Node.js, Java | .NET, Python, Node.js, Java |
50+
| **MCP transport** | Streamable HTTP | JSON-RPC over HTTP | Streamable HTTP | MCP extension or self-hosted |
51+
| **Authentication** | Built-in auth (Microsoft Entra ID) or custom | API key (`x-ms-apikey`) | App Service auth (Microsoft Entra ID) | Function keys or Microsoft Entra ID |
52+
| **Isolation** | Container-level | Hyper-V per session | App-level | Function-level |
53+
| **Scaling** | Revision autoscale, scale-to-zero | Per-session, pool-managed | App Service Plan | Consumption or Premium plan |
54+
| **Cold start** | Yes (mitigate with min replicas) | Subsecond (prewarmed pool) | Depends on plan | Yes (Consumption plan) |
55+
| **Microservices** | Native (environments, Dapr) | No | Limited | Limited |
56+
| **Operational overhead** | Medium (Dockerfile, registry) | Low (platform-managed) | Low (code deployment) | Low (function deployment) |
57+
| **Pricing model** | Per vCPU-second, per GiB-second | Per session (consumption) | App Service Plan | Per execution |
58+
59+
## Choose by scenario
60+
61+
Use the following guidance to narrow your decision based on common workload patterns.
62+
63+
### Build a custom MCP server with Azure
64+
65+
**Recommended: Azure Container Apps (standalone) or Azure App Service**
66+
67+
Both services let you build an MCP server with any official MCP SDK and expose it over streamable HTTP. Choose between them based on your deployment model and feature needs.
68+
69+
- **Container Apps**: Prefer containers, need autoscaling with scale-to-zero, want Dapr integration, or are building a microservices architecture with multiple MCP servers. For interactive MCP clients (like GitHub Copilot), set a minimum replica count of 1 to avoid cold-start latency.
70+
- **App Service**: Prefer code-based deployment without a Dockerfile, have an existing App Service Plan, or want the simplicity of `az webapp up`.
71+
72+
Tutorials:
73+
74+
- [Deploy an MCP server to Container Apps (.NET)](tutorial-mcp-server-dotnet.md)
75+
- [Deploy an MCP server to Container Apps (Python)](tutorial-mcp-server-python.md)
76+
- [Deploy an MCP server to Container Apps (Node.js)](tutorial-mcp-server-nodejs.md)
77+
- [Deploy an MCP server to Container Apps (Java)](tutorial-mcp-server-java.md)
78+
79+
### Run sandboxed code for an AI agent
80+
81+
**Recommended: Azure Container Apps dynamic sessions**
82+
83+
Session pools with MCP enabled provide Hyper-V-isolated environments for running untrusted or LLM-generated code. The platform manages the MCP server, so you don't write or deploy server code. The built-in tools cover code execution scenarios without custom development:
84+
85+
- `launchShell`: Creates a new environment
86+
- `runShellCommandInRemoteEnvironment`: Executes shell commands
87+
- `runPythonCodeInRemoteEnvironment`: Executes Python code
88+
89+
Dynamic sessions maintain prewarmed instances, so you don't experience cold-start latency.
90+
91+
Tutorials:
92+
93+
- [Use MCP with dynamic sessions (shell)](sessions-tutorial-shell-mcp.md)
94+
- [Use MCP with dynamic sessions (Python)](sessions-tutorial-python-mcp.md)
95+
96+
### Add MCP to an existing web app
97+
98+
**Recommended: Azure App Service**
99+
100+
If your app already runs on App Service, add the MCP SDK to your existing codebase, mount the MCP endpoint alongside your existing routes, and redeploy.
101+
102+
- [Integrate an App Service app as an MCP server (.NET)](/azure/app-service/tutorial-ai-model-context-protocol-server-dotnet)
103+
- [Integrate an App Service app as an MCP server (Python)](/azure/app-service/tutorial-ai-model-context-protocol-server-python)
104+
105+
If your existing app runs on Container Apps, follow the [standalone tutorials](#build-a-custom-mcp-server-with-azure).
106+
107+
### Create lightweight, event-driven tool endpoints
108+
109+
**Recommended: Azure Functions**
110+
111+
The Azure Functions [MCP extension](/azure/azure-functions/scenario-custom-remote-mcp-server) maps function triggers to MCP tools. Azure Functions is a good fit when:
112+
113+
- Each tool is independent and stateless.
114+
- You want per-invocation pricing.
115+
- You're integrating with [Foundry Agent Service](/azure/azure-functions/functions-mcp-foundry-tools).
116+
117+
For interactive MCP clients, use a Functions Premium plan to avoid cold-start latency on the Consumption plan.
118+
119+
### Run multiple MCP servers with service-to-service communication
120+
121+
**Recommended: Azure Container Apps (standalone)**
122+
123+
Container Apps environments support internal service discovery, Dapr sidecars, and managed identities for service-to-service calls. Deploy multiple MCP servers as separate container apps within the same environment and let them communicate securely without exposing internal endpoints to the internet. Container Apps gives you full control over the runtime and dependencies for each server, but you manage a Dockerfile and container images for each one.
124+
125+
## Quick decision guide
126+
127+
Use these questions to narrow your choice:
128+
129+
1. **Do you need sandboxed code execution for untrusted or LLM-generated code?** Use Azure Container Apps dynamic sessions.
130+
1. **Do you already have a web app running on App Service?** Add MCP to your existing Azure App Service app.
131+
1. **Do you need event-driven, per-invocation tool execution?** Use Azure Functions.
132+
1. **Do you need full container control, custom languages, or a microservices architecture?** Use Azure Container Apps (standalone).
133+
1. **Not sure where to start?** Begin with Azure Container Apps (standalone), which is the most flexible default.
134+
135+
## Related content
136+
137+
- [MCP servers on Azure Container Apps](mcp-overview.md)
138+
- [Azure Container Apps overview](overview.md)
139+
- [Dynamic sessions in Azure Container Apps](sessions.md)
140+
- [Azure App Service overview](/azure/app-service/overview)
141+
- [Azure Functions overview](/azure/azure-functions/functions-overview)
142+
- [Connect an MCP server on Azure Functions to a Foundry Agent Service agent](/azure/azure-functions/functions-mcp-foundry-tools)

0 commit comments

Comments
 (0)