v0.37.0
OAuth-gated MCP servers
Adds a fourth MCP transport, type: "oauth", for remote servers that require their own sign-in — Vercel, Figma, Linear and most hosted vendor MCP servers. The plugin brokers the whole OAuth 2.1 + PKCE flow itself: discovery, Dynamic Client Registration, consent via the Web Viewer, token custody, refresh and revocation, all behind a local per-server proxy. Neither Claude nor Codex needs any OAuth-specific code.
Connect one two ways, both running the same flow and validation:
- Settings → MCP → Add MCP server → OAuth — name and URL required; scopes, an allow/deny tool filter, and client-ID / authorization-server overrides optional.
- Ask an agent to call
mcp_register_serverwithtype: "oauth".
Access and refresh tokens live only in the OS keychain, never in data.json and never returned to the calling thread. Settings → MCP → OAuth MCP servers shows each server's live status — connected with an expiry countdown, expiring soon, or needs re-authorization — with a Disconnect that revokes upstream, clears the keychain and stops the proxy.
Fixes found by hand-testing before release
The broker passed its full test suite while being completely non-functional in the real app. Three defects, all invisible to CI:
- Authorization-server traffic could never leave the renderer. Discovery, DCR, token exchange and refresh used the renderer's
fetch, but the renderer runs on afile://origin and Chromium blocks cross-origin fetch from an opaque origin regardless of the server's CORS headers. The MCP SDK converts that failure into empty metadata, so it surfaced as "this authorization server does not support Dynamic Client Registration" — a claim about the server caused by a failure in our HTTP layer. All AS traffic now routes through Obsidian'srequestUrl. - Agents never picked
type: "oauth". The tool description advertised only "stdio, HTTP or SSE", so asked to connect Vercel a model reasonably chosehttp— saving a server that could never authenticate and never opened a consent screen. - The consent callback page was mojibaked. Served without a charset, so its UTF-8 body decoded as Latin-1 and the em dash rendered as
—. Now correctly encoded, and redesigned with distinct success and error states.
Full changelog: v0.36.0...v0.37.0