n Microsoft 365 Copilot Cowork (Frontier), a custom remote MCP server connector declared via agentConnectors → toolSource.remoteMcpServer:
- Works perfectly when authorization.type = "None".
- With authorization.type = "OAuthPluginVault" (Entra OAuth), the connector connects and completes the OAuth consent successfully, skills load, but every tool invocation from chat fails ("…the connector isn't responding in this session"). The server never receives an authenticated tool call.
The same MCP server and the same OAuth client registration work end-to-end with full per-user OAuth in a declarative agent in Microsoft 365 Copilot chat (m365.cloud.microsoft/chat). Only Cowork's OAuthPluginVault path fails.
This looks like Cowork is not carrying the connector's OAuth token into chat-time tool calls.
Environment
- Microsoft 365 Copilot Cowork (Frontier preview), plugin sideloaded via M365 Admin Center → Manage Apps.
- App package: M365 unified manifest v1.28, contains agentSkills + one agentConnectors entry.
- MCP server: .NET 8, official ModelContextProtocol / ModelContextProtocol.AspNetCore SDK, Streamable HTTP transport, public HTTPS (TLS 1.2+), behind nginx at https://<your-domain>/mcp/.
- Auth: Microsoft Entra ID. Single app registration, multi-tenant; OAuth client registration created in Teams Developer Portal with usage = "Any Microsoft 365 Organization"; exposes scope api://<app-id>/access_as_user; requestedAccessTokenVersion = 2. Redirect URIs include https://teams.microsoft.com/api/platform/v1.0/oAuthRedirect and https://teams.microsoft.com/api/platform/v1.0/oAuthConsentRedirect.
Connector manifest (sanitized)
"agentConnectors": [
{
"id": "cts-cowork-server",
"displayName": "CTS Inbox Agent",
"description": "Entra-secured MCP server",
"toolSource": {
"remoteMcpServer": {
"mcpServerUrl": "https://<your-domain>/mcp/",
"authorization": {
"type": "OAuthPluginVault",
"referenceId": "<oauth-client-registration-id>"
}
}
}
}
]
(Tools are discovered dynamically; behavior is identical with or without a static mcpToolDescription.)
What works ✅
- authorization.type = "None" on the exact same connector/server → Cowork connects, discovers tools, and invokes them correctly in chat. So the transport, dynamic discovery (initialize / tools/list / tools/call, Streamable HTTP), and tool schemas are all compatible with Cowork.
- The same OAuth client registration (referenceId) and the same MCP server, used by a declarative agent in M365 Copilot chat, work end-to-end: the OAuth consent succeeds and tool calls arrive at the server bearing a valid Entra v2 access token (aud = the API app, azp = the client app, scp = access_as_user, correct issuer). The server validates it and returns live data.
What fails ❌
With authorization.type = "OAuthPluginVault" in Cowork:
- In Sources & Skills, the connector appears and "Connect" / sign-in completes successfully (Entra consent succeeds — no AADSTS error).
- Skills load in chat.
- When the user asks something that triggers a connector tool, Cowork responds with a message like "I'm unable to reach the connector — it isn't responding in this session."
- Server-side during the failure: the connect/validation phase reaches the server, but chat-time tool invocations do not arrive at the server as authenticated calls. (The server logs every request, including unauthenticated ones, which it correctly rejects with 401 while EnforceAuth = true. During the chat-time failure, no valid authenticated tool call is received.)
What I've ruled out
- MCP transport / discovery — Cowork's own docs state it uses Streamable HTTP with dynamic discovery; my server supports initialize / tools/list / tools/call, and a Microsoft Copilot client (the declarative agent) completes full sessions against it.
- Tool schema completeness — tools expose full JSON inputSchema; the None-auth connector invokes them fine.
- The OAuth registration itself — it mints valid per-user v2 tokens that my server accepts; verified by the declarative agent working with the identical referenceId.
- Redirect URIs / consent — the OAuth/consent step in Cowork succeeds; there is no AADSTS failure in Entra sign-in logs at connect time.
- Server availability / auth enforcement — server is up; anonymous requests are correctly 401 with EnforceAuth = true; the None-auth path only works when enforcement is disabled.
Conclusion / the question
Everything points to a Cowork-side issue in the OAuthPluginVault flow: the connector authenticates at connect time, but the acquired token is not bound to / sent on chat-time MCP tool calls, so the server never receives an authenticated request and the tool call fails.
- Is this a known limitation/bug in the current Cowork (Frontier) preview for third-party remote MCP connectors using OAuthPluginVault?
- Is there any additional manifest, registration, or scope configuration required for Cowork specifically (vs. a declarative agent) to carry the user token into tool calls?
- If it's a known preview gap, is there an ETA or a supported interim pattern for per-user authenticated custom MCP connectors in Cowork (i.e., something other than None, which is anonymous and unacceptable for production data)?


Minimal repro
- Stand up a remote MCP server (Streamable HTTP) that requires an Entra bearer token.
- Register an OAuth client (TDP, "Any Microsoft 365 Organization") for it.
- Package a Cowork plugin with one agentConnectors → remoteMcpServer using OAuthPluginVault + that referenceId. Sideload.
- Connect the connector in Sources & Skills (consent succeeds), then ask chat to use a tool → fails; server receives no authenticated tool call.
- Switch the same connector to authorization.type = "None" (and allow anonymous on the server) → works. Use the same referenceId in a declarative agent in M365 Copilot chat → works.
n Microsoft 365 Copilot Cowork (Frontier), a custom remote MCP server connector declared via agentConnectors → toolSource.remoteMcpServer:
- Works perfectly when authorization.type = "None".
- With authorization.type = "OAuthPluginVault" (Entra OAuth), the connector connects and completes the OAuth consent successfully, skills load, but every tool invocation from chat fails ("…the connector isn't responding in this session"). The server never receives an authenticated tool call.
The same MCP server and the same OAuth client registration work end-to-end with full per-user OAuth in a declarative agent in Microsoft 365 Copilot chat (m365.cloud.microsoft/chat). Only Cowork's OAuthPluginVault path fails.
This looks like Cowork is not carrying the connector's OAuth token into chat-time tool calls.
Environment
- Microsoft 365 Copilot Cowork (Frontier preview), plugin sideloaded via M365 Admin Center → Manage Apps.
- App package: M365 unified manifest v1.28, contains agentSkills + one agentConnectors entry.
- MCP server: .NET 8, official ModelContextProtocol / ModelContextProtocol.AspNetCore SDK, Streamable HTTP transport, public HTTPS (TLS 1.2+), behind nginx at https://<your-domain>/mcp/.
- Auth: Microsoft Entra ID. Single app registration, multi-tenant; OAuth client registration created in Teams Developer Portal with usage = "Any Microsoft 365 Organization"; exposes scope api://<app-id>/access_as_user; requestedAccessTokenVersion = 2. Redirect URIs include https://teams.microsoft.com/api/platform/v1.0/oAuthRedirect and https://teams.microsoft.com/api/platform/v1.0/oAuthConsentRedirect.
Connector manifest (sanitized)
"agentConnectors": [
{
"id": "cts-cowork-server",
"displayName": "CTS Inbox Agent",
"description": "Entra-secured MCP server",
"toolSource": {
"remoteMcpServer": {
"mcpServerUrl": "https://<your-domain>/mcp/",
"authorization": {
"type": "OAuthPluginVault",
"referenceId": "<oauth-client-registration-id>"
}
}
}
}
]
(Tools are discovered dynamically; behavior is identical with or without a static mcpToolDescription.)
What works ✅
- authorization.type = "None" on the exact same connector/server → Cowork connects, discovers tools, and invokes them correctly in chat. So the transport, dynamic discovery (initialize / tools/list / tools/call, Streamable HTTP), and tool schemas are all compatible with Cowork.
- The same OAuth client registration (referenceId) and the same MCP server, used by a declarative agent in M365 Copilot chat, work end-to-end: the OAuth consent succeeds and tool calls arrive at the server bearing a valid Entra v2 access token (aud = the API app, azp = the client app, scp = access_as_user, correct issuer). The server validates it and returns live data.
What fails ❌
With authorization.type = "OAuthPluginVault" in Cowork:
- In Sources & Skills, the connector appears and "Connect" / sign-in completes successfully (Entra consent succeeds — no AADSTS error).
- Skills load in chat.
- When the user asks something that triggers a connector tool, Cowork responds with a message like "I'm unable to reach the connector — it isn't responding in this session."
- Server-side during the failure: the connect/validation phase reaches the server, but chat-time tool invocations do not arrive at the server as authenticated calls. (The server logs every request, including unauthenticated ones, which it correctly rejects with 401 while EnforceAuth = true. During the chat-time failure, no valid authenticated tool call is received.)
What I've ruled out
- MCP transport / discovery — Cowork's own docs state it uses Streamable HTTP with dynamic discovery; my server supports initialize / tools/list / tools/call, and a Microsoft Copilot client (the declarative agent) completes full sessions against it.
- Tool schema completeness — tools expose full JSON inputSchema; the None-auth connector invokes them fine.
- The OAuth registration itself — it mints valid per-user v2 tokens that my server accepts; verified by the declarative agent working with the identical referenceId.
- Redirect URIs / consent — the OAuth/consent step in Cowork succeeds; there is no AADSTS failure in Entra sign-in logs at connect time.
- Server availability / auth enforcement — server is up; anonymous requests are correctly 401 with EnforceAuth = true; the None-auth path only works when enforcement is disabled.
Conclusion / the question
Everything points to a Cowork-side issue in the OAuthPluginVault flow: the connector authenticates at connect time, but the acquired token is not bound to / sent on chat-time MCP tool calls, so the server never receives an authenticated request and the tool call fails.
- Is this a known limitation/bug in the current Cowork (Frontier) preview for third-party remote MCP connectors using OAuthPluginVault?
- Is there any additional manifest, registration, or scope configuration required for Cowork specifically (vs. a declarative agent) to carry the user token into tool calls?
- If it's a known preview gap, is there an ETA or a supported interim pattern for per-user authenticated custom MCP connectors in Cowork (i.e., something other than None, which is anonymous and unacceptable for production data)?
Minimal repro
- Stand up a remote MCP server (Streamable HTTP) that requires an Entra bearer token.
- Register an OAuth client (TDP, "Any Microsoft 365 Organization") for it.
- Package a Cowork plugin with one agentConnectors → remoteMcpServer using OAuthPluginVault + that referenceId. Sideload.
- Connect the connector in Sources & Skills (consent succeeds), then ask chat to use a tool → fails; server receives no authenticated tool call.
- Switch the same connector to authorization.type = "None" (and allow anonymous on the server) → works. Use the same referenceId in a declarative agent in M365 Copilot chat → works.