fast-agent-mcp 0.0.14__py3-none-any.whl → 0.0.15__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.
@@ -10,291 +10,57 @@ from typing import (
10
10
  Callable,
11
11
  TypeVar,
12
12
  Any,
13
- Union,
14
- TypeAlias,
15
13
  Literal,
16
14
  )
17
- from enum import Enum
18
15
  import yaml
19
16
  import argparse
20
17
  from contextlib import asynccontextmanager
21
18
 
22
- from mcp_agent.core.exceptions import (
23
- AgentConfigError,
24
- CircularDependencyError,
25
- ModelConfigError,
26
- PromptExitError,
27
- ServerConfigError,
28
- ProviderKeyError,
29
- ServerInitializationError,
30
- )
31
-
32
19
  from mcp_agent.app import MCPApp
33
20
  from mcp_agent.agents.agent import Agent, AgentConfig
34
21
  from mcp_agent.context_dependent import ContextDependent
22
+ from mcp_agent.config import Settings
35
23
  from mcp_agent.event_progress import ProgressAction
36
- from mcp_agent.workflows.orchestrator.orchestrator import Orchestrator
37
- from mcp_agent.workflows.parallel.parallel_llm import ParallelLLM
38
24
  from mcp_agent.workflows.evaluator_optimizer.evaluator_optimizer import (
39
25
  EvaluatorOptimizerLLM,
40
26
  QualityRating,
41
27
  )
42
- from mcp_agent.workflows.router.router_llm import LLMRouter
43
- from mcp_agent.config import Settings
44
- from rich import print
45
- from mcp_agent.progress_display import progress_display
46
- from mcp_agent.workflows.llm.model_factory import ModelFactory
47
28
  from mcp_agent.workflows.llm.augmented_llm import AugmentedLLM, RequestParams
29
+ from mcp_agent.workflows.llm.model_factory import ModelFactory
30
+ from mcp_agent.workflows.orchestrator.orchestrator import Orchestrator
31
+ from mcp_agent.workflows.parallel.parallel_llm import ParallelLLM
32
+ from mcp_agent.workflows.router.router_llm import LLMRouter
48
33
 
49
- # TODO -- resintate once Windows&Python 3.13 platform issues are fixed
50
- # import readline # noqa: F401
51
-
52
- # Type aliases for better readability
53
- WorkflowType: TypeAlias = Union[
54
- Orchestrator, ParallelLLM, EvaluatorOptimizerLLM, LLMRouter
55
- ]
56
- AgentOrWorkflow: TypeAlias = Union[Agent, WorkflowType]
57
- ProxyDict: TypeAlias = Dict[str, "BaseAgentProxy"]
58
-
59
-
60
- class AgentType(Enum):
61
- """Enumeration of supported agent types."""
34
+ from mcp_agent.core.agent_app import AgentApp
35
+ from mcp_agent.core.agent_types import AgentType
36
+ from mcp_agent.core.agent_utils import unwrap_proxy, get_agent_instances, log_agent_load
37
+ from mcp_agent.core.error_handling import handle_error
38
+ from mcp_agent.core.proxies import (
39
+ BaseAgentProxy,
40
+ LLMAgentProxy,
41
+ WorkflowProxy,
42
+ RouterProxy,
43
+ ChainProxy,
44
+ )
45
+ from mcp_agent.core.types import AgentOrWorkflow, ProxyDict
46
+ from mcp_agent.core.exceptions import (
47
+ AgentConfigError,
48
+ CircularDependencyError,
49
+ ModelConfigError,
50
+ PromptExitError,
51
+ ServerConfigError,
52
+ ProviderKeyError,
53
+ ServerInitializationError,
54
+ )
62
55
 
63
- BASIC = "agent"
64
- ORCHESTRATOR = "orchestrator"
65
- PARALLEL = "parallel"
66
- EVALUATOR_OPTIMIZER = "evaluator_optimizer"
67
- ROUTER = "router"
68
- CHAIN = "chain"
56
+ # TODO -- reinstate once Windows&Python 3.13 platform issues are fixed
57
+ # import readline # noqa: F401
69
58
 
59
+ from rich import print
70
60
 
71
61
  T = TypeVar("T") # For the wrapper classes
72
62
 
73
63
 
74
- class BaseAgentProxy:
75
- """Base class for all proxy types"""
76
-
77
- def __init__(self, app: MCPApp, name: str):
78
- self._app = app
79
- self._name = name
80
-
81
- async def __call__(self, message: Optional[str] = None) -> str:
82
- """Allow: agent.researcher('message')"""
83
- return await self.send(message)
84
-
85
- async def send(self, message: Optional[str] = None) -> str:
86
- """Allow: agent.researcher.send('message')"""
87
- if message is None:
88
- return await self.prompt()
89
- return await self.generate_str(message)
90
-
91
- async def prompt(self, default_prompt: str = "") -> str:
92
- """Allow: agent.researcher.prompt()"""
93
- return await self._app.prompt(self._name, default_prompt)
94
-
95
- async def generate_str(self, message: str) -> str:
96
- """Generate response for a message - must be implemented by subclasses"""
97
- raise NotImplementedError("Subclasses must implement generate_str")
98
-
99
-
100
- class AgentProxy(BaseAgentProxy):
101
- """Legacy proxy for individual agent operations"""
102
-
103
- async def generate_str(self, message: str) -> str:
104
- return await self._app.send(self._name, message)
105
-
106
-
107
- class LLMAgentProxy(BaseAgentProxy):
108
- """Proxy for regular agents that use _llm.generate_str()"""
109
-
110
- def __init__(self, app: MCPApp, name: str, agent: Agent):
111
- super().__init__(app, name)
112
- self._agent = agent
113
-
114
- async def generate_str(self, message: str) -> str:
115
- return await self._agent._llm.generate_str(message)
116
-
117
-
118
- class WorkflowProxy(BaseAgentProxy):
119
- """Proxy for workflow types that implement generate_str() directly"""
120
-
121
- def __init__(self, app: MCPApp, name: str, workflow: WorkflowType):
122
- super().__init__(app, name)
123
- self._workflow = workflow
124
-
125
- async def generate_str(self, message: str) -> str:
126
- return await self._workflow.generate_str(message)
127
-
128
-
129
- class RouterProxy(BaseAgentProxy):
130
- """Proxy for LLM Routers"""
131
-
132
- def __init__(self, app: MCPApp, name: str, workflow: WorkflowType):
133
- super().__init__(app, name)
134
- self._workflow = workflow
135
-
136
- async def generate_str(self, message: str) -> str:
137
- results = await self._workflow.route(message)
138
- if not results:
139
- return "No appropriate route found for the request."
140
-
141
- # Get the top result
142
- top_result = results[0]
143
- if isinstance(top_result.result, Agent):
144
- # Agent route - delegate to the agent
145
- agent = top_result.result
146
-
147
- return await agent._llm.generate_str(message)
148
- elif isinstance(top_result.result, str):
149
- # Server route - use the router directly
150
- return "Tool call requested by router - not yet supported"
151
-
152
- return f"Routed to: {top_result.result} ({top_result.confidence}): {top_result.reasoning}"
153
-
154
-
155
- class ChainProxy(BaseAgentProxy):
156
- """Proxy for chained agent operations"""
157
-
158
- def __init__(
159
- self, app: MCPApp, name: str, sequence: List[str], agent_proxies: ProxyDict
160
- ):
161
- super().__init__(app, name)
162
- self._sequence = sequence
163
- self._agent_proxies = agent_proxies
164
-
165
- async def generate_str(self, message: str) -> str:
166
- """Chain message through a sequence of agents"""
167
- current_message = message
168
-
169
- for agent_name in self._sequence:
170
- proxy = self._agent_proxies[agent_name]
171
- current_message = await proxy.generate_str(current_message)
172
-
173
- return current_message
174
-
175
-
176
- class AgentApp:
177
- """Main application wrapper"""
178
-
179
- def __init__(self, app: MCPApp, agents: ProxyDict):
180
- self._app = app
181
- self._agents = agents
182
- # Optional: set default agent for direct calls
183
- self._default = next(iter(agents)) if agents else None
184
-
185
- async def send(self, agent_name: str, message: Optional[str]) -> str:
186
- """Core message sending"""
187
- if agent_name not in self._agents:
188
- raise ValueError(f"No agent named '{agent_name}'")
189
-
190
- if not message or "" == message:
191
- return await self.prompt(agent_name)
192
-
193
- proxy = self._agents[agent_name]
194
- return await proxy.generate_str(message)
195
-
196
- async def prompt(self, agent_name: Optional[str] = None, default: str = "") -> str:
197
- """
198
- Interactive prompt for sending messages with advanced features.
199
-
200
- Args:
201
- agent_name: Optional target agent name (uses default if not specified)
202
- default: Default message to use when user presses enter
203
- """
204
- from .enhanced_prompt import get_enhanced_input, handle_special_commands
205
-
206
- agent = agent_name or self._default
207
-
208
- if agent not in self._agents:
209
- raise ValueError(f"No agent named '{agent}'")
210
-
211
- # Pass all available agent names for auto-completion
212
- available_agents = list(self._agents.keys())
213
-
214
- # Create agent_types dictionary mapping agent names to their types
215
- agent_types = {}
216
- for name, proxy in self._agents.items():
217
- # Determine agent type based on the proxy type
218
- if isinstance(proxy, LLMAgentProxy):
219
- # Convert AgentType.BASIC.value ("agent") to "Agent"
220
- agent_types[name] = "Agent"
221
- elif isinstance(proxy, RouterProxy):
222
- agent_types[name] = "Router"
223
- elif isinstance(proxy, ChainProxy):
224
- agent_types[name] = "Chain"
225
- elif isinstance(proxy, WorkflowProxy):
226
- # For workflow proxies, check the workflow type
227
- workflow = proxy._workflow
228
- if isinstance(workflow, Orchestrator):
229
- agent_types[name] = "Orchestrator"
230
- elif isinstance(workflow, ParallelLLM):
231
- agent_types[name] = "Parallel"
232
- elif isinstance(workflow, EvaluatorOptimizerLLM):
233
- agent_types[name] = "Evaluator"
234
- else:
235
- agent_types[name] = "Workflow"
236
-
237
- result = ""
238
- while True:
239
- with progress_display.paused():
240
- # Use the enhanced input method with advanced features
241
- user_input = await get_enhanced_input(
242
- agent_name=agent,
243
- default=default,
244
- show_default=(default != ""),
245
- show_stop_hint=True,
246
- multiline=False, # Default to single-line mode
247
- available_agent_names=available_agents,
248
- syntax=None, # Can enable syntax highlighting for code input
249
- agent_types=agent_types, # Pass agent types for display
250
- )
251
-
252
- # Handle special commands
253
- command_result = await handle_special_commands(user_input, self)
254
-
255
- # Check if we should switch agents
256
- if (
257
- isinstance(command_result, dict)
258
- and "switch_agent" in command_result
259
- ):
260
- agent = command_result["switch_agent"]
261
- continue
262
-
263
- # Skip further processing if command was handled
264
- if command_result:
265
- continue
266
-
267
- if user_input.upper() == "STOP":
268
- return result
269
- if user_input == "":
270
- continue
271
-
272
- result = await self.send(agent, user_input)
273
-
274
- return result
275
-
276
- def __getattr__(self, name: str) -> AgentProxy:
277
- """Support: agent.researcher"""
278
- if name not in self._agents:
279
- raise AttributeError(f"No agent named '{name}'")
280
- return AgentProxy(self, name)
281
-
282
- def __getitem__(self, name: str) -> AgentProxy:
283
- """Support: agent['researcher']"""
284
- if name not in self._agents:
285
- raise KeyError(f"No agent named '{name}'")
286
- return AgentProxy(self, name)
287
-
288
- async def __call__(
289
- self, message: Optional[str] = "", agent_name: Optional[str] = None
290
- ) -> str:
291
- """Support: agent('message')"""
292
- target = agent_name or self._default
293
- if not target:
294
- raise ValueError("No default agent available")
295
- return await self.send(target, message)
296
-
297
-
298
64
  class FastAgent(ContextDependent):
299
65
  """
300
66
  A decorator-based interface for MCP Agent applications.
@@ -318,11 +84,27 @@ class FastAgent(ContextDependent):
318
84
  "--model",
319
85
  help="Override the default model for all agents. Precedence is default < config_file < command line < constructor",
320
86
  )
87
+ parser.add_argument(
88
+ "--agent",
89
+ help="Specify the agent to send a message to (used with --message)",
90
+ )
91
+ parser.add_argument(
92
+ "-m", "--message",
93
+ help="Message to send to the specified agent (requires --agent)",
94
+ )
95
+ parser.add_argument(
96
+ "--quiet", action="store_true",
97
+ help="Disable progress display, tool and message logging for cleaner output",
98
+ )
321
99
  self.args = parser.parse_args()
100
+
101
+ # Quiet mode will be handled in _load_config()
322
102
 
323
103
  self.name = name
324
104
  self.config_path = config_path
325
105
  self._load_config()
106
+
107
+ # Create the MCPApp with the config
326
108
  self.app = MCPApp(
327
109
  name=name,
328
110
  settings=Settings(**self.config) if hasattr(self, "config") else None,
@@ -350,7 +132,7 @@ class FastAgent(ContextDependent):
350
132
  AgentType.EVALUATOR_OPTIMIZER.value,
351
133
  AgentType.CHAIN.value,
352
134
  ]:
353
- self._log_agent_load(name)
135
+ log_agent_load(self.app, name)
354
136
  if agent_type == AgentType.BASIC.value:
355
137
  if not isinstance(instance, Agent):
356
138
  raise TypeError(
@@ -397,7 +179,7 @@ class FastAgent(ContextDependent):
397
179
  """Load configuration from YAML file, properly handling without dotenv processing"""
398
180
  if self.config_path:
399
181
  with open(self.config_path) as f:
400
- self.config = yaml.safe_load(f)
182
+ self.config = yaml.safe_load(f) or {}
401
183
 
402
184
  def _validate_server_references(self) -> None:
403
185
  """
@@ -638,6 +420,7 @@ class FastAgent(ContextDependent):
638
420
  def agent(
639
421
  self,
640
422
  name: str = "Agent",
423
+ instruction_or_kwarg: str = None,
641
424
  *,
642
425
  instruction: str = "You are a helpful agent.",
643
426
  servers: List[str] = [],
@@ -651,13 +434,26 @@ class FastAgent(ContextDependent):
651
434
 
652
435
  Args:
653
436
  name: Name of the agent
654
- instruction: Base instruction for the agent
437
+ instruction_or_kwarg: Optional positional parameter for instruction
438
+ instruction: Base instruction for the agent (keyword arg)
655
439
  servers: List of server names the agent should connect to
656
440
  model: Model specification string (highest precedence)
657
441
  use_history: Whether to maintain conversation history
658
442
  request_params: Additional request parameters for the LLM
659
443
  human_input: Whether to enable human input capabilities
444
+
445
+ The instruction can be provided either as a second positional argument
446
+ or as a keyword argument. Positional argument takes precedence when both are provided.
447
+
448
+ Usage:
449
+ @fast.agent("agent_name", "Your instruction here") # Using positional arg
450
+ @fast.agent("agent_name", instruction="Your instruction here") # Using keyword arg
660
451
  """
452
+ # Use positional argument if provided, otherwise use keyword argument
453
+ final_instruction = (
454
+ instruction_or_kwarg if instruction_or_kwarg is not None else instruction
455
+ )
456
+
661
457
  decorator = self._create_decorator(
662
458
  AgentType.BASIC,
663
459
  default_name="Agent",
@@ -665,7 +461,7 @@ class FastAgent(ContextDependent):
665
461
  default_use_history=True,
666
462
  )(
667
463
  name=name,
668
- instruction=instruction,
464
+ instruction=final_instruction,
669
465
  servers=servers,
670
466
  model=model,
671
467
  use_history=use_history,
@@ -726,25 +522,47 @@ class FastAgent(ContextDependent):
726
522
  def parallel(
727
523
  self,
728
524
  name: str,
729
- fan_in: str,
730
525
  fan_out: List[str],
526
+ fan_in: Optional[str] = None,
731
527
  instruction: str = "",
732
528
  model: str | None = None,
733
529
  use_history: bool = True,
734
530
  request_params: Optional[Dict] = None,
531
+ include_request: bool = True,
735
532
  ) -> Callable:
736
533
  """
737
534
  Decorator to create and register a parallel executing agent.
738
535
 
739
536
  Args:
740
537
  name: Name of the parallel executing agent
741
- fan_in: Name of collecting agent
742
538
  fan_out: List of parallel execution agents
539
+ fan_in: Optional name of collecting agent. If not provided, a passthrough agent
540
+ will be created automatically with the name "{name}_fan_in"
743
541
  instruction: Optional instruction for the parallel agent
744
542
  model: Model specification string
745
543
  use_history: Whether to maintain conversation history
746
544
  request_params: Additional request parameters for the LLM
545
+ include_request: Whether to include the original request in the fan-in message
747
546
  """
547
+ # If fan_in is not provided, create a passthrough agent with a derived name
548
+ if fan_in is None:
549
+ passthrough_name = f"{name}_fan_in"
550
+
551
+ # Register the passthrough agent directly in self.agents
552
+ self.agents[passthrough_name] = {
553
+ "config": AgentConfig(
554
+ name=passthrough_name,
555
+ instruction=f"Passthrough fan-in for {name}",
556
+ servers=[],
557
+ use_history=use_history,
558
+ ),
559
+ "type": AgentType.BASIC.value, # Using BASIC type since we're just attaching a PassthroughLLM
560
+ "func": lambda x: x, # Simple passthrough function (never actually called)
561
+ }
562
+
563
+ # Use this passthrough as the fan-in
564
+ fan_in = passthrough_name
565
+
748
566
  decorator = self._create_decorator(
749
567
  AgentType.PARALLEL,
750
568
  default_instruction="",
@@ -758,6 +576,7 @@ class FastAgent(ContextDependent):
758
576
  model=model,
759
577
  use_history=use_history,
760
578
  request_params=request_params,
579
+ include_request=include_request,
761
580
  )
762
581
  return decorator
763
582
 
@@ -848,6 +667,7 @@ class FastAgent(ContextDependent):
848
667
  model: str | None = None,
849
668
  use_history: bool = True,
850
669
  request_params: Optional[Dict] = None,
670
+ continue_with_final: bool = True,
851
671
  ) -> Callable:
852
672
  """
853
673
  Decorator to create and register a chain of agents.
@@ -860,6 +680,7 @@ class FastAgent(ContextDependent):
860
680
  model: Model specification string (not used directly in chain)
861
681
  use_history: Whether to maintain conversation history
862
682
  request_params: Additional request parameters
683
+ continue_with_final: When using prompt(), whether to continue with the final agent after processing chain (default: True)
863
684
  """
864
685
  # Support both parameter names
865
686
  agent_sequence = sequence or agents
@@ -884,6 +705,37 @@ class FastAgent(ContextDependent):
884
705
  model=model,
885
706
  use_history=use_history,
886
707
  request_params=request_params,
708
+ continue_with_final=continue_with_final,
709
+ )
710
+ return decorator
711
+
712
+ def passthrough(
713
+ self,
714
+ name: str = "Passthrough",
715
+ use_history: bool = True,
716
+ **kwargs
717
+ ) -> Callable:
718
+ """
719
+ Decorator to create and register a passthrough agent.
720
+ A passthrough agent simply returns any input message without modification.
721
+
722
+ This is useful for parallel workflows where no fan-in aggregation is needed
723
+ (the fan-in agent can be a passthrough that simply returns the combined outputs).
724
+
725
+ Args:
726
+ name: Name of the passthrough agent
727
+ use_history: Whether to maintain conversation history
728
+ **kwargs: Additional parameters (ignored, for compatibility)
729
+ """
730
+ decorator = self._create_decorator(
731
+ AgentType.BASIC, # Using BASIC agent type since we'll use a regular agent with PassthroughLLM
732
+ default_name="Passthrough",
733
+ default_instruction="Passthrough agent that returns input without modification",
734
+ default_use_history=use_history,
735
+ wrapper_needed=True,
736
+ )(
737
+ name=name,
738
+ use_history=use_history,
887
739
  )
888
740
  return decorator
889
741
 
@@ -920,19 +772,42 @@ class FastAgent(ContextDependent):
920
772
 
921
773
  # Type-specific initialization
922
774
  if agent_type == AgentType.BASIC:
923
- # Create basic agent with configuration
924
- agent = Agent(config=config, context=agent_app.context)
925
-
926
- # Set up LLM with proper configuration
927
- async with agent:
928
- llm_factory = self._get_model_factory(
929
- model=config.model,
930
- request_params=config.default_request_params,
931
- )
932
- agent._llm = await agent.attach_llm(llm_factory)
933
-
934
- # Store the agent
935
- instance = agent
775
+ # Get the agent name for special handling
776
+ agent_name = agent_data["config"].name
777
+
778
+ # Check if this is an agent that should use the PassthroughLLM
779
+ if agent_name.endswith("_fan_in") or agent_name.startswith("passthrough"):
780
+ # Import here to avoid circular imports
781
+ from mcp_agent.workflows.llm.augmented_llm import PassthroughLLM
782
+
783
+ # Create basic agent with configuration
784
+ agent = Agent(config=config, context=agent_app.context)
785
+
786
+ # Set up a PassthroughLLM directly
787
+ async with agent:
788
+ agent._llm = PassthroughLLM(
789
+ name=f"{config.name}_llm",
790
+ context=agent_app.context,
791
+ agent=agent,
792
+ default_request_params=config.default_request_params,
793
+ )
794
+
795
+ # Store the agent
796
+ instance = agent
797
+ else:
798
+ # Standard basic agent with LLM
799
+ agent = Agent(config=config, context=agent_app.context)
800
+
801
+ # Set up LLM with proper configuration
802
+ async with agent:
803
+ llm_factory = self._get_model_factory(
804
+ model=config.model,
805
+ request_params=config.default_request_params,
806
+ )
807
+ agent._llm = await agent.attach_llm(llm_factory)
808
+
809
+ # Store the agent
810
+ instance = agent
936
811
 
937
812
  elif agent_type == AgentType.ORCHESTRATOR:
938
813
  # Get base params configured with model settings
@@ -1049,17 +924,17 @@ class FastAgent(ContextDependent):
1049
924
  elif agent_type == AgentType.CHAIN:
1050
925
  # Get the sequence from either parameter
1051
926
  sequence = agent_data.get("sequence", agent_data.get("agents", []))
1052
-
927
+
1053
928
  # Auto-generate instruction if not provided or if it's just the default
1054
929
  default_instruction = f"Chain of agents: {', '.join(sequence)}"
1055
-
930
+
1056
931
  # If user provided a custom instruction, use that
1057
932
  # Otherwise, generate a description based on the sequence and their servers
1058
933
  if config.instruction == default_instruction:
1059
934
  # Get all agent names in the sequence
1060
935
  agent_names = []
1061
936
  all_servers = set()
1062
-
937
+
1063
938
  # Collect information about the agents and their servers
1064
939
  for agent_name in sequence:
1065
940
  if agent_name in active_agents:
@@ -1073,15 +948,26 @@ class FastAgent(ContextDependent):
1073
948
  elif hasattr(agent_proxy, "_workflow"):
1074
949
  # For WorkflowProxy
1075
950
  agent_names.append(agent_name)
1076
-
951
+
1077
952
  # Generate a better description
1078
953
  if agent_names:
1079
- server_part = f" with access to servers: {', '.join(sorted(all_servers))}" if all_servers else ""
954
+ server_part = (
955
+ f" with access to servers: {', '.join(sorted(all_servers))}"
956
+ if all_servers
957
+ else ""
958
+ )
1080
959
  config.instruction = f"Sequence of agents: {', '.join(agent_names)}{server_part}."
1081
-
960
+
1082
961
  # Create a ChainProxy without needing a new instance
1083
962
  # Just pass the agent proxies and sequence
1084
963
  instance = ChainProxy(self.app, name, sequence, active_agents)
964
+ # Set continue_with_final behavior from configuration
965
+ instance._continue_with_final = agent_data.get(
966
+ "continue_with_final", True
967
+ )
968
+
969
+ # We removed the AgentType.PASSTHROUGH case
970
+ # Passthrough agents are now created as BASIC agents with a special LLM
1085
971
 
1086
972
  elif agent_type == AgentType.PARALLEL:
1087
973
  # Get fan-out agents (could be basic agents or other parallels)
@@ -1104,6 +990,7 @@ class FastAgent(ContextDependent):
1104
990
  context=agent_app.context,
1105
991
  llm_factory=llm_factory,
1106
992
  default_request_params=config.default_request_params,
993
+ include_request=agent_data.get("include_request", True),
1107
994
  )
1108
995
 
1109
996
  else:
@@ -1337,9 +1224,7 @@ class FastAgent(ContextDependent):
1337
1224
  Returns:
1338
1225
  The underlying Agent or workflow instance
1339
1226
  """
1340
- if isinstance(proxy, LLMAgentProxy):
1341
- return proxy._agent
1342
- return proxy._workflow
1227
+ return unwrap_proxy(proxy)
1343
1228
 
1344
1229
  def _get_agent_instances(
1345
1230
  self, agent_names: List[str], active_agents: ProxyDict
@@ -1354,7 +1239,7 @@ class FastAgent(ContextDependent):
1354
1239
  Returns:
1355
1240
  List of unwrapped agent/workflow instances
1356
1241
  """
1357
- return [self._unwrap_proxy(active_agents[name]) for name in agent_names]
1242
+ return get_agent_instances(agent_names, active_agents)
1358
1243
 
1359
1244
  @asynccontextmanager
1360
1245
  async def run(self):
@@ -1364,14 +1249,30 @@ class FastAgent(ContextDependent):
1364
1249
  """
1365
1250
  active_agents = {}
1366
1251
  had_error = False
1252
+
1253
+ # Handle quiet mode by disabling logger settings after initialization
1254
+ quiet_mode = hasattr(self, "args") and self.args.quiet
1255
+
1367
1256
  try:
1368
1257
  async with self.app.run() as agent_app:
1258
+ # Apply quiet mode directly to the context's config if needed
1259
+ if quiet_mode and hasattr(agent_app.context, "config") and hasattr(agent_app.context.config, "logger"):
1260
+ # Apply after initialization but before agents are created
1261
+ agent_app.context.config.logger.progress_display = False
1262
+ agent_app.context.config.logger.show_chat = False
1263
+ agent_app.context.config.logger.show_tools = False
1264
+
1265
+ # Directly disable the progress display singleton
1266
+ from mcp_agent.progress_display import progress_display
1267
+ progress_display.stop() # This will stop and hide the display
1268
+
1369
1269
  # Pre-flight validation
1370
1270
  self._validate_server_references()
1371
1271
  self._validate_workflow_references()
1372
1272
 
1373
1273
  # Create all types of agents in dependency order
1374
1274
  active_agents = await self._create_basic_agents(agent_app)
1275
+
1375
1276
  orchestrators = await self._create_orchestrators(
1376
1277
  agent_app, active_agents
1377
1278
  )
@@ -1395,6 +1296,30 @@ class FastAgent(ContextDependent):
1395
1296
 
1396
1297
  # Create wrapper with all agents
1397
1298
  wrapper = AgentApp(agent_app, active_agents)
1299
+
1300
+ # Handle direct message sending if --agent and --message are provided
1301
+ if self.args.agent and self.args.message:
1302
+ agent_name = self.args.agent
1303
+ message = self.args.message
1304
+
1305
+ if agent_name not in active_agents:
1306
+ available_agents = ", ".join(active_agents.keys())
1307
+ print(f"\n\nError: Agent '{agent_name}' not found. Available agents: {available_agents}")
1308
+ raise SystemExit(1)
1309
+
1310
+ try:
1311
+ # Get response
1312
+ response = await wrapper[agent_name].send(message)
1313
+
1314
+ # Only print the response in quiet mode
1315
+ if self.args.quiet:
1316
+ print(f"{response}")
1317
+
1318
+ raise SystemExit(0)
1319
+ except Exception as e:
1320
+ print(f"\n\nError sending message to agent '{agent_name}': {str(e)}")
1321
+ raise SystemExit(1)
1322
+
1398
1323
  yield wrapper
1399
1324
 
1400
1325
  except ServerConfigError as e:
@@ -1481,19 +1406,8 @@ class FastAgent(ContextDependent):
1481
1406
  error_type: Type of error to display
1482
1407
  suggestion: Optional suggestion message to display
1483
1408
  """
1484
- print(f"\n[bold red]{error_type}:")
1485
- print(getattr(e, "message", str(e)))
1486
- if hasattr(e, "details") and e.details:
1487
- print("\nDetails:")
1488
- print(e.details)
1489
- if suggestion:
1490
- print(f"\n{suggestion}")
1409
+ handle_error(e, error_type, suggestion)
1491
1410
 
1492
1411
  def _log_agent_load(self, agent_name: str) -> None:
1493
- self.app._logger.info(
1494
- f"Loaded {agent_name}",
1495
- data={
1496
- "progress_action": ProgressAction.LOADED,
1497
- "agent_name": agent_name,
1498
- },
1499
- )
1412
+ # Using the imported function
1413
+ log_agent_load(self.app, agent_name)