Reference implementations of MCP servers that integrate with Autodesk Platform Services, covering every combination of:
- How the server authenticates to APS
2LO- 2-legged OAuth, app-wide3LO- 3-legged OAuth, per-userPKCE- 3-legged OAuth for public clients, per-userSSA- 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).
| Folder | MCP-client auth | What it demonstrates |
|---|---|---|
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 |
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 |
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 |
(is the proxy) | The standalone, provider-agnostic OAuth proxy service consumed by aps-mcp-server-remote-proxy, built with Python + FastMCP. |
shared |
(library) | The four APS auth provider classes, the two MCP tools, and small helpers reused by every example above. |
- Register an APS application at https://aps.autodesk.com/myapps (a
Traditional Web App if you'll use any
3LOexample; a Server-to-Server / API-key style app is enough for2LO-only use). ForSSA, additionally create a Secure Service Account and register its public key — see the SSA guide. npm installat the repo root — this is an npm workspaces project, so one install resolvessharedand all four TypeScript servers.- Run any TypeScript example with
npm startfrom inside its folder (ornpm run start -w <package-name>from the root), after copying its.env.exampleto.envand filling in the values for your chosenAPS_AUTH_MODE. simple-oauth-proxyis a separate Python/FastMCP service — see its own README for setup; it's only needed if you're tryingaps-mcp-server-remote-proxy.
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-projectsandlist-contentsread only the first page, so a large account silently sees a truncated list. A real implementation follows thelinks.nextcursor until it's absent. - Unbounded fan-out.
list-projectsissues onegetHubProjectscall 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.
ThreeLeggedAuthProvidertreats every failed refresh as "sign in again". In production you'd distinguish a dead grant (HTTP 400invalid_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-auth0keeps its per-user auth providers and pending sign-ins inlru-cacheinstances rather than plainMaps, so a long-running process doesn't grow without bound — but scaling out still means moving them to a shared store.