How We Connected an Azure AI Foundry Agent to Dynamics 365 Using the Dataverse MCP Server for a Texas Industrial Cybersecurity Company - CloudFronts

How We Connected an Azure AI Foundry Agent to Dynamics 365 Using the Dataverse MCP Server for a Texas Industrial Cybersecurity Company

Watch first

Twenty seconds: what the agent does, and why per-user CRM security is the part that is actually hard.

01Summary

A Houston-based cybersecurity firm wanted their sales team to ask plain-language questions about CRM data, “how many active leads do we have”, “summarise this account’s open opportunities”, without opening Dynamics 365 and building a view for every question.

We built it as an Azure AI Foundry agent connected to Microsoft Dataverse over the Model Context Protocol (MCP). No custom data layer, no synchronised copy of CRM, no bespoke API surface. Dataverse itself is the MCP server, and Foundry’s Agent Service calls it as a tool.

The agent runs on GPT-4.1 and took an afternoon to wire up. The two things that actually cost time were an OAuth redirect URL that Foundry generates after you click Connect, and an architecture decision we got wrong the first time: the tenant your agent lives in decides whether per-user CRM security is achievable at all.

02In This Blog

A practitioner walkthrough of connecting Foundry to Dynamics 365 over MCP, written around the two failures that cost us the most time rather than the happy path alone.

  • The Scenario: A sales team that wanted answers from CRM without building a view for every question.
  • The Approach: Use Dataverse as a first-party MCP server rather than building an API layer and hand-written function schemas.
  • The Action: Environment enablement, an Entra app registration, the MCP tool with OAuth identity passthrough, and the redirect URL nobody warns you about.
  • The Outcome: Per-user security enforced by Dataverse itself, with no parallel permission model to maintain.

The connection is an afternoon. The question of whose identity the agent is acting as decides whether any of it survives contact with production.

Table of Contents

03Why MCP Changes the Shape of This Problem

Before MCP, connecting an LLM to Dynamics 365 meant building glue: an API layer, a set of hand-written function definitions describing each operation, schema documentation maintained by hand, and a deployment to own forever. Every new table meant another function.

The Model Context Protocol replaces that with a contract. The server advertises its tools, the model discovers them at runtime, and the plumbing is the same whichever client connects. Microsoft ships Dataverse as a first-party MCP server, so the glue layer is simply gone.

Dataverse MCP endpointurl
https://{organisation}.crm{n}.dynamics.com/api/mcp

A preview endpoint exists at /api/mcp_preview with additional tools, gated behind a separate environment setting.

The tool surface

ToolWhat it does
searchSearches table schemas and business skills by keyword
search_dataSearches structured and unstructured data
describeReturns details for tables, records, schemas, skills and apps
read_queryRuns supported Dataverse SQL SELECT queries
create_recordInserts a row, returns the Gcfb-mcp7fd
update_recordUpdates an existing row
delete_recordDeletes a row, only after explicit user approval
create_table, update_table, delete_tableSchema operations
upsert_skill, delete_skillManages Dataverse skills and playbooks
init_file_upload, commit_file_upload, file_downloadSAS-based file handling

04Step 1: Enable the MCP Server and Allow Your Client

This is admin work in the Power Platform admin center, not maker work, and it is where most failed attempts stall.

  1. Open the environment settingsPower Platform admin center, then Manage, then Environments. Open the target environment and select Settings on the command bar.
  2. Turn on the MCP serverExpand Product, select Features, find Dataverse Model Context Protocol, and enable Allow MCP clients to interact with Dataverse MCP server.
  3. Open the allowed client listSelect Advanced Settings. This is where non-Microsoft and custom clients are registered individually.
  4. Add your client and enable itCreate an Allowed MCP Client record carrying the Application Id of the app registration you create in Step 2, then set Is Enabled to Yes.
FieldValue
NameA readable label, for example Foundry Dataverse Agent
Unique NameA unique identifier for the record
Application IdThe client (app) ID of the Entra app registration from Step 2
Is EnabledYes
Figure 1: The New Allowed MCP Client form in Dynamics 365, showing Name, Unique Name, Application Id and Is Enabled.
Figure 1: The Allowed MCP Client record. Is Enabled defaults to No.

This is a chicken-and-egg with Step 2: you need the Application Id before you can fill this in. Create the app registration first, then come back.

Two constraints worth knowing before you plan the rollout:

  • Managing the MCP server through Advanced connector policies requires the environment to be a Managed Environment.
  • The allow list applies only to the /api/mcp agent entrypoint. MCP-named custom APIs are ordinary Dataverse APIs and are not restricted by this setting.

If you want the preview tools, enable Allow MCP clients to interact with Dataverse MCP server (Preview version) as a separate step and point at /api/mcp_preview. Preview tools are not covered by Microsoft support agreements and the APIs can change without notice, so keep them out of anything you intend to run in production.

05Step 2: Register the Entra App

Foundry’s MCP tool needs an OAuth client to authenticate as. Create an app registration in the same tenant as your Dataverse environment.

  1. Create the registrationAzure portal, Entra ID, App registrations, New registration. Name it something like Foundry-Dataverse-Agent.
  2. Create a client secretUnder Certificates and secrets. Record the value immediately, because you cannot read it again.
  3. Grant the Dynamics CRM permissionUnder API permissions, add Dynamics CRM, user_impersonation, delegated. Grant admin consent if your tenant requires it.
  4. Copy the two identifiersThe Application (client) ID and the Directory (tenant) ID. You need both in Step 3, and the client ID again for the Allowed MCP Client record in Step 1.

Leave the redirect URI blank for now. Foundry generates it, and you come back in Step 4 to add it.

06Step 3: Add the MCP Tool to the Agent

In the Foundry portal, open your project, go to Build, then Agents, and open or create an agent. Ours runs on gpt-4.1 on a Global Standard deployment.

Figure 2: The Tools section of a Foundry agent with the Add dropdown open and Add tools highlighted.
Figure 2: Tools, Add, then Add tools. The two toggles above it are not what you want.
Figure 3: The Select a tool dialog on the Custom tab with Model Context Protocol highlighted.
Figure 3: The Custom tab, then Model Context Protocol (MCP), then Create.

Foundry warns you here that third-party MCP services are Non-Microsoft Products under the Product Terms. Dataverse is a Microsoft service, so this warning does not apply to what we are building, but it is worth reading before you connect anything else.

Figure 4: The empty Add Model Context Protocol tool dialog showing its placeholder values.
Figure 4: The empty dialog. Note the placeholder Refresh URL, which is misleading.

Now fill it in. Choose OAuth Identity Passthrough as the authentication method, which is what makes per-user security work.

FieldValue
NameA unique name, for example Dataverse-MCP
Remote MCP Server endpointhttps://{organisation}.crm.dynamics.com/api/mcp
AuthenticationOAuth Identity Passthrough
Client IDThe Application (client) ID from Step 2
Client secretThe secret value from Step 2
Token URLhttps://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token
Auth URLhttps://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/authorize
Refresh URLhttps://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token
Scopeshttps://{organisation}.crm.dynamics.com/user_impersonation offline_access
Figure 5: The completed Add Model Context Protocol tool dialog configured for Dataverse.
Figure 5: The same dialog filled in for Dataverse with OAuth identity passthrough.

Three details in that table are worth calling out, because the placeholder text in the dialog does not hint at any of them.

  • The Refresh URL is the same as the Token URL. The placeholder shows https://token.endpoint.com/refresh, which suggests a distinct endpoint. Entra does not have one. Refresh grants go to /oauth2/v2.0/token.
  • The scope needs the full Dataverse resource URI. It is https://{organisation}.crm.dynamics.com/user_impersonation, with your organisation’s own host. A scope of just user_impersonation will not resolve.
  • Keep offline_access in the scope list. It is prefilled by default and it is doing real work. Without it no refresh token is issued, and the agent will re-prompt for consent on a cadence that makes it unusable.

Select Connect.

07Step 4: Close the Redirect URL Loop

This is the step that is easy to miss, because it only appears after you have already clicked Connect and feel finished.

Foundry responds with a dialog headed You’ve created a credential provider, containing a redirect URL of this shape:

Generated redirect URLurl
https://global.consent.azure-apim.net/redirect/{generated-guid}
Figure 6: The credential provider dialog showing the generated OAuth redirect URL.
Figure 6: This appears after you click Connect. The redirect URL is not optional.

Copy it. Go back to your app registration in Entra, open Authentication, select Add Redirect URI, and add it there. The tool is already attached to the agent at this point and looks configured, but until this URI is registered the OAuth handshake cannot complete.

Figure 7: The app registration Redirect URI configuration blade with the consent redirect URI registered.
Figure 7: The consent redirect URI registered on the app registration.

We had this wrong, and the symptom is specific enough to be worth describing precisely.

09Calling the Agent From Your Own Application

The portal playground is fine for testing. Ours needed to sit inside a web application, which meant calling the Foundry data plane directly. Three details cost us the most time, and none of them are obvious from the portal.

The token scope is https://ai.azure.com/.default. Not the older cognitive services audience. A token for the wrong audience is issued happily and rejected at the call.

The request body key is agent_reference, and it needs a type. The portal’s JavaScript sample is wrong on both counts. The Python sample is correct.

agent reference payloadjs
// Rejected: "The 'agent' property is deprecated. Use 'agent_reference' instead."
body: { agent: { name, version } }

// Rejected: "Required property 'type' is missing"
body: { agent_reference: { name, version } }

// Correct
body: {
  agent_reference: {
    name: 'your-agent-name',
    version: '1',
    type: 'agent_reference'
  }
}

Multi-turn uses the conversations API, not previous_response_id. Create a conversation once, then pass its id on every subsequent response. Do not replay the transcript, and do not chain response ids.

multi-turn conversationjs
const conversation = await client.conversations.create();
// hold conversation.id for the life of the chat session
const response = await client.responses.create({
  conversation: conversation.id,
  input: userMessage
});

One more that cost an afternoon: a 403 on agents/write from a local machine is almost always the wrong Azure CLI tenant, not a missing role. DefaultAzureCredential picks up whatever az session is active, and on a consultant’s laptop that is rarely the tenant you think. Run az login --tenant {tenant} and restart the process. End users separately need the Azure AI User role on the Foundry resource.

10The Tenancy Trap

This is the part we would tell anyone before they start.

Our first build put the Foundry agent in our own tenant and pointed it at the client’s Dataverse. It worked. It demonstrated well. It was also architecturally dead, and we did not see why until we asked the security question properly.

A user signing into tenant A cannot have their identity flowed into tenant B’s Dataverse. On-behalf-of does not cross that boundary.

Everything in Steps 2 through 5 assumes the app registration, the Foundry project and the Dataverse environment are in one tenant. They have to be. Take that away and OAuth identity passthrough has nothing to pass through, so the MCP tool authenticates with one fixed connection and Dataverse sees the same identity for every user of the agent. A sales rep and a regional director get byte-identical results, because Dataverse is not being asked “what can this person see”, it is being asked “what can the service account see”.

That matters more than it first sounds. The Dataverse MCP server respects security roles and row-level security properly. It is a genuinely well-behaved server. But it can only enforce the security of the identity presented to it, and in a split-tenant design that identity is a constant.

DesignPer-user CRM securityVerdict
Agent in your tenant, Dataverse in the client’sNot achievable. One fixed identity for all users.Demo only
Agent in the same tenant as Dataverse, OAuth identity passthroughAchievable. Dataverse enforces roles per user.Production
Split tenant, filtering in your own middlewarePossible, but you are rebuilding Dataverse security by hand, outside DataverseAvoid

We rebuilt with the agent inside the client’s own tenant. The constraint disappeared, and the security model became Dataverse’s problem again, which is exactly where it belongs.

The same trap has a second face. A Power Apps code app cannot solve this for you either: the Power Apps host signs the user in but seals the credential, and exposes no public API for getting a token for an arbitrary audience. If your front end is a code app, the route to an external service is a custom connector over an Azure Function, where the Dataverse user token flows automatically and the Function performs the on-behalf-of exchange. Browser MSAL is the alternative, but the Foundry data plane does not offer CORS to arbitrary origins, so expect friction.

11Licensing

Since 15 December 2025, Dataverse MCP tools are charged when accessed by AI agents created outside Microsoft Copilot Studio. A Foundry agent is exactly that.

If the organisation holds qualifying Dynamics 365 Premium licences or Microsoft 365 Copilot user subscription licences, access to Dynamics 365 data is not charged. Otherwise it is metered. Model this before the pilot, not after, because the cost profile of a chat interface scales with curiosity, not with seats.

12Impact

  • 1Afternoon to wire up, once the traps were known
  • 0Lines of custom API or function schema
  • 14Dataverse tools available with no per-table work
  • 1Security model, Dataverse’s own, not a parallel one
  • Build time measured in hours, not weeks. No custom API layer, no hand-written function schemas, no data synchronisation.
  • Zero maintenance as the schema evolves. New tables and columns are discoverable through describe without touching the agent.
  • Security stays in Dataverse. With identity passthrough and a single tenant, existing security roles and row-level security apply unchanged.
  • One rebuild avoided in future engagements. The tenancy question is now the first thing we ask, before any agent is provisioned.

13Conclusion

The technical build here is genuinely easy, and that is the trap. Four dialogs and a form, and the agent is talking to CRM.

What is not easy is everything the dialogs do not tell you: that the client allow list defaults to disabled, that Foundry hands you a redirect URL after you thought you were done, that the platform you register it under decides whether consent ever sticks, and above all whose identity the agent is acting as.

Answer that last one before you provision anything. If the agent and the data live in different tenants, per-user security is off the table, and every hour spent after that point is spent on a demo.

14FAQ

01Do I need a Managed Environment?

Only if you want to govern the MCP server through advanced connector policies. Basic enablement does not require it.

02Can I restrict which tables the agent can reach?

Yes, through ordinary Dataverse security roles and row-level security. With identity passthrough, the signed-in user’s own roles apply. No MCP-specific access controls are needed. You can also limit the tool subset when configuring the connection in Foundry, which is worth doing for least privilege.

03Why does the agent keep asking for consent after I have already approved it?

The token exchange is failing after approval, so nothing is cached. Check that the redirect URI is registered under the Web platform rather than Single-page application, that it matches the generated URL exactly, and that offline_access is in the scope list.

04Why does my agent ignore the Dataverse tool entirely, with no consent prompt?

That is a configuration failure rather than a consent failure. A tool with a failed connection still shows as attached and produces zero tool calls with no error. Confirm the Allowed MCP Client record exists with Is Enabled set to Yes and the right Application Id, then add explicit instructions naming the tool.

05Does the MCP server work with Finance and Operations?

When the environment URL is an F&O host, requests route to the ERP MCP server automatically.

06Can I use this with Copilot Studio instead?

Yes, and it is considerably simpler: Copilot Studio is allow-listed by default and the Dataverse MCP Server appears directly under Add tool, with no app registration, no redirect URL and no manual OAuth configuration. Choose Foundry when you need control over the model, the orchestration, or the hosting surface.

15Get in Touch

Building an AI agent over Dynamics 365 or Dataverse?

The connection is the easy part. If you want a second opinion on the identity and tenancy model before you commit to an architecture, we are happy to look at it with you.

Talk to us
Suchit Chaudhari

Suchit Chaudhari

Dynamics 365 and Power Platform Consultant · CloudFronts

Works across Dynamics 365 and the Power Platform, with a focus on turning manual, document driven business processes into governed Dataverse solutions that survive audit and scale past their first release.


Share Story :

SEARCH BLOGS :

FOLLOW CLOUDFRONTS BLOG :


Categories

Secured By miniOrange