fast-agent-mcp 0.2.16__py3-none-any.whl → 0.2.17__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 (44) hide show
  1. {fast_agent_mcp-0.2.16.dist-info → fast_agent_mcp-0.2.17.dist-info}/METADATA +4 -6
  2. {fast_agent_mcp-0.2.16.dist-info → fast_agent_mcp-0.2.17.dist-info}/RECORD +43 -43
  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/setup.py +1 -1
  8. mcp_agent/config.py +16 -13
  9. mcp_agent/context.py +4 -22
  10. mcp_agent/core/agent_types.py +2 -2
  11. mcp_agent/core/direct_decorators.py +2 -2
  12. mcp_agent/core/direct_factory.py +2 -1
  13. mcp_agent/core/fastagent.py +1 -1
  14. mcp_agent/core/request_params.py +5 -1
  15. mcp_agent/executor/workflow_signal.py +0 -2
  16. mcp_agent/llm/augmented_llm.py +183 -57
  17. mcp_agent/llm/augmented_llm_passthrough.py +1 -1
  18. mcp_agent/llm/augmented_llm_playback.py +21 -1
  19. mcp_agent/llm/memory.py +3 -3
  20. mcp_agent/llm/model_factory.py +3 -1
  21. mcp_agent/llm/provider_key_manager.py +1 -0
  22. mcp_agent/llm/provider_types.py +2 -1
  23. mcp_agent/llm/providers/augmented_llm_anthropic.py +49 -10
  24. mcp_agent/llm/providers/augmented_llm_deepseek.py +0 -2
  25. mcp_agent/llm/providers/augmented_llm_google.py +30 -0
  26. mcp_agent/llm/providers/augmented_llm_openai.py +95 -158
  27. mcp_agent/llm/providers/multipart_converter_openai.py +10 -27
  28. mcp_agent/llm/providers/sampling_converter_openai.py +5 -6
  29. mcp_agent/mcp/interfaces.py +6 -1
  30. mcp_agent/mcp/mcp_aggregator.py +2 -8
  31. mcp_agent/mcp/prompt_message_multipart.py +25 -2
  32. mcp_agent/resources/examples/data-analysis/analysis-campaign.py +2 -2
  33. mcp_agent/resources/examples/in_dev/agent_build.py +1 -1
  34. mcp_agent/resources/examples/internal/job.py +1 -1
  35. mcp_agent/resources/examples/mcp/state-transfer/fastagent.config.yaml +1 -1
  36. mcp_agent/resources/examples/prompting/agent.py +0 -2
  37. mcp_agent/resources/examples/prompting/fastagent.config.yaml +2 -3
  38. mcp_agent/resources/examples/researcher/fastagent.config.yaml +1 -6
  39. mcp_agent/resources/examples/workflows/fastagent.config.yaml +0 -1
  40. mcp_agent/resources/examples/workflows/parallel.py +1 -1
  41. mcp_agent/executor/decorator_registry.py +0 -112
  42. {fast_agent_mcp-0.2.16.dist-info → fast_agent_mcp-0.2.17.dist-info}/WHEEL +0 -0
  43. {fast_agent_mcp-0.2.16.dist-info → fast_agent_mcp-0.2.17.dist-info}/entry_points.txt +0 -0
  44. {fast_agent_mcp-0.2.16.dist-info → fast_agent_mcp-0.2.17.dist-info}/licenses/LICENSE +0 -0
@@ -5,9 +5,8 @@ This provides a simplified implementation that routes messages to agents
5
5
  by determining the best agent for a request and dispatching to it.
6
6
  """
7
7
 
8
- from typing import TYPE_CHECKING, List, Optional, Tuple, Type
8
+ from typing import TYPE_CHECKING, Callable, List, Optional, Tuple, Type
9
9
 
10
- from mcp.types import TextContent
11
10
  from pydantic import BaseModel
12
11
 
13
12
  from mcp_agent.agents.agent import Agent
@@ -17,10 +16,12 @@ from mcp_agent.core.exceptions import AgentConfigError
17
16
  from mcp_agent.core.prompt import Prompt
18
17
  from mcp_agent.core.request_params import RequestParams
19
18
  from mcp_agent.logging.logger import get_logger
20
- from mcp_agent.mcp.interfaces import ModelT
19
+ from mcp_agent.mcp.interfaces import AugmentedLLMProtocol, ModelT
21
20
  from mcp_agent.mcp.prompt_message_multipart import PromptMessageMultipart
22
21
 
23
22
  if TYPE_CHECKING:
23
+ from a2a_types.types import AgentCard
24
+
24
25
  from mcp_agent.context import Context
25
26
 
26
27
  logger = get_logger(__name__)
@@ -36,47 +37,17 @@ Follow these guidelines:
36
37
  - Provide your confidence level (high, medium, low) and brief reasoning for your selection
37
38
  """
38
39
 
39
- # Default routing instruction with placeholders for context and request
40
+ # Default routing instruction with placeholders for context (AgentCard JSON)
40
41
  DEFAULT_ROUTING_INSTRUCTION = """
41
- You are a highly accurate request router that directs incoming requests to the most appropriate agent.
42
-
43
- <fastagent:data>
42
+ Select from the following agents to handle the request:
44
43
  <fastagent:agents>
44
+ [
45
45
  {context}
46
+ ]
46
47
  </fastagent:agents>
47
48
 
48
- <fastagent:request>
49
- {request}
50
- </fastagent:request>
51
- </fastagent:data>
52
-
53
- Your task is to analyze the request and determine the most appropriate agent from the options above.
54
-
55
- <fastagent:instruction>
56
- Respond with JSON following the schema below:
57
- {{
58
- "type": "object",
59
- "required": ["agent", "confidence", "reasoning"],
60
- "properties": {{
61
- "agent": {{
62
- "type": "string",
63
- "description": "The exact name of the selected agent"
64
- }},
65
- "confidence": {{
66
- "type": "string",
67
- "enum": ["high", "medium", "low"],
68
- "description": "Your confidence level in this selection"
69
- }},
70
- "reasoning": {{
71
- "type": "string",
72
- "description": "Brief explanation for your selection"
73
- }}
74
- }}
75
- }}
76
-
77
- Supply only the JSON with no preamble. Use "reasoning" field to describe actions. NEVER EMIT CODE FENCES.
78
-
79
- </fastagent:instruction>
49
+ You must respond with the 'name' of one of the agents listed above.
50
+
80
51
  """
81
52
 
82
53
 
@@ -85,18 +56,7 @@ class RoutingResponse(BaseModel):
85
56
 
86
57
  agent: str
87
58
  confidence: str
88
- reasoning: Optional[str] = None
89
-
90
-
91
- class RouterResult(BaseModel):
92
- """Router result with agent reference and confidence rating."""
93
-
94
- result: BaseAgent
95
- confidence: str
96
- reasoning: Optional[str] = None
97
-
98
- # Allow Agent objects to be stored without serialization
99
- model_config = {"arbitrary_types_allowed": True}
59
+ reasoning: str | None = None
100
60
 
101
61
 
102
62
  class RouterAgent(BaseAgent):
@@ -142,9 +102,7 @@ class RouterAgent(BaseAgent):
142
102
  # Set up base router request parameters
143
103
  base_params = {"systemPrompt": ROUTING_SYSTEM_INSTRUCTION, "use_history": False}
144
104
 
145
- # Merge with provided defaults if any
146
105
  if default_request_params:
147
- # Start with defaults and override with router-specific settings
148
106
  merged_params = default_request_params.model_copy(update=base_params)
149
107
  else:
150
108
  merged_params = RequestParams(**base_params)
@@ -174,32 +132,16 @@ class RouterAgent(BaseAgent):
174
132
  except Exception as e:
175
133
  logger.warning(f"Error shutting down agent: {str(e)}")
176
134
 
177
- async def _get_routing_result(
135
+ async def attach_llm(
178
136
  self,
179
- messages: List[PromptMessageMultipart],
180
- ) -> Optional[RouterResult]:
181
- """
182
- Common method to extract request and get routing result.
183
-
184
- Args:
185
- messages: The messages to extract request from
186
-
187
- Returns:
188
- RouterResult containing the selected agent, or None if no suitable agent found
189
- """
190
- if not self.initialized:
191
- await self.initialize()
192
-
193
- # Extract the request text from the last message
194
- request = messages[-1].all_text() if messages else ""
195
-
196
- # Determine which agent to route to
197
- routing_result = await self._route_request(request)
198
-
199
- if not routing_result:
200
- logger.warning("Could not determine appropriate agent for this request")
201
-
202
- return routing_result
137
+ llm_factory: type[AugmentedLLMProtocol] | Callable[..., AugmentedLLMProtocol],
138
+ model: str | None = None,
139
+ request_params: RequestParams | None = None,
140
+ **additional_kwargs,
141
+ ) -> AugmentedLLMProtocol:
142
+ return await super().attach_llm(
143
+ llm_factory, model, request_params, verb="Routing", **additional_kwargs
144
+ )
203
145
 
204
146
  async def generate(
205
147
  self,
@@ -216,32 +158,21 @@ class RouterAgent(BaseAgent):
216
158
  Returns:
217
159
  The response from the selected agent
218
160
  """
219
- routing_result = await self._get_routing_result(multipart_messages)
220
-
221
- if not routing_result:
222
- return PromptMessageMultipart(
223
- role="assistant",
224
- content=[
225
- TextContent(
226
- type="text", text="Could not determine appropriate agent for this request."
227
- )
228
- ],
229
- )
230
161
 
231
- # Get the selected agent
232
- selected_agent = routing_result.result
162
+ route, warn = await self._route_request(multipart_messages[-1])
233
163
 
234
- # Log the routing decision
235
- logger.info(
236
- f"Routing request to agent: {selected_agent.name} (confidence: {routing_result.confidence})"
237
- )
164
+ if not route:
165
+ return Prompt.assistant(warn or "No routing result or warning received")
166
+
167
+ # Get the selected agent
168
+ agent: Agent = self.agent_map[route.agent]
238
169
 
239
170
  # Dispatch the request to the selected agent
240
- return await selected_agent.generate(multipart_messages, request_params)
171
+ return await agent.generate(multipart_messages, request_params)
241
172
 
242
173
  async def structured(
243
174
  self,
244
- prompt: List[PromptMessageMultipart],
175
+ multipart_messages: List[PromptMessageMultipart],
245
176
  model: Type[ModelT],
246
177
  request_params: Optional[RequestParams] = None,
247
178
  ) -> Tuple[ModelT | None, PromptMessageMultipart]:
@@ -256,23 +187,22 @@ class RouterAgent(BaseAgent):
256
187
  Returns:
257
188
  The parsed response from the selected agent, or None if parsing fails
258
189
  """
259
- routing_result = await self._get_routing_result(prompt)
190
+ route, warn = await self._route_request(multipart_messages[-1])
260
191
 
261
- if not routing_result:
262
- return None, Prompt.assistant("No routing result")
192
+ if not route:
193
+ return None, Prompt.assistant(
194
+ warn or "No routing result or warning received (structured)"
195
+ )
263
196
 
264
197
  # Get the selected agent
265
- selected_agent = routing_result.result
266
-
267
- # Log the routing decision
268
- logger.info(
269
- f"Routing structured request to agent: {selected_agent.name} (confidence: {routing_result.confidence})"
270
- )
198
+ agent: Agent = self.agent_map[route.agent]
271
199
 
272
200
  # Dispatch the request to the selected agent
273
- return await selected_agent.structured(prompt, model, request_params)
201
+ return await agent.structured(multipart_messages, model, request_params)
274
202
 
275
- async def _route_request(self, request: str) -> Optional[RouterResult]:
203
+ async def _route_request(
204
+ self, message: PromptMessageMultipart
205
+ ) -> Tuple[RoutingResponse | None, str | None]:
276
206
  """
277
207
  Determine which agent to route the request to.
278
208
 
@@ -283,49 +213,53 @@ class RouterAgent(BaseAgent):
283
213
  RouterResult containing the selected agent, or None if no suitable agent was found
284
214
  """
285
215
  if not self.agents:
286
- logger.warning("No agents available for routing")
287
- return None
216
+ logger.error("No agents available for routing")
217
+ raise AgentConfigError("No agents available for routing - fatal error")
288
218
 
289
219
  # If only one agent is available, use it directly
290
220
  if len(self.agents) == 1:
291
- return RouterResult(
292
- result=self.agents[0], confidence="high", reasoning="Only one agent available"
293
- )
221
+ return RoutingResponse(
222
+ agent=self.agents[0].name, confidence="high", reasoning="Only one agent available"
223
+ ), None
294
224
 
295
225
  # Generate agent descriptions for the context
296
226
  agent_descriptions = []
297
- for i, agent in enumerate(self.agents, 1):
298
- description = agent.instruction if isinstance(agent.instruction, str) else ""
299
- agent_descriptions.append(f"{i}. Name: {agent.name} - {description}")
227
+ for agent in self.agents:
228
+ agent_card: AgentCard = await agent.agent_card()
229
+ agent_descriptions.append(
230
+ agent_card.model_dump_json(
231
+ include={"name", "description", "skills"}, exclude_none=True
232
+ )
233
+ )
300
234
 
301
- context = "\n\n".join(agent_descriptions)
235
+ context = ",\n".join(agent_descriptions)
302
236
 
303
237
  # Format the routing prompt
304
238
  routing_instruction = self.routing_instruction or DEFAULT_ROUTING_INSTRUCTION
305
- prompt_text = routing_instruction.format(context=context, request=request)
306
-
307
- # Create multipart message for the router
308
- prompt = PromptMessageMultipart(
309
- role="user", content=[TextContent(type="text", text=prompt_text)]
310
- )
239
+ routing_instruction = routing_instruction.format(context=context)
311
240
 
312
- # Get structured response from LLM
313
241
  assert self._llm
242
+ mutated = message.model_copy(deep=True)
243
+ mutated.add_text(routing_instruction)
314
244
  response, _ = await self._llm.structured(
315
- [prompt], RoutingResponse, self._default_request_params
245
+ [mutated],
246
+ RoutingResponse,
247
+ self._default_request_params,
316
248
  )
317
249
 
250
+ warn: str | None = None
318
251
  if not response:
319
- logger.warning("No routing response received from LLM")
320
- return None
252
+ warn = "No routing response received from LLM"
253
+ elif response.agent not in self.agent_map:
254
+ warn = f"A response was received, but the agent {response.agent} was not known to the Router"
321
255
 
322
- # Look up the agent by name
323
- selected_agent = self.agent_map.get(response.agent)
324
-
325
- if not selected_agent:
326
- logger.warning(f"Agent '{response.agent}' not found in available agents")
327
- return None
256
+ if warn:
257
+ logger.warning(warn)
258
+ return None, warn
259
+ else:
260
+ assert response
261
+ logger.info(
262
+ f"Routing structured request to agent: {response.agent or 'error'} (confidence: {response.confidence or ''})"
263
+ )
328
264
 
329
- return RouterResult(
330
- result=selected_agent, confidence=response.confidence, reasoning=response.reasoning
331
- )
265
+ return response, None
mcp_agent/app.py CHANGED
@@ -1,7 +1,6 @@
1
1
  import asyncio
2
2
  from contextlib import asynccontextmanager
3
- from datetime import timedelta
4
- from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Type, TypeVar
3
+ from typing import TYPE_CHECKING, Dict, Optional, Type, TypeVar
5
4
 
6
5
  from mcp_agent.config import Settings
7
6
  from mcp_agent.context import Context, cleanup_context, initialize_context
@@ -174,125 +173,3 @@ class MCPApp:
174
173
  yield self
175
174
  finally:
176
175
  await self.cleanup()
177
-
178
- def workflow(self, cls: Type, *args, workflow_id: str | None = None, **kwargs) -> Type:
179
- """
180
- Decorator for a workflow class. By default it's a no-op,
181
- but different executors can use this to customize behavior
182
- for workflow registration.
183
-
184
- Example:
185
- If Temporal is available & we use a TemporalExecutor,
186
- this decorator will wrap with temporal_workflow.defn.
187
- """
188
- decorator_registry = self.context.decorator_registry
189
- execution_engine = self.engine
190
- workflow_defn_decorator = decorator_registry.get_workflow_defn_decorator(execution_engine)
191
-
192
- if workflow_defn_decorator:
193
- return workflow_defn_decorator(cls, *args, **kwargs)
194
-
195
- cls._app = self
196
- self._workflows[workflow_id or cls.__name__] = cls
197
-
198
- # Default no-op
199
- return cls
200
-
201
- def workflow_run(self, fn: Callable[..., R]) -> Callable[..., R]:
202
- """
203
- Decorator for a workflow's main 'run' method.
204
- Different executors can use this to customize behavior for workflow execution.
205
-
206
- Example:
207
- If Temporal is in use, this gets converted to @workflow.run.
208
- """
209
-
210
- decorator_registry = self.context.decorator_registry
211
- execution_engine = self.engine
212
- workflow_run_decorator = decorator_registry.get_workflow_run_decorator(execution_engine)
213
-
214
- if workflow_run_decorator:
215
- return workflow_run_decorator(fn)
216
-
217
- # Default no-op
218
- def wrapper(*args, **kwargs):
219
- # no-op wrapper
220
- return fn(*args, **kwargs)
221
-
222
- return wrapper
223
-
224
- def workflow_task(
225
- self,
226
- name: str | None = None,
227
- schedule_to_close_timeout: timedelta | None = None,
228
- retry_policy: Dict[str, Any] | None = None,
229
- **kwargs: Any,
230
- ) -> Callable[[Callable[..., R]], Callable[..., R]]:
231
- """
232
- Decorator to mark a function as a workflow task,
233
- automatically registering it in the global activity registry.
234
-
235
- Args:
236
- name: Optional custom name for the activity
237
- schedule_to_close_timeout: Maximum time the task can take to complete
238
- retry_policy: Retry policy configuration
239
- **kwargs: Additional metadata passed to the activity registration
240
-
241
- Returns:
242
- Decorated function that preserves async and typing information
243
-
244
- Raises:
245
- TypeError: If the decorated function is not async
246
- ValueError: If the retry policy or timeout is invalid
247
- """
248
-
249
- def decorator(func: Callable[..., R]) -> Callable[..., R]:
250
- if not asyncio.iscoroutinefunction(func):
251
- raise TypeError(f"Function {func.__name__} must be async.")
252
-
253
- actual_name = name or f"{func.__module__}.{func.__qualname__}"
254
- timeout = schedule_to_close_timeout or timedelta(minutes=10)
255
- metadata = {
256
- "activity_name": actual_name,
257
- "schedule_to_close_timeout": timeout,
258
- "retry_policy": retry_policy or {},
259
- **kwargs,
260
- }
261
- activity_registry = self.context.task_registry
262
- activity_registry.register(actual_name, func, metadata)
263
-
264
- setattr(func, "is_workflow_task", True)
265
- setattr(func, "execution_metadata", metadata)
266
-
267
- # TODO: saqadri - determine if we need this
268
- # Preserve metadata through partial application
269
- # @functools.wraps(func)
270
- # async def wrapper(*args: Any, **kwargs: Any) -> R:
271
- # result = await func(*args, **kwargs)
272
- # return cast(R, result) # Ensure type checking works
273
-
274
- # # Add metadata that survives partial application
275
- # wrapper.is_workflow_task = True # type: ignore
276
- # wrapper.execution_metadata = metadata # type: ignore
277
-
278
- # # Make metadata accessible through partial
279
- # def __getattr__(name: str) -> Any:
280
- # if name == "is_workflow_task":
281
- # return True
282
- # if name == "execution_metadata":
283
- # return metadata
284
- # raise AttributeError(f"'{func.__name__}' has no attribute '{name}'")
285
-
286
- # wrapper.__getattr__ = __getattr__ # type: ignore
287
-
288
- # return wrapper
289
-
290
- return func
291
-
292
- return decorator
293
-
294
- def is_workflow_task(self, func: Callable[..., Any]) -> bool:
295
- """
296
- Check if a function is marked as a workflow task.
297
- This gets set for functions that are decorated with @workflow_task."""
298
- return bool(getattr(func, "is_workflow_task", False))
@@ -15,7 +15,7 @@ FASTAGENT_CONFIG_TEMPLATE = """
15
15
  # Takes format:
16
16
  # <provider>.<model_string>.<reasoning_effort?> (e.g. anthropic.claude-3-5-sonnet-20241022 or openai.o3-mini.low)
17
17
  # Accepts aliases for Anthropic Models: haiku, haiku3, sonnet, sonnet35, opus, opus3
18
- # and OpenAI Models: gpt-4o-mini, gpt-4o, o1, o1-mini, o3-mini
18
+ # and OpenAI Models: gpt-4.1, gpt-4.1-mini, o1, o1-mini, o3-mini
19
19
  #
20
20
  # If not specified, defaults to "haiku".
21
21
  # Can be overriden with a command line switch --model=<model>, or within the Agent constructor.
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
 
@@ -381,7 +381,7 @@ class FastAgent:
381
381
  handle_error(
382
382
  e,
383
383
  "Model Configuration Error",
384
- "Common models: gpt-4o, o3-mini, sonnet, haiku. for o3, set reasoning effort with o3-mini.high",
384
+ "Common models: gpt-4.1, o3-mini, sonnet, haiku. for o3, set reasoning effort with o3-mini.high",
385
385
  )
386
386
  elif isinstance(e, CircularDependencyError):
387
387
  handle_error(
@@ -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
+ """