在 API 管理中以编程方式管理 MCP 服务器

本文介绍如何使用 REST API、ARM 模板、Bicep、Azure CLI和 Terraform 在 Azure API 管理 中创建和管理 MCP 服务器。 

Important

本文中所述的 MCP 服务器管理功能需要 API 管理 REST API 版本 2025-09-01-preview 或更高版本。 在每个请求中固定此版本。 

有关 MCP 服务器功能的背景信息,请参阅Azure API 管理中的关于 MCP 服务器

先决条件

  • 你的身份需要具备读取 API 管理服务以及创建或更新 API、API 工具、API 策略和产品 API 绑定关系的权限。 对于 Terraform 而言,该标识还需要对 azurerm_api_management 数据源使用的现有 API 管理服务具有读取权限。

  • 对于 Azure CLI:

    • 如需在本地运行 CLI 参考命令,请安装 Azure CLI。 如果在 Windows 或 macOS 上运行,请考虑在 Docker 容器中运行 Azure CLI。 有关详细信息,请参阅如何在 Docker 容器中运行 Azure CLI

      • 如果使用本地安装,请使用 az login 命令登录到Azure CLI。 若要完成身份验证过程,请遵循终端中显示的步骤。 有关其他登录选项,请参阅使用 Azure CLI 登录

      • 出现提示时,请在首次使用时安装 Azure CLI 扩展。 有关扩展的详细信息,请参阅 将扩展与 Azure CLI 配合使用

      • 运行az version命令,以查看已安装的版本和依赖库。 若要升级到最新版本,请运行az upgrade

  • 对于 Azure PowerShell:

  • 对于 Terraform: 安装和配置 Terraform

资源模型

Azure 资源管理器 将 MCP 服务器表示如下:

  • MCP server: 类型为的 API Management API 资源。

  • 直通服务器: 指向现有外部 MCP 后端。 MCP 服务器资源声明后端 URL 和传输类型(可流式传输 HTTP 或 SSE)。

  • 工具: MCP 服务器的 API 工具 子资源。 可以从 CI/CD 安全地管理工具资源。 无需重新创建 MCP 服务器,即可添加、重命名或删除工具。

  • 政策: 与常规 API 一样,将 API 策略策略 子资源附加到 MCP 服务器。

  • 产品: 产品绑定是单独的子关系(products/{productId}/apis/{mcpServerId}),可实现独立的部署和多产品绑定。

REST 示例

为了清楚起见,以下示例显示了缩写的响应正文。 有关完整响应架构,请参阅 API 管理 REST API 参考

在 PUT 和 DELETE 调用示例中添加 If-Match: * 标头,会使这些请求具有幂等性。 无论资源是否已存在,此标头都适用,这是 CI/CD 管道的推荐模式。

在您开始之前

运行任何示例之前,请设置以下变量。 本节中的所有示例都引用这些变量。

SUBSCRIPTION_ID="<your-subscription-id>"
RESOURCE_GROUP="<your-resource-group>"
APIM_NAME="<your-api-management-service-name>"
API_VERSION="2025-09-01-preview"
BASE_URL="https://management.chinacloudapi.cn/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP}/providers/Microsoft.ApiManagement/service/${APIM_NAME}"
TOKEN=$(az account get-access-token --resource https://management.chinacloudapi.cn --query accessToken -o tsv)

列出 MCP 服务器

返回筛选为类型 mcp实例中的所有 API。 使用 $top$skip 查询参数对大型结果集进行分页。

参考: Api - 按服务列出

curl -sG "${BASE_URL}/apis" \
  --data-urlencode "api-version=${API_VERSION}" \
  --data-urlencode "\$filter=type eq 'mcp'" \
  -H "Authorization: Bearer ${TOKEN}"

响应(200 OK)

{
  "count": 1,
  "value": [
    {
      "id": "/subscriptions/.../apis/my-mcp-server",
      "name": "my-mcp-server",
      "type": "Microsoft.ApiManagement/service/apis",
      "properties": {
        "type": "mcp",
        "displayName": "My MCP Server",
        "path": "my-mcp",
        "protocols": [ "https" ]
      }
    }
  ]
}

常见错误:401 Unauthorized. 持有者令牌已过期。 重新运行令牌获取命令。


获取单个 MCP 服务器

参考:API - 获取

MCP_SERVER_ID="my-mcp-server"

curl -s "${BASE_URL}/apis/${MCP_SERVER_ID}?api-version=${API_VERSION}" \
  -H "Authorization: Bearer ${TOKEN}"

响应(200 OK)

{
  "id": "/subscriptions/.../apis/my-mcp-server",
  "name": "my-mcp-server",
  "type": "Microsoft.ApiManagement/service/apis",
  "properties": {
    "type": "mcp",
    "displayName": "My MCP Server",
    "path": "my-mcp",
    "protocols": [ "https" ],
    "serviceUrl": "https://api.contoso.com"
  }
}

常见错误:404 Not Found. 确认 mcpServerId 与 List 操作返回的 name 字段匹配。


创建 REST API 支持的 MCP 服务器

创建 MCP 服务器资源。 创建后,使用 “添加”或更新工具 操作单独向其添加工具。 每个工具引用支持 REST API 资源中的特定操作。

参考: Api - 创建或更新

MCP_SERVER_ID="my-mcp-server"

curl -s -X PUT \
  "${BASE_URL}/apis/${MCP_SERVER_ID}?api-version=${API_VERSION}" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -H "If-Match: *" \
  -d '{
    "properties": {
      "type": "mcp",
      "path": "my-mcp",
      "displayName": "My MCP Server",
      "description": "MCP server backed by a REST API",
      "protocols": ["https"]
    }
  }'

响应(201 已创建)

{
  "id": "/subscriptions/.../apis/my-mcp-server",
  "name": "my-mcp-server",
  "type": "Microsoft.ApiManagement/service/apis",
  "properties": {
    "type": "mcp",
    "displayName": "My MCP Server",
    "path": "my-mcp",
    "protocols": ["https"],
    "provisioningState": "InProgress"
  }
}

Note

对于异步 PUT 操作,应使用 provisioningState: InProgress。 轮询响应头中返回的 Azure-AsyncOperation URL,以确认操作已完成。

常见错误:400 Bad Request. 确保type"mcp",且path在服务实例中是唯一的。


创建透传 MCP 服务器

直通服务器将所有 MCP 请求直接转发到外部 MCP 后端。 将 mcpProperties.transportType 设置为与你的后端实现的传输方式相匹配的值。

在创建直通服务器之前,请确认后端可从 API 管理网关访问,并在配置的终结点路径上实现所选 MCP 传输。 如果后端需要身份验证,请使用 API 管理策略或后端配置配置所需的凭据或标头。

参考: Api - 创建或更新

可以流式传输的 HTTP

对于实现了当前 MCP 可流式传输的 HTTP 传输规范的后端,请使用 streamable。 需要单个终结点定义。

MCP_SERVER_ID="my-mcp-passthrough"

curl -s -X PUT \
  "${BASE_URL}/apis/${MCP_SERVER_ID}?api-version=${API_VERSION}" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -H "If-Match: *" \
  -d '{
    "properties": {
      "type": "mcp",
      "path": "my-mcp-passthrough",
      "displayName": "My Passthrough MCP Server",
      "description": "Passthrough MCP server using streamable HTTP transport",
      "protocols": ["https"],
      "serviceUrl": "https://mcp-backend.contoso.com",
      "mcpProperties": {
        "transportType": "streamable",
        "endpoints": [
          { "name": "message", "uriTemplate": "/mcp" }
        ]
      }
    }
  }'

响应(201 已创建)

{
  "id": "/subscriptions/.../apis/my-mcp-passthrough",
  "name": "my-mcp-passthrough",
  "type": "Microsoft.ApiManagement/service/apis",
  "properties": {
    "type": "mcp",
    "displayName": "My Passthrough MCP Server",
    "path": "my-mcp-passthrough",
    "protocols": ["https"],
    "serviceUrl": "https://mcp-backend.contoso.com",
    "provisioningState": "InProgress",
    "mcpProperties": {
      "transportType": "streamable",
      "endpoints": [ { "name": "message", "uriTemplate": "/mcp" } ]
    }
  }
}

SSE 传输

对于实现 HTTP+SSE(服务器发送事件)传输的后端,请使用 sse。 定义两个终结点:一个用于 SSE 事件流,一个用于消息通道。

MCP_SERVER_ID="my-mcp-sse"

curl -s -X PUT \
  "${BASE_URL}/apis/${MCP_SERVER_ID}?api-version=${API_VERSION}" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -H "If-Match: *" \
  -d '{
    "properties": {
      "type": "mcp",
      "path": "my-mcp-sse",
      "displayName": "My SSE MCP Server",
      "description": "Passthrough MCP server using SSE transport",
      "protocols": ["https"],
      "serviceUrl": "https://mcp-backend.contoso.com",
      "mcpProperties": {
        "transportType": "sse",
        "endpoints": [
          { "name": "sse",     "uriTemplate": "/sse" },
          { "name": "message", "uriTemplate": "/messages" }
        ]
      }
    }
  }'

常见错误:

  • 400 Bad RequestmcpProperties 无效。 验证 transportType 是否 streamablesse,并且每个 uriTemplate 开头都以 /
  • 400 Bad Request。 SSE 传输需要两个终结点(ssemessage)。 流式传输需要一个(message)。

添加或更新工具

将新工具添加到 REST API 支持的 MCP 服务器,或更新现有工具。 该 operationId 字段将该工具链接到支持 REST API 资源中的特定操作。 无需重新创建父服务器,即可独立添加、更新或删除工具。

参考: API 工具 - 创建或更新

MCP_SERVER_ID="my-mcp-server"
TOOL_ID="listOrders"
BACKING_API_ID="orders-api"
BACKING_OP_ID="list-orders"
OP_ID="/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP}/providers/Microsoft.ApiManagement/service/${APIM_NAME}/apis/${BACKING_API_ID}/operations/${BACKING_OP_ID}"

curl -s -X PUT \
  "${BASE_URL}/apis/${MCP_SERVER_ID}/tools/${TOOL_ID}?api-version=${API_VERSION}" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -H "If-Match: *" \
  --data-raw "{
    \"properties\": {
      \"displayName\": \"listOrders\",
      \"description\": \"List all orders for a customer\",
      \"operationId\": \"${OP_ID}\"
    }
  }"

响应(201 已创建)

{
  "id": "/subscriptions/.../apis/my-mcp-server/tools/listOrders",
  "name": "listOrders",
  "type": "Microsoft.ApiManagement/service/apis/tools",
  "properties": {
    "displayName": "listOrders",
    "description": "List all orders for a customer",
    "operationId": "/subscriptions/.../apis/orders-api/operations/list-orders"
  }
}

常见错误:

  • 400 Bad RequestoperationId路径格式不正确或引用的操作不存在。
  • 404 Not Found。 父 MCP 服务器不存在。 在添加工具之前创建服务器。

删除工具

从 MCP 服务器中删除工具。 在删除它们引用的后备 REST API 操作之前删除工具;否则,删除失败并出现依赖项错误。

参考: API 工具 - 删除

MCP_SERVER_ID="my-mcp-server"
TOOL_ID="listOrders"

curl -s -X DELETE \
  "${BASE_URL}/apis/${MCP_SERVER_ID}/tools/${TOOL_ID}?api-version=${API_VERSION}" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "If-Match: *"

响应:200 OK 成功时返回。

常见错误:412 Precondition FailedIf-Match 需要删除。 使用 If-Match: * 来匹配任意 ETag。


在 MCP 范围内应用策略

创建或替换附加到 MCP 服务器的策略文档。 服务器在此范围内评估每个工具调用的策略。 格式 rawxml 接受未编码的策略 XML。

参考: Api 策略 - 创建或更新

MCP_SERVER_ID="my-mcp-server"
POLICY='<policies><inbound><base /><rate-limit calls="100" renewal-period="60" /></inbound><backend><forward-request /></backend><outbound><base /></outbound></policies>'

curl -s -X PUT \
  "${BASE_URL}/apis/${MCP_SERVER_ID}/policies/policy?api-version=${API_VERSION}" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -H "If-Match: *" \
  --data-raw "{\"properties\":{\"format\":\"rawxml\",\"value\":\"${POLICY}\"}}"

响应(200 OK)

{
  "id": "/subscriptions/.../apis/my-mcp-server/policies/policy",
  "name": "policy",
  "type": "Microsoft.ApiManagement/service/apis/policies",
  "properties": {
    "value": "<policies>...</policies>"
  }
}

常见错误:400 Bad Request. 策略 XML 格式错误。 在发送之前验证文档。


将 MCP 服务器绑定到产品

将 MCP 服务器与产品相关联,因此该产品的订阅者可以调用服务器的工具。 请求没有正文。

将 MCP 服务器绑定到某个产品时,可以通过该产品使用它,但客户端仍需要根据产品的配置进行访问。 如果产品需要订阅,客户端必须为该产品使用有效的订阅密钥。

参考: 产品 API - 创建或更新

MCP_SERVER_ID="my-mcp-server"
PRODUCT_ID="my-product"

curl -s -X PUT \
  "${BASE_URL}/products/${PRODUCT_ID}/apis/${MCP_SERVER_ID}?api-version=${API_VERSION}" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Length: 0"

响应:201 Created正文中包含 MCP 服务器的 API 契约。

常见错误:404 Not Found. 在创建绑定之前,请验证这两者是否存在productIdmcpServerId


删除 MCP 服务器

删除 MCP 服务器及其所有工具和策略子资源。 在删除服务器之前,请删除引用支持 API 中操作的任何工具;否则,不能在工具引用存在时删除这些操作。

参考:API - 删除

MCP_SERVER_ID="my-mcp-server"

curl -s -X DELETE \
  "${BASE_URL}/apis/${MCP_SERVER_ID}?api-version=${API_VERSION}" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "If-Match: *"

响应:200 OK 成功时返回。

常见错误:412 Precondition Failed. 删除操作需要 If-Match。 使用 If-Match: * 可绕过 ETag 检查。

ARM 和 Bicep 模板

以下模板在单个部署中部署完整的 MCP 服务器配置。 每个模板假设有一个现有的 API 管理服务实例,并使用参数表,以便通过仅更改参数值,在环境中重复使用同一文件。

REST API 支持的 MCP 服务器

这些模板创建 MCP 服务器,定义一个工具,该工具映射到现有支持 REST API 中的操作,在服务器范围内附加速率限制策略,并将服务器绑定到现有产品。 可以在单个部署中执行所有这些任务。

先决条件:现有 API 管理服务、至少有一个操作()的 REST API(backingApiIdbackingOperationId)和现有产品(productId)。

参数 必需 默认 说明
serviceName 是的 现有 API 管理服务实例的名称。
mcpServerId orders-mcp 新 MCP 服务器的资源名称。 在服务中必须是唯一的。
backingApiId 是的 用于备份此 MCP 服务器的现有 REST API 的资源名称。
backingOperationId 是的 要作为工具公开的操作的资源名称。
toolId sampleTool 要创建的 MCP 工具的资源名称和显示名称。
productId starter 要将服务器绑定到的现有产品的资源名称。
@description('Name of the existing API Management service instance.')
param serviceName string

@description('Resource name for the new MCP server.')
param mcpServerId string = 'orders-mcp'

@description('Resource name of the existing REST API that backs this MCP server.')
param backingApiId string

@description('Resource name of the operation in the backing REST API to expose as a tool.')
param backingOperationId string

@description('Resource name and display name of the MCP tool to create.')
param toolId string = 'sampleTool'

@description('Resource name of the existing product to bind the MCP server to.')
param productId string = 'starter'

resource apimService 'Microsoft.ApiManagement/service@2025-09-01-preview' existing = {
  name: serviceName
}

resource mcpServer 'Microsoft.ApiManagement/service/apis@2025-09-01-preview' = {
  parent: apimService
  name: mcpServerId
  properties: {
    type: 'mcp'
    displayName: 'Orders MCP Server'
    description: 'MCP server backed by the Orders REST API'
    path: mcpServerId
    protocols: [ 'https' ]
    subscriptionRequired: true
  }
}

resource mcpTool 'Microsoft.ApiManagement/service/apis/tools@2025-09-01-preview' = {
  parent: mcpServer
  name: toolId
  properties: {
    displayName: toolId
    description: 'MCP tool backed by an API operation'
    operationId: resourceId(
      'Microsoft.ApiManagement/service/apis/operations',
      serviceName, backingApiId, backingOperationId
    )
  }
}

resource mcpPolicy 'Microsoft.ApiManagement/service/apis/policies@2025-09-01-preview' = {
  parent: mcpServer
  name: 'policy'
  properties: {
    format: 'rawxml'
    value: '''<policies>
  <inbound>
    <base />
    <rate-limit calls="100" renewal-period="60" />
  </inbound>
  <backend>
    <forward-request />
  </backend>
  <outbound>
    <base />
  </outbound>
</policies>'''
  }
}

resource product 'Microsoft.ApiManagement/service/products@2025-09-01-preview' existing = {
  parent: apimService
  name: productId
}

resource productBinding 'Microsoft.ApiManagement/service/products/apis@2025-09-01-preview' = {
  parent: product
  name: mcpServerId
  dependsOn: [ mcpServer ]
}

部署:

# Use orders-mcp.json if you're deploying the ARM template.
az deployment group create \
  --resource-group <resource-group> \
  --template-file orders-mcp.bicep \
  --parameters serviceName=<api-management-name> \
               backingApiId=orders-api \
               backingOperationId=get-orders \
               toolId=getOrders

透传 MCP 服务器

这些模板会创建一个使用支持流式传输的 HTTP 传输的透传 MCP 服务器。 服务器范围具有速率限制策略,服务器绑定到现有产品。 模板不定义任何工具子资源。 外部后端决定工具界面。

Note

以下模板使用 transportType: streamable,它实现当前的 MCP 可流式传输 HTTP 规范。 若要改用 SSE 传输,请将 transportType 设置为 sse,并将 endpoints 数组替换为两个条目:{ "name": "sse", "uriTemplate": "/sse" }{ "name": "message", "uriTemplate": "/messages" }。 在Bicep中,对单引号字符串使用相同的值。

先决条件: 现有的 API 管理服务、可实现所选传输和终结点路径的可访问 MCP 后端 URLbackendUrl 以及现有产品(productId)。

参数 必需 默认 说明
serviceName 是的 现有 API 管理服务实例的名称。
mcpServerId external-mcp 新 MCP 服务器的资源名称。 在服务中必须是唯一的。
backendUrl 是的 外部 MCP 后端的绝对网址。
productId starter 要将服务器绑定到的现有产品的资源名称。
@description('Name of the existing API Management service instance.')
param serviceName string

@description('Resource name for the new MCP server.')
param mcpServerId string = 'external-mcp'

@description('Absolute URL of the external MCP backend.')
param backendUrl string

@description('Resource name of the existing product to bind the MCP server to.')
param productId string = 'starter'

resource apimService 'Microsoft.ApiManagement/service@2025-09-01-preview' existing = {
  name: serviceName
}

resource mcpServer 'Microsoft.ApiManagement/service/apis@2025-09-01-preview' = {
  parent: apimService
  name: mcpServerId
  properties: {
    type: 'mcp'
    displayName: 'External MCP Server'
    description: 'Passthrough MCP server using streamable HTTP transport'
    path: mcpServerId
    protocols: [ 'https' ]
    serviceUrl: backendUrl
    subscriptionRequired: true
    mcpProperties: {
      transportType: 'streamable'
      endpoints: [
        {
          name: 'message'
          uriTemplate: '/mcp'
        }
      ]
    }
  }
}

resource mcpPolicy 'Microsoft.ApiManagement/service/apis/policies@2025-09-01-preview' = {
  parent: mcpServer
  name: 'policy'
  properties: {
    format: 'rawxml'
    value: '''<policies>
  <inbound>
    <base />
    <rate-limit calls="100" renewal-period="60" />
  </inbound>
  <backend>
    <forward-request />
  </backend>
  <outbound>
    <base />
  </outbound>
</policies>'''
  }
}

resource product 'Microsoft.ApiManagement/service/products@2025-09-01-preview' existing = {
  parent: apimService
  name: productId
}

resource productBinding 'Microsoft.ApiManagement/service/products/apis@2025-09-01-preview' = {
  parent: product
  name: mcpServerId
  dependsOn: [ mcpServer ]
}

部署:

# Use external-mcp.json if you're deploying the ARM template.
az deployment group create \
  --resource-group <resource-group> \
  --template-file external-mcp.bicep \
  --parameters serviceName=<api-management-name> \
               backendUrl=https://mcp-backend.contoso.com

Azure CLI

目前,可用于 az rest 直接调用 REST API。 以下脚本创建直通 MCP 服务器,附加速率限制策略,并将其绑定到产品。 此流程涵盖了与上一部分中的 Bicep 模板相同的场景。

设置变量,然后按顺序运行四 az rest 个调用。

Note

az rest 使用当前 az login 会话中的凭据。 不需要单独的身份验证步骤。

# Variables. Edit these for your environment
SUBSCRIPTION_ID=$(az account show --query id -o tsv)
RESOURCE_GROUP="<your-resource-group>"
APIM_NAME="<your-apim-service-name>"
MCP_SERVER_ID="external-mcp"
BACKEND_URL="https://mcp-backend.contoso.com"
PRODUCT_ID="starter"
API_VERSION="2025-09-01-preview"

BASE="https://management.chinacloudapi.cn/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP}/providers/Microsoft.ApiManagement/service/${APIM_NAME}"

# 1. Create the passthrough MCP server

az rest --method PUT \
  --uri "${BASE}/apis/${MCP_SERVER_ID}?api-version=${API_VERSION}" \
  --headers "If-Match=*" \
  --body '{
    "properties": {
      "type": "mcp",
      "displayName": "External MCP Server",
      "description": "Passthrough MCP server using streamable HTTP transport",
      "path": "external-mcp",
      "protocols": ["https"],
      "serviceUrl": "'"${BACKEND_URL}"'",
      "subscriptionRequired": true,
      "mcpProperties": {
        "transportType": "streamable",
        "endpoints": [
          { "name": "message", "uriTemplate": "/mcp" }
        ]
      }
    }
  }'

# 2. Attach a rate-limit policy at the server scope

az rest --method PUT \
  --uri "${BASE}/apis/${MCP_SERVER_ID}/policies/policy?api-version=${API_VERSION}" \
  --headers "If-Match=*" \
  --body '{
    "properties": {
      "format": "rawxml",
      "value": "<policies><inbound><base /><rate-limit calls=\"100\" renewal-period=\"60\" /></inbound><backend><forward-request /></backend><outbound><base /></outbound></policies>"
    }
  }'


# 3. Bind the server to a product

az rest --method PUT \
  --uri "${BASE}/products/${PRODUCT_ID}/apis/${MCP_SERVER_ID}?api-version=${API_VERSION}"

每个步骤都是幂等的。 重新运行该脚本将就地更新该资源。 若要验证是否已创建服务器,请运行以下命令:

az rest --method GET \
  --uri "${BASE}/apis/${MCP_SERVER_ID}?api-version=${API_VERSION}"

Terraform

AzureRM Terraform 提供程序尚未提供适用于 MCP 服务器的原生资源。 目前,可以使用 azapi_resource中的资源类型,这样就可以针对任何 API 版本管理任何Azure资源类型。 以下示例与直通 MCP 服务器的 Bicep 模板一致。

先决条件: 现有的 API 管理服务、可访问的 MCP 后端 URL 和现有产品。 如果 terraform 块中尚未包含 AzAPI 提供程序,请将其添加进去。

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = ">= 3.0"
    }
    azapi = {
      source  = "Azure/azapi"
      version = ">= 1.13"
    }
  }
}

provider "azurerm" {
  features {}
}

provider "azapi" {}

Variables

variable "resource_group_name" {
  description = "Name of the resource group containing the API Management service."
  type        = string
}

variable "service_name" {
  description = "Name of the existing API Management service instance."
  type        = string
}

variable "mcp_server_id" {
  description = "Resource name for the new MCP server."
  type        = string
  default     = "external-mcp"
}

variable "backend_url" {
  description = "Absolute URL of the external MCP backend."
  type        = string
}

variable "product_id" {
  description = "Resource name of the existing product to bind the server to."
  type        = string
  default     = "starter"
}

Resources

# Reference the existing API Management service
data "azurerm_api_management" "apim" {
  name                = var.service_name
  resource_group_name = var.resource_group_name
}

# 1. Create the passthrough MCP server
resource "azapi_resource" "mcp_server" {
  type      = "Microsoft.ApiManagement/service/apis@2025-09-01-preview"
  name      = var.mcp_server_id
  parent_id = data.azurerm_api_management.apim.id

  body = {
    properties = {
      type                = "mcp"
      displayName         = "External MCP Server"
      description         = "Passthrough MCP server using streamable HTTP transport"
      path                = var.mcp_server_id
      protocols           = ["https"]
      serviceUrl          = var.backend_url
      subscriptionRequired = true
      mcpProperties = {
        transportType = "streamable"
        endpoints = [
          {
            name        = "message"
            uriTemplate = "/mcp"
          }
        ]
      }
    }
  }
}

# 2. Attach a rate-limit policy at the server scope
resource "azapi_resource" "mcp_policy" {
  type      = "Microsoft.ApiManagement/service/apis/policies@2025-09-01-preview"
  name      = "policy"
  parent_id = azapi_resource.mcp_server.id

  body = {
    properties = {
      format = "rawxml"
      value  = "<policies><inbound><base /><rate-limit calls=\"100\" renewal-period=\"60\" /></inbound><backend><forward-request /></backend><outbound><base /></outbound></policies>"
    }
  }

  depends_on = [azapi_resource.mcp_server]
}

# 3. Bind the server to a product
resource "azapi_resource" "product_binding" {
  type      = "Microsoft.ApiManagement/service/products/apis@2025-09-01-preview"
  name      = var.mcp_server_id
  parent_id = "${data.azurerm_api_management.apim.id}/products/${var.product_id}"

  body = {}

  depends_on = [azapi_resource.mcp_server]
}

部署:

Note

azapi_resource使用 AzAPI 提供程序的身份验证,该身份验证从与Azure CLI相同的az login凭据读取。 在本地运行时,无需配置单独的身份验证。

terraform init
terraform apply \
  -var="resource_group_name=<resource-group>" \
  -var="service_name=<api-management-name>" \
  -var="backend_url=https://mcp-backend.contoso.com"

CI/CD 模式

  • 幂等插入或更新:发送带有 If-Match: "*" 的 PUT 请求,这样无论资源是否已存在,都可应用同一模板。 

  • 跨环境提升配置:将 MCP 服务器定义和工具列表视为源代码管理的项目。 仅参数化特定于环境的值,例如实例名称和后端 URL。 

  • 从 API 规范生成工具列表:根据源 OpenAPI 文件生成并维护工具子资源,使工具接口随着底层 API 的演进而始终保持同步。 

  • 按正确的顺序删除:删除 MCP 工具引用,然后再删除它们指向的后盾 API 或操作。 否则,删除操作会因外键检查失败。