Skip to content

Repository files navigation

Terraform AzAPI API Management MCP

MCP servers on Azure API Management, via the Azure/azapi provider: a surface azurerm does not have at all. Both modes, typed: REST-backed (gateway API operations become agent tools, declared per tool against a backing operation) and passthrough (the gateway fronts an existing remote MCP server, an Azure Functions MCP app among them), with server-scope policies and product bindings alongside. The third layer of the estate's MCP gateway architecture.

CI Release Terraform Registry License


Notes worth knowing

  • An MCP server is an API of type: mcp on the instance (Microsoft.ApiManagement/service/apis), its tools are child resources referencing backing operations, and clients connect to https://<gateway>/<path>/mcp. The feature needs REST API version 2025-09-01-preview or later (the api_version variable), and any classic or v2 tier except Consumption.
  • subscription_required defaults to true: the subscription key is the gateway's front door while policies (validate-jwt, header injection from named values) hold the backend's own credentials server-side. That split is what makes the gateway an MCP auth adapter: OAuth or key inbound, whatever the backend needs outbound.
  • A passthrough server declares no tools (the backend's surface is what clients see) and fails at plan if it tries. Transport defaults to streamable with the /mcp message endpoint; sse is supported for legacy backends but deprecated upstream.
  • Delete tools before the backing operations they reference; the service enforces the order.

Security scan exceptions

CI runs a trivy config scan that gates on HIGH and CRITICAL findings. Waivers live in .trivyignore.yaml, and every waiver is recorded here. There are currently none.

ID Where Justification

Requirements

Name Version
terraform >= 1.9.0, < 2.0.0
azapi >= 2.0.0, < 3.0.0

Providers

Name Version
azapi 2.11.0

Modules

No modules.

Resources

Name Type
azapi_resource.mcp_servers resource
azapi_resource.policies resource
azapi_resource.product_bindings resource
azapi_resource.tools resource

Inputs

Name Description Type Default Required
api_management_id Resource id of the API Management instance the MCP servers live on (the azapi parent), per the estate's pass-ids principle. MCP servers need a classic or v2 tier; Consumption is not supported. string n/a yes
api_version API version for the Microsoft.ApiManagement MCP surface. 2025-09-01-preview is the version the feature requires; variablised so consumers move the day a stable version ships. string "2025-09-01-preview" no
mcp_servers MCP servers on the instance, keyed by server name (also the default URL path segment). Two modes,
chosen by backend_url:

REST-backed (backend_url null): tools map API operations on the instance. Each tool names its
backing operation either by full operation resource id (operation_id) or by
backing_api_name + backing_operation_name, which compose against this instance. Clients connect
to https:////mcp.

Passthrough (backend_url set): the gateway forwards MCP traffic to an existing MCP server (an
Azure Functions MCP app among them). transport is streamable (default; one message endpoint,
/mcp) or sse (deprecated upstream; sse + message endpoints). Tools come from the backend, so a
passthrough server declares none.

subscription_required defaults to true: callers present Ocp-Apim-Subscription-Key (or the
configured header), which is how the gateway holds the front door while policies hold the
backend's own credentials server-side. policy_xml_content attaches the server-scope policy
(rate limits, validate-jwt, header injection); products binds by product id (Developer tier
ships starter and unlimited built in).
map(object({
display_name = optional(string)
path = optional(string)
description = optional(string)
protocols = optional(list(string), ["https"])
subscription_required = optional(bool, true)

backend_url = optional(string)
transport = optional(string, "streamable")
endpoints = optional(list(object({
name = string
uri_template = string
})))

tools = optional(map(object({
description = string
operation_id = optional(string)
backing_api_name = optional(string)
backing_operation_name = optional(string)
})), {})

policy_xml_content = optional(string)
products = optional(list(string), [])
}))
{} no
resource_timeouts Timeouts for the MCP resources; short creates surface real errors fast.
object({
create = optional(string, "10m")
read = optional(string, "5m")
update = optional(string, "10m")
delete = optional(string, "30m")
})
{} no
retry_error_message_regex Optional azapi retry: regexes matched against error messages that should be retried. list(string) null no

Outputs

Name Description
mcp_server_ids Map of server key to resource id.
mcp_server_ids_zipmap Map of server key to {name, id} for easy composition.
mcp_server_paths Map of server key to its gateway path segment; a client's endpoint is https:////mcp.
tool_ids Map of server-tool key to resource id.

About

🤖 Terraform azapi module for MCP servers on Azure API Management: REST-as-MCP tools and passthrough gateways

Topics

Resources

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages