@@ -3521,12 +3521,12 @@ describe("prompt()", () => {
3521
3521
) ;
3522
3522
3523
3523
expect ( result . resources ) . toHaveLength ( 2 ) ;
3524
-
3524
+
3525
3525
// Resource 1 should have its own metadata
3526
3526
expect ( result . resources [ 0 ] . name ) . toBe ( "Resource 1" ) ;
3527
3527
expect ( result . resources [ 0 ] . description ) . toBe ( "Individual resource description" ) ;
3528
3528
expect ( result . resources [ 0 ] . mimeType ) . toBe ( "text/plain" ) ;
3529
-
3529
+
3530
3530
// Resource 2 should inherit template metadata
3531
3531
expect ( result . resources [ 1 ] . name ) . toBe ( "Resource 2" ) ;
3532
3532
expect ( result . resources [ 1 ] . description ) . toBe ( "Template description" ) ;
@@ -3592,7 +3592,7 @@ describe("prompt()", () => {
3592
3592
) ;
3593
3593
3594
3594
expect ( result . resources ) . toHaveLength ( 1 ) ;
3595
-
3595
+
3596
3596
// All fields should be from the individual resource, not the template
3597
3597
expect ( result . resources [ 0 ] . name ) . toBe ( "Overridden Name" ) ;
3598
3598
expect ( result . resources [ 0 ] . description ) . toBe ( "Overridden description" ) ;
@@ -3698,41 +3698,274 @@ describe("Tool title precedence", () => {
3698
3698
} ) ;
3699
3699
3700
3700
test ( "getDisplayName unit tests for title precedence" , ( ) => {
3701
-
3701
+
3702
3702
// Test 1: Only name
3703
3703
expect ( getDisplayName ( { name : "tool_name" } ) ) . toBe ( "tool_name" ) ;
3704
-
3704
+
3705
3705
// 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"
3709
3709
} ) ) . toBe ( "Tool Title" ) ;
3710
-
3710
+
3711
3711
// Test 3: Name and annotations.title - annotations.title wins
3712
- expect ( getDisplayName ( {
3712
+ expect ( getDisplayName ( {
3713
3713
name : "tool_name" ,
3714
3714
annotations : { title : "Annotations Title" }
3715
3715
} ) ) . toBe ( "Annotations Title" ) ;
3716
-
3716
+
3717
3717
// Test 4: All three - title wins (correct precedence)
3718
- expect ( getDisplayName ( {
3719
- name : "tool_name" ,
3718
+ expect ( getDisplayName ( {
3719
+ name : "tool_name" ,
3720
3720
title : "Regular Title" ,
3721
3721
annotations : { title : "Annotations Title" }
3722
3722
} ) ) . toBe ( "Regular Title" ) ;
3723
-
3723
+
3724
3724
// Test 5: Empty title should not be used
3725
- expect ( getDisplayName ( {
3726
- name : "tool_name" ,
3725
+ expect ( getDisplayName ( {
3726
+ name : "tool_name" ,
3727
3727
title : "" ,
3728
3728
annotations : { title : "Annotations Title" }
3729
3729
} ) ) . toBe ( "Annotations Title" ) ;
3730
-
3730
+
3731
3731
// Test 6: Undefined vs null handling
3732
- expect ( getDisplayName ( {
3733
- name : "tool_name" ,
3732
+ expect ( getDisplayName ( {
3733
+ name : "tool_name" ,
3734
3734
title : undefined ,
3735
3735
annotations : { title : "Annotations Title" }
3736
3736
} ) ) . toBe ( "Annotations Title" ) ;
3737
3737
} ) ;
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
+ } ) ;
3738
3971
} ) ;
0 commit comments