fast-agent-mcp 0.2.13__py3-none-any.whl → 0.2.14__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. {fast_agent_mcp-0.2.13.dist-info → fast_agent_mcp-0.2.14.dist-info}/METADATA +1 -1
  2. {fast_agent_mcp-0.2.13.dist-info → fast_agent_mcp-0.2.14.dist-info}/RECORD +33 -33
  3. mcp_agent/agents/agent.py +2 -2
  4. mcp_agent/agents/base_agent.py +3 -3
  5. mcp_agent/agents/workflow/chain_agent.py +2 -2
  6. mcp_agent/agents/workflow/evaluator_optimizer.py +3 -3
  7. mcp_agent/agents/workflow/orchestrator_agent.py +3 -3
  8. mcp_agent/agents/workflow/parallel_agent.py +2 -2
  9. mcp_agent/agents/workflow/router_agent.py +2 -2
  10. mcp_agent/cli/commands/check_config.py +450 -0
  11. mcp_agent/cli/commands/setup.py +1 -1
  12. mcp_agent/cli/main.py +8 -15
  13. mcp_agent/core/agent_types.py +8 -8
  14. mcp_agent/core/direct_decorators.py +10 -8
  15. mcp_agent/core/direct_factory.py +4 -1
  16. mcp_agent/core/validation.py +6 -4
  17. mcp_agent/event_progress.py +6 -6
  18. mcp_agent/llm/augmented_llm.py +10 -2
  19. mcp_agent/llm/augmented_llm_passthrough.py +5 -3
  20. mcp_agent/llm/augmented_llm_playback.py +2 -1
  21. mcp_agent/llm/model_factory.py +7 -27
  22. mcp_agent/llm/provider_key_manager.py +83 -0
  23. mcp_agent/llm/provider_types.py +16 -0
  24. mcp_agent/llm/providers/augmented_llm_anthropic.py +5 -26
  25. mcp_agent/llm/providers/augmented_llm_deepseek.py +5 -24
  26. mcp_agent/llm/providers/augmented_llm_generic.py +2 -16
  27. mcp_agent/llm/providers/augmented_llm_openai.py +4 -26
  28. mcp_agent/llm/providers/augmented_llm_openrouter.py +17 -45
  29. mcp_agent/mcp/interfaces.py +2 -1
  30. mcp_agent/mcp_server/agent_server.py +120 -38
  31. mcp_agent/cli/commands/config.py +0 -11
  32. mcp_agent/executor/temporal.py +0 -383
  33. mcp_agent/executor/workflow.py +0 -195
  34. {fast_agent_mcp-0.2.13.dist-info → fast_agent_mcp-0.2.14.dist-info}/WHEEL +0 -0
  35. {fast_agent_mcp-0.2.13.dist-info → fast_agent_mcp-0.2.14.dist-info}/entry_points.txt +0 -0
  36. {fast_agent_mcp-0.2.13.dist-info → fast_agent_mcp-0.2.14.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,450 @@
1
+ """Command to check FastAgent configuration."""
2
+
3
+ import platform
4
+ import sys
5
+ from importlib.metadata import version
6
+ from pathlib import Path
7
+ from typing import Optional
8
+
9
+ import typer
10
+ import yaml
11
+ from rich.console import Console
12
+ from rich.panel import Panel
13
+ from rich.table import Table
14
+
15
+ from mcp_agent.llm.provider_key_manager import API_KEY_HINT_TEXT, ProviderKeyManager
16
+ from mcp_agent.llm.provider_types import Provider
17
+
18
+ app = typer.Typer(
19
+ help="Check and diagnose FastAgent configuration",
20
+ no_args_is_help=False, # Allow showing our custom help instead
21
+ )
22
+ console = Console()
23
+
24
+
25
+ def find_config_files(start_path: Path) -> dict[str, Optional[Path]]:
26
+ """Find FastAgent configuration files in current directory or parents."""
27
+ results = {
28
+ "config": None,
29
+ "secrets": None,
30
+ }
31
+
32
+ current = start_path
33
+ while current != current.parent: # Stop at root directory
34
+ config_path = current / "fastagent.config.yaml"
35
+ secrets_path = current / "fastagent.secrets.yaml"
36
+
37
+ if config_path.exists() and results["config"] is None:
38
+ results["config"] = config_path
39
+
40
+ if secrets_path.exists() and results["secrets"] is None:
41
+ results["secrets"] = secrets_path
42
+
43
+ # Stop searching if we found both files
44
+ if results["config"] is not None and results["secrets"] is not None:
45
+ break
46
+
47
+ current = current.parent
48
+
49
+ return results
50
+
51
+
52
+ def get_system_info() -> dict:
53
+ """Get system information including Python version, OS, etc."""
54
+ return {
55
+ "platform": platform.system(),
56
+ "platform_version": platform.version(),
57
+ "python_version": sys.version,
58
+ "python_path": sys.executable,
59
+ }
60
+
61
+
62
+ def get_secrets_summary(secrets_path: Optional[Path]) -> dict:
63
+ """Extract information from the secrets file."""
64
+ result = {
65
+ "status": "not_found", # Default status: not found
66
+ "error": None,
67
+ "secrets": {},
68
+ }
69
+
70
+ if not secrets_path:
71
+ return result
72
+
73
+ if not secrets_path.exists():
74
+ result["status"] = "not_found"
75
+ return result
76
+
77
+ # File exists, attempt to parse
78
+ try:
79
+ with open(secrets_path, "r") as f:
80
+ secrets = yaml.safe_load(f)
81
+
82
+ # Mark as successfully parsed
83
+ result["status"] = "parsed"
84
+ result["secrets"] = secrets or {}
85
+
86
+ except Exception as e:
87
+ # File exists but has parse errors
88
+ result["status"] = "error"
89
+ result["error"] = str(e)
90
+ console.print(f"[yellow]Warning:[/yellow] Error parsing secrets file: {e}")
91
+
92
+ return result
93
+
94
+
95
+ def check_api_keys(secrets_summary: dict) -> dict:
96
+ """Check if API keys are configured in secrets file or environment."""
97
+ import os
98
+
99
+ # Initialize results dict using Provider enum values
100
+ results = {
101
+ provider.value: {"env": None, "config": None}
102
+ for provider in Provider
103
+ if provider != Provider.FAST_AGENT
104
+ } # Include GENERIC but exclude FAST_AGENT
105
+
106
+ # Get secrets if available
107
+ secrets = secrets_summary.get("secrets", {})
108
+ secrets_status = secrets_summary.get("status", "not_found")
109
+
110
+ # Check both environment variables and config file for each provider
111
+ for provider_value in results:
112
+ # Check environment variables using ProviderKeyManager
113
+ env_key_name = ProviderKeyManager.get_env_key_name(provider_value)
114
+ env_key_value = os.environ.get(env_key_name)
115
+ if env_key_value:
116
+ # Store the last 5 characters if key is long enough
117
+ if len(env_key_value) > 5:
118
+ results[provider_value]["env"] = f"...{env_key_value[-5:]}"
119
+ else:
120
+ results[provider_value]["env"] = "...***"
121
+
122
+ # Check secrets file if it was parsed successfully
123
+ if secrets_status == "parsed":
124
+ config_key = ProviderKeyManager.get_config_file_key(provider_value, secrets)
125
+ if config_key and config_key != API_KEY_HINT_TEXT:
126
+ # Store the last 5 characters if key is long enough
127
+ if len(config_key) > 5:
128
+ results[provider_value]["config"] = f"...{config_key[-5:]}"
129
+ else:
130
+ results[provider_value]["config"] = "...***"
131
+
132
+ return results
133
+
134
+
135
+ def get_fastagent_version() -> str:
136
+ """Get the installed version of FastAgent."""
137
+ try:
138
+ return version("fast-agent-mcp")
139
+ except: # noqa: E722
140
+ return "unknown"
141
+
142
+
143
+ def get_config_summary(config_path: Optional[Path]) -> dict:
144
+ """Extract key information from the configuration file."""
145
+ result = {
146
+ "status": "not_found", # Default status: not found
147
+ "error": None,
148
+ "default_model": "haiku (system default)",
149
+ "logger": {},
150
+ "mcp_servers": [],
151
+ }
152
+
153
+ if not config_path:
154
+ return result
155
+
156
+ if not config_path.exists():
157
+ result["status"] = "not_found"
158
+ return result
159
+
160
+ # File exists, attempt to parse
161
+ try:
162
+ with open(config_path, "r") as f:
163
+ config = yaml.safe_load(f)
164
+
165
+ # Mark as successfully parsed
166
+ result["status"] = "parsed"
167
+
168
+ if not config:
169
+ return result
170
+
171
+ # Get default model
172
+ if "default_model" in config:
173
+ result["default_model"] = config["default_model"]
174
+
175
+ # Get logger settings
176
+ if "logger" in config:
177
+ logger_config = config["logger"]
178
+ result["logger"] = {
179
+ "level": logger_config.get("level", "info (default)"),
180
+ "type": logger_config.get("type", "console (default)"),
181
+ "progress_display": str(logger_config.get("progress_display", True)),
182
+ "show_chat": str(logger_config.get("show_chat", True)),
183
+ "show_tools": str(logger_config.get("show_tools", True)),
184
+ }
185
+
186
+ # Get MCP server info
187
+ if "mcp" in config and "servers" in config["mcp"]:
188
+ for server_name, server_config in config["mcp"]["servers"].items():
189
+ server_info = {
190
+ "name": server_name,
191
+ "transport": "STDIO", # Default transport type
192
+ "command": "",
193
+ "url": "",
194
+ }
195
+
196
+ # Determine transport type
197
+ if "url" in server_config:
198
+ server_info["transport"] = "SSE"
199
+ server_info["url"] = server_config.get("url", "")
200
+
201
+ # Get command and args
202
+ command = server_config.get("command", "")
203
+ args = server_config.get("args", [])
204
+
205
+ if command:
206
+ if args:
207
+ args_str = " ".join([str(arg) for arg in args])
208
+ full_cmd = f"{command} {args_str}"
209
+ # Truncate if too long
210
+ if len(full_cmd) > 60:
211
+ full_cmd = full_cmd[:57] + "..."
212
+ server_info["command"] = full_cmd
213
+ else:
214
+ server_info["command"] = command
215
+
216
+ # Truncate URL if too long
217
+ if server_info["url"] and len(server_info["url"]) > 60:
218
+ server_info["url"] = server_info["url"][:57] + "..."
219
+
220
+ result["mcp_servers"].append(server_info)
221
+
222
+ except Exception as e:
223
+ # File exists but has parse errors
224
+ result["status"] = "error"
225
+ result["error"] = str(e)
226
+ console.print(f"[red]Error parsing configuration file:[/red] {e}")
227
+
228
+ return result
229
+
230
+
231
+ def show_check_summary() -> None:
232
+ """Show a summary of checks with colorful styling."""
233
+ cwd = Path.cwd()
234
+ config_files = find_config_files(cwd)
235
+ system_info = get_system_info()
236
+ config_summary = get_config_summary(config_files["config"])
237
+ secrets_summary = get_secrets_summary(config_files["secrets"])
238
+ api_keys = check_api_keys(secrets_summary)
239
+ fastagent_version = get_fastagent_version()
240
+
241
+ # System info panel
242
+ system_table = Table(show_header=False, box=None)
243
+ system_table.add_column("Key", style="cyan")
244
+ system_table.add_column("Value")
245
+
246
+ system_table.add_row("FastAgent Version", fastagent_version)
247
+ system_table.add_row("Platform", system_info["platform"])
248
+ system_table.add_row("Python Version", ".".join(system_info["python_version"].split(".")[:3]))
249
+ system_table.add_row("Python Path", system_info["python_path"])
250
+
251
+ console.print(Panel(system_table, title="System Information", border_style="blue"))
252
+
253
+ # Configuration files panel
254
+ config_path = config_files["config"]
255
+ secrets_path = config_files["secrets"]
256
+
257
+ files_table = Table(show_header=False, box=None)
258
+ files_table.add_column("Setting", style="cyan")
259
+ files_table.add_column("Value")
260
+
261
+ # Show secrets file status
262
+ secrets_status = secrets_summary.get("status", "not_found")
263
+ if secrets_status == "not_found":
264
+ files_table.add_row("Secrets File", "[yellow]Not found[/yellow]")
265
+ elif secrets_status == "error":
266
+ files_table.add_row("Secrets File", f"[orange_red1]Errors[/orange_red1] ({secrets_path})")
267
+ files_table.add_row(
268
+ "Secrets Error",
269
+ f"[orange_red1]{secrets_summary.get('error', 'Unknown error')}[/orange_red1]",
270
+ )
271
+ else: # parsed successfully
272
+ files_table.add_row("Secrets File", f"[green]Found[/green] ({secrets_path})")
273
+
274
+ # Show config file status
275
+ config_status = config_summary.get("status", "not_found")
276
+ if config_status == "not_found":
277
+ files_table.add_row("Config File", "[red]Not found[/red]")
278
+ elif config_status == "error":
279
+ files_table.add_row("Config File", f"[orange_red1]Errors[/orange_red1] ({config_path})")
280
+ files_table.add_row(
281
+ "Config Error",
282
+ f"[orange_red1]{config_summary.get('error', 'Unknown error')}[/orange_red1]",
283
+ )
284
+ else: # parsed successfully
285
+ files_table.add_row("Config File", f"[green]Found[/green] ({config_path})")
286
+ files_table.add_row(
287
+ "Default Model", config_summary.get("default_model", "haiku (system default)")
288
+ )
289
+
290
+ # Add logger settings if available
291
+ logger = config_summary.get("logger", {})
292
+ if logger:
293
+ files_table.add_row("Logger Level", logger.get("level", "info (default)"))
294
+ files_table.add_row("Logger Type", logger.get("type", "console (default)"))
295
+ files_table.add_row("Progress Display", logger.get("progress_display", "True"))
296
+ files_table.add_row("Show Chat", logger.get("show_chat", "True"))
297
+ files_table.add_row("Show Tools", logger.get("show_tools", "True"))
298
+
299
+ console.print(Panel(files_table, title="Configuration Files", border_style="blue"))
300
+
301
+ # API keys panel
302
+ keys_table = Table(show_header=True, box=None)
303
+ keys_table.add_column("Provider", style="cyan")
304
+ keys_table.add_column("Env", justify="center")
305
+ keys_table.add_column("Config", justify="center")
306
+ keys_table.add_column("Active Key", style="green")
307
+
308
+ for provider, status in api_keys.items():
309
+ # Environment key indicator
310
+ if status["env"] and status["config"]:
311
+ # Both exist but config takes precedence (env is present but not active)
312
+ env_status = "[yellow]✓[/yellow]"
313
+ elif status["env"]:
314
+ # Only env exists and is active
315
+ env_status = "[bold green]✓[/bold green]"
316
+ else:
317
+ # No env key
318
+ env_status = "[dim]✗[/dim]"
319
+
320
+ # Config file key indicator
321
+ if status["config"]:
322
+ # Config exists and takes precedence (is active)
323
+ config_status = "[bold green]✓[/bold green]"
324
+ else:
325
+ # No config key
326
+ config_status = "[dim]✗[/dim]"
327
+
328
+ # Display active key
329
+ if status["config"]:
330
+ # Config key is active
331
+ active = f"[bold green]{status['config']}[/bold green]"
332
+ elif status["env"]:
333
+ # Env key is active
334
+ active = f"[bold green]{status['env']}[/bold green]"
335
+ elif provider == "generic":
336
+ # Generic provider uses "ollama" as a default when no key is set
337
+ active = "[green]ollama (default)[/green]"
338
+ else:
339
+ # No key available for other providers
340
+ active = "[dim]Not configured[/dim]"
341
+
342
+ keys_table.add_row(provider.capitalize(), env_status, config_status, active)
343
+
344
+ console.print(Panel(keys_table, title="API Keys", border_style="blue"))
345
+
346
+ # MCP Servers panel (shown after API Keys)
347
+ if config_summary.get("status") == "parsed":
348
+ mcp_servers = config_summary.get("mcp_servers", [])
349
+ if mcp_servers:
350
+ servers_table = Table(show_header=True, box=None)
351
+ servers_table.add_column("Name", style="cyan")
352
+ servers_table.add_column("Transport", style="magenta")
353
+ servers_table.add_column("Command/URL")
354
+
355
+ for server in mcp_servers:
356
+ name = server["name"]
357
+ transport = server["transport"]
358
+
359
+ # Show either command or URL based on transport type
360
+ if transport == "STDIO":
361
+ command_url = server["command"] or "[dim]Not configured[/dim]"
362
+ servers_table.add_row(name, transport, command_url)
363
+ else: # SSE
364
+ command_url = server["url"] or "[dim]Not configured[/dim]"
365
+ servers_table.add_row(name, transport, command_url)
366
+
367
+ console.print(Panel(servers_table, title="MCP Servers", border_style="blue"))
368
+
369
+ # Show help tips
370
+ if config_status == "not_found" or secrets_status == "not_found":
371
+ console.print("\n[bold]Setup Tips:[/bold]")
372
+ console.print("Run [cyan]fast-agent setup[/cyan] to create configuration files")
373
+ elif config_status == "error" or secrets_status == "error":
374
+ console.print("\n[bold]Config File Issues:[/bold]")
375
+ console.print("Fix the YAML syntax errors in your configuration files")
376
+
377
+ if all(
378
+ not api_keys[provider]["env"] and not api_keys[provider]["config"] for provider in api_keys
379
+ ):
380
+ console.print(
381
+ "\n[yellow]No API keys configured. Set up API keys to use LLM services:[/yellow]"
382
+ )
383
+ console.print("1. Add keys to fastagent.secrets.yaml")
384
+ env_vars = ", ".join(
385
+ [
386
+ ProviderKeyManager.get_env_key_name(p.value)
387
+ for p in Provider
388
+ if p != Provider.FAST_AGENT
389
+ ]
390
+ )
391
+ console.print(f"2. Or set environment variables ({env_vars})")
392
+
393
+
394
+ @app.command()
395
+ def show(
396
+ path: Optional[str] = typer.Argument(None, help="Path to configuration file to display"),
397
+ secrets: bool = typer.Option(
398
+ False, "--secrets", "-s", help="Show secrets file instead of config"
399
+ ),
400
+ ) -> None:
401
+ """Display the configuration file content or search for it."""
402
+ file_type = "secrets" if secrets else "config"
403
+
404
+ if path:
405
+ config_path = Path(path).resolve()
406
+ if not config_path.exists():
407
+ console.print(
408
+ f"[red]Error:[/red] {file_type.capitalize()} file not found at {config_path}"
409
+ )
410
+ raise typer.Exit(1)
411
+ else:
412
+ config_files = find_config_files(Path.cwd())
413
+ config_path = config_files[file_type]
414
+ if not config_path:
415
+ console.print(
416
+ f"[yellow]No {file_type} file found in current directory or parents[/yellow]"
417
+ )
418
+ console.print("Run [cyan]fast-agent setup[/cyan] to create configuration files")
419
+ raise typer.Exit(1)
420
+
421
+ console.print(f"\n[bold]{file_type.capitalize()} file:[/bold] {config_path}\n")
422
+
423
+ try:
424
+ with open(config_path, "r") as f:
425
+ content = f.read()
426
+
427
+ # Try to parse as YAML to check validity
428
+ parsed = yaml.safe_load(content)
429
+
430
+ # Show parsing success status
431
+ console.print("[green]YAML syntax is valid[/green]")
432
+ if parsed is None:
433
+ console.print("[yellow]Warning: File is empty or contains only comments[/yellow]\n")
434
+ else:
435
+ console.print(
436
+ f"[green]Successfully parsed {len(parsed) if isinstance(parsed, dict) else 0} root keys[/green]\n"
437
+ )
438
+
439
+ # Print the content
440
+ console.print(content)
441
+
442
+ except Exception as e:
443
+ console.print(f"[red]Error parsing {file_type} file:[/red] {e}")
444
+
445
+
446
+ @app.callback(invoke_without_command=True)
447
+ def main(ctx: typer.Context) -> None:
448
+ """Check and diagnose FastAgent configuration."""
449
+ if ctx.invoked_subcommand is None:
450
+ show_check_summary()
@@ -190,7 +190,7 @@ def init(
190
190
  # Check for existing .gitignore
191
191
  needs_gitignore = not find_gitignore(config_path)
192
192
 
193
- console.print("\n[bold]fast-agent Setup[/bold]\n")
193
+ console.print("\n[bold]fast-agent setup[/bold]\n")
194
194
  console.print("This will create the following files:")
195
195
  console.print(f" - {config_path}/fastagent.config.yaml")
196
196
  console.print(f" - {config_path}/fastagent.secrets.yaml")
mcp_agent/cli/main.py CHANGED
@@ -4,7 +4,7 @@ import typer
4
4
  from rich.console import Console
5
5
  from rich.table import Table
6
6
 
7
- from mcp_agent.cli.commands import quickstart, setup
7
+ from mcp_agent.cli.commands import check_config, quickstart, setup
8
8
  from mcp_agent.cli.terminal import Application
9
9
 
10
10
  app = typer.Typer(
@@ -14,6 +14,7 @@ app = typer.Typer(
14
14
 
15
15
  # Subcommands
16
16
  app.add_typer(setup.app, name="setup", help="Set up a new agent project")
17
+ app.add_typer(check_config.app, name="check", help="Show or diagnose fast-agent configuration")
17
18
  app.add_typer(quickstart.app, name="bootstrap", help="Create example applications")
18
19
  app.add_typer(quickstart.app, name="quickstart", help="Create example applications")
19
20
 
@@ -31,30 +32,22 @@ def show_welcome() -> None:
31
32
  except: # noqa: E722
32
33
  app_version = "unknown"
33
34
 
34
- console.print(f"\n[bold]fast-agent (fast-agent-mcp) {app_version}[/bold]")
35
- console.print("Build effective agents using Model Context Protocol (MCP)")
35
+ console.print(f"\nfast-agent {app_version} [dim](fast-agent-mcp)[/dim] ")
36
36
 
37
37
  # Create a table for commands
38
38
  table = Table(title="\nAvailable Commands")
39
39
  table.add_column("Command", style="green")
40
40
  table.add_column("Description")
41
41
 
42
- table.add_row("setup", "Set up a new agent project with configuration files")
42
+ table.add_row("setup", "Create a new agent and configuration files")
43
+ table.add_row("check", "Show or diagnose fast-agent configuration")
43
44
  table.add_row("quickstart", "Create example applications (workflow, researcher, etc.)")
44
- # table.add_row("config", "Manage agent configuration settings")
45
45
 
46
46
  console.print(table)
47
47
 
48
- console.print("\n[bold]Getting Started:[/bold]")
49
- console.print("1. Set up a new project:")
50
- console.print(" fastagent setup")
51
- console.print("\n2. Create Building Effective Agents workflow examples:")
52
- console.print(" fastagent quickstart workflow")
53
- console.print("\n3. Explore other examples:")
54
- console.print(" fastagent quickstart")
55
-
56
- console.print("\nUse --help with any command for more information")
57
- console.print("Example: fastagent quickstart --help")
48
+ console.print(
49
+ "\n[italic]get started with:[/italic] [cyan]fast-agent[/cyan] [green]setup[/green]"
50
+ )
58
51
 
59
52
 
60
53
  @app.callback(invoke_without_command=True)
@@ -2,11 +2,11 @@
2
2
  Type definitions for agents and agent configurations.
3
3
  """
4
4
 
5
- import dataclasses
6
- from dataclasses import dataclass
7
5
  from enum import Enum
8
6
  from typing import List
9
7
 
8
+ from pydantic import BaseModel, Field, model_validator
9
+
10
10
  # Forward imports to avoid circular dependencies
11
11
  from mcp_agent.core.request_params import RequestParams
12
12
 
@@ -22,22 +22,21 @@ class AgentType(Enum):
22
22
  CHAIN = "chain"
23
23
 
24
24
 
25
- @dataclass
26
- class AgentConfig:
25
+ class AgentConfig(BaseModel):
27
26
  """Configuration for an Agent instance"""
28
27
 
29
28
  name: str
30
29
  instruction: str = "You are a helpful agent."
31
- servers: List[str] = dataclasses.field(default_factory=list)
30
+ servers: List[str] = Field(default_factory=list)
32
31
  model: str | None = None
33
32
  use_history: bool = True
34
33
  default_request_params: RequestParams | None = None
35
34
  human_input: bool = False
36
- agent_type: str = AgentType.BASIC.value
35
+ agent_type: AgentType = AgentType.BASIC
37
36
 
38
- def __post_init__(self) -> None:
37
+ @model_validator(mode='after')
38
+ def ensure_default_request_params(self) -> 'AgentConfig':
39
39
  """Ensure default_request_params exists with proper history setting"""
40
-
41
40
  if self.default_request_params is None:
42
41
  self.default_request_params = RequestParams(
43
42
  use_history=self.use_history, systemPrompt=self.instruction
@@ -45,3 +44,4 @@ class AgentConfig:
45
44
  else:
46
45
  # Override the request params history setting if explicitly configured
47
46
  self.default_request_params.use_history = self.use_history
47
+ return self
@@ -206,15 +206,22 @@ def agent(
206
206
  )
207
207
 
208
208
 
209
+ DEFAULT_INSTRUCTION_ORCHESTRATOR = """
210
+ You are an expert planner. Given an objective task and a list of Agents
211
+ (which are collections of capabilities), your job is to break down the objective
212
+ into a series of steps, which can be performed by these agents.
213
+ """
214
+
215
+
209
216
  def orchestrator(
210
217
  self,
211
218
  name: str,
212
219
  *,
213
220
  agents: List[str],
214
- instruction: Optional[str] = None,
221
+ instruction: str = DEFAULT_INSTRUCTION_ORCHESTRATOR,
215
222
  model: Optional[str] = None,
216
- use_history: bool = False,
217
223
  request_params: RequestParams | None = None,
224
+ use_history: bool = False,
218
225
  human_input: bool = False,
219
226
  plan_type: Literal["full", "iterative"] = "full",
220
227
  max_iterations: int = 30,
@@ -236,11 +243,6 @@ def orchestrator(
236
243
  Returns:
237
244
  A decorator that registers the orchestrator with proper type annotations
238
245
  """
239
- default_instruction = """
240
- You are an expert planner. Given an objective task and a list of Agents
241
- (which are collections of capabilities), your job is to break down the objective
242
- into a series of steps, which can be performed by these agents.
243
- """
244
246
 
245
247
  # Create final request params with max_iterations
246
248
 
@@ -250,7 +252,7 @@ def orchestrator(
250
252
  self,
251
253
  AgentType.ORCHESTRATOR,
252
254
  name=name,
253
- instruction=instruction or default_instruction,
255
+ instruction=instruction,
254
256
  servers=[], # Orchestrators don't connect to servers directly
255
257
  model=model,
256
258
  use_history=use_history,
@@ -133,11 +133,13 @@ async def create_agents_by_type(
133
133
  },
134
134
  )
135
135
 
136
+ # Compare type string from config with Enum value
136
137
  if agent_data["type"] == agent_type.value:
137
138
  # Get common configuration
138
139
  config = agent_data["config"]
139
140
 
140
- # Type-specific initialization
141
+ # Type-specific initialization based on the Enum type
142
+ # Note: Above we compared string values from config, here we compare Enum objects directly
141
143
  if agent_type == AgentType.BASIC:
142
144
  # Create a basic agent
143
145
  agent = Agent(
@@ -338,6 +340,7 @@ async def create_agents_in_dependency_order(
338
340
  # Create agent proxies for each group in dependency order
339
341
  for group in dependencies:
340
342
  # Create basic agents first
343
+ # Note: We compare string values from config with the Enum's string value
341
344
  if AgentType.BASIC.value in [agents_dict[name]["type"] for name in group]:
342
345
  basic_agents = await create_agents_by_type(
343
346
  app_instance,
@@ -51,8 +51,9 @@ def validate_workflow_references(agents: Dict[str, Dict[str, Any]]) -> None:
51
51
  available_components = set(agents.keys())
52
52
 
53
53
  for name, agent_data in agents.items():
54
- agent_type = agent_data["type"]
55
-
54
+ agent_type = agent_data["type"] # This is a string from config
55
+
56
+ # Note: Compare string values from config with the Enum's string value
56
57
  if agent_type == AgentType.PARALLEL.value:
57
58
  # Check fan_in exists
58
59
  fan_in = agent_data["fan_in"]
@@ -224,8 +225,9 @@ def get_dependencies_groups(
224
225
 
225
226
  # Build the dependency graph
226
227
  for name, agent_data in agents_dict.items():
227
- agent_type = agent_data["type"]
228
-
228
+ agent_type = agent_data["type"] # This is a string from config
229
+
230
+ # Note: Compare string values from config with the Enum's string value
229
231
  if agent_type == AgentType.PARALLEL.value:
230
232
  # Parallel agents depend on their fan-out and fan-in agents
231
233
  dependencies[name].update(agent_data.get("parallel_agents", []))
@@ -1,9 +1,10 @@
1
1
  """Module for converting log events to progress events."""
2
2
 
3
- from dataclasses import dataclass
4
3
  from enum import Enum
5
4
  from typing import Optional
6
5
 
6
+ from pydantic import BaseModel
7
+
7
8
  from mcp_agent.logging.events import Event
8
9
 
9
10
 
@@ -24,8 +25,7 @@ class ProgressAction(str, Enum):
24
25
  FATAL_ERROR = "Error"
25
26
 
26
27
 
27
- @dataclass
28
- class ProgressEvent:
28
+ class ProgressEvent(BaseModel):
29
29
  """Represents a progress event converted from a log event."""
30
30
 
31
31
  action: ProgressAction
@@ -87,8 +87,8 @@ def convert_log_event(event: Event) -> Optional[ProgressEvent]:
87
87
  target = event_data.get("target", "unknown")
88
88
 
89
89
  return ProgressEvent(
90
- ProgressAction(progress_action),
91
- target,
92
- details,
90
+ action=ProgressAction(progress_action),
91
+ target=target,
92
+ details=details,
93
93
  agent_name=event_data.get("agent_name"),
94
94
  )