@@ -27,6 +27,7 @@ import {
2727 UnauthorizedClientError
2828} from '../server/auth/errors.js' ;
2929import { FetchLike } from '../shared/transport.js' ;
30+ import { UserAgentProvider } from '../shared/userAgent.js' ;
3031
3132/**
3233 * Implements an end-to-end OAuth client to be used with one MCP server.
@@ -296,6 +297,7 @@ export async function auth(
296297 scope ?: string ;
297298 resourceMetadataUrl ?: URL ;
298299 fetchFn ?: FetchLike ;
300+ userAgentProvider : UserAgentProvider ;
299301 }
300302) : Promise < AuthResult > {
301303 try {
@@ -322,19 +324,21 @@ async function authInternal(
322324 authorizationCode,
323325 scope,
324326 resourceMetadataUrl,
325- fetchFn
327+ fetchFn,
328+ userAgentProvider
326329 } : {
327330 serverUrl : string | URL ;
328331 authorizationCode ?: string ;
329332 scope ?: string ;
330333 resourceMetadataUrl ?: URL ;
331334 fetchFn ?: FetchLike ;
335+ userAgentProvider : UserAgentProvider ;
332336 }
333337) : Promise < AuthResult > {
334338 let resourceMetadata : OAuthProtectedResourceMetadata | undefined ;
335339 let authorizationServerUrl : string | URL | undefined ;
336340 try {
337- resourceMetadata = await discoverOAuthProtectedResourceMetadata ( serverUrl , { resourceMetadataUrl } , fetchFn ) ;
341+ resourceMetadata = await discoverOAuthProtectedResourceMetadata ( serverUrl , userAgentProvider , { resourceMetadataUrl } , fetchFn ) ;
338342 if ( resourceMetadata . authorization_servers && resourceMetadata . authorization_servers . length > 0 ) {
339343 authorizationServerUrl = resourceMetadata . authorization_servers [ 0 ] ;
340344 }
@@ -352,7 +356,7 @@ async function authInternal(
352356
353357 const resource : URL | undefined = await selectResourceURL ( serverUrl , provider , resourceMetadata ) ;
354358
355- const metadata = await discoverAuthorizationServerMetadata ( authorizationServerUrl , {
359+ const metadata = await discoverAuthorizationServerMetadata ( authorizationServerUrl , userAgentProvider , {
356360 fetchFn
357361 } ) ;
358362
@@ -370,6 +374,7 @@ async function authInternal(
370374 const fullInformation = await registerClient ( authorizationServerUrl , {
371375 metadata,
372376 clientMetadata : provider . clientMetadata ,
377+ userAgentProvider,
373378 fetchFn
374379 } ) ;
375380
@@ -388,7 +393,8 @@ async function authInternal(
388393 redirectUri : provider . redirectUrl ,
389394 resource,
390395 addClientAuthentication : provider . addClientAuthentication ,
391- fetchFn : fetchFn
396+ fetchFn : fetchFn ,
397+ userAgentProvider
392398 } ) ;
393399
394400 await provider . saveTokens ( tokens ) ;
@@ -407,7 +413,8 @@ async function authInternal(
407413 refreshToken : tokens . refresh_token ,
408414 resource,
409415 addClientAuthentication : provider . addClientAuthentication ,
410- fetchFn
416+ fetchFn,
417+ userAgentProvider
411418 } ) ;
412419
413420 await provider . saveTokens ( newTokens ) ;
@@ -500,10 +507,11 @@ export function extractResourceMetadataUrl(res: Response): URL | undefined {
500507 */
501508export async function discoverOAuthProtectedResourceMetadata (
502509 serverUrl : string | URL ,
510+ userAgentProvider : UserAgentProvider ,
503511 opts ?: { protocolVersion ?: string ; resourceMetadataUrl ?: string | URL } ,
504512 fetchFn : FetchLike = fetch
505513) : Promise < OAuthProtectedResourceMetadata > {
506- const response = await discoverMetadataWithFallback ( serverUrl , 'oauth-protected-resource' , fetchFn , {
514+ const response = await discoverMetadataWithFallback ( serverUrl , 'oauth-protected-resource' , userAgentProvider , fetchFn , {
507515 protocolVersion : opts ?. protocolVersion ,
508516 metadataUrl : opts ?. resourceMetadataUrl
509517 } ) ;
@@ -557,9 +565,15 @@ function buildWellKnownPath(
557565/**
558566 * Tries to discover OAuth metadata at a specific URL
559567 */
560- async function tryMetadataDiscovery ( url : URL , protocolVersion : string , fetchFn : FetchLike = fetch ) : Promise < Response | undefined > {
568+ async function tryMetadataDiscovery (
569+ url : URL ,
570+ protocolVersion : string ,
571+ userAgentProvider : UserAgentProvider ,
572+ fetchFn : FetchLike = fetch
573+ ) : Promise < Response | undefined > {
561574 const headers = {
562- 'MCP-Protocol-Version' : protocolVersion
575+ 'MCP-Protocol-Version' : protocolVersion ,
576+ 'User-Agent' : await userAgentProvider ( )
563577 } ;
564578 return await fetchWithCorsRetry ( url , headers , fetchFn ) ;
565579}
@@ -577,6 +591,7 @@ function shouldAttemptFallback(response: Response | undefined, pathname: string)
577591async function discoverMetadataWithFallback (
578592 serverUrl : string | URL ,
579593 wellKnownType : 'oauth-authorization-server' | 'oauth-protected-resource' ,
594+ userAgentProvider : UserAgentProvider ,
580595 fetchFn : FetchLike ,
581596 opts ?: { protocolVersion ?: string ; metadataUrl ?: string | URL ; metadataServerUrl ?: string | URL }
582597) : Promise < Response | undefined > {
@@ -593,12 +608,12 @@ async function discoverMetadataWithFallback(
593608 url . search = issuer . search ;
594609 }
595610
596- let response = await tryMetadataDiscovery ( url , protocolVersion , fetchFn ) ;
611+ let response = await tryMetadataDiscovery ( url , protocolVersion , userAgentProvider , fetchFn ) ;
597612
598613 // If path-aware discovery fails with 404 and we're not already at root, try fallback to root discovery
599614 if ( ! opts ?. metadataUrl && shouldAttemptFallback ( response , issuer . pathname ) ) {
600615 const rootUrl = new URL ( `/.well-known/${ wellKnownType } ` , issuer ) ;
601- response = await tryMetadataDiscovery ( rootUrl , protocolVersion , fetchFn ) ;
616+ response = await tryMetadataDiscovery ( rootUrl , protocolVersion , userAgentProvider , fetchFn ) ;
602617 }
603618
604619 return response ;
@@ -614,6 +629,7 @@ async function discoverMetadataWithFallback(
614629 */
615630export async function discoverOAuthMetadata (
616631 issuer : string | URL ,
632+ userAgentProvider : UserAgentProvider ,
617633 {
618634 authorizationServerUrl,
619635 protocolVersion
@@ -634,7 +650,7 @@ export async function discoverOAuthMetadata(
634650 }
635651 protocolVersion ??= LATEST_PROTOCOL_VERSION ;
636652
637- const response = await discoverMetadataWithFallback ( authorizationServerUrl , 'oauth-authorization-server' , fetchFn , {
653+ const response = await discoverMetadataWithFallback ( authorizationServerUrl , 'oauth-authorization-server' , userAgentProvider , fetchFn , {
638654 protocolVersion,
639655 metadataServerUrl : authorizationServerUrl
640656 } ) ;
@@ -730,6 +746,7 @@ export function buildDiscoveryUrls(authorizationServerUrl: string | URL): { url:
730746 */
731747export async function discoverAuthorizationServerMetadata (
732748 authorizationServerUrl : string | URL ,
749+ userAgentProvider : UserAgentProvider ,
733750 {
734751 fetchFn = fetch ,
735752 protocolVersion = LATEST_PROTOCOL_VERSION
@@ -740,7 +757,8 @@ export async function discoverAuthorizationServerMetadata(
740757) : Promise < AuthorizationServerMetadata | undefined > {
741758 const headers = {
742759 'MCP-Protocol-Version' : protocolVersion ,
743- Accept : 'application/json'
760+ Accept : 'application/json' ,
761+ 'User-Agent' : await userAgentProvider ( )
744762 } ;
745763
746764 // Get the list of URLs to try
@@ -873,7 +891,8 @@ export async function exchangeAuthorization(
873891 redirectUri,
874892 resource,
875893 addClientAuthentication,
876- fetchFn
894+ fetchFn,
895+ userAgentProvider
877896 } : {
878897 metadata ?: AuthorizationServerMetadata ;
879898 clientInformation : OAuthClientInformation ;
@@ -883,6 +902,7 @@ export async function exchangeAuthorization(
883902 resource ?: URL ;
884903 addClientAuthentication ?: OAuthClientProvider [ 'addClientAuthentication' ] ;
885904 fetchFn ?: FetchLike ;
905+ userAgentProvider : UserAgentProvider ;
886906 }
887907) : Promise < OAuthTokens > {
888908 const grantType = 'authorization_code' ;
@@ -896,7 +916,8 @@ export async function exchangeAuthorization(
896916 // Exchange code for tokens
897917 const headers = new Headers ( {
898918 'Content-Type' : 'application/x-www-form-urlencoded' ,
899- Accept : 'application/json'
919+ Accept : 'application/json' ,
920+ 'User-Agent' : await userAgentProvider ( )
900921 } ) ;
901922 const params = new URLSearchParams ( {
902923 grant_type : grantType ,
@@ -952,14 +973,16 @@ export async function refreshAuthorization(
952973 refreshToken,
953974 resource,
954975 addClientAuthentication,
955- fetchFn
976+ fetchFn,
977+ userAgentProvider
956978 } : {
957979 metadata ?: AuthorizationServerMetadata ;
958980 clientInformation : OAuthClientInformation ;
959981 refreshToken : string ;
960982 resource ?: URL ;
961983 addClientAuthentication ?: OAuthClientProvider [ 'addClientAuthentication' ] ;
962984 fetchFn ?: FetchLike ;
985+ userAgentProvider : UserAgentProvider ;
963986 }
964987) : Promise < OAuthTokens > {
965988 const grantType = 'refresh_token' ;
@@ -977,7 +1000,8 @@ export async function refreshAuthorization(
9771000
9781001 // Exchange refresh token
9791002 const headers = new Headers ( {
980- 'Content-Type' : 'application/x-www-form-urlencoded'
1003+ 'Content-Type' : 'application/x-www-form-urlencoded' ,
1004+ 'User-Agent' : await userAgentProvider ( )
9811005 } ) ;
9821006 const params = new URLSearchParams ( {
9831007 grant_type : grantType ,
@@ -1018,11 +1042,13 @@ export async function registerClient(
10181042 {
10191043 metadata,
10201044 clientMetadata,
1021- fetchFn
1045+ fetchFn,
1046+ userAgentProvider
10221047 } : {
10231048 metadata ?: AuthorizationServerMetadata ;
10241049 clientMetadata : OAuthClientMetadata ;
10251050 fetchFn ?: FetchLike ;
1051+ userAgentProvider : UserAgentProvider ;
10261052 }
10271053) : Promise < OAuthClientInformationFull > {
10281054 let registrationUrl : URL ;
@@ -1040,7 +1066,8 @@ export async function registerClient(
10401066 const response = await ( fetchFn ?? fetch ) ( registrationUrl , {
10411067 method : 'POST' ,
10421068 headers : {
1043- 'Content-Type' : 'application/json'
1069+ 'Content-Type' : 'application/json' ,
1070+ 'User-Agent' : await userAgentProvider ( )
10441071 } ,
10451072 body : JSON . stringify ( clientMetadata )
10461073 } ) ;
0 commit comments