Gérer les serveurs MCP par programmation dans Gestion des API

Dans cet article, vous allez apprendre à créer et gérer des serveurs MCP dans Gestion des API Azure à l’aide de l’API REST, des modèles ARM, des Bicep, des Azure CLI et terraform. 

Important

Les fonctionnalités de gestion des serveurs MCP décrites dans cet article nécessitent l’API REST d’API Management, version 2025-09-01-preview ou une version ultérieure. Épinglez cette version dans chaque requête. 

Pour plus d’informations sur les fonctionnalités du serveur MCP, consultez À propos des serveurs MCP dans Gestion des API Azure.

Prerequisites

Modèle de ressource

Azure Resource Manager représente les serveurs MCP comme suit :

  • Serveur MCP : ressource API d'API Management de typeMCP.

  • Serveur de relais : Pointe vers un backend MCP externe existant. La ressource de serveur MCP déclare l’URL du serveur principal et le type de transport (HTTP ou SSE pouvant être diffusé en continu).

  • Outil: Sous-ressource de l’outil API d’un serveur MCP. Vous pouvez gérer en toute sécurité les ressources d’outils à partir de CI/CD. Vous pouvez ajouter, renommer ou supprimer des outils sans recréer le serveur MCP.

  • Politiques: Comme avec les API régulières, attachez une stratégie d’API ou des sous-ressources de stratégie à un serveur MCP.

  • Produits : la liaison de produit est une relation enfant distincte (products/{productId}/apis/{mcpServerId}), permettant un déploiement indépendant et une liaison à plusieurs produits.

Exemples REST

Pour plus de clarté, les exemples suivants montrent des corps de réponse abrégés. Pour obtenir des schémas de réponse complets, consultez la référence de l’API REST Gestion des API.

L’ajout de l’en-tête If-Match: * dans les exemples d’appels PUT et DELETE rend les requêtes idempotentes. Cet en-tête s'applique que la ressource existe déjà ou non, ce qui constitue le modèle recommandé pour les pipelines CI/CD.

Avant de commencer

Définissez les variables suivantes avant d’exécuter un exemple. Tous les exemples de cette section référencent ces variables.

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)

Répertorier les serveurs MCP

Renvoie toutes les API de l’instance filtrées par type mcp. Utilisez les paramètres de requête $top et $skip pour paginer dans de grands ensembles de résultats.

Référence : API - Liste par service

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

Réponse (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" ]
      }
    }
  ]
}

Erreur courante :401 Unauthorized. Le token Bearer a expiré. Réexécutez la commande d’acquisition de jetons.


Obtenir un seul serveur MCP

Référence : API - GET

MCP_SERVER_ID="my-mcp-server"

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

Réponse (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"
  }
}

Erreur courante :404 Not Found. Vérifiez que mcpServerId correspond au champ name renvoyé par l’opération List.


Créer un serveur MCP soutenu par l’API REST

Crée la ressource de serveur MCP. Après la création, ajoutez des outils individuellement à l’aide de l’opération Ajouter ou mettre à jour un outil . Chaque outil fait référence à une opération spécifique dans une ressource d’API REST de stockage.

Référence : API - Créer ou mettre à jour

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"]
    }
  }'

Réponse (201 créé)

{
  "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 est attendu pour les opérations PUT asynchrones. Interrogez l'URL renvoyée dans l'en-tête de réponse Azure-AsyncOperation afin de confirmer l'achèvement de l'opération.

Erreur courante :400 Bad Request. Vérifiez que type est "mcp" et que path est unique dans l’instance de service.


Créer un serveur MCP relais

Un serveur passthrough transfère toutes les requêtes MCP directement à un serveur principal MCP externe. Défini mcpProperties.transportType pour correspondre au transport que votre back-end implémente.

Avant de créer un serveur passthrough, vérifiez que le serveur principal est accessible à partir de la passerelle Gestion des API et implémente le transport MCP sélectionné sur les chemins d’accès au point de terminaison que vous configurez. Si le back-end nécessite une authentification, configurez les informations d’identification ou les en-têtes requis à l’aide de stratégies de gestion des API ou de la configuration back-end.

Référence : API - Créer ou mettre à jour

Transport HTTP diffusable en continu

Utiliser streamable pour les back-ends qui implémentent la spécification de transport HTTP streamable mcP actuelle. Une définition de point de terminaison unique est requise.

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" }
        ]
      }
    }
  }'

Réponse (201 créé)

{
  "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" } ]
    }
  }
}

Transport SSE

Utiliser sse pour les back-ends qui implémentent le transport HTTP+SSE (Server-Sent Events). Définissez deux points de terminaison : un pour le flux d’événements SSE et un pour le canal de message.

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" }
        ]
      }
    }
  }'

Erreurs courantes :

  • 400 Bad Request. Non valide mcpProperties. Vérifiez que transportType est streamable ou sse et que chaque uriTemplate commence par /.
  • 400 Bad Request. Le transport SSE nécessite exactement deux points de terminaison (sse et message). Le transport Streamable en exige un (message).

Ajouter ou mettre à jour un outil

Ajoute un nouvel outil à un serveur MCP soutenu par l’API REST ou met à jour un serveur MCP existant. Le champ operationId associe l’outil à une opération spécifique dans une ressource sous-jacente d’API REST. Vous pouvez ajouter, mettre à jour ou supprimer des outils indépendamment sans recréer le serveur parent.

Référence : Outil Api - Créer ou mettre à jour

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}\"
    }
  }"

Réponse (201 créé)

{
  "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"
  }
}

Erreurs courantes :

  • 400 Bad Request. Le operationId chemin d’accès est incorrect ou l’opération référencée n’existe pas.
  • 404 Not Found. Le serveur MCP parent n’existe pas. Créez le serveur avant d’ajouter des outils.

Supprimer un outil

Supprime un outil d’un serveur MCP. Supprimez les outils avant de supprimer les opérations de l’API REST de stockage qu’ils référencent ; sinon, la suppression échoue avec une erreur de dépendance.

Référence : Outil Api - Supprimer

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: *"

Réponse:200 OK en cas de succès.

Erreur courante :412 Precondition FailedIf-Match est requis pour les suppressions. Utilisez If-Match: * pour correspondre à n’importe quel ETag.


Appliquer une stratégie à l'étendue MCP

Crée ou remplace le document de stratégie attaché à un serveur MCP. Le serveur évalue les stratégies à cette étendue pour chaque appel d’outil. Le rawxml format accepte le code XML de stratégie non codé.

Référence : Stratégie d’API - Créer ou mettre à jour

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}\"}}"

Réponse (200 OK)

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

Erreur courante :400 Bad Request. XML de stratégie mal formé. Validez le document avant d’envoyer.


Lier un serveur MCP à un produit

Associe le serveur MCP à un produit afin que les abonnés de ce produit puissent appeler les outils du serveur. La requête ne comporte aucun corps.

Lorsque vous liez le serveur MCP à un produit, vous le rendez disponible via ce produit, mais les clients ont toujours besoin d’un accès en fonction de la configuration du produit. Si le produit nécessite des abonnements, le client doit utiliser une clé d’abonnement valide pour ce produit.

Référence : API produit - Créer ou mettre à jour

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"

Réponse :201 Created avec le contrat d’API du serveur MCP dans le corps.

Erreur courante :404 Not Found. Vérifiez que les deux productId et mcpServerId existent avant de créer la liaison.


Supprimer un serveur MCP

Supprime un serveur MCP et tous ses outils et sous-ressources de stratégie. Avant de supprimer le serveur, supprimez tous les outils qui référencent les opérations dans les API de stockage ; sinon, vous ne pouvez pas supprimer ces opérations pendant que la référence de l’outil existe.

Référence : API - Supprimer

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: *"

Réponse:200 OK en cas de succès.

Erreur courante :412 Precondition Failed. If-Match est requis pour les suppressions. Utilisez If-Match: * pour contourner la vérification des ETag.

Modèles ARM et Bicep

Les modèles suivants déploient une configuration complète du serveur MCP dans un seul déploiement. Chaque modèle suppose une instance de service Gestion des API existante et utilise une table de paramètres pour pouvoir réutiliser le même fichier dans les environnements en modifiant uniquement les valeurs de paramètre.

Serveur MCP soutenu par l’API REST

Ces modèles créent un serveur MCP, définissent un outil qui correspond à une opération dans une API REST sous-jacente existante, appliquent une stratégie de limitation du débit au niveau du serveur et lient le serveur à un produit existant. Vous pouvez effectuer toutes ces tâches dans un seul déploiement.

Prérequis : un service Gestion des API existant, une API REST (backingApiId) avec au moins une opération (backingOperationId) et un produit existant (productId).

Paramètre Obligatoire Default Description
serviceName Oui Nom de l’instance de service Gestion des API existante.
mcpServerId Non orders-mcp Nom de la ressource pour le nouveau serveur MCP. Doit être unique au sein du service.
backingApiId Oui Nom de ressource de l’API REST existante qui sauvegarde ce serveur MCP.
backingOperationId Oui Nom de la ressource de l’opération à exposer en tant qu’outil.
toolId Non sampleTool Nom de la ressource et nom complet de l’outil MCP à créer.
productId Non starter Nom de ressource du produit existant auquel lier le serveur.
@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 ]
}

Pour déployer :

# 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

Serveur MCP en mode passthrough

Ces modèles créent un serveur MCP passthrough qui utilise le transport HTTP streamable. L’étendue du serveur a une stratégie de limite de débit et le serveur est lié à un produit existant. Les modèles ne définissent aucune sous-ressource d’outil. Le back-end externe détermine la surface de l’outil.

Note

Les modèles suivants utilisent transportType: streamable, qui implémente la spécification HTTP streamable MCP actuelle. Pour utiliser le transport SSE à la place, définissez transportTypesse et remplacez le endpoints tableau par deux entrées : { "name": "sse", "uriTemplate": "/sse" } et { "name": "message", "uriTemplate": "/messages" }. En Bicep, utilisez les mêmes valeurs avec des chaînes entre apostrophes.

Conditions préalables : un service Gestion des API existant, une URL principale MCP accessible (backendUrl) qui implémente les chemins de transport et de point de terminaison sélectionnés et un produit existant (productId).

Paramètre Obligatoire Default Description
serviceName Oui Nom de l’instance de service Gestion des API existante.
mcpServerId Non external-mcp Nom de la ressource pour le nouveau serveur MCP. Doit être unique au sein du service.
backendUrl Oui URL absolue du back-end MCP externe.
productId Non starter Nom de ressource du produit existant auquel lier le serveur.
@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 ]
}

Pour déployer :

# 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

Actuellement, vous pouvez utiliser az rest pour appeler directement l’API REST. Le script suivant crée un serveur MCP passthrough, attache une stratégie de limite de débit et la lie à un produit. Ce processus couvre le même scénario que le modèle Bicep dans la section précédente.

Définissez des variables, puis exécutez les quatre az rest appels dans l’ordre.

Note

az rest utilise les informations d’identification de votre session active az login . Vous n’avez pas besoin d’une étape d’authentification distincte.

# 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}"

Chaque étape est idempotente. La réexécutation du script met à jour la ressource en place. Pour vérifier que le serveur a été créé, exécutez la commande suivante :

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

Terraform

Le fournisseur Terraform AzureRM n’a pas encore de ressources natives pour les serveurs MCP. Actuellement, vous pouvez utiliser le azapi_resource type de ressource à partir du fournisseur AzAPI, ce qui vous permet de gérer n’importe quel type de ressource Azure par rapport à n’importe quelle version de l’API. L’exemple suivant reprend le modèle Bicep du serveur MCP passthrough.

Conditions préalables : un service gestion des API existant, une URL principale MCP accessible et un produit existant. Ajoutez le fournisseur AzAPI à votre terraform bloc s’il n’est pas déjà présent.

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"
}

Ressources

# 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]
}

Pour déployer :

Note

azapi_resource utilise l’authentification du fournisseur AzAPI, qui s’appuie sur les mêmes az login informations d’identification que l’Azure CLI. Vous n’avez pas besoin de configurer l’authentification distincte lors de l’exécution locale.

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

Modèles CI/CD

  • Upserts idempotents : envoyez des requêtes PUT avec If-Match: "*" afin que le même modèle s'applique que la ressource existe déjà ou non. 

  • Promouvoir les configurations entre les environnements : traitez les définitions de serveur MCP et les listes d’outils en tant qu’artefacts contrôlés par la source. Paramétrez uniquement les valeurs propres à l’environnement, telles que le nom de l’instance et l’URL du back-end. 

  • Générez la liste des outils à partir de votre spécification d’API : alimentez la sous-ressource « outils » à partir de votre fichier OpenAPI source afin que la surface des outils reste synchronisée avec l’API sous-jacente à mesure que celle-ci évolue. 

  • Supprimer dans l’ordre approprié : supprimez les références d’outils MCP avant de supprimer les API de stockage ou les opérations vers lesquelles elles pointent. Sinon, la suppression échoue en raison d'une vérification de clé étrangère.