Skip to content

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.

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.

  1. The API app registration (the one exonaut already uses):
    • api.requestedAccessTokenVersion is 2. It sits inside the api object, not at the top level. Set it before adding an https identifier URI.
    • identifierUris carries the canonical MCP URL (Mcp:PublicUrl, for example https://mcp.contoso.com/mcp, no trailing slash) next to the existing api://<client-id>. MCP clients send that URL as the OAuth resource, 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:ClientSecret and IPPS: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).
  2. A public client registration for interactive clients (“exonaut MCP client”, no secret, isFallbackPublicClient true, single tenant): publicClient.redirectUris lists the callbacks of the clients below, requiredResourceAccess lists the API’s delegated scopes, and an admin consents them once (an oauth2PermissionGrant with consentType AllPrincipals on the client’s service principal). Grant it in the admin center (the registration’s API permissions, “Grant admin consent”) or through POST /oauth2PermissionGrants, which needs the Graph permission DelegatedPermissionGrant.ReadWrite.All. Start with the read scopes, the ones the metadata advertises, and add .ReadWrite scopes when a client is meant to apply changes.
  3. 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 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.

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:

Terminal window
npx @modelcontextprotocol/inspector --cli http://localhost:5043/mcp --transport http --method tools/list
npx @modelcontextprotocol/inspector --cli http://localhost:5043/mcp --transport http --method tools/call --tool-name exonaut_status

A 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.

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.