Releases: burrellka/URSA-OSCAR
Release list
URSA-OSCAR 1.1.5 — RFC 7591 Dynamic Client Registration on MCP server
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 got400 Bad Requestat/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 /registerper RFC 7591: the caller suppliesclient_name,redirect_uris,grant_types,response_types, and receives back a freshclient_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 reportsdcr=ENABLEDinstead ofdcr=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.5brain40/ursa-oscar-mcp:1.1.5brain40/ursa-oscar-web:1.1.5brain40/ursa-oscar-watcher:1.1.5
Upgrade
-
Update your compose file: change
image:tags to 1.1.5 on all four services. Change the MCP container's/datamount from:roto:rw. -
docker compose pull && docker compose up -d --force-recreate. -
Verify by sending a DCR request. The MCP SDK requires the request to include both
authorization_codeandrefresh_tokeningrant_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 aclient_secret.If you forget
refresh_tokeningrant_types, you'll seeerror: "invalid_client_metadata", error_description: "grant_types must be authorization_code and refresh_token". That comes from the upstream MCP SDK'sRegistrationHandler, 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.
URSA-OSCAR 1.1.4 — Local-model malformed-tool-call diagnostic
URSA-OSCAR 1.1.4
Two production-debugged data fixes, one architectural cleanup, and a UX polish for local-model users. The full 1.1.2 → 1.1.4 changeset (a separate 1.1.3 Release was skipped; Docker Hub :1.1.3 images still work for anyone who pulled them; this Release supersedes them).
Fixed in 1.1.4
- Malformed-tool-call diagnostic for under-capable local models (
backend/src/ursa_oscar/api/ai.py). When a local model isn't capable enough to format an OpenAI-style tool call against URSA's full 18-tool surface (Qwen3-4b on CPU is the canonical example), it typically emits{"as text content trying to write a JSON tool-call, then finishes withstop_reason="stop". The chat panel previously rendered the partial JSON literally, leaving the user staring at a confusing single{. The chat handler now detects this shape (text content ≤10 chars starting with{, stop_reason=stop, no tool_calls) and surfaces a friendly diagnostic error with concrete next steps: switch to Claude API, use a larger local model (Qwen3-30b-instruct, Llama-3.3-70b-instruct), or run on GPU instead of CPU. Two regression tests cover the detection firing for the failure shape and not misfiring on legitimate short responses likeYes..
Fixed in 1.1.3
-
Session boundary detection no longer splits on clock-minute boundaries (
backend/src/ursa_oscar/analytics/edf_parser.py). The EDF importer grouped files into sessions by hour-and-minute filename prefix. When a session's events file landed at01:04:53and the waveforms file landed at01:05:00(7 seconds apart but crossing the minute mark), they were treated as two separate sessions: one events-only, one waveforms-only. Mask-on duration inflated by the length of the affected session. Replaced with sliding-window temporal clustering at 30-second tolerance. Operators with affected nights must force re-import to pick up the corrected session boundaries viaURSA_OSCAR_WATCH_FORCE_REIMPORT=trueand a watcher recreate. Three regression tests cover the minute-boundary case, the 50-second-real-restart case, and the multi-minute-separation case. -
Thinking-mode model support in the OpenAI-compat adapter (
backend/src/ursa_oscar/ai_proxy/providers/openai_compat.py,frontend/src/components/AiChatPanel.tsx). Qwen3 (via LocalAI / Ollama) emits chain-of-thought indelta.reasoning; DeepSeek-R1 usesdelta.reasoning_content. Both naming conventions are now read and surfaced as a newreasoningevent type. The chat panel renders these as a collapsible "Reasoning" trail above the assistant's content, open by default while in-flight, collapsed once the answer arrives. Reasoning content stays out of the assistant message'scontentfield so follow-up turns don't carry the chain-of-thought back to the LLM. Stream timeout bumped from 120s to 300s on the read path. -
Settings page image-version chips are now self-introspecting, eliminating the dual-update problem (
backend/src/ursa_oscar/api/system.py,mcp-server/src/ursa_oscar_mcp/__main__.py,watcher/src/ursa_oscar_watcher/__main__.py,frontend/vite.config.ts). Previously the chips read fromURSA_OSCAR_*_IMAGE_VERSIONenv vars that the operator had to keep in sync with theimage:lines in their compose file. Now each service is the source of truth for its own version: API readsimportlib.metadata.version('ursa-oscar-backend'); MCP exposes an unauthenticated/versionendpoint the API queries; the watcher writes/data/versions/watcher.txtat startup; the web bundle bakes__URSA_WEB_VERSION__via Vite at build time. Env-var overrides are retained for testing scenarios but the common case requires no operator coordination. 11 regression tests cover the introspection helpers.
Fixed in 1.1.2
-
Test connection button reflects the saved config, not in-flight edits (
frontend/src/pages/SettingsAi.tsx). The button used to enable the moment the operator typed an API key, but the/ai/testendpoint probes the persisted config. Five disabled states now, each with a tooltip naming the missing piece. Local LLM presets skip the key requirement. -
Gemini provider preset refreshed (
backend/src/ursa_oscar/ai_proxy/providers/presets.py). The default model list shipped withgemini-2.0-flash-expandgemini-1.5-pro(both deprecated by Google as of May 2026) andgemini-1.5-flash(on the deprecation track). Replaced withgemini-3.5-flash. Also rolled OpenRouter's stalegoogle/gemini-2.0-flashtogoogle/gemini-3.5-flash.
README addition
The README now includes a section on the AI value proposition: the MCP-exposed-data-plus-specialized-AI-conversation pattern that motivated URSA in the first place, and the in-app AI assistant as the fallback path for deployments that can't expose a public MCP endpoint. The reusable architectural pattern is linked to the new template repo at https://github.com/burrellka/mcp-server-template.
Re-import note for operators
If your URSA installation has nights where mask-on duration looks inflated (typically 10+ hours when the real value should be 6-7 hours), at least one of those nights hit the minute-boundary bug. The fix corrects future imports automatically; existing nights need a force re-import to rewrite session boundaries.
Easiest path:
- In your compose env, set
URSA_OSCAR_WATCH_FORCE_REIMPORT=true docker compose up -d --force-recreate ursa-oscar-watcher- Wait one watcher poll cycle (default 30 seconds). The watcher re-imports every night present on the SD card source.
- Set
URSA_OSCAR_WATCH_FORCE_REIMPORT=falseagain (or remove the override) and recreate the watcher.
The re-import is idempotent for nights not affected by the bug; nights with the corrupted session pair get rewritten with the corrected single-session shape.
No data migrations
No schema changes. No env-var changes. Same /data volume layout. Upgrade is docker compose pull && docker compose up -d --force-recreate.
Architecturally locked in 1.0 (carried into 1.1.x)
- Single-tenant. One operator, one instance.
- Self-hosted. No cloud sync, no SaaS option.
- Password authentication only. No SSO / OAuth providers for operator login.
- No email password recovery. Recovery is filesystem-level (delete
auth.json, re-bootstrap). - Statistical method declaration in every analytical response.
- Sample-size discipline as refusals, not warnings (
INSUFFICIENT_DATAreturns when below the floor).
Docker images
All four images are available at brain40/ursa-oscar-*:1.1.4 + :latest:
brain40/ursa-oscar-api:1.1.4brain40/ursa-oscar-mcp:1.1.4brain40/ursa-oscar-web:1.1.4brain40/ursa-oscar-watcher:1.1.4
Upgrade
Operators on 1.0.x, 1.1.0, 1.1.1, 1.1.2, or 1.1.3: bump the four image tags to 1.1.4 and docker compose up -d --force-recreate. No data migrations. After upgrade, follow the Re-import note above if you have affected nights from the session-boundary bug.
Test coverage
- Backend: 380+ tests pass (including 53/53 AI proxy, 9/9 EDF parser with new regression coverage, 11/11 version-introspection)
- MCP: 40+ tests pass
- Watcher: 25+ tests pass
- Frontend:
tsc -b && vite buildclean
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.
URSA-OSCAR 1.1.2 — Settings polish + README AI value-prop
URSA-OSCAR 1.1.2
Settings polish ahead of the community launch.
Fixed in 1.1.2
-
Test connection button reflects the saved config, not in-flight edits (
frontend/src/pages/SettingsAi.tsx). The button used to enable the moment the operator typed an API key, but the/ai/testendpoint probes the persisted config. The disagreement produced a confusing 400 ("no provider configured") when the operator clicked Test before clicking Save. Five disabled states now, each with a tooltip that names the missing piece: no provider saved, selected provider not saved, no key saved, in-flight edits diverge from saved state, or ready to fire. Local LLM presets withsupports_local_routing=trueskip the key requirement (most local servers accept empty auth). -
Gemini provider preset refreshed (
backend/src/ursa_oscar/ai_proxy/providers/presets.py). The default model list shipped withgemini-2.0-flash-expandgemini-1.5-pro(both deprecated by Google as of May 2026) andgemini-1.5-flash(on the deprecation track). Replaced withgemini-3.5-flash. The freeform model field still accepts any model string for operators who want a specific snapshot. Also rolled OpenRouter's stalegoogle/gemini-2.0-flashdefault togoogle/gemini-3.5-flash.
README addition
The README now includes a section on the AI value proposition: the MCP-exposed-data-plus-specialized-AI-conversation pattern that motivated URSA in the first place, and the in-app AI assistant as the fallback path for deployments that can't expose a public MCP endpoint. The reusable architectural pattern is linked to the new template repo at https://github.com/burrellka/mcp-server-template.
No data migrations
No env-var changes. Same /data volume layout. Upgrade is docker compose pull && docker compose up -d --force-recreate.
Architecturally locked in 1.0 (carried into 1.1.x)
- Single-tenant. One operator, one instance.
- Self-hosted. No cloud sync, no SaaS option.
- Password authentication only. No SSO / OAuth providers for operator login.
- No email password recovery. Recovery is filesystem-level (delete
auth.json, re-bootstrap). - Statistical method declaration in every analytical response.
- Sample-size discipline as refusals, not warnings (
INSUFFICIENT_DATAreturns when below the floor).
Docker images
All four images are available at brain40/ursa-oscar-*:1.1.2 + :latest:
brain40/ursa-oscar-api:1.1.2brain40/ursa-oscar-mcp:1.1.2brain40/ursa-oscar-web:1.1.2brain40/ursa-oscar-watcher:1.1.2
Upgrade
Operators on 1.0.x, 1.1.0, or 1.1.1: bump the four image tags to 1.1.2 and docker compose up -d --force-recreate. No data migrations.
Test coverage
- Backend: 350+ tests pass (including 50/50 AI proxy)
- MCP: 40+ tests pass
- Watcher: 25+ tests pass
- Frontend:
tsc -b && vite buildclean
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.
URSA-OSCAR 1.1.1 — Auth fix for in-app chat
URSA-OSCAR 1.1.1
A bugfix-only release ahead of the community launch.
Why 1.1.1 exists
While preparing the 1.1.0 community launch, a launch-day test of the in-app AI chat surfaced two latent auth bugs that would have been visible on the very first user query. 1.1.1 fixes both before the public Release.
If you already pulled :1.1.0, the right move is to bump to :1.1.1 directly. There is no :1.1.0 Release announcement — the tag exists but the Release object is being skipped.
Fixed in 1.1.1
- In-app AI chat tool calls now authenticate (
backend/src/ursa_oscar/ai_proxy/tools.py,backend/src/ursa_oscar/api/ai.py). Phase 6.4 added_AUTH_REQUIREDto every API router but the AI proxy's internal loopback to the API didn't forward the operator's JWT. Every tool call from the chat panel returned{"detail":"Not authenticated."}. The chat endpoint now reads the JWT from either theursa_oscar_sessioncookie (browser session) or theAuthorization: Bearerheader (MCP/CLI client), constructs a Bearer header, and passes it via the newauth_headerkwarg onexecute_tool. TheTest connectionbutton in Settings worked because it calls the LLM provider directly without an URSA loopback — only tool-call queries hit the broken path. New regression testtest_chat_forwards_session_cookie_as_bearer_to_loopbackcovers both auth paths. generate_reportMCP tool now authenticates (mcp-server/src/ursa_oscar_mcp/tools/generate_report.py). Same Phase 6.4 oversight in a different code path: this tool used rawhttpx.Client()instead of the auth-attachingget_client()helper inclient.py. Reports requested from a claude.ai Custom Connector returned 401. Now usesget_client()so the resolved service token is attached.- No data migrations. No env-var changes. Same
/datavolume layout.
1.1.0 content (carried into 1.1.1)
1.1.1 includes everything that was scheduled for 1.1.0:
- In-app Help system — 37 topics across 7 sections covering installation, every feature, every statistical method, architecture, deployment, and troubleshooting. The Methodology section is verbatim with the PDF report's methodology disclosure (single source of truth, no drift).
- AI assistant Help integration — the AI assistant queries Help content via the new
get_help_topicMCP tool, grounding responses in URSA-OSCAR-specific documentation rather than general LLM knowledge. - About modal — version info, license, OSCAR attribution, GitHub link, accessible from the sidebar footer.
- No-drift regression test — every tool / endpoint reference in the Help markdown is verified against the actual codebase. Catches stale references on every CI run.
Architecturally locked in 1.0 (carried into 1.1.x)
These are the architectural decisions that 1.0 committed to. They aren't likely to change in any near-term release:
- Single-tenant. One operator, one instance.
- Self-hosted. No cloud sync, no SaaS option.
- Password authentication only. No SSO / OAuth providers for operator login.
- No email password recovery. Recovery is filesystem-level (delete
auth.json, re-bootstrap). - Statistical method declaration in every analytical response.
- Sample-size discipline as refusals, not warnings (
INSUFFICIENT_DATAreturns when below the floor).
If any of these don't fit your needs, URSA-OSCAR may not be the right tool. See frontend/src/help/content/arch-single-tenant.md for the rationale.
Installation
See the README Quick Start or the in-app Help → Getting Started topics:
Upgrade from earlier versions
Operators on 0.12.x: see Docs/35-migration-0.12-to-0.13.md.
Operators already on 1.0.x or 1.1.0: just bump the four image tags to 1.1.1 and docker compose up -d --force-recreate. No data migrations, no env-var changes.
Docker images
All four images are available at brain40/ursa-oscar-*:1.1.1 + :latest:
brain40/ursa-oscar-api:1.1.1brain40/ursa-oscar-mcp:1.1.1brain40/ursa-oscar-web:1.1.1brain40/ursa-oscar-watcher:1.1.1
Test coverage
- Backend: 350+ tests pass (auth, AI proxy, ingestion, analytics, reports, help-no-drift)
- MCP: 40+ tests pass (auth provider + tool layer + token resolution)
- Watcher: 25+ tests pass (loop, API client, token resolution)
- Frontend:
tsc -b && vite buildclean
What's next
URSA-OSCAR 1.1.x is feature-stable. Future releases respond to community demand. Items currently under consideration:
- Multi-profile support (would require re-architecting the single-tenant boundary; non-trivial)
- Richer authentication models (TOTP, OAuth providers for operator login)
- Token usage / cost surfacing in the AI chat panel
- Mobile-narrow UI polish
- Conversation export (PDF / Markdown)
If any of these matter to you, file a GitHub Issue. Real user feedback shapes priorities; vague intent doesn't.
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. Without years of OSCAR-community reverse-engineering on ResMed's SD-card format, URSA-OSCAR wouldn't exist.