Skip to content

URSA-OSCAR 1.1.5 — RFC 7591 Dynamic Client Registration on MCP server

Latest

Choose a tag to compare

@burrellka burrellka released this 01 Jun 02:03
· 22 commits to main since this release

URSA-OSCAR 1.1.5

MCP spec compliance: RFC 7591 Dynamic Client Registration is now supported.

Fixed in 1.1.5

  • MCP server now accepts non-claude.ai OAuth clients via RFC 7591 DCR (mcp-server/src/ursa_oscar_mcp/auth.py). Prior versions had DCR disabled — the MCP server's only OAuth client was the pre-registered claude.ai one, so KAIROS or any other MCP client following the spec's standard discovery flow got 400 Bad Request at /authorize ("Redirect URI not registered for client") when trying to use its own redirect_uri. The MCP spec requires DCR support for general-purpose servers; URSA now provides it. POST /register per RFC 7591: the caller supplies client_name, redirect_uris, grant_types, response_types, and receives back a fresh client_id + client_secret. Registrations persist to /data/mcp_oauth_clients.json (mode 0600, atomic writes via tmpfile+rename) so they survive container restart. The pre-registered claude.ai client is reconstructed from env vars on every boot and is intentionally excluded from the persisted store (env vars remain the source of truth for it; persisting would risk a stale entry masking an env-var rotation). Startup banner now reports dcr=ENABLED instead of dcr=DISABLED. Five regression tests cover DCR registration with caller-specified redirect_uris, persistence across provider reconstruction, exclusion of the pre-registered client from disk, graceful handling of corrupt JSON stores, and skip-and-continue on individual corrupt entries.

Breaking compose change — MCP /data mount

The MCP container's /data mount must change from :ro to :rw so the DCR client store can be written. Both infra/docker-compose.yml and infra/docker-compose.production.yml have been updated. Operators on a Dockge-managed or hand-edited compose stack must mirror this change in their own compose file before redeploy, otherwise the MCP container will be unable to persist DCR registrations and they will be lost on container restart (the in-memory dict still works for the immediate caller; only persistence is lost).

The trust model is unchanged: the same operator who controls the API and watcher containers (both already :rw) also controls MCP. The /data directory is operator-owned on a private LAN volume.

No data migrations

No schema changes. No env-var changes. The DCR client store is created on first registration; missing file is normal on a fresh install.

Docker images

All four images are available at brain40/ursa-oscar-*:1.1.5 + :latest:

  • brain40/ursa-oscar-api:1.1.5
  • brain40/ursa-oscar-mcp:1.1.5
  • brain40/ursa-oscar-web:1.1.5
  • brain40/ursa-oscar-watcher:1.1.5

Upgrade

  1. Update your compose file: change image: tags to 1.1.5 on all four services. Change the MCP container's /data mount from :ro to :rw.

  2. docker compose pull && docker compose up -d --force-recreate.

  3. Verify by sending a DCR request. The MCP SDK requires the request to include both authorization_code and refresh_token in grant_types (per the MCP connector spec's refresh-token requirement):

    curl -X POST https://your-mcp-host/register \
      -H 'content-type: application/json' \
      -d '{"client_name":"test","redirect_uris":["https://example.test/cb"],"grant_types":["authorization_code","refresh_token"],"response_types":["code"]}'

    You should get a 201 response with a fresh client_id (not the pre-registered claude.ai one) and a client_secret.

    If you forget refresh_token in grant_types, you'll see error: "invalid_client_metadata", error_description: "grant_types must be authorization_code and refresh_token". That comes from the upstream MCP SDK's RegistrationHandler, not from URSA — it's the spec-mandated check. Send both grant types and it works.

Acknowledgments

Bug surfaced by the KAIROS Claude Code dev during a connector integration attempt. The diagnostic walkthrough (URSA's /.well-known advertised endpoints, the specific /authorize 400 error pattern, the recommended RFC 7591 implementation steps) made this fix straightforward to scope.

License

GNU GPL-3.0-or-later, preserving OSCAR's license terms.

Attribution

Built in the spirit and license terms of the OSCAR project. URSA-OSCAR is independent of OSCAR (different codebase, different deployment model) but downstream of OSCAR's file-format work.