fast-agent-mcp 0.2.40__py3-none-any.whl → 0.2.41__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.

Potentially problematic release.


This version of fast-agent-mcp might be problematic. Click here for more details.

Files changed (41) hide show
  1. {fast_agent_mcp-0.2.40.dist-info → fast_agent_mcp-0.2.41.dist-info}/METADATA +1 -1
  2. {fast_agent_mcp-0.2.40.dist-info → fast_agent_mcp-0.2.41.dist-info}/RECORD +41 -37
  3. {fast_agent_mcp-0.2.40.dist-info → fast_agent_mcp-0.2.41.dist-info}/entry_points.txt +2 -2
  4. mcp_agent/cli/__main__.py +29 -3
  5. mcp_agent/cli/commands/check_config.py +140 -81
  6. mcp_agent/cli/commands/go.py +151 -38
  7. mcp_agent/cli/commands/quickstart.py +6 -2
  8. mcp_agent/cli/commands/server_helpers.py +106 -0
  9. mcp_agent/cli/constants.py +25 -0
  10. mcp_agent/cli/main.py +1 -1
  11. mcp_agent/config.py +94 -44
  12. mcp_agent/core/agent_app.py +104 -15
  13. mcp_agent/core/agent_types.py +1 -0
  14. mcp_agent/core/direct_decorators.py +9 -0
  15. mcp_agent/core/direct_factory.py +18 -4
  16. mcp_agent/core/enhanced_prompt.py +165 -13
  17. mcp_agent/core/fastagent.py +4 -0
  18. mcp_agent/core/interactive_prompt.py +37 -37
  19. mcp_agent/core/usage_display.py +11 -1
  20. mcp_agent/core/validation.py +21 -2
  21. mcp_agent/human_input/elicitation_form.py +53 -21
  22. mcp_agent/llm/augmented_llm.py +28 -9
  23. mcp_agent/llm/augmented_llm_silent.py +48 -0
  24. mcp_agent/llm/model_database.py +20 -0
  25. mcp_agent/llm/model_factory.py +12 -0
  26. mcp_agent/llm/provider_key_manager.py +22 -8
  27. mcp_agent/llm/provider_types.py +19 -12
  28. mcp_agent/llm/providers/augmented_llm_anthropic.py +7 -2
  29. mcp_agent/llm/providers/augmented_llm_azure.py +7 -1
  30. mcp_agent/llm/providers/augmented_llm_google_native.py +4 -1
  31. mcp_agent/llm/providers/augmented_llm_openai.py +9 -2
  32. mcp_agent/llm/providers/augmented_llm_xai.py +38 -0
  33. mcp_agent/llm/usage_tracking.py +28 -3
  34. mcp_agent/mcp/mcp_agent_client_session.py +2 -0
  35. mcp_agent/mcp/mcp_aggregator.py +38 -44
  36. mcp_agent/mcp/sampling.py +15 -11
  37. mcp_agent/resources/examples/mcp/elicitations/forms_demo.py +0 -6
  38. mcp_agent/resources/examples/workflows/router.py +9 -0
  39. mcp_agent/ui/console_display.py +125 -13
  40. {fast_agent_mcp-0.2.40.dist-info → fast_agent_mcp-0.2.41.dist-info}/WHEEL +0 -0
  41. {fast_agent_mcp-0.2.40.dist-info → fast_agent_mcp-0.2.41.dist-info}/licenses/LICENSE +0 -0
@@ -9,6 +9,7 @@ from mcp.types import PromptMessage
9
9
  from rich import print as rich_print
10
10
 
11
11
  from mcp_agent.agents.agent import Agent
12
+ from mcp_agent.core.agent_types import AgentType
12
13
  from mcp_agent.core.interactive_prompt import InteractivePrompt
13
14
  from mcp_agent.mcp.prompt_message_multipart import PromptMessageMultipart
14
15
  from mcp_agent.progress_display import progress_display
@@ -234,13 +235,14 @@ class AgentApp:
234
235
  """
235
236
  return await self.interactive(agent_name=agent_name, default_prompt=default_prompt)
236
237
 
237
- async def interactive(self, agent_name: str | None = None, default_prompt: str = "") -> str:
238
+ async def interactive(self, agent_name: str | None = None, default_prompt: str = "", pretty_print_parallel: bool = False) -> str:
238
239
  """
239
240
  Interactive prompt for sending messages with advanced features.
240
241
 
241
242
  Args:
242
243
  agent_name: Optional target agent name (uses default if not specified)
243
244
  default: Default message to use when user presses enter
245
+ pretty_print_parallel: Enable clean parallel results display for parallel agents
244
246
 
245
247
  Returns:
246
248
  The result of the interactive session
@@ -275,10 +277,18 @@ class AgentApp:
275
277
  # Define the wrapper for send function
276
278
  async def send_wrapper(message, agent_name):
277
279
  result = await self.send(message, agent_name)
278
-
280
+
281
+ # Show parallel results if enabled and this is a parallel agent
282
+ if pretty_print_parallel:
283
+ agent = self._agents.get(agent_name)
284
+ if agent and agent.agent_type == AgentType.PARALLEL:
285
+ from mcp_agent.ui.console_display import ConsoleDisplay
286
+ display = ConsoleDisplay(config=None)
287
+ display.show_parallel_results(agent)
288
+
279
289
  # Show usage info after each turn if progress display is enabled
280
290
  self._show_turn_usage(agent_name)
281
-
291
+
282
292
  return result
283
293
 
284
294
  # Start the prompt loop with the agent name (not the agent object)
@@ -293,32 +303,111 @@ class AgentApp:
293
303
  def _show_turn_usage(self, agent_name: str) -> None:
294
304
  """Show subtle usage information after each turn."""
295
305
  agent = self._agents.get(agent_name)
296
- if not agent or not agent.usage_accumulator:
306
+ if not agent:
297
307
  return
298
-
308
+
309
+ # Check if this is a parallel agent
310
+ if agent.agent_type == AgentType.PARALLEL:
311
+ self._show_parallel_agent_usage(agent)
312
+ else:
313
+ self._show_regular_agent_usage(agent)
314
+
315
+ def _show_regular_agent_usage(self, agent) -> None:
316
+ """Show usage for a regular (non-parallel) agent."""
317
+ usage_info = self._format_agent_usage(agent)
318
+ if usage_info:
319
+ with progress_display.paused():
320
+ rich_print(
321
+ f"[dim]Last turn: {usage_info['display_text']}[/dim]{usage_info['cache_suffix']}"
322
+ )
323
+
324
+ def _show_parallel_agent_usage(self, parallel_agent) -> None:
325
+ """Show usage for a parallel agent and its children."""
326
+ # Collect usage from all child agents
327
+ child_usage_data = []
328
+ total_input = 0
329
+ total_output = 0
330
+ total_tool_calls = 0
331
+
332
+ # Get usage from fan-out agents
333
+ if hasattr(parallel_agent, "fan_out_agents") and parallel_agent.fan_out_agents:
334
+ for child_agent in parallel_agent.fan_out_agents:
335
+ usage_info = self._format_agent_usage(child_agent)
336
+ if usage_info:
337
+ child_usage_data.append({**usage_info, "name": child_agent.name})
338
+ total_input += usage_info["input_tokens"]
339
+ total_output += usage_info["output_tokens"]
340
+ total_tool_calls += usage_info["tool_calls"]
341
+
342
+ # Get usage from fan-in agent
343
+ if hasattr(parallel_agent, "fan_in_agent") and parallel_agent.fan_in_agent:
344
+ usage_info = self._format_agent_usage(parallel_agent.fan_in_agent)
345
+ if usage_info:
346
+ child_usage_data.append({**usage_info, "name": parallel_agent.fan_in_agent.name})
347
+ total_input += usage_info["input_tokens"]
348
+ total_output += usage_info["output_tokens"]
349
+ total_tool_calls += usage_info["tool_calls"]
350
+
351
+ if not child_usage_data:
352
+ return
353
+
354
+ # Show aggregated usage for parallel agent (no context percentage)
355
+ with progress_display.paused():
356
+ tool_info = f", {total_tool_calls} tool calls" if total_tool_calls > 0 else ""
357
+ rich_print(
358
+ f"[dim]Last turn (parallel): {total_input:,} Input, {total_output:,} Output{tool_info}[/dim]"
359
+ )
360
+
361
+ # Show individual child agent usage
362
+ for i, usage_data in enumerate(child_usage_data):
363
+ is_last = i == len(child_usage_data) - 1
364
+ prefix = "└─" if is_last else "├─"
365
+ rich_print(
366
+ f"[dim] {prefix} {usage_data['name']}: {usage_data['display_text']}[/dim]{usage_data['cache_suffix']}"
367
+ )
368
+
369
+ def _format_agent_usage(self, agent) -> Optional[Dict]:
370
+ """Format usage information for a single agent."""
371
+ if not agent or not agent.usage_accumulator:
372
+ return None
373
+
299
374
  # Get the last turn's usage (if any)
300
375
  turns = agent.usage_accumulator.turns
301
376
  if not turns:
302
- return
303
-
377
+ return None
378
+
304
379
  last_turn = turns[-1]
305
380
  input_tokens = last_turn.display_input_tokens
306
381
  output_tokens = last_turn.output_tokens
307
-
382
+
308
383
  # Build cache indicators with bright colors
309
384
  cache_indicators = ""
310
385
  if last_turn.cache_usage.cache_write_tokens > 0:
311
386
  cache_indicators += "[bright_yellow]^[/bright_yellow]"
312
- if last_turn.cache_usage.cache_read_tokens > 0 or last_turn.cache_usage.cache_hit_tokens > 0:
387
+ if (
388
+ last_turn.cache_usage.cache_read_tokens > 0
389
+ or last_turn.cache_usage.cache_hit_tokens > 0
390
+ ):
313
391
  cache_indicators += "[bright_green]*[/bright_green]"
314
-
392
+
315
393
  # Build context percentage - get from accumulator, not individual turn
316
394
  context_info = ""
317
395
  context_percentage = agent.usage_accumulator.context_usage_percentage
318
396
  if context_percentage is not None:
319
397
  context_info = f" ({context_percentage:.1f}%)"
320
-
321
- # Show subtle usage line - pause progress display to ensure visibility
322
- with progress_display.paused():
323
- cache_suffix = f" {cache_indicators}" if cache_indicators else ""
324
- rich_print(f"[dim]Last turn: {input_tokens:,} Input, {output_tokens:,} Output{context_info}[/dim]{cache_suffix}")
398
+
399
+ # Build tool call info
400
+ tool_info = f", {last_turn.tool_calls} tool calls" if last_turn.tool_calls > 0 else ""
401
+
402
+ # Build display text
403
+ display_text = f"{input_tokens:,} Input, {output_tokens:,} Output{tool_info}{context_info}"
404
+ cache_suffix = f" {cache_indicators}" if cache_indicators else ""
405
+
406
+ return {
407
+ "input_tokens": input_tokens,
408
+ "output_tokens": output_tokens,
409
+ "tool_calls": last_turn.tool_calls,
410
+ "context_percentage": context_percentage,
411
+ "display_text": display_text,
412
+ "cache_suffix": cache_suffix,
413
+ }
@@ -38,6 +38,7 @@ class AgentConfig:
38
38
  agent_type: AgentType = AgentType.BASIC
39
39
  default: bool = False
40
40
  elicitation_handler: ElicitationFnT | None = None
41
+ api_key: str | None = None
41
42
 
42
43
  def __post_init__(self):
43
44
  """Ensure default_request_params exists with proper history setting"""
@@ -138,6 +138,7 @@ def _decorator_impl(
138
138
  human_input=human_input,
139
139
  default=default,
140
140
  elicitation_handler=extra_kwargs.get("elicitation_handler"),
141
+ api_key=extra_kwargs.get("api_key"),
141
142
  )
142
143
 
143
144
  # Update request params if provided
@@ -182,6 +183,7 @@ def agent(
182
183
  human_input: bool = False,
183
184
  default: bool = False,
184
185
  elicitation_handler: Optional[ElicitationFnT] = None,
186
+ api_key: str | None = None,
185
187
  ) -> Callable[[AgentCallable[P, R]], DecoratedAgentProtocol[P, R]]:
186
188
  """
187
189
  Decorator to create and register a standard agent with type-safe signature.
@@ -215,6 +217,7 @@ def agent(
215
217
  human_input=human_input,
216
218
  default=default,
217
219
  elicitation_handler=elicitation_handler,
220
+ api_key=api_key,
218
221
  )
219
222
 
220
223
 
@@ -232,6 +235,7 @@ def custom(
232
235
  human_input: bool = False,
233
236
  default: bool = False,
234
237
  elicitation_handler: Optional[ElicitationFnT] = None,
238
+ api_key: str | None = None,
235
239
  ) -> Callable[[AgentCallable[P, R]], DecoratedAgentProtocol[P, R]]:
236
240
  """
237
241
  Decorator to create and register a standard agent with type-safe signature.
@@ -265,6 +269,7 @@ def custom(
265
269
  agent_class=cls,
266
270
  default=default,
267
271
  elicitation_handler=elicitation_handler,
272
+ api_key=api_key,
268
273
  )
269
274
 
270
275
 
@@ -288,6 +293,7 @@ def orchestrator(
288
293
  plan_type: Literal["full", "iterative"] = "full",
289
294
  plan_iterations: int = 5,
290
295
  default: bool = False,
296
+ api_key: str | None = None,
291
297
  ) -> Callable[[AgentCallable[P, R]], DecoratedOrchestratorProtocol[P, R]]:
292
298
  """
293
299
  Decorator to create and register an orchestrator agent with type-safe signature.
@@ -326,6 +332,7 @@ def orchestrator(
326
332
  plan_type=plan_type,
327
333
  plan_iterations=plan_iterations,
328
334
  default=default,
335
+ api_key=api_key,
329
336
  ),
330
337
  )
331
338
 
@@ -345,6 +352,7 @@ def router(
345
352
  elicitation_handler: Optional[
346
353
  ElicitationFnT
347
354
  ] = None, ## exclude from docs, decide whether allowable
355
+ api_key: str | None = None,
348
356
  ) -> Callable[[AgentCallable[P, R]], DecoratedRouterProtocol[P, R]]:
349
357
  """
350
358
  Decorator to create and register a router agent with type-safe signature.
@@ -383,6 +391,7 @@ def router(
383
391
  default=default,
384
392
  router_agents=agents,
385
393
  elicitation_handler=elicitation_handler,
394
+ api_key=api_key,
386
395
  ),
387
396
  )
388
397
 
@@ -150,7 +150,11 @@ async def create_agents_by_type(
150
150
 
151
151
  # Attach LLM to the agent
152
152
  llm_factory = model_factory_func(model=config.model)
153
- await agent.attach_llm(llm_factory, request_params=config.default_request_params)
153
+ await agent.attach_llm(
154
+ llm_factory,
155
+ request_params=config.default_request_params,
156
+ api_key=config.api_key
157
+ )
154
158
  result_agents[name] = agent
155
159
 
156
160
  elif agent_type == AgentType.CUSTOM:
@@ -165,7 +169,11 @@ async def create_agents_by_type(
165
169
 
166
170
  # Attach LLM to the agent
167
171
  llm_factory = model_factory_func(model=config.model)
168
- await agent.attach_llm(llm_factory, request_params=config.default_request_params)
172
+ await agent.attach_llm(
173
+ llm_factory,
174
+ request_params=config.default_request_params,
175
+ api_key=config.api_key
176
+ )
169
177
  result_agents[name] = agent
170
178
 
171
179
  elif agent_type == AgentType.ORCHESTRATOR:
@@ -200,7 +208,9 @@ async def create_agents_by_type(
200
208
  # Attach LLM to the orchestrator
201
209
  llm_factory = model_factory_func(model=config.model)
202
210
  await orchestrator.attach_llm(
203
- llm_factory, request_params=config.default_request_params
211
+ llm_factory,
212
+ request_params=config.default_request_params,
213
+ api_key=config.api_key
204
214
  )
205
215
 
206
216
  result_agents[name] = orchestrator
@@ -261,7 +271,11 @@ async def create_agents_by_type(
261
271
 
262
272
  # Attach LLM to the router
263
273
  llm_factory = model_factory_func(model=config.model)
264
- await router.attach_llm(llm_factory, request_params=config.default_request_params)
274
+ await router.attach_llm(
275
+ llm_factory,
276
+ request_params=config.default_request_params,
277
+ api_key=config.api_key
278
+ )
265
279
  result_agents[name] = router
266
280
 
267
281
  elif agent_type == AgentType.CHAIN:
@@ -71,27 +71,178 @@ async def _display_agent_info_helper(agent_name: str, agent_provider: object) ->
71
71
  prompts_dict = await agent.list_prompts()
72
72
  prompt_count = sum(len(prompts) for prompts in prompts_dict.values()) if prompts_dict else 0
73
73
 
74
- # Display with proper pluralization and subdued formatting
75
- if server_count == 0:
76
- rich_print(
77
- f"[dim]Agent [/dim][blue]{agent_name}[/blue][dim]: No MCP Servers attached[/dim]"
78
- )
74
+ # Handle different agent types
75
+ if agent.agent_type == AgentType.PARALLEL:
76
+ # Count child agents for parallel agents
77
+ child_count = 0
78
+ if hasattr(agent, "fan_out_agents") and agent.fan_out_agents:
79
+ child_count += len(agent.fan_out_agents)
80
+ if hasattr(agent, "fan_in_agent") and agent.fan_in_agent:
81
+ child_count += 1
82
+
83
+ if child_count > 0:
84
+ child_word = "child agent" if child_count == 1 else "child agents"
85
+ rich_print(
86
+ f"[dim]Agent [/dim][blue]{agent_name}[/blue][dim]:[/dim] {child_count:,}[dim] {child_word}[/dim]"
87
+ )
88
+ elif agent.agent_type == AgentType.ROUTER:
89
+ # Count child agents for router agents
90
+ child_count = 0
91
+ if hasattr(agent, "routing_agents") and agent.routing_agents:
92
+ child_count = len(agent.routing_agents)
93
+ elif hasattr(agent, "agents") and agent.agents:
94
+ child_count = len(agent.agents)
95
+
96
+ if child_count > 0:
97
+ child_word = "child agent" if child_count == 1 else "child agents"
98
+ rich_print(
99
+ f"[dim]Agent [/dim][blue]{agent_name}[/blue][dim]:[/dim] {child_count:,}[dim] {child_word}[/dim]"
100
+ )
79
101
  else:
102
+ # For regular agents, only display if they have MCP servers attached
103
+ if server_count > 0:
104
+ # Pluralization helpers
105
+ server_word = "Server" if server_count == 1 else "Servers"
106
+ tool_word = "tool" if tool_count == 1 else "tools"
107
+ prompt_word = "prompt" if prompt_count == 1 else "prompts"
108
+
109
+ rich_print(
110
+ f"[dim]Agent [/dim][blue]{agent_name}[/blue][dim]:[/dim] {server_count:,}[dim] MCP {server_word}, [/dim]{tool_count:,}[dim] {tool_word}, [/dim]{prompt_count:,}[dim] {prompt_word} available[/dim]"
111
+ )
112
+
113
+ # Mark as shown
114
+ _agent_info_shown.add(agent_name)
115
+
116
+ except Exception:
117
+ # Silently ignore errors to not disrupt the user experience
118
+ pass
119
+
120
+
121
+ async def _display_all_agents_with_hierarchy(available_agents: List[str], agent_provider) -> None:
122
+ """Display all agents with tree structure for workflow agents."""
123
+ # Track which agents are children to avoid displaying them twice
124
+ child_agents = set()
125
+
126
+ # First pass: identify all child agents
127
+ for agent_name in available_agents:
128
+ try:
129
+ if hasattr(agent_provider, "_agent"):
130
+ agent = agent_provider._agent(agent_name)
131
+ else:
132
+ agent = agent_provider
133
+
134
+ if agent.agent_type == AgentType.PARALLEL:
135
+ if hasattr(agent, "fan_out_agents") and agent.fan_out_agents:
136
+ for child_agent in agent.fan_out_agents:
137
+ child_agents.add(child_agent.name)
138
+ if hasattr(agent, "fan_in_agent") and agent.fan_in_agent:
139
+ child_agents.add(agent.fan_in_agent.name)
140
+ elif agent.agent_type == AgentType.ROUTER:
141
+ if hasattr(agent, "routing_agents") and agent.routing_agents:
142
+ for child_agent in agent.routing_agents:
143
+ child_agents.add(child_agent.name)
144
+ elif hasattr(agent, "agents") and agent.agents:
145
+ for child_agent in agent.agents:
146
+ child_agents.add(child_agent.name)
147
+ except Exception:
148
+ continue
149
+
150
+ # Second pass: display agents (parents with children, standalone agents without children)
151
+ for agent_name in sorted(available_agents):
152
+ # Skip if this agent is a child of another agent
153
+ if agent_name in child_agents:
154
+ continue
155
+
156
+ try:
157
+ if hasattr(agent_provider, "_agent"):
158
+ agent = agent_provider._agent(agent_name)
159
+ else:
160
+ agent = agent_provider
161
+
162
+ # Display parent agent
163
+ await _display_agent_info_helper(agent_name, agent_provider)
164
+
165
+ # If it's a workflow agent, display its children
166
+ if agent.agent_type == AgentType.PARALLEL:
167
+ await _display_parallel_children(agent, agent_provider)
168
+ elif agent.agent_type == AgentType.ROUTER:
169
+ await _display_router_children(agent, agent_provider)
170
+
171
+ except Exception:
172
+ continue
173
+
174
+
175
+ async def _display_parallel_children(parallel_agent, agent_provider) -> None:
176
+ """Display child agents of a parallel agent in tree format."""
177
+ children = []
178
+
179
+ # Collect fan-out agents
180
+ if hasattr(parallel_agent, "fan_out_agents") and parallel_agent.fan_out_agents:
181
+ for child_agent in parallel_agent.fan_out_agents:
182
+ children.append(child_agent)
183
+
184
+ # Collect fan-in agent
185
+ if hasattr(parallel_agent, "fan_in_agent") and parallel_agent.fan_in_agent:
186
+ children.append(parallel_agent.fan_in_agent)
187
+
188
+ # Display children with tree formatting
189
+ for i, child_agent in enumerate(children):
190
+ is_last = i == len(children) - 1
191
+ prefix = "└─" if is_last else "├─"
192
+ await _display_child_agent_info(child_agent, prefix, agent_provider)
193
+
194
+
195
+ async def _display_router_children(router_agent, agent_provider) -> None:
196
+ """Display child agents of a router agent in tree format."""
197
+ children = []
198
+
199
+ # Collect routing agents
200
+ if hasattr(router_agent, "routing_agents") and router_agent.routing_agents:
201
+ children = router_agent.routing_agents
202
+ elif hasattr(router_agent, "agents") and router_agent.agents:
203
+ children = router_agent.agents
204
+
205
+ # Display children with tree formatting
206
+ for i, child_agent in enumerate(children):
207
+ is_last = i == len(children) - 1
208
+ prefix = "└─" if is_last else "├─"
209
+ await _display_child_agent_info(child_agent, prefix, agent_provider)
210
+
211
+
212
+ async def _display_child_agent_info(child_agent, prefix: str, agent_provider) -> None:
213
+ """Display info for a child agent with tree prefix."""
214
+ try:
215
+ # Get counts for child agent
216
+ servers = await child_agent.list_servers()
217
+ server_count = len(servers) if servers else 0
218
+
219
+ tools_result = await child_agent.list_tools()
220
+ tool_count = (
221
+ len(tools_result.tools) if tools_result and hasattr(tools_result, "tools") else 0
222
+ )
223
+
224
+ prompts_dict = await child_agent.list_prompts()
225
+ prompt_count = sum(len(prompts) for prompts in prompts_dict.values()) if prompts_dict else 0
226
+
227
+ # Only display if child has MCP servers
228
+ if server_count > 0:
80
229
  # Pluralization helpers
81
230
  server_word = "Server" if server_count == 1 else "Servers"
82
231
  tool_word = "tool" if tool_count == 1 else "tools"
83
232
  prompt_word = "prompt" if prompt_count == 1 else "prompts"
84
233
 
85
234
  rich_print(
86
- f"[dim]Agent [/dim][blue]{agent_name}[/blue][dim]:[/dim] {server_count:,}[dim] MCP {server_word}, [/dim]{tool_count:,}[dim] {tool_word}, [/dim]{prompt_count:,}[dim] {prompt_word} available[/dim]"
235
+ f"[dim] {prefix} [/dim][blue]{child_agent.name}[/blue][dim]:[/dim] {server_count:,}[dim] MCP {server_word}, [/dim]{tool_count:,}[dim] {tool_word}, [/dim]{prompt_count:,}[dim] {prompt_word} available[/dim]"
236
+ )
237
+ else:
238
+ # Show child even without MCP servers for context
239
+ rich_print(
240
+ f"[dim] {prefix} [/dim][blue]{child_agent.name}[/blue][dim]: No MCP Servers[/dim]"
87
241
  )
88
-
89
- # Mark as shown
90
- _agent_info_shown.add(agent_name)
91
242
 
92
243
  except Exception:
93
- # Silently ignore errors to not disrupt the user experience
94
- pass
244
+ # Fallback: just show the name
245
+ rich_print(f"[dim] {prefix} [/dim][blue]{child_agent.name}[/blue]")
95
246
 
96
247
 
97
248
  class AgentCompleter(Completer):
@@ -429,12 +580,13 @@ async def get_enhanced_input(
429
580
  rich_print("[dim]Type /help for commands. Ctrl+T toggles multiline mode.[/dim]")
430
581
  else:
431
582
  rich_print(
432
- "[dim]Type /help for commands, @agent to switch agent. Ctrl+T toggles multiline mode.[/dim]\n"
583
+ "[dim]Type '/' for commands, '@' to switch agent. Ctrl+T multiline, CTRL+E external editor.[/dim]\n"
433
584
  )
434
585
 
435
586
  # Display agent info right after help text if agent_provider is available
436
587
  if agent_provider and not is_human_input:
437
- await _display_agent_info_helper(agent_name, agent_provider)
588
+ # Display info for all available agents with tree structure for workflows
589
+ await _display_all_agents_with_hierarchy(available_agents, agent_provider)
438
590
 
439
591
  rich_print()
440
592
  help_message_shown = True
@@ -56,6 +56,7 @@ from mcp_agent.core.exceptions import (
56
56
  )
57
57
  from mcp_agent.core.usage_display import display_usage_report
58
58
  from mcp_agent.core.validation import (
59
+ validate_provider_keys_post_creation,
59
60
  validate_server_references,
60
61
  validate_workflow_references,
61
62
  )
@@ -313,6 +314,9 @@ class FastAgent:
313
314
  self.agents,
314
315
  model_factory_func,
315
316
  )
317
+
318
+ # Validate API keys after agent creation
319
+ validate_provider_keys_post_creation(active_agents)
316
320
 
317
321
  # Create a wrapper with all agents for simplified access
318
322
  wrapper = AgentApp(active_agents)