fast-agent-mcp 0.1.12__py3-none-any.whl → 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/METADATA +3 -4
  2. fast_agent_mcp-0.2.0.dist-info/RECORD +123 -0
  3. mcp_agent/__init__.py +75 -0
  4. mcp_agent/agents/agent.py +61 -415
  5. mcp_agent/agents/base_agent.py +522 -0
  6. mcp_agent/agents/workflow/__init__.py +1 -0
  7. mcp_agent/agents/workflow/chain_agent.py +173 -0
  8. mcp_agent/agents/workflow/evaluator_optimizer.py +362 -0
  9. mcp_agent/agents/workflow/orchestrator_agent.py +591 -0
  10. mcp_agent/{workflows/orchestrator → agents/workflow}/orchestrator_models.py +11 -21
  11. mcp_agent/agents/workflow/parallel_agent.py +182 -0
  12. mcp_agent/agents/workflow/router_agent.py +307 -0
  13. mcp_agent/app.py +15 -19
  14. mcp_agent/cli/commands/bootstrap.py +19 -38
  15. mcp_agent/cli/commands/config.py +4 -4
  16. mcp_agent/cli/commands/setup.py +7 -14
  17. mcp_agent/cli/main.py +7 -10
  18. mcp_agent/cli/terminal.py +3 -3
  19. mcp_agent/config.py +25 -40
  20. mcp_agent/context.py +12 -21
  21. mcp_agent/context_dependent.py +3 -5
  22. mcp_agent/core/agent_types.py +10 -7
  23. mcp_agent/core/direct_agent_app.py +179 -0
  24. mcp_agent/core/direct_decorators.py +443 -0
  25. mcp_agent/core/direct_factory.py +476 -0
  26. mcp_agent/core/enhanced_prompt.py +23 -55
  27. mcp_agent/core/exceptions.py +8 -8
  28. mcp_agent/core/fastagent.py +145 -371
  29. mcp_agent/core/interactive_prompt.py +424 -0
  30. mcp_agent/core/mcp_content.py +17 -17
  31. mcp_agent/core/prompt.py +6 -9
  32. mcp_agent/core/request_params.py +6 -3
  33. mcp_agent/core/validation.py +92 -18
  34. mcp_agent/executor/decorator_registry.py +9 -17
  35. mcp_agent/executor/executor.py +8 -17
  36. mcp_agent/executor/task_registry.py +2 -4
  37. mcp_agent/executor/temporal.py +19 -41
  38. mcp_agent/executor/workflow.py +3 -5
  39. mcp_agent/executor/workflow_signal.py +15 -21
  40. mcp_agent/human_input/handler.py +4 -7
  41. mcp_agent/human_input/types.py +2 -3
  42. mcp_agent/llm/__init__.py +2 -0
  43. mcp_agent/llm/augmented_llm.py +450 -0
  44. mcp_agent/llm/augmented_llm_passthrough.py +162 -0
  45. mcp_agent/llm/augmented_llm_playback.py +83 -0
  46. mcp_agent/llm/memory.py +103 -0
  47. mcp_agent/{workflows/llm → llm}/model_factory.py +22 -16
  48. mcp_agent/{workflows/llm → llm}/prompt_utils.py +1 -3
  49. mcp_agent/llm/providers/__init__.py +8 -0
  50. mcp_agent/{workflows/llm → llm/providers}/anthropic_utils.py +8 -25
  51. mcp_agent/{workflows/llm → llm/providers}/augmented_llm_anthropic.py +56 -194
  52. mcp_agent/llm/providers/augmented_llm_deepseek.py +53 -0
  53. mcp_agent/{workflows/llm → llm/providers}/augmented_llm_openai.py +99 -190
  54. mcp_agent/{workflows/llm → llm}/providers/multipart_converter_anthropic.py +72 -71
  55. mcp_agent/{workflows/llm → llm}/providers/multipart_converter_openai.py +65 -71
  56. mcp_agent/{workflows/llm → llm}/providers/openai_multipart.py +16 -44
  57. mcp_agent/{workflows/llm → llm/providers}/openai_utils.py +4 -4
  58. mcp_agent/{workflows/llm → llm}/providers/sampling_converter_anthropic.py +9 -11
  59. mcp_agent/{workflows/llm → llm}/providers/sampling_converter_openai.py +8 -12
  60. mcp_agent/{workflows/llm → llm}/sampling_converter.py +3 -31
  61. mcp_agent/llm/sampling_format_converter.py +37 -0
  62. mcp_agent/logging/events.py +1 -5
  63. mcp_agent/logging/json_serializer.py +7 -6
  64. mcp_agent/logging/listeners.py +20 -23
  65. mcp_agent/logging/logger.py +17 -19
  66. mcp_agent/logging/rich_progress.py +10 -8
  67. mcp_agent/logging/tracing.py +4 -6
  68. mcp_agent/logging/transport.py +22 -22
  69. mcp_agent/mcp/gen_client.py +1 -3
  70. mcp_agent/mcp/interfaces.py +117 -110
  71. mcp_agent/mcp/logger_textio.py +97 -0
  72. mcp_agent/mcp/mcp_agent_client_session.py +7 -7
  73. mcp_agent/mcp/mcp_agent_server.py +8 -8
  74. mcp_agent/mcp/mcp_aggregator.py +102 -143
  75. mcp_agent/mcp/mcp_connection_manager.py +20 -27
  76. mcp_agent/mcp/prompt_message_multipart.py +68 -16
  77. mcp_agent/mcp/prompt_render.py +77 -0
  78. mcp_agent/mcp/prompt_serialization.py +30 -48
  79. mcp_agent/mcp/prompts/prompt_constants.py +18 -0
  80. mcp_agent/mcp/prompts/prompt_helpers.py +327 -0
  81. mcp_agent/mcp/prompts/prompt_load.py +109 -0
  82. mcp_agent/mcp/prompts/prompt_server.py +155 -195
  83. mcp_agent/mcp/prompts/prompt_template.py +35 -66
  84. mcp_agent/mcp/resource_utils.py +7 -14
  85. mcp_agent/mcp/sampling.py +17 -17
  86. mcp_agent/mcp_server/agent_server.py +13 -17
  87. mcp_agent/mcp_server_registry.py +13 -22
  88. mcp_agent/resources/examples/{workflows → in_dev}/agent_build.py +3 -2
  89. mcp_agent/resources/examples/in_dev/slides.py +110 -0
  90. mcp_agent/resources/examples/internal/agent.py +6 -3
  91. mcp_agent/resources/examples/internal/fastagent.config.yaml +8 -2
  92. mcp_agent/resources/examples/internal/job.py +2 -1
  93. mcp_agent/resources/examples/internal/prompt_category.py +1 -1
  94. mcp_agent/resources/examples/internal/prompt_sizing.py +3 -5
  95. mcp_agent/resources/examples/internal/sizer.py +2 -1
  96. mcp_agent/resources/examples/internal/social.py +2 -1
  97. mcp_agent/resources/examples/prompting/agent.py +2 -1
  98. mcp_agent/resources/examples/prompting/image_server.py +4 -8
  99. mcp_agent/resources/examples/prompting/work_with_image.py +19 -0
  100. mcp_agent/ui/console_display.py +16 -20
  101. fast_agent_mcp-0.1.12.dist-info/RECORD +0 -161
  102. mcp_agent/core/agent_app.py +0 -646
  103. mcp_agent/core/agent_utils.py +0 -71
  104. mcp_agent/core/decorators.py +0 -455
  105. mcp_agent/core/factory.py +0 -463
  106. mcp_agent/core/proxies.py +0 -269
  107. mcp_agent/core/types.py +0 -24
  108. mcp_agent/eval/__init__.py +0 -0
  109. mcp_agent/mcp/stdio.py +0 -111
  110. mcp_agent/resources/examples/data-analysis/analysis-campaign.py +0 -188
  111. mcp_agent/resources/examples/data-analysis/analysis.py +0 -65
  112. mcp_agent/resources/examples/data-analysis/fastagent.config.yaml +0 -41
  113. mcp_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +0 -1471
  114. mcp_agent/resources/examples/mcp_researcher/researcher-eval.py +0 -53
  115. mcp_agent/resources/examples/researcher/fastagent.config.yaml +0 -66
  116. mcp_agent/resources/examples/researcher/researcher-eval.py +0 -53
  117. mcp_agent/resources/examples/researcher/researcher-imp.py +0 -190
  118. mcp_agent/resources/examples/researcher/researcher.py +0 -38
  119. mcp_agent/resources/examples/workflows/chaining.py +0 -44
  120. mcp_agent/resources/examples/workflows/evaluator.py +0 -78
  121. mcp_agent/resources/examples/workflows/fastagent.config.yaml +0 -24
  122. mcp_agent/resources/examples/workflows/human_input.py +0 -25
  123. mcp_agent/resources/examples/workflows/orchestrator.py +0 -73
  124. mcp_agent/resources/examples/workflows/parallel.py +0 -78
  125. mcp_agent/resources/examples/workflows/router.py +0 -53
  126. mcp_agent/resources/examples/workflows/sse.py +0 -23
  127. mcp_agent/telemetry/__init__.py +0 -0
  128. mcp_agent/telemetry/usage_tracking.py +0 -18
  129. mcp_agent/workflows/__init__.py +0 -0
  130. mcp_agent/workflows/embedding/__init__.py +0 -0
  131. mcp_agent/workflows/embedding/embedding_base.py +0 -61
  132. mcp_agent/workflows/embedding/embedding_cohere.py +0 -49
  133. mcp_agent/workflows/embedding/embedding_openai.py +0 -46
  134. mcp_agent/workflows/evaluator_optimizer/__init__.py +0 -0
  135. mcp_agent/workflows/evaluator_optimizer/evaluator_optimizer.py +0 -481
  136. mcp_agent/workflows/intent_classifier/__init__.py +0 -0
  137. mcp_agent/workflows/intent_classifier/intent_classifier_base.py +0 -120
  138. mcp_agent/workflows/intent_classifier/intent_classifier_embedding.py +0 -134
  139. mcp_agent/workflows/intent_classifier/intent_classifier_embedding_cohere.py +0 -45
  140. mcp_agent/workflows/intent_classifier/intent_classifier_embedding_openai.py +0 -45
  141. mcp_agent/workflows/intent_classifier/intent_classifier_llm.py +0 -161
  142. mcp_agent/workflows/intent_classifier/intent_classifier_llm_anthropic.py +0 -60
  143. mcp_agent/workflows/intent_classifier/intent_classifier_llm_openai.py +0 -60
  144. mcp_agent/workflows/llm/__init__.py +0 -0
  145. mcp_agent/workflows/llm/augmented_llm.py +0 -753
  146. mcp_agent/workflows/llm/augmented_llm_passthrough.py +0 -241
  147. mcp_agent/workflows/llm/augmented_llm_playback.py +0 -109
  148. mcp_agent/workflows/llm/providers/__init__.py +0 -8
  149. mcp_agent/workflows/llm/sampling_format_converter.py +0 -22
  150. mcp_agent/workflows/orchestrator/__init__.py +0 -0
  151. mcp_agent/workflows/orchestrator/orchestrator.py +0 -578
  152. mcp_agent/workflows/parallel/__init__.py +0 -0
  153. mcp_agent/workflows/parallel/fan_in.py +0 -350
  154. mcp_agent/workflows/parallel/fan_out.py +0 -187
  155. mcp_agent/workflows/parallel/parallel_llm.py +0 -166
  156. mcp_agent/workflows/router/__init__.py +0 -0
  157. mcp_agent/workflows/router/router_base.py +0 -368
  158. mcp_agent/workflows/router/router_embedding.py +0 -240
  159. mcp_agent/workflows/router/router_embedding_cohere.py +0 -59
  160. mcp_agent/workflows/router/router_embedding_openai.py +0 -59
  161. mcp_agent/workflows/router/router_llm.py +0 -320
  162. mcp_agent/workflows/swarm/__init__.py +0 -0
  163. mcp_agent/workflows/swarm/swarm.py +0 -320
  164. mcp_agent/workflows/swarm/swarm_anthropic.py +0 -42
  165. mcp_agent/workflows/swarm/swarm_openai.py +0 -41
  166. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/WHEEL +0 -0
  167. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/entry_points.txt +0 -0
  168. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/licenses/LICENSE +0 -0
  169. /mcp_agent/{workflows/orchestrator → agents/workflow}/orchestrator_prompts.py +0 -0
@@ -0,0 +1,424 @@
1
+ """
2
+ Interactive prompt functionality for agents.
3
+
4
+ This module provides interactive command-line functionality for agents,
5
+ extracted from the original AgentApp implementation to support the new DirectAgentApp.
6
+
7
+ Usage:
8
+ prompt = InteractivePrompt()
9
+ await prompt.prompt_loop(
10
+ send_func=agent_app.send,
11
+ default_agent="default_agent",
12
+ available_agents=["agent1", "agent2"],
13
+ apply_prompt_func=agent_app.apply_prompt
14
+ )
15
+ """
16
+
17
+ from typing import Dict, List, Optional
18
+
19
+ from rich import print as rich_print
20
+ from rich.console import Console
21
+ from rich.table import Table
22
+
23
+ from mcp_agent.core.enhanced_prompt import (
24
+ get_argument_input,
25
+ get_enhanced_input,
26
+ get_selection_input,
27
+ handle_special_commands,
28
+ )
29
+ from mcp_agent.progress_display import progress_display
30
+
31
+
32
+ class InteractivePrompt:
33
+ """
34
+ Provides interactive prompt functionality that works with any agent implementation.
35
+ This is extracted from the original AgentApp implementation to support DirectAgentApp.
36
+ """
37
+
38
+ def __init__(self, agent_types: Optional[Dict[str, str]] = None) -> None:
39
+ """
40
+ Initialize the interactive prompt.
41
+
42
+ Args:
43
+ agent_types: Dictionary mapping agent names to their types for display
44
+ """
45
+ self.agent_types = agent_types or {}
46
+
47
+ async def prompt_loop(
48
+ self,
49
+ send_func,
50
+ default_agent: str,
51
+ available_agents: List[str],
52
+ apply_prompt_func=None,
53
+ list_prompts_func=None,
54
+ default: str = "",
55
+ ) -> str:
56
+ """
57
+ Start an interactive prompt session.
58
+
59
+ Args:
60
+ send_func: Function to send messages to agents (signature: async (message, agent_name))
61
+ default_agent: Name of the default agent to use
62
+ available_agents: List of available agent names
63
+ apply_prompt_func: Optional function to apply prompts (signature: async (name, args, agent))
64
+ list_prompts_func: Optional function to list available prompts (signature: async (agent_name))
65
+ default: Default message to use when user presses enter
66
+
67
+ Returns:
68
+ The result of the interactive session
69
+ """
70
+ agent = default_agent
71
+ if not agent:
72
+ if available_agents:
73
+ agent = available_agents[0]
74
+ else:
75
+ raise ValueError("No default agent available")
76
+
77
+ if agent not in available_agents:
78
+ raise ValueError(f"No agent named '{agent}'")
79
+
80
+ # Create agent_types dictionary if not provided
81
+ available_agents_set = set(available_agents)
82
+
83
+ result = ""
84
+ while True:
85
+ with progress_display.paused():
86
+ # Use the enhanced input method with advanced features
87
+ user_input = await get_enhanced_input(
88
+ agent_name=agent,
89
+ default=default,
90
+ show_default=(default != ""),
91
+ show_stop_hint=True,
92
+ multiline=False, # Default to single-line mode
93
+ available_agent_names=available_agents,
94
+ agent_types=self.agent_types, # Pass agent types for display
95
+ )
96
+
97
+ # Handle special commands - pass "True" to enable agent switching
98
+ command_result = await handle_special_commands(user_input, True)
99
+
100
+ # Check if we should switch agents
101
+ if isinstance(command_result, dict):
102
+ if "switch_agent" in command_result:
103
+ new_agent = command_result["switch_agent"]
104
+ if new_agent in available_agents_set:
105
+ agent = new_agent
106
+ continue
107
+ else:
108
+ rich_print(f"[red]Agent '{new_agent}' not found[/red]")
109
+ continue
110
+ elif "list_prompts" in command_result and list_prompts_func:
111
+ # Use the list_prompts_func directly
112
+ await self._list_prompts(list_prompts_func, agent)
113
+ continue
114
+ elif "select_prompt" in command_result and (list_prompts_func and apply_prompt_func):
115
+ # Handle prompt selection, using both list_prompts and apply_prompt
116
+ prompt_name = command_result.get("prompt_name")
117
+ await self._select_prompt(list_prompts_func, apply_prompt_func, agent, prompt_name)
118
+ continue
119
+
120
+ # Skip further processing if command was handled
121
+ if command_result:
122
+ continue
123
+
124
+ if user_input.upper() == "STOP":
125
+ return result
126
+ if user_input == "":
127
+ continue
128
+
129
+ # Send the message to the agent
130
+ result = await send_func(user_input, agent)
131
+
132
+ return result
133
+
134
+ async def _list_prompts(self, list_prompts_func, agent_name) -> None:
135
+ """
136
+ List available prompts for an agent.
137
+
138
+ Args:
139
+ list_prompts_func: Function to get available prompts
140
+ agent_name: Name of the agent
141
+ """
142
+ from rich import print as rich_print
143
+
144
+ try:
145
+ # Directly call the list_prompts function for this agent
146
+ rich_print(f"\n[bold]Fetching prompts for agent [cyan]{agent_name}[/cyan]...[/bold]")
147
+
148
+ prompt_servers = await list_prompts_func(agent_name)
149
+
150
+ # Process the returned prompt servers
151
+ if prompt_servers:
152
+ found_prompts = False
153
+ for server_name, prompts_info in prompt_servers.items():
154
+ if prompts_info and hasattr(prompts_info, "prompts") and prompts_info.prompts:
155
+ rich_print(f"\n[bold cyan]{server_name}:[/bold cyan]")
156
+ for prompt in prompts_info.prompts:
157
+ rich_print(f" {prompt.name}")
158
+ found_prompts = True
159
+ elif isinstance(prompts_info, list) and prompts_info:
160
+ rich_print(f"\n[bold cyan]{server_name}:[/bold cyan]")
161
+ for prompt in prompts_info:
162
+ if isinstance(prompt, dict) and "name" in prompt:
163
+ rich_print(f" {prompt['name']}")
164
+ else:
165
+ rich_print(f" {prompt}")
166
+ found_prompts = True
167
+
168
+ if not found_prompts:
169
+ rich_print("[yellow]No prompts available[/yellow]")
170
+ else:
171
+ rich_print("[yellow]No prompts available[/yellow]")
172
+ except Exception as e:
173
+ import traceback
174
+ rich_print(f"[red]Error listing prompts: {e}[/red]")
175
+ rich_print(f"[dim]{traceback.format_exc()}[/dim]")
176
+
177
+ async def _select_prompt(self, list_prompts_func, apply_prompt_func, agent_name, requested_name=None) -> None:
178
+ """
179
+ Select and apply a prompt.
180
+
181
+ Args:
182
+ list_prompts_func: Function to get available prompts
183
+ apply_prompt_func: Function to apply prompts
184
+ agent_name: Name of the agent
185
+ requested_name: Optional name of the prompt to apply
186
+ """
187
+ # We already imported these at the top
188
+ from rich import print as rich_print
189
+
190
+ console = Console()
191
+
192
+ try:
193
+ # Get all available prompts directly from the list_prompts function
194
+ rich_print(f"\n[bold]Fetching prompts for agent [cyan]{agent_name}[/cyan]...[/bold]")
195
+ prompt_servers = await list_prompts_func(agent_name)
196
+
197
+ if not prompt_servers:
198
+ rich_print("[yellow]No prompts available for this agent[/yellow]")
199
+ return
200
+
201
+ # Process fetched prompts
202
+ all_prompts = []
203
+ for server_name, prompts_info in prompt_servers.items():
204
+ if not prompts_info:
205
+ continue
206
+
207
+ # Extract prompts
208
+ prompts = []
209
+ if hasattr(prompts_info, "prompts"):
210
+ prompts = prompts_info.prompts
211
+ elif isinstance(prompts_info, list):
212
+ prompts = prompts_info
213
+
214
+ # Process each prompt
215
+ for prompt in prompts:
216
+ # Get basic prompt info
217
+ prompt_name = getattr(prompt, "name", "Unknown")
218
+ description = getattr(prompt, "description", "No description")
219
+
220
+ # Extract argument information
221
+ arg_names = []
222
+ required_args = []
223
+ optional_args = []
224
+ arg_descriptions = {}
225
+
226
+ # Get arguments list
227
+ arguments = getattr(prompt, "arguments", None)
228
+ if arguments:
229
+ for arg in arguments:
230
+ name = getattr(arg, "name", None)
231
+ if name:
232
+ arg_names.append(name)
233
+
234
+ # Store description if available
235
+ description = getattr(arg, "description", None)
236
+ if description:
237
+ arg_descriptions[name] = description
238
+
239
+ # Check if required
240
+ if getattr(arg, "required", False):
241
+ required_args.append(name)
242
+ else:
243
+ optional_args.append(name)
244
+
245
+ # Create namespaced version
246
+ namespaced_name = f"{server_name}-{prompt_name}"
247
+
248
+ # Add to collection
249
+ all_prompts.append(
250
+ {
251
+ "server": server_name,
252
+ "name": prompt_name,
253
+ "namespaced_name": namespaced_name,
254
+ "description": description,
255
+ "arg_count": len(arg_names),
256
+ "arg_names": arg_names,
257
+ "required_args": required_args,
258
+ "optional_args": optional_args,
259
+ "arg_descriptions": arg_descriptions,
260
+ }
261
+ )
262
+
263
+ if not all_prompts:
264
+ rich_print("[yellow]No prompts available for this agent[/yellow]")
265
+ return
266
+
267
+ # Sort prompts by server then name
268
+ all_prompts.sort(key=lambda p: (p["server"], p["name"]))
269
+
270
+ # Handle specifically requested prompt
271
+ if requested_name:
272
+ matching_prompts = [
273
+ p
274
+ for p in all_prompts
275
+ if p["name"] == requested_name or p["namespaced_name"] == requested_name
276
+ ]
277
+
278
+ if not matching_prompts:
279
+ rich_print(f"[red]Prompt '{requested_name}' not found[/red]")
280
+ rich_print("[yellow]Available prompts:[/yellow]")
281
+ for p in all_prompts:
282
+ rich_print(f" {p['namespaced_name']}")
283
+ return
284
+
285
+ # If exactly one match, use it
286
+ if len(matching_prompts) == 1:
287
+ selected_prompt = matching_prompts[0]
288
+ else:
289
+ # Handle multiple matches
290
+ rich_print(f"[yellow]Multiple prompts match '{requested_name}':[/yellow]")
291
+ for i, p in enumerate(matching_prompts):
292
+ rich_print(f" {i + 1}. {p['namespaced_name']} - {p['description']}")
293
+
294
+ # Get user selection
295
+ selection = (
296
+ await get_selection_input("Enter prompt number to select: ", default="1")
297
+ or ""
298
+ )
299
+
300
+ try:
301
+ idx = int(selection) - 1
302
+ if 0 <= idx < len(matching_prompts):
303
+ selected_prompt = matching_prompts[idx]
304
+ else:
305
+ rich_print("[red]Invalid selection[/red]")
306
+ return
307
+ except ValueError:
308
+ rich_print("[red]Invalid input, please enter a number[/red]")
309
+ return
310
+ else:
311
+ # Show prompt selection UI
312
+ table = Table(title="Available MCP Prompts")
313
+ table.add_column("#", justify="right", style="cyan")
314
+ table.add_column("Server", style="green")
315
+ table.add_column("Prompt Name", style="bright_blue")
316
+ table.add_column("Description")
317
+ table.add_column("Args", justify="center")
318
+
319
+ # Add prompts to table
320
+ for i, prompt in enumerate(all_prompts):
321
+ required_args = prompt["required_args"]
322
+ optional_args = prompt["optional_args"]
323
+
324
+ # Format args column
325
+ if required_args and optional_args:
326
+ args_display = f"[bold]{len(required_args)}[/bold]+{len(optional_args)}"
327
+ elif required_args:
328
+ args_display = f"[bold]{len(required_args)}[/bold]"
329
+ elif optional_args:
330
+ args_display = f"{len(optional_args)} opt"
331
+ else:
332
+ args_display = "0"
333
+
334
+ table.add_row(
335
+ str(i + 1),
336
+ prompt["server"],
337
+ prompt["name"],
338
+ prompt["description"] or "No description",
339
+ args_display,
340
+ )
341
+
342
+ console.print(table)
343
+ prompt_names = [str(i + 1) for i in range(len(all_prompts))]
344
+
345
+ # Get user selection
346
+ selection = await get_selection_input(
347
+ "Enter prompt number to select (or press Enter to cancel): ",
348
+ options=prompt_names,
349
+ allow_cancel=True,
350
+ )
351
+
352
+ # Handle cancellation
353
+ if not selection or selection.strip() == "":
354
+ rich_print("[yellow]Prompt selection cancelled[/yellow]")
355
+ return
356
+
357
+ try:
358
+ idx = int(selection) - 1
359
+ if 0 <= idx < len(all_prompts):
360
+ selected_prompt = all_prompts[idx]
361
+ else:
362
+ rich_print("[red]Invalid selection[/red]")
363
+ return
364
+ except ValueError:
365
+ rich_print("[red]Invalid input, please enter a number[/red]")
366
+ return
367
+
368
+ # Get prompt arguments
369
+ required_args = selected_prompt["required_args"]
370
+ optional_args = selected_prompt["optional_args"]
371
+ arg_descriptions = selected_prompt.get("arg_descriptions", {})
372
+ arg_values = {}
373
+
374
+ # Show argument info if any
375
+ if required_args or optional_args:
376
+ if required_args and optional_args:
377
+ rich_print(
378
+ f"\n[bold]Prompt [cyan]{selected_prompt['name']}[/cyan] requires {len(required_args)} arguments and has {len(optional_args)} optional arguments:[/bold]"
379
+ )
380
+ elif required_args:
381
+ rich_print(
382
+ f"\n[bold]Prompt [cyan]{selected_prompt['name']}[/cyan] requires {len(required_args)} arguments:[/bold]"
383
+ )
384
+ elif optional_args:
385
+ rich_print(
386
+ f"\n[bold]Prompt [cyan]{selected_prompt['name']}[/cyan] has {len(optional_args)} optional arguments:[/bold]"
387
+ )
388
+
389
+ # Collect required arguments
390
+ for arg_name in required_args:
391
+ description = arg_descriptions.get(arg_name, "")
392
+ arg_value = await get_argument_input(
393
+ arg_name=arg_name,
394
+ description=description,
395
+ required=True,
396
+ )
397
+ if arg_value is not None:
398
+ arg_values[arg_name] = arg_value
399
+
400
+ # Collect optional arguments
401
+ if optional_args:
402
+ for arg_name in optional_args:
403
+ description = arg_descriptions.get(arg_name, "")
404
+ arg_value = await get_argument_input(
405
+ arg_name=arg_name,
406
+ description=description,
407
+ required=False,
408
+ )
409
+ if arg_value:
410
+ arg_values[arg_name] = arg_value
411
+
412
+ # Apply the prompt
413
+ rich_print(
414
+ f"\n[bold]Applying prompt [cyan]{selected_prompt['namespaced_name']}[/cyan]...[/bold]"
415
+ )
416
+
417
+ # Call apply_prompt function with the prompt name and arguments
418
+ await apply_prompt_func(selected_prompt["namespaced_name"], arg_values, agent_name)
419
+
420
+ except Exception as e:
421
+ import traceback
422
+
423
+ rich_print(f"[red]Error selecting or applying prompt: {e}[/red]")
424
+ rich_print(f"[dim]{traceback.format_exc()}[/dim]")
@@ -7,15 +7,17 @@ EmbeddedResource, and other MCP content types with minimal boilerplate.
7
7
 
8
8
  import base64
9
9
  from pathlib import Path
10
- from typing import Literal, Optional, Union, List, Any
10
+ from typing import Any, List, Literal, Optional, Union
11
11
 
12
12
  from mcp.types import (
13
- TextContent,
14
- ImageContent,
13
+ Annotations,
14
+ BlobResourceContents,
15
15
  EmbeddedResource,
16
+ ImageContent,
17
+ TextContent,
16
18
  TextResourceContents,
17
- BlobResourceContents,
18
19
  )
20
+ from pydantic import AnyUrl
19
21
 
20
22
  from mcp_agent.mcp.mime_utils import (
21
23
  guess_mime_type,
@@ -27,7 +29,7 @@ from mcp_agent.mcp.mime_utils import (
27
29
  def MCPText(
28
30
  text: str,
29
31
  role: Literal["user", "assistant"] = "user",
30
- annotations: Optional[dict] = None,
32
+ annotations: Annotations = None,
31
33
  ) -> dict:
32
34
  """
33
35
  Create a message with text content.
@@ -47,11 +49,11 @@ def MCPText(
47
49
 
48
50
 
49
51
  def MCPImage(
50
- path: Union[str, Path] = None,
51
- data: bytes = None,
52
+ path: str | Path | None = None,
53
+ data: bytes | None = None,
52
54
  mime_type: Optional[str] = None,
53
55
  role: Literal["user", "assistant"] = "user",
54
- annotations: Optional[dict] = None,
56
+ annotations: Annotations | None = None,
55
57
  ) -> dict:
56
58
  """
57
59
  Create a message with image content.
@@ -96,7 +98,7 @@ def MCPFile(
96
98
  path: Union[str, Path],
97
99
  mime_type: Optional[str] = None,
98
100
  role: Literal["user", "assistant"] = "user",
99
- annotations: Optional[dict] = None,
101
+ annotations: Annotations | None = None,
100
102
  ) -> dict:
101
103
  """
102
104
  Create a message with an embedded resource from a file.
@@ -124,30 +126,28 @@ def MCPFile(
124
126
  binary_data = path.read_bytes()
125
127
  b64_data = base64.b64encode(binary_data).decode("ascii")
126
128
 
127
- resource = BlobResourceContents(uri=uri, blob=b64_data, mimeType=mime_type)
129
+ resource = BlobResourceContents(uri=AnyUrl(uri), blob=b64_data, mimeType=mime_type)
128
130
  else:
129
131
  # Read as text
130
132
  try:
131
133
  text_data = path.read_text(encoding="utf-8")
132
- resource = TextResourceContents(uri=uri, text=text_data, mimeType=mime_type)
134
+ resource = TextResourceContents(uri=AnyUrl(uri), text=text_data, mimeType=mime_type)
133
135
  except UnicodeDecodeError:
134
136
  # Fallback to binary if text read fails
135
137
  binary_data = path.read_bytes()
136
138
  b64_data = base64.b64encode(binary_data).decode("ascii")
137
139
  resource = BlobResourceContents(
138
- uri=uri, blob=b64_data, mimeType=mime_type or "application/octet-stream"
140
+ uri=AnyUrl(uri), blob=b64_data, mimeType=mime_type or "application/octet-stream"
139
141
  )
140
142
 
141
143
  return {
142
144
  "role": role,
143
- "content": EmbeddedResource(
144
- type="resource", resource=resource, annotations=annotations
145
- ),
145
+ "content": EmbeddedResource(type="resource", resource=resource, annotations=annotations),
146
146
  }
147
147
 
148
148
 
149
149
  def MCPPrompt(
150
- *content_items, role: Literal["user", "assistant"] = "user"
150
+ *content_items: Union[dict, str, Path, bytes], role: Literal["user", "assistant"] = "user"
151
151
  ) -> List[dict]:
152
152
  """
153
153
  Create one or more prompt messages with various content types.
@@ -172,7 +172,7 @@ def MCPPrompt(
172
172
  if isinstance(item, dict) and "role" in item and "content" in item:
173
173
  # Already a fully formed message
174
174
  result.append(item)
175
- elif isinstance(item, str) and not Path(item).exists():
175
+ elif isinstance(item, str):
176
176
  # Simple text content (that's not a file path)
177
177
  result.append(MCPText(item, role=role))
178
178
  elif isinstance(item, Path) or isinstance(item, str):
mcp_agent/core/prompt.py CHANGED
@@ -5,10 +5,11 @@ Prompt class for easily creating and working with MCP prompt content.
5
5
  from typing import List, Literal
6
6
 
7
7
  from mcp.types import PromptMessage
8
+
8
9
  from mcp_agent.mcp.prompt_message_multipart import PromptMessageMultipart
9
10
 
10
11
  # Import our content helper functions
11
- from .mcp_content import User, Assistant, MCPPrompt
12
+ from .mcp_content import Assistant, MCPPrompt, User
12
13
 
13
14
 
14
15
  class Prompt:
@@ -39,9 +40,7 @@ class Prompt:
39
40
  A PromptMessageMultipart with user role and the specified content
40
41
  """
41
42
  messages = User(*content_items)
42
- return PromptMessageMultipart(
43
- role="user", content=[msg["content"] for msg in messages]
44
- )
43
+ return PromptMessageMultipart(role="user", content=[msg["content"] for msg in messages])
45
44
 
46
45
  @classmethod
47
46
  def assistant(cls, *content_items) -> PromptMessageMultipart:
@@ -100,7 +99,7 @@ class Prompt:
100
99
  for item in messages:
101
100
  if isinstance(item, PromptMessageMultipart):
102
101
  # Convert PromptMessageMultipart to a list of PromptMessages
103
- result.extend(item.to_prompt_messages())
102
+ result.extend(item.from_multipart())
104
103
  elif isinstance(item, dict) and "role" in item and "content" in item:
105
104
  # Convert a single message dict to PromptMessage
106
105
  result.append(PromptMessage(**item))
@@ -114,9 +113,7 @@ class Prompt:
114
113
  return result
115
114
 
116
115
  @classmethod
117
- def from_multipart(
118
- cls, multipart: List[PromptMessageMultipart]
119
- ) -> List[PromptMessage]:
116
+ def from_multipart(cls, multipart: List[PromptMessageMultipart]) -> List[PromptMessage]:
120
117
  """
121
118
  Convert a list of PromptMessageMultipart objects to PromptMessages.
122
119
 
@@ -128,5 +125,5 @@ class Prompt:
128
125
  """
129
126
  result = []
130
127
  for mp in multipart:
131
- result.extend(mp.to_prompt_messages())
128
+ result.extend(mp.from_multipart())
132
129
  return result
@@ -2,8 +2,11 @@
2
2
  Request parameters definitions for LLM interactions.
3
3
  """
4
4
 
5
- from pydantic import Field
5
+ from typing import List
6
+
7
+ from mcp import SamplingMessage
6
8
  from mcp.types import CreateMessageRequestParams
9
+ from pydantic import Field
7
10
 
8
11
 
9
12
  class RequestParams(CreateMessageRequestParams):
@@ -11,7 +14,7 @@ class RequestParams(CreateMessageRequestParams):
11
14
  Parameters to configure the AugmentedLLM 'generate' requests.
12
15
  """
13
16
 
14
- messages: None = Field(exclude=True, default=None)
17
+ messages: List[SamplingMessage] = Field(exclude=True, default=[])
15
18
  """
16
19
  Ignored. 'messages' are removed from CreateMessageRequestParams
17
20
  to avoid confusion with the 'message' parameter on 'generate' method.
@@ -40,4 +43,4 @@ class RequestParams(CreateMessageRequestParams):
40
43
  """
41
44
  Whether to allow multiple tool calls per iteration.
42
45
  Also known as multi-step tool use.
43
- """
46
+ """