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.
- {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/METADATA +3 -4
- fast_agent_mcp-0.2.0.dist-info/RECORD +123 -0
- mcp_agent/__init__.py +75 -0
- mcp_agent/agents/agent.py +61 -415
- mcp_agent/agents/base_agent.py +522 -0
- mcp_agent/agents/workflow/__init__.py +1 -0
- mcp_agent/agents/workflow/chain_agent.py +173 -0
- mcp_agent/agents/workflow/evaluator_optimizer.py +362 -0
- mcp_agent/agents/workflow/orchestrator_agent.py +591 -0
- mcp_agent/{workflows/orchestrator → agents/workflow}/orchestrator_models.py +11 -21
- mcp_agent/agents/workflow/parallel_agent.py +182 -0
- mcp_agent/agents/workflow/router_agent.py +307 -0
- mcp_agent/app.py +15 -19
- mcp_agent/cli/commands/bootstrap.py +19 -38
- mcp_agent/cli/commands/config.py +4 -4
- mcp_agent/cli/commands/setup.py +7 -14
- mcp_agent/cli/main.py +7 -10
- mcp_agent/cli/terminal.py +3 -3
- mcp_agent/config.py +25 -40
- mcp_agent/context.py +12 -21
- mcp_agent/context_dependent.py +3 -5
- mcp_agent/core/agent_types.py +10 -7
- mcp_agent/core/direct_agent_app.py +179 -0
- mcp_agent/core/direct_decorators.py +443 -0
- mcp_agent/core/direct_factory.py +476 -0
- mcp_agent/core/enhanced_prompt.py +23 -55
- mcp_agent/core/exceptions.py +8 -8
- mcp_agent/core/fastagent.py +145 -371
- mcp_agent/core/interactive_prompt.py +424 -0
- mcp_agent/core/mcp_content.py +17 -17
- mcp_agent/core/prompt.py +6 -9
- mcp_agent/core/request_params.py +6 -3
- mcp_agent/core/validation.py +92 -18
- mcp_agent/executor/decorator_registry.py +9 -17
- mcp_agent/executor/executor.py +8 -17
- mcp_agent/executor/task_registry.py +2 -4
- mcp_agent/executor/temporal.py +19 -41
- mcp_agent/executor/workflow.py +3 -5
- mcp_agent/executor/workflow_signal.py +15 -21
- mcp_agent/human_input/handler.py +4 -7
- mcp_agent/human_input/types.py +2 -3
- mcp_agent/llm/__init__.py +2 -0
- mcp_agent/llm/augmented_llm.py +450 -0
- mcp_agent/llm/augmented_llm_passthrough.py +162 -0
- mcp_agent/llm/augmented_llm_playback.py +83 -0
- mcp_agent/llm/memory.py +103 -0
- mcp_agent/{workflows/llm → llm}/model_factory.py +22 -16
- mcp_agent/{workflows/llm → llm}/prompt_utils.py +1 -3
- mcp_agent/llm/providers/__init__.py +8 -0
- mcp_agent/{workflows/llm → llm/providers}/anthropic_utils.py +8 -25
- mcp_agent/{workflows/llm → llm/providers}/augmented_llm_anthropic.py +56 -194
- mcp_agent/llm/providers/augmented_llm_deepseek.py +53 -0
- mcp_agent/{workflows/llm → llm/providers}/augmented_llm_openai.py +99 -190
- mcp_agent/{workflows/llm → llm}/providers/multipart_converter_anthropic.py +72 -71
- mcp_agent/{workflows/llm → llm}/providers/multipart_converter_openai.py +65 -71
- mcp_agent/{workflows/llm → llm}/providers/openai_multipart.py +16 -44
- mcp_agent/{workflows/llm → llm/providers}/openai_utils.py +4 -4
- mcp_agent/{workflows/llm → llm}/providers/sampling_converter_anthropic.py +9 -11
- mcp_agent/{workflows/llm → llm}/providers/sampling_converter_openai.py +8 -12
- mcp_agent/{workflows/llm → llm}/sampling_converter.py +3 -31
- mcp_agent/llm/sampling_format_converter.py +37 -0
- mcp_agent/logging/events.py +1 -5
- mcp_agent/logging/json_serializer.py +7 -6
- mcp_agent/logging/listeners.py +20 -23
- mcp_agent/logging/logger.py +17 -19
- mcp_agent/logging/rich_progress.py +10 -8
- mcp_agent/logging/tracing.py +4 -6
- mcp_agent/logging/transport.py +22 -22
- mcp_agent/mcp/gen_client.py +1 -3
- mcp_agent/mcp/interfaces.py +117 -110
- mcp_agent/mcp/logger_textio.py +97 -0
- mcp_agent/mcp/mcp_agent_client_session.py +7 -7
- mcp_agent/mcp/mcp_agent_server.py +8 -8
- mcp_agent/mcp/mcp_aggregator.py +102 -143
- mcp_agent/mcp/mcp_connection_manager.py +20 -27
- mcp_agent/mcp/prompt_message_multipart.py +68 -16
- mcp_agent/mcp/prompt_render.py +77 -0
- mcp_agent/mcp/prompt_serialization.py +30 -48
- mcp_agent/mcp/prompts/prompt_constants.py +18 -0
- mcp_agent/mcp/prompts/prompt_helpers.py +327 -0
- mcp_agent/mcp/prompts/prompt_load.py +109 -0
- mcp_agent/mcp/prompts/prompt_server.py +155 -195
- mcp_agent/mcp/prompts/prompt_template.py +35 -66
- mcp_agent/mcp/resource_utils.py +7 -14
- mcp_agent/mcp/sampling.py +17 -17
- mcp_agent/mcp_server/agent_server.py +13 -17
- mcp_agent/mcp_server_registry.py +13 -22
- mcp_agent/resources/examples/{workflows → in_dev}/agent_build.py +3 -2
- mcp_agent/resources/examples/in_dev/slides.py +110 -0
- mcp_agent/resources/examples/internal/agent.py +6 -3
- mcp_agent/resources/examples/internal/fastagent.config.yaml +8 -2
- mcp_agent/resources/examples/internal/job.py +2 -1
- mcp_agent/resources/examples/internal/prompt_category.py +1 -1
- mcp_agent/resources/examples/internal/prompt_sizing.py +3 -5
- mcp_agent/resources/examples/internal/sizer.py +2 -1
- mcp_agent/resources/examples/internal/social.py +2 -1
- mcp_agent/resources/examples/prompting/agent.py +2 -1
- mcp_agent/resources/examples/prompting/image_server.py +4 -8
- mcp_agent/resources/examples/prompting/work_with_image.py +19 -0
- mcp_agent/ui/console_display.py +16 -20
- fast_agent_mcp-0.1.12.dist-info/RECORD +0 -161
- mcp_agent/core/agent_app.py +0 -646
- mcp_agent/core/agent_utils.py +0 -71
- mcp_agent/core/decorators.py +0 -455
- mcp_agent/core/factory.py +0 -463
- mcp_agent/core/proxies.py +0 -269
- mcp_agent/core/types.py +0 -24
- mcp_agent/eval/__init__.py +0 -0
- mcp_agent/mcp/stdio.py +0 -111
- mcp_agent/resources/examples/data-analysis/analysis-campaign.py +0 -188
- mcp_agent/resources/examples/data-analysis/analysis.py +0 -65
- mcp_agent/resources/examples/data-analysis/fastagent.config.yaml +0 -41
- mcp_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +0 -1471
- mcp_agent/resources/examples/mcp_researcher/researcher-eval.py +0 -53
- mcp_agent/resources/examples/researcher/fastagent.config.yaml +0 -66
- mcp_agent/resources/examples/researcher/researcher-eval.py +0 -53
- mcp_agent/resources/examples/researcher/researcher-imp.py +0 -190
- mcp_agent/resources/examples/researcher/researcher.py +0 -38
- mcp_agent/resources/examples/workflows/chaining.py +0 -44
- mcp_agent/resources/examples/workflows/evaluator.py +0 -78
- mcp_agent/resources/examples/workflows/fastagent.config.yaml +0 -24
- mcp_agent/resources/examples/workflows/human_input.py +0 -25
- mcp_agent/resources/examples/workflows/orchestrator.py +0 -73
- mcp_agent/resources/examples/workflows/parallel.py +0 -78
- mcp_agent/resources/examples/workflows/router.py +0 -53
- mcp_agent/resources/examples/workflows/sse.py +0 -23
- mcp_agent/telemetry/__init__.py +0 -0
- mcp_agent/telemetry/usage_tracking.py +0 -18
- mcp_agent/workflows/__init__.py +0 -0
- mcp_agent/workflows/embedding/__init__.py +0 -0
- mcp_agent/workflows/embedding/embedding_base.py +0 -61
- mcp_agent/workflows/embedding/embedding_cohere.py +0 -49
- mcp_agent/workflows/embedding/embedding_openai.py +0 -46
- mcp_agent/workflows/evaluator_optimizer/__init__.py +0 -0
- mcp_agent/workflows/evaluator_optimizer/evaluator_optimizer.py +0 -481
- mcp_agent/workflows/intent_classifier/__init__.py +0 -0
- mcp_agent/workflows/intent_classifier/intent_classifier_base.py +0 -120
- mcp_agent/workflows/intent_classifier/intent_classifier_embedding.py +0 -134
- mcp_agent/workflows/intent_classifier/intent_classifier_embedding_cohere.py +0 -45
- mcp_agent/workflows/intent_classifier/intent_classifier_embedding_openai.py +0 -45
- mcp_agent/workflows/intent_classifier/intent_classifier_llm.py +0 -161
- mcp_agent/workflows/intent_classifier/intent_classifier_llm_anthropic.py +0 -60
- mcp_agent/workflows/intent_classifier/intent_classifier_llm_openai.py +0 -60
- mcp_agent/workflows/llm/__init__.py +0 -0
- mcp_agent/workflows/llm/augmented_llm.py +0 -753
- mcp_agent/workflows/llm/augmented_llm_passthrough.py +0 -241
- mcp_agent/workflows/llm/augmented_llm_playback.py +0 -109
- mcp_agent/workflows/llm/providers/__init__.py +0 -8
- mcp_agent/workflows/llm/sampling_format_converter.py +0 -22
- mcp_agent/workflows/orchestrator/__init__.py +0 -0
- mcp_agent/workflows/orchestrator/orchestrator.py +0 -578
- mcp_agent/workflows/parallel/__init__.py +0 -0
- mcp_agent/workflows/parallel/fan_in.py +0 -350
- mcp_agent/workflows/parallel/fan_out.py +0 -187
- mcp_agent/workflows/parallel/parallel_llm.py +0 -166
- mcp_agent/workflows/router/__init__.py +0 -0
- mcp_agent/workflows/router/router_base.py +0 -368
- mcp_agent/workflows/router/router_embedding.py +0 -240
- mcp_agent/workflows/router/router_embedding_cohere.py +0 -59
- mcp_agent/workflows/router/router_embedding_openai.py +0 -59
- mcp_agent/workflows/router/router_llm.py +0 -320
- mcp_agent/workflows/swarm/__init__.py +0 -0
- mcp_agent/workflows/swarm/swarm.py +0 -320
- mcp_agent/workflows/swarm/swarm_anthropic.py +0 -42
- mcp_agent/workflows/swarm/swarm_openai.py +0 -41
- {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/licenses/LICENSE +0 -0
- /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]")
|
mcp_agent/core/mcp_content.py
CHANGED
@@ -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
|
10
|
+
from typing import Any, List, Literal, Optional, Union
|
11
11
|
|
12
12
|
from mcp.types import (
|
13
|
-
|
14
|
-
|
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:
|
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:
|
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:
|
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:
|
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)
|
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
|
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.
|
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.
|
128
|
+
result.extend(mp.from_multipart())
|
132
129
|
return result
|
mcp_agent/core/request_params.py
CHANGED
@@ -2,8 +2,11 @@
|
|
2
2
|
Request parameters definitions for LLM interactions.
|
3
3
|
"""
|
4
4
|
|
5
|
-
from
|
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:
|
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
|
+
"""
|