Clients
What each client needs to connect. The server speaks
Streamable HTTP at https://<host>/mcp, protocol 2026-07-28 with the 2025-11-25 handshake kept,
and authenticates with Entra ID OAuth only. The “verified” column records what was exercised against
a public exonaut host with the Entra registrations below; “not verified” rows describe the documented
client configuration and have not been run.
Entra registrations (customer tenant)
Section titled “Entra registrations (customer tenant)”The API app registration is the MCP server’s registration too, as Microsoft’s guide for Entra-protected MCP servers allows; interactive clients need a second, public one. The admin center shows registrations in the Microsoft Graph manifest format, which the property names below follow.
- The API app registration (the one exonaut already uses):
api.requestedAccessTokenVersionis2. It sits inside theapiobject, not at the top level. Set it before adding an https identifier URI.identifierUriscarries the canonical MCP URL (Mcp:PublicUrl, for examplehttps://mcp.contoso.com/mcp, no trailing slash) next to the existingapi://<client-id>. MCP clients send that URL as the OAuthresource, and the server names it in its metadata. With v2 tokens Entra accepted an https URL whose domain is not verified in the tenant (the default app management policy exempts apps that receive v2 tokens from the unique-tenant-identifier restriction); Microsoft’s guidance still recommends a verified domain.- Scopes and app roles stay as they are: the delegated scopes are the MCP scopes
(
api://<client-id>/<scope>), the app roles serve app-only callers. - The host’s own credential (
ExchangeOnline:ClientSecretandIPPS:ClientSecret, or a certificate) signs the host into Exchange and has nothing to do with MCP callers. The default app management policy caps a secret at 365 days; rotate before expiry and remove the expired entry (POST /applications/{id}/removePassword).
- A public client registration for interactive clients (“exonaut MCP client”, no secret,
isFallbackPublicClienttrue, single tenant):publicClient.redirectUrislists the callbacks of the clients below,requiredResourceAccesslists the API’s delegated scopes, and an admin consents them once (anoauth2PermissionGrantwithconsentTypeAllPrincipals on the client’s service principal). Grant it in the admin center (the registration’s API permissions, “Grant admin consent”) or throughPOST /oauth2PermissionGrants, which needs the Graph permissionDelegatedPermissionGrant.ReadWrite.All. Start with the read scopes, the ones the metadata advertises, and add.ReadWritescopes when a client is meant to apply changes. - Copilot Studio and ServiceNow need a confidential client with a secret (“exonaut MCP connector”), registered separately, with the callback URL each wizard prints as a web redirect URI.
Client matrix
Section titled “Client matrix”| Client | Transport | Sign-in | Configuration | v1 | Verified |
|---|---|---|---|---|---|
| Claude Code 2.1.311 | Streamable HTTP | pre-registered public client: claude mcp add --transport http --client-id <id> --callback-port 8080 -s user exonaut https://<host>/mcp, then /mcp > Authenticate (browser sign-in) |
user scope (~/.claude.json) or project .mcp.json: type: "http", oauth: { clientId, callbackPort } |
yes | verified 2026-10-27: sign-in via Entra, exonaut_query on mailboxes answered through a quick tunnel |
| VS Code 1.143.1 (Copilot) | type: "http" |
oauth.clientId in .vscode/mcp.json; MCP: List Servers > Start, then the sign-in prompt opens the browser. VS Code redirects to http://localhost:<random port>, so the public client needs the port-less http://localhost redirect URI |
{"servers":{"exonaut":{"type":"http","url":"https://<host>/mcp","oauth":{"clientId":"<id>"}}}} |
yes | verified 2026-10-27: sign-in via Entra, exonaut_query answered through a quick tunnel |
| Copilot Studio | Streamable HTTP | OAuth 2.0 manual in the wizard: confidential client id and secret, Entra authorize and token URLs, scope api://<app>/.default or the delegated scopes; the wizard prints the callback URL to register |
MCP onboarding wizard | yes | not verified in v1.2.0: not run, no Copilot Studio environment exercised |
| Microsoft 365 Copilot declarative agents | remote MCP plugin via Agents Toolkit 6.12+ | OAuth 2.1 or Entra SSO; redirect URIs https://teams.microsoft.com/api/platform/v1.0/oAuthRedirect, https://vscode.dev/redirect, SSO consent redirect |
plugin manifest | yes | not verified in v1.2.0: not run, no Microsoft 365 Copilot licence exercised |
| Agent 365 BYO MCP server (preview) | public HTTPS | a365 develop-mcp register-external-mcp-server --auth-type EntraOAuth --remote-scopes "<Mcp:PublicUrl>/.default"; admin approval |
Agent 365 CLI | after registration | optional |
| ServiceNow Zurich (AI Agent Studio) | Streamable HTTP, protocol 2025-06-18 | OAuth 2.1 authorization-code profile (client id and secret on a Connection and Credential Alias) | “Add an MCP server” | yes | not verified in v1.2.0: not run, no ServiceNow instance exercised |
| GitHub Copilot coding agent and code review | type: "http" with static headers |
none; no OAuth support | repository settings | not in v1 | unsupported |
GitHub Copilot’s coding agent is unsupported in v1 because it cannot perform an OAuth sign-in: it only sends static headers from repository secrets, and exonaut does not issue static keys. It will follow when the agent gains OAuth or when exonaut offers a client-credentials grant for it.
Local verification
Section titled “Local verification”Against a host in Development mode with the permissive auth handler, the MCP Inspector in CLI mode
lists the eight tools with their annotations and calls exonaut_status; the same commands:
npx @modelcontextprotocol/inspector --cli http://localhost:5043/mcp --transport http --method tools/listnpx @modelcontextprotocol/inspector --cli http://localhost:5043/mcp --transport http --method tools/call --tool-name exonaut_statusA Claude Code session is added with claude mcp add --transport http exonaut-local http://localhost:5043/mcp
(project scope, never committed).
Why localhost is not enough for an interactive client
Section titled “Why localhost is not enough for an interactive client”Entra issues a token only when the client’s resource value equals one of the registered
Application ID URIs character for character, and an Application ID URI cannot be http://localhost.
Clients such as Claude Code and VS Code send the URL they connected to, so a local host behind
http://localhost:5043/mcp ends at Entra’s invalid_target (AADSTS9010010). Test with a public
HTTPS URL that is registered as an Application ID URI; a Cloudflare quick tunnel to the local host
is enough. A client-credentials or device-code grant sends no resource and works against localhost.
Scopes
Section titled “Scopes”The metadata advertises the read scopes by default (Mcp:ScopesSupported=Read), so a first
consent is read-only. A client that will apply changes needs the family’s .ReadWrite scope as
well; a tool that lacks it answers the API’s 403 naming the scope to add, and the client
re-authorizes with the union of scopes.

