Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Build the runtime image
FROM mcr.microsoft.com/dotnet/runtime-deps:10.0-alpine AS runtime

# Install libsecret so MSAL token-cache persistence works on Alpine Linux.
# Without it, InteractiveBrowserCredential throws MsalCachePersistenceException
# in headless environments (Docker, WSL without a running keyring daemon).
RUN apk add --no-cache libsecret

# Add build argument for publish directory
ARG PUBLISH_DIR

Expand Down Expand Up @@ -30,8 +35,10 @@ RUN if [ ! -f $EXECUTABLE_NAME ]; then \
echo "ERROR: $EXECUTABLE_NAME executable does not exist" && exit 1; \
fi

# Copy the server binary to a known location and make it executable
# Copy the server binary to a known location and make it executable.
# Also chmod the original executable name so users can invoke it directly
# inside the container (e.g. via --entrypoint or exec).
COPY ${PUBLISH_DIR}/${EXECUTABLE_NAME} server-binary
RUN chmod +x server-binary && test -x server-binary
RUN chmod +x server-binary ${EXECUTABLE_NAME} && test -x server-binary && test -x ${EXECUTABLE_NAME}

ENTRYPOINT ["./server-binary", "server", "start"]
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,11 @@ private static TokenCredential CreateCredential(string? tenantId, ILogger<Custom

if (shouldAddBrowserFallback)
{
creds.Add(CreateBrowserCredential(tenantId, authRecord));
// Wrap in SafeTokenCredential so that MsalCachePersistenceException
// (libsecret missing / no keyring daemon in Docker/headless environments)
// is converted to CredentialUnavailableException, allowing the chain to
// continue rather than propagating an unhandled exception.
creds.Add(new SafeTokenCredential(CreateBrowserCredential(tenantId, authRecord), "InteractiveBrowserCredential"));
}

return new ChainedTokenCredential([.. creds]);
Expand Down
31 changes: 31 additions & 0 deletions docs/Authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,36 @@ az account show

The default credential chain includes `InteractiveBrowserCredential` as a fallback for development convenience.

### Docker Containers

When running Azure MCP Server in a Docker container (e.g., `docker run -i --rm --env-file .env mcr.microsoft.com/azure-sdk/azure-mcp:latest`), use a non-interactive credential to avoid requiring a browser or terminal.

**Recommended: Service Principal via EnvironmentCredential**

Set `AZURE_TOKEN_CREDENTIALS=prod` together with your service principal credentials in your `.env` file:

```env
AZURE_TOKEN_CREDENTIALS=prod
AZURE_TENANT_ID=<your-tenant-id>
AZURE_CLIENT_ID=<your-app-client-id>
AZURE_CLIENT_SECRET=<your-app-client-secret>
```

`AZURE_TOKEN_CREDENTIALS=prod` restricts the credential chain to `EnvironmentCredential`, `WorkloadIdentityCredential`, and `ManagedIdentityCredential`. This skips `InteractiveBrowserCredential` entirely, preventing errors caused by a missing GUI or keyring daemon inside the container.

> [!IMPORTANT]
> Omitting `AZURE_TOKEN_CREDENTIALS=prod` in a Docker container causes the server to fall back to `InteractiveBrowserCredential`, which tries to initialize the MSAL token cache via `libsecret`. If no keyring daemon is running in the container, authentication fails with `Persistence check failed` / `libsecret-1.so.0` not found errors even though your service principal credentials are correct.

**Alternative: Managed Identity (for Azure-hosted containers)**

When running on Azure Container Instances, AKS, or App Service with a managed identity, set:

```env
AZURE_TOKEN_CREDENTIALS=prod
# For user-assigned managed identity only:
AZURE_CLIENT_ID=<your-managed-identity-client-id>
```

### CI/CD Pipelines

For automated builds and deployments, set the following environment variables:
Expand All @@ -93,6 +123,7 @@ For automated builds and deployments, set the following environment variables:
export AZURE_CLIENT_ID="<pipeline-sp-client-id>"
export AZURE_CLIENT_SECRET="<pipeline-sp-secret>"
export AZURE_TENANT_ID="<your-tenant-id>"
export AZURE_TOKEN_CREDENTIALS=prod
```

## Authentication Scenarios in Enterprise Environments
Expand Down