Skip to main content
Use this guide when you have agents in Microsoft Copilot Studio and MCP servers hosted in your own Azure subscription. Rather than letting the agent call those servers directly, you route the calls through the TrueFoundry MCP Gateway and enforce governance: it authenticates the caller, resolves it to a registered agent, applies your access policy, and performs the token exchange your MCP server requires. Your MCP server does not have to do the heavy lifting of trusting each agent call, and every call is attributable in the audit trail.
The distinctive thing about Copilot Studio is that there is no agent code. You cannot add token logic to the agent. This is solved by two OAuth exchanges, neither of which you have to code:
  1. The connector performs the first OAuth on-behalf-of exchange. It does not simply forward a token — Power Platform’s Azure API Connections service takes the user’s Copilot Studio session and exchanges it for an access token audienced to your agent, using the connector’s own client credentials. You configure this on the connector’s Security tab.
  2. The gateway performs the second exchange, turning that token into one your MCP server accepts. You configure this on the MCP server’s Auth Data.
If you are coming from a stack where the agent performed its own exchange in code, there is no equivalent to that code here.

What you are setting up

Prerequisites

  • A Copilot Studio environment, and rights to create custom connectors in the same Power Platform environment as the agent.
  • An MCP server in your Azure subscription that validates Entra JWTs and speaks Streamable HTTP (Server-Sent Events is no longer supported). To write one, see Create OAuth app registration with Azure Entra.
  • Entra: Cloud Application Administrator is the least-privilege role that covers creating the app registrations and granting admin consent.
  • Entra registered as an identity provider in TrueFoundry. See Identity Providers.
  • For the CLI path through the app registrations: Azure CLI signed in to the tenant (az login), plus uuidgen. The portal path needs neither.

The two identities, and why there are two

“The agent’s identity” means two different objects in two different systems. Entra decides who the agent is; TrueFoundry decides what that agent may do, and on whose behalf. The registry entry binds them by mapping a claim in the incoming Entra token to a TrueFoundry principal you can write policy against.

Copilot Studio and Entra Agent ID

Copilot Studio creates an Entra Agent ID for each agent automatically. You will find it under Entra admin center → Agents, with the person who created the agent recorded as its sponsor, and governed by a Microsoft-published agent identity blueprint.
That Agent ID does not appear in the tokens your gateway sees. Power Platform’s connector infrastructure acquires tokens using the connector’s own app registration, and the blueprint behind the Copilot Studio agent identity belongs to Microsoft — so nothing in this path can mint a token as that identity. Map your TrueFoundry agent to the connector application ID, which is what actually arrives.

What this leads to

Because the Copilot Studio agent is a managed identity you cannot address, two objects in this setup are stand-ins for it:
  • Use the Agent ID for — inventory, the sponsor relationship, and Conditional Access on the agent’s own sign-ins within Microsoft 365.
  • Use the TrueFoundry agent identity for the call path — which MCP servers and tools the agent may reach, and whom it may act for. That is the part the Agent ID cannot express.
If you need the agent’s Entra identity to reach the MCP server itself, the agent has to be one that can perform its own token exchange — an Azure AI Foundry or custom-hosted agent, not Copilot Studio.

Mapping to a TrueFoundry agent

Create an entry in the Agent Registry: Embedded because Microsoft hosts the agent: the gateway is not in front of the agent, it is in front of the agent’s tools. One registry entry covers both modes below — unlike some other agent platforms, the claim the gateway matches on is the same either way.

App registrations you need

Three, and all three are needed for both modes.
Entra requires that the application performing an on-behalf-of exchange is the audience of the token it redeems. The gateway therefore exchanges as agent-service, using its client secret.

Creating them

Each step below can be done in the Entra portal or with the Azure CLI — the two tabs produce the same result, so pick one and stay on it.
The CLI snippets are written to run in one shell session, in order: later steps reuse $MCP_API, $AGENT_SVC, and $AGENT_CLIENT from earlier ones. If you start a new shell, re-derive them with az ad app list --display-name <name> --query "[0].appId" -o tsv.
1

mcp-api — the tool's audience

Create the registration and set its Application ID URI to api://<mcp-api-app-id>. Then add:
  • a delegated scope Tool.Write (type: User, state Enabled) — used by on-behalf-of
  • an app role Tool.Invoke with allowedMemberTypes: ["Application"] — used by client credentials
Set requestedAccessTokenVersion: 2 in the manifest.
The delegated scope serves on-behalf-of and the app role serves client credentials, so mcp-api needs both even if you only run one mode today.
2

agent-service — the agent's audience, and the gateway's credential

Create the registration and set its Application ID URI to api://<agent-service-app-id> and add a delegated scope access_as_user. Then, under Expose an API → Authorized client applications, add fe053c5f-3692-4f14-aef2-ee34fc081cae, ticking access_as_user.Under API permissions, add from mcp-api:
  • DelegatedTool.Write (for on-behalf-of)
  • ApplicationTool.Invoke (for client credentials)
plus Delegated → Microsoft Graph → offline_access. Then create a client secret — this is the one the gateway will hold. Set requestedAccessTokenVersion: 2.Make sure Tool.Invoke is added as an Application permission, not a delegated one. Admin consent in the last step turns it into the app-role assignment on agent-service’s own service principal — which is what the client-credentials path needs. There is no separate screen for that assignment: Enterprise applications → mcp-api → Users and groups takes only users and groups, so the API-permission plus admin-consent pair above is how an application gets assigned.
To verify: on agent-serviceAPI permissions, the Tool.Invoke row should read type Application with Status “Granted for <tenant>”. A “Not granted” warning there is the same condition that surfaces later as AADSTS501051. Granting only the delegated Tool.Write leaves it missing, because a delegated grant contributes scp to a user token and nothing at all to an app-only one.
That fe053c5f-… GUID is Microsoft’s Azure API Connections service — the thing that obtains tokens on your users’ behalf. Without it authorized on access_as_user, users get an interactive sign-in prompt on every call instead of a silent hand-off, which presents as a broken connector rather than a missing grant.
3

agent-client — the connector's credentials

Create the registration and add a Delegated permission on api://<agent-service>/access_as_user, plus Microsoft Graph offline_access. Create a client secret. Under Authentication → Web → Redirect URIs, add https://global.consent.azure-apim.net/redirect.
Use Delegated (=Scope), never Application (=Role), throughout this chain. An application permission yields a token with no user to delegate — it validates cleanly and silently drops the person you were representing.
4

Grant admin consent

On both agent-service and agent-client: API permissions → Grant admin consent.
This is mandatory. A missing grant surfaces as AADSTS65001 from the token endpoint.
agent-service’s secret goes to TrueFoundry. agent-client’s secret goes to the Power Apps connector.

The custom connector

The connector is where the agent’s token is obtained. Build it in Power Apps, not with Copilot Studio’s MCP onboarding wizard — the wizard’s OAuth options are all authorization-code and have no on-behalf-of switch, so the user’s identity does not carry through.
1

Import a definition

Split the gateway URL from the MCP server’s How To Use tab into host and the key under paths:. Keep basePath: /.
Put the whole path under paths: and leave basePath: /. Folding the path into basePath and leaving paths: / yields an identical URL but places the operation at the root path, which Power Platform does not register as an MCP operation — the connector imports cleanly, the connection succeeds, and the agent’s tool list is silently empty.
2

Configure the Security tab

Resource URL is the field to get right. It is agent-service — not your MCP server, and not the gateway. Point it at the MCP and the gateway has nothing left to exchange.One resource only in Scope: Entra derives the audience from the resource of the requested scopes and rejects a request naming two. offline_access is resource-agnostic and yields the refresh token.
3

Share the connector

Can view for end users, Can edit for makers. Without this the connector works for its author and fails for everyone else, with an error that says nothing about sharing. (Sharing is not needed for yourself — the owner can always use their own connector.)
4

Add it to the agent

In Copilot Studio: Settings → Generative AI → Orchestration = Generative first, then Tools → Add a tool → Connector → your connector → Add to agent, and create a connection.With classic orchestration the agent never calls MCP tools at all — the tool appears attached and is silently ignored. And because on-behalf-of is on, creating the connection shows a short consent card rather than a full sign-in page; a full sign-in page means on-behalf-of is not active.

Choosing between client credentials and on-behalf-of

Both modes share the same connector, the same agent, and the same MCP server. Only the gateway’s outbound exchange differs — so you register the MCP server twice and point the connector at whichever entry you want.

Part 1: On-behalf-of

The signed-in user’s identity is preserved all the way to your MCP server.

Token flow

On-behalf-of flow: the user signs in to Copilot Studio, the Power Platform custom connector performs an OAuth on-behalf-of exchange via Azure API Connections producing a token audienced to agent-service that carries the user in oid and the agent in azp, the TrueFoundry MCP Gateway resolves both identities and performs a second on-behalf-of exchange as agent-service, and the MCP server receives a delegated token whose audience is mcp-api and whose user is unchanged.
Point to note: the audience moves to your MCP server, the user stays the same. That is delegation. If the user changes, something is impersonating rather than delegating; if the user disappears, you have an application token and the wrong grant type.

Register the MCP server on Truefoundry

Then add agent:<your-agent> as an MCP Server User collaborator. Three fields are easy to get wrong:
  • Grant Type = JWT Bearer, not Token Exchange. Token Exchange is the RFC 8693 profile other identity providers use; Entra implements RFC 7523 (jwt-bearer + requested_token_use=on_behalf_of) and rejects the other shape.
  • Client ID = agent-service, matching the inbound token’s audience. Anything else gives AADSTS50013.
  • Scopes names the destination — your MCP server. Point it elsewhere and the exchange succeeds while your MCP server rejects the audience, which reads like an MCP bug.

What your MCP server receives

Validate the audience, then require Tool.Write in scp. Use oid as the stable key for the user, not sub — in Entra sub is a pairwise identifier that differs per application, so the same person gets a different sub at every audience.

What you can enforce

Permissions intersect. A delegated token can only carry permissions the user actually has and that the agent has been granted. Neither party can exceed its own access by going through the other — the main reason to choose on-behalf-of over client credentials.

Part 2: Client credentials

The gateway calls your MCP server under one shared authority. The user is authenticated at the gateway and then deliberately not propagated.

Token flow

Everything up to the gateway is identical to Part 1 — same connector, same TOKEN A, same inbound validation and RBAC. Only exchange 2 changes.
Client credentials flow: everything above the gateway is identical to on-behalf-of, the TrueFoundry MCP Gateway resolves both the user and the agent and applies RBAC, then exchanges via client credentials as agent-service so the MCP server receives an application token carrying the Tool.Invoke role and no user claims.
The user is lost at the gateway’s outbound hop, not earlier. That matters for two reasons: the gateway’s audit trail still records which user made every call, and per-user policy still applies at the gateway even though your MCP server cannot see the person. An MCP server that reports “no user” here is behaving correctly, not failing.

Register the MCP server

The same MCP server, registered a second time. Only two fields differ from Part 1: /.default, not Tool.Write. An app-only grant cannot request named delegated scopes; it resolves whatever app roles the service principal holds — which is why Tool.Invoke had to be assigned to agent-service, not merely granted. Add the same agent as an MCP Server User here too.

What your MCP server receives

Validate the audience, then require Tool.Invoke in roles.

What you can enforce

RBAC you can set using the agent identity

Once an agent resolves at the gateway it becomes a first-class principal in your access policy, alongside users, teams and virtual accounts. Two properties are what a per-credential model cannot give you: Access and delegation are separate grants. Being a collaborator on an MCP server lets the agent reach it. Agent Access decides whom it may act for once there. A user with no Agent Access cannot be acted for — even if that same user could reach the MCP server directly. Both must hold. Every hop is checked against the agent actually calling. In a chain of agents, each needs its own access to the target and its own permission to act for the user. Nothing is inherited from the caller, so a chain cannot accumulate reach that no single agent was granted.

Comparison and limits

Two limits worth knowing

The Copilot Studio Entra Agent ID never reaches your MCP server — and cannot be registered directly anywhere in this chain. If you need the agent’s own Entra identity to travel with the call, the agent must be one that performs its own token exchange — Azure AI Foundry or custom-hosted, not Copilot Studio. Entra emits no nested delegation chain. azp is single-valued and is overwritten at each exchange, so the token your MCP server receives names the app that performed the last exchange — agent-service — not every hop before it. Per-hop attribution lives in the gateway’s audit trail.

Scaling to many agents

Point every agent at the same agent-service audience. The audience identifies the tier of callers, while each agent is still distinguished by its own agent-client ID in the token. A second agent needs its own agent-client plus one Agent Registry entry; mcp-api and agent-service are shared.

Troubleshooting

Next steps