@@ -10,7 +10,7 @@ import { Disposable, DisposableMap, DisposableStore, IDisposable, toDisposable }
10
10
import { SSEParser } from '../../../base/common/sseParser.js' ;
11
11
import { ExtensionIdentifier , IExtensionDescription } from '../../../platform/extensions/common/extensions.js' ;
12
12
import { createDecorator } from '../../../platform/instantiation/common/instantiation.js' ;
13
- import { LogLevel } from '../../../platform/log/common/log.js' ;
13
+ import { canLog , ILogService , LogLevel } from '../../../platform/log/common/log.js' ;
14
14
import { StorageScope } from '../../../platform/storage/common/storage.js' ;
15
15
import { extensionPrefixedIdentifier , McpCollectionDefinition , McpConnectionState , McpServerDefinition , McpServerLaunch , McpServerTransportHTTP , McpServerTransportType } from '../../contrib/mcp/common/mcpTypes.js' ;
16
16
import { ExtHostMcpShape , MainContext , MainThreadMcpShape } from './extHost.protocol.js' ;
@@ -40,6 +40,7 @@ export class ExtHostMcpService extends Disposable implements IExtHostMpcService
40
40
41
41
constructor (
42
42
@IExtHostRpcService extHostRpc : IExtHostRpcService ,
43
+ @ILogService private readonly _logService : ILogService ,
43
44
@IExtHostInitDataService private readonly _extHostInitData : IExtHostInitDataService
44
45
) {
45
46
super ( ) ;
@@ -52,7 +53,7 @@ export class ExtHostMcpService extends Disposable implements IExtHostMpcService
52
53
53
54
protected _startMcp ( id : number , launch : McpServerLaunch ) : void {
54
55
if ( launch . type === McpServerTransportType . HTTP ) {
55
- this . _sseEventSources . set ( id , new McpHTTPHandle ( id , launch , this . _proxy ) ) ;
56
+ this . _sseEventSources . set ( id , new McpHTTPHandle ( id , launch , this . _proxy , this . _logService ) ) ;
56
57
return ;
57
58
}
58
59
@@ -197,7 +198,8 @@ class McpHTTPHandle extends Disposable {
197
198
constructor (
198
199
private readonly _id : number ,
199
200
private readonly _launch : McpServerTransportHTTP ,
200
- private readonly _proxy : MainThreadMcpShape
201
+ private readonly _proxy : MainThreadMcpShape ,
202
+ private readonly _logService : ILogService ,
201
203
) {
202
204
super ( ) ;
203
205
@@ -246,7 +248,6 @@ class McpHTTPHandle extends Disposable {
246
248
this . _launch . uri . toString ( true ) ,
247
249
{
248
250
method : 'POST' ,
249
- signal : this . _abortCtrl . signal ,
250
251
headers,
251
252
body : asBytes ,
252
253
} ,
@@ -280,7 +281,7 @@ class McpHTTPHandle extends Disposable {
280
281
281
282
this . _proxy . $onDidChangeState ( this . _id , {
282
283
state : McpConnectionState . Kind . Error ,
283
- message : `${ res . status } status sending message to ${ this . _launch . uri } : ${ await this . _getErrText ( res ) } ` + retryWithSessionId ? `; will retry with new session ID` : '' ,
284
+ message : `${ res . status } status sending message to ${ this . _launch . uri } : ${ await this . _getErrText ( res ) } ` + ( retryWithSessionId ? `; will retry with new session ID` : '' ) ,
284
285
shouldRetry : retryWithSessionId ,
285
286
} ) ;
286
287
return ;
@@ -374,9 +375,8 @@ class McpHTTPHandle extends Disposable {
374
375
...Object . fromEntries ( this . _launch . headers )
375
376
} ;
376
377
}
377
- const resourceMetadataResponse = await fetch ( resourceMetadata , {
378
+ const resourceMetadataResponse = await this . _fetch ( resourceMetadata , {
378
379
method : 'GET' ,
379
- signal : this . _abortCtrl . signal ,
380
380
headers : {
381
381
...additionalHeaders ,
382
382
'Accept' : 'application/json' ,
@@ -401,9 +401,8 @@ class McpHTTPHandle extends Disposable {
401
401
const authorizationServerUrl = new URL ( authorizationServer ) ;
402
402
const extraPath = authorizationServerUrl . pathname === '/' ? '' : authorizationServerUrl . pathname ;
403
403
const pathToFetch = new URL ( AUTH_SERVER_METADATA_DISCOVERY_PATH , authorizationServer ) . toString ( ) + extraPath ;
404
- let authServerMetadataResponse = await fetch ( pathToFetch , {
404
+ let authServerMetadataResponse = await this . _fetch ( pathToFetch , {
405
405
method : 'GET' ,
406
- signal : this . _abortCtrl . signal ,
407
406
headers : {
408
407
...addtionalHeaders ,
409
408
'Accept' : 'application/json' ,
@@ -414,11 +413,10 @@ class McpHTTPHandle extends Disposable {
414
413
// Try fetching the other discovery URL. For the openid metadata discovery
415
414
// path, we _ADD_ the well known path after the existing path.
416
415
// https://datatracker.ietf.org/doc/html/rfc8414#section-3
417
- authServerMetadataResponse = await fetch (
416
+ authServerMetadataResponse = await this . _fetch (
418
417
URI . joinPath ( URI . parse ( authorizationServer ) , '.well-known' , 'openid-configuration' ) . toString ( true ) ,
419
418
{
420
419
method : 'GET' ,
421
- signal : this . _abortCtrl . signal ,
422
420
headers : {
423
421
...addtionalHeaders ,
424
422
'Accept' : 'application/json' ,
@@ -505,7 +503,6 @@ class McpHTTPHandle extends Disposable {
505
503
this . _launch . uri . toString ( true ) ,
506
504
{
507
505
method : 'GET' ,
508
- signal : this . _abortCtrl . signal ,
509
506
headers,
510
507
} ,
511
508
headers
@@ -557,7 +554,6 @@ class McpHTTPHandle extends Disposable {
557
554
this . _launch . uri . toString ( true ) ,
558
555
{
559
556
method : 'GET' ,
560
- signal : this . _abortCtrl . signal ,
561
557
headers,
562
558
} ,
563
559
headers
@@ -599,9 +595,8 @@ class McpHTTPHandle extends Disposable {
599
595
'Content-Length' : String ( asBytes . length ) ,
600
596
} ;
601
597
await this . _addAuthHeader ( headers ) ;
602
- const res = await fetch ( url , {
598
+ const res = await this . _fetch ( url , {
603
599
method : 'POST' ,
604
- signal : this . _abortCtrl . signal ,
605
600
headers,
606
601
body : asBytes ,
607
602
} ) ;
@@ -670,8 +665,8 @@ class McpHTTPHandle extends Disposable {
670
665
* If the initial request returns 401 and we don't have auth metadata,
671
666
* it will populate the auth metadata and retry once.
672
667
*/
673
- private async _fetchWithAuthRetry ( url : string , init : RequestInit , headers : Record < string , string > ) : Promise < Response > {
674
- const doFetch = ( ) => fetch ( url , init ) ;
668
+ private async _fetchWithAuthRetry ( url : string , init : MinimalRequestInit , headers : Record < string , string > ) : Promise < Response > {
669
+ const doFetch = ( ) => this . _fetch ( url , init ) ;
675
670
676
671
let res = await doFetch ( ) ;
677
672
if ( res . status === 401 ) {
@@ -687,6 +682,40 @@ class McpHTTPHandle extends Disposable {
687
682
}
688
683
return res ;
689
684
}
685
+
686
+ private async _fetch ( url : string , init : MinimalRequestInit ) : Promise < Response > {
687
+ if ( canLog ( this . _logService . getLevel ( ) , LogLevel . Trace ) ) {
688
+ const traceObj : any = { ...init , headers : { ...init . headers } } ;
689
+ if ( traceObj . body ) {
690
+ traceObj . body = new TextDecoder ( ) . decode ( traceObj . body ) ;
691
+ }
692
+ if ( traceObj . headers ?. Authorization ) {
693
+ traceObj . headers . Authorization = '***' ; // don't log the auth header
694
+ }
695
+ this . _log ( LogLevel . Trace , `Fetching ${ url } with options: ${ JSON . stringify ( traceObj ) } ` ) ;
696
+ }
697
+ const res = await fetch ( url , {
698
+ ...init ,
699
+ signal : this . _abortCtrl . signal ,
700
+ } ) ;
701
+
702
+ if ( canLog ( this . _logService . getLevel ( ) , LogLevel . Trace ) ) {
703
+ const headers : Record < string , string > = { } ;
704
+ res . headers . forEach ( ( value , key ) => { headers [ key ] = value ; } ) ;
705
+ this . _log ( LogLevel . Trace , `Fetched ${ url } : ${ JSON . stringify ( {
706
+ status : res . status ,
707
+ headers : headers ,
708
+ } ) } `) ;
709
+ }
710
+
711
+ return res ;
712
+ }
713
+ }
714
+
715
+ interface MinimalRequestInit {
716
+ method : string ;
717
+ headers : Record < string , string > ;
718
+ body ?: Uint8Array < ArrayBuffer > ;
690
719
}
691
720
692
721
function isJSON ( str : string ) : boolean {
0 commit comments