fast-agent-mcp 0.2.17__py3-none-any.whl → 0.2.19__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.2.17.dist-info → fast_agent_mcp-0.2.19.dist-info}/METADATA +15 -14
- {fast_agent_mcp-0.2.17.dist-info → fast_agent_mcp-0.2.19.dist-info}/RECORD +24 -24
- mcp_agent/agents/base_agent.py +6 -2
- mcp_agent/agents/workflow/parallel_agent.py +53 -38
- mcp_agent/agents/workflow/router_agent.py +22 -17
- mcp_agent/cli/commands/go.py +133 -0
- mcp_agent/cli/commands/setup.py +1 -1
- mcp_agent/cli/main.py +5 -3
- mcp_agent/config.py +2 -4
- mcp_agent/context.py +13 -10
- mcp_agent/core/enhanced_prompt.py +12 -7
- mcp_agent/core/fastagent.py +250 -188
- mcp_agent/core/interactive_prompt.py +6 -2
- mcp_agent/core/validation.py +12 -1
- mcp_agent/executor/executor.py +8 -9
- mcp_agent/llm/augmented_llm.py +2 -0
- mcp_agent/llm/providers/augmented_llm_anthropic.py +2 -1
- mcp_agent/llm/providers/augmented_llm_deepseek.py +1 -3
- mcp_agent/llm/providers/augmented_llm_openai.py +4 -1
- mcp_agent/mcp/mcp_aggregator.py +15 -10
- mcp_agent/mcp/mcp_connection_manager.py +1 -1
- mcp_agent/logging/tracing.py +0 -138
- {fast_agent_mcp-0.2.17.dist-info → fast_agent_mcp-0.2.19.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.2.17.dist-info → fast_agent_mcp-0.2.19.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.2.17.dist-info → fast_agent_mcp-0.2.19.dist-info}/licenses/LICENSE +0 -0
mcp_agent/context.py
CHANGED
@@ -9,6 +9,8 @@ from typing import TYPE_CHECKING, Any, Optional, Union
|
|
9
9
|
from mcp import ServerSession
|
10
10
|
from opentelemetry import trace
|
11
11
|
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
12
|
+
from opentelemetry.instrumentation.anthropic import AnthropicInstrumentor
|
13
|
+
from opentelemetry.instrumentation.openai import OpenAIInstrumentor
|
12
14
|
from opentelemetry.propagate import set_global_textmap
|
13
15
|
from opentelemetry.sdk.resources import Resource
|
14
16
|
from opentelemetry.sdk.trace import TracerProvider
|
@@ -51,7 +53,7 @@ class Context(BaseModel):
|
|
51
53
|
server_registry: Optional[ServerRegistry] = None
|
52
54
|
task_registry: Optional[ActivityRegistry] = None
|
53
55
|
|
54
|
-
tracer:
|
56
|
+
tracer: trace.Tracer | None = None
|
55
57
|
|
56
58
|
model_config = ConfigDict(
|
57
59
|
extra="allow",
|
@@ -63,19 +65,19 @@ async def configure_otel(config: "Settings") -> None:
|
|
63
65
|
"""
|
64
66
|
Configure OpenTelemetry based on the application config.
|
65
67
|
"""
|
66
|
-
if not config.otel.enabled:
|
67
|
-
return
|
68
|
-
|
69
|
-
# Check if a provider is already set to avoid re-initialization
|
70
|
-
if trace.get_tracer_provider().__class__.__name__ != "NoOpTracerProvider":
|
68
|
+
if not config.otel or not config.otel.enabled:
|
71
69
|
return
|
72
70
|
|
73
71
|
# Set up global textmap propagator first
|
74
72
|
set_global_textmap(TraceContextTextMapPropagator())
|
75
73
|
|
76
74
|
service_name = config.otel.service_name
|
77
|
-
|
78
|
-
|
75
|
+
from importlib.metadata import version
|
76
|
+
|
77
|
+
try:
|
78
|
+
app_version = version("fast-agent-mcp")
|
79
|
+
except: # noqa: E722
|
80
|
+
app_version = "unknown"
|
79
81
|
|
80
82
|
# Create resource identifying this service
|
81
83
|
resource = Resource.create(
|
@@ -83,8 +85,7 @@ async def configure_otel(config: "Settings") -> None:
|
|
83
85
|
key: value
|
84
86
|
for key, value in {
|
85
87
|
"service.name": service_name,
|
86
|
-
"service.
|
87
|
-
"service.version": service_version,
|
88
|
+
"service.version": app_version,
|
88
89
|
}.items()
|
89
90
|
if value is not None
|
90
91
|
}
|
@@ -107,6 +108,8 @@ async def configure_otel(config: "Settings") -> None:
|
|
107
108
|
|
108
109
|
# Set as global tracer provider
|
109
110
|
trace.set_tracer_provider(tracer_provider)
|
111
|
+
AnthropicInstrumentor().instrument()
|
112
|
+
OpenAIInstrumentor().instrument()
|
110
113
|
|
111
114
|
|
112
115
|
async def configure_logger(config: "Settings") -> None:
|
@@ -289,14 +289,19 @@ async def get_enhanced_input(
|
|
289
289
|
# Return a dictionary with select_prompt action instead of a string
|
290
290
|
# This way it will match what the command handler expects
|
291
291
|
return {"select_prompt": True, "prompt_name": None}
|
292
|
-
elif cmd == "prompt"
|
293
|
-
#
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
292
|
+
elif cmd == "prompt":
|
293
|
+
# Handle /prompt with no arguments the same way as /prompts
|
294
|
+
if len(cmd_parts) > 1:
|
295
|
+
# Direct prompt selection with name or number
|
296
|
+
prompt_arg = cmd_parts[1].strip()
|
297
|
+
# Check if it's a number (use as index) or a name (use directly)
|
298
|
+
if prompt_arg.isdigit():
|
299
|
+
return {"select_prompt": True, "prompt_index": int(prompt_arg)}
|
300
|
+
else:
|
301
|
+
return f"SELECT_PROMPT:{prompt_arg}"
|
298
302
|
else:
|
299
|
-
|
303
|
+
# If /prompt is used without arguments, treat it the same as /prompts
|
304
|
+
return {"select_prompt": True, "prompt_name": None}
|
300
305
|
elif cmd == "exit":
|
301
306
|
return "EXIT"
|
302
307
|
elif cmd.lower() == "stop":
|
mcp_agent/core/fastagent.py
CHANGED
@@ -9,9 +9,11 @@ import asyncio
|
|
9
9
|
import sys
|
10
10
|
from contextlib import asynccontextmanager
|
11
11
|
from importlib.metadata import version as get_version
|
12
|
-
from
|
12
|
+
from pathlib import Path
|
13
|
+
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, TypeVar
|
13
14
|
|
14
15
|
import yaml
|
16
|
+
from opentelemetry import trace
|
15
17
|
|
16
18
|
from mcp_agent import config
|
17
19
|
from mcp_agent.app import MCPApp
|
@@ -54,9 +56,11 @@ from mcp_agent.core.validation import (
|
|
54
56
|
validate_workflow_references,
|
55
57
|
)
|
56
58
|
from mcp_agent.logging.logger import get_logger
|
59
|
+
from mcp_agent.mcp.prompts.prompt_load import load_prompt_multipart
|
57
60
|
|
58
61
|
if TYPE_CHECKING:
|
59
62
|
from mcp_agent.agents.agent import Agent
|
63
|
+
from mcp_agent.mcp.prompt_message_multipart import PromptMessageMultipart
|
60
64
|
|
61
65
|
F = TypeVar("F", bound=Callable[..., Any]) # For decorated functions
|
62
66
|
logger = get_logger(__name__)
|
@@ -73,77 +77,97 @@ class FastAgent:
|
|
73
77
|
name: str,
|
74
78
|
config_path: str | None = None,
|
75
79
|
ignore_unknown_args: bool = False,
|
80
|
+
parse_cli_args: bool = True, # Add new parameter with default True
|
76
81
|
) -> None:
|
77
82
|
"""
|
78
|
-
Initialize the
|
83
|
+
Initialize the fast-agent application.
|
79
84
|
|
80
85
|
Args:
|
81
86
|
name: Name of the application
|
82
87
|
config_path: Optional path to config file
|
83
88
|
ignore_unknown_args: Whether to ignore unknown command line arguments
|
89
|
+
when parse_cli_args is True.
|
90
|
+
parse_cli_args: If True, parse command line arguments using argparse.
|
91
|
+
Set to False when embedding FastAgent in another framework
|
92
|
+
(like FastAPI/Uvicorn) that handles its own arguments.
|
84
93
|
"""
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
94
|
+
self.args = argparse.Namespace() # Initialize args always
|
95
|
+
|
96
|
+
# --- Wrap argument parsing logic ---
|
97
|
+
if parse_cli_args:
|
98
|
+
# Setup command line argument parsing
|
99
|
+
parser = argparse.ArgumentParser(description="DirectFastAgent Application")
|
100
|
+
parser.add_argument(
|
101
|
+
"--model",
|
102
|
+
help="Override the default model for all agents",
|
103
|
+
)
|
104
|
+
parser.add_argument(
|
105
|
+
"--agent",
|
106
|
+
default="default",
|
107
|
+
help="Specify the agent to send a message to (used with --message)",
|
108
|
+
)
|
109
|
+
parser.add_argument(
|
110
|
+
"-m",
|
111
|
+
"--message",
|
112
|
+
help="Message to send to the specified agent",
|
113
|
+
)
|
114
|
+
parser.add_argument(
|
115
|
+
"-p", "--prompt-file", help="Path to a prompt file to use (either text or JSON)"
|
116
|
+
)
|
117
|
+
parser.add_argument(
|
118
|
+
"--quiet",
|
119
|
+
action="store_true",
|
120
|
+
help="Disable progress display, tool and message logging for cleaner output",
|
121
|
+
)
|
122
|
+
parser.add_argument(
|
123
|
+
"--version",
|
124
|
+
action="store_true",
|
125
|
+
help="Show version and exit",
|
126
|
+
)
|
127
|
+
parser.add_argument(
|
128
|
+
"--server",
|
129
|
+
action="store_true",
|
130
|
+
help="Run as an MCP server",
|
131
|
+
)
|
132
|
+
parser.add_argument(
|
133
|
+
"--transport",
|
134
|
+
choices=["sse", "stdio"],
|
135
|
+
default="sse",
|
136
|
+
help="Transport protocol to use when running as a server (sse or stdio)",
|
137
|
+
)
|
138
|
+
parser.add_argument(
|
139
|
+
"--port",
|
140
|
+
type=int,
|
141
|
+
default=8000,
|
142
|
+
help="Port to use when running as a server with SSE transport",
|
143
|
+
)
|
144
|
+
parser.add_argument(
|
145
|
+
"--host",
|
146
|
+
default="0.0.0.0",
|
147
|
+
help="Host address to bind to when running as a server with SSE transport",
|
148
|
+
)
|
138
149
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
150
|
+
if ignore_unknown_args:
|
151
|
+
known_args, _ = parser.parse_known_args()
|
152
|
+
self.args = known_args
|
153
|
+
else:
|
154
|
+
# Use parse_known_args here too, to avoid crashing on uvicorn args etc.
|
155
|
+
# even if ignore_unknown_args is False, we only care about *our* args.
|
156
|
+
known_args, unknown = parser.parse_known_args()
|
157
|
+
self.args = known_args
|
158
|
+
# Optionally, warn about unknown args if not ignoring?
|
159
|
+
# if unknown and not ignore_unknown_args:
|
160
|
+
# logger.warning(f"Ignoring unknown command line arguments: {unknown}")
|
161
|
+
|
162
|
+
# Handle version flag
|
163
|
+
if self.args.version:
|
164
|
+
try:
|
165
|
+
app_version = get_version("fast-agent-mcp")
|
166
|
+
except: # noqa: E722
|
167
|
+
app_version = "unknown"
|
168
|
+
print(f"fast-agent-mcp v{app_version}")
|
169
|
+
sys.exit(0)
|
170
|
+
# --- End of wrapped logic ---
|
147
171
|
|
148
172
|
self.name = name
|
149
173
|
self.config_path = config_path
|
@@ -213,137 +237,175 @@ class FastAgent:
|
|
213
237
|
had_error = False
|
214
238
|
await self.app.initialize()
|
215
239
|
|
216
|
-
# Handle quiet mode
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
self.context,
|
247
|
-
model=model,
|
248
|
-
request_params=request_params,
|
249
|
-
cli_model=self.args.model if hasattr(self, "args") else None,
|
250
|
-
)
|
251
|
-
|
252
|
-
# Create all agents in dependency order
|
253
|
-
active_agents = await create_agents_in_dependency_order(
|
254
|
-
self.app,
|
255
|
-
self.agents,
|
256
|
-
model_factory_func,
|
257
|
-
)
|
258
|
-
|
259
|
-
# Create a wrapper with all agents for simplified access
|
260
|
-
wrapper = AgentApp(active_agents)
|
261
|
-
|
262
|
-
# Handle command line options that should be processed after agent initialization
|
263
|
-
|
264
|
-
# Handle --server option
|
265
|
-
if hasattr(self, "args") and self.args.server:
|
266
|
-
try:
|
267
|
-
# Print info message if not in quiet mode
|
268
|
-
if not quiet_mode:
|
269
|
-
print(f"Starting FastAgent '{self.name}' in server mode")
|
270
|
-
print(f"Transport: {self.args.transport}")
|
271
|
-
if self.args.transport == "sse":
|
272
|
-
print(f"Listening on {self.args.host}:{self.args.port}")
|
273
|
-
print("Press Ctrl+C to stop")
|
274
|
-
|
275
|
-
# Create the MCP server
|
276
|
-
from mcp_agent.mcp_server import AgentMCPServer
|
277
|
-
|
278
|
-
mcp_server = AgentMCPServer(
|
279
|
-
agent_app=wrapper,
|
280
|
-
server_name=f"{self.name}-MCP-Server",
|
281
|
-
)
|
282
|
-
|
283
|
-
# Run the server directly (this is a blocking call)
|
284
|
-
await mcp_server.run_async(
|
285
|
-
transport=self.args.transport, host=self.args.host, port=self.args.port
|
240
|
+
# Handle quiet mode and CLI model override safely
|
241
|
+
# Define these *before* they are used, checking if self.args exists and has the attributes
|
242
|
+
quiet_mode = hasattr(self.args, "quiet") and self.args.quiet
|
243
|
+
cli_model_override = (
|
244
|
+
self.args.model if hasattr(self.args, "model") and self.args.model else None
|
245
|
+
) # Define cli_model_override here
|
246
|
+
tracer = trace.get_tracer(__name__)
|
247
|
+
with tracer.start_as_current_span(self.name):
|
248
|
+
try:
|
249
|
+
async with self.app.run():
|
250
|
+
# Apply quiet mode if requested
|
251
|
+
if (
|
252
|
+
quiet_mode
|
253
|
+
and hasattr(self.app.context, "config")
|
254
|
+
and hasattr(self.app.context.config, "logger")
|
255
|
+
):
|
256
|
+
# Update our app's config directly
|
257
|
+
self.app.context.config.logger.progress_display = False
|
258
|
+
self.app.context.config.logger.show_chat = False
|
259
|
+
self.app.context.config.logger.show_tools = False
|
260
|
+
|
261
|
+
# Directly disable the progress display singleton
|
262
|
+
from mcp_agent.progress_display import progress_display
|
263
|
+
|
264
|
+
progress_display.stop()
|
265
|
+
|
266
|
+
# Pre-flight validation
|
267
|
+
if 0 == len(self.agents):
|
268
|
+
raise AgentConfigError(
|
269
|
+
"No agents defined. Please define at least one agent."
|
286
270
|
)
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
if hasattr(self, "args") and self.args.agent and self.args.message:
|
299
|
-
agent_name = self.args.agent
|
300
|
-
message = self.args.message
|
301
|
-
|
302
|
-
if agent_name not in active_agents:
|
303
|
-
available_agents = ", ".join(active_agents.keys())
|
304
|
-
print(
|
305
|
-
f"\n\nError: Agent '{agent_name}' not found. Available agents: {available_agents}"
|
271
|
+
validate_server_references(self.context, self.agents)
|
272
|
+
validate_workflow_references(self.agents)
|
273
|
+
|
274
|
+
# Get a model factory function
|
275
|
+
# Now cli_model_override is guaranteed to be defined
|
276
|
+
def model_factory_func(model=None, request_params=None):
|
277
|
+
return get_model_factory(
|
278
|
+
self.context,
|
279
|
+
model=model,
|
280
|
+
request_params=request_params,
|
281
|
+
cli_model=cli_model_override, # Use the variable defined above
|
306
282
|
)
|
307
|
-
raise SystemExit(1)
|
308
|
-
|
309
|
-
try:
|
310
|
-
# Get response from the agent
|
311
|
-
agent = active_agents[agent_name]
|
312
|
-
response = await agent.send(message)
|
313
283
|
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
284
|
+
# Create all agents in dependency order
|
285
|
+
active_agents = await create_agents_in_dependency_order(
|
286
|
+
self.app,
|
287
|
+
self.agents,
|
288
|
+
model_factory_func,
|
289
|
+
)
|
318
290
|
|
291
|
+
# Create a wrapper with all agents for simplified access
|
292
|
+
wrapper = AgentApp(active_agents)
|
293
|
+
|
294
|
+
# Handle command line options that should be processed after agent initialization
|
295
|
+
|
296
|
+
# Handle --server option
|
297
|
+
# Check if parse_cli_args was True before checking self.args.server
|
298
|
+
if hasattr(self.args, "server") and self.args.server:
|
299
|
+
try:
|
300
|
+
# Print info message if not in quiet mode
|
301
|
+
if not quiet_mode:
|
302
|
+
print(f"Starting FastAgent '{self.name}' in server mode")
|
303
|
+
print(f"Transport: {self.args.transport}")
|
304
|
+
if self.args.transport == "sse":
|
305
|
+
print(f"Listening on {self.args.host}:{self.args.port}")
|
306
|
+
print("Press Ctrl+C to stop")
|
307
|
+
|
308
|
+
# Create the MCP server
|
309
|
+
from mcp_agent.mcp_server import AgentMCPServer
|
310
|
+
|
311
|
+
mcp_server = AgentMCPServer(
|
312
|
+
agent_app=wrapper,
|
313
|
+
server_name=f"{self.name}-MCP-Server",
|
314
|
+
)
|
315
|
+
|
316
|
+
# Run the server directly (this is a blocking call)
|
317
|
+
await mcp_server.run_async(
|
318
|
+
transport=self.args.transport,
|
319
|
+
host=self.args.host,
|
320
|
+
port=self.args.port,
|
321
|
+
)
|
322
|
+
except KeyboardInterrupt:
|
323
|
+
if not quiet_mode:
|
324
|
+
print("\nServer stopped by user (Ctrl+C)")
|
325
|
+
except Exception as e:
|
326
|
+
if not quiet_mode:
|
327
|
+
print(f"\nServer stopped with error: {e}")
|
328
|
+
|
329
|
+
# Exit after server shutdown
|
319
330
|
raise SystemExit(0)
|
320
|
-
except Exception as e:
|
321
|
-
print(f"\n\nError sending message to agent '{agent_name}': {str(e)}")
|
322
|
-
raise SystemExit(1)
|
323
|
-
|
324
|
-
yield wrapper
|
325
|
-
|
326
|
-
except (
|
327
|
-
ServerConfigError,
|
328
|
-
ProviderKeyError,
|
329
|
-
AgentConfigError,
|
330
|
-
ServerInitializationError,
|
331
|
-
ModelConfigError,
|
332
|
-
CircularDependencyError,
|
333
|
-
PromptExitError,
|
334
|
-
) as e:
|
335
|
-
had_error = True
|
336
|
-
self._handle_error(e)
|
337
|
-
raise SystemExit(1)
|
338
331
|
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
332
|
+
# Handle direct message sending if --message is provided
|
333
|
+
if self.args.message:
|
334
|
+
agent_name = self.args.agent
|
335
|
+
message = self.args.message
|
336
|
+
|
337
|
+
if agent_name not in active_agents:
|
338
|
+
available_agents = ", ".join(active_agents.keys())
|
339
|
+
print(
|
340
|
+
f"\n\nError: Agent '{agent_name}' not found. Available agents: {available_agents}"
|
341
|
+
)
|
342
|
+
raise SystemExit(1)
|
343
|
+
|
344
|
+
try:
|
345
|
+
# Get response from the agent
|
346
|
+
agent = active_agents[agent_name]
|
347
|
+
response = await agent.send(message)
|
348
|
+
|
349
|
+
# In quiet mode, just print the raw response
|
350
|
+
# The chat display should already be turned off by the configuration
|
351
|
+
if self.args.quiet:
|
352
|
+
print(f"{response}")
|
353
|
+
|
354
|
+
raise SystemExit(0)
|
355
|
+
except Exception as e:
|
356
|
+
print(f"\n\nError sending message to agent '{agent_name}': {str(e)}")
|
357
|
+
raise SystemExit(1)
|
358
|
+
|
359
|
+
if self.args.prompt_file:
|
360
|
+
agent_name = self.args.agent
|
361
|
+
prompt: List[PromptMessageMultipart] = load_prompt_multipart(
|
362
|
+
Path(self.args.prompt_file)
|
363
|
+
)
|
364
|
+
if agent_name not in active_agents:
|
365
|
+
available_agents = ", ".join(active_agents.keys())
|
366
|
+
print(
|
367
|
+
f"\n\nError: Agent '{agent_name}' not found. Available agents: {available_agents}"
|
368
|
+
)
|
369
|
+
raise SystemExit(1)
|
370
|
+
|
371
|
+
try:
|
372
|
+
# Get response from the agent
|
373
|
+
agent = active_agents[agent_name]
|
374
|
+
response = await agent.generate(prompt)
|
375
|
+
|
376
|
+
# In quiet mode, just print the raw response
|
377
|
+
# The chat display should already be turned off by the configuration
|
378
|
+
if self.args.quiet:
|
379
|
+
print(f"{response.last_text()}")
|
380
|
+
|
381
|
+
raise SystemExit(0)
|
382
|
+
except Exception as e:
|
383
|
+
print(f"\n\nError sending message to agent '{agent_name}': {str(e)}")
|
384
|
+
raise SystemExit(1)
|
385
|
+
|
386
|
+
yield wrapper
|
387
|
+
|
388
|
+
except (
|
389
|
+
ServerConfigError,
|
390
|
+
ProviderKeyError,
|
391
|
+
AgentConfigError,
|
392
|
+
ServerInitializationError,
|
393
|
+
ModelConfigError,
|
394
|
+
CircularDependencyError,
|
395
|
+
PromptExitError,
|
396
|
+
) as e:
|
397
|
+
had_error = True
|
398
|
+
self._handle_error(e)
|
399
|
+
raise SystemExit(1)
|
400
|
+
|
401
|
+
finally:
|
402
|
+
# Clean up any active agents
|
403
|
+
if active_agents and not had_error:
|
404
|
+
for agent in active_agents.values():
|
405
|
+
try:
|
406
|
+
await agent.shutdown()
|
407
|
+
except Exception:
|
408
|
+
pass
|
347
409
|
|
348
410
|
def _handle_error(self, e: Exception, error_type: Optional[str] = None) -> None:
|
349
411
|
"""
|
@@ -153,8 +153,12 @@ class InteractivePrompt:
|
|
153
153
|
)
|
154
154
|
continue
|
155
155
|
|
156
|
-
# Skip further processing if
|
157
|
-
|
156
|
+
# Skip further processing if:
|
157
|
+
# 1. The command was handled (command_result is truthy)
|
158
|
+
# 2. The original input was a dictionary (special command like /prompt)
|
159
|
+
# 3. The command result itself is a dictionary (special command handling result)
|
160
|
+
# This fixes the issue where /prompt without arguments gets sent to the LLM
|
161
|
+
if command_result or isinstance(user_input, dict) or isinstance(command_result, dict):
|
158
162
|
continue
|
159
163
|
|
160
164
|
if user_input.upper() == "STOP":
|
mcp_agent/core/validation.py
CHANGED
@@ -231,6 +231,12 @@ def get_dependencies_groups(
|
|
231
231
|
if agent_type == AgentType.PARALLEL.value:
|
232
232
|
# Parallel agents depend on their fan-out and fan-in agents
|
233
233
|
dependencies[name].update(agent_data.get("parallel_agents", []))
|
234
|
+
# Also add explicit fan_out dependencies if present
|
235
|
+
if "fan_out" in agent_data:
|
236
|
+
dependencies[name].update(agent_data["fan_out"])
|
237
|
+
# Add explicit fan_in dependency if present
|
238
|
+
if "fan_in" in agent_data and agent_data["fan_in"]:
|
239
|
+
dependencies[name].add(agent_data["fan_in"])
|
234
240
|
elif agent_type == AgentType.CHAIN.value:
|
235
241
|
# Chain agents depend on the agents in their sequence
|
236
242
|
dependencies[name].update(agent_data.get("sequence", []))
|
@@ -241,7 +247,12 @@ def get_dependencies_groups(
|
|
241
247
|
# Orchestrator agents depend on their child agents
|
242
248
|
dependencies[name].update(agent_data.get("child_agents", []))
|
243
249
|
elif agent_type == AgentType.EVALUATOR_OPTIMIZER.value:
|
244
|
-
# Evaluator-Optimizer agents depend on their
|
250
|
+
# Evaluator-Optimizer agents depend on their evaluator and generator agents
|
251
|
+
if "evaluator" in agent_data:
|
252
|
+
dependencies[name].add(agent_data["evaluator"])
|
253
|
+
if "generator" in agent_data:
|
254
|
+
dependencies[name].add(agent_data["generator"])
|
255
|
+
# For backward compatibility - also check eval_optimizer_agents if present
|
245
256
|
dependencies[name].update(agent_data.get("eval_optimizer_agents", []))
|
246
257
|
|
247
258
|
# Check for cycles if not allowed
|