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 OAuth exchanges you configure rather than write:
  1. The connector performs an 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, using the connector’s own client credentials. You configure this on the connector’s Security tab, and it happens in all three modes below.
  2. The gateway performs a second exchange, turning that token into one your MCP server accepts. You configure this on the MCP server’s Auth Data. Token passthrough is the mode where this second exchange does not happen at all.
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. 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. TrueFoundry agent identity is mirror identity that you add in TrueFoundry agent registry. The registry entry binds them by mapping a claim in the incoming Entra token to a TrueFoundry principal you can write policy against. Entra decides who the agent is; TrueFoundry decides what that agent may do, and on whose behalf.
However please note that the Entra Agent ID does not appear in the tokens that is sent by agent to the gateway. 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.

Authentication modes and identity flows

There are three ways in which TrueFoundry gateway supports authentication and token exchanges on this journey
Each mechanism listed above impacts the ability to enforce governance, and hence please go through the pros and cons and decide which ones suites you best.
The CLI snippets in each part 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.

Part 1: Token passthrough

The copilot connector obtains a token for your MCP server directly and the gateway forwards it unchanged, so there is no token exchange happening at gateway.

Token flow

Token passthrough flow: the user signs in to Copilot Studio, the Power Platform custom connector performs a single on-behalf-of exchange whose Resource URL is mcp-api rather than agent-service, producing a token already audienced to the MCP server that carries the user in oid and the agent in azp; the TrueFoundry MCP Gateway validates it, resolves the user and the agent, applies RBAC and audit, and forwards the same token unchanged; a dashed arrow shows the same token also reaching the MCP server without the gateway, which only network controls can prevent.Token passthrough flow: the user signs in to Copilot Studio, the Power Platform custom connector performs a single on-behalf-of exchange whose Resource URL is mcp-api rather than agent-service, producing a token already audienced to the MCP server that carries the user in oid and the agent in azp; the TrueFoundry MCP Gateway validates it, resolves the user and the agent, applies RBAC and audit, and forwards the same token unchanged; a dashed arrow shows the same token also reaching the MCP server without the gateway, which only network controls can prevent.

App registrations you need

1

mcp-api — the tool's audience

Create the registration and set its Application ID URI to api://<mcp-api-app-id>. Add a delegated scope Tool.Write (type: User, state Enabled), and set requestedAccessTokenVersion: 2 in the manifest.
A v1 token carries appid instead of azp and a different issuer, which breaks the gateway’s issuer match and its agent lookup at once — hence requestedAccessTokenVersion: 2.
2

mcp-api — authorize Azure API Connections

Under Expose an API → Authorized client applications, add fe053c5f-3692-4f14-aef2-ee34fc081cae, ticking Tool.Write.
That fe053c5f-… GUID is Microsoft’s Azure API Connections service — the thing that obtains tokens on your users’ behalf. Without it authorized here, 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://<mcp-api-app-id>/Tool.Write, 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). 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 agent-client: API permissions → Grant admin consent.
Mandatory. The connector’s exchange runs with no user present to click a consent dialog, so an unconsented grant fails outright with AADSTS65001 rather than prompting.
agent-client’s secret goes to the Power Apps connector. Nothing in this mode gives a secret to TrueFoundry — the gateway holds no credential of yours at all.

The custom connector

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 what makes this passthrough. It names your MCP server, so the token comes out already accepted there and nothing downstream needs to change it.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. This is the primary con with token passthrough approach as the connector now gets the broadest scope and there is no way to control that unless we make a token exchange hop through gateway.
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.

Register the MCP server on TrueFoundry

Then add agent:<your-agent> as an MCP Server User collaborator. On the gateway’s Identity Provider, list your MCP server’s audience — both api://<mcp-api-app-id> and <mcp-api-app-id> — under Allowed Audiences. That is what the connector’s token is minted for, and without it the gateway returns 401 before it ever calls your MCP server.
Token Passthrough is a mode, not the absence of one. Leave the Auth Data toggle on and select it — it sits alongside API Key, OAuth2 and AWS SigV4, and its description is exact: “forward the user’s existing auth token directly to the MCP server.” Switching Auth Data off is a different setting: the upstream call then carries no credential, your MCP server returns 401, and the symptom points at your server rather than at the gateway entry that caused it.

What your MCP server receives

Validate the audience, require Tool.Write in scp, and key the user on oid — not sub. In Entra sub is a pairwise identifier that differs per application, so a per-user store keyed on sub gives one person a different record depending on which mode was used to reach it. actor_app is agent-client, the connector that actually called. This is the only mode where that is true; the other two overwrite it during their exchange.
scp carries every scope agent-client was consented for on mcp-api, not only the one the connector requested — Entra returns all consented scopes for a resource. The connector’s consent is therefore the ceiling for every MCP server behind it.

What you can enforce

The governance gap with token passthrough

On-behalf-of closes all three, and closes them structurally rather than by configuration. The connector’s token is audienced to agent-service, which your MCP server rejects — so the only route to your tools runs through the gateway, where the exchange happens. Bypass stops being something to prevent and becomes something that cannot be expressed. Scope narrowing moves to the gateway’s per-server Scopes field, and one connector audience fans out to every MCP server behind it.

Part 2: On-behalf-of

The signed-in user’s identity is preserved all the way to your MCP server, and the gateway is on the token path by construction.

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.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.

App registrations you need

Three. The third, agent-service, is the one passthrough does not need — and it is what makes the gateway unbypassable, because it gives the connector’s token an audience your MCP server refuses.
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 — which is also why TrueFoundry needs no app registration of its own.
1

mcp-api — the tool's audience

Create the registration and set its Application ID URI to api://<mcp-api-app-id>. Add a delegated scope Tool.Write (type: User, state Enabled), and set requestedAccessTokenVersion: 2 in the manifest.
Unlike Part 1, do not authorize Azure API Connections on mcp-api here. In this mode the connector’s token targets agent-service, and the pre-authorization belongs there instead.
2

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

Create the registration, 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, plus Delegated → Microsoft Graph → offline_access. Then create a client secret — this is the one the gateway will hold. Set requestedAccessTokenVersion: 2.
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. Keep them apart — the gateway has no reason to hold the connector’s credential, and the connector has no reason to hold the gateway’s.

The custom connector

Identical to Part 1’s except for two fields on the Security tab. If you already built a passthrough connector, build a second one rather than editing it: Resource URL is single-valued, so changing it converts a connector from one mode to the other. Both can share the same agent-client registration.
1

Import a definition

Split the gateway URL from this MCP server entry’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 your MCP server and you have built Part 1 instead.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, then add it to the agent

Can view for end users, Can edit for makers. Then 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.Attach only one connector at a time. An agent with both a passthrough and an on-behalf-of connector has two sets of identically-named tools and picks between them unpredictably.

Register the MCP server on TrueFoundry

Then add agent:<your-agent> as an MCP Server User collaborator. On the gateway’s Identity Provider, list api://<agent-service-app-id> and <agent-service-app-id> under Allowed Audiences — that is what the connector’s token is minted for in this mode. 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. This is not theoretical: sub changes across the gateway’s exchange while oid does not, so a per-user store keyed on sub gives one person a different record depending on which mode was used to reach it. actor_app is agent-service, not the connector: the exchange overwrites it. Your MCP server can prove an authorized agent called, but not which connector — per-hop attribution lives in the gateway’s audit trail.

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 3: Client credentials

The gateway calls your MCP server under one shared authority. The user is authenticated at the gateway and then deliberately not propagated. Use it for tools that genuinely act under one authority — a shared mailbox, a batch job, a system-of-record write attributed to the service rather than the person. Not as a shortcut past a delegated setup.

Token flow

Everything up to the gateway is identical to Part 2 — 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.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.

App registrations you need

The same three as Part 2, plus one app role and one assignment. If you have already built Part 2, this mode adds to it rather than replacing anything — the connector and the inbound token are identical, and only the gateway’s outbound call differs.
1

mcp-api — add an app role

Alongside the Tool.Write delegated scope, add an app role Tool.Invoke with allowedMemberTypes: ["Application"].
An app role, not a scope: application tokens carry roles, never scp. Your MCP server must accept either shape, or this mode fails validation on a token that is perfectly valid.
2

agent-service — request and be assigned the app role

Under API permissions, add from mcp-api: ApplicationTool.Invoke. Then Grant admin consent.Make sure it is added as an Application permission, not a delegated one. Admin consent turns each Application permission 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.
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.
The permission and the assignment are different things. Without the assignment the client-credentials call fails with AADSTS501051, which names neither.
3

agent-client — unchanged

Nothing to do. The connector still obtains an agent-service-audienced user token exactly as in Part 2; the user is dropped later, at the gateway’s outbound hop.

The custom connector

Unchanged from Part 2. Same connector, same Resource URL, same scope. Point its paths: key at this MCP server entry’s gateway path, or reuse the Part 2 connector and switch entries — the difference between the two modes is entirely on the gateway side.

Register the MCP server on TrueFoundry

The same MCP server, registered again. Only two fields differ from Part 2: /.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.

What the gateway is worth in each mode

The gateway does two separable jobs. It is a control plane — the catalog of which agent may call which server and tool, on whose behalf, under which guardrails, with an audit record naming both parties. And it is a token broker — the thing that converts a token your MCP server would reject into one it accepts. The first job is identical in all three modes. Only the second changes, and every difference below follows from that.

Reading the table

The last four rows are the same in all three modes. Identity resolution, agent RBAC, Agent Access, guardrails, rate limits and audit do not depend on the gateway performing an exchange. This is the point most easily missed: passthrough does not reduce the gateway to a proxy. On-behalf-of buys enforcement you cannot get any other way. Because your MCP server rejects the connector’s token, the gateway is on the path by construction — not by configuration, not by convention, and not by anything an application team can accidentally undo. Nothing else in the table is worth as much in a large tenant, and it is why on-behalf-of is the default recommendation. Client credentials buys simplicity at the cost of the user. There is no user in the token, so per-user authorization can only happen at the gateway. Choose it for tools that genuinely act under one shared authority, not as a shortcut past a delegated setup. Passthrough buys two real things, and they are not the ones usually claimed for it. Not “less configuration” — it needs an extra connector per MCP server, which grows faster than the on-behalf-of setup does. What it buys is that the gateway holds no credential of yours, and that your MCP server sees the real calling client rather than the app that performed the last exchange. If your threat model cares more about what a compromised gateway could mint than about whether the gateway can be bypassed, that is a coherent choice. The two delegated modes are not interchangeable. Passthrough and on-behalf-of both deliver a verified end user, which makes them look equivalent at a glance. They differ on the two rows that matter most — bypassability and azp — and they differ in opposite directions. Neither dominates.

Limits and scaling

Three 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. This is also why passthrough preserves agent-client at your MCP server: with no exchange, there is nothing to overwrite it. Passthrough moves one guarantee out of identity and into networking. No gateway setting can restore it. If you cannot restrict your MCP server’s network reachability to the gateway, do not use passthrough — the RBAC you configure will be advisory rather than enforced.

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. Passthrough (Part 1) scales differently, and less well: Resource URL names one MCP server, so each MCP server needs its own connector. On-behalf-of adds a delegated permission on the shared agent-service instead. With a handful of MCP servers the difference is unimportant; with dozens it is the main argument for on-behalf-of on operational grounds alone.

Troubleshooting

Next steps