fast-agent-mcp 0.0.7__py3-none-any.whl → 0.0.8__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.
- {fast_agent_mcp-0.0.7.dist-info → fast_agent_mcp-0.0.8.dist-info}/METADATA +22 -57
- {fast_agent_mcp-0.0.7.dist-info → fast_agent_mcp-0.0.8.dist-info}/RECORD +29 -23
- mcp_agent/agents/agent.py +8 -4
- mcp_agent/app.py +5 -1
- mcp_agent/cli/commands/bootstrap.py +180 -121
- mcp_agent/cli/commands/setup.py +20 -16
- mcp_agent/core/__init__.py +0 -0
- mcp_agent/core/exceptions.py +47 -0
- mcp_agent/core/fastagent.py +176 -85
- mcp_agent/core/server_validation.py +44 -0
- mcp_agent/event_progress.py +4 -1
- mcp_agent/logging/rich_progress.py +11 -0
- mcp_agent/mcp/mcp_connection_manager.py +11 -2
- mcp_agent/resources/examples/data-analysis/analysis.py +35 -0
- mcp_agent/resources/examples/data-analysis/fastagent.config.yaml +20 -0
- mcp_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +1471 -0
- mcp_agent/resources/examples/workflows/chaining.py +31 -0
- mcp_agent/resources/examples/{decorator/optimizer.py → workflows/evaluator.py} +7 -10
- mcp_agent/resources/examples/workflows/human_input.py +26 -0
- mcp_agent/resources/examples/{decorator → workflows}/orchestrator.py +20 -11
- mcp_agent/resources/examples/{decorator → workflows}/parallel.py +14 -18
- mcp_agent/resources/examples/{decorator → workflows}/router.py +9 -10
- mcp_agent/workflows/llm/augmented_llm_anthropic.py +48 -12
- mcp_agent/workflows/llm/augmented_llm_openai.py +38 -9
- mcp_agent/resources/examples/decorator/main.py +0 -26
- mcp_agent/resources/examples/decorator/tiny.py +0 -22
- {fast_agent_mcp-0.0.7.dist-info → fast_agent_mcp-0.0.8.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.0.7.dist-info → fast_agent_mcp-0.0.8.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.0.7.dist-info → fast_agent_mcp-0.0.8.dist-info}/licenses/LICENSE +0 -0
- /mcp_agent/resources/examples/mcp_researcher/{main-evalopt.py → researcher-eval.py} +0 -0
- /mcp_agent/resources/examples/mcp_researcher/{main.py → researcher.py} +0 -0
mcp_agent/core/fastagent.py
CHANGED
|
@@ -9,6 +9,13 @@ import yaml
|
|
|
9
9
|
import argparse
|
|
10
10
|
from contextlib import asynccontextmanager
|
|
11
11
|
|
|
12
|
+
from mcp_agent.core.exceptions import (
|
|
13
|
+
AgentConfigError,
|
|
14
|
+
ServerConfigError,
|
|
15
|
+
ProviderKeyError,
|
|
16
|
+
ServerInitializationError,
|
|
17
|
+
)
|
|
18
|
+
|
|
12
19
|
from mcp_agent.app import MCPApp
|
|
13
20
|
from mcp_agent.agents.agent import Agent, AgentConfig
|
|
14
21
|
from mcp_agent.context_dependent import ContextDependent
|
|
@@ -28,7 +35,6 @@ from mcp_agent.workflows.llm.augmented_llm import RequestParams
|
|
|
28
35
|
|
|
29
36
|
import readline # noqa: F401
|
|
30
37
|
|
|
31
|
-
|
|
32
38
|
# Type aliases for better readability
|
|
33
39
|
WorkflowType: TypeAlias = Union[
|
|
34
40
|
Orchestrator, ParallelLLM, EvaluatorOptimizerLLM, LLMRouter
|
|
@@ -304,6 +310,85 @@ class FastAgent(ContextDependent):
|
|
|
304
310
|
with open(self.config_path) as f:
|
|
305
311
|
self.config = yaml.safe_load(f)
|
|
306
312
|
|
|
313
|
+
def _validate_server_references(self) -> None:
|
|
314
|
+
"""
|
|
315
|
+
Validate that all server references in agent configurations exist in config.
|
|
316
|
+
Raises ServerConfigError if any referenced servers are not defined.
|
|
317
|
+
"""
|
|
318
|
+
if not self.context.config.mcp or not self.context.config.mcp.servers:
|
|
319
|
+
available_servers = set()
|
|
320
|
+
else:
|
|
321
|
+
available_servers = set(self.context.config.mcp.servers.keys())
|
|
322
|
+
|
|
323
|
+
# Check each agent's server references
|
|
324
|
+
for name, agent_data in self.agents.items():
|
|
325
|
+
config = agent_data["config"]
|
|
326
|
+
if config.servers:
|
|
327
|
+
missing = [s for s in config.servers if s not in available_servers]
|
|
328
|
+
if missing:
|
|
329
|
+
raise ServerConfigError(
|
|
330
|
+
f"Missing server configuration for agent '{name}'",
|
|
331
|
+
f"The following servers are referenced but not defined in config: {', '.join(missing)}",
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
def _validate_workflow_references(self) -> None:
|
|
335
|
+
"""
|
|
336
|
+
Validate that all workflow references point to valid agents/workflows.
|
|
337
|
+
Raises ValueError if any referenced components are not defined.
|
|
338
|
+
"""
|
|
339
|
+
available_components = set(self.agents.keys())
|
|
340
|
+
|
|
341
|
+
for name, agent_data in self.agents.items():
|
|
342
|
+
agent_type = agent_data["type"]
|
|
343
|
+
|
|
344
|
+
if agent_type == AgentType.PARALLEL.value:
|
|
345
|
+
# Check fan_in exists
|
|
346
|
+
fan_in = agent_data["fan_in"]
|
|
347
|
+
if fan_in not in available_components:
|
|
348
|
+
raise AgentConfigError(
|
|
349
|
+
f"Parallel workflow '{name}' references non-existent fan_in component: {fan_in}"
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
# Check fan_out agents exist
|
|
353
|
+
fan_out = agent_data["fan_out"]
|
|
354
|
+
missing = [a for a in fan_out if a not in available_components]
|
|
355
|
+
if missing:
|
|
356
|
+
raise AgentConfigError(
|
|
357
|
+
f"Parallel workflow '{name}' references non-existent fan_out components: {', '.join(missing)}"
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
elif agent_type == AgentType.ORCHESTRATOR.value:
|
|
361
|
+
# Check all child agents exist
|
|
362
|
+
child_agents = agent_data["child_agents"]
|
|
363
|
+
missing = [a for a in child_agents if a not in available_components]
|
|
364
|
+
if missing:
|
|
365
|
+
raise AgentConfigError(
|
|
366
|
+
f"Orchestrator '{name}' references non-existent agents: {', '.join(missing)}"
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
elif agent_type == AgentType.ROUTER.value:
|
|
370
|
+
# Check all referenced agents exist
|
|
371
|
+
router_agents = agent_data["agents"]
|
|
372
|
+
missing = [a for a in router_agents if a not in available_components]
|
|
373
|
+
if missing:
|
|
374
|
+
raise AgentConfigError(
|
|
375
|
+
f"Router '{name}' references non-existent agents: {', '.join(missing)}"
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
elif agent_type == AgentType.EVALUATOR_OPTIMIZER.value:
|
|
379
|
+
# Check both evaluator and optimizer exist
|
|
380
|
+
evaluator = agent_data["evaluator"]
|
|
381
|
+
optimizer = agent_data["optimizer"]
|
|
382
|
+
missing = []
|
|
383
|
+
if evaluator not in available_components:
|
|
384
|
+
missing.append(f"evaluator: {evaluator}")
|
|
385
|
+
if optimizer not in available_components:
|
|
386
|
+
missing.append(f"optimizer: {optimizer}")
|
|
387
|
+
if missing:
|
|
388
|
+
raise AgentConfigError(
|
|
389
|
+
f"Evaluator-Optimizer '{name}' references non-existent components: {', '.join(missing)}"
|
|
390
|
+
)
|
|
391
|
+
|
|
307
392
|
def _get_model_factory(
|
|
308
393
|
self,
|
|
309
394
|
model: Optional[str] = None,
|
|
@@ -350,6 +435,7 @@ class FastAgent(ContextDependent):
|
|
|
350
435
|
model: Optional[str] = None,
|
|
351
436
|
use_history: bool = True,
|
|
352
437
|
request_params: Optional[Dict] = None,
|
|
438
|
+
human_input: bool = False,
|
|
353
439
|
) -> Callable:
|
|
354
440
|
"""
|
|
355
441
|
Decorator to create and register an agent with configuration.
|
|
@@ -362,9 +448,9 @@ class FastAgent(ContextDependent):
|
|
|
362
448
|
use_history: Whether to maintain conversation history
|
|
363
449
|
request_params: Additional request parameters for the LLM
|
|
364
450
|
"""
|
|
365
|
-
# print(f"\nDecorating agent {name} with model={model}")
|
|
366
451
|
|
|
367
452
|
def decorator(func: Callable) -> Callable:
|
|
453
|
+
# Create base request params
|
|
368
454
|
base_params = RequestParams(
|
|
369
455
|
use_history=use_history,
|
|
370
456
|
model=model, # Include model in initial params
|
|
@@ -380,6 +466,7 @@ class FastAgent(ContextDependent):
|
|
|
380
466
|
model=model, # Highest precedence
|
|
381
467
|
use_history=use_history,
|
|
382
468
|
default_request_params=base_params,
|
|
469
|
+
human_input=human_input,
|
|
383
470
|
)
|
|
384
471
|
|
|
385
472
|
# Store the agent configuration
|
|
@@ -389,10 +476,7 @@ class FastAgent(ContextDependent):
|
|
|
389
476
|
"func": func,
|
|
390
477
|
}
|
|
391
478
|
|
|
392
|
-
|
|
393
|
-
return await func(*args, **kwargs)
|
|
394
|
-
|
|
395
|
-
return wrapper
|
|
479
|
+
return func # Don't wrap the function, just return it
|
|
396
480
|
|
|
397
481
|
return decorator
|
|
398
482
|
|
|
@@ -404,6 +488,7 @@ class FastAgent(ContextDependent):
|
|
|
404
488
|
model: str | None = None,
|
|
405
489
|
use_history: bool = True,
|
|
406
490
|
request_params: Optional[Dict] = None,
|
|
491
|
+
human_input: bool = False,
|
|
407
492
|
) -> Callable:
|
|
408
493
|
"""
|
|
409
494
|
Decorator to create and register an orchestrator.
|
|
@@ -431,6 +516,7 @@ class FastAgent(ContextDependent):
|
|
|
431
516
|
model=model, # Highest precedence
|
|
432
517
|
use_history=use_history,
|
|
433
518
|
default_request_params=base_params,
|
|
519
|
+
human_input=human_input,
|
|
434
520
|
)
|
|
435
521
|
|
|
436
522
|
# Store the orchestrator configuration
|
|
@@ -441,10 +527,7 @@ class FastAgent(ContextDependent):
|
|
|
441
527
|
"func": func,
|
|
442
528
|
}
|
|
443
529
|
|
|
444
|
-
|
|
445
|
-
return await func(*args, **kwargs)
|
|
446
|
-
|
|
447
|
-
return wrapper
|
|
530
|
+
return func
|
|
448
531
|
|
|
449
532
|
return decorator
|
|
450
533
|
|
|
@@ -495,10 +578,7 @@ class FastAgent(ContextDependent):
|
|
|
495
578
|
"func": func,
|
|
496
579
|
}
|
|
497
580
|
|
|
498
|
-
|
|
499
|
-
return await func(*args, **kwargs)
|
|
500
|
-
|
|
501
|
-
return wrapper
|
|
581
|
+
return func
|
|
502
582
|
|
|
503
583
|
return decorator
|
|
504
584
|
|
|
@@ -557,10 +637,11 @@ class FastAgent(ContextDependent):
|
|
|
557
637
|
self,
|
|
558
638
|
name: str,
|
|
559
639
|
agents: List[str],
|
|
560
|
-
servers: List[str] = [],
|
|
640
|
+
# servers: List[str] = [],
|
|
561
641
|
model: Optional[str] = None,
|
|
562
642
|
use_history: bool = True,
|
|
563
643
|
request_params: Optional[Dict] = None,
|
|
644
|
+
human_input: bool = False,
|
|
564
645
|
) -> Callable:
|
|
565
646
|
"""
|
|
566
647
|
Decorator to create and register a router.
|
|
@@ -584,10 +665,11 @@ class FastAgent(ContextDependent):
|
|
|
584
665
|
config = AgentConfig(
|
|
585
666
|
name=name,
|
|
586
667
|
instruction="", # Router uses its own routing instruction
|
|
587
|
-
servers=servers
|
|
668
|
+
servers=[], # , servers are not supported now
|
|
588
669
|
model=model,
|
|
589
670
|
use_history=use_history,
|
|
590
671
|
default_request_params=base_params,
|
|
672
|
+
human_input=human_input,
|
|
591
673
|
)
|
|
592
674
|
|
|
593
675
|
# Store the router configuration
|
|
@@ -937,77 +1019,86 @@ class FastAgent(ContextDependent):
|
|
|
937
1019
|
async def run(self):
|
|
938
1020
|
"""
|
|
939
1021
|
Context manager for running the application.
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
agent_app
|
|
952
|
-
|
|
953
|
-
|
|
1022
|
+
Performs validation and provides user-friendly error messages.
|
|
1023
|
+
"""
|
|
1024
|
+
active_agents = {}
|
|
1025
|
+
had_error = False
|
|
1026
|
+
try:
|
|
1027
|
+
async with self.app.run() as agent_app:
|
|
1028
|
+
# Pre-flight validation
|
|
1029
|
+
self._validate_server_references()
|
|
1030
|
+
self._validate_workflow_references()
|
|
1031
|
+
|
|
1032
|
+
# Create all types of agents
|
|
1033
|
+
active_agents = await self._create_basic_agents(agent_app)
|
|
1034
|
+
orchestrators = self._create_orchestrators(agent_app, active_agents)
|
|
1035
|
+
parallel_agents = self._create_parallel_agents(agent_app, active_agents)
|
|
1036
|
+
evaluator_optimizers = await self._create_evaluator_optimizers(
|
|
1037
|
+
agent_app, active_agents
|
|
1038
|
+
)
|
|
1039
|
+
routers = self._create_routers(agent_app, active_agents)
|
|
954
1040
|
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
1041
|
+
# Merge all agents into active_agents
|
|
1042
|
+
active_agents.update(orchestrators)
|
|
1043
|
+
active_agents.update(parallel_agents)
|
|
1044
|
+
active_agents.update(evaluator_optimizers)
|
|
1045
|
+
active_agents.update(routers)
|
|
960
1046
|
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
try:
|
|
1047
|
+
# Create wrapper with all agents
|
|
1048
|
+
wrapper = AgentApp(agent_app, active_agents)
|
|
964
1049
|
yield wrapper
|
|
965
|
-
|
|
966
|
-
|
|
1050
|
+
|
|
1051
|
+
except ServerConfigError as e:
|
|
1052
|
+
had_error = True
|
|
1053
|
+
print("\n[bold red]Server Configuration Error:")
|
|
1054
|
+
print(e.message)
|
|
1055
|
+
if e.details:
|
|
1056
|
+
print("\nDetails:")
|
|
1057
|
+
print(e.details)
|
|
1058
|
+
print(
|
|
1059
|
+
"\nPlease check your 'fastagent.config.yaml' configuration file and add the missing server definitions."
|
|
1060
|
+
)
|
|
1061
|
+
raise SystemExit(1)
|
|
1062
|
+
|
|
1063
|
+
except ProviderKeyError as e:
|
|
1064
|
+
had_error = True
|
|
1065
|
+
print("\n[bold red]Provider Configuration Error:")
|
|
1066
|
+
print(e.message)
|
|
1067
|
+
if e.details:
|
|
1068
|
+
print("\nDetails:")
|
|
1069
|
+
print(e.details)
|
|
1070
|
+
print(
|
|
1071
|
+
"\nPlease check your 'fastagent.secrets.yaml' configuration file and ensure all required API keys are set."
|
|
1072
|
+
)
|
|
1073
|
+
raise SystemExit(1)
|
|
1074
|
+
|
|
1075
|
+
except AgentConfigError as e:
|
|
1076
|
+
had_error = True
|
|
1077
|
+
print("\n[bold red]Workflow or Agent Configuration Error:")
|
|
1078
|
+
print(e.message)
|
|
1079
|
+
if e.details:
|
|
1080
|
+
print("\nDetails:")
|
|
1081
|
+
print(e.details)
|
|
1082
|
+
print(
|
|
1083
|
+
"\nPlease check your agent definition and ensure names and references are correct."
|
|
1084
|
+
)
|
|
1085
|
+
raise SystemExit(1)
|
|
1086
|
+
|
|
1087
|
+
except ServerInitializationError as e:
|
|
1088
|
+
had_error = True
|
|
1089
|
+
print("\n[bold red]Server Startup Error:")
|
|
1090
|
+
print(e.message)
|
|
1091
|
+
if e.details:
|
|
1092
|
+
print("\nDetails:")
|
|
1093
|
+
print(e.details)
|
|
1094
|
+
print("\nThere was an error starting up the MCP Server.")
|
|
1095
|
+
raise SystemExit(1)
|
|
1096
|
+
finally:
|
|
1097
|
+
# Clean up any active agents without re-raising errors
|
|
1098
|
+
if active_agents and not had_error:
|
|
967
1099
|
for name, proxy in active_agents.items():
|
|
968
1100
|
if isinstance(proxy, LLMAgentProxy):
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
# Send a message to a specific agent and get the response.
|
|
974
|
-
|
|
975
|
-
# Args:
|
|
976
|
-
# agent_name: Name of the target agent
|
|
977
|
-
# message: Message to send
|
|
978
|
-
|
|
979
|
-
# Returns:
|
|
980
|
-
# Agent's response
|
|
981
|
-
|
|
982
|
-
# Raises:
|
|
983
|
-
# ValueError: If agent not found
|
|
984
|
-
# RuntimeError: If agent has no LLM attached
|
|
985
|
-
# """
|
|
986
|
-
# if agent_name not in self.agents:
|
|
987
|
-
# raise ValueError(f"Agent {agent_name} not found")
|
|
988
|
-
|
|
989
|
-
# agent = self.agents[agent_name]
|
|
990
|
-
|
|
991
|
-
# # Special handling for routers
|
|
992
|
-
# if isinstance(agent._llm, LLMRouter):
|
|
993
|
-
# # Route the message and get results
|
|
994
|
-
# results = await agent._llm.route(message)
|
|
995
|
-
# if not results:
|
|
996
|
-
# return "No appropriate route found for the request."
|
|
997
|
-
|
|
998
|
-
# # Get the top result
|
|
999
|
-
# top_result = results[0]
|
|
1000
|
-
# if isinstance(top_result.result, Agent):
|
|
1001
|
-
# # Agent route - delegate to the agent
|
|
1002
|
-
# return await top_result.result._llm.generate_str(message)
|
|
1003
|
-
# elif isinstance(top_result.result, str):
|
|
1004
|
-
# # Server route - use the router directly
|
|
1005
|
-
# return "Tool call requested by router - not yet supported"
|
|
1006
|
-
# else:
|
|
1007
|
-
# return f"Routed to: {top_result.result} ({top_result.confidence}): {top_result.reasoning}"
|
|
1008
|
-
|
|
1009
|
-
# # Normal agent handling
|
|
1010
|
-
# if not hasattr(agent, "_llm") or agent._llm is None:
|
|
1011
|
-
# raise RuntimeError(f"Agent {agent_name} has no LLM attached")
|
|
1012
|
-
|
|
1013
|
-
# return await agent._llm.generate_str(message)
|
|
1101
|
+
try:
|
|
1102
|
+
await proxy._agent.__aexit__(None, None, None)
|
|
1103
|
+
except Exception:
|
|
1104
|
+
pass # Ignore cleanup errors
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""FastAgent validation methods."""
|
|
2
|
+
|
|
3
|
+
from mcp_agent.core.exceptions import ServerConfigError
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _validate_server_references(self) -> None:
|
|
7
|
+
"""
|
|
8
|
+
Validate that all server references in agent configurations exist in config.
|
|
9
|
+
Raises ServerConfigError if any referenced servers are not defined.
|
|
10
|
+
"""
|
|
11
|
+
# First check if any agents need servers
|
|
12
|
+
agents_needing_servers = {
|
|
13
|
+
name: agent_data["config"].servers
|
|
14
|
+
for name, agent_data in self.agents.items()
|
|
15
|
+
if agent_data["config"].servers
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if not agents_needing_servers:
|
|
19
|
+
return # No validation needed
|
|
20
|
+
|
|
21
|
+
# If we need servers, verify MCP config exists
|
|
22
|
+
if not hasattr(self.context.config, "mcp"):
|
|
23
|
+
raise ServerConfigError(
|
|
24
|
+
"MCP configuration missing",
|
|
25
|
+
"Agents require server access but no MCP configuration found.\n"
|
|
26
|
+
"Add an 'mcp' section to your configuration file.",
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
if not self.context.config.mcp.servers:
|
|
30
|
+
raise ServerConfigError(
|
|
31
|
+
"No MCP servers configured",
|
|
32
|
+
"Agents require server access but no servers are defined.\n"
|
|
33
|
+
"Add server definitions under mcp.servers in your configuration file.",
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# Now check each agent's servers exist
|
|
37
|
+
available_servers = set(self.context.config.mcp.servers.keys())
|
|
38
|
+
for name, servers in agents_needing_servers.items():
|
|
39
|
+
missing = [s for s in servers if s not in available_servers]
|
|
40
|
+
if missing:
|
|
41
|
+
raise ServerConfigError(
|
|
42
|
+
f"Missing server configuration for agent '{name}'",
|
|
43
|
+
f"The following servers are referenced but not defined in config: {', '.join(missing)}",
|
|
44
|
+
)
|
mcp_agent/event_progress.py
CHANGED
|
@@ -19,6 +19,7 @@ class ProgressAction(str, Enum):
|
|
|
19
19
|
SHUTDOWN = "Shutdown"
|
|
20
20
|
AGGREGATOR_INITIALIZED = "Running"
|
|
21
21
|
ROUTING = "Routing"
|
|
22
|
+
FATAL_ERROR = "Error"
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
@dataclass
|
|
@@ -61,7 +62,9 @@ def convert_log_event(event: Event) -> Optional[ProgressEvent]:
|
|
|
61
62
|
agent_name = event_data.get("agent_name")
|
|
62
63
|
target = agent_name
|
|
63
64
|
details = ""
|
|
64
|
-
if
|
|
65
|
+
if progress_action == ProgressAction.FATAL_ERROR:
|
|
66
|
+
details = event_data.get("error_message", "An error occurred")
|
|
67
|
+
elif "mcp_aggregator" in namespace:
|
|
65
68
|
server_name = event_data.get("server_name", "")
|
|
66
69
|
tool_name = event_data.get("tool_name")
|
|
67
70
|
if tool_name:
|
|
@@ -76,6 +76,7 @@ class RichProgressDisplay:
|
|
|
76
76
|
ProgressAction.FINISHED: "black on green",
|
|
77
77
|
ProgressAction.SHUTDOWN: "black on red",
|
|
78
78
|
ProgressAction.AGGREGATOR_INITIALIZED: "bold green",
|
|
79
|
+
ProgressAction.FATAL_ERROR: "black on red",
|
|
79
80
|
}.get(action, "white")
|
|
80
81
|
|
|
81
82
|
def update(self, event: ProgressEvent) -> None:
|
|
@@ -116,5 +117,15 @@ class RichProgressDisplay:
|
|
|
116
117
|
for task in self._progress.tasks:
|
|
117
118
|
if task.id != task_id:
|
|
118
119
|
task.visible = False
|
|
120
|
+
elif event.action == ProgressAction.FATAL_ERROR:
|
|
121
|
+
self._progress.update(
|
|
122
|
+
task_id,
|
|
123
|
+
completed=100,
|
|
124
|
+
total=100,
|
|
125
|
+
details=f" / {event.details}",
|
|
126
|
+
)
|
|
127
|
+
for task in self._progress.tasks:
|
|
128
|
+
if task.id != task_id:
|
|
129
|
+
task.visible = False
|
|
119
130
|
else:
|
|
120
131
|
self._progress.reset(task_id)
|
|
@@ -24,6 +24,8 @@ from mcp.client.sse import sse_client
|
|
|
24
24
|
from mcp.types import JSONRPCMessage
|
|
25
25
|
|
|
26
26
|
from mcp_agent.config import MCPServerSettings
|
|
27
|
+
from mcp_agent.core.exceptions import ServerInitializationError
|
|
28
|
+
from mcp_agent.event_progress import ProgressAction
|
|
27
29
|
from mcp_agent.logging.logger import get_logger
|
|
28
30
|
from mcp_agent.mcp.stdio import stdio_client_with_rich_stderr
|
|
29
31
|
from mcp_agent.context_dependent import ContextDependent
|
|
@@ -144,7 +146,9 @@ async def _server_lifecycle_task(server_conn: ServerConnection) -> None:
|
|
|
144
146
|
transport_context = server_conn._transport_context_factory()
|
|
145
147
|
|
|
146
148
|
async with transport_context as (read_stream, write_stream):
|
|
149
|
+
# try:
|
|
147
150
|
server_conn.create_session(read_stream, write_stream)
|
|
151
|
+
# except FileNotFoundError as e:
|
|
148
152
|
|
|
149
153
|
async with server_conn.session:
|
|
150
154
|
await server_conn.initialize_session()
|
|
@@ -153,7 +157,12 @@ async def _server_lifecycle_task(server_conn: ServerConnection) -> None:
|
|
|
153
157
|
|
|
154
158
|
except Exception as exc:
|
|
155
159
|
logger.error(
|
|
156
|
-
f"{server_name}: Lifecycle task encountered an error: {exc}",
|
|
160
|
+
f"{server_name}: Lifecycle task encountered an error: {exc}",
|
|
161
|
+
exc_info=True,
|
|
162
|
+
data={
|
|
163
|
+
"progress_action": ProgressAction.FATAL_ERROR,
|
|
164
|
+
"server_name": server_name,
|
|
165
|
+
},
|
|
157
166
|
)
|
|
158
167
|
# If there's an error, we should also set the event so that
|
|
159
168
|
# 'get_server' won't hang
|
|
@@ -295,7 +304,7 @@ class MCPConnectionManager(ContextDependent):
|
|
|
295
304
|
|
|
296
305
|
# If the session is still None, it means the lifecycle task crashed
|
|
297
306
|
if not server_conn or not server_conn.session:
|
|
298
|
-
raise
|
|
307
|
+
raise ServerInitializationError(
|
|
299
308
|
f"{server_name}: Failed to initialize server; check logs for errors."
|
|
300
309
|
)
|
|
301
310
|
return server_conn
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
from mcp_agent.core.fastagent import FastAgent
|
|
4
|
+
|
|
5
|
+
# Create the application
|
|
6
|
+
fast = FastAgent("Data Analysis (Roots)")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@fast.agent(
|
|
10
|
+
name="Data_Analysis",
|
|
11
|
+
instruction="""
|
|
12
|
+
You have access to a Python 3.12 interpreter and you can use this to analyse and process data.
|
|
13
|
+
Common analysis packages such as Pandas, Seaborn and Matplotlib are already installed.
|
|
14
|
+
You can add further packages if needed.
|
|
15
|
+
Data files are accessible from the /mnt/data/ directory (this is the current working directory).
|
|
16
|
+
Visualisations should be saved as .png files in the current working directory.
|
|
17
|
+
""",
|
|
18
|
+
servers=["interpreter"],
|
|
19
|
+
)
|
|
20
|
+
async def main():
|
|
21
|
+
# Use the app's context manager
|
|
22
|
+
async with fast.run() as agent:
|
|
23
|
+
await agent(
|
|
24
|
+
"There is a csv file in the current directory. "
|
|
25
|
+
"Analyse the file, produce a detailed description of the data, and any patterns it contains.",
|
|
26
|
+
)
|
|
27
|
+
await agent(
|
|
28
|
+
"Consider the data, and how to usefully group it for presentation to a Human. Find insights, using the Python Interpreter as needed.\n"
|
|
29
|
+
"Use MatPlotLib to produce insightful visualisations. Save them as '.png' files in the current directory. Be sure to run the code and save the files.\n"
|
|
30
|
+
"Produce a summary with major insights to the data",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
if __name__ == "__main__":
|
|
35
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
default_model: sonnet
|
|
2
|
+
|
|
3
|
+
mcp:
|
|
4
|
+
servers:
|
|
5
|
+
interpreter:
|
|
6
|
+
command: "docker"
|
|
7
|
+
args:
|
|
8
|
+
[
|
|
9
|
+
"run",
|
|
10
|
+
"-i",
|
|
11
|
+
"--rm",
|
|
12
|
+
"--pull=always",
|
|
13
|
+
"-v",
|
|
14
|
+
"./mount-point:/mnt/data/",
|
|
15
|
+
"ghcr.io/evalstate/mcp-py-repl:latest",
|
|
16
|
+
]
|
|
17
|
+
roots:
|
|
18
|
+
- uri: "file://./mount-point/"
|
|
19
|
+
name: "test_data"
|
|
20
|
+
server_uri_alias: "file:///mnt/data/"
|