fast-agent-mcp 0.2.16__py3-none-any.whl → 0.2.18__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 (49) hide show
  1. {fast_agent_mcp-0.2.16.dist-info → fast_agent_mcp-0.2.18.dist-info}/METADATA +6 -7
  2. {fast_agent_mcp-0.2.16.dist-info → fast_agent_mcp-0.2.18.dist-info}/RECORD +48 -47
  3. mcp_agent/agents/base_agent.py +50 -6
  4. mcp_agent/agents/workflow/orchestrator_agent.py +6 -7
  5. mcp_agent/agents/workflow/router_agent.py +70 -136
  6. mcp_agent/app.py +1 -124
  7. mcp_agent/cli/commands/go.py +133 -0
  8. mcp_agent/cli/commands/setup.py +2 -2
  9. mcp_agent/cli/main.py +5 -3
  10. mcp_agent/config.py +16 -13
  11. mcp_agent/context.py +4 -22
  12. mcp_agent/core/agent_types.py +2 -2
  13. mcp_agent/core/direct_decorators.py +2 -2
  14. mcp_agent/core/direct_factory.py +2 -1
  15. mcp_agent/core/enhanced_prompt.py +12 -7
  16. mcp_agent/core/fastagent.py +39 -5
  17. mcp_agent/core/interactive_prompt.py +6 -2
  18. mcp_agent/core/request_params.py +5 -1
  19. mcp_agent/core/validation.py +12 -1
  20. mcp_agent/executor/workflow_signal.py +0 -2
  21. mcp_agent/llm/augmented_llm.py +183 -57
  22. mcp_agent/llm/augmented_llm_passthrough.py +1 -1
  23. mcp_agent/llm/augmented_llm_playback.py +21 -1
  24. mcp_agent/llm/memory.py +3 -3
  25. mcp_agent/llm/model_factory.py +3 -1
  26. mcp_agent/llm/provider_key_manager.py +1 -0
  27. mcp_agent/llm/provider_types.py +2 -1
  28. mcp_agent/llm/providers/augmented_llm_anthropic.py +50 -10
  29. mcp_agent/llm/providers/augmented_llm_deepseek.py +1 -5
  30. mcp_agent/llm/providers/augmented_llm_google.py +30 -0
  31. mcp_agent/llm/providers/augmented_llm_openai.py +96 -159
  32. mcp_agent/llm/providers/multipart_converter_openai.py +10 -27
  33. mcp_agent/llm/providers/sampling_converter_openai.py +5 -6
  34. mcp_agent/mcp/interfaces.py +6 -1
  35. mcp_agent/mcp/mcp_aggregator.py +2 -8
  36. mcp_agent/mcp/prompt_message_multipart.py +25 -2
  37. mcp_agent/resources/examples/data-analysis/analysis-campaign.py +2 -2
  38. mcp_agent/resources/examples/in_dev/agent_build.py +1 -1
  39. mcp_agent/resources/examples/internal/job.py +1 -1
  40. mcp_agent/resources/examples/mcp/state-transfer/fastagent.config.yaml +1 -1
  41. mcp_agent/resources/examples/prompting/agent.py +0 -2
  42. mcp_agent/resources/examples/prompting/fastagent.config.yaml +2 -3
  43. mcp_agent/resources/examples/researcher/fastagent.config.yaml +1 -6
  44. mcp_agent/resources/examples/workflows/fastagent.config.yaml +0 -1
  45. mcp_agent/resources/examples/workflows/parallel.py +1 -1
  46. mcp_agent/executor/decorator_registry.py +0 -112
  47. {fast_agent_mcp-0.2.16.dist-info → fast_agent_mcp-0.2.18.dist-info}/WHEEL +0 -0
  48. {fast_agent_mcp-0.2.16.dist-info → fast_agent_mcp-0.2.18.dist-info}/entry_points.txt +0 -0
  49. {fast_agent_mcp-0.2.16.dist-info → fast_agent_mcp-0.2.18.dist-info}/licenses/LICENSE +0 -0
mcp_agent/config.py CHANGED
@@ -139,40 +139,42 @@ class DeepSeekSettings(BaseModel):
139
139
  model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)
140
140
 
141
141
 
142
- class GenericSettings(BaseModel):
142
+ class GoogleSettings(BaseModel):
143
143
  """
144
144
  Settings for using OpenAI models in the fast-agent application.
145
145
  """
146
146
 
147
147
  api_key: str | None = None
148
+ # reasoning_effort: Literal["low", "medium", "high"] = "medium"
148
149
 
149
150
  base_url: str | None = None
150
151
 
151
152
  model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)
152
153
 
153
154
 
154
- class OpenRouterSettings(BaseModel):
155
+ class GenericSettings(BaseModel):
155
156
  """
156
- Settings for using OpenRouter models via its OpenAI-compatible API.
157
+ Settings for using OpenAI models in the fast-agent application.
157
158
  """
158
159
 
159
160
  api_key: str | None = None
160
161
 
161
- base_url: str | None = None # Optional override, defaults handled in provider
162
+ base_url: str | None = None
162
163
 
163
164
  model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)
164
165
 
165
166
 
166
- class TemporalSettings(BaseModel):
167
+ class OpenRouterSettings(BaseModel):
167
168
  """
168
- Temporal settings for the fast-agent application.
169
+ Settings for using OpenRouter models via its OpenAI-compatible API.
169
170
  """
170
171
 
171
- host: str
172
- namespace: str = "default"
173
- task_queue: str
174
172
  api_key: str | None = None
175
173
 
174
+ base_url: str | None = None # Optional override, defaults handled in provider
175
+
176
+ model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)
177
+
176
178
 
177
179
  class OpenTelemetrySettings(BaseModel):
178
180
  """
@@ -254,16 +256,14 @@ class Settings(BaseSettings):
254
256
  mcp: MCPSettings | None = MCPSettings()
255
257
  """MCP config, such as MCP servers"""
256
258
 
257
- execution_engine: Literal["asyncio", "temporal"] = "asyncio"
259
+ execution_engine: Literal["asyncio"] = "asyncio"
258
260
  """Execution engine for the fast-agent application"""
259
261
 
260
262
  default_model: str | None = "haiku"
261
263
  """
262
264
  Default model for agents. Format is provider.model_name.<reasoning_effort>, for example openai.o3-mini.low
263
- Aliases are provided for common models e.g. sonnet, haiku, gpt-4o, o3-mini etc.
265
+ Aliases are provided for common models e.g. sonnet, haiku, gpt-4.1, o3-mini etc.
264
266
  """
265
- temporal: TemporalSettings | None = None
266
- """Settings for Temporal workflow orchestration"""
267
267
 
268
268
  anthropic: AnthropicSettings | None = None
269
269
  """Settings for using Anthropic models in the fast-agent application"""
@@ -277,6 +277,9 @@ class Settings(BaseSettings):
277
277
  deepseek: DeepSeekSettings | None = None
278
278
  """Settings for using DeepSeek models in the fast-agent application"""
279
279
 
280
+ google: GoogleSettings | None = None
281
+ """Settings for using DeepSeek models in the fast-agent application"""
282
+
280
283
  openrouter: OpenRouterSettings | None = None
281
284
  """Settings for using OpenRouter models in the fast-agent application"""
282
285
 
mcp_agent/context.py CHANGED
@@ -17,10 +17,6 @@ from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapProp
17
17
  from pydantic import BaseModel, ConfigDict
18
18
 
19
19
  from mcp_agent.config import Settings, get_settings
20
- from mcp_agent.executor.decorator_registry import (
21
- DecoratorRegistry,
22
- register_asyncio_decorators,
23
- )
24
20
  from mcp_agent.executor.executor import AsyncioExecutor, Executor
25
21
  from mcp_agent.executor.task_registry import ActivityRegistry
26
22
  from mcp_agent.logging.events import EventFilter
@@ -54,7 +50,6 @@ class Context(BaseModel):
54
50
  # Registries
55
51
  server_registry: Optional[ServerRegistry] = None
56
52
  task_registry: Optional[ActivityRegistry] = None
57
- decorator_registry: Optional[DecoratorRegistry] = None
58
53
 
59
54
  tracer: Optional[trace.Tracer] = None
60
55
 
@@ -142,18 +137,7 @@ async def configure_executor(config: "Settings"):
142
137
  """
143
138
  Configure the executor based on the application config.
144
139
  """
145
- if config.execution_engine == "asyncio":
146
- return AsyncioExecutor()
147
- elif config.execution_engine == "temporal":
148
- # Configure Temporal executor
149
- from mcp_agent.executor.temporal import TemporalExecutor
150
-
151
- executor = TemporalExecutor(config=config.temporal)
152
- return executor
153
- else:
154
- # Default to asyncio executor
155
- executor = AsyncioExecutor()
156
- return executor
140
+ return AsyncioExecutor()
157
141
 
158
142
 
159
143
  async def initialize_context(
@@ -180,11 +164,9 @@ async def initialize_context(
180
164
  context.executor = await configure_executor(config)
181
165
  context.task_registry = ActivityRegistry()
182
166
 
183
- context.decorator_registry = DecoratorRegistry()
184
- register_asyncio_decorators(context.decorator_registry)
185
-
186
167
  # Store the tracer in context if needed
187
- context.tracer = trace.get_tracer(config.otel.service_name)
168
+ if config.otel:
169
+ context.tracer = trace.get_tracer(config.otel.service_name)
188
170
 
189
171
  if store_globally:
190
172
  global _global_context
@@ -234,7 +216,7 @@ def get_current_context() -> Context:
234
216
  def get_current_config():
235
217
  """
236
218
  Get the current application config.
237
-
219
+
238
220
  Returns the context config if available, otherwise falls back to global settings.
239
221
  """
240
222
  return get_current_context().config or get_settings()
@@ -34,8 +34,8 @@ class AgentConfig(BaseModel):
34
34
  human_input: bool = False
35
35
  agent_type: AgentType = AgentType.BASIC
36
36
 
37
- @model_validator(mode='after')
38
- def ensure_default_request_params(self) -> 'AgentConfig':
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
40
  if self.default_request_params is None:
41
41
  self.default_request_params = RequestParams(
@@ -224,7 +224,7 @@ def orchestrator(
224
224
  use_history: bool = False,
225
225
  human_input: bool = False,
226
226
  plan_type: Literal["full", "iterative"] = "full",
227
- max_iterations: int = 30,
227
+ plan_iterations: int = 5,
228
228
  ) -> Callable[[AgentCallable[P, R]], DecoratedOrchestratorProtocol[P, R]]:
229
229
  """
230
230
  Decorator to create and register an orchestrator agent with type-safe signature.
@@ -260,7 +260,7 @@ def orchestrator(
260
260
  human_input=human_input,
261
261
  child_agents=agents,
262
262
  plan_type=plan_type,
263
- max_iterations=max_iterations,
263
+ plan_iterations=plan_iterations,
264
264
  ),
265
265
  )
266
266
 
@@ -138,7 +138,7 @@ async def create_agents_by_type(
138
138
  # Get common configuration
139
139
  config = agent_data["config"]
140
140
 
141
- # Type-specific initialization based on the Enum type
141
+ # Type-specific initialization based on the Enum type
142
142
  # Note: Above we compared string values from config, here we compare Enum objects directly
143
143
  if agent_type == AgentType.BASIC:
144
144
  # Create a basic agent
@@ -175,6 +175,7 @@ async def create_agents_by_type(
175
175
  config=config,
176
176
  context=app_instance.context,
177
177
  agents=child_agents,
178
+ plan_iterations=agent_data.get("plan_iterations", 5),
178
179
  plan_type=agent_data.get("plan_type", "full"),
179
180
  )
180
181
 
@@ -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" and len(cmd_parts) > 1:
293
- # Direct prompt selection with name or number
294
- prompt_arg = cmd_parts[1].strip()
295
- # Check if it's a number (use as index) or a name (use directly)
296
- if prompt_arg.isdigit():
297
- return {"select_prompt": True, "prompt_index": int(prompt_arg)}
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
- return f"SELECT_PROMPT:{prompt_arg}"
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":
@@ -9,7 +9,8 @@ import asyncio
9
9
  import sys
10
10
  from contextlib import asynccontextmanager
11
11
  from importlib.metadata import version as get_version
12
- from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, TypeVar
12
+ from pathlib import Path
13
+ from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, TypeVar
13
14
 
14
15
  import yaml
15
16
 
@@ -54,9 +55,11 @@ from mcp_agent.core.validation import (
54
55
  validate_workflow_references,
55
56
  )
56
57
  from mcp_agent.logging.logger import get_logger
58
+ from mcp_agent.mcp.prompts.prompt_load import load_prompt_multipart
57
59
 
58
60
  if TYPE_CHECKING:
59
61
  from mcp_agent.agents.agent import Agent
62
+ from mcp_agent.mcp.prompt_message_multipart import PromptMessageMultipart
60
63
 
61
64
  F = TypeVar("F", bound=Callable[..., Any]) # For decorated functions
62
65
  logger = get_logger(__name__)
@@ -90,12 +93,16 @@ class FastAgent:
90
93
  )
91
94
  parser.add_argument(
92
95
  "--agent",
96
+ default="default",
93
97
  help="Specify the agent to send a message to (used with --message)",
94
98
  )
95
99
  parser.add_argument(
96
100
  "-m",
97
101
  "--message",
98
- help="Message to send to the specified agent (requires --agent)",
102
+ help="Message to send to the specified agent",
103
+ )
104
+ parser.add_argument(
105
+ "-p", "--prompt-file", help="Path to a prompt file to use (either text or JSON)"
99
106
  )
100
107
  parser.add_argument(
101
108
  "--quiet",
@@ -294,8 +301,8 @@ class FastAgent:
294
301
  # Exit after server shutdown
295
302
  raise SystemExit(0)
296
303
 
297
- # Handle direct message sending if --agent and --message are provided
298
- if hasattr(self, "args") and self.args.agent and self.args.message:
304
+ # Handle direct message sending if --message is provided
305
+ if self.args.message:
299
306
  agent_name = self.args.agent
300
307
  message = self.args.message
301
308
 
@@ -321,6 +328,33 @@ class FastAgent:
321
328
  print(f"\n\nError sending message to agent '{agent_name}': {str(e)}")
322
329
  raise SystemExit(1)
323
330
 
331
+ if self.args.prompt_file:
332
+ agent_name = self.args.agent
333
+ prompt: List[PromptMessageMultipart] = load_prompt_multipart(
334
+ Path(self.args.prompt_file)
335
+ )
336
+ if agent_name not in active_agents:
337
+ available_agents = ", ".join(active_agents.keys())
338
+ print(
339
+ f"\n\nError: Agent '{agent_name}' not found. Available agents: {available_agents}"
340
+ )
341
+ raise SystemExit(1)
342
+
343
+ try:
344
+ # Get response from the agent
345
+ agent = active_agents[agent_name]
346
+ response = await agent.generate(prompt)
347
+
348
+ # In quiet mode, just print the raw response
349
+ # The chat display should already be turned off by the configuration
350
+ if self.args.quiet:
351
+ print(f"{response.last_text()}")
352
+
353
+ raise SystemExit(0)
354
+ except Exception as e:
355
+ print(f"\n\nError sending message to agent '{agent_name}': {str(e)}")
356
+ raise SystemExit(1)
357
+
324
358
  yield wrapper
325
359
 
326
360
  except (
@@ -381,7 +415,7 @@ class FastAgent:
381
415
  handle_error(
382
416
  e,
383
417
  "Model Configuration Error",
384
- "Common models: gpt-4o, o3-mini, sonnet, haiku. for o3, set reasoning effort with o3-mini.high",
418
+ "Common models: gpt-4.1, o3-mini, sonnet, haiku. for o3, set reasoning effort with o3-mini.high",
385
419
  )
386
420
  elif isinstance(e, CircularDependencyError):
387
421
  handle_error(
@@ -153,8 +153,12 @@ class InteractivePrompt:
153
153
  )
154
154
  continue
155
155
 
156
- # Skip further processing if command was handled
157
- if command_result:
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":
@@ -2,7 +2,7 @@
2
2
  Request parameters definitions for LLM interactions.
3
3
  """
4
4
 
5
- from typing import List
5
+ from typing import Any, List
6
6
 
7
7
  from mcp import SamplingMessage
8
8
  from mcp.types import CreateMessageRequestParams
@@ -44,3 +44,7 @@ class RequestParams(CreateMessageRequestParams):
44
44
  Whether to allow multiple tool calls per iteration.
45
45
  Also known as multi-step tool use.
46
46
  """
47
+ response_format: Any | None = None
48
+ """
49
+ Override response format for structured calls. Prefer sending pydantic model - only use in exceptional circumstances
50
+ """
@@ -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 evaluation and optimization agents
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
@@ -7,8 +7,6 @@ from pydantic import BaseModel, ConfigDict
7
7
 
8
8
  SignalValueT = TypeVar("SignalValueT")
9
9
 
10
- # TODO: saqadri - handle signals properly that works with other execution backends like Temporal as well
11
-
12
10
 
13
11
  class Signal(BaseModel, Generic[SignalValueT]):
14
12
  """Represents a signal that can be sent to a workflow."""