Skip to main content
Glama
NathanPolaris09

client-report-mcp

README.md
# client-report-mcp

A remote MCP server that lets Claude fetch client report data (starting with
Arbor Energy, designed to extend to other clients) from
arborreport.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 handing
off to end users:

1. **The real report API contract.** `src/report-client.ts` currently
   *guesses* a REST shape (`GET {REPORT_API_BASE_URL}/reports/{clientName}`).
   Confirm the actual endpoint, auth requirements, and response shape with
   whoever owns arborreport.polarisconsulting.net, then update that file.
2. **Real Entra ID app registration values** (tenant ID, client ID, client
   secret) — see `.env.example`. Also confirm whether the report API expects
   the Entra access token issued for basic sign-in scopes, or a specific API
   scope (`api://<report-app-id>/...`) — this is a common gap and will show
   up 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 MCP
tool 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-step
of the authorization flow.

## Local setup

```bash
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 this
behind a tunnel (e.g. `ngrok http 3000`) and set `PUBLIC_URL` to the tunnel's
HTTPS URL before starting the server.

## Deploying

This is a plain Express app — deploy it anywhere that can run Node and is
reachable over the public internet (or reachable via an allowlisted outbound
connection if going through a corporate firewall). Cloudflare Workers/another
managed platform can also handle hosting + autoscaling if preferred; you'd
port the Express routes to that platform's request model.

Set the real `PUBLIC_URL` to wherever it ends up living, since the OAuth
callback URL (`{PUBLIC_URL}/oauth/callback`) has to match what's registered
in Entra ID for this app.

**Important:** the token stores in `src/oauth.ts` (`pendingAuthorizations`,
`pendingCodes`, `issuedTokens`) are in-memory `Map`s. They will not survive a
restart and won't work if you run more than one instance behind a load
balancer. Replace with Redis or a database before this goes beyond a
single-instance pilot.

## Handing this off to a Polaris Owner

Once deployed and confirmed working end-to-end:

1. Give the Owner (e.g. Andrew Shea or Nathan Loy) the deployed **MCP server
   URL** (`{PUBLIC_URL}/mcp`).
2. They add it under **Organization settings > Connectors > Add > Custom**
   in Claude — no OAuth Client ID/Secret needs to be entered there, since
   this server implements its own Dynamic Client Registration and handles
   the Entra exchange internally.
3. Each tech then connects individually from **Customize > Connectors** and
   signs in with their own Microsoft account — access mirrors whatever that
   person can already see in the report portal.

## Testing the OAuth flow before handoff

The official MCP inspector can walk through the full authorization flow
against a locally-tunneled instance of this server before you hand it to
anyone — see the "Test your connector" step in Anthropic's custom connector
docs.