Chapter 5 of 6 · Implementation

MCP and tool security

Runtime assurance 3.5 hours in a non-production POC

Chapter 5 of 6

Implement#

1. Load the approved definitions#

Complete sandbox.json, agent-mcp-binding.json, the security evaluation, and the threat model. The approved backend role assignment must already be active.

PowerShell

$approvedSubscriptionId = $env:AZURE_SUBSCRIPTION_ID
$artifactRoot = (Resolve-Path .\artifacts).Path
$environment = Get-Content (Join-Path $artifactRoot "environments\sandbox.json") -Raw | ConvertFrom-Json
$binding = Get-Content (Join-Path $artifactRoot "governance\agent-mcp-binding.json") -Raw | ConvertFrom-Json

Bash

approved_subscription_id="${AZURE_SUBSCRIPTION_ID:?Set AZURE_SUBSCRIPTION_ID.}"
artifact_root="$(cd ./artifacts && pwd)"
environment_path="$artifact_root/environments/sandbox.json"
binding_path="$artifact_root/governance/agent-mcp-binding.json"

2. Run preflight and inspect the preview#

PowerShell

.\scripts\preflight.ps1 -ApprovedSubscriptionId $approvedSubscriptionId

Bash

./scripts/preflight.sh --approved-subscription-id "$approved_subscription_id"

Preflight checks the implementation files and sentinels, the approved subscription and APIM scope, the supported tier, identities, exact backend read assignment, payload-free diagnostics, Foundry project, name collision, Bicep build, and ARM what-if.

Continue only when the preview is limited to the five MCP control values, MCP API, its one tool, policy, and diagnostic.

3. Deploy the APIM control#

PowerShell

.\scripts\deploy.ps1 -ApprovedSubscriptionId $approvedSubscriptionId

Bash

./scripts/deploy.sh --approved-subscription-id "$approved_subscription_id"

The deployment creates the marked policy-catalog-mcp API, get_policy, five nonsecret named values, the inbound and managed-identity policy, and the payload-free diagnostic. Troubleshoot with status, error type, correlation, approved APIM tracing, and governed backend diagnostics. Do not turn on payload logging.

4. Update API Center#

After APIM synchronization creates one policy-catalog-mcp entry, the API program owner applies the APIM gateway process and records the owner, classification, consumer, residency, risk, evaluation, review, and expiry metadata.

Stop on a duplicate, missing runtime owner, or metadata broader than the threat model. The candidate cannot be enabled until this entry is complete.

5. Create the Foundry connection#

PowerShell

$env:session09_MCP_SERVER_URL = "https://$($environment.apiManagementName).azure-api.net/$($environment.mcpServerPath)/mcp"
$projectUrl = "https://$($environment.foundryAccountName).services.ai.azure.com/api/projects/$($environment.foundryProjectName)"
azd ai project set $projectUrl
azd ai connection create $binding.projectConnectionName `
  --kind remote-tool `
  --target $env:session09_MCP_SERVER_URL `
  --auth-type agentic-identity `
  --audience $environment.mcpAudience

Bash

readarray -t foundry_values < <(ENVIRONMENT_PATH="$environment_path" BINDING_PATH="$binding_path" python3 - <<'PY'
import json, os, pathlib
e = json.loads(pathlib.Path(os.environ["ENVIRONMENT_PATH"]).read_text())
b = json.loads(pathlib.Path(os.environ["BINDING_PATH"]).read_text())
print(f"https://{e['apiManagementName']}.azure-api.net/{e['mcpServerPath']}/mcp")
print(f"https://{e['foundryAccountName']}.services.ai.azure.com/api/projects/{e['foundryProjectName']}")
print(b["projectConnectionName"])
print(e["mcpAudience"])
PY
)
export session09_MCP_SERVER_URL="${foundry_values[0]}"
azd ai project set "${foundry_values[1]}"
azd ai connection create "${foundry_values[2]}" \
  --kind remote-tool \
  --target "$session09_MCP_SERVER_URL" \
  --auth-type agentic-identity \
  --audience "${foundry_values[3]}"

If the installed azd ai surface differs, use the current Foundry portal flow with the same name, target, agentic identity, and audience. Do not use a key or pasted bearer token.

6. Create an unpinned candidate#

Copy the pinned governed agent definition. Keep its model, RAI policy, instructions, temperature, endpoint authorization, and identity behavior. Remove the direct OpenAPI tool, add the MCP connection, set allowed_tools to get_policy, and set require_approval to always.

Save without changing the stable selector. Show the candidate version ID and the prior stable selector to the release owner.

Session 08

MCP and tool security slide deck