Remote MCP server for ProductAI, deployable to DigitalOcean App Platform. Auth is delegated to your existing Auth0 tenant (productai.eu.auth0.com). Upstream API calls hit relum-api with each user's per-account x-api-key.
Sibling project to ShapeStudio/productai-mcp — that one is the local stdio server. This one is the remote HTTP server that can be submitted to Anthropic's Connectors directory.
Claude ──OAuth─▶ productai-mcp-server ──OAuth (RS256)──▶ Auth0
│
│ on tool call:
│ 1. lookup Users.auth0Id → users.id
│ 2. lookup ApiKeys.key (active, latest) for users.id
│ 3. call relum-api/api/* with x-api-key
▼
api.productai.photo (relum-api)
We are the OAuth provider exposed to Claude (with RFC 7591 Dynamic Client Registration, PKCE S256), but we delegate the actual user authentication to Auth0. After Auth0 returns, we mint our own short-lived HS256 JWTs.
| Path | Purpose |
|---|---|
GET /healthz |
Liveness |
GET /.well-known/oauth-authorization-server |
RFC 8414 |
GET /.well-known/oauth-protected-resource |
RFC 9728 (MCP discovery) |
POST /oauth/register |
Dynamic Client Registration |
GET /oauth/authorize |
Validates PKCE request, redirects user to Auth0 |
GET /oauth/callback |
Auth0 redirects here; we mint an MCP code |
POST /oauth/token |
authorization_code + refresh_token grants |
ALL /mcp |
Streamable HTTP MCP transport (Bearer required) |
| Tool | Read-only? | Description |
|---|---|---|
generate_image |
no | Start a generation job |
generate_and_wait |
no | Generate + poll until complete |
get_job |
yes | Status of a job |
wait_for_job |
yes | Poll an existing job until done |
All tools carry MCP annotations (title, readOnlyHint, destructiveHint, idempotentHint, openWorldHint) — required by Anthropic Connectors review.
- In
productai.eu.auth0.comdashboard, Applications → Create Application → Regular Web Application, name itProductAI MCP. - In its Settings:
- Allowed Callback URLs:
https://mcp.productai.photo/oauth/callback - Allowed Logout URLs:
https://mcp.productai.photo - Token Endpoint Authentication Method:
Post
- Allowed Callback URLs:
- APIs → relum-api → Machine to Machine Applications → ProductAI MCP: authorize, leave scopes blank.
- Grab the Client ID and Client Secret — those become
AUTH0_MCP_CLIENT_ID/AUTH0_MCP_CLIENT_SECRET.
Connect to the same Postgres as relum-api:
psql "$DATABASE_URL" -f migrations/0001_oauth.sqlThis creates three new tables: mcp_oauth_clients, mcp_oauth_transactions, mcp_oauth_codes. The existing Users / ApiKeys tables are read but not modified.
cp .env.example .env # fill in the real values
npm install
npm run dev
BASE=http://localhost:8080 npm run test:smokeFor the full OAuth dance locally, point Auth0's callback at http://localhost:8080/oauth/callback (use ngrok if Auth0 rejects loopback).
- Push this repo to GitHub at
ShapeStudio/productai-mcp-server. - DigitalOcean → Apps → Create App → from GitHub. DO detects
.do/app.yaml. - Fill in the four
SECRETenv vars in the DO console:AUTH0_MCP_CLIENT_ID,AUTH0_MCP_CLIENT_SECRETDATABASE_URL(same as relum-api)OAUTH_JWT_SECRET—openssl rand -base64 64
- Settings → Domains → add
mcp.productai.photo. - Verify:
curl https://mcp.productai.photo/.well-known/oauth-authorization-server
Once deployed and live:
- Run through https://claude.com/docs/connectors/building/review-criteria
- Submit: https://clau.de/mcp-directory-submission
Required collateral:
- Public docs page (link or blog post) covering setup + use cases
- Logo + favicon
- Privacy policy URL
- Test account credentials for Anthropic review
MIT