client-report-mcp
A remote MCP server that lets Claude fetch client report data (starting withArbor Energy, designed to extend to other clients) fromarborreport.polarisconsulting.net, authenticated via Microsoft Entra ID.
Status: scaffold, not yet production-ready
This was built without access to two things — fill these in before handingoff to end users:
- The real report API contract.
src/report-client.tscurrentlyguesses a REST shape (GET {REPORT_API_BASE_URL}/reports/{clientName}).Confirm the actual endpoint, auth requirements, and response shape withwhoever owns arborreport.polarisconsulting.net, then update that file. - Real Entra ID app registration values (tenant ID, client ID, clientsecret) — see
.env.example. Also confirm whether the report API expectsthe Entra access token issued for basic sign-in scopes, or a specific APIscope (api://<report-app-id>/...) — this is a common gap and will showup as 401/403 errors from the report API if wrong.
Everything else — the OAuth 2.1/DCR proxy in front of Entra ID, and the MCPtool wiring — is implemented and has been type-checked and smoke-tested(server boots, serves /healthz and OAuth metadata correctly).
How it fits together
Claude <-- OAuth 2.1 / Dynamic Client Registration --> this server <-- OAuth2 --> Entra ID
|
v
arborreport.polarisconsulting.net
See the comment block at the top of src/oauth.ts for the full step-by-stepof the authorization flow.
Local setup
npm install
cp .env.example .env # fill in real ENTRA_* and REPORT_API_BASE_URL values
npm run dev
For local testing with Claude (which needs a public HTTPS URL), run thisbehind a tunnel (e.g. ngrok http 3000) and set PUBLIC_URL to the tunnel'sHTTPS URL before starting the server.
Deploying
This is a plain Express app — deploy it anywhere that can run Node and isreachable over the public internet (or reachable via an allowlisted outboundconnection if going through a corporate firewall). Cloudflare Workers/anothermanaged platform can also handle hosting + autoscaling if preferred; you'dport the Express routes to that platform's request model.
Set the real PUBLIC_URL to wherever it ends up living, since the OAuthcallback URL ({PUBLIC_URL}/oauth/callback) has to match what's registeredin Entra ID for this app.
Important: the token stores in src/oauth.ts (pendingAuthorizations,pendingCodes, issuedTokens) are in-memory Maps. They will not survive arestart and won't work if you run more than one instance behind a loadbalancer. Replace with Redis or a database before this goes beyond asingle-instance pilot.
Handing this off to a Polaris Owner
Once deployed and confirmed working end-to-end:
- Give the Owner (e.g. Andrew Shea or Nathan Loy) the deployed MCP serverURL (
{PUBLIC_URL}/mcp). - They add it under Organization settings > Connectors > Add > Customin Claude — no OAuth Client ID/Secret needs to be entered there, sincethis server implements its own Dynamic Client Registration and handlesthe Entra exchange internally.
- Each tech then connects individually from Customize > Connectors andsigns in with their own Microsoft account — access mirrors whatever thatperson can already see in the report portal.
Testing the OAuth flow before handoff
The official MCP inspector can walk through the full authorization flowagainst a locally-tunneled instance of this server before you hand it toanyone — see the "Test your connector" step in Anthropic's custom connectordocs.