Repository navigation
feat(oauth): dynamic client registration so claude.ai and ChatGPT can connect to /mcp - #428
Merged
Merged
Conversation
… connect to /mcp nixamp.com/mcp and backtoschool.help/mcp already used OAuth 2.1 + PKCE S256 with nixamp.com as the authorization server, but only built-in clients could sign in, so neither server could be added as a Claude or ChatGPT connector. - RFC 7591 registration at /api/v1/oauth/register (and POST /register, the default path, which now answers JSON instead of falling through to the web app): public clients only (token_endpoint_auth_method none), https redirects or http on localhost/127.0.0.1 with any port. Stored in nixamp_oauth_clients (created on first use, like the other OAuth tables). - AS metadata advertises registration_endpoint and authorization_response_iss_parameter_supported; every authorization response (code, error, access_denied) carries iss (RFC 9207). - A registered connector asking for no scope gets profile parties streams stations offline_access, and always a rotating refresh token (claude.ai and ChatGPT never ask for offline_access). Access tokens stay 1 h. - Protected-resource metadata: `resource` is now exactly the MCP URL (https://nixamp.com/mcp, https://backtoschool.help/mcp), also served at /.well-known/oauth-protected-resource/mcp. - Consent: not signed in now signs in on the page itself and reloads to the consent screen, which names the client; the pages refuse framing. - Every tool (nixamp, public directory, school) has a title and readOnly/destructive/openWorld hints; tools/list carries resultType, ttlMs and cacheScope. Existing nixamp tokens (CLI, session) and the built-in bittorrented and backtoschool clients work as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ThreatCrush Security Scan12 finding(s) in the 10 file(s) this pull request changes. HIGH: 11 | MEDIUM: 1
44 pre-existing finding(s) elsewhere in the repository — **HIGH**: 6 | **MEDIUM**: 14 | **LOW**: 24Not introduced by this pull request.
…and 24 more; the full set is in the SARIF artifact. Snippets are redacted; ThreatCrush never prints matched credential material. |
Contributor
Author
|
Local evidence (CI here runs scans only, no test job): |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
nixamp.com/mcp and backtoschool.help/mcp already did OAuth 2.1 + PKCE S256 (nixamp.com is the AS for both), but only built-in clients could sign in, so neither could be added as a Claude Connectors Directory or ChatGPT connector.
What
/api/v1/oauth/register, andPOST /register(the default path) now answers JSON instead of the web app. Public clients only (token_endpoint_auth_method: none); redirects must be https (no fragment/credentials) or http on localhost/127.0.0.1 with any port, which covershttps://claude.ai/api/mcp/auth_callback,https://chatgpt.com/connector_platform_oauth_redirectandhttp://localhost:*/callback. Rows innixamp_oauth_clients(created on first use like the other OAuth tables; no manual migration).registration_endpoint,authorization_response_iss_parameter_supported;iss(RFC 9207) on every authorization response.profile parties streams stations offline_access(notemail) and always a rotating refresh token; access tokens stay 1 h.resourceis now exactly the MCP URL (https://nixamp.com/mcp,https://backtoschool.help/mcp), also at/.well-known/oauth-protected-resource/mcp.frame-ancestors 'none'/X-Frame-Options: DENY.title+ readOnly/destructive/openWorld hints (src/mcp-annotations.ts, test keeps the table complete);tools/listcarriesresultType/ttlMs/cacheScope.Existing nixamp tokens (CLI/session) and the built-in bittorrented/backtoschool clients are unchanged.
Tests
test/oauth-server.test.ts: metadata, DCR (accepts claude/chatgpt/loopback, refuses confidential, plain http, fragment, bad grants;/registerJSON), full connector flow (sign-in page, consent names client, loopback port varies, code+state+iss, form-encoded exchange, refresh w/o offline_access, rotation, connections list, unknown client is a page),/mcp401 + PRM at both paths + OAuth token and old token both list annotated tools.test/school.test.ts: backtoschool PRM resource + annotated tools + cache fields.test/mcp-annotations.test.ts: no tool unannotated.nixamp party/nixamp dnswithout a session: environment-dependent CLI tests,nixamp partyfails identically on main).🤖 Generated with Claude Code