Programowe zarządzanie serwerami MCP w usłudze API Management

Z tego artykułu dowiesz się, jak tworzyć serwery MCP i zarządzać nimi w Azure API Management przy użyciu interfejsu API REST, szablonów usługi ARM, Bicep, Azure CLI i narzędzia Terraform. 

Important

Funkcje zarządzania serwerami MCP opisane w tym artykule wymagają interfejsu API REST usługi API Management w wersji 2025-09-01-preview lub nowszej. Przypnij tę wersję w każdym żądaniu. 

Informacje o możliwościach serwera MCP znajdują się w temacie About MCP servers in Azure API Management (Informacje o serwerach MCP w Azure API Management).

Wymagania wstępne

Model zasobów

Azure Resource Manager reprezentuje serwery MCP w następujący sposób:

  • Serwer MCP: Zasób interfejsu API usługi API ManagementtypuMCP.

  • Serwer pośredniczący: Wskazuje na istniejący zewnętrzny backend MCP. Zasób serwera MCP określa adres URL backendu i typ transportu (strumieniowe HTTP lub SSE).

  • Narzędzie: Podrzędny zasób narzędzia interfejsu API serwera MCP. Możesz bezpiecznie zarządzać zasobami narzędzia z poziomu CI/CD. Narzędzia można dodawać, zmieniać nazwy lub usuwać bez ponownego tworzenia serwera MCP.

  • Zasady: Podobnie jak w przypadku zwykłych interfejsów API do serwera MCP dołącz zasadę interfejsu API lub podzasoby zasad.

  • Produkty: Powiązanie produktów jest oddzielną relacją podrzędną (products/{productId}/apis/{mcpServerId}), umożliwiającą niezależne wdrażanie i wiązanie wielu produktów.

Przykłady REST

Dla przejrzystości w poniższych przykładach przedstawiono skrócone treści odpowiedzi. Aby uzyskać pełne schematy odpowiedzi, zobacz dokumentację interfejsu API REST usługi API Management.

Dodanie nagłówka If-Match: * w przykładach wywołań PUT i DELETE powoduje, że żądania są idempotentne. Ten nagłówek obowiązuje niezależnie od tego, czy zasób już istnieje, co jest zalecanym podejściem w przypadku potoków CI/CD.

Przed rozpoczęciem

Przed uruchomieniem dowolnego przykładu ustaw następujące zmienne. Wszystkie przykłady w tej sekcji odwołują się do tych zmiennych.

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)

Wyświetlanie listy serwerów MCP

Zwraca wszystkie interfejsy API w instancji przefiltrowane według typu mcp. Użyj parametrów zapytania $top i $skip, aby stronicować duże zestawy wyników.

Informacje: Api — lista według usługi

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

Odpowiedź (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" ]
      }
    }
  ]
}

Typowy błąd:401 Unauthorized. Token okaziciela wygasł. Uruchom ponownie polecenie pozyskiwania tokenu.


Pobierz pojedynczy serwer MCP

Dokumentacja: Interfejs API — Pobierz

MCP_SERVER_ID="my-mcp-server"

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

Odpowiedź (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"
  }
}

Typowy błąd:404 Not Found. Upewnij się, że mcpServerId jest zgodne z polem name zwróconym przez operację List.


Tworzenie serwera MCP opartego na interfejsie API REST

Tworzy zasób serwera MCP. Po utworzeniu dodaj do niego narzędzia indywidualnie przy użyciu operacji Dodaj lub zaktualizuj narzędzie . Każde narzędzie odnosi się do określonej operacji w odpowiednim zasobie interfejsu API REST.

Dokumentacja: Interfejs API — tworzenie lub aktualizowanie

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

Odpowiedź (201 — Utworzono)

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

Uwaga / Notatka

provisioningState: InProgress jest oczekiwana w przypadku asynchronicznych operacji PUT. Sonduj adres URL zwrócony w nagłówku Azure-AsyncOperation odpowiedzi, aby potwierdzić ukończenie.

Typowy błąd:400 Bad Request. Upewnij się, że type jest "mcp" oraz że path jest unikalny w ramach wystąpienia usługi.


Utwórz pośredniczący serwer MCP

Serwer pośredniczący przekazuje wszystkie żądania MCP bezpośrednio do zewnętrznego backendu MCP. Ustaw mcpProperties.transportType tak, aby odpowiadał transportowi obsługiwanemu przez backend.

Przed utworzeniem serwera tranzytowego upewnij się, że zaplecze jest dostępne z bramy usługi API Management i obsługuje wybrany mechanizm transportu MCP w ścieżkach punktów końcowych, które skonfigurujesz. Jeśli zaplecze wymaga uwierzytelniania, skonfiguruj wymagane poświadczenia lub nagłówki za pomocą zasad usługi API Management lub w konfiguracji zaplecza.

Dokumentacja: Interfejs API — tworzenie lub aktualizowanie

Strumieniowy transport HTTP

Użyj streamable do backendów, które implementują obecną specyfikację transportu HTTP MCP z obsługą strumieniowania. Wymagana jest pojedyncza definicja punktu końcowego.

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

Odpowiedź (201 — Utworzono)

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

Użyj sse dla zapleczy obsługujących transport HTTP+SSE (Server-Sent Events). Zdefiniuj dwa punkty końcowe: jeden dla strumienia zdarzeń SSE i jeden dla kanału komunikatów.

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

Typowe błędy:

  • 400 Bad Request. Nieprawidłowy mcpProperties. Zweryfikuj, czy transportType to streamable lub sse, oraz czy każda uriTemplate zaczyna się od /.
  • 400 Bad Request. Transport SSE wymaga dokładnie dwóch punktów końcowych (sse i message). Transport strumieniowy wymaga jednego (message).

Dodawanie lub aktualizowanie narzędzia

Dodaje nowe narzędzie do serwera MCP opartego na interfejsie API REST lub aktualizuje istniejące. Pole operationId łączy narzędzie z określoną operacją w zasobie bazowego interfejsu API REST. Narzędzia można dodawać, aktualizować lub usuwać niezależnie bez ponownego tworzenia serwera nadrzędnego.

Dokumentacja: Narzędzie interfejsu API — tworzenie lub aktualizowanie

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

Odpowiedź (201 — Utworzono)

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

Typowe błędy:

  • 400 Bad Request. Ścieżka operationId jest źle sformułowana lub operacja, do których się odwołujesz, nie istnieje.
  • 404 Not Found. Nadrzędny serwer MCP nie istnieje. Utwórz serwer przed dodaniem narzędzi.

Usuwanie narzędzia

Usuwa narzędzie z serwera MCP. Usuń narzędzia przed usunięciem operacji interfejsu API REST, do których odwołują się; w przeciwnym razie usuwanie kończy się niepowodzeniem z powodu błędu zależności.

Dokumentacja referencyjna: Narzędzie API — Usuń

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

Odpowiedź:200 OK w przypadku powodzenia.

Typowy błąd:412 Precondition FailedIf-Match jest wymagane do usunięcia. Użyj If-Match: *, aby dopasować dowolny tag ETag.


Stosowanie zasad w zakresie MCP

Tworzy lub zastępuje dokument zasad dołączony do serwera MCP. Serwer ocenia zasady w tym zakresie dla każdego wywołania narzędzia. Format rawxml akceptuje kod XML zasad niekodowanych.

Dokumentacja: Zasady interfejsu API — tworzenie lub aktualizowanie

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

Odpowiedź (200 OK)

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

Typowy błąd:400 Bad Request. Źle sformułowany kod XML zasad. Przed wysłaniem zweryfikuj dokument.


Wiązanie serwera MCP z produktem

Kojarzy serwer MCP z produktem, aby subskrybenci tego produktu mogli wywoływać narzędzia serwera. Żądanie nie ma treści.

Po powiązaniu serwera MCP z produktem należy udostępnić go za pośrednictwem tego produktu, ale klienci nadal potrzebują dostępu zgodnie z konfiguracją produktu. Jeśli produkt wymaga subskrypcji, klient musi użyć prawidłowego klucza subskrypcji dla tego produktu.

Dokumentacja: Interfejs API produktu — tworzenie lub aktualizowanie

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"

Odpowiedź:201 Created z kontraktem API serwera MCP w treści odpowiedzi.

Typowy błąd:404 Not Found. Przed utworzeniem powiązania sprawdź, czy istnieją zarówno productId, jak i mcpServerId.


Usuwanie serwera MCP

Usuwa serwer MCP oraz wszystkie jego podrzędne zasoby narzędzi i zasad. Przed usunięciem serwera usuń wszystkie narzędzia, które odwołują się do operacji w interfejsach API tworzenia kopii zapasowych; W przeciwnym razie nie można usunąć tych operacji, gdy odwołanie do narzędzia istnieje.

Odwołanie: Api — Usuń

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

Odpowiedź:200 OK w przypadku powodzenia.

Typowy błąd:412 Precondition Failed. If-Match jest wymagane do usunięcia. Użyj polecenia If-Match: * , aby pominąć sprawdzanie elementu ETag.

Szablony ARM i Bicep

Poniższe szablony wdrażają pełną konfigurację serwera MCP w jednym wdrożeniu. Każdy szablon zakłada istniejące wystąpienie usługi API Management i używa tabeli parametrów, aby można było ponownie użyć tego samego pliku w różnych środowiskach, zmieniając tylko wartości parametrów.

Serwer MCP oparty na interfejsie API REST

Te szablony tworzą serwer MCP, definiują jedno narzędzie mapowane na operację w istniejącym bazowym interfejsie API REST, dołączają zasadę ograniczania liczby żądań na poziomie serwera i wiążą serwer z istniejącym produktem. Wszystkie te zadania można wykonać w jednym wdrożeniu.

Wymagania wstępne: istniejąca usługa API Management, interfejs API REST (backingApiId) z co najmniej jedną operacją (backingOperationId) i istniejący produkt (productId).

Parameter Wymagane Default Description
serviceName Yes Nazwa istniejącego wystąpienia usługi API Management.
mcpServerId No orders-mcp Nazwa zasobu nowego serwera MCP. Musi być unikatowa w ramach usługi.
backingApiId Yes Nazwa zasobu istniejącego interfejsu API REST, który wspiera ten serwer MCP.
backingOperationId Yes Nazwa zasobu operacji, która ma zostać udostępniona jako narzędzie.
toolId No sampleTool Nazwa zasobu i nazwa wyświetlana narzędzia MCP, które ma zostać utworzone.
productId No starter Nazwa zasobu istniejącego produktu, z którym ma zostać powiązany serwer.
@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 ]
}

Aby wdrożyć, wykonaj następujące kroki:

# 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

Serwer MCP przekazujący

Te szablony tworzą serwer pośredniczący MCP, który używa transportu HTTP z obsługą strumieniowania. Zakres serwera ma zasady limitu szybkości, a serwer jest powiązany z istniejącym produktem. Szablony nie definiują żadnych zasobów podrzędnych narzędzi. Zewnętrzny backend wyznacza powierzchnię narzędzia.

Uwaga / Notatka

Poniższe szablony używają elementu transportType: streamable, który implementuje aktualną specyfikację strumieniowego protokołu HTTP MCP. Aby zamiast tego użyć transportu SSE, ustaw wartość transportType na sse i zastąp tablicę endpoints dwoma elementami: { "name": "sse", "uriTemplate": "/sse" } i { "name": "message", "uriTemplate": "/messages" }. W języku Bicep użyj tych samych wartości w ciągach znaków ujętych w pojedyncze cudzysłowy.

Wymagania wstępne: istniejąca usługa API Management, dostępny adres URL zaplecza MCP (backendUrl), który implementuje wybrane ścieżki transportu i punktu końcowego oraz istniejący produkt (productId).

Parameter Wymagane Default Description
serviceName Yes Nazwa istniejącego wystąpienia usługi API Management.
mcpServerId No external-mcp Nazwa zasobu nowego serwera MCP. Musi być unikatowa w ramach usługi.
backendUrl Yes Bezwzględny adres URL zewnętrznego zaplecza MCP.
productId No starter Nazwa zasobu istniejącego produktu, z którym ma zostać powiązany serwer.
@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 ]
}

Aby wdrożyć, wykonaj następujące kroki:

# 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

Obecnie możesz użyć az rest metody do bezpośredniego wywołania interfejsu API REST. Poniższy skrypt tworzy serwer pośredniczący MCP, przypisuje politykę ograniczania szybkości i wiąże go z produktem. Ten proces obejmuje ten sam scenariusz, co szablon Bicep w poprzedniej sekcji.

Ustaw zmienne, a następnie uruchom po kolei cztery wywołania az rest.

Uwaga / Notatka

az rest używa poświadczeń z bieżącej az login sesji. Nie potrzebujesz oddzielnego kroku uwierzytelniania.

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

Każdy krok jest idempotentny. Ponowne uruchomienie skryptu spowoduje zaktualizowanie zasobu. Aby sprawdzić, czy serwer został utworzony, uruchom następujące polecenie:

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

Terraform

Dostawca Terraform AzureRM nie ma jeszcze wbudowanych zasobów dla serwerów MCP. Obecnie możesz użyć typu zasobu azapi_resource od dostawcy AzAPI, który umożliwia zarządzanie dowolnym typem zasobu platformy Azure przy użyciu dowolnej wersji interfejsu API. Poniższy przykład odpowiada szablonowi Bicep dla przekazującego serwera MCP.

Wymagania wstępne: istniejąca usługa API Management, dostępny adres URL zaplecza MCP i istniejący produkt. Dodaj dostawcę AzAPI do terraform bloku, jeśli jeszcze go nie ma.

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

Aby wdrożyć, wykonaj następujące kroki:

Uwaga / Notatka

azapi_resourceużywa uwierzytelniania dostawcy AzAPI, które odczytuje z tego samego az login poświadczenia co Azure CLI. Podczas uruchamiania lokalnego nie trzeba konfigurować oddzielnego uwierzytelniania.

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

Wzorce CI/CD

  • Idempotentne operacje upsert: wysyłaj żądania PUT z użyciem If-Match: "*", aby ten sam szablon miał zastosowanie niezależnie od tego, czy zasób już istnieje. 

  • Podwyższ poziom konfiguracji w różnych środowiskach: Traktuj definicje serwera MCP i listy narzędzi jako artefakty kontrolowane przez źródło. Parametryzuj tylko wartości zależne od środowiska, takie jak nazwa wystąpienia i adres URL zaplecza. 

  • Wygeneruj listę narzędzi na podstawie specyfikacji API: generuj podzasób narzędzi na podstawie źródłowego pliku OpenAPI, tak aby interfejs narzędzi pozostawał zsynchronizowany z bazowym interfejsem API w miarę jego rozwoju. 

  • Usuń we właściwej kolejności: usuń odwołania do narzędzi MCP przed usunięciem bazowych interfejsów API lub operacji, na które wskazują. W przeciwnym razie usuwanie kończy się niepowodzeniem podczas sprawdzania klucza obcego.