API Management で MCP サーバーをプログラムで管理する

この記事では、REST API、ARM テンプレート、Bicep、Azure CLI、Terraform を使用して、Azure API Managementで MCP サーバーを作成および管理する方法について説明します。 

Important

この記事で説明する MCP サーバー管理機能には、API Management REST API バージョン 2025-09-01-preview 以降が必要です。 すべての要求にこのバージョンをピン留めします。 

MCP サーバー機能の背景については、Azure API Managementの MCP サーバーについてを参照してください。

Prerequisites

  • ID には、API Management サービスを読み取り、API、API ツール、API ポリシー、および製品 API バインドを作成または更新するためのアクセス許可が必要です。 Terraform の場合、ID には、 azurerm_api_management データ ソースによって使用される既存の API Management サービスへの読み取りアクセスも必要です。

  • Azure CLI の場合:

    • Azure Cloud Shell で Bash 環境を使用します。 詳細については、「Get started with Azure Cloud Shell」を参照してください。

    • CLI 参照コマンドをローカルで実行する場合は、Azure CLI を インストール します。 Windows または macOS で実行している場合は、Docker コンテナーで Azure CLI を実行することを検討してください。 詳細については、「Docker コンテナーで Azure CLI を実行する方法」を参照してください。

      • ローカル インストールを使用する場合は、az login コマンドを使用して Azure CLI にサインインします。 認証プロセスを完了するには、ターミナルに表示される手順に従います。 他のサインインオプションについては、「Azure CLI を使用して Azure に認証する」を参照してください。

      • 初回使用時にインストールを求められたら、Azure CLI 拡張機能をインストールします。 拡張機能の詳細については、「Azure CLI で拡張機能を使用および管理する」を参照してください。

      • az version を実行して、インストールされているバージョンと依存ライブラリを検索します。 最新バージョンにアップグレードするには、az upgrade を実行します。

  • Azure PowerShell の場合:

  • Terraform の場合: Terraform をインストールして構成する

リソース モデル

Azure Resource Managerは、MCP サーバーを次のように表します。

  • MCP サーバー:種類の An API Management MCP リソース。

  • パススルー サーバー: 既存の外部 MCP バックエンドをポイントします。 MCP サーバー リソースは、バックエンド URL とトランスポートの種類 (ストリーミング可能な HTTP または SSE) を宣言します。

  • ツール: MCP サーバーの API ツール サブリソース。 CI/CD からツール リソースを安全に管理できます。 MCP サーバーを再作成せずに、ツールの追加、名前変更、または削除を行うことができます。

  • ポリシー: 通常の API と同様に、 API ポリシー または ポリシー サブリソースを MCP サーバーにアタッチします。

  • 製品: 製品バインドは別の子リレーションシップ (products/{productId}/apis/{mcpServerId}) であり、独立した展開と複数製品のバインドを有効にします。

REST の例

わかりやすくするために、次の例は省略された応答本文を示しています。 完全な応答スキーマについては、 API Management 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.azure.com/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP}/providers/Microsoft.ApiManagement/service/${APIM_NAME}"
TOKEN=$(az account get-access-token --resource https://management.azure.com --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 - Get

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 FoundmcpServerIdが、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

provisioningState: InProgress は非同期 PUT 操作に必要です。 Azure-AsyncOperation応答ヘッダーに返された URL をポーリングして、完了を確認します。

一般的なエラー:400 Bad Requesttype"mcp"され、pathがサービス インスタンス内で一意であることを確認します。


パススルー MCP サーバーを作成する

パススルー サーバーは、すべての MCP 要求を外部 MCP バックエンドに直接転送します。 バックエンドが実装するトランスポートに一致するように mcpProperties.transportType を設定します。

パススルー サーバーを作成する前に、バックエンドが API Management ゲートウェイから到達可能であることを確認し、構成したエンドポイント パスで選択した MCP トランスポートを実装します。 バックエンドで認証が必要な場合は、API Management ポリシーまたはバックエンド構成を使用して、必要な資格情報またはヘッダーを構成します。

リファレンス: 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 (Server-Sent イベント) トランスポートを実装するバックエンドには、 sse を使用します。 2 つのエンドポイントを定義します。1 つは SSE イベント ストリーム用、1 つはメッセージ チャネル用です。

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 が無効です。 transportTypestreamableまたはsseであり、すべてのuriTemplate/で始まるかどうかを確認します。
  • 400 Bad Request。 SSE トランスポートには、正確に 2 つのエンドポイント (ssemessage) が必要です。 ストリーミング可能なトランスポートには 1 つ (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 Failed — 削除には If-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 サーバーを製品にバインドする場合は、その製品を介して MCP サーバーを使用できるようになりますが、クライアントは製品の構成に従ってアクセスする必要があります。 製品にサブスクリプションが必要な場合、クライアントはその製品の有効なサブスクリプション キーを使用する必要があります。

リファレンス: Product 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 FailedIf-Match は削除に必要です。 ETag チェックをバイパスするには、 If-Match: * を使用します。

ARM テンプレートとBicep テンプレート

次のテンプレートは、1 つのデプロイで完全な MCP サーバー構成をデプロイします。 各テンプレートは、既存の API Management サービス インスタンスを前提とし、パラメーター テーブルを使用するため、パラメーター値のみを変更することで、複数の環境で同じファイルを再利用できます。

REST API ベースの MCP サーバー

これらのテンプレートは、MCP サーバーを作成し、既存のバッキング REST API の操作にマップする 1 つのツールを定義し、サーバー スコープでレート制限ポリシーをアタッチして、サーバーを既存の製品にバインドします。 これらのタスクはすべて、1 つのデプロイで実行できます。

前提条件: 既存の API Management サービス、少なくとも 1 つの操作 (backingApiId) を含む REST API (backingOperationId)、既存の製品 (productId)。

Parameter 必須 Default Description
serviceName はい 既存の API Management サービス インスタンスの名前。
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

次のテンプレートでは、現在の MCP ストリーミング可能 HTTP 仕様を実装する transportType: streamableを使用します。 代わりに SSE トランスポートを使用するには、 transportTypesse に設定し、 endpoints 配列を { "name": "sse", "uriTemplate": "/sse" }{ "name": "message", "uriTemplate": "/messages" } の 2 つのエントリに置き換えます。 Bicepでは、一重引用符で囲まれた文字列と同じ値を使用します。

前提条件: 既存の API Management サービス、選択したトランスポートパスとエンドポイント パスを実装する到達可能な MCP バックエンド URL (backendUrl)、既存の製品 (productId)。

Parameter 必須 Default Description
serviceName はい 既存の API Management サービス インスタンスの名前。
mcpServerId いいえ external-mcp 新しい MCP サーバーのリソース名。 サービス内で一意である必要があります。
backendUrl はい 外部 MCP バックエンドの絶対 URL。
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 テンプレートと同じシナリオについて説明します。

変数を設定し、4 つの 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.azure.com/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 Management サービス、到達可能な MCP バックエンド URL、および既存の製品。 AzAPI プロバイダーがまだ存在しない場合は、 terraform ブロックに追加します。

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

provider "azurerm" {
  features {}
}

provider "azapi" {}

変数

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 または操作を削除します。 それ以外の場合、外部キーチェックで削除は失敗します。