Skip to main content

New features

Optimize Session Store DB Operations

Reduces redundant database round-trips across the OAuth connection lifecycle by 40–87%, depending on auth type and scenario. Eleven areas of waste were identified and eliminated:

Single-table session + credentials

mcp_credentials table eliminated. All credential fields (client_information, tokens, code_verifier, client_id, oauth_state) live as nullable columns on mcp_sessions. get({ includeCredentials: true }) is a plain SELECT * with no JOIN. getCredentials(), patchCredentials(), clearAll(), and delete() are all single-operation queries. SessionResult = Session β€” credential fields accessed directly (session.clientId). Cost eliminated per deployment: 1 table, 1 UUID PK + sequence, 1 UNIQUE constraint, 1 FK constraint, 1 trigger, 1 index, and (Supabase) 4 RLS policies.

Challenge-based code verifier (in-memory only)

PKCE code verifier stored in-memory (_cachedCodeVerifier) β€” no DB write on saveCodeVerifier(). redirectToAuthorization() compares SHA-256 challenge in-memory, persists raw verifier to DB only on match for cross-instance callback. codeVerifier() reads in-memory β†’ ALS context β†’ DB fallback (3-tier lookup). codeVerifierChallenge and codeVerifierNonce removed from all backends, types, and migrations β€” no DB columns, no persistence overhead.

authUrl in-memory only

redirectToAuthorization() stores _authUrl in-memory β€” does NOT persist to DB (was waste on every OAuth start, never read back).

PKCE dead-write elimination

Removed all patchCredentials() calls referencing codeVerifierChallenge and codeVerifierNonce from StorageOAuthClientProvider. DB columns code_verifier_challenge and code_verifier_nonce removed from all migrations and backends.

Internal provider overhead eliminated

  • _hasCodeVerifier flag β€” first saveCodeVerifier() sets in-memory flag; subsequent calls return immediately (0 ops)
  • Cached tokens β€” tokens() returns _cachedTokens from constructor (0R); saveTokens() updates both DB and cache
  • hasSession flag β€” MCPOAuthClientOptions.hasSession: true skips sessions.get() existence check when caller already loaded the session
  • Combined fetch β€” get({ includeCredentials: true }) returns credential fields on the Session object directly, eliminating separate getCredentials() call

Remove redundant checkState() in finishAuth()

Before: called checkState() (1–2R), then consumeState() which called checkState() again (1–2R). After: calls consumeState() only β€” it validates internally. Saves 1–2R per finishAuth().

Unified observability

Single sendEvent callback for RPC timing, DB read/write, and client lifecycle events. withDbObservability auto-wraps session store β€” all DB read/write timing flows through sendEvent. handleRequest() emits rpc:start/rpc:end with method, sessionId, and duration. Consumer gets one stream of McpObservabilityEvents covering RPC timing, DB operations, and client lifecycle β€” one callback, no manual wiring.

SSE handler refactor

SSEConnectionManager cleaned up with 5 extracted private helpers (restoreClient, cacheClient, attachClientEvents, requireSession, connectAndDiscover, findExistingSession), 2 event factories, JSDoc on all 14 RPC methods, structured sections, and rpc:start/rpc:end timing observability in the unified dispatch() wrapper.

Tool policy management

ToolPolicyGateway accepts a ToolClient implementing getServerUrl, checkToolAllowed, listAllowedTools, and addAllowedTool for per-session tool allowlists with optional SQL storage.

OAuth popup reconnect

SSEClient gained a reconnect method for transport re-establishment without a fresh OAuth handshake. React ConnectionItem and ConnectionList components expose reconnect controls with streamlined error handling.

allTools in tools_discovered

The tools_discovered event now includes an allTools payload for management UIs. MCPClient implements in-memory caching for tool fetches.

Client credentials flow

MCPClient accepts clientId and clientSecret as connection parameters for OAuth client credentials grant flows.

Session enable/disable

updateSession() toggles enabled on a session. Disabled sessions are excluded from AI tools via MultiSessionClient.fetchActiveSessions() (s.enabled !== false), blocked at SSEConnectionManager.getOrCreateClient() with β€œSession is disabled”, and exposed in listSessions() for UI toggle state. Re-enabling requires no re-authentication. React useMcp hook exposes updateSession(sessionId, enabled).

ServerInfo capture

MCPClient captures serverInfo from this.client.getServerVersion() after connect(). getServerInfo() returns the full Implementation object (name, version, title, icons). getServerName() uses title ?? name ?? config.serverName. mcp-server analytics pipeline passes serverIcons through recordToolCallEvent(), stored as server_icons JSONB in mcp_tool_call_events.

Performance

First-time connection DB round-trips:
Auth typeBeforeAfterSavings
No Auth3R 2W1R 2Wβˆ’2R (66%)
Client Credentials3R 4W1R 3Wβˆ’2R (66%)
OAuth9R 10W5R 8Wβˆ’4R (44%)
Cold-start reconnection (serverless):
Auth typeBeforeAfterSavings
No Auth4R 1W1R 1Wβˆ’3R (75%)
Client Credentials4R 1W1R 1Wβˆ’3R (75%)
OAuth (valid tokens)8R 2W1R 1Wβˆ’7R (87%)
OAuth (expired tokens)8R 2W~3R 2Wβˆ’5R (62%)

Updates

  • MCPClient refactored β€” Configuration consolidated into a single options object. ensureSession replaces initialize for session management.
  • Monorepo restructured β€” SDK moved to packages/sdk/ with npm workspaces. release.yml paths updated. AGENTS.md added at repo root.

Bug fixes

  • Stale client pruning β€” MultiSessionClient prunes stale MCPClient instances to prevent OAuth refresh issues after session expiry (PR #167)
  • Session expiry recovery β€” MCPClient recovers from MCP_SESSION_EXPIRED using withRetry, nulling the OAuth client to trigger a clean reconnect (PR #170)
  • Disconnect logic β€” Streamlined disconnect handling in MCPClient and MultiSessionClient to prevent orphaned connections during session teardown