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.
- {fast_agent_mcp-0.2.16.dist-info → fast_agent_mcp-0.2.18.dist-info}/METADATA +6 -7
- {fast_agent_mcp-0.2.16.dist-info → fast_agent_mcp-0.2.18.dist-info}/RECORD +48 -47
- mcp_agent/agents/base_agent.py +50 -6
- mcp_agent/agents/workflow/orchestrator_agent.py +6 -7
- mcp_agent/agents/workflow/router_agent.py +70 -136
- mcp_agent/app.py +1 -124
- mcp_agent/cli/commands/go.py +133 -0
- mcp_agent/cli/commands/setup.py +2 -2
- mcp_agent/cli/main.py +5 -3
- mcp_agent/config.py +16 -13
- mcp_agent/context.py +4 -22
- mcp_agent/core/agent_types.py +2 -2
- mcp_agent/core/direct_decorators.py +2 -2
- mcp_agent/core/direct_factory.py +2 -1
- mcp_agent/core/enhanced_prompt.py +12 -7
- mcp_agent/core/fastagent.py +39 -5
- mcp_agent/core/interactive_prompt.py +6 -2
- mcp_agent/core/request_params.py +5 -1
- mcp_agent/core/validation.py +12 -1
- mcp_agent/executor/workflow_signal.py +0 -2
- mcp_agent/llm/augmented_llm.py +183 -57
- mcp_agent/llm/augmented_llm_passthrough.py +1 -1
- mcp_agent/llm/augmented_llm_playback.py +21 -1
- mcp_agent/llm/memory.py +3 -3
- mcp_agent/llm/model_factory.py +3 -1
- mcp_agent/llm/provider_key_manager.py +1 -0
- mcp_agent/llm/provider_types.py +2 -1
- mcp_agent/llm/providers/augmented_llm_anthropic.py +50 -10
- mcp_agent/llm/providers/augmented_llm_deepseek.py +1 -5
- mcp_agent/llm/providers/augmented_llm_google.py +30 -0
- mcp_agent/llm/providers/augmented_llm_openai.py +96 -159
- mcp_agent/llm/providers/multipart_converter_openai.py +10 -27
- mcp_agent/llm/providers/sampling_converter_openai.py +5 -6
- mcp_agent/mcp/interfaces.py +6 -1
- mcp_agent/mcp/mcp_aggregator.py +2 -8
- mcp_agent/mcp/prompt_message_multipart.py +25 -2
- mcp_agent/resources/examples/data-analysis/analysis-campaign.py +2 -2
- mcp_agent/resources/examples/in_dev/agent_build.py +1 -1
- mcp_agent/resources/examples/internal/job.py +1 -1
- mcp_agent/resources/examples/mcp/state-transfer/fastagent.config.yaml +1 -1
- mcp_agent/resources/examples/prompting/agent.py +0 -2
- mcp_agent/resources/examples/prompting/fastagent.config.yaml +2 -3
- mcp_agent/resources/examples/researcher/fastagent.config.yaml +1 -6
- mcp_agent/resources/examples/workflows/fastagent.config.yaml +0 -1
- mcp_agent/resources/examples/workflows/parallel.py +1 -1
- mcp_agent/executor/decorator_registry.py +0 -112
- {fast_agent_mcp-0.2.16.dist-info → fast_agent_mcp-0.2.18.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.2.16.dist-info → fast_agent_mcp-0.2.18.dist-info}/entry_points.txt +0 -0
- {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
|
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
|
155
|
+
class GenericSettings(BaseModel):
|
155
156
|
"""
|
156
|
-
Settings for using
|
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
|
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
|
167
|
+
class OpenRouterSettings(BaseModel):
|
167
168
|
"""
|
168
|
-
|
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"
|
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-
|
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
|
-
|
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
|
-
|
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()
|
mcp_agent/core/agent_types.py
CHANGED
@@ -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=
|
38
|
-
def ensure_default_request_params(self) ->
|
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
|
-
|
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
|
-
|
263
|
+
plan_iterations=plan_iterations,
|
264
264
|
),
|
265
265
|
)
|
266
266
|
|
mcp_agent/core/direct_factory.py
CHANGED
@@ -138,7 +138,7 @@ async def create_agents_by_type(
|
|
138
138
|
# Get common configuration
|
139
139
|
config = agent_data["config"]
|
140
140
|
|
141
|
-
|
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"
|
293
|
-
#
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
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
|
-
|
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":
|
mcp_agent/core/fastagent.py
CHANGED
@@ -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
|
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
|
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
|
298
|
-
if
|
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-
|
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
|
157
|
-
|
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":
|
mcp_agent/core/request_params.py
CHANGED
@@ -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
|
+
"""
|
mcp_agent/core/validation.py
CHANGED
@@ -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
|
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."""
|