Liking cljdoc? Tell your friends :D

com.blockether.vis.internal.foundation.mcp.oauth

OAuth 2.1 client for Model Context Protocol servers (spec 2025-06-18).

Flow — on HTTP 401 from an MCP server, we:

  1. read WWW-Authenticate: Bearer resource_metadata="..." (RFC 9728), fall back to ${origin}/.well-known/oauth-protected-resource;
  2. GET the resource-metadata JSON → pick an authorization_servers[0];
  3. GET its .well-known/oauth-authorization-server (RFC 8414) or .well-known/openid-configuration for endpoints + capabilities;
  4. dynamic-client-register (RFC 7591) if the AS supports it, or use the caller-supplied client_id;
  5. prepare an allowed callback and register it, then hand the adapter to provider.flow, the SAME lifecycle used by model-provider authentication. The initiating client opens the browser and returns directly to its gateway;
  6. exchange code → access + refresh tokens; persist to ~/.vis/mcp-tokens/<server>.edn;
  7. on later expiry / 401, refresh the token single-flight through com.blockether.vis.internal.provider.oauth/make-file-refresher.

The returned bearer-fn is a 0/1-arg function: 0-arg yields the current bearer token; 1-arg (with the token the server just rejected) forces a refresh. It never opens a browser and never waits — with nothing to refresh it throws :mcp/oauth-required, which callers turn into sign in.

OAuth 2.1 client for Model Context Protocol servers (spec `2025-06-18`).

Flow — on HTTP 401 from an MCP server, we:
  1. read `WWW-Authenticate: Bearer resource_metadata="..."` (RFC 9728),
     fall back to `${origin}/.well-known/oauth-protected-resource`;
  2. GET the resource-metadata JSON → pick an `authorization_servers[0]`;
  3. GET its `.well-known/oauth-authorization-server` (RFC 8414) or
     `.well-known/openid-configuration` for endpoints + capabilities;
  4. dynamic-client-register (RFC 7591) if the AS supports it, or use
     the caller-supplied `client_id`;
  5. prepare an allowed callback and register it, then hand the adapter to
     `provider.flow`, the SAME lifecycle used by model-provider authentication.
     The initiating client opens the browser and returns directly to its gateway;
  6. exchange code → access + refresh tokens; persist to
     `~/.vis/mcp-tokens/<server>.edn`;
  7. on later expiry / 401, refresh the token single-flight through
     `com.blockether.vis.internal.provider.oauth/make-file-refresher`.

The returned `bearer-fn` is a 0/1-arg function: 0-arg yields the current
bearer token; 1-arg (with the token the server just rejected) forces a
refresh. It never opens a browser and never waits — with nothing to refresh
it throws `:mcp/oauth-required`, which callers turn into `sign in`.
raw docstring

cancel-authorization!clj

(cancel-authorization! flow-id)

Forget an MCP attempt and release its callback worker.

Forget an MCP attempt and release its callback worker.
sourceraw docstring

complete-authorization!clj

(complete-authorization! flow-id input)

Validate and complete via the shared engine; app flows require the full callback.

Validate and complete via the shared engine; app flows require the full callback.
sourceraw docstring

forget!clj

(forget! server-name)

Drop persisted tokens for server-name after sign-out or a revoked refresh grant.

Drop persisted tokens for `server-name` after sign-out or a revoked refresh grant.
sourceraw docstring

make-bearer-fnclj

(make-bearer-fn server-name server-url www-auth-atom)

Build a 0/1-arg fn returning the current Bearer token string for server-name. 0-arg yields the persisted token; 1-arg (the token the server just rejected) forces a single-flight refresh. server-url is the MCP endpoint and www-auth-atom is the atom the HTTP transport keeps pointed at the latest WWW-Authenticate header, so a refusal can carry the LIVE 401 details.

It never runs an interactive authorization. Every caller sits on a path a human is waiting on - a tool call, a reconcile tick, the health loop - and the browser dance blocked each of them for up to five minutes on a server nobody had signed into yet, which looked exactly like a hung daemon. With nothing to refresh it throws :mcp/oauth-required immediately; authorizing is the explicit headless flow below, which hands back a URL instead of waiting for one.

Build a 0/1-arg fn returning the current Bearer token string for `server-name`.
0-arg yields the persisted token; 1-arg (the token the server just rejected)
forces a single-flight refresh. `server-url` is the MCP endpoint and
`www-auth-atom` is the atom the HTTP transport keeps pointed at the latest
`WWW-Authenticate` header, so a refusal can carry the LIVE 401 details.

It never runs an interactive authorization. Every caller sits on a path a
human is waiting on - a tool call, a reconcile tick, the health loop - and the
browser dance blocked each of them for up to five minutes on a server nobody
had signed into yet, which looked exactly like a hung daemon. With nothing to
refresh it throws `:mcp/oauth-required` immediately; authorizing is the
explicit headless flow below, which hands back a URL instead of waiting for
one.
sourceraw docstring

poll-authorization!clj

(poll-authorization! flow-id)

Read the common retained browser/app verdict.

Read the common retained browser/app verdict.
sourceraw docstring

start-authorization!clj

(start-authorization! server-name
                      server-url
                      {:keys [www-auth auth-hint callback-mode]})

Discover/register the MCP protocol adapter; the shared engine owns its lifecycle. App returns go directly through the paired client, never through a relay.

Discover/register the MCP protocol adapter; the shared engine owns its lifecycle.
App returns go directly through the paired client, never through a relay.
sourceraw docstring

token-statusclj

(token-status server-name)

Non-secret view of the persisted OAuth tokens for server-name. Never returns the token itself: a client may render it, and a client is never trusted with credentials.

Non-secret view of the persisted OAuth tokens for `server-name`. Never returns
the token itself: a client may render it, and a client is never trusted with
credentials.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close