This page walks through creating the Authorizer's policy fragments in Azure API Management (APIM), defining the named values they read, and applying them to your API operations in three patterns: authorizing a REST API, governing an MCP server, and enabling MCP discovery and Insights.
The Authorizer is modular. Each capability comes from its own fragment, so you choose which capabilities to apply and add only the fragments they need. You make this choice per operation: one API can use enforcement and Insights together, while another uses Insights alone.
You create each fragment once per APIM instance and then include it in any operation, API, or product policy. Because the fragments read Policies from PlainID at request time, you can change who can call what without editing your APIM configuration.
Prerequisites
Before you start, ensure you have:
- Access to the Microsoft Azure Portal and an APIM instance with the APIs or MCP servers you want to protect.
- A PlainID Environment with access to the PDP (Runtime) endpoint.
- A Scope in PlainID with a Client ID and Client Secret.
- For MCP tool governance, an Asset Type for MCP tools (for example
MCP-TOOL) and Policies that grant access to it. See Tools and MCP for more information. - For MCP discovery or Insights, your PlainID collector endpoint URL and a client certificate for it, uploaded to your APIM instance under Security > Certificates. See Microsoft's guide to client certificate authentication for more information.
You need only the prerequisites for the capabilities you choose.
Upgrading from an Earlier Version
If you configured an earlier version of the Authorizer, make the following changes:
- Replace the PlainID policy XML you pasted into your operations with the fragments and
include-fragmentreferences described on this page. - Create the
plainidPdpUrlnamed value and set it to your PDP Runtime base URL, for examplehttps://<host>/api/runtime. Earlier versions readplainidUrland expected a full decision URL. The fragments now append the endpoint path themselves. - Add the new named values listed below. APIM rejects a policy that references a named value that doesn't exist, so define every value the fragments you use require.
Step 1: Choose Your Capabilities and Create Their Fragments
Choose the capabilities you want, then create only the fragments they use. You can add more capabilities later by creating their fragments and including them in your policies.
| Capability | Fragment ID | Section | What it does |
|---|---|---|---|
| API authorization | api-enforcement |
Inbound | Authorizes each REST API call against the PDP and blocks denied calls. |
| MCP tool governance | mcp-enforcement |
Inbound and Outbound | Authorizes MCP tools/call requests and filters tools/list responses to the tools the caller may use. |
| MCP discovery | mcp-discovery |
Outbound | Registers each MCP server's full tool inventory with PlainID for Policy authoring. |
| Insights and Agent discovery | insights-discoverer |
Inbound | Intercepts the transactions that pass through APIM and reports them to PlainID, so PlainID discovers the Agents, clients, and APIs that call through APIM. |
Each capability works on its own, and you can combine them freely, with one exception: an operation is either a REST API or an MCP endpoint, so don't include api-enforcement and mcp-enforcement on the same operation.
To create a fragment:
- In your APIM instance, go to APIs > Policy fragments.
- Select + Create.
- Enter the Fragment ID exactly as it appears in the table above.
- Paste the contents of the matching file from the
fragmentsfolder of the package. - Select Create.
See Microsoft's policy fragments documentation for more information.
Step 2: Define Named Values
In your APIM instance, go to Named values and create the keys the fragments you use require. The tables below group the named values by fragment, so you define only the ones for the capabilities you chose. Use type Secret for clientSecret, and type Plain for everything else. See Use named values in Azure API Management policies for more information.
APIM doesn't allow a blank named value. For values marked Optional, enter none to turn the setting off.
Used by All Fragments
| Name | Description |
|---|---|
enableTracing |
true writes Authorizer traces to Application Insights. false turns tracing off. |
Used by api-enforcement and mcp-enforcement
| Name | Description |
|---|---|
plainidPdpUrl |
Your PDP Runtime base URL, for example https://<host>/api/runtime. |
clientId |
The Client ID from your PlainID Scope. |
clientSecret |
The Client Secret from your PlainID Scope. Required even when authenticationMethod is token. |
authenticationMethod |
token forwards the caller's Authorization header (JWT) to the PDP. secret authenticates with the Client Secret instead. |
runtimeFineTune |
Optional. A JSON object added to every PDP request, for example to request Policy details or set the Identity type. |
runtimeRequestTimeout |
The PDP request timeout in seconds, for example 60. |
headersToForward |
Optional. A comma-separated list of up to 5 request headers to forward to the PDP, for example X-User-Token,X-Agent-Token. Header names are case-insensitive. |
Used by api-enforcement Only
| Name | Description |
|---|---|
onPreventStatusCode |
The HTTP status code APIM returns when it blocks a request, for example 403. |
onPreventContentType |
The Content-Type of the blocked response, for example application/json. |
onPreventBody |
The body of the blocked response, for example { "code": 403, "error": "Forbidden" }. |
Used by mcp-enforcement Only
| Name | Description |
|---|---|
toolsResourceType |
The Asset Type for MCP tools in PlainID, for example MCP-TOOL. |
mcpErrorCode |
The JSON-RPC error code returned when the Authorizer denies a tool call. Must be an integer, for example -32603. |
mcpErrorMessage |
The JSON-RPC error message returned when the Authorizer denies a tool call, for example Access Denied. |
Used by mcp-discovery and insights-discoverer
| Name | Description |
|---|---|
collectorUrl |
Your PlainID collector endpoint URL. |
collectorCertificateThumbprint |
The thumbprint of the client certificate APIM presents to the collector. The certificate must exist under Security > Certificates. |
Example Configuration
This example defines every named value, for an APIM instance that uses all capabilities.
plainidPdpUrl: https://<host>/api/runtime
clientId: PYX9B9H5S29IBG9OEISG
clientSecret: ****
authenticationMethod: token
enableTracing: true
runtimeRequestTimeout: 60
headersToForward: X-User-Token,X-Agent-Token
runtimeFineTune: {
"includeAccessPolicy": "true",
"includeAccessPolicyId": "true",
"includeDetails": true,
"includeAssetAttributes": true,
"includeIdentity": true,
"includeIdentityAttributes": true,
"entityId": "userId",
"entityAttributes": {
"identityattr1": ["identityAtt1Value"]
},
"entityTypeId": "User"
}
onPreventStatusCode: 403
onPreventContentType: application/json
onPreventBody: { "code": 403, "error": "Forbidden" }
toolsResourceType: MCP-TOOL
mcpErrorCode: -32603
mcpErrorMessage: Access Denied
collectorUrl: https://<host>/collector
collectorCertificateThumbprint: <certificate-thumbprint>
Step 3: Apply the Fragments
To add a policy to an operation:
- In the Azure Portal, open the API and select the operation you want to protect.
- In Inbound processing, select Add policy > Other policies.
- Paste the matching example below into the policy editor, adjusting fragment references as needed.
- Select Save.
See Azure's guide to configuring policies in the portal for detailed steps.
The examples below show common combinations. To match the capabilities you chose, add or remove include-fragment lines. For example, to use only Insights on an operation, include insights-discoverer and nothing else.
Authorize a REST API
This example authorizes every call to the operation and reports it to Insights. Remove the insights-discoverer line if you don't need Insights.
<policies>
<inbound>
<include-fragment fragment-id="insights-discoverer" />
<include-fragment fragment-id="api-enforcement" />
<base />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
You can also apply this policy to a whole product under APIs > Products > your product > Policies > Inbound processing.
With this configuration, APIM handles each call as follows:
| PDP result | What APIM does |
|---|---|
| Permit | Forwards the request to the backend service with an added X-Authorized-By: PlainID header. |
| Deny | Returns onPreventStatusCode, onPreventContentType, and onPreventBody to the caller. |
| Error or timeout | Blocks the request and returns the same deny response. |
Govern an MCP Server
This example filters the tools each Agent sees, authorizes every tool call, and registers the MCP server's tool inventory with PlainID. mcp-enforcement appears in both the inbound and outbound sections, and mcp-discovery comes before it in outbound, so PlainID receives the complete tool list before the Authorizer filters it. Remove the insights-discoverer or mcp-discovery line if you don't need that capability.
<policies>
<inbound>
<include-fragment fragment-id="insights-discoverer" />
<include-fragment fragment-id="mcp-enforcement" />
<base />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
<include-fragment fragment-id="mcp-discovery" />
<include-fragment fragment-id="mcp-enforcement" />
</outbound>
<on-error>
<base />
</on-error>
</policies>
With this configuration, APIM handles each MCP request as follows:
| Request | What APIM does |
|---|---|
tools/list |
Returns only the tools the caller's Policies allow. If PlainID returns no entitlements, the list is empty. |
tools/call, permitted |
Forwards the call to the MCP server. |
tools/call, denied, error, or timeout |
Returns a JSON-RPC error with mcpErrorCode and mcpErrorMessage. The response id matches the request, so MCP clients match the error to the pending call. |
POST with a body that isn't a single JSON object, including JSON-RPC batches |
Returns the same JSON-RPC error. |
GET and DELETE (SSE stream and session requests) |
Passes through unchanged. |
Other JSON-RPC methods, such as initialize |
Passes through unchanged. |
The Authorizer supports both plain JSON and SSE-framed (Streamable HTTP) responses from the MCP server, and keeps the original framing when it filters a tool list.
Enable MCP Discovery and Insights
MCP discovery and Insights run alongside enforcement or on their own:
insights-discoverer(Insights) intercepts every transaction that reaches the operation and reports it to PlainID, including the caller's source IP, headers, URL, destination, and body. Place it first in the inbound section, before any enforcement fragment.mcp-discovery(MCP discovery) registers the MCP server's full tool list each time an Agent callstools/list.
Neither fragment affects the authorization decision. Each report uses a one-second timeout and ignores collector errors, so a collector outage adds at most one second to a request and doesn't block it.
Troubleshooting
Set enableTracing to true to write traces to the traces table in Application Insights. Filter by source to find the Authorizer's entries:
| Trace source | Written by |
|---|---|
PlainID Authorization |
api-enforcement |
PlainID MCP Enforcement |
mcp-enforcement |
PlainID MCP Discovery |
mcp-discovery |
PlainID Insights Discoverer |
insights-discoverer |
Each REST API trace includes the request ID, the authorization result, the request sent to the PDP, and the PDP response. The Authorizer adds an X-Request-ID header to requests that don't have one and returns it on blocked responses, so you can match a caller's error to its trace.
Traces contain full request headers, including tokens. Turn tracing off when you finish troubleshooting.
Notes
mcp-enforcementmust appear in both the inbound and outbound sections of an MCP operation. Including it in only one section isn't supported.headersToForwardsupports up to 5 headers.- To filter tool lists and register tools,
mcp-enforcementandmcp-discoveryread and rewrite the MCP server's response body. This can cause APIM to buffer the response. Validate this against your MCP server before relying on it in production, especially if your server streams large or long-lived responses.