@@ -7,8 +7,11 @@ import { testEffect } from "../lib/effect"
77
88// Per-client state for controlling mock behavior
99interface MockClientState {
10+ capabilities : { tools ?: object ; prompts ?: object ; resources ?: object }
1011 tools : Array < { name : string ; description ?: string ; inputSchema : object ; outputSchema ?: object } >
1112 listToolsCalls : number
13+ listPromptsCalls : number
14+ listResourcesCalls : number
1215 requestCalls : number
1316 listToolsShouldFail : boolean
1417 listToolsError : string
@@ -35,8 +38,11 @@ function getOrCreateClientState(name?: string): MockClientState {
3538 let state = clientStates . get ( key )
3639 if ( ! state ) {
3740 state = {
41+ capabilities : { tools : { } , prompts : { } , resources : { } } ,
3842 tools : [ { name : "test_tool" , description : "A test tool" , inputSchema : { type : "object" , properties : { } } } ] ,
3943 listToolsCalls : 0 ,
44+ listPromptsCalls : 0 ,
45+ listResourcesCalls : 0 ,
4046 requestCalls : 0 ,
4147 listToolsShouldFail : false ,
4248 listToolsError : "listTools failed" ,
@@ -133,6 +139,10 @@ void mock.module("@modelcontextprotocol/sdk/client/index.js", () => ({
133139 this . _state ?. notificationHandlers . set ( schema , handler )
134140 }
135141
142+ getServerCapabilities ( ) {
143+ return this . _state ?. capabilities
144+ }
145+
136146 async listTools ( ) {
137147 if ( this . _state ) this . _state . listToolsCalls ++
138148 if ( this . _state ?. listToolsShouldFail ) {
@@ -148,13 +158,15 @@ void mock.module("@modelcontextprotocol/sdk/client/index.js", () => ({
148158 }
149159
150160 async listPrompts ( ) {
161+ if ( this . _state ) this . _state . listPromptsCalls ++
151162 if ( this . _state ?. listPromptsShouldFail ) {
152163 throw new Error ( "listPrompts failed" )
153164 }
154165 return { prompts : this . _state ?. prompts ?? [ ] }
155166 }
156167
157168 async listResources ( ) {
169+ if ( this . _state ) this . _state . listResourcesCalls ++
158170 if ( this . _state ?. listResourcesShouldFail ) {
159171 throw new Error ( "listResources failed" )
160172 }
@@ -598,6 +610,84 @@ it.instance(
598610 } ,
599611)
600612
613+ it . instance (
614+ "resource-only servers connect without listing tools" ,
615+ ( ) =>
616+ MCP . Service . use ( ( mcp : MCPNS . Interface ) =>
617+ Effect . gen ( function * ( ) {
618+ lastCreatedClientName = "resource-only-server"
619+ const serverState = getOrCreateClientState ( "resource-only-server" )
620+ serverState . capabilities = { resources : { } }
621+ serverState . resources = [ { name : "docs" , uri : "docs://readme" } ]
622+
623+ const result = yield * mcp . add ( "resource-only-server" , {
624+ type : "local" ,
625+ command : [ "echo" , "test" ] ,
626+ } )
627+
628+ expect ( statusName ( result . status , "resource-only-server" ) ) . toBe ( "connected" )
629+ expect ( serverState . listToolsCalls ) . toBe ( 0 )
630+ expect ( Object . keys ( yield * mcp . tools ( ) ) ) . toHaveLength ( 0 )
631+ expect ( Object . keys ( yield * mcp . resources ( ) ) ) . toEqual ( [ "resource-only-server:docs" ] )
632+ expect ( serverState . listResourcesCalls ) . toBe ( 1 )
633+ expect ( serverState . listPromptsCalls ) . toBe ( 0 )
634+ } ) ,
635+ ) ,
636+ { config : { mcp : { } } } ,
637+ )
638+
639+ it . instance (
640+ "prompt-only servers connect without listing tools" ,
641+ ( ) =>
642+ MCP . Service . use ( ( mcp : MCPNS . Interface ) =>
643+ Effect . gen ( function * ( ) {
644+ lastCreatedClientName = "prompt-only-server"
645+ const serverState = getOrCreateClientState ( "prompt-only-server" )
646+ serverState . capabilities = { prompts : { } }
647+ serverState . prompts = [ { name : "review" } ]
648+
649+ const result = yield * mcp . add ( "prompt-only-server" , {
650+ type : "local" ,
651+ command : [ "echo" , "test" ] ,
652+ } )
653+
654+ expect ( statusName ( result . status , "prompt-only-server" ) ) . toBe ( "connected" )
655+ expect ( serverState . listToolsCalls ) . toBe ( 0 )
656+ expect ( Object . keys ( yield * mcp . tools ( ) ) ) . toHaveLength ( 0 )
657+ expect ( Object . keys ( yield * mcp . prompts ( ) ) ) . toEqual ( [ "prompt-only-server:review" ] )
658+ expect ( serverState . listPromptsCalls ) . toBe ( 1 )
659+ expect ( serverState . listResourcesCalls ) . toBe ( 0 )
660+ } ) ,
661+ ) ,
662+ { config : { mcp : { } } } ,
663+ )
664+
665+ it . instance (
666+ "tools-only servers skip optional prompt and resource discovery" ,
667+ ( ) =>
668+ MCP . Service . use ( ( mcp : MCPNS . Interface ) =>
669+ Effect . gen ( function * ( ) {
670+ lastCreatedClientName = "tools-only-server"
671+ const serverState = getOrCreateClientState ( "tools-only-server" )
672+ serverState . capabilities = { tools : { } }
673+
674+ const result = yield * mcp . add ( "tools-only-server" , {
675+ type : "local" ,
676+ command : [ "echo" , "test" ] ,
677+ } )
678+
679+ expect ( statusName ( result . status , "tools-only-server" ) ) . toBe ( "connected" )
680+ expect ( serverState . listToolsCalls ) . toBe ( 1 )
681+ expect ( Object . keys ( yield * mcp . tools ( ) ) ) . toEqual ( [ "tools-only-server_test_tool" ] )
682+ expect ( yield * mcp . prompts ( ) ) . toEqual ( { } )
683+ expect ( yield * mcp . resources ( ) ) . toEqual ( { } )
684+ expect ( serverState . listPromptsCalls ) . toBe ( 0 )
685+ expect ( serverState . listResourcesCalls ) . toBe ( 0 )
686+ } ) ,
687+ ) ,
688+ { config : { mcp : { } } } ,
689+ )
690+
601691it . instance (
602692 "prompts() skips disconnected servers" ,
603693 ( ) =>
0 commit comments