MCPcopy Index your code
hub / github.com/codeaashu/claude-code / createClaudeAiProxyFetch

Function createClaudeAiProxyFetch

src/services/mcp/client.ts:372–422  ·  view source on GitHub ↗
(innerFetch: FetchLike)

Source from the content-addressed store, hash-verified

370 * 15-min needs-auth cache.
371 */
372export function createClaudeAiProxyFetch(innerFetch: FetchLike): FetchLike {
373 return async (url, init) => {
374 const doRequest = async () => {
375 await checkAndRefreshOAuthTokenIfNeeded()
376 const currentTokens = getClaudeAIOAuthTokens()
377 if (!currentTokens) {
378 throw new Error('No claude.ai OAuth token available')
379 }
380 // eslint-disable-next-line eslint-plugin-n/no-unsupported-features/node-builtins
381 const headers = new Headers(init?.headers)
382 headers.set('Authorization', `Bearer ${currentTokens.accessToken}`)
383 const response = await innerFetch(url, { ...init, headers })
384 // Return the exact token that was sent. Reading getClaudeAIOAuthTokens()
385 // again after the request is wrong under concurrent 401s: another
386 // connector's handleOAuth401Error clears the memoize cache, so we'd read
387 // the NEW token from keychain, pass it to handleOAuth401Error, which
388 // finds same-as-keychain → returns false → skips retry. Same pattern as
389 // bridgeApi.ts withOAuthRetry (token passed as fn param).
390 return { response, sentToken: currentTokens.accessToken }
391 }
392
393 const { response, sentToken } = await doRequest()
394 if (response.status !== 401) {
395 return response
396 }
397 // handleOAuth401Error returns true only if the token actually changed
398 // (keychain had a newer one, or force-refresh succeeded). Gate retry on
399 // that — otherwise we double round-trip time for every connector whose
400 // downstream service genuinely needs auth (the common case: 30+ servers
401 // with "MCP server requires authentication but no OAuth token configured").
402 const tokenChanged = await handleOAuth401Error(sentToken).catch(() => false)
403 logEvent('tengu_mcp_claudeai_proxy_401', {
404 tokenChanged:
405 tokenChanged as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
406 })
407 if (!tokenChanged) {
408 // ELOCKED contention: another connector may have won the lockfile and refreshed — check if token changed underneath us
409 const now = getClaudeAIOAuthTokens()?.accessToken
410 if (!now || now === sentToken) {
411 return response
412 }
413 }
414 try {
415 return (await doRequest()).response
416 } catch {
417 // Retry itself failed (network error). Return the original 401 so the
418 // outer handler can classify it.
419 return response
420 }
421 }
422}
423
424// Minimal interface for WebSocket instances passed to mcpWebSocketTransport
425type WsClientLike = {

Callers 1

client.tsFile · 0.85

Calls 3

doRequestFunction · 0.85
handleOAuth401ErrorFunction · 0.85
logEventFunction · 0.85

Tested by

no test coverage detected