DEV Community

Daniel Jonathan
Daniel Jonathan

Posted on

๐Ÿ” Fine-Grained Role Control for MCP Tools with APIM

๐Ÿ” Fine-Grained Role Control for MCP Tools with APIM

In an earlier article, we looked at how to enforce role-based access control (RBAC) for individual Logic App workflows by combining Azure API Management (APIM) with Easy Auth.

๐Ÿ‘‰ The same principle now applies when you expose your Logic Apps MCP server through APIM.

MCP tools are mapped to Logic App workflows. We enforce authentication and authorization in APIM (validate-jwt + role checks) before routing to the workflow. Because Easy Auth is enabled on Logic Apps, the backend also authenticates the requestโ€”providing defense in depth.


1. Why enforce authorization at the tool level?

MCP (Model Context Protocol) servers expose tools as callable operations.

For example, an MCP server might surface tools like:

  • wf_arithmetic_add
  • wf_arithmetic_sub
  • wf_arithmetic_div

Without access control, any authenticated user could call any tool.

But in many scenarios, you need fine-grained authorization:

  • Finance users can only run financial calculation tools.
  • Engineering users can only run design tools.
  • Ops users can only run monitoring tools.

2. Configure policy at the MCP Server level

When you expose a Logic Apps MCP server in APIM, you can attach a policy at the MCP Server scope , you gain the ability to:

  • Intercept every MCP JSON-RPC request
  • Parse the request payload (method, params, arguments)
  • Validate and enforce JWT tokens
  • Map roles in the token to MCP tool names

๐Ÿ“ Portal steps:

  1. Go to API Management โ†’ MCP Servers (preview).
  2. Select your MCP server (e.g., ArithmeticOperations).
  3. Open the Policies tab.
  4. Paste the policy snippet below and save.

This ensures all operations on that MCP Server inherit the same rules.

Image description5


3. Distinguish Connect vs Tool Calls

An MCP client first issues a connect call to discover capabilities.

We donโ€™t want to enforce auth on these metadata requests.

So in APIM, we branch on the request body:

  • If method = "tools/call" โ†’ ๐Ÿ”’ require JWT + role validation
  • Otherwise (connect, capabilities, etc.) โ†’ โœ… allow without auth

4. Policy Example

Hereโ€™s a simplified APIM policy that enforces RBAC on tool calls:

<inbound>
  <base />

  <!-- Parse JSON body -->
  <set-variable name="mcpBody" value="@(
    context.Request.Body.As<JObject>(preserveContent: true)
  )" />
  <set-variable name="mcpMethod" value="@{
    var body = (JObject)context.Variables["mcpBody"];
    return (string)body?["method"] ?? string.Empty;
  }" />

  <choose>
    <!-- ๐Ÿ”’ Authenticate only tool calls -->
    <when condition="@((context.Variables.GetValueOrDefault<string>("mcpMethod") ?? "").Equals("tools/call", StringComparison.OrdinalIgnoreCase))">

      <!-- Validate JWT -->
      <validate-jwt header-name="Authorization" failed-validation-httpcode="401">
        <openid-config url="https://login.microsoftonline.com/<TENANT_ID>/v2.0/.well-known/openid-configuration" />
        <audiences>
          <audience>api://<YOUR-APP-ID-URI></audience>
        </audiences>
      </validate-jwt>

      <!-- Extract tool name -->
      <set-variable name="mcpToolName" value="@{
        var body = context.Request.Body.As<JObject>(preserveContent: true);
        return (string)body["params"]?["name"];
      }" />

      <!-- Extract roles -->
      <set-variable name="roles_csv" value="@{
        var tok = context.Request.Headers.GetValueOrDefault("Authorization","").Substring(7);
        var jwt = string.IsNullOrEmpty(tok) ? null : tok.AsJwt();
        var arr = (jwt != null && jwt.Claims != null && jwt.Claims.ContainsKey("roles"))
                    ? jwt.Claims["roles"]
                    : new string[0];
        return string.Join(",", arr);   // e.g. "wf_arithmetic_add,wf_arithmetic_sub"
      }" />

      <!-- Compare tool name to roles -->
      <set-variable name="isAuthorized" value="@{
        var wf = ((string)context.Variables.GetValueOrDefault("mcpToolName","")).ToLower();
        var roles = ((string)context.Variables.GetValueOrDefault("roles_csv","")).ToLower().Replace(" ", "");
        if (string.IsNullOrEmpty(wf) || string.IsNullOrEmpty(roles)) { return false; }
        var haystack = "," + roles + ",";
        var needle = "," + wf + ",";
        return haystack.Contains(needle);
      }" />

      <!-- Block if not authorized -->
      <choose>
        <when condition="@((bool)context.Variables["isAuthorized"])">
          <!-- Authorized, pass through -->
        </when>
        <otherwise>
          <return-response>
            <set-status code="403" reason="Forbidden" />
            <set-body>@("{\"error\":\"Role missing or invalid\"}")</set-body>
          </return-response>
        </otherwise>
      </choose>
    </when>

    <!-- โœ… Allow non-tool calls (connect, resources, etc.) -->
    <otherwise />
  </choose>
</inbound>

Enter fullscreen mode Exit fullscreen mode

4. Validation โ€” Calling MCP Tools via APIM

With the policy in place at the MCP Server level, letโ€™s validate the behavior.

4.1 Authorized tool call

When a valid token is provided and the role matches the tool (wf_arithmetic_add), the request succeeds:

Request

{
  "method": "tools/call",
  "params": {
    "name": "wf_arithmetic_add",
    "arguments": {
      "number1": 1,
      "number2": 2
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Response

{
    "content": [
        {
            "type": "text",
            "text": "{\"result\":3}"
        }
    ],
    "isError": false
}
Enter fullscreen mode Exit fullscreen mode

4.2 Unauthorized tool call (role mismatch)

If the caller tries to invoke a tool without having the required role in their token,

APIM blocks the request before it reaches the Logic Apps MCP server.

Request

{
  "method": "tools/call",
  "params": {
    "name": "wf_arithmetic_mul",
    "arguments": {
      "number1": 2,
      "number2": 2
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

Response

"Error calling method: tools/call. Error: Error POSTing to endpoint (HTTP 403): {\"error\":\"Role missing or invalid\"}"
Enter fullscreen mode Exit fullscreen mode

๐Ÿ”Ž Final Summary

By combining Azure API Management (APIM) with Logic Apps MCP Server, we can achieve fine-grained role-based authorization at the tool level:

  • Connect / discovery calls โ†’ Pass through without auth.
  • Tool calls (tools/call) โ†’ Require a valid JWT.
  • Role check โ†’ Tool name must match one of the roles in the token.
  • Responses
    • 401 Unauthorized โ†’ No or invalid token.
    • 403 Forbidden โ†’ Token valid but role mismatch.
    • 200 OK โ†’ Token valid and role matches tool.

Image description2

โœ… Result:

  • Each MCP tool can be locked down to specific roles.
  • Logic Apps MCP backend remains unchanged.
  • APIM acts as the policy enforcement point, handling security, logging, and governance.

With this setup, you can safely expose Logic Apps MCP servers to LLMs and agents, while ensuring every call is authenticated, authorized, and audited.

Top comments (0)