diff --git a/samples/applications/azure-sql-mcp/.gitignore b/samples/applications/azure-sql-mcp/.gitignore
new file mode 100644
index 0000000000..6aab378106
--- /dev/null
+++ b/samples/applications/azure-sql-mcp/.gitignore
@@ -0,0 +1,3 @@
+.azure/
+dab-config.generated.json
+infra/main.json
diff --git a/samples/applications/azure-sql-mcp/README.md b/samples/applications/azure-sql-mcp/README.md
new file mode 100644
index 0000000000..4794420a9d
--- /dev/null
+++ b/samples/applications/azure-sql-mcp/README.md
@@ -0,0 +1,205 @@
+
+
+# Azure SQL MCP Server with Connector Namespace
+
+Deploy a hosted Azure SQL Model Context Protocol (MCP) server to [Azure Connector Namespace](https://learn.microsoft.com/azure/logic-apps/connector-namespace/connector-namespace-hosted-mcp). The sample provisions Azure SQL Database, exposes a `BlogPosts` table through Data API Builder MCP, and configures managed identity access so MCP clients such as GitHub Copilot in Visual Studio Code can query the database.
+
+### Contents
+
+[About this sample](#about-this-sample)
+[Before you begin](#before-you-begin)
+[Run this sample](#run-this-sample)
+[Sample details](#sample-details)
+[Clean up](#clean-up)
+[Related links](#related-links)
+
+
+
+## About this sample
+
+- **Applies to:** Azure SQL Database
+- **Key features:** Azure Connector Namespace, hosted MCP server, Data API Builder, managed identity, Application Insights
+- **Workload:** AI agent data access
+- **Programming Language:** Bicep, PowerShell, Bash, JSON
+
+This sample deploys a hosted `mcp-sql` server in Azure Connector Namespace. The hosted MCP server uses Data API Builder configuration to expose a SQL table through MCP tools. The SQL database is seeded with a `dbo.BlogPosts` table containing links to Microsoft Learn and .NET Blog posts. For more background, see [Hosted MCP servers in Azure Connector Namespace](https://learn.microsoft.com/azure/logic-apps/connector-namespace/connector-namespace-hosted-mcp).
+
+
+
+## Before you begin
+
+To run this sample, you need the following prerequisites.
+
+**Software prerequisites:**
+
+1. [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) (`az`)
+1. [Azure Developer CLI](https://learn.microsoft.com/azure/developer/azure-developer-cli/install-azd) (`azd`)
+1. PowerShell 7+ on Windows, or Bash on Linux/macOS
+
+**Azure prerequisites:**
+
+1. An Azure subscription with permissions to create resource groups and resources.
+1. Permission to create Azure SQL Database, Application Insights, Log Analytics workspace, and Connector Namespace resources.
+1. Permission to create an Azure SQL Microsoft Entra administrator for the signed-in user.
+
+
+
+## Run this sample
+
+From this folder:
+
+```bash
+azd auth login
+azd init # only needed once; select "Use code in current directory"
+azd up --location eastasia
+```
+
+These commands follow the [hosted MCP SQL quickstart](https://learn.microsoft.com/azure/logic-apps/connector-namespace/hosted-mcp-quickstart?pivots=sql) pattern and use this sample's Bicep templates and post-provision hooks.
+
+When prompted:
+
+- **Environment name:** Pick any name, for example `mcp-dev`.
+- **Location:** Choose **East Asia** (`eastasia`).
+- **`deployerLoginName` infrastructure parameter:** Enter your Azure sign-in email or user principal name, for example `user@contoso.com`. If you don't know the value, run the following command in a different terminal instance and use the result:
+
+ ```bash
+ az account show --query user.name -o tsv
+ ```
+
+The `deployerLoginName` value is used to create the Azure SQL server with Microsoft Entra-only authentication and set you as the SQL Entra admin.
+
+### Connect from Visual Studio Code
+
+After `azd up` completes, the MCP endpoint URL is printed. Add it to VS Code using the UI:
+
+1. In VS Code, open the Command Palette:
+ - Windows/Linux: Ctrl+Shift+P
+ - macOS: Cmd+Shift+P
+1. Run **MCP: Add Server**.
+1. Choose **HTTP** as the server type.
+1. Paste the MCP endpoint URL printed by `azd up`.
+1. Enter a server name, for example `sql-mcp`.
+1. Choose whether to save the server in user settings or workspace settings.
+1. Start the `sql-mcp` server when VS Code prompts you.
+
+VS Code prompts you to sign in with Microsoft. Then use Copilot Chat to query your database, for example: *"List the blog posts in the database."*
+
+
+
+## Sample details
+
+### Architecture
+
+```
+┌─────────────────────────────────────┐
+│ Connector Namespace │
+│ (Microsoft.Web/connectorGateways) │
+│ │
+│ ┌───────────────────────────────┐ │
+│ │ Hosted SQL MCP Server │ │
+│ │ (Data API Builder) │ │
+│ │ │ │
+│ │ SAMI ──► Azure SQL DB │ │
+│ └───────────────────────────────┘ │
+└─────────────────────────────────────┘
+ ▲
+ │ MCP (HTTP + SSE)
+ │
+ VS Code / Copilot / MCP Client
+```
+
+### Resources deployed
+
+| Resource | Purpose |
+|----------|---------|
+| **Resource Group** | Container for all deployed resources |
+| **Azure SQL Server** | Entra-only auth, with you as SQL admin |
+| **Azure SQL Database** | Basic SKU database with a `BlogPosts` sample table used by the MCP server |
+| **SQL Firewall Rules** | Allows Azure services/resources, Azure Portal Query Editor, and your public IP for setup |
+| **Log Analytics Workspace** | Stores Application Insights telemetry |
+| **Application Insights** | Collects telemetry from the hosted MCP server |
+| **Connector Namespace** | Hosts MCP servers with system-assigned managed identity |
+| **Hosted SQL MCP Server** | `mcp-sql` server config on the namespace |
+| **MCP Access Policy** | Grants you access to invoke MCP tools |
+
+Resource names use the pattern `--` where possible, for example `sql-mcp-dev-a1b2c3d4`. The suffix is deterministic for the subscription, environment name, and location so names are readable and stable across redeployments.
+
+### What `azd up` does
+
+| Step | Action |
+|------|--------|
+| **Provision** | Deploys Azure SQL, SQL firewall rules, Log Analytics, Application Insights, Connector Namespace, hosted `mcp-sql`, and MCP access policy. |
+| **Post-provision** | Allows your public IP through the SQL firewall, creates and seeds `dbo.BlogPosts`, creates the Connector Namespace managed identity SQL user, grants SQL permissions, generates `dab-config.generated.json`, and prints the MCP endpoint plus Azure Portal resource group link. |
+
+The hosted MCP server receives:
+
+- the included `dab-config.json` as `properties.hostedMcpServer.configuration.configFile`
+- the generated SQL connection string as `SQL_CONNECTION_STRING`
+- the Application Insights connection string as `APPLICATIONINSIGHTS_CONNECTION_STRING`
+
+No SQL or Application Insights connection string is checked in.
+
+This sample includes a ready-to-use `dab-config.json`. If you want to create or customize a Data API Builder configuration from scratch, install the DAB CLI and use it to generate a config file. For more information, see [Install the Data API Builder CLI](https://learn.microsoft.com/azure/data-api-builder/command-line/install).
+
+For details about the hosted MCP server resource model and supported server types, see [Hosted MCP servers in Azure Connector Namespace](https://learn.microsoft.com/azure/logic-apps/connector-namespace/connector-namespace-hosted-mcp). For a walkthrough focused on the SQL hosted MCP server, see [Hosted MCP server quickstart for SQL](https://learn.microsoft.com/azure/logic-apps/connector-namespace/hosted-mcp-quickstart?pivots=sql).
+
+### Inspect resources in Azure Portal
+
+After deployment, the post-provision output includes a link to the Azure resource group in the Azure Portal. Use that page to inspect the SQL server, Application Insights resource, Log Analytics workspace, Connector Namespace, and hosted MCP server.
+
+To allow additional users to connect to the MCP server:
+
+1. Open the deployed **Connector Namespace** resource in the Azure Portal.
+1. Open the hosted MCP server configuration, for example `sql-mcp`.
+1. Add an access policy for each additional user or group that should be allowed to invoke the MCP server.
+
+### Sample data
+
+The post-provision hook creates and seeds `dbo.BlogPosts` with these entries:
+
+| Title | Source |
+|-------|--------|
+| Hosted MCP servers in Azure Connector Namespace | Microsoft Learn |
+| Durable Workflows in Microsoft Agent Framework | .NET Blog |
+
+### SQL firewall access
+
+The deployment configures two SQL firewall paths:
+
+| Rule | When | Purpose |
+|------|------|---------|
+| `AllowAzureServices` | During Bicep provisioning | Allows Azure services/resources, including Azure Portal Query Editor, to reach the SQL server. |
+| `AllowDeployerIp` | During post-provision | Detects your current public IP and allows your local machine to seed and query the database. |
+
+If SQL reports a different blocked client IP during post-provision, the script adds that IP and retries.
+
+
+
+## Clean up
+
+Using azd:
+
+```bash
+azd down --purge
+```
+
+Or with Azure CLI:
+
+```bash
+# Replace with your azd environment name.
+az group delete --name rg- --yes --no-wait
+
+# Optional: remove the subscription-scope deployment record.
+az deployment sub delete --name
+```
+
+
+
+## Related links
+
+- [Hosted MCP servers in Azure Connector Namespace](https://learn.microsoft.com/azure/logic-apps/connector-namespace/connector-namespace-hosted-mcp)
+- [Hosted MCP server quickstart for SQL](https://learn.microsoft.com/azure/logic-apps/connector-namespace/hosted-mcp-quickstart?pivots=sql)
+- [Azure SQL MCP server support in Data API Builder](https://learn.microsoft.com/azure/data-api-builder/mcp/overview)
+- [Install the Data API Builder CLI](https://learn.microsoft.com/azure/data-api-builder/command-line/install)
+- [Connector Namespace overview](https://learn.microsoft.com/azure/logic-apps/connector-namespace/connector-namespace-overview)
+- [Azure Developer CLI](https://learn.microsoft.com/azure/developer/azure-developer-cli/)
diff --git a/samples/applications/azure-sql-mcp/azure.yaml b/samples/applications/azure-sql-mcp/azure.yaml
new file mode 100644
index 0000000000..eda0c16691
--- /dev/null
+++ b/samples/applications/azure-sql-mcp/azure.yaml
@@ -0,0 +1,15 @@
+name: azure-sql-mcp
+metadata:
+ template: azure-sql-mcp
+hooks:
+ postprovision:
+ windows:
+ shell: pwsh
+ run: ./scripts/post-provision.ps1
+ interactive: true
+ continueOnError: false
+ posix:
+ shell: bash
+ run: ./scripts/post-provision.sh
+ interactive: true
+ continueOnError: false
diff --git a/samples/applications/azure-sql-mcp/dab-config.json b/samples/applications/azure-sql-mcp/dab-config.json
new file mode 100644
index 0000000000..71675e24db
--- /dev/null
+++ b/samples/applications/azure-sql-mcp/dab-config.json
@@ -0,0 +1,64 @@
+{
+ "$schema": "https://github.com/Azure/data-api-builder/releases/download/v1.7.90/dab.draft.schema.json",
+ "data-source": {
+ "database-type": "mssql",
+ "connection-string": "@env('SQL_CONNECTION_STRING')",
+ "options": {
+ "set-session-context": false
+ }
+ },
+ "runtime": {
+ "rest": {
+ "enabled": false,
+ "path": "/api",
+ "request-body-strict": true
+ },
+ "graphql": {
+ "enabled": false,
+ "path": "/graphql",
+ "allow-introspection": true
+ },
+ "mcp": {
+ "enabled": true,
+ "path": "/mcp"
+ },
+ "host": {
+ "cors": {
+ "origins": [],
+ "allow-credentials": false
+ },
+ "authentication": {
+ "provider": "AppService"
+ },
+ "mode": "development"
+ }
+ },
+ "entities": {
+ "BlogPosts": {
+ "source": {
+ "object": "dbo.BlogPosts",
+ "type": "table"
+ },
+ "graphql": {
+ "enabled": true,
+ "type": {
+ "singular": "BlogPost",
+ "plural": "BlogPosts"
+ }
+ },
+ "rest": {
+ "enabled": true
+ },
+ "permissions": [
+ {
+ "role": "anonymous",
+ "actions": [
+ {
+ "action": "*"
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/applications/azure-sql-mcp/deploy.ps1 b/samples/applications/azure-sql-mcp/deploy.ps1
new file mode 100644
index 0000000000..8da72caf36
--- /dev/null
+++ b/samples/applications/azure-sql-mcp/deploy.ps1
@@ -0,0 +1,260 @@
+<#
+.SYNOPSIS
+ Deploy a Connector Namespace with a hosted SQL MCP Server.
+
+.DESCRIPTION
+ Single-script deployment: provisions Azure SQL, Connector Namespace,
+ hosted MCP server (mcp-sql), seeds the database, grants SAMI access,
+ and prints the MCP endpoint URL ready for VS Code.
+
+.PARAMETER DabConfigPath
+ Path to the DAB configuration file. Defaults to dab-config.json in the project root.
+
+.PARAMETER EnvironmentName
+ Name for the deployment environment. Used to generate unique resource names.
+
+.PARAMETER Location
+ Azure region for SQL and general resources. Default: eastasia.
+
+.PARAMETER ConnectorNamespaceLocation
+ Azure region for the Connector Namespace. Must be a preview region.
+ Default: eastasia.
+
+.PARAMETER DatabaseName
+ Name of the SQL database. Default: mcpdb.
+
+.EXAMPLE
+ .\deploy.ps1 -EnvironmentName mcp-dev
+ # Uses ./dab-config.json, deploys to eastasia
+
+.EXAMPLE
+ .\deploy.ps1 -EnvironmentName mcp-dev -DabConfigPath .\my-custom-dab.json -Location eastasia
+#>
+
+[CmdletBinding()]
+param(
+ [Parameter(Mandatory)]
+ [string]$EnvironmentName,
+
+ [string]$DabConfigPath,
+
+ [string]$Location = 'eastasia',
+
+ [string]$ConnectorNamespaceLocation = 'eastasia',
+
+ [string]$DatabaseName = 'mcpdb'
+)
+
+Set-StrictMode -Version Latest
+$ErrorActionPreference = 'Stop'
+
+$ProjectRoot = $PSScriptRoot
+
+# Resolve DAB config path
+if (-not $DabConfigPath) {
+ $DabConfigPath = Join-Path $ProjectRoot 'dab-config.json'
+}
+if (-not (Test-Path $DabConfigPath)) {
+ Write-Error "DAB config not found at: $DabConfigPath. Provide -DabConfigPath or place dab-config.json in the project root."
+ exit 1
+}
+$DabConfigPath = Resolve-Path $DabConfigPath
+Write-Host "Using DAB config: $DabConfigPath" -ForegroundColor Cyan
+
+# ── Step 1: Detect deployer identity ──────────────────────────────────────────
+
+Write-Host ""
+Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Cyan
+Write-Host " Step 1/5: Detecting deployer identity" -ForegroundColor Cyan
+Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Cyan
+
+$deployerLogin = az account show --query user.name -o tsv
+if (-not $deployerLogin) {
+ Write-Error "Not logged in. Run 'az login' first."
+ exit 1
+}
+Write-Host " Deployer: $deployerLogin" -ForegroundColor Green
+
+$deployerObjectId = az ad signed-in-user show --query id -o tsv
+Write-Host " Object ID: $deployerObjectId" -ForegroundColor Green
+
+$deployerIp = try { (Invoke-RestMethod -Uri 'https://api.ipify.org' -TimeoutSec 10) } catch { '' }
+if ($deployerIp) {
+ Write-Host " Public IP: $deployerIp" -ForegroundColor Green
+} else {
+ Write-Host " Public IP: (could not detect — SQL firewall rule skipped)" -ForegroundColor Yellow
+}
+
+# ── Step 2: Deploy Bicep ──────────────────────────────────────────────────────
+
+Write-Host ""
+Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Cyan
+Write-Host " Step 2/5: Deploying infrastructure (Bicep)" -ForegroundColor Cyan
+Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Cyan
+
+$bicepFile = Join-Path $ProjectRoot 'infra' 'main.bicep'
+if (-not (Test-Path $bicepFile)) {
+ Write-Error "Bicep file not found at: $bicepFile"
+ exit 1
+}
+
+# Copy DAB config to expected location for loadTextContent('../dab-config.json')
+$expectedDabPath = Join-Path $ProjectRoot 'dab-config.json'
+if ($DabConfigPath -ne (Resolve-Path $expectedDabPath -ErrorAction SilentlyContinue)) {
+ Write-Host " Copying DAB config to project root for Bicep..." -ForegroundColor Yellow
+ Copy-Item -Path $DabConfigPath -Destination $expectedDabPath -Force
+}
+
+Write-Host " Deploying to subscription..." -ForegroundColor Yellow
+
+$deployment = az deployment sub create `
+ --name "mcp-deploy-$EnvironmentName" `
+ --location $Location `
+ --template-file $bicepFile `
+ --parameters environmentName=$EnvironmentName `
+ location=$Location `
+ connectorNamespaceLocation=$ConnectorNamespaceLocation `
+ deployerLoginName=$deployerLogin `
+ deployerPrincipalId=$deployerObjectId `
+ deployerIpAddress=$deployerIp `
+ databaseName=$DatabaseName `
+ --query "properties.outputs" `
+ -o json 2>&1
+
+if ($LASTEXITCODE -ne 0) {
+ Write-Host $deployment -ForegroundColor Red
+ Write-Error "Bicep deployment failed."
+ exit 1
+}
+
+$outputs = $deployment | ConvertFrom-Json
+
+$sqlServerFqdn = $outputs.SQL_SERVER_FQDN.value
+$sqlDbName = $outputs.SQL_DATABASE_NAME.value
+$connectorNsName = $outputs.CONNECTOR_NAMESPACE_NAME.value
+$connectorNsSami = $outputs.CONNECTOR_NAMESPACE_PRINCIPAL_ID.value
+$mcpEndpointUrl = $outputs.MCP_ENDPOINT_URL.value
+$rgName = $outputs.RESOURCE_GROUP_NAME.value
+
+Write-Host " Deployment succeeded!" -ForegroundColor Green
+Write-Host " Resource Group: $rgName"
+Write-Host " SQL Server: $sqlServerFqdn"
+Write-Host " Connector Namespace: $connectorNsName"
+Write-Host " MCP Endpoint: $mcpEndpointUrl"
+
+# ── Step 3: Seed the database ─────────────────────────────────────────────────
+
+Write-Host ""
+Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Cyan
+Write-Host " Step 3/5: Seeding the database" -ForegroundColor Cyan
+Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Cyan
+
+$token = az account get-access-token --resource https://database.windows.net/ --query accessToken -o tsv
+
+$seedSql = @"
+IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'BlogPosts')
+BEGIN
+ CREATE TABLE dbo.BlogPosts (
+ Id int IDENTITY(1,1) PRIMARY KEY,
+ Title nvarchar(300) NOT NULL,
+ Url nvarchar(1000) NOT NULL,
+ Source nvarchar(100) NOT NULL
+ );
+END
+IF NOT EXISTS (SELECT 1 FROM dbo.BlogPosts WHERE Url = N'https://learn.microsoft.com/en-us/azure/logic-apps/connector-namespace/connector-namespace-hosted-mcp')
+BEGIN
+ INSERT INTO dbo.BlogPosts (Title, Url, Source)
+ VALUES (N'Hosted MCP servers in Azure Connector Namespace', N'https://learn.microsoft.com/en-us/azure/logic-apps/connector-namespace/connector-namespace-hosted-mcp', N'Microsoft Learn');
+END
+IF NOT EXISTS (SELECT 1 FROM dbo.BlogPosts WHERE Url = N'https://devblogs.microsoft.com/dotnet/durable-workflows-in-microsoft-agent-framework/')
+BEGIN
+ INSERT INTO dbo.BlogPosts (Title, Url, Source)
+ VALUES (N'Durable Workflows in Microsoft Agent Framework', N'https://devblogs.microsoft.com/dotnet/durable-workflows-in-microsoft-agent-framework/', N'.NET Blog');
+END
+PRINT 'BlogPosts table seeded.';
+"@
+
+$sqlSuccess = $false
+try {
+ Invoke-Sqlcmd -ServerInstance $sqlServerFqdn -Database $sqlDbName -AccessToken $token -Query $seedSql
+ Write-Host " Database seeded." -ForegroundColor Green
+ $sqlSuccess = $true
+} catch {
+ Write-Host " Invoke-Sqlcmd unavailable, trying sqlcmd CLI..." -ForegroundColor Yellow
+ try {
+ sqlcmd -S $sqlServerFqdn -d $sqlDbName -Q $seedSql --authentication-method=ActiveDirectoryDefault
+ Write-Host " Database seeded." -ForegroundColor Green
+ $sqlSuccess = $true
+ } catch {
+ Write-Host " Could not seed automatically." -ForegroundColor Red
+ }
+}
+
+# ── Step 4: Grant SAMI access ─────────────────────────────────────────────────
+
+Write-Host ""
+Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Cyan
+Write-Host " Step 4/5: Granting Connector Namespace SAMI database access" -ForegroundColor Cyan
+Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Cyan
+
+$grantSql = @"
+IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = '$connectorNsName')
+BEGIN
+ CREATE USER [$connectorNsName] FROM EXTERNAL PROVIDER;
+END
+IF ISNULL(IS_ROLEMEMBER('db_datareader', '$connectorNsName'), 0) = 0
+ ALTER ROLE db_datareader ADD MEMBER [$connectorNsName];
+IF ISNULL(IS_ROLEMEMBER('db_datawriter', '$connectorNsName'), 0) = 0
+ ALTER ROLE db_datawriter ADD MEMBER [$connectorNsName];
+GRANT VIEW DEFINITION TO [$connectorNsName];
+IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = '$connectorNsName')
+ THROW 51000, 'Connector Namespace managed identity SQL user was not created.', 1;
+IF ISNULL(IS_ROLEMEMBER('db_datareader', '$connectorNsName'), 0) <> 1
+ THROW 51001, 'Connector Namespace managed identity is not a member of db_datareader.', 1;
+IF ISNULL(IS_ROLEMEMBER('db_datawriter', '$connectorNsName'), 0) <> 1
+ THROW 51002, 'Connector Namespace managed identity is not a member of db_datawriter.', 1;
+PRINT 'SAMI access granted.';
+"@
+
+if ($sqlSuccess) {
+ try {
+ Invoke-Sqlcmd -ServerInstance $sqlServerFqdn -Database $sqlDbName -AccessToken $token -Query $grantSql
+ Write-Host " SAMI access granted." -ForegroundColor Green
+ } catch {
+ try {
+ sqlcmd -S $sqlServerFqdn -d $sqlDbName -Q $grantSql --authentication-method=ActiveDirectoryDefault
+ Write-Host " SAMI access granted." -ForegroundColor Green
+ } catch {
+ $sqlSuccess = $false
+ }
+ }
+}
+
+if (-not $sqlSuccess) {
+ Write-Host ""
+ Write-Host " Run these SQL commands manually in Azure Portal Query Editor:" -ForegroundColor Yellow
+ Write-Host " (SQL Server → $sqlServerFqdn → Database → $sqlDbName → Query editor)" -ForegroundColor Yellow
+ Write-Host ""
+ Write-Host $seedSql -ForegroundColor White
+ Write-Host ""
+ Write-Host $grantSql -ForegroundColor White
+ Write-Host ""
+}
+
+# ── Step 5: Done ──────────────────────────────────────────────────────────────
+
+Write-Host ""
+Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Cyan
+Write-Host " Step 5/5: Deployment Complete!" -ForegroundColor Cyan
+Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Cyan
+Write-Host ""
+Write-Host " MCP Endpoint: $mcpEndpointUrl" -ForegroundColor Green
+$subscriptionId = az account show --query id -o tsv
+$resourceGroupUrl = "https://portal.azure.com/#@/resource/subscriptions/$subscriptionId/resourceGroups/$rgName/overview"
+Write-Host " Azure Portal: $resourceGroupUrl" -ForegroundColor Green
+Write-Host ""
+Write-Host " Use the MCP endpoint with any MCP client that supports HTTP transport." -ForegroundColor White
+Write-Host ""
+Write-Host " Clean up later with:" -ForegroundColor White
+Write-Host " az group delete --name $rgName --yes" -ForegroundColor Gray
+Write-Host ""
diff --git a/samples/applications/azure-sql-mcp/infra/main.bicep b/samples/applications/azure-sql-mcp/infra/main.bicep
new file mode 100644
index 0000000000..ed1db4f499
--- /dev/null
+++ b/samples/applications/azure-sql-mcp/infra/main.bicep
@@ -0,0 +1,166 @@
+targetScope = 'subscription'
+
+// ---------------------------------------------------------------------------
+// Parameters
+// ---------------------------------------------------------------------------
+
+@minLength(1)
+@maxLength(64)
+@description('Name of the environment (used to generate unique resource names).')
+param environmentName string
+
+@description('Primary location for SQL and other resources.')
+@metadata({
+ azd: {
+ type: 'location'
+ }
+})
+param location string = 'eastasia'
+
+@description('Location for the Connector Namespace. Preview regions: westcentralus, eastasia, centralus, northeurope.')
+param connectorNamespaceLocation string = 'eastasia'
+
+@description('Object ID of the deployer user. Used as Entra admin for SQL and for access policies.')
+@metadata({
+ azd: {
+ type: 'principalId'
+ }
+})
+param deployerPrincipalId string = deployer().objectId
+
+@description('Login name (email) of the deployer user for SQL Entra admin.')
+param deployerLoginName string
+
+@description('Name of the SQL Database to create.')
+param databaseName string = 'mcpdb'
+
+@description('Optional public IP address to allow through the SQL firewall. The azd post-provision hook also configures this for local setup.')
+param deployerIpAddress string = ''
+
+// ---------------------------------------------------------------------------
+// Variables
+// ---------------------------------------------------------------------------
+
+var readableEnvironmentName = take(toLower(replace(replace(replace(environmentName, '_', '-'), '.', '-'), ' ', '-')), 40)
+var resourceToken = take(toLower(uniqueString(subscription().id, environmentName, location)), 8)
+var tags = { 'azd-env-name': environmentName }
+var resourceGroupName = 'rg-${readableEnvironmentName}'
+var sqlServerName = 'sql-${readableEnvironmentName}-${resourceToken}'
+var connectorNamespaceName = 'cn-${readableEnvironmentName}-${resourceToken}'
+var logAnalyticsWorkspaceName = 'log-${readableEnvironmentName}-${resourceToken}'
+var appInsightsName = 'appi-${readableEnvironmentName}-${resourceToken}'
+
+// Load the included DAB config and base64-encode it for the ARM API.
+var dabConfigBase64 = base64(loadTextContent('../dab-config.json'))
+var dabConnectionString = 'Server=${sql.outputs.sqlServerFqdn};Database=${databaseName};Authentication=Active Directory Default;Encrypt=True;TrustServerCertificate=False;'
+
+// ---------------------------------------------------------------------------
+// Resource Group
+// ---------------------------------------------------------------------------
+
+resource rg 'Microsoft.Resources/resourceGroups@2024-03-01' = {
+ name: resourceGroupName
+ location: location
+ tags: tags
+}
+
+// ---------------------------------------------------------------------------
+// Azure SQL Server + Database
+// ---------------------------------------------------------------------------
+
+module sql './modules/sql.bicep' = {
+ scope: rg
+ name: 'sql-${resourceToken}'
+ params: {
+ sqlServerName: sqlServerName
+ databaseName: databaseName
+ location: location
+ tags: tags
+ entraAdminObjectId: deployerPrincipalId
+ entraAdminLogin: deployerLoginName
+ deployerIpAddress: deployerIpAddress
+ }
+}
+
+// ---------------------------------------------------------------------------
+// Application Insights
+// ---------------------------------------------------------------------------
+
+module appInsights './modules/appInsights.bicep' = {
+ scope: rg
+ name: 'appi-${resourceToken}'
+ params: {
+ workspaceName: logAnalyticsWorkspaceName
+ appInsightsName: appInsightsName
+ location: location
+ tags: tags
+ }
+}
+
+// ---------------------------------------------------------------------------
+// Connector Namespace (with System-Assigned Managed Identity)
+// ---------------------------------------------------------------------------
+
+module connectorNamespace './modules/connectorNamespace.bicep' = {
+ scope: rg
+ name: 'cn-${resourceToken}'
+ params: {
+ name: connectorNamespaceName
+ location: connectorNamespaceLocation
+ tags: tags
+ }
+}
+
+// ---------------------------------------------------------------------------
+// Hosted SQL MCP Server + Access Policy
+// ---------------------------------------------------------------------------
+
+module hostedMcpServer './modules/hostedMcpServer.bicep' = {
+ scope: rg
+ name: 'mcp-${resourceToken}'
+ params: {
+ connectorNamespaceName: connectorNamespaceName
+ name: 'sql-mcp'
+ deployerPrincipalId: deployerPrincipalId
+ dabConfigBase64: dabConfigBase64
+ sqlConnectionString: dabConnectionString
+ applicationInsightsConnectionString: appInsights.outputs.connectionString
+ }
+ dependsOn: [
+ connectorNamespace
+ ]
+}
+
+// ---------------------------------------------------------------------------
+// Outputs
+// ---------------------------------------------------------------------------
+
+@description('The name of the resource group.')
+output RESOURCE_GROUP_NAME string = rg.name
+
+@description('The name of the SQL Server.')
+output SQL_SERVER_NAME string = sql.outputs.sqlServerName
+
+@description('The fully qualified domain name of the SQL Server.')
+output SQL_SERVER_FQDN string = sql.outputs.sqlServerFqdn
+
+@description('The name of the SQL Database.')
+output SQL_DATABASE_NAME string = sql.outputs.databaseName
+
+@description('The name of the Connector Namespace.')
+output CONNECTOR_NAMESPACE_NAME string = connectorNamespace.outputs.name
+
+@description('The principal ID of the Connector Namespace SAMI.')
+output CONNECTOR_NAMESPACE_PRINCIPAL_ID string = connectorNamespace.outputs.principalId
+
+@description('The name of the Application Insights resource.')
+output APPLICATIONINSIGHTS_NAME string = appInsights.outputs.appInsightsName
+
+@description('The name of the Log Analytics workspace backing Application Insights.')
+output LOG_ANALYTICS_WORKSPACE_NAME string = appInsights.outputs.workspaceName
+
+@description('Connection string for DAB config (SAMI-based).')
+output DAB_CONNECTION_STRING string = dabConnectionString
+
+@description('MCP endpoint URL — point VS Code / MCP clients here.')
+output MCP_ENDPOINT_URL string = hostedMcpServer.outputs.mcpEndpointUrl
diff --git a/samples/applications/azure-sql-mcp/infra/main.parameters.json b/samples/applications/azure-sql-mcp/infra/main.parameters.json
new file mode 100644
index 0000000000..fa2e41ef88
--- /dev/null
+++ b/samples/applications/azure-sql-mcp/infra/main.parameters.json
@@ -0,0 +1,15 @@
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ "environmentName": {
+ "value": "${AZURE_ENV_NAME}"
+ },
+ "location": {
+ "value": "${AZURE_LOCATION}"
+ },
+ "deployerLoginName": {
+ "value": "${AZURE_DEPLOYER_LOGIN}"
+ }
+ }
+}
diff --git a/samples/applications/azure-sql-mcp/infra/modules/appInsights.bicep b/samples/applications/azure-sql-mcp/infra/modules/appInsights.bicep
new file mode 100644
index 0000000000..2c48cacf17
--- /dev/null
+++ b/samples/applications/azure-sql-mcp/infra/modules/appInsights.bicep
@@ -0,0 +1,47 @@
+@description('Name for the Log Analytics workspace backing Application Insights.')
+param workspaceName string
+
+@description('Name for the Application Insights resource.')
+param appInsightsName string
+
+@description('Location for monitoring resources.')
+param location string
+
+@description('Tags to apply to resources.')
+param tags object = {}
+
+resource workspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' = {
+ name: workspaceName
+ location: location
+ tags: tags
+ properties: {
+ sku: {
+ name: 'PerGB2018'
+ }
+ retentionInDays: 30
+ publicNetworkAccessForIngestion: 'Enabled'
+ publicNetworkAccessForQuery: 'Enabled'
+ }
+}
+
+resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
+ name: appInsightsName
+ location: location
+ tags: tags
+ kind: 'web'
+ properties: {
+ Application_Type: 'web'
+ WorkspaceResourceId: workspace.id
+ publicNetworkAccessForIngestion: 'Enabled'
+ publicNetworkAccessForQuery: 'Enabled'
+ }
+}
+
+@description('The name of the Log Analytics workspace.')
+output workspaceName string = workspace.name
+
+@description('The name of the Application Insights resource.')
+output appInsightsName string = appInsights.name
+
+@description('The Application Insights connection string.')
+output connectionString string = appInsights.properties.ConnectionString
diff --git a/samples/applications/azure-sql-mcp/infra/modules/connectorNamespace.bicep b/samples/applications/azure-sql-mcp/infra/modules/connectorNamespace.bicep
new file mode 100644
index 0000000000..62a4602a2b
--- /dev/null
+++ b/samples/applications/azure-sql-mcp/infra/modules/connectorNamespace.bicep
@@ -0,0 +1,27 @@
+@description('Name for the Connector Namespace.')
+param name string
+
+@description('Location for the Connector Namespace. During preview, only select regions are supported: westcentralus, eastasia, centralus, northeurope.')
+param location string
+
+@description('Tags to apply to resources.')
+param tags object = {}
+
+resource connectorNamespace 'Microsoft.Web/connectorGateways@2026-05-01-preview' = {
+ name: name
+ location: location
+ tags: tags
+ identity: {
+ type: 'SystemAssigned'
+ }
+ properties: {}
+}
+
+@description('The resource ID of the Connector Namespace.')
+output resourceId string = connectorNamespace.id
+
+@description('The name of the Connector Namespace.')
+output name string = connectorNamespace.name
+
+@description('The principal ID of the system-assigned managed identity.')
+output principalId string = connectorNamespace.identity.principalId
diff --git a/samples/applications/azure-sql-mcp/infra/modules/hostedMcpServer.bicep b/samples/applications/azure-sql-mcp/infra/modules/hostedMcpServer.bicep
new file mode 100644
index 0000000000..af45ac8047
--- /dev/null
+++ b/samples/applications/azure-sql-mcp/infra/modules/hostedMcpServer.bicep
@@ -0,0 +1,75 @@
+@sys.description('Name of the parent Connector Namespace.')
+param connectorNamespaceName string
+
+@sys.description('Name for the MCP server config (2-64 chars).')
+@minLength(2)
+@maxLength(64)
+param name string
+
+@sys.description('Description shown to MCP clients.')
+param mcpServerDescription string = 'SQL MCP server bound to DAB config with managed identity.'
+
+@sys.description('Object ID of the deployer user to grant MCP access.')
+param deployerPrincipalId string
+
+@sys.description('Tenant ID for access policies.')
+param tenantId string = tenant().tenantId
+
+@sys.description('Base64-encoded DAB configuration file content.')
+param dabConfigBase64 string
+
+@secure()
+@sys.description('SQL connection string exposed to the hosted MCP server as SQL_CONNECTION_STRING.')
+param sqlConnectionString string
+
+@sys.description('Application Insights connection string exposed to the hosted MCP server as APPLICATIONINSIGHTS_CONNECTION_STRING.')
+param applicationInsightsConnectionString string
+
+// Reference the existing Connector Namespace
+resource connectorNamespace 'Microsoft.Web/connectorGateways@2026-05-01-preview' existing = {
+ name: connectorNamespaceName
+}
+
+// Hosted MCP Server — runs the curated mcp-sql container image with DAB config
+resource mcpServer 'Microsoft.Web/connectorGateways/mcpServerConfigs@2026-05-01-preview' = {
+ parent: connectorNamespace
+ name: name
+ kind: 'HostedMcpServer'
+ properties: {
+ description: mcpServerDescription
+ hostedMcpServer: {
+ hostedMcpServerId: 'mcp-sql'
+ configuration: {
+ configFile: dabConfigBase64
+ SQL_CONNECTION_STRING: sqlConnectionString
+ APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsightsConnectionString
+ }
+ }
+ }
+}
+
+// Grant the deployer access to invoke the MCP server tools.
+// The access-policy name must equal the principal's objectId.
+resource mcpAccessPolicy 'Microsoft.Web/connectorGateways/mcpServerConfigs/accessPolicies@2026-05-01-preview' = {
+ parent: mcpServer
+ name: deployerPrincipalId
+ properties: {
+ principal: {
+ type: 'ActiveDirectory'
+ identity: {
+ objectId: deployerPrincipalId
+ tenantId: tenantId
+ }
+ }
+ principalType: 'User'
+ }
+}
+
+@sys.description('Resource ID of the MCP server config.')
+output id string = mcpServer.id
+
+@sys.description('Name of the MCP server config.')
+output mcpServerName string = mcpServer.name
+
+@sys.description('MCP endpoint URL for clients to connect to.')
+output mcpEndpointUrl string = mcpServer.properties.mcpEndpointUrl
diff --git a/samples/applications/azure-sql-mcp/infra/modules/sql.bicep b/samples/applications/azure-sql-mcp/infra/modules/sql.bicep
new file mode 100644
index 0000000000..c345536b5e
--- /dev/null
+++ b/samples/applications/azure-sql-mcp/infra/modules/sql.bicep
@@ -0,0 +1,86 @@
+@description('Azure SQL Server name.')
+param sqlServerName string
+
+@description('Azure SQL Database name.')
+param databaseName string
+
+@description('Location for resources.')
+param location string
+
+@description('Tags to apply to resources.')
+param tags object = {}
+
+@description('Object ID of the Entra ID admin for the SQL Server.')
+param entraAdminObjectId string
+
+@description('Login name of the Entra ID admin (email or display name).')
+param entraAdminLogin string
+
+@description('Tenant ID for Entra ID authentication.')
+param tenantId string = tenant().tenantId
+
+@description('Public IP of the deployer for SQL firewall rule (allows post-provision scripts to connect). Leave empty to skip.')
+param deployerIpAddress string = ''
+
+resource sqlServer 'Microsoft.Sql/servers@2023-08-01-preview' = {
+ name: sqlServerName
+ location: location
+ tags: tags
+ properties: {
+ administrators: {
+ administratorType: 'ActiveDirectory'
+ azureADOnlyAuthentication: true
+ login: entraAdminLogin
+ sid: entraAdminObjectId
+ tenantId: tenantId
+ principalType: 'User'
+ }
+ minimalTlsVersion: '1.2'
+ publicNetworkAccess: 'Enabled'
+ }
+}
+
+// Allow Azure services to access the SQL Server
+resource firewallRuleAzure 'Microsoft.Sql/servers/firewallRules@2023-08-01-preview' = {
+ parent: sqlServer
+ name: 'AllowAzureServices'
+ properties: {
+ startIpAddress: '0.0.0.0'
+ endIpAddress: '0.0.0.0'
+ }
+}
+
+// Allow the deployer's IP to connect for post-provision seeding/granting
+resource firewallRuleDeployer 'Microsoft.Sql/servers/firewallRules@2023-08-01-preview' = if (!empty(deployerIpAddress)) {
+ parent: sqlServer
+ name: 'AllowDeployerIp'
+ properties: {
+ startIpAddress: deployerIpAddress
+ endIpAddress: deployerIpAddress
+ }
+}
+
+resource database 'Microsoft.Sql/servers/databases@2023-08-01-preview' = {
+ parent: sqlServer
+ name: databaseName
+ location: location
+ tags: tags
+ sku: {
+ name: 'Basic'
+ tier: 'Basic'
+ capacity: 5
+ }
+ properties: {
+ collation: 'SQL_Latin1_General_CP1_CI_AS'
+ maxSizeBytes: 2147483648
+ }
+}
+
+@description('The fully qualified domain name of the SQL Server.')
+output sqlServerFqdn string = sqlServer.properties.fullyQualifiedDomainName
+
+@description('The name of the SQL Server.')
+output sqlServerName string = sqlServer.name
+
+@description('The name of the SQL Database.')
+output databaseName string = database.name
diff --git a/samples/applications/azure-sql-mcp/scripts/post-provision.ps1 b/samples/applications/azure-sql-mcp/scripts/post-provision.ps1
new file mode 100644
index 0000000000..1acbe71ccd
--- /dev/null
+++ b/samples/applications/azure-sql-mcp/scripts/post-provision.ps1
@@ -0,0 +1,253 @@
+#!/usr/bin/env pwsh
+# post-provision.ps1 — Runs after `azd provision` to seed the database and
+# generate the DAB config file.
+
+Set-StrictMode -Version Latest
+$ErrorActionPreference = 'Stop'
+
+function Add-SqlFirewallRuleForIp {
+ param(
+ [Parameter(Mandatory)]
+ [string]$IpAddress,
+
+ [Parameter(Mandatory)]
+ [string]$RuleName
+ )
+
+ az sql server firewall-rule create `
+ --resource-group $resourceGroupName `
+ --server $sqlServerName `
+ --name $RuleName `
+ --start-ip-address $IpAddress `
+ --end-ip-address $IpAddress `
+ --only-show-errors | Out-Null
+}
+
+function Invoke-SqlcmdWithFirewallRetry {
+ param(
+ [Parameter(Mandatory)]
+ [string]$Query
+ )
+
+ $maxAttempts = 20
+ for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) {
+ try {
+ Invoke-Sqlcmd -ServerInstance $sqlServerFqdn -Database $databaseName -AccessToken $token -Query $Query
+ return
+ } catch {
+ $message = $_.Exception.Message
+ if ($message -match "Client with IP address '([^']+)' is not allowed") {
+ $blockedIp = $Matches[1]
+ Write-Host " SQL reported blocked client IP $blockedIp; adding firewall rule and retrying ($attempt/$maxAttempts)..." -ForegroundColor Yellow
+ Add-SqlFirewallRuleForIp -IpAddress $blockedIp -RuleName 'AllowSqlClientIp'
+ Start-Sleep -Seconds 15
+ continue
+ }
+
+ throw
+ }
+ }
+
+ throw "Timed out waiting for SQL firewall rules to allow this client."
+}
+
+# Read outputs from azd
+$resourceGroupName = (azd env get-value RESOURCE_GROUP_NAME)
+$sqlServerName = (azd env get-value SQL_SERVER_NAME)
+$sqlServerFqdn = (azd env get-value SQL_SERVER_FQDN)
+$databaseName = (azd env get-value SQL_DATABASE_NAME)
+$connectorNsName = (azd env get-value CONNECTOR_NAMESPACE_NAME)
+$connectorNsPrincipal = (azd env get-value CONNECTOR_NAMESPACE_PRINCIPAL_ID)
+$dabConnectionString = (azd env get-value DAB_CONNECTION_STRING)
+
+Write-Host ""
+Write-Host "============================================================" -ForegroundColor Cyan
+Write-Host " Post-Provision Setup" -ForegroundColor Cyan
+Write-Host "============================================================" -ForegroundColor Cyan
+Write-Host ""
+Write-Host "SQL Server: $sqlServerFqdn"
+Write-Host "Database: $databaseName"
+Write-Host "Connector Namespace: $connectorNsName"
+Write-Host "Connector NS SAMI ID: $connectorNsPrincipal"
+Write-Host ""
+
+# --- Step 1: Allow this machine through the SQL firewall ---
+Write-Host "[1/4] Configuring SQL firewall for this machine..." -ForegroundColor Yellow
+try {
+ $detectedIps = @()
+ foreach ($uri in @('https://api.ipify.org', 'https://ifconfig.me/ip')) {
+ try {
+ $ip = (Invoke-RestMethod -Uri $uri -TimeoutSec 10)
+ if ($ip -and $ip -match '^\d{1,3}(\.\d{1,3}){3}$') {
+ $detectedIps += $ip
+ }
+ } catch {
+ Write-Host " Could not detect public IP from $uri." -ForegroundColor DarkYellow
+ }
+ }
+
+ $index = 0
+ foreach ($deployerIp in ($detectedIps | Select-Object -Unique)) {
+ $index++
+ $ruleName = if ($index -eq 1) { 'AllowDeployerIp' } else { "AllowDeployerIp$index" }
+ Add-SqlFirewallRuleForIp -IpAddress $deployerIp -RuleName $ruleName
+ Write-Host " Allowed public IP: $deployerIp" -ForegroundColor Green
+ }
+} catch {
+ Write-Host " WARNING: Could not detect or configure public IP. SQL commands may need to be run manually." -ForegroundColor Yellow
+}
+
+# --- Step 2: Get an access token for Azure SQL ---
+Write-Host "[2/4] Getting access token for Azure SQL..." -ForegroundColor Yellow
+$token = az account get-access-token --resource https://database.windows.net/ --query accessToken -o tsv
+if (-not $token) {
+ Write-Host "ERROR: Failed to get access token. Make sure you are logged in with 'az login'." -ForegroundColor Red
+ exit 1
+}
+
+# --- Step 3: Seed the database ---
+Write-Host "[3/4] Seeding the database with BlogPosts table..." -ForegroundColor Yellow
+
+$seedSql = @"
+IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'BlogPosts')
+BEGIN
+ CREATE TABLE dbo.BlogPosts (
+ Id int IDENTITY(1,1) PRIMARY KEY,
+ Title nvarchar(300) NOT NULL,
+ Url nvarchar(1000) NOT NULL,
+ Source nvarchar(100) NOT NULL
+ );
+END
+IF NOT EXISTS (SELECT 1 FROM dbo.BlogPosts WHERE Url = N'https://learn.microsoft.com/en-us/azure/logic-apps/connector-namespace/connector-namespace-hosted-mcp')
+BEGIN
+ INSERT INTO dbo.BlogPosts (Title, Url, Source)
+ VALUES (N'Hosted MCP servers in Azure Connector Namespace', N'https://learn.microsoft.com/en-us/azure/logic-apps/connector-namespace/connector-namespace-hosted-mcp', N'Microsoft Learn');
+END
+IF NOT EXISTS (SELECT 1 FROM dbo.BlogPosts WHERE Url = N'https://devblogs.microsoft.com/dotnet/durable-workflows-in-microsoft-agent-framework/')
+BEGIN
+ INSERT INTO dbo.BlogPosts (Title, Url, Source)
+ VALUES (N'Durable Workflows in Microsoft Agent Framework', N'https://devblogs.microsoft.com/dotnet/durable-workflows-in-microsoft-agent-framework/', N'.NET Blog');
+END
+PRINT 'BlogPosts table seeded.';
+"@
+
+$grantSql = @"
+IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = '$connectorNsName')
+BEGIN
+ CREATE USER [$connectorNsName] FROM EXTERNAL PROVIDER;
+END
+IF ISNULL(IS_ROLEMEMBER('db_datareader', '$connectorNsName'), 0) = 0
+ ALTER ROLE db_datareader ADD MEMBER [$connectorNsName];
+IF ISNULL(IS_ROLEMEMBER('db_datawriter', '$connectorNsName'), 0) = 0
+ ALTER ROLE db_datawriter ADD MEMBER [$connectorNsName];
+GRANT VIEW DEFINITION TO [$connectorNsName];
+IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = '$connectorNsName')
+ THROW 51000, 'Connector Namespace managed identity SQL user was not created.', 1;
+IF ISNULL(IS_ROLEMEMBER('db_datareader', '$connectorNsName'), 0) <> 1
+ THROW 51001, 'Connector Namespace managed identity is not a member of db_datareader.', 1;
+IF ISNULL(IS_ROLEMEMBER('db_datawriter', '$connectorNsName'), 0) <> 1
+ THROW 51002, 'Connector Namespace managed identity is not a member of db_datawriter.', 1;
+PRINT 'Granted SAMI access to database.';
+"@
+
+if (-not (Get-Command Invoke-Sqlcmd -ErrorAction SilentlyContinue)) {
+ Write-Host "ERROR: Invoke-Sqlcmd is required for automatic post-provision database setup." -ForegroundColor Red
+ Write-Host "Install the SqlServer PowerShell module or run the SQL commands manually in Azure Portal Query Editor." -ForegroundColor Red
+ exit 1
+}
+
+Invoke-SqlcmdWithFirewallRetry -Query $seedSql
+Write-Host " Database seeded successfully." -ForegroundColor Green
+
+Write-Host "[4/4] Granting Connector Namespace SAMI access to database..." -ForegroundColor Yellow
+Invoke-SqlcmdWithFirewallRetry -Query $grantSql
+Write-Host " SAMI access granted." -ForegroundColor Green
+
+# --- Generate DAB config ---
+Write-Host ""
+Write-Host "Generating dab-config.json..." -ForegroundColor Yellow
+
+$dabConfig = @{
+ '$schema' = 'https://github.com/Azure/data-api-builder/releases/download/v1.7.93/dab.draft.schema.json'
+ 'data-source' = @{
+ 'database-type' = 'mssql'
+ 'connection-string' = $dabConnectionString
+ 'options' = @{
+ 'set-session-context' = $false
+ }
+ }
+ 'runtime' = @{
+ 'rest' = @{
+ 'enabled' = $false
+ 'path' = '/api'
+ 'request-body-strict' = $true
+ }
+ 'graphql' = @{
+ 'enabled' = $false
+ 'path' = '/graphql'
+ 'allow-introspection' = $true
+ }
+ 'mcp' = @{
+ 'enabled' = $true
+ 'path' = '/mcp'
+ }
+ 'host' = @{
+ 'cors' = @{
+ 'origins' = @()
+ 'allow-credentials' = $false
+ }
+ 'authentication' = @{
+ 'provider' = 'AppService'
+ }
+ 'mode' = 'development'
+ }
+ }
+ 'entities' = @{
+ 'BlogPosts' = @{
+ 'source' = @{
+ 'object' = 'dbo.BlogPosts'
+ 'type' = 'table'
+ }
+ 'graphql' = @{
+ 'enabled' = $true
+ 'type' = @{
+ 'singular' = 'BlogPost'
+ 'plural' = 'BlogPosts'
+ }
+ }
+ 'rest' = @{
+ 'enabled' = $true
+ }
+ 'permissions' = @(
+ @{
+ 'role' = 'anonymous'
+ 'actions' = @(
+ @{ 'action' = '*' }
+ )
+ }
+ )
+ }
+ }
+} | ConvertTo-Json -Depth 10
+
+$dabConfig | Set-Content -Path (Join-Path $PSScriptRoot '..' 'dab-config.generated.json') -Encoding utf8
+Write-Host " Created dab-config.generated.json" -ForegroundColor Green
+
+# --- Print MCP endpoint and portal link ---
+$mcpEndpointUrl = (azd env get-value MCP_ENDPOINT_URL)
+$subscriptionId = (azd env get-value AZURE_SUBSCRIPTION_ID)
+if (-not $subscriptionId) {
+ $subscriptionId = az account show --query id -o tsv
+}
+$resourceGroupUrl = "https://portal.azure.com/#@/resource/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/overview"
+
+Write-Host ""
+Write-Host "============================================================" -ForegroundColor Cyan
+Write-Host " Deployment Complete!" -ForegroundColor Cyan
+Write-Host "============================================================" -ForegroundColor Cyan
+Write-Host ""
+Write-Host "MCP Endpoint: $mcpEndpointUrl" -ForegroundColor Green
+Write-Host "Azure Portal: $resourceGroupUrl" -ForegroundColor Green
+Write-Host ""
+Write-Host "Use the MCP endpoint with any MCP client that supports HTTP transport." -ForegroundColor White
+Write-Host ""
diff --git a/samples/applications/azure-sql-mcp/scripts/post-provision.sh b/samples/applications/azure-sql-mcp/scripts/post-provision.sh
new file mode 100644
index 0000000000..e17b1cf99c
--- /dev/null
+++ b/samples/applications/azure-sql-mcp/scripts/post-provision.sh
@@ -0,0 +1,213 @@
+#!/usr/bin/env bash
+# post-provision.sh — Runs after `azd provision` to seed the database and
+# generate the DAB config file.
+
+set -euo pipefail
+
+add_sql_firewall_rule_for_ip() {
+ local ip_address="$1"
+ local rule_name="$2"
+
+ az sql server firewall-rule create \
+ --resource-group "$RESOURCE_GROUP_NAME" \
+ --server "$SQL_SERVER_NAME" \
+ --name "$rule_name" \
+ --start-ip-address "$ip_address" \
+ --end-ip-address "$ip_address" \
+ --only-show-errors >/dev/null
+}
+
+run_sqlcmd_with_firewall_retry() {
+ local query="$1"
+ local output
+ local max_attempts=20
+ local attempt=1
+
+ while [ "$attempt" -le "$max_attempts" ]; do
+ set +e
+ output=$(echo "$query" | sqlcmd -S "$SQL_SERVER_FQDN" -d "$DATABASE_NAME" -G 2>&1)
+ local exit_code=$?
+ set -e
+
+ if [ "$exit_code" -eq 0 ]; then
+ echo "$output"
+ return 0
+ fi
+
+ if [[ "$output" =~ Client\ with\ IP\ address\ \'([0-9.]+)\'\ is\ not\ allowed ]]; then
+ local blocked_ip="${BASH_REMATCH[1]}"
+ echo " SQL reported blocked client IP $blocked_ip; adding firewall rule and retrying ($attempt/$max_attempts)..."
+ add_sql_firewall_rule_for_ip "$blocked_ip" "AllowSqlClientIp"
+ sleep 15
+ attempt=$((attempt + 1))
+ continue
+ fi
+
+ echo "$output"
+ return "$exit_code"
+ done
+
+ echo "Timed out waiting for SQL firewall rules to allow this client."
+ return 1
+}
+
+# Read outputs from azd
+RESOURCE_GROUP_NAME=$(azd env get-value RESOURCE_GROUP_NAME)
+SQL_SERVER_NAME=$(azd env get-value SQL_SERVER_NAME)
+SQL_SERVER_FQDN=$(azd env get-value SQL_SERVER_FQDN)
+DATABASE_NAME=$(azd env get-value SQL_DATABASE_NAME)
+CONNECTOR_NS_NAME=$(azd env get-value CONNECTOR_NAMESPACE_NAME)
+CONNECTOR_NS_PRINCIPAL=$(azd env get-value CONNECTOR_NAMESPACE_PRINCIPAL_ID)
+DAB_CONNECTION_STRING=$(azd env get-value DAB_CONNECTION_STRING)
+
+echo ""
+echo "============================================================"
+echo " Post-Provision Setup"
+echo "============================================================"
+echo ""
+echo "SQL Server: $SQL_SERVER_FQDN"
+echo "Database: $DATABASE_NAME"
+echo "Connector Namespace: $CONNECTOR_NS_NAME"
+echo "Connector NS SAMI ID: $CONNECTOR_NS_PRINCIPAL"
+echo ""
+
+# --- Step 1: Allow this machine through the SQL firewall ---
+echo "[1/4] Configuring SQL firewall for this machine..."
+DETECTED_IPS=()
+for IP_ENDPOINT in "https://api.ipify.org" "https://ifconfig.me/ip"; do
+ DETECTED_IP=$(curl -s --max-time 10 "$IP_ENDPOINT" || true)
+ if [[ "$DETECTED_IP" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]]; then
+ DETECTED_IPS+=("$DETECTED_IP")
+ fi
+done
+
+if [ "${#DETECTED_IPS[@]}" -eq 0 ]; then
+ echo " WARNING: Could not detect public IP. SQL commands may need to be run manually."
+else
+ INDEX=0
+ printf "%s\n" "${DETECTED_IPS[@]}" | sort -u | while read -r DEPLOYER_IP; do
+ INDEX=$((INDEX + 1))
+ RULE_NAME="AllowDeployerIp"
+ if [ "$INDEX" -gt 1 ]; then
+ RULE_NAME="AllowDeployerIp$INDEX"
+ fi
+ add_sql_firewall_rule_for_ip "$DEPLOYER_IP" "$RULE_NAME"
+ echo " Allowed public IP: $DEPLOYER_IP"
+ done
+fi
+
+# --- Step 2: Get an access token for Azure SQL ---
+echo "[2/4] Getting access token for Azure SQL..."
+TOKEN=$(az account get-access-token --resource https://database.windows.net/ --query accessToken -o tsv)
+if [ -z "$TOKEN" ]; then
+ echo "ERROR: Failed to get access token. Make sure you are logged in with 'az login'."
+ exit 1
+fi
+
+# --- Step 3: Seed the database ---
+echo "[3/4] Seeding the database with BlogPosts table..."
+
+SEED_SQL="IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'BlogPosts')
+BEGIN
+ CREATE TABLE dbo.BlogPosts (
+ Id int IDENTITY(1,1) PRIMARY KEY,
+ Title nvarchar(300) NOT NULL,
+ Url nvarchar(1000) NOT NULL,
+ Source nvarchar(100) NOT NULL
+ );
+END;
+IF NOT EXISTS (SELECT 1 FROM dbo.BlogPosts WHERE Url = N'https://learn.microsoft.com/en-us/azure/logic-apps/connector-namespace/connector-namespace-hosted-mcp')
+BEGIN
+ INSERT INTO dbo.BlogPosts (Title, Url, Source)
+ VALUES (N'Hosted MCP servers in Azure Connector Namespace', N'https://learn.microsoft.com/en-us/azure/logic-apps/connector-namespace/connector-namespace-hosted-mcp', N'Microsoft Learn');
+END;
+IF NOT EXISTS (SELECT 1 FROM dbo.BlogPosts WHERE Url = N'https://devblogs.microsoft.com/dotnet/durable-workflows-in-microsoft-agent-framework/')
+BEGIN
+ INSERT INTO dbo.BlogPosts (Title, Url, Source)
+ VALUES (N'Durable Workflows in Microsoft Agent Framework', N'https://devblogs.microsoft.com/dotnet/durable-workflows-in-microsoft-agent-framework/', N'.NET Blog');
+END;"
+
+GRANT_SQL="IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = '${CONNECTOR_NS_NAME}')
+BEGIN
+ CREATE USER [${CONNECTOR_NS_NAME}] FROM EXTERNAL PROVIDER;
+END;
+IF ISNULL(IS_ROLEMEMBER('db_datareader', '${CONNECTOR_NS_NAME}'), 0) = 0
+ ALTER ROLE db_datareader ADD MEMBER [${CONNECTOR_NS_NAME}];
+IF ISNULL(IS_ROLEMEMBER('db_datawriter', '${CONNECTOR_NS_NAME}'), 0) = 0
+ ALTER ROLE db_datawriter ADD MEMBER [${CONNECTOR_NS_NAME}];
+GRANT VIEW DEFINITION TO [${CONNECTOR_NS_NAME}];
+IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = '${CONNECTOR_NS_NAME}')
+ THROW 51000, 'Connector Namespace managed identity SQL user was not created.', 1;
+IF ISNULL(IS_ROLEMEMBER('db_datareader', '${CONNECTOR_NS_NAME}'), 0) <> 1
+ THROW 51001, 'Connector Namespace managed identity is not a member of db_datareader.', 1;
+IF ISNULL(IS_ROLEMEMBER('db_datawriter', '${CONNECTOR_NS_NAME}'), 0) <> 1
+ THROW 51002, 'Connector Namespace managed identity is not a member of db_datawriter.', 1;"
+
+if command -v sqlcmd &> /dev/null; then
+ run_sqlcmd_with_firewall_retry "$SEED_SQL"
+ echo " Database seeded."
+ echo "[4/4] Granting Connector Namespace SAMI access..."
+ run_sqlcmd_with_firewall_retry "$GRANT_SQL"
+ echo " SAMI access granted."
+else
+ echo "WARNING: sqlcmd not found. Please run these SQL commands in the Azure Portal Query Editor:"
+ echo ""
+ echo "$SEED_SQL"
+ echo ""
+ echo "$GRANT_SQL"
+ exit 1
+fi
+
+# --- Generate DAB config ---
+echo ""
+echo "Generating dab-config.generated.json..."
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+cat > "$SCRIPT_DIR/../dab-config.generated.json" <$null
+if (-not $currentLogin) {
+ Write-Host "Detecting deployer login from Azure CLI..." -ForegroundColor Yellow
+ $login = az account show --query user.name -o tsv
+ if ($login) {
+ azd env set AZURE_DEPLOYER_LOGIN $login
+ Write-Host " Set AZURE_DEPLOYER_LOGIN=$login" -ForegroundColor Green
+ } else {
+ Write-Host "ERROR: Could not detect login. Run: azd env set AZURE_DEPLOYER_LOGIN your-email@example.com" -ForegroundColor Red
+ exit 1
+ }
+}
+
+# Auto-detect deployer public IP for SQL firewall
+$currentIp = azd env get-value AZURE_DEPLOYER_IP 2>$null
+if (-not $currentIp) {
+ Write-Host "Detecting deployer public IP..." -ForegroundColor Yellow
+ try {
+ $ip = (Invoke-RestMethod -Uri 'https://api.ipify.org' -TimeoutSec 10)
+ azd env set AZURE_DEPLOYER_IP $ip
+ Write-Host " Set AZURE_DEPLOYER_IP=$ip" -ForegroundColor Green
+ } catch {
+ Write-Host "WARNING: Could not detect public IP. SQL post-provision scripts may fail." -ForegroundColor Yellow
+ azd env set AZURE_DEPLOYER_IP ''
+ }
+}
diff --git a/samples/applications/azure-sql-mcp/scripts/pre-provision.sh b/samples/applications/azure-sql-mcp/scripts/pre-provision.sh
new file mode 100644
index 0000000000..510212acc1
--- /dev/null
+++ b/samples/applications/azure-sql-mcp/scripts/pre-provision.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+# pre-provision.sh — Auto-detect deployer login and public IP before provisioning.
+
+set -euo pipefail
+
+# Auto-detect deployer login (email) if not already set
+CURRENT_LOGIN=$(azd env get-value AZURE_DEPLOYER_LOGIN 2>/dev/null || true)
+if [ -z "$CURRENT_LOGIN" ]; then
+ echo "Detecting deployer login from Azure CLI..."
+ LOGIN=$(az account show --query user.name -o tsv)
+ if [ -n "$LOGIN" ]; then
+ azd env set AZURE_DEPLOYER_LOGIN "$LOGIN"
+ echo " Set AZURE_DEPLOYER_LOGIN=$LOGIN"
+ else
+ echo "ERROR: Could not detect login. Run: azd env set AZURE_DEPLOYER_LOGIN your-email@example.com"
+ exit 1
+ fi
+fi
+
+# Auto-detect deployer public IP for SQL firewall
+CURRENT_IP=$(azd env get-value AZURE_DEPLOYER_IP 2>/dev/null || true)
+if [ -z "$CURRENT_IP" ]; then
+ echo "Detecting deployer public IP..."
+ IP=$(curl -s --max-time 10 https://api.ipify.org || true)
+ if [ -n "$IP" ]; then
+ azd env set AZURE_DEPLOYER_IP "$IP"
+ echo " Set AZURE_DEPLOYER_IP=$IP"
+ else
+ echo "WARNING: Could not detect public IP. SQL post-provision scripts may fail."
+ azd env set AZURE_DEPLOYER_IP ""
+ fi
+fi