Skip to main content
Glama
autodesk-platform-services

APS MCP Auth Examples

Official
README.md
# APS MCP Auth Examples

Reference implementations of MCP servers that integrate with
[Autodesk Platform Services](https://aps.autodesk.com/), covering every combination of:

- **How the server authenticates to APS**
  - `2LO` - 2-legged OAuth, app-wide
  - `3LO` - 3-legged OAuth, per-user
  - `PKCE` - 3-legged OAuth for public clients, per-user
  - `SSA` - Secure Service Account, non-human identity
- **How MCP clients authenticate to the server**
  - not at all (STDIO)
  - via an external identity provider
  - via an OAuth proxy service

All HTTP-based examples target the **2026-07-28 MCP specification revision**
via the split **MCP TypeScript SDK v2**
(`@modelcontextprotocol/{server,express,node}`), which requires MCP servers to
act as OAuth 2.1 *resource servers* backed by a dedicated authorization server,
and prefers **Client ID Metadata Documents (CIMD)** over Dynamic Client
Registration for identifying MCP clients.

Every example exposes the same MCP tools, implemented once in `shared/` and
reused everywhere: `list-projects` (hubs + projects, via the Data Management
API) and `list-contents` (a project's top-level folders, or a specific folder's
contents).

## The examples

| Folder | MCP-client auth | What it demonstrates |
| --- | --- | --- |
| [`aps-mcp-server-local`](aps-mcp-server-local) | none (STDIO) | The simplest possible setup — a locally spawned process, no MCP-layer auth at all. |
| [`aps-mcp-server-remote-auth0`](aps-mcp-server-remote-auth0) | External IdP (Auth0) | This server only *verifies* tokens; Auth0 (or any OIDC/JWKS provider) remains the authorization server. Per-IdP-user APS providers cached in memory. |
| [`aps-mcp-server-remote-proxy`](aps-mcp-server-remote-proxy) | Separate OAuth proxy service | Relies on an OAuth proxy in front of APS authentication (`simple-oauth-proxy`) to generate "MCP tokens", and uses `/internal/exchange` endpoint to exchange these for "APS tokens". |
| [`simple-oauth-proxy`](simple-oauth-proxy) | *(is the proxy)* | The standalone, provider-agnostic OAuth proxy service consumed by `aps-mcp-server-remote-proxy`, built with Python + FastMCP. |
| [`shared`](shared) | *(library)* | The four APS auth provider classes, the two MCP tools, and small helpers reused by every example above. |

## Setup common to every example

1. Register an APS application at https://aps.autodesk.com/myapps (a
   **Traditional Web App** if you'll use any `3LO` example; a **Server-to-Server**
   / API-key style app is enough for `2LO`-only use). For `SSA`, additionally
   create a Secure Service Account and register its public key — see
   [the SSA guide](https://aps.autodesk.com/en/docs/ssa/v1/developers_guide/overview/).
2. `npm install` at the repo root — this is an npm workspaces project, so one
   install resolves `shared` and all four TypeScript servers.
3. Run any TypeScript example with `npm start` from inside its folder (or
   `npm run start -w <package-name>` from the root), after copying its
   `.env.example` to `.env` and filling in the values for your chosen
   `APS_AUTH_MODE`.
4. `simple-oauth-proxy` is a separate Python/FastMCP service — see its own
   README for setup; it's only needed if you're trying
   `aps-mcp-server-remote-proxy`.

## A note on scope

These are teaching examples, optimized to be read end-to-end in one sitting.
Several corners intentionally cut for brevity are called out in the relevant
README (in-memory-only state with no horizontal-scaling story, a simplified
CIMD fetch without full SSRF hardening in the OAuth proxy example, etc.).
Don't copy the security-relevant bits verbatim into production without reading
those notes.

Two more that apply to the shared tools rather than to any one example:

- **No pagination.** The Data Management API returns hubs, projects and folder
  contents one page at a time; `list-projects` and `list-contents` read only the
  first page, so a large account silently sees a truncated list. A real
  implementation follows the `links.next` cursor until it's absent.
- **Unbounded fan-out.** `list-projects` issues one `getHubProjects` call per hub
  concurrently. Fine for the handful of hubs a typical account has; with many
  hubs it will hit APS rate limits, so cap the concurrency.

And three that apply to the auth providers in `shared/`:

- **Token caching is deliberately naive.** Each provider holds one access token and
  re-requests it shortly before expiry. A real provider would key the cache by scope
  and share one in-flight request between concurrent callers.
- **Any refresh failure ends the session.** `ThreeLeggedAuthProvider` treats every
  failed refresh as "sign in again". In production you'd distinguish a dead grant
  (HTTP 400 `invalid_grant` — refresh token expired or revoked) from a transient one,
  so an APS outage doesn't discard a perfectly good session.
- **State is in-memory and per-instance.** `aps-mcp-server-remote-auth0` keeps its
  per-user auth providers and pending sign-ins in
  [`lru-cache`](https://www.npmjs.com/package/lru-cache) instances rather than plain
  `Map`s, so a long-running process doesn't grow without bound — but scaling out still
  means moving them to a shared store.

Maintenance

ActivityMaintained
ResponsivenessNo issues