1010 send_func=agent_app.send,
1111 default_agent="default_agent",
1212 available_agents=["agent1", "agent2"],
13- apply_prompt_func =agent_app.apply_prompt
13+ prompt_provider =agent_app
1414 )
1515"""
1616
17- from typing import Dict , List , Optional
17+ from typing import Awaitable , Callable , Dict , List , Mapping , Optional , Protocol , Union
1818
19+ from mcp .types import Prompt , PromptMessage
1920from rich import print as rich_print
2021from rich .console import Console
2122from rich .table import Table
2829 handle_special_commands ,
2930)
3031from mcp_agent .mcp .mcp_aggregator import SEP # Import SEP once at the top
32+ from mcp_agent .mcp .prompt_message_multipart import PromptMessageMultipart
3133from mcp_agent .progress_display import progress_display
3234
35+ # Type alias for the send function
36+ SendFunc = Callable [[Union [str , PromptMessage , PromptMessageMultipart ], str ], Awaitable [str ]]
37+
38+
39+ class PromptProvider (Protocol ):
40+ """Protocol for objects that can provide prompt functionality."""
41+
42+ async def list_prompts (self , server_name : Optional [str ] = None , agent_name : Optional [str ] = None ) -> Mapping [str , List [Prompt ]]:
43+ """List available prompts."""
44+ ...
45+
46+ async def apply_prompt (self , prompt_name : str , arguments : Optional [Dict [str , str ]] = None , agent_name : Optional [str ] = None , ** kwargs ) -> str :
47+ """Apply a prompt."""
48+ ...
49+
3350
3451class InteractivePrompt :
3552 """
@@ -48,22 +65,20 @@ def __init__(self, agent_types: Optional[Dict[str, AgentType]] = None) -> None:
4865
4966 async def prompt_loop (
5067 self ,
51- send_func ,
68+ send_func : SendFunc ,
5269 default_agent : str ,
5370 available_agents : List [str ],
54- apply_prompt_func = None ,
55- list_prompts_func = None ,
71+ prompt_provider : Optional [PromptProvider ] = None ,
5672 default : str = "" ,
5773 ) -> str :
5874 """
5975 Start an interactive prompt session.
6076
6177 Args:
62- send_func: Function to send messages to agents (signature: async (message, agent_name))
78+ send_func: Function to send messages to agents
6379 default_agent: Name of the default agent to use
6480 available_agents: List of available agent names
65- apply_prompt_func: Optional function to apply prompts (signature: async (name, args, agent))
66- list_prompts_func: Optional function to list available prompts (signature: async (agent_name))
81+ prompt_provider: Optional provider that implements list_prompts and apply_prompt
6782 default: Default message to use when user presses enter
6883
6984 Returns:
@@ -110,21 +125,19 @@ async def prompt_loop(
110125 rich_print (f"[red]Agent '{ new_agent } ' not found[/red]" )
111126 continue
112127 # Keep the existing list_prompts handler for backward compatibility
113- elif "list_prompts" in command_result and list_prompts_func :
114- # Use the list_prompts_func directly
115- await self ._list_prompts (list_prompts_func , agent )
128+ elif "list_prompts" in command_result and prompt_provider :
129+ # Use the prompt_provider directly
130+ await self ._list_prompts (prompt_provider , agent )
116131 continue
117- elif "select_prompt" in command_result and (
118- list_prompts_func and apply_prompt_func
119- ):
132+ elif "select_prompt" in command_result and prompt_provider :
120133 # Handle prompt selection, using both list_prompts and apply_prompt
121134 prompt_name = command_result .get ("prompt_name" )
122135 prompt_index = command_result .get ("prompt_index" )
123136
124137 # If a specific index was provided (from /prompt <number>)
125138 if prompt_index is not None :
126139 # First get a list of all prompts to look up the index
127- all_prompts = await self ._get_all_prompts (list_prompts_func , agent )
140+ all_prompts = await self ._get_all_prompts (prompt_provider , agent )
128141 if not all_prompts :
129142 rich_print ("[yellow]No prompts available[/yellow]" )
130143 continue
@@ -135,8 +148,7 @@ async def prompt_loop(
135148 selected_prompt = all_prompts [prompt_index - 1 ]
136149 # Use the already created namespaced_name to ensure consistency
137150 await self ._select_prompt (
138- list_prompts_func ,
139- apply_prompt_func ,
151+ prompt_provider ,
140152 agent ,
141153 selected_prompt ["namespaced_name" ],
142154 )
@@ -145,11 +157,11 @@ async def prompt_loop(
145157 f"[red]Invalid prompt number: { prompt_index } . Valid range is 1-{ len (all_prompts )} [/red]"
146158 )
147159 # Show the prompt list for convenience
148- await self ._list_prompts (list_prompts_func , agent )
160+ await self ._list_prompts (prompt_provider , agent )
149161 else :
150162 # Use the name-based selection
151163 await self ._select_prompt (
152- list_prompts_func , apply_prompt_func , agent , prompt_name
164+ prompt_provider , agent , prompt_name
153165 )
154166 continue
155167
@@ -171,21 +183,21 @@ async def prompt_loop(
171183
172184 return result
173185
174- async def _get_all_prompts (self , list_prompts_func , agent_name ):
186+ async def _get_all_prompts (self , prompt_provider : PromptProvider , agent_name : Optional [ str ] = None ):
175187 """
176188 Get a list of all available prompts.
177189
178190 Args:
179- list_prompts_func: Function to get available prompts
180- agent_name: Name of the agent
191+ prompt_provider: Provider that implements list_prompts
192+ agent_name: Optional agent name (for multi- agent apps)
181193
182194 Returns:
183195 List of prompt info dictionaries, sorted by server and name
184196 """
185197 try :
186- # Pass None instead of agent_name to get prompts from all servers
187- # the agent_name parameter should never be used as a server name
188- prompt_servers = await list_prompts_func ( None )
198+ # Call list_prompts on the provider
199+ prompt_servers = await prompt_provider . list_prompts ( server_name = None , agent_name = agent_name )
200+
189201 all_prompts = []
190202
191203 # Process the returned prompt servers
@@ -219,14 +231,18 @@ async def _get_all_prompts(self, list_prompts_func, agent_name):
219231 }
220232 )
221233 else :
234+ # Handle Prompt objects from mcp.types
235+ prompt_name = getattr (prompt , "name" , str (prompt ))
236+ description = getattr (prompt , "description" , "No description" )
237+ arguments = getattr (prompt , "arguments" , [])
222238 all_prompts .append (
223239 {
224240 "server" : server_name ,
225- "name" : str ( prompt ) ,
226- "namespaced_name" : f"{ server_name } { SEP } { str ( prompt ) } " ,
227- "description" : "No description" ,
228- "arg_count" : 0 ,
229- "arguments" : [] ,
241+ "name" : prompt_name ,
242+ "namespaced_name" : f"{ server_name } { SEP } { prompt_name } " ,
243+ "description" : description ,
244+ "arg_count" : len ( arguments ) ,
245+ "arguments" : arguments ,
230246 }
231247 )
232248
@@ -244,27 +260,22 @@ async def _get_all_prompts(self, list_prompts_func, agent_name):
244260 rich_print (f"[dim]{ traceback .format_exc ()} [/dim]" )
245261 return []
246262
247- async def _list_prompts (self , list_prompts_func , agent_name ) -> None :
263+ async def _list_prompts (self , prompt_provider : PromptProvider , agent_name : str ) -> None :
248264 """
249265 List available prompts for an agent.
250266
251267 Args:
252- list_prompts_func: Function to get available prompts
268+ prompt_provider: Provider that implements list_prompts
253269 agent_name: Name of the agent
254270 """
255- from rich import print as rich_print
256- from rich .console import Console
257- from rich .table import Table
258-
259271 console = Console ()
260272
261273 try :
262274 # Directly call the list_prompts function for this agent
263275 rich_print (f"\n [bold]Fetching prompts for agent [cyan]{ agent_name } [/cyan]...[/bold]" )
264276
265- # Get all prompts using the helper function - pass None as server name
266- # to get prompts from all available servers
267- all_prompts = await self ._get_all_prompts (list_prompts_func , None )
277+ # Get all prompts using the helper function
278+ all_prompts = await self ._get_all_prompts (prompt_provider , agent_name )
268279
269280 if all_prompts :
270281 # Create a table for better display
@@ -300,28 +311,24 @@ async def _list_prompts(self, list_prompts_func, agent_name) -> None:
300311 rich_print (f"[dim]{ traceback .format_exc ()} [/dim]" )
301312
302313 async def _select_prompt (
303- self , list_prompts_func , apply_prompt_func , agent_name , requested_name = None
314+ self , prompt_provider : PromptProvider , agent_name : str , requested_name : Optional [ str ] = None
304315 ) -> None :
305316 """
306317 Select and apply a prompt.
307318
308319 Args:
309- list_prompts_func: Function to get available prompts
310- apply_prompt_func: Function to apply prompts
320+ prompt_provider: Provider that implements list_prompts and apply_prompt
311321 agent_name: Name of the agent
312322 requested_name: Optional name of the prompt to apply
313323 """
314- # We already imported these at the top
315- from rich import print as rich_print
316-
317324 console = Console ()
318325
319326 try :
320- # Get all available prompts directly from the list_prompts function
327+ # Get all available prompts directly from the prompt provider
321328 rich_print (f"\n [bold]Fetching prompts for agent [cyan]{ agent_name } [/cyan]...[/bold]" )
322- # IMPORTANT: list_prompts_func gets MCP server prompts, not agent prompts
323- # So we pass None to get prompts from all servers, not using agent_name as server name
324- prompt_servers = await list_prompts_func ( None )
329+
330+ # Call list_prompts on the provider
331+ prompt_servers = await prompt_provider . list_prompts ( server_name = None , agent_name = agent_name )
325332
326333 if not prompt_servers :
327334 rich_print ("[yellow]No prompts available for this agent[/yellow]" )
@@ -542,8 +549,8 @@ async def _select_prompt(
542549 namespaced_name = selected_prompt ["namespaced_name" ]
543550 rich_print (f"\n [bold]Applying prompt [cyan]{ namespaced_name } [/cyan]...[/bold]" )
544551
545- # Call apply_prompt function with the prompt name and arguments
546- await apply_prompt_func (namespaced_name , arg_values , agent_name )
552+ # Call apply_prompt on the provider with the prompt name and arguments
553+ await prompt_provider . apply_prompt (namespaced_name , arg_values , agent_name )
547554
548555 except Exception as e :
549556 import traceback
0 commit comments