Skip to main content
The hosted ish MCP server signs clients in over OAuth and forwards the resulting token to the ish backend. You never paste a token into a config file. For the steps to add the server and sign in, see Connecting; this page documents the auth machinery behind it.

Auth modes

The server runs in one of two modes, selected by the ISH_AUTH_MODE environment variable.
ModeWhere it runsInbound validationToken source for backend calls
jwtHosted (mcp.ishlabs.io), defaultValidates the inbound Supabase JWTThe validated access token
localLocal dev onlyNone~/.ish/config.json, refreshed on demand
The hosted server you connect to always runs in jwt mode. The local mode exists for running the server yourself against a local backend.

jwt mode

In jwt mode the server validates every inbound request and acts as an OAuth resource server. Validation. Inbound Supabase access tokens are verified as ES256 against the project’s JWKS, with audience authenticated. The backend re-validates the same token, so a request is checked twice (defense in depth). Discovery. The server advertises Supabase as its authorization server using two discovery documents: OAuth 2.0 Protected Resource metadata (RFC 9728) at /.well-known/oauth-protected-resource, and Authorization Server metadata (RFC 8414) at /.well-known/oauth-authorization-server. Supabase serves its OAuth metadata at /.well-known/openid-configuration rather than the RFC 8414 path, so the server forwards the OIDC document at the RFC 8414 path and injects a registration_endpoint (Supabase’s Dynamic Client Registration endpoint, which the OIDC document omits). That metadata is what lets a client (Claude Code, Cursor, and others) bootstrap sign-in with no pre-issued token: it discovers Supabase, registers itself over Dynamic Client Registration, runs the OAuth code flow, and presents the resulting access token to the server.
ISH_AUTH_MODE
string
default:"jwt"
Selects the auth mode. jwt for the hosted server, local for dev.
ISH_MCP_BASE_URL
string
default:"http://localhost:8765"
Public base URL of this server. Required in jwt mode so the discovery metadata advertises the correct base.
ISH_SUPABASE_PROJECT_REF
string
default:"hngymyxdyamokpbeakps"
Supabase project reference. Derives the project URL, the auth base, and the JWKS URI.
ISH_JWT_AUDIENCE
string
default:"authenticated"
Required aud claim on inbound tokens.

OAuthProxy

When the upstream OAuth client credentials are set, the server fronts Supabase with a FastMCP OAuthProxy instead of pointing clients straight at Supabase. Why it exists. Supabase’s OAuth server validates redirect_uri by exact string match and does not implement the RFC 8252 loopback exception (where an auth server ignores the port for http://127.0.0.1 and http://localhost redirects). A client that binds a fresh ephemeral loopback port per session, with VS Code being the notable one, reuses a cached registered client_id whose redirect_uris no longer match the port it bound, so Supabase rejects the authorize request with invalid redirect_uri. Cursor, Claude Code, and the app builders keep a consistent redirect through register, authorize, and callback, so they never hit this. What the proxy does. It advertises this server as the authorization server, presents a full Dynamic Client Registration interface that accepts any loopback port, and holds one fixed redirect ({ISH_MCP_BASE_URL}/auth/callback) registered with Supabase. It translates between the client’s dynamic loopback URI and the fixed upstream one, so every client works regardless of how it picks its redirect. The proxy runs its own PKCE leg upstream (S256). It does not forward RFC 8707 resource indicators, since Supabase does not implement them.
ISH_OAUTH_UPSTREAM_CLIENT_ID
string | null
default:"null"
Client ID of a confidential OAuth client registered with Supabase whose redirect_uris is exactly {ISH_MCP_BASE_URL}/auth/callback. Set with the secret to enable the proxy.
ISH_OAUTH_UPSTREAM_CLIENT_SECRET
string | null
default:"null"
Secret for the upstream client (token_endpoint_auth_method client_secret_basic).
The proxy turns on only when both variables are set. With either unset, the server falls back to advertising Supabase directly, which keeps it bootable before the operator provisions the upstream client and works for clients with a stable redirect.
The proxy keeps short-lived state (authorization codes, in-flight transactions, and the reference-token to upstream-token mapping) in an in-memory store by default. Run the service as a single instance, or wire a shared store, so the instance that handles /authorize and /auth/callback is the same one that handles /token and later tool calls.

Token forwarding

Tools never read headers or the local config directly. Each tool obtains the Authorization header to send to api.ishlabs.io through a single chokepoint.
The forwarded token is the validated access token, which is the upstream Supabase JWT under both the OAuthProxy and the direct provider.Under the proxy, the inbound bearer is a short reference token the proxy mints to the client; the proxy resolves it server-side to the stored upstream Supabase JWT, and that JWT is what gets forwarded. Under the direct provider, the resolved token equals the inbound bearer. If the resolved access-token context is unavailable, the server forwards the inbound Authorization header verbatim; a request with no bearer is rejected.
The backend re-validates whatever token it receives, so forwarding is defense in depth rather than the only check.

local mode

local mode is for running the server against a local backend during development. It does no inbound validation: the request is trusted, and the bearer comes from the CLI’s saved session via the token-forwarding path above.
ISH_HOME
path
default:"~/.ish"
Config directory the server reads config.json from. Shared with the CLI.
ISH_SUPABASE_URL
string
Override the Supabase project used for token refresh. Pair with ISH_SUPABASE_ANON_KEY to point a dev session at the dev project; otherwise the project is resolved from the token’s iss claim.
ISH_SUPABASE_ANON_KEY
string
Publishable key for the refresh project. Required alongside ISH_SUPABASE_URL.
Run ish login before starting the server in local mode. With no config.json, an expired token and no refresh_token, or a config written by an older CLI without oauth_client_id, tool calls fail with a message telling you to log in again.

Confirm the session

Read the ish://identity/me resource, or call a read-only tool like workspace_get, to see whose account the session is acting as. A read draws no credits. See Connecting for the full check.