fast-agent-mcp 0.1.12__py3-none-any.whl → 0.1.13__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.1.12.dist-info → fast_agent_mcp-0.1.13.dist-info}/METADATA +1 -1
- fast_agent_mcp-0.1.13.dist-info/RECORD +164 -0
- mcp_agent/agents/agent.py +37 -79
- mcp_agent/app.py +16 -22
- mcp_agent/cli/commands/bootstrap.py +22 -52
- mcp_agent/cli/commands/config.py +4 -4
- mcp_agent/cli/commands/setup.py +11 -26
- mcp_agent/cli/main.py +6 -9
- mcp_agent/cli/terminal.py +2 -2
- mcp_agent/config.py +1 -5
- mcp_agent/context.py +13 -24
- mcp_agent/context_dependent.py +3 -7
- mcp_agent/core/agent_app.py +45 -121
- mcp_agent/core/agent_utils.py +3 -5
- mcp_agent/core/decorators.py +5 -12
- mcp_agent/core/enhanced_prompt.py +25 -52
- mcp_agent/core/exceptions.py +8 -8
- mcp_agent/core/factory.py +29 -70
- mcp_agent/core/fastagent.py +48 -88
- mcp_agent/core/mcp_content.py +8 -16
- mcp_agent/core/prompt.py +8 -15
- mcp_agent/core/proxies.py +34 -25
- mcp_agent/core/request_params.py +6 -3
- mcp_agent/core/types.py +4 -6
- mcp_agent/core/validation.py +4 -3
- mcp_agent/executor/decorator_registry.py +11 -23
- mcp_agent/executor/executor.py +8 -17
- mcp_agent/executor/task_registry.py +2 -4
- mcp_agent/executor/temporal.py +28 -74
- mcp_agent/executor/workflow.py +3 -5
- mcp_agent/executor/workflow_signal.py +17 -29
- mcp_agent/human_input/handler.py +4 -9
- mcp_agent/human_input/types.py +2 -3
- mcp_agent/logging/events.py +1 -5
- mcp_agent/logging/json_serializer.py +7 -6
- mcp_agent/logging/listeners.py +20 -23
- mcp_agent/logging/logger.py +15 -17
- mcp_agent/logging/rich_progress.py +10 -8
- mcp_agent/logging/tracing.py +4 -6
- mcp_agent/logging/transport.py +22 -22
- mcp_agent/mcp/gen_client.py +4 -12
- mcp_agent/mcp/interfaces.py +71 -86
- mcp_agent/mcp/mcp_agent_client_session.py +11 -19
- mcp_agent/mcp/mcp_agent_server.py +8 -10
- mcp_agent/mcp/mcp_aggregator.py +45 -117
- mcp_agent/mcp/mcp_connection_manager.py +16 -37
- mcp_agent/mcp/prompt_message_multipart.py +12 -18
- mcp_agent/mcp/prompt_serialization.py +13 -38
- mcp_agent/mcp/prompts/prompt_load.py +99 -0
- mcp_agent/mcp/prompts/prompt_server.py +21 -128
- mcp_agent/mcp/prompts/prompt_template.py +20 -42
- mcp_agent/mcp/resource_utils.py +8 -17
- mcp_agent/mcp/sampling.py +5 -14
- mcp_agent/mcp/stdio.py +11 -8
- mcp_agent/mcp_server/agent_server.py +10 -17
- mcp_agent/mcp_server_registry.py +13 -35
- mcp_agent/resources/examples/data-analysis/analysis-campaign.py +1 -1
- mcp_agent/resources/examples/data-analysis/analysis.py +1 -1
- mcp_agent/resources/examples/data-analysis/slides.py +110 -0
- mcp_agent/resources/examples/internal/agent.py +2 -1
- mcp_agent/resources/examples/internal/job.py +2 -1
- mcp_agent/resources/examples/internal/prompt_category.py +1 -1
- mcp_agent/resources/examples/internal/prompt_sizing.py +3 -5
- mcp_agent/resources/examples/internal/sizer.py +2 -1
- mcp_agent/resources/examples/internal/social.py +2 -1
- mcp_agent/resources/examples/mcp_researcher/researcher-eval.py +1 -1
- mcp_agent/resources/examples/prompting/agent.py +2 -1
- mcp_agent/resources/examples/prompting/image_server.py +5 -11
- mcp_agent/resources/examples/researcher/researcher-eval.py +1 -1
- mcp_agent/resources/examples/researcher/researcher-imp.py +3 -4
- mcp_agent/resources/examples/researcher/researcher.py +2 -1
- mcp_agent/resources/examples/workflows/agent_build.py +2 -1
- mcp_agent/resources/examples/workflows/chaining.py +2 -1
- mcp_agent/resources/examples/workflows/evaluator.py +2 -1
- mcp_agent/resources/examples/workflows/human_input.py +2 -1
- mcp_agent/resources/examples/workflows/orchestrator.py +2 -1
- mcp_agent/resources/examples/workflows/parallel.py +2 -1
- mcp_agent/resources/examples/workflows/router.py +2 -1
- mcp_agent/resources/examples/workflows/sse.py +1 -1
- mcp_agent/telemetry/usage_tracking.py +2 -1
- mcp_agent/ui/console_display.py +15 -39
- mcp_agent/workflows/embedding/embedding_base.py +1 -4
- mcp_agent/workflows/embedding/embedding_cohere.py +2 -2
- mcp_agent/workflows/embedding/embedding_openai.py +4 -13
- mcp_agent/workflows/evaluator_optimizer/evaluator_optimizer.py +23 -57
- mcp_agent/workflows/intent_classifier/intent_classifier_base.py +5 -8
- mcp_agent/workflows/intent_classifier/intent_classifier_embedding.py +7 -11
- mcp_agent/workflows/intent_classifier/intent_classifier_embedding_cohere.py +4 -8
- mcp_agent/workflows/intent_classifier/intent_classifier_embedding_openai.py +4 -8
- mcp_agent/workflows/intent_classifier/intent_classifier_llm.py +11 -22
- mcp_agent/workflows/intent_classifier/intent_classifier_llm_anthropic.py +3 -3
- mcp_agent/workflows/intent_classifier/intent_classifier_llm_openai.py +4 -6
- mcp_agent/workflows/llm/anthropic_utils.py +8 -29
- mcp_agent/workflows/llm/augmented_llm.py +69 -247
- mcp_agent/workflows/llm/augmented_llm_anthropic.py +39 -73
- mcp_agent/workflows/llm/augmented_llm_openai.py +42 -97
- mcp_agent/workflows/llm/augmented_llm_passthrough.py +13 -20
- mcp_agent/workflows/llm/augmented_llm_playback.py +8 -6
- mcp_agent/workflows/llm/memory.py +103 -0
- mcp_agent/workflows/llm/model_factory.py +8 -20
- mcp_agent/workflows/llm/openai_utils.py +1 -1
- mcp_agent/workflows/llm/prompt_utils.py +1 -3
- mcp_agent/workflows/llm/providers/multipart_converter_anthropic.py +47 -89
- mcp_agent/workflows/llm/providers/multipart_converter_openai.py +20 -55
- mcp_agent/workflows/llm/providers/openai_multipart.py +19 -61
- mcp_agent/workflows/llm/providers/sampling_converter_anthropic.py +10 -12
- mcp_agent/workflows/llm/providers/sampling_converter_openai.py +7 -11
- mcp_agent/workflows/llm/sampling_converter.py +4 -11
- mcp_agent/workflows/llm/sampling_format_converter.py +12 -12
- mcp_agent/workflows/orchestrator/orchestrator.py +24 -67
- mcp_agent/workflows/orchestrator/orchestrator_models.py +14 -40
- mcp_agent/workflows/parallel/fan_in.py +17 -47
- mcp_agent/workflows/parallel/fan_out.py +6 -12
- mcp_agent/workflows/parallel/parallel_llm.py +9 -26
- mcp_agent/workflows/router/router_base.py +19 -49
- mcp_agent/workflows/router/router_embedding.py +11 -25
- mcp_agent/workflows/router/router_embedding_cohere.py +2 -2
- mcp_agent/workflows/router/router_embedding_openai.py +2 -2
- mcp_agent/workflows/router/router_llm.py +12 -28
- mcp_agent/workflows/swarm/swarm.py +20 -48
- mcp_agent/workflows/swarm/swarm_anthropic.py +2 -2
- mcp_agent/workflows/swarm/swarm_openai.py +2 -2
- fast_agent_mcp-0.1.12.dist-info/RECORD +0 -161
- {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.1.13.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.1.13.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.1.13.dist-info}/licenses/LICENSE +0 -0
mcp_agent/core/exceptions.py
CHANGED
@@ -7,7 +7,7 @@ Enables user-friendly error handling for common issues.
|
|
7
7
|
class FastAgentError(Exception):
|
8
8
|
"""Base exception class for FastAgent errors"""
|
9
9
|
|
10
|
-
def __init__(self, message: str, details: str = ""):
|
10
|
+
def __init__(self, message: str, details: str = "") -> None:
|
11
11
|
self.message = message
|
12
12
|
self.details = details
|
13
13
|
super().__init__(f"{message}\n\n{details}" if details else message)
|
@@ -18,7 +18,7 @@ class ServerConfigError(FastAgentError):
|
|
18
18
|
Example: Server name referenced in agent.servers[] but not defined in config
|
19
19
|
"""
|
20
20
|
|
21
|
-
def __init__(self, message: str, details: str = ""):
|
21
|
+
def __init__(self, message: str, details: str = "") -> None:
|
22
22
|
super().__init__(message, details)
|
23
23
|
|
24
24
|
|
@@ -27,7 +27,7 @@ class AgentConfigError(FastAgentError):
|
|
27
27
|
Example: Parallel fan-in references unknown agent
|
28
28
|
"""
|
29
29
|
|
30
|
-
def __init__(self, message: str, details: str = ""):
|
30
|
+
def __init__(self, message: str, details: str = "") -> None:
|
31
31
|
super().__init__(message, details)
|
32
32
|
|
33
33
|
|
@@ -36,14 +36,14 @@ class ProviderKeyError(FastAgentError):
|
|
36
36
|
Example: OpenAI/Anthropic key not configured but model requires it
|
37
37
|
"""
|
38
38
|
|
39
|
-
def __init__(self, message: str, details: str = ""):
|
39
|
+
def __init__(self, message: str, details: str = "") -> None:
|
40
40
|
super().__init__(message, details)
|
41
41
|
|
42
42
|
|
43
43
|
class ServerInitializationError(FastAgentError):
|
44
44
|
"""Raised when a server fails to initialize properly."""
|
45
45
|
|
46
|
-
def __init__(self, message: str, details: str = ""):
|
46
|
+
def __init__(self, message: str, details: str = "") -> None:
|
47
47
|
super().__init__(message, details)
|
48
48
|
|
49
49
|
|
@@ -52,14 +52,14 @@ class ModelConfigError(FastAgentError):
|
|
52
52
|
Example: Unknown model name in model specification string
|
53
53
|
"""
|
54
54
|
|
55
|
-
def __init__(self, message: str, details: str = ""):
|
55
|
+
def __init__(self, message: str, details: str = "") -> None:
|
56
56
|
super().__init__(message, details)
|
57
57
|
|
58
58
|
|
59
59
|
class CircularDependencyError(FastAgentError):
|
60
60
|
"""Raised when we detect a Circular Dependency in the workflow"""
|
61
61
|
|
62
|
-
def __init__(self, message: str, details: str = ""):
|
62
|
+
def __init__(self, message: str, details: str = "") -> None:
|
63
63
|
super().__init__(message, details)
|
64
64
|
|
65
65
|
|
@@ -67,5 +67,5 @@ class PromptExitError(FastAgentError):
|
|
67
67
|
"""Raised from enhanced_prompt when the user requests hard exits"""
|
68
68
|
|
69
69
|
# TODO an exception for flow control :(
|
70
|
-
def __init__(self, message: str, details: str = ""):
|
70
|
+
def __init__(self, message: str, details: str = "") -> None:
|
71
71
|
super().__init__(message, details)
|
mcp_agent/core/factory.py
CHANGED
@@ -2,11 +2,22 @@
|
|
2
2
|
Factory functions for creating agent and workflow instances.
|
3
3
|
"""
|
4
4
|
|
5
|
-
from typing import
|
5
|
+
from typing import Any, Callable, Dict, Optional, TypeVar
|
6
6
|
|
7
|
-
from mcp_agent.app import MCPApp
|
8
7
|
from mcp_agent.agents.agent import Agent
|
8
|
+
from mcp_agent.app import MCPApp
|
9
9
|
from mcp_agent.core.agent_types import AgentConfig, AgentType
|
10
|
+
from mcp_agent.core.agent_utils import get_agent_instances, log_agent_load, unwrap_proxy
|
11
|
+
from mcp_agent.core.exceptions import AgentConfigError
|
12
|
+
from mcp_agent.core.proxies import (
|
13
|
+
BaseAgentProxy,
|
14
|
+
ChainProxy,
|
15
|
+
LLMAgentProxy,
|
16
|
+
RouterProxy,
|
17
|
+
WorkflowProxy,
|
18
|
+
)
|
19
|
+
from mcp_agent.core.types import AgentOrWorkflow, ProxyDict
|
20
|
+
from mcp_agent.core.validation import get_dependencies
|
10
21
|
from mcp_agent.event_progress import ProgressAction
|
11
22
|
from mcp_agent.workflows.evaluator_optimizer.evaluator_optimizer import (
|
12
23
|
EvaluatorOptimizerLLM,
|
@@ -17,24 +28,11 @@ from mcp_agent.workflows.llm.model_factory import ModelFactory
|
|
17
28
|
from mcp_agent.workflows.orchestrator.orchestrator import Orchestrator
|
18
29
|
from mcp_agent.workflows.parallel.parallel_llm import ParallelLLM
|
19
30
|
from mcp_agent.workflows.router.router_llm import LLMRouter
|
20
|
-
from mcp_agent.core.exceptions import AgentConfigError
|
21
|
-
from mcp_agent.core.proxies import (
|
22
|
-
BaseAgentProxy,
|
23
|
-
LLMAgentProxy,
|
24
|
-
WorkflowProxy,
|
25
|
-
RouterProxy,
|
26
|
-
ChainProxy,
|
27
|
-
)
|
28
|
-
from mcp_agent.core.types import AgentOrWorkflow, ProxyDict
|
29
|
-
from mcp_agent.core.agent_utils import log_agent_load, unwrap_proxy, get_agent_instances
|
30
|
-
from mcp_agent.core.validation import get_dependencies
|
31
31
|
|
32
32
|
T = TypeVar("T") # For the wrapper classes
|
33
33
|
|
34
34
|
|
35
|
-
def create_proxy(
|
36
|
-
app: MCPApp, name: str, instance: AgentOrWorkflow, agent_type: str
|
37
|
-
) -> BaseAgentProxy:
|
35
|
+
def create_proxy(app: MCPApp, name: str, instance: AgentOrWorkflow, agent_type: str) -> BaseAgentProxy:
|
38
36
|
"""Create appropriate proxy type based on agent type and validate instance type
|
39
37
|
|
40
38
|
Args:
|
@@ -61,27 +59,19 @@ def create_proxy(
|
|
61
59
|
return LLMAgentProxy(app, name, instance)
|
62
60
|
elif agent_type == AgentType.ORCHESTRATOR.value:
|
63
61
|
if not isinstance(instance, Orchestrator):
|
64
|
-
raise TypeError(
|
65
|
-
f"Expected Orchestrator instance for {name}, got {type(instance)}"
|
66
|
-
)
|
62
|
+
raise TypeError(f"Expected Orchestrator instance for {name}, got {type(instance)}")
|
67
63
|
return WorkflowProxy(app, name, instance)
|
68
64
|
elif agent_type == AgentType.PARALLEL.value:
|
69
65
|
if not isinstance(instance, ParallelLLM):
|
70
|
-
raise TypeError(
|
71
|
-
f"Expected ParallelLLM instance for {name}, got {type(instance)}"
|
72
|
-
)
|
66
|
+
raise TypeError(f"Expected ParallelLLM instance for {name}, got {type(instance)}")
|
73
67
|
return WorkflowProxy(app, name, instance)
|
74
68
|
elif agent_type == AgentType.EVALUATOR_OPTIMIZER.value:
|
75
69
|
if not isinstance(instance, EvaluatorOptimizerLLM):
|
76
|
-
raise TypeError(
|
77
|
-
f"Expected EvaluatorOptimizerLLM instance for {name}, got {type(instance)}"
|
78
|
-
)
|
70
|
+
raise TypeError(f"Expected EvaluatorOptimizerLLM instance for {name}, got {type(instance)}")
|
79
71
|
return WorkflowProxy(app, name, instance)
|
80
72
|
elif agent_type == AgentType.ROUTER.value:
|
81
73
|
if not isinstance(instance, LLMRouter):
|
82
|
-
raise TypeError(
|
83
|
-
f"Expected LLMRouter instance for {name}, got {type(instance)}"
|
84
|
-
)
|
74
|
+
raise TypeError(f"Expected LLMRouter instance for {name}, got {type(instance)}")
|
85
75
|
return RouterProxy(app, name, instance)
|
86
76
|
elif agent_type == AgentType.CHAIN.value:
|
87
77
|
# Chain proxy is directly returned from _create_agents_by_type
|
@@ -187,11 +177,7 @@ async def create_agents_by_type(
|
|
187
177
|
|
188
178
|
elif agent_type == AgentType.ORCHESTRATOR:
|
189
179
|
# Get base params configured with model settings
|
190
|
-
base_params = (
|
191
|
-
config.default_request_params.model_copy()
|
192
|
-
if config.default_request_params
|
193
|
-
else RequestParams()
|
194
|
-
)
|
180
|
+
base_params = config.default_request_params.model_copy() if config.default_request_params else RequestParams()
|
195
181
|
base_params.use_history = False # Force no history for orchestrator
|
196
182
|
|
197
183
|
# Get the child agents - need to unwrap proxies and validate LLM config
|
@@ -231,7 +217,6 @@ async def create_agents_by_type(
|
|
231
217
|
)
|
232
218
|
|
233
219
|
planner = await planner_agent.attach_llm(planner_factory)
|
234
|
-
await planner.initialize()
|
235
220
|
# Create the orchestrator with pre-configured planner
|
236
221
|
instance = Orchestrator(
|
237
222
|
name=config.name,
|
@@ -239,9 +224,7 @@ async def create_agents_by_type(
|
|
239
224
|
available_agents=child_agents,
|
240
225
|
context=app_instance.context,
|
241
226
|
request_params=planner.default_request_params, # Base params already include model settings
|
242
|
-
plan_type=agent_data.get(
|
243
|
-
"plan_type", "full"
|
244
|
-
), # Get plan_type from agent_data
|
227
|
+
plan_type=agent_data.get("plan_type", "full"), # Get plan_type from agent_data
|
245
228
|
verb=ProgressAction.PLANNING,
|
246
229
|
)
|
247
230
|
|
@@ -251,19 +234,13 @@ async def create_agents_by_type(
|
|
251
234
|
evaluator = unwrap_proxy(active_agents[agent_data["evaluator"]])
|
252
235
|
|
253
236
|
if not generator or not evaluator:
|
254
|
-
raise ValueError(
|
255
|
-
f"Missing agents for workflow {name}: "
|
256
|
-
f"generator={agent_data['generator']}, "
|
257
|
-
f"evaluator={agent_data['evaluator']}"
|
258
|
-
)
|
237
|
+
raise ValueError(f"Missing agents for workflow {name}: generator={agent_data['generator']}, evaluator={agent_data['evaluator']}")
|
259
238
|
|
260
239
|
# Get model from generator if it's an Agent, or from config otherwise
|
261
240
|
optimizer_model = None
|
262
241
|
if isinstance(generator, Agent):
|
263
242
|
optimizer_model = generator.config.model
|
264
|
-
elif hasattr(generator, "_sequence") and hasattr(
|
265
|
-
generator, "_agent_proxies"
|
266
|
-
):
|
243
|
+
elif hasattr(generator, "_sequence") and hasattr(generator, "_agent_proxies"):
|
267
244
|
# For ChainProxy, use the config model directly
|
268
245
|
optimizer_model = config.model
|
269
246
|
|
@@ -328,27 +305,19 @@ async def create_agents_by_type(
|
|
328
305
|
|
329
306
|
# Generate a better description
|
330
307
|
if agent_names:
|
331
|
-
server_part = (
|
332
|
-
f" with access to servers: {', '.join(sorted(all_servers))}"
|
333
|
-
if all_servers
|
334
|
-
else ""
|
335
|
-
)
|
308
|
+
server_part = f" with access to servers: {', '.join(sorted(all_servers))}" if all_servers else ""
|
336
309
|
config.instruction = f"Sequence of agents: {', '.join(agent_names)}{server_part}."
|
337
310
|
|
338
311
|
# Create a ChainProxy without needing a new instance
|
339
312
|
# Just pass the agent proxies and sequence
|
340
313
|
instance = ChainProxy(app_instance, name, sequence, active_agents)
|
341
314
|
# Set continue_with_final behavior from configuration
|
342
|
-
instance._continue_with_final = agent_data.get(
|
343
|
-
"continue_with_final", True
|
344
|
-
)
|
315
|
+
instance._continue_with_final = agent_data.get("continue_with_final", True)
|
345
316
|
# Set cumulative behavior from configuration
|
346
317
|
instance._cumulative = agent_data.get("cumulative", False)
|
347
318
|
|
348
319
|
elif agent_type == AgentType.PARALLEL:
|
349
|
-
fan_out_agents = get_agent_instances(
|
350
|
-
agent_data["fan_out"], active_agents
|
351
|
-
)
|
320
|
+
fan_out_agents = get_agent_instances(agent_data["fan_out"], active_agents)
|
352
321
|
|
353
322
|
# Get fan-in agent - unwrap proxy
|
354
323
|
fan_in_agent = unwrap_proxy(active_agents[agent_data["fan_in"]])
|
@@ -370,9 +339,7 @@ async def create_agents_by_type(
|
|
370
339
|
raise ValueError(f"Unsupported agent type: {agent_type}")
|
371
340
|
|
372
341
|
# Create the appropriate proxy and store in results
|
373
|
-
result_agents[name] = create_proxy(
|
374
|
-
app_instance, name, instance, agent_type.value
|
375
|
-
)
|
342
|
+
result_agents[name] = create_proxy(app_instance, name, instance, agent_type.value)
|
376
343
|
|
377
344
|
return result_agents
|
378
345
|
|
@@ -426,24 +393,16 @@ async def create_agents_in_dependency_order(
|
|
426
393
|
visited = set()
|
427
394
|
|
428
395
|
# Get all agents of the specified type
|
429
|
-
agent_names = [
|
430
|
-
name
|
431
|
-
for name, agent_data in agents_dict.items()
|
432
|
-
if agent_data["type"] == agent_type.value
|
433
|
-
]
|
396
|
+
agent_names = [name for name, agent_data in agents_dict.items() if agent_data["type"] == agent_type.value]
|
434
397
|
|
435
398
|
# Create agents in dependency order
|
436
399
|
for name in agent_names:
|
437
400
|
# Get ordered dependencies if not already processed
|
438
401
|
if name not in visited:
|
439
402
|
try:
|
440
|
-
ordered_agents = get_dependencies(
|
441
|
-
name, agents_dict, visited, set(), agent_type
|
442
|
-
)
|
403
|
+
ordered_agents = get_dependencies(name, agents_dict, visited, set(), agent_type)
|
443
404
|
except ValueError as e:
|
444
|
-
raise ValueError(
|
445
|
-
f"Error creating {agent_type.name.lower()} agent {name}: {str(e)}"
|
446
|
-
)
|
405
|
+
raise ValueError(f"Error creating {agent_type.name.lower()} agent {name}: {str(e)}")
|
447
406
|
|
448
407
|
# Create each agent in order
|
449
408
|
for agent_name in ordered_agents:
|
mcp_agent/core/fastagent.py
CHANGED
@@ -3,61 +3,59 @@ Decorator-based interface for MCP Agent applications.
|
|
3
3
|
Provides a simplified way to create and manage agents using decorators.
|
4
4
|
"""
|
5
5
|
|
6
|
+
import argparse
|
6
7
|
import asyncio
|
8
|
+
from contextlib import asynccontextmanager
|
9
|
+
from functools import partial
|
7
10
|
from typing import (
|
8
|
-
|
11
|
+
Any,
|
9
12
|
Dict,
|
13
|
+
Optional,
|
10
14
|
TypeVar,
|
11
|
-
Any,
|
12
15
|
)
|
16
|
+
|
13
17
|
import yaml
|
14
|
-
|
15
|
-
|
16
|
-
|
18
|
+
|
19
|
+
# TODO -- reinstate once Windows&Python 3.13 platform issues are fixed
|
20
|
+
# import readline # noqa: F401
|
21
|
+
from rich import print
|
17
22
|
|
18
23
|
from mcp_agent.app import MCPApp
|
19
24
|
from mcp_agent.config import Settings
|
20
|
-
|
21
25
|
from mcp_agent.core.agent_app import AgentApp
|
22
26
|
from mcp_agent.core.agent_types import AgentType
|
27
|
+
from mcp_agent.core.decorators import (
|
28
|
+
_create_decorator,
|
29
|
+
agent,
|
30
|
+
chain,
|
31
|
+
evaluator_optimizer,
|
32
|
+
orchestrator,
|
33
|
+
parallel,
|
34
|
+
passthrough,
|
35
|
+
router,
|
36
|
+
)
|
23
37
|
from mcp_agent.core.error_handling import handle_error
|
24
|
-
from mcp_agent.core.proxies import LLMAgentProxy
|
25
|
-
from mcp_agent.core.types import ProxyDict
|
26
38
|
from mcp_agent.core.exceptions import (
|
27
39
|
AgentConfigError,
|
28
40
|
CircularDependencyError,
|
29
41
|
ModelConfigError,
|
30
42
|
PromptExitError,
|
31
|
-
ServerConfigError,
|
32
43
|
ProviderKeyError,
|
44
|
+
ServerConfigError,
|
33
45
|
ServerInitializationError,
|
34
46
|
)
|
35
|
-
from mcp_agent.core.
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
evaluator_optimizer,
|
41
|
-
router,
|
42
|
-
chain,
|
43
|
-
passthrough,
|
47
|
+
from mcp_agent.core.factory import (
|
48
|
+
create_agents_by_type,
|
49
|
+
create_agents_in_dependency_order,
|
50
|
+
create_basic_agents,
|
51
|
+
get_model_factory,
|
44
52
|
)
|
53
|
+
from mcp_agent.core.proxies import LLMAgentProxy
|
54
|
+
from mcp_agent.core.types import ProxyDict
|
45
55
|
from mcp_agent.core.validation import (
|
46
56
|
validate_server_references,
|
47
57
|
validate_workflow_references,
|
48
58
|
)
|
49
|
-
from mcp_agent.core.factory import (
|
50
|
-
get_model_factory,
|
51
|
-
create_basic_agents,
|
52
|
-
create_agents_in_dependency_order,
|
53
|
-
create_agents_by_type,
|
54
|
-
)
|
55
|
-
|
56
|
-
# TODO -- reinstate once Windows&Python 3.13 platform issues are fixed
|
57
|
-
# import readline # noqa: F401
|
58
|
-
|
59
|
-
from rich import print
|
60
|
-
|
61
59
|
from mcp_agent.mcp_server import AgentMCPServer
|
62
60
|
|
63
61
|
T = TypeVar("T") # For the wrapper classes
|
@@ -74,7 +72,7 @@ class FastAgent:
|
|
74
72
|
name: str,
|
75
73
|
config_path: Optional[str] = None,
|
76
74
|
ignore_unknown_args: bool = False,
|
77
|
-
):
|
75
|
+
) -> None:
|
78
76
|
"""
|
79
77
|
Initialize the decorator interface.
|
80
78
|
|
@@ -218,9 +216,7 @@ class FastAgent:
|
|
218
216
|
model_factory_func = partial(self._get_model_factory)
|
219
217
|
return await create_basic_agents(agent_app, self.agents, model_factory_func)
|
220
218
|
|
221
|
-
async def _create_orchestrators(
|
222
|
-
self, agent_app: MCPApp, active_agents: ProxyDict
|
223
|
-
) -> ProxyDict:
|
219
|
+
async def _create_orchestrators(self, agent_app: MCPApp, active_agents: ProxyDict) -> ProxyDict:
|
224
220
|
"""
|
225
221
|
Create orchestrator agents.
|
226
222
|
|
@@ -231,13 +227,9 @@ class FastAgent:
|
|
231
227
|
Returns:
|
232
228
|
Dictionary of initialized orchestrator agents wrapped in appropriate proxies
|
233
229
|
"""
|
234
|
-
return await self._create_agents_by_type(
|
235
|
-
agent_app, AgentType.ORCHESTRATOR, active_agents
|
236
|
-
)
|
230
|
+
return await self._create_agents_by_type(agent_app, AgentType.ORCHESTRATOR, active_agents)
|
237
231
|
|
238
|
-
async def _create_evaluator_optimizers(
|
239
|
-
self, agent_app: MCPApp, active_agents: ProxyDict
|
240
|
-
) -> ProxyDict:
|
232
|
+
async def _create_evaluator_optimizers(self, agent_app: MCPApp, active_agents: ProxyDict) -> ProxyDict:
|
241
233
|
"""
|
242
234
|
Create evaluator-optimizer workflows.
|
243
235
|
|
@@ -248,13 +240,9 @@ class FastAgent:
|
|
248
240
|
Returns:
|
249
241
|
Dictionary of initialized evaluator-optimizer workflows
|
250
242
|
"""
|
251
|
-
return await self._create_agents_by_type(
|
252
|
-
agent_app, AgentType.EVALUATOR_OPTIMIZER, active_agents
|
253
|
-
)
|
243
|
+
return await self._create_agents_by_type(agent_app, AgentType.EVALUATOR_OPTIMIZER, active_agents)
|
254
244
|
|
255
|
-
async def _create_parallel_agents(
|
256
|
-
self, agent_app: MCPApp, active_agents: ProxyDict
|
257
|
-
) -> ProxyDict:
|
245
|
+
async def _create_parallel_agents(self, agent_app: MCPApp, active_agents: ProxyDict) -> ProxyDict:
|
258
246
|
"""
|
259
247
|
Create parallel execution agents in dependency order.
|
260
248
|
|
@@ -274,9 +262,7 @@ class FastAgent:
|
|
274
262
|
model_factory_func,
|
275
263
|
)
|
276
264
|
|
277
|
-
async def _create_agents_in_dependency_order(
|
278
|
-
self, agent_app: MCPApp, active_agents: ProxyDict, agent_type: AgentType
|
279
|
-
) -> ProxyDict:
|
265
|
+
async def _create_agents_in_dependency_order(self, agent_app: MCPApp, active_agents: ProxyDict, agent_type: AgentType) -> ProxyDict:
|
280
266
|
"""
|
281
267
|
Create agents in dependency order to avoid circular references.
|
282
268
|
Works for both Parallel and Chain workflows.
|
@@ -290,13 +276,9 @@ class FastAgent:
|
|
290
276
|
Dictionary of initialized agents
|
291
277
|
"""
|
292
278
|
model_factory_func = partial(self._get_model_factory)
|
293
|
-
return await create_agents_in_dependency_order(
|
294
|
-
agent_app, self.agents, active_agents, agent_type, model_factory_func
|
295
|
-
)
|
279
|
+
return await create_agents_in_dependency_order(agent_app, self.agents, active_agents, agent_type, model_factory_func)
|
296
280
|
|
297
|
-
async def _create_routers(
|
298
|
-
self, agent_app: MCPApp, active_agents: ProxyDict
|
299
|
-
) -> ProxyDict:
|
281
|
+
async def _create_routers(self, agent_app: MCPApp, active_agents: ProxyDict) -> ProxyDict:
|
300
282
|
"""
|
301
283
|
Create router agents.
|
302
284
|
|
@@ -307,9 +289,7 @@ class FastAgent:
|
|
307
289
|
Returns:
|
308
290
|
Dictionary of initialized router agents
|
309
291
|
"""
|
310
|
-
return await self._create_agents_by_type(
|
311
|
-
agent_app, AgentType.ROUTER, active_agents
|
312
|
-
)
|
292
|
+
return await self._create_agents_by_type(agent_app, AgentType.ROUTER, active_agents)
|
313
293
|
|
314
294
|
@asynccontextmanager
|
315
295
|
async def run(self):
|
@@ -327,11 +307,7 @@ class FastAgent:
|
|
327
307
|
try:
|
328
308
|
async with self.app.run() as agent_app:
|
329
309
|
# Apply quiet mode directly to the context's config if needed
|
330
|
-
if (
|
331
|
-
quiet_mode
|
332
|
-
and hasattr(agent_app.context, "config")
|
333
|
-
and hasattr(agent_app.context.config, "logger")
|
334
|
-
):
|
310
|
+
if quiet_mode and hasattr(agent_app.context, "config") and hasattr(agent_app.context.config, "logger"):
|
335
311
|
# Apply after initialization but before agents are created
|
336
312
|
agent_app.context.config.logger.progress_display = False
|
337
313
|
agent_app.context.config.logger.show_chat = False
|
@@ -351,9 +327,7 @@ class FastAgent:
|
|
351
327
|
active_agents = await self._create_basic_agents(agent_app)
|
352
328
|
|
353
329
|
# Create parallel agents next as they might be dependencies
|
354
|
-
parallel_agents = await self._create_parallel_agents(
|
355
|
-
agent_app, active_agents
|
356
|
-
)
|
330
|
+
parallel_agents = await self._create_parallel_agents(agent_app, active_agents)
|
357
331
|
active_agents.update(parallel_agents)
|
358
332
|
|
359
333
|
# Create routers next
|
@@ -361,21 +335,15 @@ class FastAgent:
|
|
361
335
|
active_agents.update(routers)
|
362
336
|
|
363
337
|
# Create chains next - MOVED UP because evaluator-optimizers might depend on chains
|
364
|
-
chains = await self._create_agents_in_dependency_order(
|
365
|
-
agent_app, active_agents, AgentType.CHAIN
|
366
|
-
)
|
338
|
+
chains = await self._create_agents_in_dependency_order(agent_app, active_agents, AgentType.CHAIN)
|
367
339
|
active_agents.update(chains)
|
368
340
|
|
369
341
|
# Now create evaluator-optimizers AFTER chains are available
|
370
|
-
evaluator_optimizers = await self._create_evaluator_optimizers(
|
371
|
-
agent_app, active_agents
|
372
|
-
)
|
342
|
+
evaluator_optimizers = await self._create_evaluator_optimizers(agent_app, active_agents)
|
373
343
|
active_agents.update(evaluator_optimizers)
|
374
344
|
|
375
345
|
# Create orchestrators last as they might depend on any other agent type
|
376
|
-
orchestrators = await self._create_orchestrators(
|
377
|
-
agent_app, active_agents
|
378
|
-
)
|
346
|
+
orchestrators = await self._create_orchestrators(agent_app, active_agents)
|
379
347
|
|
380
348
|
# Add orchestrators to active_agents (other types were already added)
|
381
349
|
active_agents.update(orchestrators)
|
@@ -393,9 +361,7 @@ class FastAgent:
|
|
393
361
|
|
394
362
|
if agent_name not in active_agents:
|
395
363
|
available_agents = ", ".join(active_agents.keys())
|
396
|
-
print(
|
397
|
-
f"\n\nError: Agent '{agent_name}' not found. Available agents: {available_agents}"
|
398
|
-
)
|
364
|
+
print(f"\n\nError: Agent '{agent_name}' not found. Available agents: {available_agents}")
|
399
365
|
raise SystemExit(1)
|
400
366
|
|
401
367
|
try:
|
@@ -408,9 +374,7 @@ class FastAgent:
|
|
408
374
|
|
409
375
|
raise SystemExit(0)
|
410
376
|
except Exception as e:
|
411
|
-
print(
|
412
|
-
f"\n\nError sending message to agent '{agent_name}': {str(e)}"
|
413
|
-
)
|
377
|
+
print(f"\n\nError sending message to agent '{agent_name}': {str(e)}")
|
414
378
|
raise SystemExit(1)
|
415
379
|
|
416
380
|
yield wrapper
|
@@ -488,9 +452,7 @@ class FastAgent:
|
|
488
452
|
print(f"DEBUG {e.message}")
|
489
453
|
pass # Ignore cleanup errors
|
490
454
|
|
491
|
-
def _handle_error(
|
492
|
-
self, e: Exception, error_type: str, suggestion: str = None
|
493
|
-
) -> None:
|
455
|
+
def _handle_error(self, e: Exception, error_type: str, suggestion: str = None) -> None:
|
494
456
|
"""
|
495
457
|
Handle errors with consistent formatting and messaging.
|
496
458
|
|
@@ -535,7 +497,7 @@ class FastAgent:
|
|
535
497
|
port: int = 8000,
|
536
498
|
server_name: str = None,
|
537
499
|
server_description: str = None,
|
538
|
-
):
|
500
|
+
) -> None:
|
539
501
|
"""
|
540
502
|
Run the FastAgent application and expose agents through an MCP server.
|
541
503
|
|
@@ -555,9 +517,7 @@ class FastAgent:
|
|
555
517
|
)
|
556
518
|
|
557
519
|
# Run the MCP server in a separate task
|
558
|
-
server_task = asyncio.create_task(
|
559
|
-
mcp_server.run_async(transport=transport, host=host, port=port)
|
560
|
-
)
|
520
|
+
server_task = asyncio.create_task(mcp_server.run_async(transport=transport, host=host, port=port))
|
561
521
|
|
562
522
|
try:
|
563
523
|
# Wait for the server task to complete (or be cancelled)
|
mcp_agent/core/mcp_content.py
CHANGED
@@ -7,14 +7,14 @@ EmbeddedResource, and other MCP content types with minimal boilerplate.
|
|
7
7
|
|
8
8
|
import base64
|
9
9
|
from pathlib import Path
|
10
|
-
from typing import
|
10
|
+
from typing import Any, List, Literal, Optional, Union
|
11
11
|
|
12
12
|
from mcp.types import (
|
13
|
-
|
14
|
-
ImageContent,
|
13
|
+
BlobResourceContents,
|
15
14
|
EmbeddedResource,
|
15
|
+
ImageContent,
|
16
|
+
TextContent,
|
16
17
|
TextResourceContents,
|
17
|
-
BlobResourceContents,
|
18
18
|
)
|
19
19
|
|
20
20
|
from mcp_agent.mcp.mime_utils import (
|
@@ -86,9 +86,7 @@ def MCPImage(
|
|
86
86
|
|
87
87
|
return {
|
88
88
|
"role": role,
|
89
|
-
"content": ImageContent(
|
90
|
-
type="image", data=b64_data, mimeType=mime_type, annotations=annotations
|
91
|
-
),
|
89
|
+
"content": ImageContent(type="image", data=b64_data, mimeType=mime_type, annotations=annotations),
|
92
90
|
}
|
93
91
|
|
94
92
|
|
@@ -134,21 +132,15 @@ def MCPFile(
|
|
134
132
|
# Fallback to binary if text read fails
|
135
133
|
binary_data = path.read_bytes()
|
136
134
|
b64_data = base64.b64encode(binary_data).decode("ascii")
|
137
|
-
resource = BlobResourceContents(
|
138
|
-
uri=uri, blob=b64_data, mimeType=mime_type or "application/octet-stream"
|
139
|
-
)
|
135
|
+
resource = BlobResourceContents(uri=uri, blob=b64_data, mimeType=mime_type or "application/octet-stream")
|
140
136
|
|
141
137
|
return {
|
142
138
|
"role": role,
|
143
|
-
"content": EmbeddedResource(
|
144
|
-
type="resource", resource=resource, annotations=annotations
|
145
|
-
),
|
139
|
+
"content": EmbeddedResource(type="resource", resource=resource, annotations=annotations),
|
146
140
|
}
|
147
141
|
|
148
142
|
|
149
|
-
def MCPPrompt(
|
150
|
-
*content_items, role: Literal["user", "assistant"] = "user"
|
151
|
-
) -> List[dict]:
|
143
|
+
def MCPPrompt(*content_items, role: Literal["user", "assistant"] = "user") -> List[dict]:
|
152
144
|
"""
|
153
145
|
Create one or more prompt messages with various content types.
|
154
146
|
|
mcp_agent/core/prompt.py
CHANGED
@@ -5,10 +5,11 @@ Prompt class for easily creating and working with MCP prompt content.
|
|
5
5
|
from typing import List, Literal
|
6
6
|
|
7
7
|
from mcp.types import PromptMessage
|
8
|
+
|
8
9
|
from mcp_agent.mcp.prompt_message_multipart import PromptMessageMultipart
|
9
10
|
|
10
11
|
# Import our content helper functions
|
11
|
-
from .mcp_content import
|
12
|
+
from .mcp_content import Assistant, MCPPrompt, User
|
12
13
|
|
13
14
|
|
14
15
|
class Prompt:
|
@@ -39,9 +40,7 @@ class Prompt:
|
|
39
40
|
A PromptMessageMultipart with user role and the specified content
|
40
41
|
"""
|
41
42
|
messages = User(*content_items)
|
42
|
-
return PromptMessageMultipart(
|
43
|
-
role="user", content=[msg["content"] for msg in messages]
|
44
|
-
)
|
43
|
+
return PromptMessageMultipart(role="user", content=[msg["content"] for msg in messages])
|
45
44
|
|
46
45
|
@classmethod
|
47
46
|
def assistant(cls, *content_items) -> PromptMessageMultipart:
|
@@ -55,14 +54,10 @@ class Prompt:
|
|
55
54
|
A PromptMessageMultipart with assistant role and the specified content
|
56
55
|
"""
|
57
56
|
messages = Assistant(*content_items)
|
58
|
-
return PromptMessageMultipart(
|
59
|
-
role="assistant", content=[msg["content"] for msg in messages]
|
60
|
-
)
|
57
|
+
return PromptMessageMultipart(role="assistant", content=[msg["content"] for msg in messages])
|
61
58
|
|
62
59
|
@classmethod
|
63
|
-
def message(
|
64
|
-
cls, *content_items, role: Literal["user", "assistant"] = "user"
|
65
|
-
) -> PromptMessageMultipart:
|
60
|
+
def message(cls, *content_items, role: Literal["user", "assistant"] = "user") -> PromptMessageMultipart:
|
66
61
|
"""
|
67
62
|
Create a PromptMessageMultipart with the specified role and content items.
|
68
63
|
|
@@ -100,7 +95,7 @@ class Prompt:
|
|
100
95
|
for item in messages:
|
101
96
|
if isinstance(item, PromptMessageMultipart):
|
102
97
|
# Convert PromptMessageMultipart to a list of PromptMessages
|
103
|
-
result.extend(item.
|
98
|
+
result.extend(item.from_multipart())
|
104
99
|
elif isinstance(item, dict) and "role" in item and "content" in item:
|
105
100
|
# Convert a single message dict to PromptMessage
|
106
101
|
result.append(PromptMessage(**item))
|
@@ -114,9 +109,7 @@ class Prompt:
|
|
114
109
|
return result
|
115
110
|
|
116
111
|
@classmethod
|
117
|
-
def from_multipart(
|
118
|
-
cls, multipart: List[PromptMessageMultipart]
|
119
|
-
) -> List[PromptMessage]:
|
112
|
+
def from_multipart(cls, multipart: List[PromptMessageMultipart]) -> List[PromptMessage]:
|
120
113
|
"""
|
121
114
|
Convert a list of PromptMessageMultipart objects to PromptMessages.
|
122
115
|
|
@@ -128,5 +121,5 @@ class Prompt:
|
|
128
121
|
"""
|
129
122
|
result = []
|
130
123
|
for mp in multipart:
|
131
|
-
result.extend(mp.
|
124
|
+
result.extend(mp.from_multipart())
|
132
125
|
return result
|