MCP服务器与Azure APIM集成实验室

通过四步实验实现MCP服务器与Azure API管理(APIM)的集成,包括部署、访问控制、身份验证和OBO流程,最终构建一个安全的AI代理架构。

  • by thiagomendes
  • Shell
  • 本地部署
  • 2 星标
  • 更新于
在 GitHub 查看 thiagomendes/lab-apim-mcp

使用说明

MCP+AAzure APIM零信任架构全实践实验室

这个实验室被设计成一个故事 4法案。您将首先在云中部署一个简单的MCP服务器,并逐步发展您的架构,直到您达到具有身份验证和代表(OBO)流程的复杂企业场景。

目标(目标状态)

在本次旅程结束时,您将拥有一个安全的架构,其中AI代理代表登录用户访问敏感数据(Microsoft Graph),并受到MCP网关的保护。


场景

你是一名平台工程师,负责为公司的开发人员提供人工智能工具。

  1. 第1幕: 证明该概念有效(部署)。
  2. 第2幕: 管理对这些工具的访问(APIM MCP网关)。
  3. 第3幕: 保护周边(身份)。
  4. 第4幕: 确保AI使用用户自己的身份访问敏感数据(OBO Flow)。

先决条件

  • Azure命令行界面 已安装并登录(az login).
  • Azure功能核心工具 (func).
  • Python 3.10+.
  • Visual Studio Code 随着 GitHub Copilot 扩展(或兼容的MCP客户端)。

舞台准备(环境)

为了确保资源名称的唯一性和可重复性,我们将使用设置脚本。

  1. 导航到实验室文件夹:

    cd mcp-lab
    
  2. 配置环境变量(在Bash/WSL终端中运行):

    source env-setup.sh
    

    此命令生成一个随机后缀,并设置以下变量 $GRP, $FUNC, $APIM, $AZURE_CLI_APP_ID 等等让这个终端在整个实验室都保持开放。


第一幕:MCP服务器的诞生

在此阶段,您的目标很简单:让MCP服务器在云中运行(Azure Functions)并可公开访问。

1.1提供基础设施

# Create Resource Group
az group create --name $GRP --location $LOC

# Create Storage Account (Required for Functions)
az storage account create --name $STG --location $LOC --resource-group $GRP --sku Standard_LRS

# Create the Function App (Serverless Linux)
az functionapp create --name $FUNC --storage-account $STG --consumption-plan-location $LOC --resource-group $GRP --runtime python --runtime-version 3.11 --functions-version 4 --os-type Linux

1.2部署代码

当前代码有一个简单的工具(echo_message)以及一个安全的工具(get_my_profile_info).让我们发布它。

# Publish the Function App
func azure functionapp publish $FUNC

1.3烟雾测试

让我们通过测试这个简单的工具来验证MCP服务器是否处于活动状态。

  1. 在VS Code中,编辑 .vscode/mcp.json 文件(在项目的根目录下创建 lab-apim-mcp 如果它不存在):

    {
      "mcpServers": {
        "azure-func-direct": {
          "url": "https://<YOUR_FUNCTION_NAME>.azurewebsites.net/mcp",
          "type": "http"
        }
      }
    }
    

    (替换 <YOUR_FUNCTION_NAME> 与价值 $FUNC.快跑 echo $FUNC 在终端中查看)。

  2. 重新启动VS代码或重新加载窗口。

  3. 打开Copilot Chat并键入:

    @azure-func-direct echo_message message="Hello Azure Function"

成功: 如果Copilot响应“来自Azure的回声:Hello Azure函数”,则动作1完成。


第二幕:守护者(Azure MCP网关)

直接访问该功能是不安全的,难以管理。让我们把 Azure API管理(APIM) 前端充当智能MCP网关。

2.1提供APIM

注意:创建“开发人员”层大约需要30-45分钟。喝咖啡的好时机。

az apim create --name $APIM --resource-group $GRP --location $LOC --publisher-name "Lab Admin" --publisher-email "admin@lab.com" --sku-name Developer

2.2将MCP服务器导入APIM

APIM对MCP服务器(GenAI网关)具有本机支持。我们将通过门户网站进行此操作。

  1. 转到 Azure门户 > API管理 ($APIM).

  2. 在左侧菜单中,选择 MCP服务器.

  3. 点击 +创建 > 暴露现有的MCP服务器.

  4. 按如下方式填写表单字段:

    后端MCP服务器

    • MCP服务器基本url: https://<YOUR_FUNCTION_NAME>.azurewebsites.net/mcp (替换 <YOUR_FUNCTION_NAME> 随着 $FUNC.

    新建MCP服务器

    • 显示名称: MCP Lab Gateway
    • 名字: mcp-lab
    • 基础路径: lab
    • 描述: Azure Function acting as MCP Server
  5. 点击 创建.

注意:为了简单起见,此设置使用公共后端端点。在生产环境中,始终使用专用端点或ExpressRoute限制隔离您的Function App,以防止直接访问。

2.3通过网关进行测试

现在让我们将VS代码指向APIM。

  1. 更新 .vscode/mcp.json:
    {
      "mcpServers": {
        "azure-apim-gw": {
          "url": "https://<YOUR_APIM_NAME>.azure-api.net/lab/mcp",
          "type": "http"
        }
      }
    }
    
  2. 重新启动VS代码。
  3. 再次测试:

    @azure-apim-gw echo_message message="Hello via APIM"

成功: 现在的流程是:VS代码->APIM->函数。


第3幕:身份与安全

现在让我们锁上门。没有有效的徽章(JWT令牌),任何人都不应调用APIM。

3.1创建应用程序标识(后端)

我们需要在Microsoft Entra ID中注册我们的API。

# Create App Registration
BACKEND_APP_ID=$(az ad app create --display-name "mcp-lab-backend-$RND" --sign-in-audience AzureADMyOrg --query appId -o tsv)

# Create Service Principal
az ad sp create --id $BACKEND_APP_ID

# Define API URI (api://<client_id>)
az ad app update --id $BACKEND_APP_ID --identifier-uris "api://$BACKEND_APP_ID"

# Expose "MCP.Execute" scope (Using Python for safe JSON generation)
python3 -c "import sys, uuid, json; scope_id = str(uuid.uuid4()); print(scope_id)" > scope_id.txt
SCOPE_ID=$(cat scope_id.txt) && rm scope_id.txt

python3 -c "import sys, json; scope_id = sys.argv[1]; data = {'oauth2PermissionScopes': [{'adminConsentDescription': 'Access MCP', 'adminConsentDisplayName': 'Access MCP', 'id': scope_id, 'isEnabled': True, 'type': 'User', 'userConsentDescription': 'Access MCP', 'userConsentDisplayName': 'Access MCP', 'value': 'MCP.Execute'}]}; print(json.dumps(data))" "$SCOPE_ID" > scope.json

az ad app update --id $BACKEND_APP_ID --set api=@scope.json
rm scope.json

echo "Backend App ID: $BACKEND_APP_ID"

3.2在APIM中应用策略(验证JWT)

让我们配置APIM以拒绝没有令牌的调用。

  1. 在门户中,转到 MCP服务器 >点击 MCP实验室网关.
  2. 寻找 政策 选项(或 </> 图标在 入站处理).
  3. 在中插入验证策略 <inbound> 块:
<inbound>
    <base />
    <!-- Allow CORS (No credentials/wildcard compatible) -->
    <cors>
        <allowed-origins><origin>*</origin></allowed-origins>
        <allowed-methods><method>*</method></allowed-methods>
        <allowed-headers><header>*</header></allowed-headers>
    </cors>

    <!-- Validate Entra ID Token -->
    <validate-azure-ad-token tenant-id="{{YOUR_TENANT_ID}}" header-name="Authorization">
        <audiences>
            <audience>api://{{YOUR_BACKEND_APP_ID}}</audience>
        </audiences>
    </validate-azure-ad-token>
</inbound>

(替换 {{YOUR_TENANT_ID}} 和 {{YOUR_BACKEND_APP_ID}} 具有实际价值)。

如果你不知道你的租户ID,运行az account show来检索它。

3.3安全测试(预期失败)

尝试使用 @azure-apim-gw echo_message 再次在VS代码中。

预期结果: 您将收到 HTTP 401未经授权 立即出错。这意味着APIM策略正在发挥作用!


第四幕:代表(OBO)流动

“大结局”。我们将让VS Code发送一个令牌,APIM对其进行验证,然后函数将此令牌交换为另一个令牌以读取您的配置文件。

4.1配置机密和权限

该功能需要“代表用户发言”(OBO)的权限。

# 1. Generate Secret for the App
BACKEND_SECRET=$(az ad app credential reset --id $BACKEND_APP_ID --display-name "OBOSecret" --query password -o tsv)

# 2. Grant Graph permission (User.Read) using Global IDs
MS_GRAPH_ID="00000003-0000-0000-c000-000000000000" # Microsoft Graph API
USER_READ_ID="e1fe6dd8-ba31-4d61-89e7-88639da4683d" # User.Read Scope

az ad app permission add --id $BACKEND_APP_ID --api $MS_GRAPH_ID --api-permissions $USER_READ_ID=Scope
az ad app permission grant --id $BACKEND_APP_ID --api $MS_GRAPH_ID --scope User.Read

4.2配置功能应用程序

将凭据发送到云中的Function。

az functionapp config appsettings set --name $FUNC --resource-group $GRP --settings "AZURE_TENANT_ID=$(az account show --query tenantId -o tsv)" "BACKEND_CLIENT_ID=$BACKEND_APP_ID" "BACKEND_CLIENT_SECRET=$BACKEND_SECRET"

4.3配置客户端(VS代码)

我们需要一个真正的令牌。

  1. 预授权: 允许Azure CLI为您的API请求令牌。我们使用 az rest 对CLI模式更改具有鲁棒性。

    # 1. Get Object ID
    BACKEND_OBJ_ID=$(az ad app show --id $BACKEND_APP_ID --query id -o tsv)
    
    # 2. Generate JSON Config (Python one-liner for safe copy-paste)
    python3 -c "import sys, json; scope_id = sys.argv[1]; cli_id = sys.argv[2]; data = {'api': {'oauth2PermissionScopes': [{'adminConsentDescription': 'Access MCP', 'adminConsentDisplayName': 'Access MCP', 'id': scope_id, 'isEnabled': True, 'type': 'User', 'userConsentDescription': 'Access MCP', 'userConsentDisplayName': 'Access MCP', 'value': 'MCP.Execute'}], 'preAuthorizedApplications': [{'appId': cli_id, 'delegatedPermissionIds': [scope_id]}]}}; print(json.dumps(data))" "$SCOPE_ID" "$AZURE_CLI_APP_ID" > patch.json
    
    # 3. Apply via Graph API
    az rest --method PATCH --uri "https://graph.microsoft.com/v1.0/applications/$BACKEND_OBJ_ID" --headers "Content-Type=application/json" --body @patch.json
    rm patch.json
    
  2. 生成令牌:

    # Login (Refreshes session)
    az login --tenant $(az account show --query tenantId -o tsv)
    
    # Get Token
    az account get-access-token --resource "api://$BACKEND_APP_ID" --query accessToken -o tsv
    

    复制生成的令牌。

  3. 配置MCP客户端: 更新 .vscode/mcp.json 包含您的令牌 直接. (注意:我们在这里粘贴令牌是因为一些VS Code扩展尚不支持交互式输入提示。)

    {
      "mcpServers": {
        "azure-obo-final": {
          "url": "https://<YOUR_APIM_NAME>.azure-api.net/lab/mcp",
          "type": "http",
          "headers": {
            "Authorization": "Bearer YOUR_LONG_TOKEN_HERE"
          }
        }
      }
    }
    

4.4最终测试

  1. 重新加载VS Code窗口。
  2. 打开Copilot Chat并调用安全工具:

    @azure-obo-final get_my_profile_info

预期结果: 工具应作出响应 “成功!OBO Flow工作正常”,显示从Microsoft Graph安全检索到的您的姓名和职位。


祝贺 你已经完成了旅程。

清理

不要忘记删除此实验室期间创建的所有资源,以避免持续成本。

az group delete --name $GRP --yes --no-wait