Building an MCP plugin for a declarative agent is one thing; securing it is another. If you have already wired up authentication for API plugins, the good news is that MCP plugins share the same foundation. The differences are small but important, and getting them right saves you hours of debugging.
The Auth Types for MCP: A Mental Model
Every MCP plugin declares its authentication strategy in the auth object of the plugin manifest’s runtimes array. The same place as API plugins, the same reference_id mechanism, the same Teams Developer Portal registrations. But the available types are narrower:
| Type | When to Use | Token Delivery |
|---|---|---|
OAuthPluginVault | Enterprise APIs with user context (Entra ID, third-party OAuth) | Bearer token in Authorization header |
OAuthPluginVault (SSO) | Same-tenant Entra ID with silent sign-on | Silently acquired bearer token |
None | Public or development endpoints with no auth required | Nothing, anonymous calls |
Here is the quick mental model: OAuth for user-specific data, SSO for zero-friction same-tenant access, None for public or dev endpoints. Most enterprise scenarios land on OAuth or SSO.
API key authentication (ApiKeyPluginVault) is not supported for MCP plugins. If your service requires API key auth, consider exposing it as an API plugin instead.
The other key difference from API plugins: the spec object does not point to an OpenAPI document. Instead, it contains a url to your MCP server and a mcp_tool_description referencing a local file with tool definitions. Authentication always goes in the auth object at the runtime level, as a sibling of spec.
OAuth with Entra ID (OAuthPluginVault)
This is the most common pattern for enterprise MCP plugins. The user authenticates with their Microsoft Entra ID credentials, Copilot handles the token exchange, and your MCP server receives a bearer token with the user’s identity.
Here is how to wire it up end-to-end.
Step 1: Register an App in Microsoft Entra ID
Go to the Microsoft Entra admin center and register a new application (or use an existing one). This is the app that represents your MCP server: it defines what permissions exist and who can request them.
Under Authentication, add the following redirect URI to the Web platform:
https://teams.microsoft.com/api/platform/v1.0/oAuthRedirect
This is the redirect URI that the Copilot token service uses during the OAuth authorization code flow. Get this wrong and the entire flow breaks silently.
Step 2: Define Scopes
Under Expose an API, create the scopes your MCP server needs. For our Zava Insurance helpdesk MCP server, we define:
Tickets.Read: Read support ticket detailsTickets.Write: Create and update support tickets
These scopes show up in the consent prompt when a user first triggers the plugin. Be specific: broad scopes like “access everything” erode trust and may not pass admin consent review.
Step 3: Register in Teams Developer Portal
Open the Teams Developer Portal and navigate to Tools > OAuth client registration. Fill in:
- Registration name: Something descriptive:
Zava Helpdesk MCP OAuth - Base URL: Your MCP server’s base URL
- Client ID: The application ID from your Entra app registration
- Client secret: A secret you generated for that app
- Authorization endpoint:
https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/authorize - Token endpoint:
https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token - Refresh endpoint:
https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token - Scope: The scopes you defined (e.g.,
api://{app-id}/Tickets.Read) - Enable PKCE: Turn this on if your provider supports it
Save the registration. You will get an OAuth client registration ID: this is your reference_id.
When using Microsoft 365 Agents Toolkit, you can skip this manual step entirely. During project creation, select OAuth (with static registration) as the authentication type. ATK prompts you for the client ID and secret during provisioning (atk provision), creates the OAuth client registration in the Teams Developer Portal, and injects the generated reference_id into your plugin manifest automatically.
Step 4: Configure the Plugin Manifest
In your plugin manifest, set the auth block in the runtimes array:
{
"schema_version": "v2.4",
"name_for_human": "Helpdesk",
"namespace": "Helpdesk",
"description_for_human": "Create and track IT support tickets for Zava Insurance.",
"runtimes": [
{
"type": "RemoteMCPServer",
"auth": {
"type": "OAuthPluginVault",
"reference_id": "${{OAUTH2AUTHCODE_CONFIGURATION_ID}}"
},
"spec": {
"url": "https://zava-helpdesk-mcp.azurewebsites.net/mcp",
"mcp_tool_description": {
"file": "mcp-tools.json"
}
}
}
]
}
The reference_id ties your manifest to the OAuth registration you created in the Teams Developer Portal. Copilot uses this to look up the client credentials, endpoints, and scopes at runtime. Never hardcode this value: use the ${{OAUTH2AUTHCODE_CONFIGURATION_ID}} environment variable so ATK injects the correct ID during provisioning.
Entra ID SSO (OAuthPluginVault)
Single sign-on is where MCP auth gets elegant. When a user is already signed into Microsoft 365, Copilot can silently acquire a token for your MCP server: no extra consent prompt, no login screen. The manifest uses the same OAuthPluginVault type, but the registration behind the reference_id is different.
Step 1: Register an SSO Client in Teams Developer Portal
Open the Teams Developer Portal and navigate to Tools > Microsoft Entra SSO client ID registration. Fill in:
- Registration name: Something descriptive:
Zava Helpdesk SSO - Base URL: Your MCP server’s base URL
- Restrict usage by org: Select which Microsoft 365 organization has access
- Restrict usage by app: Select Any Teams app if you don’t know your final app ID yet
- Client ID: The client ID of your Entra ID app registration
Save the registration. You will get a Microsoft Entra SSO registration ID and an Application ID URI.
Step 2: Update Your Entra App Registration
After registering in the Teams Developer Portal, update your Entra ID app registration in the Microsoft Entra admin center:
- Add the generated Application ID URI to your app registration. If you already have an existing URI, use the manifest editor to add another one under
identifierUris - Under Authentication, add
https://teams.microsoft.com/api/platform/v1.0/oAuthConsentRedirectas a redirect URI in the Web platform - Under Expose an API, select Add a client application and add the enterprise token store client ID:
ab3be6b7-f5df-413d-ac2d-abf1e3fd9c0b
Step 3: Configure the Plugin Manifest
The manifest looks identical to OAuth. The difference is entirely in the registration behind the reference_id:
{
"schema_version": "v2.4",
"name_for_human": "Helpdesk",
"namespace": "Helpdesk",
"description_for_human": "Create and track IT support tickets for Zava Insurance.",
"runtimes": [
{
"type": "RemoteMCPServer",
"auth": {
"type": "OAuthPluginVault",
"reference_id": "${{SSO_CONFIGURATION_ID}}"
},
"spec": {
"url": "https://zava-helpdesk-mcp.azurewebsites.net/mcp",
"mcp_tool_description": {
"file": "mcp-tools.json"
}
}
}
]
}
Both OAuth and SSO use OAuthPluginVault as the manifest type. The reference_id determines which flow runs: a standard OAuth registration triggers the authorization code flow with a consent prompt, while an SSO registration enables silent token acquisition for users already signed into Microsoft 365.
The user experience is seamless. An employee at Zava Insurance asks, “Create a ticket for my broken badge at Building 25”, and the agent calls your MCP server with a valid token under that employee’s identity. No login prompt. No friction.
If your MCP server uses the on-behalf-of flow to access another API that requires user consent, return a 401 Unauthorized response. This causes the agent to prompt the user to sign in and grant consent.
No Authentication (None)
For MCP servers that require no credentials at all, such as public data endpoints, local development with dev tunnels, or testing tools in isolation, set the auth type to None:
{
"schema_version": "v2.4",
"name_for_human": "Helpdesk (Dev)",
"namespace": "HelpdeskDev",
"description_for_human": "Dev instance of the helpdesk MCP server.",
"runtimes": [
{
"type": "RemoteMCPServer",
"auth": {
"type": "None"
},
"spec": {
"url": "https://zava-helpdesk-local.devtunnels.ms/mcp",
"mcp_tool_description": {
"file": "mcp-tools.json"
}
}
}
]
}
No registration needed. No portal configuration. Copilot calls the MCP server directly with no auth header. This is the fastest path to a working plugin: great for prototyping or when your MCP server exposes genuinely public data.
Don’t use None as a shortcut during development if your production MCP server requires auth. The auth flow affects the entire user experience (consent prompts, token refresh, error handling), so test with real auth as early as possible.
The Runtime Auth Flow
Understanding what happens at runtime helps you debug when things go wrong. Here is the sequence when a user triggers an MCP plugin with OAuth authentication:
- User asks a question: “Create a ticket for my broken badge at Building 25.”
- Copilot selects the plugin: Based on the tool descriptions in
mcp_tool_description, the orchestrator decides to call the helpdesk MCP server. - Copilot checks auth config: It reads the
authblock from the plugin manifest and looks up the OAuth registration. - First-time consent: If the user has not consented before, Copilot presents a consent card in the chat. The user clicks to approve. On subsequent calls, this step is skipped.
- Token exchange: Copilot’s token service uses the authorization code flow to obtain an access token from your identity provider (Entra ID in our case).
- MCP call with bearer token: Copilot calls your MCP server’s Streamable HTTP endpoint with the access token in the
Authorization: Bearer {token}header. - Response returned: Your MCP server processes the tool call and returns the result. Copilot renders the response conversationally.
For SSO, the flow is simpler: steps 4 and 5 happen silently because Copilot can acquire the token without user interaction, thanks to the pre-authorized enterprise token store client. For None, steps 3 through 5 are skipped entirely.
The consent prompt only appears the first time a user interacts with a plugin that requires OAuth. After that, the token service handles refresh silently. If users report being prompted repeatedly, check that your refresh endpoint is configured correctly in the Teams Developer Portal.
Common Gotchas
After helping teams configure auth for their MCP plugins, these are the issues I see most often:
Auth properties inside spec. The most common MCP mistake: putting auth-related properties inside the spec object. The spec for MCP plugins contains only the server url and mcp_tool_description. Authentication always goes in the auth object at the runtime level, as a sibling of spec.
Trying API key auth. MCP plugins do not support ApiKeyPluginVault. If you need API key authentication, expose your service as an API plugin instead. This limitation catches teams off guard when migrating from API plugins to MCP.
Wrong redirect URI. The redirect URI in your Entra app registration must be exactly https://teams.microsoft.com/api/platform/v1.0/oAuthRedirect. A trailing slash, a typo, or using the old Bot Framework redirect will cause the auth flow to fail silently. No error message: just no token.
Missing or mismatched scopes. The scopes in your Teams Developer Portal OAuth registration must match what your MCP server expects. If your server validates for api://{app-id}/Tickets.Read but you registered Tickets.Read without the full URI, the token will not have the right claims.
Missing mcp_tool_description. The spec object needs a mcp_tool_description with a file property pointing to the tool definitions JSON file in your app package. Without it, the plugin cannot discover what tools the MCP server offers.
Token not refreshing. If you left the refresh endpoint blank in the OAuth registration, tokens expire and users get re-prompted for consent. Always fill in the refresh URL: for Entra ID, it is the same as the token endpoint.
Choosing the Right Auth Type
If you are still deciding which auth type fits your MCP scenario:
- Your MCP server serves user-specific data (employee records, personal dashboards, support tickets): use
OAuthPluginVaultwith a standard OAuth registration - Your MCP server is in the same Entra ID tenant and you want zero-friction access: use
OAuthPluginVaultwith an SSO registration - Your MCP server is public or in local development (dev tunnels, public data, testing): use
None - Your service needs API key auth: expose it as an API plugin instead, MCP does not support API keys
For the Zava Insurance helpdesk, SSO is the clear choice: the MCP server needs to know which employee is asking to create their ticket, and everyone is already signed into Microsoft 365. Anonymous for local testing with dev tunnels.
The Value You Just Unlocked
- OAuth for user-specific access: You can wire up Entra ID so Copilot handles the full token lifecycle, consent prompts included, and your MCP server always knows who is asking.
- SSO for zero-friction access: Users already signed into Microsoft 365 get seamless token acquisition with no extra login prompts, using the same
OAuthPluginVaulttype with a different Teams Developer Portal registration. - Runtime flow clarity: You now understand what happens at each step when Copilot calls your MCP server, making auth failures far easier to debug.
- Common gotcha awareness: Auth in the wrong place, missing tool descriptions, and trying API key auth are the issues that silently break MCP plugins. Now you know where to look first.
- Teams Developer Portal as the single source of truth: All credential management happens through portal registrations, keeping secrets out of your manifest files entirely.
Every declarative agent that calls an authenticated MCP server needs OAuthPluginVault configured correctly. With this knowledge, you can pick the right registration type, wire it up confidently, and debug it when something goes wrong.
Resources
- Plugin Authentication (MCP and API): Official authentication documentation covering all auth types
- Plugin Manifest v2.4 Schema: Full manifest reference including runtime auth objects
- Build MCP Plugins for Copilot: Getting started with MCP plugins using Agents Toolkit
- Declarative Agents Overview: Architecture and capabilities of declarative agents
Have questions or want to share what you're building? Connect with me on LinkedIn or check out more on The Manifest.