@@ -284,6 +284,58 @@ pub struct MCPReadResourceResult {
284284 pub contents : Vec < MCPResourceContents > ,
285285}
286286
287+ #[ derive( Clone , Debug , Default , PartialEq , Serialize , Deserialize , JsonSchema ) ]
288+ pub struct MCPPromptArgument {
289+ pub name : String ,
290+ pub description : Option < String > ,
291+ pub required : Option < bool > ,
292+ }
293+
294+ #[ derive( Clone , Debug , Default , PartialEq , Serialize , Deserialize , JsonSchema ) ]
295+ pub struct MCPPrompt {
296+ pub name : String ,
297+ pub description : Option < String > ,
298+ #[ serde( default ) ]
299+ pub arguments : Vec < MCPPromptArgument > ,
300+ pub meta : Option < Value > ,
301+ }
302+
303+ #[ derive( Clone , Debug , Default , PartialEq , Serialize , Deserialize , JsonSchema ) ]
304+ pub struct MCPPromptTextContent {
305+ pub text : String ,
306+ }
307+
308+ #[ derive( Clone , Debug , PartialEq , Serialize , Deserialize , JsonSchema ) ]
309+ #[ serde( tag = "type" , rename_all = "snake_case" ) ]
310+ pub enum MCPPromptContent {
311+ Text ( MCPPromptTextContent ) ,
312+ Json { value : Value } ,
313+ }
314+
315+ impl Default for MCPPromptContent {
316+ fn default ( ) -> Self {
317+ Self :: Text ( MCPPromptTextContent :: default ( ) )
318+ }
319+ }
320+
321+ #[ derive( Clone , Debug , Default , PartialEq , Serialize , Deserialize , JsonSchema ) ]
322+ pub struct MCPPromptMessage {
323+ pub role : String ,
324+ pub content : MCPPromptContent ,
325+ }
326+
327+ #[ derive( Clone , Debug , Default , PartialEq , Serialize , Deserialize , JsonSchema ) ]
328+ pub struct MCPListPromptsResult {
329+ pub prompts : Vec < MCPPrompt > ,
330+ pub next_cursor : Option < String > ,
331+ }
332+
333+ #[ derive( Clone , Debug , Default , PartialEq , Serialize , Deserialize , JsonSchema ) ]
334+ pub struct MCPGetPromptResult {
335+ pub description : Option < String > ,
336+ pub messages : Vec < MCPPromptMessage > ,
337+ }
338+
287339#[ async_trait]
288340pub trait MCPServer : Send + Sync {
289341 fn name ( & self ) -> & str ;
@@ -325,6 +377,22 @@ pub trait MCPServer: Send + Sync {
325377 message : "read_resource is not implemented" . to_owned ( ) ,
326378 } ) )
327379 }
380+
381+ async fn list_prompts ( & self , _cursor : Option < String > ) -> Result < MCPListPromptsResult > {
382+ Err ( AgentsError :: User ( UserError {
383+ message : "list_prompts is not implemented" . to_owned ( ) ,
384+ } ) )
385+ }
386+
387+ async fn get_prompt (
388+ & self ,
389+ _prompt_name : & str ,
390+ _arguments : Value ,
391+ ) -> Result < MCPGetPromptResult > {
392+ Err ( AgentsError :: User ( UserError {
393+ message : "get_prompt is not implemented" . to_owned ( ) ,
394+ } ) )
395+ }
328396}
329397
330398#[ derive( Clone , Debug , Default , PartialEq , Eq , Serialize , Deserialize , JsonSchema ) ]
@@ -479,6 +547,8 @@ pub struct MCPServerStdio {
479547 resources : Arc < Mutex < Vec < MCPResource > > > ,
480548 resource_templates : Arc < Mutex < Vec < MCPResourceTemplate > > > ,
481549 resource_contents : Arc < Mutex < HashMap < String , MCPReadResourceResult > > > ,
550+ prompts : Arc < Mutex < Vec < MCPPrompt > > > ,
551+ prompt_results : Arc < Mutex < HashMap < String , MCPGetPromptResult > > > ,
482552}
483553
484554impl fmt:: Debug for MCPServerStdio {
@@ -503,6 +573,8 @@ impl MCPServerStdio {
503573 resources : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
504574 resource_templates : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
505575 resource_contents : Arc :: new ( Mutex :: new ( HashMap :: new ( ) ) ) ,
576+ prompts : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
577+ prompt_results : Arc :: new ( Mutex :: new ( HashMap :: new ( ) ) ) ,
506578 }
507579 }
508580
@@ -545,6 +617,19 @@ impl MCPServerStdio {
545617 self
546618 }
547619
620+ pub fn with_prompts ( mut self , prompts : Vec < MCPPrompt > ) -> Self {
621+ self . prompts = Arc :: new ( Mutex :: new ( prompts) ) ;
622+ self
623+ }
624+
625+ pub fn with_prompt_results (
626+ mut self ,
627+ prompt_results : HashMap < String , MCPGetPromptResult > ,
628+ ) -> Self {
629+ self . prompt_results = Arc :: new ( Mutex :: new ( prompt_results) ) ;
630+ self
631+ }
632+
548633 fn ensure_connected ( & self ) -> Result < ( ) > {
549634 if self . connected . load ( Ordering :: SeqCst ) {
550635 Ok ( ( ) )
@@ -627,6 +712,28 @@ impl MCPServer for MCPServerStdio {
627712 } )
628713 } )
629714 }
715+
716+ async fn list_prompts ( & self , _cursor : Option < String > ) -> Result < MCPListPromptsResult > {
717+ self . ensure_connected ( ) ?;
718+ Ok ( MCPListPromptsResult {
719+ prompts : self . prompts . lock ( ) . await . clone ( ) ,
720+ next_cursor : None ,
721+ } )
722+ }
723+
724+ async fn get_prompt ( & self , prompt_name : & str , _arguments : Value ) -> Result < MCPGetPromptResult > {
725+ self . ensure_connected ( ) ?;
726+ self . prompt_results
727+ . lock ( )
728+ . await
729+ . get ( prompt_name)
730+ . cloned ( )
731+ . ok_or_else ( || {
732+ AgentsError :: User ( UserError {
733+ message : format ! ( "prompt `{prompt_name}` not found" ) ,
734+ } )
735+ } )
736+ }
630737}
631738
632739#[ derive( Clone ) ]
@@ -642,6 +749,8 @@ pub struct MCPServerSse {
642749 resources : Arc < Mutex < Vec < MCPResource > > > ,
643750 resource_templates : Arc < Mutex < Vec < MCPResourceTemplate > > > ,
644751 resource_contents : Arc < Mutex < HashMap < String , MCPReadResourceResult > > > ,
752+ prompts : Arc < Mutex < Vec < MCPPrompt > > > ,
753+ prompt_results : Arc < Mutex < HashMap < String , MCPGetPromptResult > > > ,
645754}
646755
647756impl fmt:: Debug for MCPServerSse {
@@ -668,6 +777,8 @@ impl MCPServerSse {
668777 resources : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
669778 resource_templates : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
670779 resource_contents : Arc :: new ( Mutex :: new ( HashMap :: new ( ) ) ) ,
780+ prompts : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
781+ prompt_results : Arc :: new ( Mutex :: new ( HashMap :: new ( ) ) ) ,
671782 }
672783 }
673784
@@ -715,6 +826,19 @@ impl MCPServerSse {
715826 self
716827 }
717828
829+ pub fn with_prompts ( mut self , prompts : Vec < MCPPrompt > ) -> Self {
830+ self . prompts = Arc :: new ( Mutex :: new ( prompts) ) ;
831+ self
832+ }
833+
834+ pub fn with_prompt_results (
835+ mut self ,
836+ prompt_results : HashMap < String , MCPGetPromptResult > ,
837+ ) -> Self {
838+ self . prompt_results = Arc :: new ( Mutex :: new ( prompt_results) ) ;
839+ self
840+ }
841+
718842 fn ensure_connected ( & self ) -> Result < ( ) > {
719843 if self . connected . load ( Ordering :: SeqCst ) {
720844 Ok ( ( ) )
@@ -835,6 +959,28 @@ impl MCPServer for MCPServerSse {
835959 } )
836960 } )
837961 }
962+
963+ async fn list_prompts ( & self , _cursor : Option < String > ) -> Result < MCPListPromptsResult > {
964+ self . ensure_connected ( ) ?;
965+ Ok ( MCPListPromptsResult {
966+ prompts : self . prompts . lock ( ) . await . clone ( ) ,
967+ next_cursor : None ,
968+ } )
969+ }
970+
971+ async fn get_prompt ( & self , prompt_name : & str , _arguments : Value ) -> Result < MCPGetPromptResult > {
972+ self . ensure_connected ( ) ?;
973+ self . prompt_results
974+ . lock ( )
975+ . await
976+ . get ( prompt_name)
977+ . cloned ( )
978+ . ok_or_else ( || {
979+ AgentsError :: User ( UserError {
980+ message : format ! ( "prompt `{prompt_name}` not found" ) ,
981+ } )
982+ } )
983+ }
838984}
839985
840986#[ derive( Clone ) ]
@@ -850,6 +996,8 @@ pub struct MCPServerStreamableHttp {
850996 resources : Arc < Mutex < Vec < MCPResource > > > ,
851997 resource_templates : Arc < Mutex < Vec < MCPResourceTemplate > > > ,
852998 resource_contents : Arc < Mutex < HashMap < String , MCPReadResourceResult > > > ,
999+ prompts : Arc < Mutex < Vec < MCPPrompt > > > ,
1000+ prompt_results : Arc < Mutex < HashMap < String , MCPGetPromptResult > > > ,
8531001}
8541002
8551003impl fmt:: Debug for MCPServerStreamableHttp {
@@ -876,6 +1024,8 @@ impl MCPServerStreamableHttp {
8761024 resources : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
8771025 resource_templates : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
8781026 resource_contents : Arc :: new ( Mutex :: new ( HashMap :: new ( ) ) ) ,
1027+ prompts : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
1028+ prompt_results : Arc :: new ( Mutex :: new ( HashMap :: new ( ) ) ) ,
8791029 }
8801030 }
8811031
@@ -923,6 +1073,19 @@ impl MCPServerStreamableHttp {
9231073 self
9241074 }
9251075
1076+ pub fn with_prompts ( mut self , prompts : Vec < MCPPrompt > ) -> Self {
1077+ self . prompts = Arc :: new ( Mutex :: new ( prompts) ) ;
1078+ self
1079+ }
1080+
1081+ pub fn with_prompt_results (
1082+ mut self ,
1083+ prompt_results : HashMap < String , MCPGetPromptResult > ,
1084+ ) -> Self {
1085+ self . prompt_results = Arc :: new ( Mutex :: new ( prompt_results) ) ;
1086+ self
1087+ }
1088+
9261089 fn ensure_connected ( & self ) -> Result < ( ) > {
9271090 if self . connected . load ( Ordering :: SeqCst ) {
9281091 Ok ( ( ) )
@@ -1051,6 +1214,28 @@ impl MCPServer for MCPServerStreamableHttp {
10511214 } )
10521215 } )
10531216 }
1217+
1218+ async fn list_prompts ( & self , _cursor : Option < String > ) -> Result < MCPListPromptsResult > {
1219+ self . ensure_connected ( ) ?;
1220+ Ok ( MCPListPromptsResult {
1221+ prompts : self . prompts . lock ( ) . await . clone ( ) ,
1222+ next_cursor : None ,
1223+ } )
1224+ }
1225+
1226+ async fn get_prompt ( & self , prompt_name : & str , _arguments : Value ) -> Result < MCPGetPromptResult > {
1227+ self . ensure_connected ( ) ?;
1228+ self . prompt_results
1229+ . lock ( )
1230+ . await
1231+ . get ( prompt_name)
1232+ . cloned ( )
1233+ . ok_or_else ( || {
1234+ AgentsError :: User ( UserError {
1235+ message : format ! ( "prompt `{prompt_name}` not found" ) ,
1236+ } )
1237+ } )
1238+ }
10541239}
10551240
10561241#[ cfg( test) ]
@@ -1399,4 +1584,67 @@ mod tests {
13991584
14001585 server. cleanup ( ) . await . expect ( "cleanup should succeed" ) ;
14011586 }
1587+
1588+ #[ tokio:: test]
1589+ async fn streamable_http_prompts_can_roundtrip ( ) {
1590+ let server = MCPServerStreamableHttp :: new (
1591+ "prompt-server" ,
1592+ MCPServerStreamableHttpParams {
1593+ url : "http://localhost:8000/mcp" . to_owned ( ) ,
1594+ ..MCPServerStreamableHttpParams :: default ( )
1595+ } ,
1596+ )
1597+ . with_prompts ( vec ! [ MCPPrompt {
1598+ name: "generate_code_review_instructions" . to_owned( ) ,
1599+ description: Some ( "Generate code review instructions." . to_owned( ) ) ,
1600+ arguments: vec![ MCPPromptArgument {
1601+ name: "focus" . to_owned( ) ,
1602+ description: Some ( "Review focus area." . to_owned( ) ) ,
1603+ required: Some ( false ) ,
1604+ } ] ,
1605+ ..MCPPrompt :: default ( )
1606+ } ] )
1607+ . with_prompt_results ( HashMap :: from ( [ (
1608+ "generate_code_review_instructions" . to_owned ( ) ,
1609+ MCPGetPromptResult {
1610+ description : Some ( "Code review prompt" . to_owned ( ) ) ,
1611+ messages : vec ! [ MCPPromptMessage {
1612+ role: "user" . to_owned( ) ,
1613+ content: MCPPromptContent :: Text ( MCPPromptTextContent {
1614+ text: "You are a senior code reviewer." . to_owned( ) ,
1615+ } ) ,
1616+ } ] ,
1617+ } ,
1618+ ) ] ) ) ;
1619+
1620+ assert ! ( server. list_prompts( None ) . await . is_err( ) ) ;
1621+ assert ! (
1622+ server
1623+ . get_prompt( "generate_code_review_instructions" , json!( { } ) )
1624+ . await
1625+ . is_err( )
1626+ ) ;
1627+
1628+ server. connect ( ) . await . expect ( "connect should succeed" ) ;
1629+ let prompts = server
1630+ . list_prompts ( None )
1631+ . await
1632+ . expect ( "prompts should load" ) ;
1633+ let prompt = server
1634+ . get_prompt (
1635+ "generate_code_review_instructions" ,
1636+ json ! ( { "focus" : "security" } ) ,
1637+ )
1638+ . await
1639+ . expect ( "prompt should load" ) ;
1640+
1641+ assert_eq ! ( prompts. prompts[ 0 ] . name, "generate_code_review_instructions" ) ;
1642+ assert_eq ! ( prompt. messages. len( ) , 1 ) ;
1643+ assert_eq ! (
1644+ prompt. messages[ 0 ] . content,
1645+ MCPPromptContent :: Text ( MCPPromptTextContent {
1646+ text: "You are a senior code reviewer." . to_owned( )
1647+ } )
1648+ ) ;
1649+ }
14021650}
0 commit comments