@@ -3521,12 +3521,12 @@ describe("prompt()", () => {
35213521 ) ;
35223522
35233523 expect ( result . resources ) . toHaveLength ( 2 ) ;
3524-
3524+
35253525 // Resource 1 should have its own metadata
35263526 expect ( result . resources [ 0 ] . name ) . toBe ( "Resource 1" ) ;
35273527 expect ( result . resources [ 0 ] . description ) . toBe ( "Individual resource description" ) ;
35283528 expect ( result . resources [ 0 ] . mimeType ) . toBe ( "text/plain" ) ;
3529-
3529+
35303530 // Resource 2 should inherit template metadata
35313531 expect ( result . resources [ 1 ] . name ) . toBe ( "Resource 2" ) ;
35323532 expect ( result . resources [ 1 ] . description ) . toBe ( "Template description" ) ;
@@ -3592,7 +3592,7 @@ describe("prompt()", () => {
35923592 ) ;
35933593
35943594 expect ( result . resources ) . toHaveLength ( 1 ) ;
3595-
3595+
35963596 // All fields should be from the individual resource, not the template
35973597 expect ( result . resources [ 0 ] . name ) . toBe ( "Overridden Name" ) ;
35983598 expect ( result . resources [ 0 ] . description ) . toBe ( "Overridden description" ) ;
@@ -3698,41 +3698,274 @@ describe("Tool title precedence", () => {
36983698 } ) ;
36993699
37003700 test ( "getDisplayName unit tests for title precedence" , ( ) => {
3701-
3701+
37023702 // Test 1: Only name
37033703 expect ( getDisplayName ( { name : "tool_name" } ) ) . toBe ( "tool_name" ) ;
3704-
3704+
37053705 // Test 2: Name and title - title wins
3706- expect ( getDisplayName ( {
3707- name : "tool_name" ,
3708- title : "Tool Title"
3706+ expect ( getDisplayName ( {
3707+ name : "tool_name" ,
3708+ title : "Tool Title"
37093709 } ) ) . toBe ( "Tool Title" ) ;
3710-
3710+
37113711 // Test 3: Name and annotations.title - annotations.title wins
3712- expect ( getDisplayName ( {
3712+ expect ( getDisplayName ( {
37133713 name : "tool_name" ,
37143714 annotations : { title : "Annotations Title" }
37153715 } ) ) . toBe ( "Annotations Title" ) ;
3716-
3716+
37173717 // Test 4: All three - title wins (correct precedence)
3718- expect ( getDisplayName ( {
3719- name : "tool_name" ,
3718+ expect ( getDisplayName ( {
3719+ name : "tool_name" ,
37203720 title : "Regular Title" ,
37213721 annotations : { title : "Annotations Title" }
37223722 } ) ) . toBe ( "Regular Title" ) ;
3723-
3723+
37243724 // Test 5: Empty title should not be used
3725- expect ( getDisplayName ( {
3726- name : "tool_name" ,
3725+ expect ( getDisplayName ( {
3726+ name : "tool_name" ,
37273727 title : "" ,
37283728 annotations : { title : "Annotations Title" }
37293729 } ) ) . toBe ( "Annotations Title" ) ;
3730-
3730+
37313731 // Test 6: Undefined vs null handling
3732- expect ( getDisplayName ( {
3733- name : "tool_name" ,
3732+ expect ( getDisplayName ( {
3733+ name : "tool_name" ,
37343734 title : undefined ,
37353735 annotations : { title : "Annotations Title" }
37363736 } ) ) . toBe ( "Annotations Title" ) ;
37373737 } ) ;
3738+
3739+ test ( "should support resource template completion with resolved context" , async ( ) => {
3740+ const mcpServer = new McpServer ( {
3741+ name : "test server" ,
3742+ version : "1.0" ,
3743+ } ) ;
3744+
3745+ const client = new Client ( {
3746+ name : "test client" ,
3747+ version : "1.0" ,
3748+ } ) ;
3749+
3750+ mcpServer . resource (
3751+ "test" ,
3752+ new ResourceTemplate ( "github://repos/{owner}/{repo}" , {
3753+ list : undefined ,
3754+ complete : {
3755+ repo : ( value , context ) => {
3756+ if ( context ?. arguments ?. [ "owner" ] === "org1" ) {
3757+ return [ "project1" , "project2" , "project3" ] . filter ( r => r . startsWith ( value ) ) ;
3758+ } else if ( context ?. arguments ?. [ "owner" ] === "org2" ) {
3759+ return [ "repo1" , "repo2" , "repo3" ] . filter ( r => r . startsWith ( value ) ) ;
3760+ }
3761+ return [ ] ;
3762+ } ,
3763+ } ,
3764+ } ) ,
3765+ async ( ) => ( {
3766+ contents : [
3767+ {
3768+ uri : "github://repos/test/test" ,
3769+ text : "Test content" ,
3770+ } ,
3771+ ] ,
3772+ } ) ,
3773+ ) ;
3774+
3775+ const [ clientTransport , serverTransport ] =
3776+ InMemoryTransport . createLinkedPair ( ) ;
3777+
3778+ await Promise . all ( [
3779+ client . connect ( clientTransport ) ,
3780+ mcpServer . server . connect ( serverTransport ) ,
3781+ ] ) ;
3782+
3783+ // Test with microsoft owner
3784+ const result1 = await client . request (
3785+ {
3786+ method : "completion/complete" ,
3787+ params : {
3788+ ref : {
3789+ type : "ref/resource" ,
3790+ uri : "github://repos/{owner}/{repo}" ,
3791+ } ,
3792+ argument : {
3793+ name : "repo" ,
3794+ value : "p" ,
3795+ } ,
3796+ context : {
3797+ arguments : {
3798+ owner : "org1" ,
3799+ } ,
3800+ } ,
3801+ } ,
3802+ } ,
3803+ CompleteResultSchema ,
3804+ ) ;
3805+
3806+ expect ( result1 . completion . values ) . toEqual ( [ "project1" , "project2" , "project3" ] ) ;
3807+ expect ( result1 . completion . total ) . toBe ( 3 ) ;
3808+
3809+ // Test with facebook owner
3810+ const result2 = await client . request (
3811+ {
3812+ method : "completion/complete" ,
3813+ params : {
3814+ ref : {
3815+ type : "ref/resource" ,
3816+ uri : "github://repos/{owner}/{repo}" ,
3817+ } ,
3818+ argument : {
3819+ name : "repo" ,
3820+ value : "r" ,
3821+ } ,
3822+ context : {
3823+ arguments : {
3824+ owner : "org2" ,
3825+ } ,
3826+ } ,
3827+ } ,
3828+ } ,
3829+ CompleteResultSchema ,
3830+ ) ;
3831+
3832+ expect ( result2 . completion . values ) . toEqual ( [ "repo1" , "repo2" , "repo3" ] ) ;
3833+ expect ( result2 . completion . total ) . toBe ( 3 ) ;
3834+
3835+ // Test with no resolved context
3836+ const result3 = await client . request (
3837+ {
3838+ method : "completion/complete" ,
3839+ params : {
3840+ ref : {
3841+ type : "ref/resource" ,
3842+ uri : "github://repos/{owner}/{repo}" ,
3843+ } ,
3844+ argument : {
3845+ name : "repo" ,
3846+ value : "t" ,
3847+ } ,
3848+ } ,
3849+ } ,
3850+ CompleteResultSchema ,
3851+ ) ;
3852+
3853+ expect ( result3 . completion . values ) . toEqual ( [ ] ) ;
3854+ expect ( result3 . completion . total ) . toBe ( 0 ) ;
3855+ } ) ;
3856+
3857+ test ( "should support prompt argument completion with resolved context" , async ( ) => {
3858+ const mcpServer = new McpServer ( {
3859+ name : "test server" ,
3860+ version : "1.0" ,
3861+ } ) ;
3862+
3863+ const client = new Client ( {
3864+ name : "test client" ,
3865+ version : "1.0" ,
3866+ } ) ;
3867+
3868+ mcpServer . prompt (
3869+ "test-prompt" ,
3870+ {
3871+ name : completable ( z . string ( ) , ( value , context ) => {
3872+ if ( context ?. arguments ?. [ "category" ] === "developers" ) {
3873+ return [ "Alice" , "Bob" , "Charlie" ] . filter ( n => n . startsWith ( value ) ) ;
3874+ } else if ( context ?. arguments ?. [ "category" ] === "managers" ) {
3875+ return [ "David" , "Eve" , "Frank" ] . filter ( n => n . startsWith ( value ) ) ;
3876+ }
3877+ return [ "Guest" ] . filter ( n => n . startsWith ( value ) ) ;
3878+ } ) ,
3879+ } ,
3880+ async ( { name } ) => ( {
3881+ messages : [
3882+ {
3883+ role : "assistant" ,
3884+ content : {
3885+ type : "text" ,
3886+ text : `Hello ${ name } ` ,
3887+ } ,
3888+ } ,
3889+ ] ,
3890+ } ) ,
3891+ ) ;
3892+
3893+ const [ clientTransport , serverTransport ] =
3894+ InMemoryTransport . createLinkedPair ( ) ;
3895+
3896+ await Promise . all ( [
3897+ client . connect ( clientTransport ) ,
3898+ mcpServer . server . connect ( serverTransport ) ,
3899+ ] ) ;
3900+
3901+ // Test with developers category
3902+ const result1 = await client . request (
3903+ {
3904+ method : "completion/complete" ,
3905+ params : {
3906+ ref : {
3907+ type : "ref/prompt" ,
3908+ name : "test-prompt" ,
3909+ } ,
3910+ argument : {
3911+ name : "name" ,
3912+ value : "A" ,
3913+ } ,
3914+ context : {
3915+ arguments : {
3916+ category : "developers" ,
3917+ } ,
3918+ } ,
3919+ } ,
3920+ } ,
3921+ CompleteResultSchema ,
3922+ ) ;
3923+
3924+ expect ( result1 . completion . values ) . toEqual ( [ "Alice" ] ) ;
3925+
3926+ // Test with managers category
3927+ const result2 = await client . request (
3928+ {
3929+ method : "completion/complete" ,
3930+ params : {
3931+ ref : {
3932+ type : "ref/prompt" ,
3933+ name : "test-prompt" ,
3934+ } ,
3935+ argument : {
3936+ name : "name" ,
3937+ value : "D" ,
3938+ } ,
3939+ context : {
3940+ arguments : {
3941+ category : "managers" ,
3942+ } ,
3943+ } ,
3944+ } ,
3945+ } ,
3946+ CompleteResultSchema ,
3947+ ) ;
3948+
3949+ expect ( result2 . completion . values ) . toEqual ( [ "David" ] ) ;
3950+
3951+ // Test with no resolved context
3952+ const result3 = await client . request (
3953+ {
3954+ method : "completion/complete" ,
3955+ params : {
3956+ ref : {
3957+ type : "ref/prompt" ,
3958+ name : "test-prompt" ,
3959+ } ,
3960+ argument : {
3961+ name : "name" ,
3962+ value : "G" ,
3963+ } ,
3964+ } ,
3965+ } ,
3966+ CompleteResultSchema ,
3967+ ) ;
3968+
3969+ expect ( result3 . completion . values ) . toEqual ( [ "Guest" ] ) ;
3970+ } ) ;
37383971} ) ;
0 commit comments