MCPJam / MCPJam/inspector

[BUG] OAuth access token refresh not triggered on 401 — expired tokens cause permanent tool failure

Open
#1,593 0 comments 1 reaction 0 assignees View on GitHub
bug
Dominant language
TypeScript
Stars
2.2k
Forks
283
Avg merge
11h 39m
Merged PRs (30d)
740

Description

# Issue

When an MCP server's OAuth access token expires during an active session...
* tool calls from the `Tools` tab fail permanently with `Streamable HTTP error: Error POSTing to endpoint: {"error":"invalid_token"}`.
* tool calls from the `App Builder` tab fail permanently _silently_; no response is shown anywhere.

The Inspector does not use the refresh token to obtain a new access token, even though the SDK (`MCPClientManager`) seems to supports automatic 401 → refresh → retry.

I've asked our good friend Claude why, it says:

> The root cause is in `createServerConfig()` in `mcpjam-inspector/client/src/lib/oauth/mcp-oauth.ts`. It bakes the access token into a static `Authorization` header and never passes `refreshToken` or `clientId` to the backend:
> ```typescript
> function createServerConfig(serverUrl: string, tokens: any): HttpServerConfig {
> // Note: We don't include authProvider in the config because it can't be serialized
> // when sent to the backend via JSON. The backend will use the Authorization header instead.
> // Token refresh should be handled separately if the token expires.
> return {
> url: serverUrl,
> requestInit: {
> headers: tokens.access_token
> ? { Authorization: `Bearer ${tokens.access_token}` }
> : {},
> },
> };
> }
> ```
> Because `refreshToken` and `clientId` are absent from the config, `MCPClientManager.connectViaHttp()` never instantiates a `RefreshTokenOAuthProvider` and the `StreamableHTTPClientTransport` has no `authProvider` — so when it receives a 401 there is no refresh path.

# How to reproduce

1. Configure an MCP server with OAuth 2.0 that issues short-lived access tokens (e.g. `expires_in: 10`).
2. Connect to the server via the Inspector — OAuth flow completes successfully, tools are listed.
3. Wait for the access token to expire (10+ seconds in this example).
4. Click **Run** on any tool.
5. The Response panel shows: `Streamable HTTP error: Error POSTing to endpoint: {"error":"invalid_token"}`.
6. No refresh token exchange is attempted. All subsequent tool calls also fail.

# Expected Behavior

When a tool call receives HTTP 401 with `WWW-Authenticate: Bearer error="invalid_token"`, the Inspector should use the refresh token to obtain a new access token from the server's token endpoint, then retry the request.

# Screenshots

Image

Image

# Platform

Linux, MCPJam Inspector v2.0.5 (latest)

# Additional Context

Our good friend Claude had the following suggestion:

> The fix should be straightforward: `createServerConfig()` should pass `refreshToken` and `clientId` (both available in `localStorage`) as top-level fields in the `HttpServerConfig` instead of a static `Authorization` header:
> ```typescript
> function createServerConfig(
> serverUrl: string,
> tokens: any,
> clientId?: string,
> ): HttpServerConfig {
> return {
> url: serverUrl,
> refreshToken: tokens.refresh_token,
> clientId: clientId,
> };
> }
> ```
>
> This would cause `MCPClientManager.connectViaHttp()` (line ~1105) to create a `RefreshTokenOAuthProvider` and pass it as `authProvider` to the transport, enabling the automatic 401 → refresh → retry flow that the SDK already supports and tests (`sdk/tests/refresh-token-auth.test.ts`).

Contributor guide

Open the contributing guide

Research direction

Start in mcpjam-inspector/client/src/lib/oauth/mcp-oauth.ts and trace createServerConfig() into MCPClientManager.connectViaHttp(). Compare the configuration with sdk/tests/refresh-token-auth.test.ts, then reproduce an expired-token tool call and verify that both Inspector tabs recover through refresh and retry rather than failing permanently.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, authentication
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.