@@ -23,6 +23,9 @@ import {
2323 type ResolvedStdioConnection ,
2424 type ResolvedStreamableHTTPConnection ,
2525 type CustomHTTPTransportOptions ,
26+ type MCPResource ,
27+ type MCPResourceTemplate ,
28+ type MCPResourceContent ,
2629 clientConfigSchema ,
2730 connectionSchema ,
2831 type LoadMcpToolsOptions ,
@@ -391,6 +394,211 @@ export class MultiServerMCPClient {
391394 } ) ;
392395 }
393396
397+ /**
398+ * List resources from specified servers.
399+ *
400+ * @param servers - Optional array of server names to filter resources by.
401+ * If not provided, returns resources from all servers.
402+ * @param options - Optional connection options for the resource listing, e.g. custom auth provider or headers.
403+ * @returns A map of server names to their resources
404+ *
405+ * @example
406+ * ```ts
407+ * // List resources from all servers
408+ * const resources = await client.listResources();
409+ * ```
410+ *
411+ * @example
412+ * ```ts
413+ * // List resources from specific servers
414+ * const resources = await client.listResources("server1", "server2");
415+ * ```
416+ */
417+ async listResources (
418+ ...servers : string [ ]
419+ ) : Promise < Record < string , MCPResource [ ] > > ;
420+ async listResources (
421+ servers : string [ ] ,
422+ options ?: CustomHTTPTransportOptions
423+ ) : Promise < Record < string , MCPResource [ ] > > ;
424+ async listResources (
425+ ...args : unknown [ ]
426+ ) : Promise < Record < string , MCPResource [ ] > > {
427+ let servers : string [ ] ;
428+ let options : CustomHTTPTransportOptions | undefined ;
429+
430+ if ( args . length === 0 || args . every ( ( arg ) => typeof arg === "string" ) ) {
431+ servers = args as string [ ] ;
432+ await this . initializeConnections ( ) ;
433+ } else {
434+ [ servers , options ] = args as [
435+ string [ ] ,
436+ CustomHTTPTransportOptions | undefined ,
437+ ] ;
438+ await this . initializeConnections ( options ) ;
439+ }
440+
441+ const targetServers =
442+ servers . length > 0 ? servers : Object . keys ( this . #config. mcpServers ) ;
443+
444+ const result : Record < string , MCPResource [ ] > = { } ;
445+
446+ for ( const serverName of targetServers ) {
447+ const client = await this . getClient ( serverName , options ) ;
448+ if ( ! client ) {
449+ debugLog ( `WARN: Server "${ serverName } " not found or not connected` ) ;
450+ continue ;
451+ }
452+
453+ try {
454+ const resourcesList = await client . listResources ( ) ;
455+ result [ serverName ] = resourcesList . resources . map ( ( resource ) => ( {
456+ uri : resource . uri ,
457+ name : resource . title ?? resource . name ,
458+ description : resource . description ,
459+ mimeType : resource . mimeType ,
460+ } ) ) ;
461+ debugLog (
462+ `INFO: Listed ${ result [ serverName ] . length } resources from server "${ serverName } "`
463+ ) ;
464+ } catch ( error ) {
465+ debugLog (
466+ `ERROR: Failed to list resources from server "${ serverName } ": ${ error } `
467+ ) ;
468+ result [ serverName ] = [ ] ;
469+ }
470+ }
471+
472+ return result ;
473+ }
474+
475+ /**
476+ * List resource templates from specified servers.
477+ *
478+ * Resource templates are used for dynamic resources with parameterized URIs.
479+ *
480+ * @param servers - Optional array of server names to filter resource templates by.
481+ * If not provided, returns resource templates from all servers.
482+ * @param options - Optional connection options for the resource template listing, e.g. custom auth provider or headers.
483+ * @returns A map of server names to their resource templates
484+ *
485+ * @example
486+ * ```ts
487+ * // List resource templates from all servers
488+ * const templates = await client.listResourceTemplates();
489+ * ```
490+ *
491+ * @example
492+ * ```ts
493+ * // List resource templates from specific servers
494+ * const templates = await client.listResourceTemplates("server1", "server2");
495+ * ```
496+ */
497+ async listResourceTemplates (
498+ ...servers : string [ ]
499+ ) : Promise < Record < string , MCPResourceTemplate [ ] > > ;
500+ async listResourceTemplates (
501+ servers : string [ ] ,
502+ options ?: CustomHTTPTransportOptions
503+ ) : Promise < Record < string , MCPResourceTemplate [ ] > > ;
504+ async listResourceTemplates (
505+ ...args : unknown [ ]
506+ ) : Promise < Record < string , MCPResourceTemplate [ ] > > {
507+ let servers : string [ ] ;
508+ let options : CustomHTTPTransportOptions | undefined ;
509+
510+ if ( args . length === 0 || args . every ( ( arg ) => typeof arg === "string" ) ) {
511+ servers = args as string [ ] ;
512+ await this . initializeConnections ( ) ;
513+ } else {
514+ [ servers , options ] = args as [
515+ string [ ] ,
516+ CustomHTTPTransportOptions | undefined ,
517+ ] ;
518+ await this . initializeConnections ( options ) ;
519+ }
520+
521+ const targetServers =
522+ servers . length > 0 ? servers : Object . keys ( this . #config. mcpServers ) ;
523+
524+ const result : Record < string , MCPResourceTemplate [ ] > = { } ;
525+
526+ for ( const serverName of targetServers ) {
527+ const client = await this . getClient ( serverName , options ) ;
528+ if ( ! client ) {
529+ debugLog ( `WARN: Server "${ serverName } " not found or not connected` ) ;
530+ continue ;
531+ }
532+
533+ try {
534+ const templatesList = await client . listResourceTemplates ( ) ;
535+ result [ serverName ] = templatesList . resourceTemplates . map (
536+ ( template ) => ( {
537+ uriTemplate : template . uriTemplate ,
538+ name : template . title ?? template . name ,
539+ description : template . description ,
540+ mimeType : template . mimeType ,
541+ } )
542+ ) ;
543+ debugLog (
544+ `INFO: Listed ${ result [ serverName ] . length } resource templates from server "${ serverName } "`
545+ ) ;
546+ } catch ( error ) {
547+ debugLog (
548+ `ERROR: Failed to list resource templates from server "${ serverName } ": ${ error } `
549+ ) ;
550+ result [ serverName ] = [ ] ;
551+ }
552+ }
553+
554+ return result ;
555+ }
556+
557+ /**
558+ * Read a resource from a specific server.
559+ *
560+ * @param serverName - The name of the server to read the resource from
561+ * @param uri - The URI of the resource to read
562+ * @param options - Optional connection options for reading the resource, e.g. custom auth provider or headers.
563+ * @returns The resource contents
564+ *
565+ * @example
566+ * ```ts
567+ * const content = await client.readResource("server1", "file://path/to/resource");
568+ * ```
569+ */
570+ async readResource (
571+ serverName : string ,
572+ uri : string ,
573+ options ?: CustomHTTPTransportOptions
574+ ) : Promise < MCPResourceContent [ ] > {
575+ await this . initializeConnections ( options ) ;
576+
577+ const client = await this . getClient ( serverName , options ) ;
578+ if ( ! client ) {
579+ throw new MCPClientError (
580+ `Server "${ serverName } " not found or not connected` ,
581+ serverName
582+ ) ;
583+ }
584+
585+ try {
586+ debugLog ( `INFO: Reading resource "${ uri } " from server "${ serverName } "` ) ;
587+ const result = await client . readResource ( { uri } ) ;
588+ return result . contents . map ( ( content ) => ( {
589+ uri : content . uri ,
590+ mimeType : content . mimeType ,
591+ text : content . text as string | undefined ,
592+ blob : content . blob as string | undefined ,
593+ } ) ) ;
594+ } catch ( error ) {
595+ throw new MCPClientError (
596+ `Failed to read resource "${ uri } " from server "${ serverName } ": ${ error } ` ,
597+ serverName
598+ ) ;
599+ }
600+ }
601+
394602 /**
395603 * Close all connections.
396604 */
0 commit comments