Build1 publisher3 min readPublished
Protected Resource Metadata lets an MCP client discover the sign-in flow behind a 401 -- but Entra can still block the token
Developer clients reach a key-protected Azure Functions MCP server by attaching a header; the Claude desktop app has no such field, and the one-click fix still left VS Code holding an invalid_target error from Entra.
The Engineer · Build desk

What happened
- The same Azure Functions MCP server that Claude Code, VS Code and MCP Inspector reached with a system key in an x-functions-key header failed as a Claude desktop connector with "Couldn't register with the sign-in service."
- Pointed at an API Management gateway without its subscription key attached, MCP Inspector reported "Dynamic Client Registration rejected (HTTP 404)."
- The function app's AI (preview) tab now carries a "Turn on MCP authentication" button that creates the Entra app registration, wires up App Service authentication and disables key-based access to the MCP endpoint in one step.
- With that setup in place, VS Code discovered the metadata and the authorization server, and Entra then refused the token request on a mismatch between the advertised resource and the requested scope.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- constraint Shipping to end users means standing up an authorization server before launch. The key path does not degrade gracefully, because a client with no header field has nowhere to accept the key you would otherwise hand out.
- contradiction The supported one-click path produced metadata that Entra's own token endpoint rejected, so an operator following the portal still has resource and scope names to reconcile by hand before a spec client can complete sign-in.
- capability One app setting is the difference between a 401 that dead-ends and a 401 that names the authorization server. The second kind lets an unknown client onboard itself without any secret being shared out of band.
- decision Choosing between header keys and Entra is now a question about who installs the server, not about setup effort: three of the four clients in this test tolerate a key, and the audience determines which three you are serving.
A spec-compliant client's response to a 401 is a fixed sequence, and no step in it asks a human to paste anything. The client fetches the server's Protected Resource Metadata, learns which authorization server protects the resource, registers itself there through dynamic client registration, and then takes the user through sign-in and consent [4]. A server that answers 401 with nothing published behind it still sends the client down that road. In the dev.to account, the key-protected servers had no metadata, the clients went looking for an OAuth story that did not exist, and the attempt died at registration with a 404 [5]. The clients "were refusing to work around a server that spoke half a protocol," the author wrote [7].
That divides the client field on one UI detail. Claude Code, VS Code and MCP Inspector let you attach arbitrary headers, so a shared key works there [6]. The Claude desktop app has no header field, by design, and the spec flow is the only flow [6]. Three of the four clients named in the post will take a key; the fourth will not [17]. The post puts the split as "Keys are for tools; OAuth is for people" [16]. In my view that is the right place to draw the line: for a server your own team installs the key is fine, and for anything you hand to a non-developer there is nowhere to type it.
Fronting it properly is two app settings and an identity provider. Key access goes off with `AzureFunctionsJobHost__extensions__mcp__system__webhookAuthorizationLevel=Anonymous`, because the platform's authentication layer now stands in front of the endpoint [9]. App Service authentication, long known as Easy Auth, is then configured with Microsoft as the identity provider, a new app registration, 401 for unauthenticated requests, and the token store enabled [10]. Discovery is the second setting, `WEBSITE_AUTH_PRM_DEFAULT_WITH_SCOPES`, pointed at the scope from Expose an API [11].
Entra returned AADSTS9010010 invalid_target: "The resource parameter provided in the request doesn't match with the requested scopes" [12]. The client had sent the RFC 8707 resource parameter copied out of the metadata, and Entra enforces that this resource matches the audience of the requested scope [13]. The setup the button produced advertised a resource of the form https://...azurewebsites.net while the scope was api://.../user_impersonation, two different names for the same app [14]. The post adds that the document clients actually read is the path-specific one [15].
What transfers off Azure is the client half, because it is specified: any client implementing the MCP authorization spec will read your 401 as the start of OAuth and go looking for metadata [4]. The rest is local. The two app settings belong to the Functions extension [9][11], the AADSTS code is Entra's [12], and the resource-versus-scope check only bites where the authorization server enforces RFC 8707 [13].
What to watch
- Whether Microsoft's tutorial adds the resource-and-scope alignment step, or the portal button starts emitting a resource value that matches the scope audience.
- Whether more end-user MCP clients follow the Claude desktop app in shipping without any header field at all.
- Whether other authorization servers begin enforcing the RFC 8707 resource parameter the way Entra now does.