# Defense-in-Depth Score: Azure MCP Server

**Repo:** https://github.com/microsoft/mcp (`servers/Azure.Mcp.Server`) · **Commit:** `040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6` · **Reviewed:** 2026-10-03
**What it is:** Official Microsoft MCP server for Azure services
**Category:** Infrastructure & Ops
**Scored configuration:** Local stdio server started with 'azmcp server start' and no flags: namespace mode, all service areas, proxy tools on, elicitation on, read-only off, the user's ambient Azure credentials, telemetry on.
**Agent surface (default):** code execution yes · filesystem write yes · network egress yes · external credentials yes · persistent memory yes · untrusted input yes · third party extensions yes · sub agents yes · external communication yes

## Score: 2.7 / 10.0 (Minimal)

| # | Criterion | S | C | D | B | Raw | Cap | Score | Confidence |
|---|---|---|---|---|---|---|---|---|---|
| C1 | Identity & least privilege | L3 | L3 | L2 | L1 | 0.60 | G1 | **0.50** (alt) | High |
| C2 | Approval gates | L1 | L1 | L0 | L0 | 0.15 | C2-SELFAPPROVE | **0.15** (alt) | High |
| C3 | Tool & action scoping | L2 | L2 | L2 | L1 | 0.45 | — | **0.45** | High |
| C4 | Code-execution isolation | L0 | L0 | L0 | L0 | 0.00 | — | **0.00** | High |
| C5 | Untrusted input blast radius | L1 | L0 | L2 | L1 | 0.23 | — | **0.23** | High |
| C6 | Memory, context & configuration integrity | L0 | L1 | L0 | L0 | 0.07 | — | **0.07** | High |
| C7 | Third-party extensions | L1 | L1 | L0 | L1 | 0.20 | — | **0.20** | High |
| C8 | Secrets & sensitive-data protection | L1 | L1 | L1 | L1 | 0.25 | — | **0.25** | High |
| C9 | Audit & traceability | L1 | L2 | L0 | L1 | 0.28 | G1 | **0.28** (alt) | Medium |
| C10 | Limits & kill switch | L2 | L2 | L3 | L2 | 0.55 | — | **0.55** | High |


Azure MCP Server acts with the signed-in user's full Azure authority across every subscription and service area by default, so a hijacked host model can do whatever the user can. Its strongest control is a server-side confirmation prompt for commands marked destructive or secret, which fails closed when the client can't ask; but about 60 state-changing commands (email/SMS send, blob upload, SRE Agent memory, scheduled tasks and an auto-approving 'yolo' investigation) skip it, and proxied MCP tools bypass it entirely. Run it with --read-only and a narrow --namespace list, or as a remote server with on-behalf-of auth, wherever possible.

## Critical gaps
- In the default stdio mode every tool uses the user's ambient Azure credential, giving a hijacked session the user's full rights across all subscriptions and tenants. (ASI03, T3, LLM06; C1) — [core/Microsoft.Mcp.Core/src/Services/Azure/Authentication/CustomChainedCredential.cs:165-173](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Services/Azure/Authentication/CustomChainedCredential.cs#L165-L173); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServerStartCommand.cs:876-892](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServerStartCommand.cs#L876-L892)
- investigate_yolo is marked non-destructive (so never elicited) and auto-approves every pending SRE Agent approval in the user's name, letting the model satisfy another agent's human approval gate. (ASI09, ASI02, T10, LLM06; C2) — [tools/Azure.Mcp.Tools.SreAgent/src/Commands/Threads/ThreadsInvestigateYoloCommand.cs:17-24](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/tools/Azure.Mcp.Tools.SreAgent/src/Commands/Threads/ThreadsInvestigateYoloCommand.cs#L17-L24); [tools/Azure.Mcp.Tools.SreAgent/src/Services/SreAgentService.cs:1028-1030](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/tools/Azure.Mcp.Tools.SreAgent/src/Services/SreAgentService.cs#L1028-L1030)
- azqr and the azd MCP server are spawned as unconfined same-user processes that inherit the server's full environment, including any Azure credential env vars. (ASI05, T11, LLM05; C4) — [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/Discovery/RegistryServerProvider.cs:338-356](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/Discovery/RegistryServerProvider.cs#L338-L356); [core/Microsoft.Mcp.Core/src/Services/ProcessExecution/ExternalProcessService.cs:246-270](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Services/ProcessExecution/ExternalProcessService.cs#L246-L270)

## Criterion details

### C1 Identity & least privilege — 0.50 (high)

In its default local (stdio) mode the server signs in with whatever Azure identity the user already has (VS Code, Azure CLI, environment, managed identity or an interactive browser login) and every tool uses that one identity, so it can do anything the user can do across all their subscriptions and tenants. There is no narrower identity, no per-tool credential and no authorization check of its own beyond Azure RBAC; local helper processes (azd, azqr) inherit the full environment. When deployed as an authenticated remote HTTP server it instead exchanges each caller's token on-behalf-of that user, checks the tenant and fails closed, which is a much better design but is not the default deployment.

- **default configuration** (default; raw 0.00 → 0.00)
  - **S L0:** The stdio default builds a chained credential from the user's ambient sources (VS Code, DefaultAzureCredential chain, interactive browser) and uses it for every tool. — [core/Microsoft.Mcp.Core/src/Services/Azure/Authentication/CustomChainedCredential.cs:165-173](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Services/Azure/Authentication/CustomChainedCredential.cs#L165-L173); [core/Microsoft.Mcp.Core/src/Services/Azure/Authentication/CustomChainedCredential.cs:202-224](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Services/Azure/Authentication/CustomChainedCredential.cs#L202-L224); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServerStartCommand.cs:876-892](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServerStartCommand.cs#L876-L892) (verified)
    - *To reach the next level:* A dedicated, narrowly scoped identity (or per-tool scoped credentials) instead of the user's full ambient Azure authority.
  - **C L0:** All tools obtain tokens from the same ambient credential provider with no authorization layer between tool call and Azure; subprocesses (azqr, azd) receive the full process environment. — [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServerStartCommand.cs:370-373](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServerStartCommand.cs#L370-L373); [core/Microsoft.Mcp.Core/src/Services/ProcessExecution/ExternalProcessService.cs:246-270](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Services/ProcessExecution/ExternalProcessService.cs#L246-L270); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/Discovery/RegistryServerProvider.cs:338-356](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/Discovery/RegistryServerProvider.cs#L338-L356) (verified)
    - *To reach the next level:* An authorization check in code on every tool path, with subprocesses given only scoped credentials.
  - **D L0:** A fresh stdio install runs with the signed-in user's full Azure privileges; narrowing requires the operator to choose a different identity or pass --read-only. — [core/Microsoft.Mcp.Core/src/Areas/Server/Options/ServerStartOptions.cs:46-47](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Options/ServerStartOptions.cs#L46-L47); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServerStartCommand.cs:876-892](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServerStartCommand.cs#L876-L892) (verified)
    - *To reach the next level:* A least-privilege default (read-only or minimal role) with write requiring explicit elevation.
  - **B L0:** A hijacked session can act with the user's full Azure rights on every subscription and tenant the user can reach (60+ service areas including delete, KV secrets, SQL, email). — [servers/Azure.Mcp.Server/src/Program.cs:172-243](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/servers/Azure.Mcp.Server/src/Program.cs#L172-L243) (verified)
    - *To reach the next level:* Scope the identity to one subscription/resource group or read-only so a hijack cannot reach the whole account.
- **Remote HTTP mode with incoming Entra auth and on-behalf-of token exchange** (alt; raw 0.60, cap G1 → 0.50) ← counted
  - **S L3:** In authenticated HTTP mode the default outgoing strategy is on-behalf-of: each request's Entra token is exchanged for a downstream token for that user, with a tenant-match check. — [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServerStartCommand.cs:876-892](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServerStartCommand.cs#L876-L892); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServerStartCommand.cs:474-481](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServerStartCommand.cs#L474-L481); [core/Microsoft.Mcp.Core/src/Services/Azure/Authentication/HttpOnBehalfOfTokenCredentialProvider.cs:21-54](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Services/Azure/Authentication/HttpOnBehalfOfTokenCredentialProvider.cs#L21-L54) (verified)
    - *To reach the next level:* Intersect the requesting user's authority with a server-side least-privilege policy and downscope tokens per task.
  - **C L3:** All in-process tools and the registry HTTP servers use the OBO provider; unauthenticated requests throw (fail closed) and local-only tools are filtered out in HTTP mode. — [core/Microsoft.Mcp.Core/src/Areas/Server/RegistryServerServiceCollectionExtensions.cs:78-84](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/RegistryServerServiceCollectionExtensions.cs#L78-L84); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/BaseToolLoader.cs:451-457](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/BaseToolLoader.cs#L451-L457) (verified)
    - *To reach the next level:* Authorization against a server-side policy in addition to the user's own RBAC on every path.
  - **D L2:** OBO is the automatic default once HTTP transport is chosen with auth on, but the operator can switch to the hosting identity or disable incoming auth with a dangerously-named flag. — [core/Microsoft.Mcp.Core/src/Areas/Server/Options/ServerStartOptions.cs:60-61](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Options/ServerStartOptions.cs#L60-L61); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServerStartCommand.cs:876-892](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServerStartCommand.cs#L876-L892) (verified)
    - *To reach the next level:* Make the remote mode read-only or minimally privileged by default.
  - **B L1:** A hijacked remote session still holds the calling user's full Azure rights (write across many services) for the token lifetime. — [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServerStartCommand.cs:466-472](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServerStartCommand.cs#L466-L472) (verified)
    - *To reach the next level:* Limit OBO tokens to read or to a narrow resource scope.
- **Cap:** G1 — Opt-in mechanism: off in the scored default configuration.
- **Notes:** Score = the opt-in remote OBO mode capped by G1; the default stdio mode scores 0.

### C2 Approval gates — 0.15 (high)

The server has a real, server-side confirmation step: commands marked destructive or secret-handling trigger an MCP elicitation asking the user to approve or reject, the run is refused if the client cannot show the prompt, and command metadata defaults to destructive when a developer forgets to set it. But in the default namespace mode the tools the host sees carry no read-only or destructive hints, the prompt names the tool without showing its arguments, about 60 state-changing commands are marked non-destructive and run without confirmation (sending email and SMS, uploading local files to blob storage, writing SRE Agent memory and scheduled tasks), and proxied tools from other MCP servers (ARM, Foundry, azd) skip the gate. One tool, SRE Agent 'investigate_yolo', is marked non-destructive and automatically grants every pending SRE Agent approval request in the user's name, so the model can satisfy another agent's human-approval gate.

- **default configuration** (default; raw 0.12, cap C2-SELFAPPROVE → 0.12)
  - **S L0:** In the default namespace mode each exposed tool (e.g. 'storage') routes both read and write commands, its annotations come from group.ToolMetadata which is null outside consolidated mode, and the learn response omits per-command hints; the server-side elicitation exists but the host gets no risk signal. — [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/NamespaceToolLoader.cs:142-149](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/NamespaceToolLoader.cs#L142-L149); [core/Microsoft.Mcp.Core/src/Commands/CommandGroup.cs:17](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Commands/CommandGroup.cs#L17); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/Discovery/ConsolidatedToolDiscoveryStrategy.cs:255](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/Discovery/ConsolidatedToolDiscoveryStrategy.cs#L255); [tools/Azure.Mcp.Tools.SreAgent/src/Commands/Threads/ThreadsInvestigateYoloCommand.cs:17-24](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/tools/Azure.Mcp.Tools.SreAgent/src/Commands/Threads/ThreadsInvestigateYoloCommand.cs#L17-L24); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/BaseToolLoader.cs:255-261](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/BaseToolLoader.cs#L255-L261); [core/Microsoft.Mcp.Core/src/Areas/Server/Models/ToolCommandInfo.cs:32-44](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Models/ToolCommandInfo.cs#L32-L44) (verified)
    - *To reach the next level:* Separate read and write tools (or per-command annotations) visible to the host in the default mode, with accurate destructive/read-only labels.
  - **C L1:** Only commands flagged Destructive or Secret are gated; non-destructive writes (email/SMS send, blob upload, SRE yolo) and all proxied registry tools reach their action without the server's elicitation. — [tools/Azure.Mcp.Tools.Communication/src/Commands/Email/EmailSendCommand.cs:18-25](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/tools/Azure.Mcp.Tools.Communication/src/Commands/Email/EmailSendCommand.cs#L18-L25); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/RegistryToolLoader.cs:185-191](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/RegistryToolLoader.cs#L185-L191); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/ServerToolLoader.cs:316-320](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/ServerToolLoader.cs#L316-L320); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/NamespaceToolLoader.cs:409-416](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/NamespaceToolLoader.cs#L409-L416) (verified)
    - *To reach the next level:* Gate every state-changing and egress command, including proxied MCP tools, not only those flagged destructive.
  - **D L1:** Elicitation is on by default, fails closed when the client lacks elicitation, and is disabled only by --dangerously-disable-elicitation (logged as a warning); rated L1 because D may be at most one level above S. — [core/Microsoft.Mcp.Core/src/Areas/Server/Options/ServerStartOptions.cs:67-68](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Options/ServerStartOptions.cs#L67-L68); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/BaseToolLoader.cs:271-284](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/BaseToolLoader.cs#L271-L284); [core/Microsoft.Mcp.Core/src/Commands/ToolMetadata.cs:36](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Commands/ToolMetadata.cs#L36) (verified)
    - *To reach the next level:* Strength must rise first; the default/tamper element itself would merit L3.
  - **B L0:** A wrongly approved or ungated call can delete Azure resources, send external email/SMS, or auto-approve SRE Agent actions; there is no preview, dry-run, or rollback. — searched `rg -n -i --type cs "dry-?run|whatif|what-if"` in `core tools` → 0 hits (No preview or dry-run facility exists anywhere in core or tools.); [tools/Azure.Mcp.Tools.Communication/src/Commands/Email/EmailSendCommand.cs:18-25](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/tools/Azure.Mcp.Tools.Communication/src/Commands/Email/EmailSendCommand.cs#L18-L25) (verified)
    - *To reach the next level:* Previews/dry-runs for destructive operations and recoverable deletes.
- **--mode all (one MCP tool per command with per-command annotations)** (alt; raw 0.15, cap C2-SELFAPPROVE → 0.15) ← counted
  - **S L1:** In 'all' mode each command is its own tool carrying readOnlyHint/destructiveHint from its metadata, but several mutating commands are labelled non-destructive (investigate_yolo, storage-sync update, App Config lock set). — [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/BaseToolLoader.cs:406-414](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/BaseToolLoader.cs#L406-L414); [tools/Azure.Mcp.Tools.StorageSync/src/Commands/StorageSyncService/StorageSyncServiceUpdateCommand.cs:17-21](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/tools/Azure.Mcp.Tools.StorageSync/src/Commands/StorageSyncService/StorageSyncServiceUpdateCommand.cs#L17-L21) (verified)
    - *To reach the next level:* Correct annotations on every mutating tool.
  - **C L1:** The elicitation gate covers only commands flagged destructive/secret; proxied registry tools bypass it. — [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/CommandFactoryToolLoader.cs:183-187](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/CommandFactoryToolLoader.cs#L183-L187) (verified)
    - *To reach the next level:* Gate every state-changing command and proxied tool.
  - **D L0:** 'all' mode is opt-in; the default is namespace mode. — [core/Microsoft.Mcp.Core/src/Areas/Server/Options/ModeTypes.cs:14](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Options/ModeTypes.cs#L14) (verified)
    - *To reach the next level:* Make per-command tools the default.
  - **B L0:** Same irreversible actions (resource deletion, email/SMS) with no preview or rollback. — searched `rg -n -i --type cs "dry-?run|whatif|what-if"` in `core tools` → 0 hits (No preview or dry-run facility.) (verified)
    - *To reach the next level:* Previews/dry-runs and recoverable deletes.
- **Cap:** C2-SELFAPPROVE — investigate_yolo (labelled non-destructive in every mode) auto-approves pending SRE Agent approvals in the user's name.

### C3 Tool & action scoping — 0.45 (high)

Tools are typed commands with named options rather than a generic HTTP or shell tool, and many service endpoints pass through an allowlist-based SSRF validator (HTTPS only, Azure domain suffixes, private-IP and DNS checks). SQL and KQL tools accept raw queries filtered only by single-statement, no-comment and management-command denylists; the blob upload tool reads any local file path without containment; validation is per tool rather than one central policy. A --read-only switch and --namespace/--tool filters exist, but by default every service area including write tools is exposed against all of the user's subscriptions.

- **S L2:** Typed options plus host allowlists for endpoints, but raw SQL/KQL is passed through behind denylist filters and local file paths are unconstrained. — [core/Microsoft.Mcp.Core/src/Helpers/EndpointValidator.cs:130-155](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Helpers/EndpointValidator.cs#L130-L155); [core/Microsoft.Mcp.Core/src/Helpers/EndpointValidator.cs:283-320](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Helpers/EndpointValidator.cs#L283-L320); [tools/Azure.Mcp.Tools.Postgres/src/Validation/SqlQueryValidator.cs:50-60](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/tools/Azure.Mcp.Tools.Postgres/src/Validation/SqlQueryValidator.cs#L50-L60); [core/Microsoft.Mcp.Core/src/Validation/KqlQueryValidator.cs:27-41](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Validation/KqlQueryValidator.cs#L27-L41); [tools/Azure.Mcp.Tools.Storage/src/Services/StorageService.cs:380-390](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/tools/Azure.Mcp.Tools.Storage/src/Services/StorageService.cs#L380-L390) (verified)
  - *To reach the next level:* Parameterized/narrow query tools and resolved-path containment for local files on every tool.
- **C L2:** Endpoint validation is called in 28 tool areas and query validators in the SQL/KQL tools, but each tool opts in individually; proxied MCP tools are not validated by this server. — searched `rg -l --type cs "EndpointValidator\.Validate" -g !**/tests/**` in `tools` → 33 hits (33 non-test source files across 28 tool areas call the validator individually; no central policy layer.) (verified)
  - *To reach the next level:* A shared validation layer that every tool, including proxied ones, inherits automatically.
- **D L2:** Namespaces and individual tools are selectable and a read-only switch exists, but the default exposes every area including write tools. — [core/Microsoft.Mcp.Core/src/Areas/Server/Options/ServerStartOptions.cs:25-26](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Options/ServerStartOptions.cs#L25-L26); [core/Microsoft.Mcp.Core/src/Areas/Server/Options/ServerStartOptions.cs:46-47](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Options/ServerStartOptions.cs#L46-L47); [core/Microsoft.Mcp.Core/src/Areas/Server/Options/ModeTypes.cs:14](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Options/ModeTypes.cs#L14) (verified)
  - *To reach the next level:* Default to the read-only tool set with write tools enabled explicitly.
- **B L1:** A misused tool reaches every subscription and resource the user can access; only some tools have row caps or query length limits. — [tools/Azure.Mcp.Tools.MySql/src/Services/MySqlService.cs:21](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/tools/Azure.Mcp.Tools.MySql/src/Services/MySqlService.cs#L21); [servers/Azure.Mcp.Server/src/Program.cs:172-243](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/servers/Azure.Mcp.Server/src/Program.cs#L172-L243) (verified)
  - *To reach the next level:* Scope tools to a configured subscription/resource group and bound quantities on write tools.
- **Cap:** none

### C4 Code-execution isolation — 0.00 (high)

The server does not run a shell, but model-influenced text is interpreted in several places: raw SQL and KQL queries run on remote databases under the user's identity, the azqr tool launches an external process whose argument handling is not strict, and the azd MCP server is launched as a local subprocess. None of this runs inside any isolation boundary: subprocesses run as the user with the server's full environment, including any Azure credentials in environment variables. No sandbox, container or restricted runtime exists in the code.

- **S L0:** Subprocesses (azqr, azd) are same-user processes and queries execute directly against remote services; there is no isolation primitive. — [core/Microsoft.Mcp.Core/src/Services/ProcessExecution/ExternalProcessService.cs:246-270](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Services/ProcessExecution/ExternalProcessService.cs#L246-L270); searched `rg -n -i --type cs "seccomp|landlock|sandbox"` in `core tools` → 1 hits (Only hit is an ALZ archetype file name ('sandbox.alz_archetype_definition.json'); no isolation code.) (verified)
  - *To reach the next level:* Run external processes in an OS sandbox or container with a reduced environment.
- **C L0:** No execution path is sandboxed: ExternalProcessService, the azd stdio launch, and remote query execution all run unconfined. — [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/Discovery/RegistryServerProvider.cs:331-359](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/Discovery/RegistryServerProvider.cs#L331-L359); [tools/Azure.Mcp.Tools.Postgres/src/Commands/Database/DatabaseQueryCommand.cs:14-17](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/tools/Azure.Mcp.Tools.Postgres/src/Commands/Database/DatabaseQueryCommand.cs#L14-L17) (verified)
  - *To reach the next level:* Route every subprocess launch through an isolation layer.
- **D L0:** There is no isolation to turn on. — searched `rg -n -i --type cs "seccomp|landlock|sandbox"` in `core tools` → 1 hits (No sandbox configuration exists.) (verified)
  - *To reach the next level:* An isolation boundary enabled by default for subprocess launches.
- **B L0:** Spawned processes inherit the full server environment (Azure credential env vars included) and the user's filesystem and network. — [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/Discovery/RegistryServerProvider.cs:338-356](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/Discovery/RegistryServerProvider.cs#L338-L356); [core/Microsoft.Mcp.Core/src/Services/ProcessExecution/ExternalProcessService.cs:265-270](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Services/ProcessExecution/ExternalProcessService.cs#L265-L270) (verified)
  - *To reach the next level:* Scrub the environment and restrict filesystem/network for spawned processes.
- **Cap:** none

### C5 Untrusted input blast radius — 0.23 (high)

The server returns results as a JSON envelope (status, message, results) that separates data from metadata, but nothing marks returned content (blob data, Cosmos items, log rows, SRE Agent messages, proxied MCP results) as untrusted, and the server's own description and some error responses carry instructions to the model mixed with returned content. The read-only mode that would remove the state-change leg is off by default. If the host model is hijacked by content it reads, it can exfiltrate data through ungated tools such as email or SMS send without any human prompt, while destructive Azure operations still require the user's approval through elicitation.

- **S L1:** Results are JSON-serialized CommandResponse objects but carry no provenance or untrusted flag; tool descriptions and missing-parameter responses embed directives to the model. — [core/Microsoft.Mcp.Core/src/Models/Command/CommandResponse.cs:12-31](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Models/Command/CommandResponse.cs#L12-L31); [servers/Azure.Mcp.Server/src/appsettings.json:7](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/servers/Azure.Mcp.Server/src/appsettings.json#L7); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/NamespaceToolLoader.cs:486-502](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/NamespaceToolLoader.cs#L486-L502); searched `rg -n -i --type cs "untrusted"` in `core/Microsoft.Mcp.Core/src` → 1 hits (Single hit is a comment about W3C trace headers; no untrusted-content tagging.) (verified)
  - *To reach the next level:* Provenance/untrusted markers on returned content and no directives mixed into outputs.
- **C L0:** No source of untrusted content (storage, databases, logs, SRE Agent threads, proxied MCP results) is distinguished from other output. — [tools/Azure.Mcp.Tools.Monitor/src/Commands/Log/WorkspaceLogQueryCommand.cs:18](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/tools/Azure.Mcp.Tools.Monitor/src/Commands/Log/WorkspaceLogQueryCommand.cs#L18); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/ServerToolLoader.cs:370-372](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/ServerToolLoader.cs#L370-L372) (verified)
  - *To reach the next level:* Tag every result from data-plane reads and proxied servers as untrusted.
- **D L2:** The JSON envelope is always on and cannot be changed by content, but the read-only mode that drops a Rule-of-Two leg is off by default (capped one level above S). — [core/Microsoft.Mcp.Core/src/Areas/Server/Options/ServerStartOptions.cs:46-47](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Options/ServerStartOptions.cs#L46-L47) (verified)
  - *To reach the next level:* Ship read-only or no-egress as the default mode.
- **B L1:** A hijacked host can read data and send it out via email/SMS/event publishing with no human step; destructive operations still need elicitation approval. — [tools/Azure.Mcp.Tools.Communication/src/Commands/Email/EmailSendCommand.cs:18-25](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/tools/Azure.Mcp.Tools.Communication/src/Commands/Email/EmailSendCommand.cs#L18-L25); [tools/Azure.Mcp.Tools.Communication/src/Commands/Sms/SmsSendCommand.cs:15-22](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/tools/Azure.Mcp.Tools.Communication/src/Commands/Sms/SmsSendCommand.cs#L15-L22); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/BaseToolLoader.cs:255-261](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/BaseToolLoader.cs#L255-L261) (verified)
  - *To reach the next level:* Require approval for every egress/communication tool so exfiltration also needs a human.
- **Cap:** none

### C6 Memory, context & configuration integrity — 0.07 (high)

The server itself keeps no long-term memory (caches are in-memory) and loads its settings only from its install directory, environment variables and command-line flags, never from the user's workspace. However, it exposes SRE Agent tools that write to that agent's persistent knowledge base, common prompts and scheduled tasks; these are marked non-destructive, so they run without any confirmation, and whatever the model writes there is retrieved by the SRE Agent in later investigations for every user of that agent and can trigger its actions. Poisoned content read in one session can therefore become a durable instruction for another agent.

- **S L0:** The model can write arbitrary documents into the SRE Agent RAG knowledge base and create scheduled tasks/prompts with no validation or approval. — [tools/Azure.Mcp.Tools.SreAgent/src/Commands/Docs/MemoriesAddCommand.cs:17-21](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/tools/Azure.Mcp.Tools.SreAgent/src/Commands/Docs/MemoriesAddCommand.cs#L17-L21); [tools/Azure.Mcp.Tools.SreAgent/src/Commands/ScheduledTasks/ScheduledTasksCreateCommand.cs:17-21](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/tools/Azure.Mcp.Tools.SreAgent/src/Commands/ScheduledTasks/ScheduledTasksCreateCommand.cs#L17-L21) (verified)
  - *To reach the next level:* Gate or validate writes to agent memory/prompts/scheduled tasks (human approval or source restrictions).
- **C L1:** Server configuration is protected (loaded from the install directory and env, not the workspace), but the agent-memory, common-prompt and scheduled-task paths are not. — [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServiceCollectionExtensions.cs:305-315](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServiceCollectionExtensions.cs#L305-L315); [tools/Azure.Mcp.Tools.SreAgent/src/Commands/CommonPrompts/CommonPromptsCreateCommand.cs:17-21](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/tools/Azure.Mcp.Tools.SreAgent/src/Commands/CommonPrompts/CommonPromptsCreateCommand.cs#L17-L21) (verified)
  - *To reach the next level:* Control every persistent write path, including downstream agent memory.
- **D L0:** Entries written to the SRE Agent knowledge base are shared by every user of that agent; the tool offers no per-user namespace. — [tools/Azure.Mcp.Tools.SreAgent/src/Commands/Docs/MemoriesAddCommand.cs:17-21](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/tools/Azure.Mcp.Tools.SreAgent/src/Commands/Docs/MemoriesAddCommand.cs#L17-L21) (verified)
  - *To reach the next level:* Per-user or per-session namespaces for written memory.
- **B L0:** Poisoned memory or a scheduled task persists across sessions and users and drives the SRE Agent's later investigations and actions. — [servers/Azure.Mcp.Server/src/Program.cs:237](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/servers/Azure.Mcp.Server/src/Program.cs#L237); [tools/Azure.Mcp.Tools.SreAgent/src/Commands/ScheduledTasks/ScheduledTasksCreateCommand.cs:17-21](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/tools/Azure.Mcp.Tools.SreAgent/src/Commands/ScheduledTasks/ScheduledTasksCreateCommand.cs#L17-L21) (verified)
  - *To reach the next level:* Require human review before persisted entries take effect.
- **Cap:** none

### C7 Third-party extensions — 0.20 (high)

Besides its own tools, the server proxies other MCP servers listed in a registry file compiled into the binary: Microsoft Learn docs, Foundry and ARM remote servers, and the azd CLI, which it launches locally from whatever 'azd' is first on PATH with only a minimum-version check. This proxying is on by default with no consent prompt, nothing is pinned or integrity-checked, and remote tool definitions are fetched at runtime without detecting changes. The workspace cannot add servers, but the launched azd process runs as the user with the server's entire environment.

- **S L1:** Extension sources are fixed in an embedded registry, but azd is resolved from PATH with only a minVersion floor and remote tool lists are used as fetched; no pinning or integrity checks. — [servers/Azure.Mcp.Server/src/Resources/registry.json:8-13](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/servers/Azure.Mcp.Server/src/Resources/registry.json#L8-L13); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/Discovery/RegistryServerProvider.cs:119-132](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/Discovery/RegistryServerProvider.cs#L119-L132); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/Discovery/RegistryDiscoveryStrategy.cs:11](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/Discovery/RegistryDiscoveryStrategy.cs#L11) (verified)
  - *To reach the next level:* Pin extension versions (or verify hashes/signatures) and detect tool-definition changes.
- **C L1:** Only the stdio server gets a version floor; HTTP remote servers get HTTPS/OAuth-scope checks but no verification of tool definitions. — [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/Discovery/RegistryServerProvider.cs:64-90](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/Discovery/RegistryServerProvider.cs#L64-L90) (verified)
  - *To reach the next level:* Verification for every extension type, including remote tool definitions.
- **D L0:** Proxy tools are enabled automatically by default (DisableProxyTools=false) with no consent or display of what will run. — [core/Microsoft.Mcp.Core/src/Areas/Server/Options/ServerStartOptions.cs:131-132](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Options/ServerStartOptions.cs#L131-L132); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServiceCollectionExtensions.cs:66-70](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServiceCollectionExtensions.cs#L66-L70) (verified)
  - *To reach the next level:* Leave proxied servers off by default and show the exact command/package when enabling one.
- **B L1:** azd runs as a separate process, same user, with the full environment merged from the server process. — [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/Discovery/RegistryServerProvider.cs:338-356](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/Discovery/RegistryServerProvider.cs#L338-L356) (verified)
  - *To reach the next level:* Launch extensions with a scrubbed environment containing only their own configuration.
- **Cap:** none

### C8 Secrets & sensitive-data protection — 0.25 (high)

The server stores no API keys of its own and relies on Azure Identity for tokens. Secret-returning tools (Key Vault secret get, app settings) are flagged and require user confirmation before secret values are sent to the model, and SRE Agent connector secrets are redacted in results. Usage telemetry is on by default and goes to Microsoft; it records tool names, parameter names (not values), the subscription ID and exception stack traces. There is no general redaction layer for logs or tool results, and spawned processes inherit the full environment.

- **S L1:** Masking exists only in specific paths (SRE Agent connector secrets, parameter-name-only telemetry); no general redaction for logs or model-bound results. — [tools/Azure.Mcp.Tools.SreAgent/src/Services/SreAgentService.cs:775](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/tools/Azure.Mcp.Tools.SreAgent/src/Services/SreAgentService.cs#L775); [core/Microsoft.Mcp.Core/src/Helpers/McpHelper.cs:78-84](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Helpers/McpHelper.cs#L78-L84); searched `rg -n -i --type cs "redact"` in `core/Microsoft.Mcp.Core/src` → 0 hits (No shared redaction helper in core.) (verified)
  - *To reach the next level:* Redaction before logs and model-bound messages on all major paths.
- **C L1:** Telemetry is content-minimised and secret tools are consent-gated, but logs, error messages and subprocess environments are not filtered. — [core/Microsoft.Mcp.Core/src/Extensions/OpenTelemetryExtensions.cs:135](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Extensions/OpenTelemetryExtensions.cs#L135); [core/Microsoft.Mcp.Core/src/Services/ProcessExecution/ExternalProcessService.cs:246-270](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Services/ProcessExecution/ExternalProcessService.cs#L246-L270) (verified)
  - *To reach the next level:* Protect logs, error paths and subprocess environments as well.
- **D L1:** Telemetry to a Microsoft-owned App Insights instance is on by default (content-light: tool/parameter names, subscription GUID, stack traces); verbose support logging is behind a dangerously-named flag. — [core/Microsoft.Mcp.Core/src/Extensions/OpenTelemetryExtensions.cs:26](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Extensions/OpenTelemetryExtensions.cs#L26); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServiceCollectionExtensions.cs:292](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServiceCollectionExtensions.cs#L292); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/Runtime/McpRuntime.cs:61-73](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/Runtime/McpRuntime.cs#L61-L73) (verified)
  - *To reach the next level:* Make telemetry opt-in.
- **B L1:** Long-lived Key Vault secrets can be returned to the model (after consent) and env-var credentials reach every subprocess. — [tools/Azure.Mcp.Tools.KeyVault/src/Commands/Secret/SecretGetCommand.cs:17-23](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/tools/Azure.Mcp.Tools.KeyVault/src/Commands/Secret/SecretGetCommand.cs#L17-L23) (verified)
  - *To reach the next level:* Keep secrets out of model context by design (handles) and scrub subprocess env.
- **Cap:** none

### C9 Audit & traceability — 0.28 (medium)

By default the operator gets no local record of tool calls: the stdio host logs only to an in-process EventSource, and per-call logging is at Trace level. Each call does produce a structured telemetry span (tool, parameter names, status, subscription), but by default it goes only to Microsoft. Operators can opt in to sending these spans and logs to their own Application Insights or an OTLP collector, which gives an off-host, correlated record, though it never includes argument values and export is best-effort.

- **default configuration** (default; raw 0.12 → 0.12)
  - **S L0:** Default stdio logging goes only to EventSource; per-call execution logs are LogTrace; the per-call spans are exported only to Microsoft. — [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServerStartCommand.cs:340-367](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServerStartCommand.cs#L340-L367); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/NamespaceToolLoader.cs:458](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/NamespaceToolLoader.cs#L458) (verified)
    - *To reach the next level:* A structured per-call record (tool, arguments, status, time) available to the operator by default.
  - **C L1:** Elicitation outcomes are logged at Information and telemetry spans cover in-process and proxied calls, but nothing reaches the operator by default. — [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/BaseToolLoader.cs:345-357](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/BaseToolLoader.cs#L345-L357); [core/Microsoft.Mcp.Core/src/Extensions/OpenTelemetryExtensions.cs:98-107](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Extensions/OpenTelemetryExtensions.cs#L98-L107) (verified)
    - *To reach the next level:* Record every tool call and approval decision to an operator-visible sink.
  - **D L1:** The only default record is vendor telemetry, which the operator can't read and can turn off with an env var. — [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServiceCollectionExtensions.cs:292](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServiceCollectionExtensions.cs#L292) (verified)
    - *To reach the next level:* An operator-readable record written by default outside the model's control.
  - **B L0:** If no exporter is configured, tool calls proceed with no operator record; nothing fails closed. — [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServerStartCommand.cs:340-367](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ServerStartCommand.cs#L340-L367) (verified)
    - *To reach the next level:* Durable, per-action records that are flushed before the action completes.
- **Opt-in export to the operator's Application Insights / OTLP collector** (alt; raw 0.28, cap G1 → 0.28) ← counted
  - **S L1:** Spans per tool call with tool id, parameter names, status, subscription and W3C trace context, shipped off-host; argument values are never recorded. — [core/Microsoft.Mcp.Core/src/Extensions/OpenTelemetryExtensions.cs:88-93](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Extensions/OpenTelemetryExtensions.cs#L88-L93); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/Runtime/McpRuntime.cs:142-150](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/Runtime/McpRuntime.cs#L142-L150) (verified)
    - *To reach the next level:* Include argument values (redacted where sensitive) and actor/approver attribution.
  - **C L2:** Spans cover in-process and proxied tool calls; elicitation decisions appear as logs. — [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/NamespaceToolLoader.cs:370-373](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/NamespaceToolLoader.cs#L370-L373); [core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/ServerToolLoader.cs:243](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Commands/ToolLoading/ServerToolLoader.cs#L243) (verified)
    - *To reach the next level:* Record config changes and credential use too, and approvals as structured events.
  - **D L0:** Export to the operator's own sink is opt-in via environment variable. — [core/Microsoft.Mcp.Core/src/Extensions/OpenTelemetryExtensions.cs:88-93](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Extensions/OpenTelemetryExtensions.cs#L88-L93) (verified)
    - *To reach the next level:* On by default.
  - **B L1:** Export uses the batched Azure Monitor / OTLP exporters, so records are best-effort and flushed late (library behaviour). — [core/Microsoft.Mcp.Core/src/Extensions/OpenTelemetryExtensions.cs:88-93](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Extensions/OpenTelemetryExtensions.cs#L88-L93) (inferred)
    - *To reach the next level:* Per-action durable records.
- **Cap:** G1 — Opt-in mechanism: off in the scored default configuration.

### C10 Limits & kill switch — 0.55 (high)

The server enforces some bounds on its own work: Azure SDK retries, delays and network timeouts are clamped to hard ceilings (10 retries, 60 s delay, 300 s timeout) that callers can't exceed unless the operator passes a dangerously-named flag, external processes time out after 300 s and are killed on timeout or cancellation, and query tools cap length and rows. There is no rate limiting on side-effecting tools and no overall cap on how many calls run, and long-running Azure operations continue in Azure after a call is cancelled.

- **S L2:** Server-enforced timeouts and retry ceilings, process kill on timeout/cancel, query length and row caps on some tools. — [core/Azure.Mcp.Core/src/Services/Azure/Helpers/AzureHelper.cs:20-22](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Azure.Mcp.Core/src/Services/Azure/Helpers/AzureHelper.cs#L20-L22); [core/Microsoft.Mcp.Core/src/Services/ProcessExecution/ExternalProcessService.cs:132-137](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Services/ProcessExecution/ExternalProcessService.cs#L132-L137); [core/Microsoft.Mcp.Core/src/Services/ProcessExecution/ExternalProcessService.cs:280-283](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Services/ProcessExecution/ExternalProcessService.cs#L280-L283); searched `rg -n --type cs "RateLimiter|AddRateLimiter"` in `core` → 0 hits (No rate limiting anywhere in core.) (verified)
  - *To reach the next level:* Caps on every operation plus concurrency or rate limits.
- **C L2:** Retry/timeout clamps apply to all Azure SDK clients via the shared helper and to subprocesses; proxied server calls and LROs rely on others' limits. — [core/Azure.Mcp.Core/src/Services/Azure/Helpers/AzureHelper.cs:119-139](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Azure.Mcp.Core/src/Services/Azure/Helpers/AzureHelper.cs#L119-L139) (verified)
  - *To reach the next level:* Bounds that also cover proxied calls and limits on concurrent work.
- **D L3:** Defaults are sensible and model-supplied retry options are clamped to hard ceilings; only an operator dangerously-named flag removes them. — [core/Microsoft.Mcp.Core/src/Areas/Server/Options/ServerStartOptions.cs:99-100](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Areas/Server/Options/ServerStartOptions.cs#L99-L100); [core/Azure.Mcp.Core/src/Services/Azure/Helpers/AzureHelper.cs:125-129](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Azure.Mcp.Core/src/Services/Azure/Helpers/AzureHelper.cs#L125-L129) (verified)
  - *To reach the next level:* Hard ceilings that no configuration can exceed.
- **B L2:** Per-call ceilings are moderate; cancellation stops waiting and kills subprocesses but Azure long-running operations continue server-side. — [core/Microsoft.Mcp.Core/src/Services/ProcessExecution/ExternalProcessService.cs:167-176](https://github.com/microsoft/mcp/blob/040fb3ee4a31d55463b53ae0b0607b6f1a02c3e6/core/Microsoft.Mcp.Core/src/Services/ProcessExecution/ExternalProcessService.cs#L167-L176) (verified)
  - *To reach the next level:* Cancellation of outstanding remote operations and tighter per-session ceilings.
- **Cap:** none

## Rule-of-Two check
[A] untrusted input: Data-plane reads such as log queries, Cosmos items, blob data, SRE Agent threads and proxied MCP results (tools/Azure.Mcp.Tools.Monitor/src/Commands/Log/WorkspaceLogQueryCommand.cs:18) · [B] sensitive data/systems: Key Vault secrets and all resources reachable with the user's Azure identity (tools/Azure.Mcp.Tools.KeyVault/src/Commands/Secret/SecretGetCommand.cs:23) · [C] state change / egress: Ungated email/SMS send and blob upload, plus elicited deletes and writes (tools/Azure.Mcp.Tools.Communication/src/Commands/Email/EmailSendCommand.cs:22) · Same default session? Yes

## Highest-impact improvements
1. Mark every state-changing or egress command (email/SMS send, blob upload, SRE Agent writes, investigate_yolo, storage-sync updates) as requiring elicitation, and remove or gate the auto-approve 'yolo' tool. — C2 C L1→L2, +0.075 before caps (Playbook 5)
2. Expose per-command readOnly/destructive annotations in namespace mode (e.g. split read and write namespace tools and include hints in learn output). — C2 S L0→L2, +0.150 before caps (Playbook 5)
3. Default to --read-only, requiring an explicit flag to enable write tools. — C3 D L2→L3, +0.050 before caps (Playbook 3)
4. Launch azd/azqr with a scrubbed environment containing only what each needs. — C7 B L1→L2, +0.050 before caps (Playbook 3)
5. Write a structured local audit log of every tool call and elicitation decision by default. — C9 S L0→L2, +0.150 before caps (Playbook 1 step 3)

## Re-audit log
- No changes.

## Limitations
- Static source review of the pinned commit only; nothing was executed, installed, or probed.
- The subpath servers/Azure.Mcp.Server contains only the entry point and resources; the server's tools and runtime are compiled in from core/ and tools/Azure.* (see Azure.Mcp.Server.csproj), so those directories were reviewed as part of the server. Fabric.* tool projects are not registered by Program.cs and were not scored.
- Command metadata was surveyed across all 473 [CommandMetadata] declarations with a script, but individual service implementations (60+ areas) were sampled, not read in full.
- Remote proxied MCP servers (learn.microsoft.com, mcp.ai.azure.com, mcp.management.azure.com) and the azd CLI are outside this repository; their own gates and annotations were not examined.
- No reviewer-injection attempts were found in the repository.
