fast-agent-mcp 0.0.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fast-agent-mcp might be problematic. Click here for more details.
- fast_agent_mcp-0.0.7.dist-info/METADATA +322 -0
- fast_agent_mcp-0.0.7.dist-info/RECORD +100 -0
- fast_agent_mcp-0.0.7.dist-info/WHEEL +4 -0
- fast_agent_mcp-0.0.7.dist-info/entry_points.txt +5 -0
- fast_agent_mcp-0.0.7.dist-info/licenses/LICENSE +201 -0
- mcp_agent/__init__.py +0 -0
- mcp_agent/agents/__init__.py +0 -0
- mcp_agent/agents/agent.py +277 -0
- mcp_agent/app.py +303 -0
- mcp_agent/cli/__init__.py +0 -0
- mcp_agent/cli/__main__.py +4 -0
- mcp_agent/cli/commands/bootstrap.py +221 -0
- mcp_agent/cli/commands/config.py +11 -0
- mcp_agent/cli/commands/setup.py +229 -0
- mcp_agent/cli/main.py +68 -0
- mcp_agent/cli/terminal.py +24 -0
- mcp_agent/config.py +334 -0
- mcp_agent/console.py +28 -0
- mcp_agent/context.py +251 -0
- mcp_agent/context_dependent.py +48 -0
- mcp_agent/core/fastagent.py +1013 -0
- mcp_agent/eval/__init__.py +0 -0
- mcp_agent/event_progress.py +88 -0
- mcp_agent/executor/__init__.py +0 -0
- mcp_agent/executor/decorator_registry.py +120 -0
- mcp_agent/executor/executor.py +293 -0
- mcp_agent/executor/task_registry.py +34 -0
- mcp_agent/executor/temporal.py +405 -0
- mcp_agent/executor/workflow.py +197 -0
- mcp_agent/executor/workflow_signal.py +325 -0
- mcp_agent/human_input/__init__.py +0 -0
- mcp_agent/human_input/handler.py +49 -0
- mcp_agent/human_input/types.py +58 -0
- mcp_agent/logging/__init__.py +0 -0
- mcp_agent/logging/events.py +123 -0
- mcp_agent/logging/json_serializer.py +163 -0
- mcp_agent/logging/listeners.py +216 -0
- mcp_agent/logging/logger.py +365 -0
- mcp_agent/logging/rich_progress.py +120 -0
- mcp_agent/logging/tracing.py +140 -0
- mcp_agent/logging/transport.py +461 -0
- mcp_agent/mcp/__init__.py +0 -0
- mcp_agent/mcp/gen_client.py +85 -0
- mcp_agent/mcp/mcp_activity.py +18 -0
- mcp_agent/mcp/mcp_agent_client_session.py +242 -0
- mcp_agent/mcp/mcp_agent_server.py +56 -0
- mcp_agent/mcp/mcp_aggregator.py +394 -0
- mcp_agent/mcp/mcp_connection_manager.py +330 -0
- mcp_agent/mcp/stdio.py +104 -0
- mcp_agent/mcp_server_registry.py +275 -0
- mcp_agent/progress_display.py +10 -0
- mcp_agent/resources/examples/decorator/main.py +26 -0
- mcp_agent/resources/examples/decorator/optimizer.py +78 -0
- mcp_agent/resources/examples/decorator/orchestrator.py +68 -0
- mcp_agent/resources/examples/decorator/parallel.py +81 -0
- mcp_agent/resources/examples/decorator/router.py +56 -0
- mcp_agent/resources/examples/decorator/tiny.py +22 -0
- mcp_agent/resources/examples/mcp_researcher/main-evalopt.py +53 -0
- mcp_agent/resources/examples/mcp_researcher/main.py +38 -0
- mcp_agent/telemetry/__init__.py +0 -0
- mcp_agent/telemetry/usage_tracking.py +18 -0
- mcp_agent/workflows/__init__.py +0 -0
- mcp_agent/workflows/embedding/__init__.py +0 -0
- mcp_agent/workflows/embedding/embedding_base.py +61 -0
- mcp_agent/workflows/embedding/embedding_cohere.py +49 -0
- mcp_agent/workflows/embedding/embedding_openai.py +46 -0
- mcp_agent/workflows/evaluator_optimizer/__init__.py +0 -0
- mcp_agent/workflows/evaluator_optimizer/evaluator_optimizer.py +359 -0
- mcp_agent/workflows/intent_classifier/__init__.py +0 -0
- mcp_agent/workflows/intent_classifier/intent_classifier_base.py +120 -0
- mcp_agent/workflows/intent_classifier/intent_classifier_embedding.py +134 -0
- mcp_agent/workflows/intent_classifier/intent_classifier_embedding_cohere.py +45 -0
- mcp_agent/workflows/intent_classifier/intent_classifier_embedding_openai.py +45 -0
- mcp_agent/workflows/intent_classifier/intent_classifier_llm.py +161 -0
- mcp_agent/workflows/intent_classifier/intent_classifier_llm_anthropic.py +60 -0
- mcp_agent/workflows/intent_classifier/intent_classifier_llm_openai.py +60 -0
- mcp_agent/workflows/llm/__init__.py +0 -0
- mcp_agent/workflows/llm/augmented_llm.py +645 -0
- mcp_agent/workflows/llm/augmented_llm_anthropic.py +539 -0
- mcp_agent/workflows/llm/augmented_llm_openai.py +615 -0
- mcp_agent/workflows/llm/llm_selector.py +345 -0
- mcp_agent/workflows/llm/model_factory.py +175 -0
- mcp_agent/workflows/orchestrator/__init__.py +0 -0
- mcp_agent/workflows/orchestrator/orchestrator.py +407 -0
- mcp_agent/workflows/orchestrator/orchestrator_models.py +154 -0
- mcp_agent/workflows/orchestrator/orchestrator_prompts.py +113 -0
- mcp_agent/workflows/parallel/__init__.py +0 -0
- mcp_agent/workflows/parallel/fan_in.py +350 -0
- mcp_agent/workflows/parallel/fan_out.py +187 -0
- mcp_agent/workflows/parallel/parallel_llm.py +141 -0
- mcp_agent/workflows/router/__init__.py +0 -0
- mcp_agent/workflows/router/router_base.py +276 -0
- mcp_agent/workflows/router/router_embedding.py +240 -0
- mcp_agent/workflows/router/router_embedding_cohere.py +59 -0
- mcp_agent/workflows/router/router_embedding_openai.py +59 -0
- mcp_agent/workflows/router/router_llm.py +301 -0
- mcp_agent/workflows/swarm/__init__.py +0 -0
- mcp_agent/workflows/swarm/swarm.py +320 -0
- mcp_agent/workflows/swarm/swarm_anthropic.py +42 -0
- mcp_agent/workflows/swarm/swarm_openai.py +41 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module defines a `ServerRegistry` class for managing MCP server configurations
|
|
3
|
+
and initialization logic.
|
|
4
|
+
|
|
5
|
+
The class loads server configurations from a YAML file,
|
|
6
|
+
supports dynamic registration of initialization hooks, and provides methods for
|
|
7
|
+
server initialization.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from contextlib import asynccontextmanager
|
|
11
|
+
from datetime import timedelta
|
|
12
|
+
from typing import Callable, Dict, AsyncGenerator
|
|
13
|
+
|
|
14
|
+
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
|
|
15
|
+
from mcp import ClientSession
|
|
16
|
+
from mcp.client.stdio import (
|
|
17
|
+
StdioServerParameters,
|
|
18
|
+
stdio_client,
|
|
19
|
+
get_default_environment,
|
|
20
|
+
)
|
|
21
|
+
from mcp.client.sse import sse_client
|
|
22
|
+
|
|
23
|
+
from mcp_agent.config import (
|
|
24
|
+
get_settings,
|
|
25
|
+
MCPServerAuthSettings,
|
|
26
|
+
MCPServerSettings,
|
|
27
|
+
Settings,
|
|
28
|
+
)
|
|
29
|
+
from mcp_agent.logging.logger import get_logger
|
|
30
|
+
from mcp_agent.mcp.mcp_connection_manager import MCPConnectionManager
|
|
31
|
+
|
|
32
|
+
logger = get_logger(__name__)
|
|
33
|
+
|
|
34
|
+
InitHookCallable = Callable[[ClientSession | None, MCPServerAuthSettings | None], bool]
|
|
35
|
+
"""
|
|
36
|
+
A type alias for an initialization hook function that is invoked after MCP server initialization.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
session (ClientSession | None): The client session for the server connection.
|
|
40
|
+
auth (MCPServerAuthSettings | None): The authentication configuration for the server.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
bool: Result of the post-init hook (false indicates failure).
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ServerRegistry:
|
|
48
|
+
"""
|
|
49
|
+
A registry for managing server configurations and initialization logic.
|
|
50
|
+
|
|
51
|
+
The `ServerRegistry` class is responsible for loading server configurations
|
|
52
|
+
from a YAML file, registering initialization hooks, initializing servers,
|
|
53
|
+
and executing post-initialization hooks dynamically.
|
|
54
|
+
|
|
55
|
+
Attributes:
|
|
56
|
+
config_path (str): Path to the YAML configuration file.
|
|
57
|
+
registry (Dict[str, MCPServerSettings]): Loaded server configurations.
|
|
58
|
+
init_hooks (Dict[str, InitHookCallable]): Registered initialization hooks.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def __init__(self, config: Settings | None = None, config_path: str | None = None):
|
|
62
|
+
"""
|
|
63
|
+
Initialize the ServerRegistry with a configuration file.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
config (Settings): The Settings object containing the server configurations.
|
|
67
|
+
config_path (str): Path to the YAML configuration file.
|
|
68
|
+
"""
|
|
69
|
+
self.registry = (
|
|
70
|
+
self.load_registry_from_file(config_path)
|
|
71
|
+
if config is None
|
|
72
|
+
else config.mcp.servers
|
|
73
|
+
)
|
|
74
|
+
self.init_hooks: Dict[str, InitHookCallable] = {}
|
|
75
|
+
self.connection_manager = MCPConnectionManager(self)
|
|
76
|
+
|
|
77
|
+
def load_registry_from_file(
|
|
78
|
+
self, config_path: str | None = None
|
|
79
|
+
) -> Dict[str, MCPServerSettings]:
|
|
80
|
+
"""
|
|
81
|
+
Load the YAML configuration file and validate it.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Dict[str, MCPServerSettings]: A dictionary of server configurations.
|
|
85
|
+
|
|
86
|
+
Raises:
|
|
87
|
+
ValueError: If the configuration is invalid.
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
servers = get_settings(config_path).mcp.servers or {}
|
|
91
|
+
return servers
|
|
92
|
+
|
|
93
|
+
@asynccontextmanager
|
|
94
|
+
async def start_server(
|
|
95
|
+
self,
|
|
96
|
+
server_name: str,
|
|
97
|
+
client_session_factory: Callable[
|
|
98
|
+
[MemoryObjectReceiveStream, MemoryObjectSendStream, timedelta | None],
|
|
99
|
+
ClientSession,
|
|
100
|
+
] = ClientSession,
|
|
101
|
+
) -> AsyncGenerator[ClientSession, None]:
|
|
102
|
+
"""
|
|
103
|
+
Starts the server process based on its configuration. To initialize, call initialize_server
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
server_name (str): The name of the server to initialize.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
StdioServerParameters: The server parameters for stdio transport.
|
|
110
|
+
|
|
111
|
+
Raises:
|
|
112
|
+
ValueError: If the server is not found or has an unsupported transport.
|
|
113
|
+
"""
|
|
114
|
+
if server_name not in self.registry:
|
|
115
|
+
raise ValueError(f"Server '{server_name}' not found in registry.")
|
|
116
|
+
|
|
117
|
+
config = self.registry[server_name]
|
|
118
|
+
|
|
119
|
+
read_timeout_seconds = (
|
|
120
|
+
timedelta(config.read_timeout_seconds)
|
|
121
|
+
if config.read_timeout_seconds
|
|
122
|
+
else None
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
if config.transport == "stdio":
|
|
126
|
+
if not config.command or not config.args:
|
|
127
|
+
raise ValueError(
|
|
128
|
+
f"Command and args are required for stdio transport: {server_name}"
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
server_params = StdioServerParameters(
|
|
132
|
+
command=config.command,
|
|
133
|
+
args=config.args,
|
|
134
|
+
env={**get_default_environment(), **(config.env or {})},
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
async with stdio_client(server_params) as (read_stream, write_stream):
|
|
138
|
+
session = client_session_factory(
|
|
139
|
+
read_stream,
|
|
140
|
+
write_stream,
|
|
141
|
+
read_timeout_seconds,
|
|
142
|
+
)
|
|
143
|
+
async with session:
|
|
144
|
+
logger.info(
|
|
145
|
+
f"{server_name}: Connected to server using stdio transport."
|
|
146
|
+
)
|
|
147
|
+
try:
|
|
148
|
+
yield session
|
|
149
|
+
finally:
|
|
150
|
+
logger.debug(f"{server_name}: Closed session to server")
|
|
151
|
+
|
|
152
|
+
elif config.transport == "sse":
|
|
153
|
+
if not config.url:
|
|
154
|
+
raise ValueError(f"URL is required for SSE transport: {server_name}")
|
|
155
|
+
|
|
156
|
+
# Use sse_client to get the read and write streams
|
|
157
|
+
async with sse_client(config.url) as (read_stream, write_stream):
|
|
158
|
+
session = client_session_factory(
|
|
159
|
+
read_stream,
|
|
160
|
+
write_stream,
|
|
161
|
+
read_timeout_seconds,
|
|
162
|
+
)
|
|
163
|
+
async with session:
|
|
164
|
+
logger.info(
|
|
165
|
+
f"{server_name}: Connected to server using SSE transport."
|
|
166
|
+
)
|
|
167
|
+
try:
|
|
168
|
+
yield session
|
|
169
|
+
finally:
|
|
170
|
+
logger.debug(f"{server_name}: Closed session to server")
|
|
171
|
+
|
|
172
|
+
# Unsupported transport
|
|
173
|
+
else:
|
|
174
|
+
raise ValueError(f"Unsupported transport: {config.transport}")
|
|
175
|
+
|
|
176
|
+
@asynccontextmanager
|
|
177
|
+
async def initialize_server(
|
|
178
|
+
self,
|
|
179
|
+
server_name: str,
|
|
180
|
+
client_session_factory: Callable[
|
|
181
|
+
[MemoryObjectReceiveStream, MemoryObjectSendStream, timedelta | None],
|
|
182
|
+
ClientSession,
|
|
183
|
+
] = ClientSession,
|
|
184
|
+
init_hook: InitHookCallable = None,
|
|
185
|
+
) -> AsyncGenerator[ClientSession, None]:
|
|
186
|
+
"""
|
|
187
|
+
Initialize a server based on its configuration.
|
|
188
|
+
After initialization, also calls any registered or provided initialization hook for the server.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
server_name (str): The name of the server to initialize.
|
|
192
|
+
init_hook (InitHookCallable): Optional initialization hook function to call after initialization.
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
StdioServerParameters: The server parameters for stdio transport.
|
|
196
|
+
|
|
197
|
+
Raises:
|
|
198
|
+
ValueError: If the server is not found or has an unsupported transport.
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
if server_name not in self.registry:
|
|
202
|
+
raise ValueError(f"Server '{server_name}' not found in registry.")
|
|
203
|
+
|
|
204
|
+
config = self.registry[server_name]
|
|
205
|
+
|
|
206
|
+
async with self.start_server(
|
|
207
|
+
server_name, client_session_factory=client_session_factory
|
|
208
|
+
) as session:
|
|
209
|
+
try:
|
|
210
|
+
logger.info(f"{server_name}: Initializing server...")
|
|
211
|
+
await session.initialize()
|
|
212
|
+
logger.info(f"{server_name}: Initialized.")
|
|
213
|
+
|
|
214
|
+
intialization_callback = (
|
|
215
|
+
init_hook
|
|
216
|
+
if init_hook is not None
|
|
217
|
+
else self.init_hooks.get(server_name)
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
if intialization_callback:
|
|
221
|
+
logger.info(f"{server_name}: Executing init hook")
|
|
222
|
+
intialization_callback(session, config.auth)
|
|
223
|
+
|
|
224
|
+
logger.info(f"{server_name}: Up and running!")
|
|
225
|
+
yield session
|
|
226
|
+
finally:
|
|
227
|
+
logger.info(f"{server_name}: Ending server session.")
|
|
228
|
+
|
|
229
|
+
def register_init_hook(self, server_name: str, hook: InitHookCallable) -> None:
|
|
230
|
+
"""
|
|
231
|
+
Register an initialization hook for a specific server. This will get called
|
|
232
|
+
after the server is initialized.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
server_name (str): The name of the server.
|
|
236
|
+
hook (callable): The initialization function to register.
|
|
237
|
+
"""
|
|
238
|
+
if server_name not in self.registry:
|
|
239
|
+
raise ValueError(f"Server '{server_name}' not found in registry.")
|
|
240
|
+
|
|
241
|
+
self.init_hooks[server_name] = hook
|
|
242
|
+
|
|
243
|
+
def execute_init_hook(self, server_name: str, session=None) -> bool:
|
|
244
|
+
"""
|
|
245
|
+
Execute the initialization hook for a specific server.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
server_name (str): The name of the server.
|
|
249
|
+
session: The session object to pass to the initialization hook.
|
|
250
|
+
"""
|
|
251
|
+
if server_name in self.init_hooks:
|
|
252
|
+
hook = self.init_hooks[server_name]
|
|
253
|
+
config = self.registry[server_name]
|
|
254
|
+
logger.info(f"Executing init hook for '{server_name}'")
|
|
255
|
+
return hook(session, config.auth)
|
|
256
|
+
else:
|
|
257
|
+
logger.info(f"No init hook registered for '{server_name}'")
|
|
258
|
+
|
|
259
|
+
def get_server_config(self, server_name: str) -> MCPServerSettings | None:
|
|
260
|
+
"""
|
|
261
|
+
Get the configuration for a specific server.
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
server_name (str): The name of the server.
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
MCPServerSettings: The server configuration.
|
|
268
|
+
"""
|
|
269
|
+
server_config = self.registry.get(server_name)
|
|
270
|
+
if server_config is None:
|
|
271
|
+
logger.warning(f"Server '{server_name}' not found in registry.")
|
|
272
|
+
return None
|
|
273
|
+
elif server_config.name is None:
|
|
274
|
+
server_config.name = server_name
|
|
275
|
+
return server_config
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Centralized progress display configuration for MCP Agent.
|
|
3
|
+
Provides a shared progress display instance for consistent progress handling.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from mcp_agent.console import console
|
|
7
|
+
from mcp_agent.logging.rich_progress import RichProgressDisplay
|
|
8
|
+
|
|
9
|
+
# Main progress display instance - shared across the application
|
|
10
|
+
progress_display = RichProgressDisplay(console)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example MCP Agent application showing simplified agent access.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from mcp_agent.core.fastagent import FastAgent
|
|
7
|
+
|
|
8
|
+
# Create the application
|
|
9
|
+
agent_app = FastAgent("Interactive Agent Example")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Define the agent
|
|
13
|
+
@agent_app.agent(
|
|
14
|
+
instruction="A simple agent that helps with basic tasks. Request Human Input when needed.",
|
|
15
|
+
servers=["mcp_root"],
|
|
16
|
+
# model="gpt-4o", model override here takes precedence
|
|
17
|
+
)
|
|
18
|
+
async def main():
|
|
19
|
+
# use the --model= command line switch to specify model
|
|
20
|
+
async with agent_app.run() as agent:
|
|
21
|
+
await agent("print the next number in the sequence")
|
|
22
|
+
await agent.prompt(default="STOP")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
if __name__ == "__main__":
|
|
26
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example showing how to use the evaluator-optimizer functionality with the decorator API.
|
|
3
|
+
This demonstrates creating an optimizer and evaluator to iteratively improve content.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import asyncio
|
|
7
|
+
from mcp_agent.core.fastagent import FastAgent
|
|
8
|
+
|
|
9
|
+
# Create the application
|
|
10
|
+
agent_app = FastAgent("Cover Letter Writer")
|
|
11
|
+
agent_app.app._human_input_callback = None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# Define optimizer agent
|
|
15
|
+
@agent_app.agent(
|
|
16
|
+
name="optimizer",
|
|
17
|
+
instruction="""You are a career coach specializing in cover letter writing.
|
|
18
|
+
You are tasked with generating a compelling cover letter given the job posting,
|
|
19
|
+
candidate details, and company information. Tailor the response to the company and job requirements.
|
|
20
|
+
""",
|
|
21
|
+
servers=["fetch"],
|
|
22
|
+
model="gpt-4o-mini", # Using a capable model for content generation
|
|
23
|
+
)
|
|
24
|
+
# Define evaluator agent
|
|
25
|
+
@agent_app.agent(
|
|
26
|
+
name="evaluator",
|
|
27
|
+
instruction="""Evaluate the following response based on the criteria below:
|
|
28
|
+
1. Clarity: Is the language clear, concise, and grammatically correct?
|
|
29
|
+
2. Specificity: Does the response include relevant and concrete details tailored to the job description?
|
|
30
|
+
3. Relevance: Does the response align with the prompt and avoid unnecessary information?
|
|
31
|
+
4. Tone and Style: Is the tone professional and appropriate for the context?
|
|
32
|
+
5. Persuasiveness: Does the response effectively highlight the candidate's value?
|
|
33
|
+
6. Grammar and Mechanics: Are there any spelling or grammatical issues?
|
|
34
|
+
7. Feedback Alignment: Has the response addressed feedback from previous iterations?
|
|
35
|
+
|
|
36
|
+
For each criterion:
|
|
37
|
+
- Provide a rating (EXCELLENT, GOOD, FAIR, or POOR).
|
|
38
|
+
- Offer specific feedback or suggestions for improvement.
|
|
39
|
+
|
|
40
|
+
Summarize your evaluation as a structured response with:
|
|
41
|
+
- Overall quality rating.
|
|
42
|
+
- Specific feedback and areas for improvement.""",
|
|
43
|
+
servers=[], # Evaluator doesn't need special server access
|
|
44
|
+
model="sonnet", # Using a capable model for evaluation
|
|
45
|
+
)
|
|
46
|
+
# Define the evaluator-optimizer workflow
|
|
47
|
+
@agent_app.evaluator_optimizer(
|
|
48
|
+
name="cover_letter_writer",
|
|
49
|
+
optimizer="optimizer", # Reference to optimizer agent
|
|
50
|
+
evaluator="evaluator", # Reference to evaluator agent
|
|
51
|
+
min_rating="EXCELLENT", # Strive for excellence
|
|
52
|
+
max_refinements=3, # Maximum iterations
|
|
53
|
+
)
|
|
54
|
+
async def main():
|
|
55
|
+
async with agent_app.run() as agent:
|
|
56
|
+
job_posting = (
|
|
57
|
+
"Software Engineer at LastMile AI. Responsibilities include developing AI systems, "
|
|
58
|
+
"collaborating with cross-functional teams, and enhancing scalability. Skills required: "
|
|
59
|
+
"Python, distributed systems, and machine learning."
|
|
60
|
+
)
|
|
61
|
+
candidate_details = (
|
|
62
|
+
"Alex Johnson, 3 years in machine learning, contributor to open-source AI projects, "
|
|
63
|
+
"proficient in Python and TensorFlow. Motivated by building scalable AI systems to solve real-world problems."
|
|
64
|
+
)
|
|
65
|
+
company_information = (
|
|
66
|
+
"Look up from the LastMile AI About page: https://lastmileai.dev/about"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Send the task
|
|
70
|
+
await agent.cover_letter_writer.send(
|
|
71
|
+
f"Write a cover letter for the following job posting: {job_posting}\n\n"
|
|
72
|
+
f"Candidate Details: {candidate_details}\n\n"
|
|
73
|
+
f"Company information: {company_information}",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example showing how to use the orchestrator functionality with the decorator API.
|
|
3
|
+
This demonstrates creating multiple agents and an orchestrator to coordinate them.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import asyncio
|
|
7
|
+
from mcp_agent.core.fastagent import FastAgent
|
|
8
|
+
|
|
9
|
+
# Create the application
|
|
10
|
+
agent_app = FastAgent("Orchestrator Example")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Define worker agents
|
|
14
|
+
@agent_app.agent(
|
|
15
|
+
name="finder",
|
|
16
|
+
instruction="""You are an agent with access to the filesystem,
|
|
17
|
+
as well as the ability to fetch URLs. Your job is to identify
|
|
18
|
+
the closest match to a user's request, make the appropriate tool calls,
|
|
19
|
+
and return the URI and CONTENTS of the closest match.""",
|
|
20
|
+
servers=["fetch", "filesystem"],
|
|
21
|
+
model="gpt-4o-mini",
|
|
22
|
+
)
|
|
23
|
+
@agent_app.agent(
|
|
24
|
+
name="writer",
|
|
25
|
+
instruction="""You are an agent that can write to the filesystem.
|
|
26
|
+
You are tasked with taking the user's input, addressing it, and
|
|
27
|
+
writing the result to disk in the appropriate location.""",
|
|
28
|
+
servers=["filesystem"],
|
|
29
|
+
model="gpt-4o",
|
|
30
|
+
)
|
|
31
|
+
@agent_app.agent(
|
|
32
|
+
name="proofreader",
|
|
33
|
+
instruction=""""Review the short story for grammar, spelling, and punctuation errors.
|
|
34
|
+
Identify any awkward phrasing or structural issues that could improve clarity.
|
|
35
|
+
Provide detailed feedback on corrections.""",
|
|
36
|
+
servers=["fetch"],
|
|
37
|
+
model="gpt-4o",
|
|
38
|
+
)
|
|
39
|
+
# Define the orchestrator to coordinate the other agents
|
|
40
|
+
@agent_app.orchestrator(
|
|
41
|
+
name="document_processor",
|
|
42
|
+
instruction="""Load the student's short story from short_story.md,
|
|
43
|
+
and generate a report with feedback across proofreading,
|
|
44
|
+
factuality/logical consistency and style adherence. Use the style rules from
|
|
45
|
+
https://apastyle.apa.org/learn/quick-guide-on-formatting and
|
|
46
|
+
https://apastyle.apa.org/learn/quick-guide-on-references.
|
|
47
|
+
Write the graded report to graded_report.md in the same directory as short_story.md""",
|
|
48
|
+
agents=["finder", "writer", "proofreader"],
|
|
49
|
+
model="sonnet", # Orchestrators typically need more capable models
|
|
50
|
+
)
|
|
51
|
+
async def main():
|
|
52
|
+
async with agent_app.run() as agent:
|
|
53
|
+
# The orchestrator can be used just like any other agent
|
|
54
|
+
task = (
|
|
55
|
+
"""Load the student's short story from short_story.md,
|
|
56
|
+
and generate a report with feedback across proofreading,
|
|
57
|
+
factuality/logical consistency and style adherence. Use the style rules from
|
|
58
|
+
https://apastyle.apa.org/learn/quick-guide-on-formatting and
|
|
59
|
+
https://apastyle.apa.org/learn/quick-guide-on-references.
|
|
60
|
+
Write the graded report to graded_report.md in the same directory as short_story.md""",
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Send the task
|
|
64
|
+
await agent.send("document_processor", task)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
if __name__ == "__main__":
|
|
68
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example MCP Agent application showing simplified agent access.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from mcp_agent.core.fastagent import FastAgent
|
|
7
|
+
|
|
8
|
+
# Create the application
|
|
9
|
+
app = FastAgent(
|
|
10
|
+
"Parallel Workflow Example",
|
|
11
|
+
# config={
|
|
12
|
+
# "human_input_handler": None # Disable human input handling
|
|
13
|
+
# },
|
|
14
|
+
)
|
|
15
|
+
app.app._human_input_callback = None
|
|
16
|
+
SHORT_STORY = """
|
|
17
|
+
The Battle of Glimmerwood
|
|
18
|
+
|
|
19
|
+
In the heart of Glimmerwood, a mystical forest knowed for its radiant trees, a small village thrived.
|
|
20
|
+
The villagers, who were live peacefully, shared their home with the forest's magical creatures,
|
|
21
|
+
especially the Glimmerfoxes whose fur shimmer like moonlight.
|
|
22
|
+
|
|
23
|
+
One fateful evening, the peace was shaterred when the infamous Dark Marauders attack.
|
|
24
|
+
Lead by the cunning Captain Thorn, the bandits aim to steal the precious Glimmerstones which was believed to grant immortality.
|
|
25
|
+
|
|
26
|
+
Amidst the choas, a young girl named Elara stood her ground, she rallied the villagers and devised a clever plan.
|
|
27
|
+
Using the forests natural defenses they lured the marauders into a trap.
|
|
28
|
+
As the bandits aproached the village square, a herd of Glimmerfoxes emerged, blinding them with their dazzling light,
|
|
29
|
+
the villagers seized the opportunity to captured the invaders.
|
|
30
|
+
|
|
31
|
+
Elara's bravery was celebrated and she was hailed as the "Guardian of Glimmerwood".
|
|
32
|
+
The Glimmerstones were secured in a hidden grove protected by an ancient spell.
|
|
33
|
+
|
|
34
|
+
However, not all was as it seemed. The Glimmerstones true power was never confirm,
|
|
35
|
+
and whispers of a hidden agenda linger among the villagers.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@app.agent(
|
|
40
|
+
name="proofreader",
|
|
41
|
+
instruction=""""Review the short story for grammar, spelling, and punctuation errors.
|
|
42
|
+
Identify any awkward phrasing or structural issues that could improve clarity.
|
|
43
|
+
Provide detailed feedback on corrections.""",
|
|
44
|
+
)
|
|
45
|
+
@app.agent(
|
|
46
|
+
name="fact_checker",
|
|
47
|
+
model="gpt-4o",
|
|
48
|
+
instruction="""Verify the factual consistency within the story. Identify any contradictions,
|
|
49
|
+
logical inconsistencies, or inaccuracies in the plot, character actions, or setting.
|
|
50
|
+
Highlight potential issues with reasoning or coherence.""",
|
|
51
|
+
)
|
|
52
|
+
@app.agent(
|
|
53
|
+
name="style_enforcer",
|
|
54
|
+
model="sonnet",
|
|
55
|
+
instruction="""Analyze the story for adherence to style guidelines.
|
|
56
|
+
Evaluate the narrative flow, clarity of expression, and tone. Suggest improvements to
|
|
57
|
+
enhance storytelling, readability, and engagement.""",
|
|
58
|
+
)
|
|
59
|
+
@app.agent(
|
|
60
|
+
name="grader",
|
|
61
|
+
model="o3-mini.high",
|
|
62
|
+
instruction="""Compile the feedback from the Proofreader, Fact Checker, and Style Enforcer
|
|
63
|
+
into a structured report. Summarize key issues and categorize them by type.
|
|
64
|
+
Provide actionable recommendations for improving the story,
|
|
65
|
+
and give an overall grade based on the feedback.""",
|
|
66
|
+
)
|
|
67
|
+
@app.parallel(
|
|
68
|
+
fan_out=["proofreader", "fact_checker", "style_enforcer"],
|
|
69
|
+
fan_in="grader",
|
|
70
|
+
name="parallel",
|
|
71
|
+
)
|
|
72
|
+
async def main():
|
|
73
|
+
# Use the app's context manager
|
|
74
|
+
async with app.run() as agent:
|
|
75
|
+
await agent.send("parallel", f"student short story submission: {SHORT_STORY}")
|
|
76
|
+
# follow-on prompt to task agent
|
|
77
|
+
# await agent.prompt("style_enforcer", default="STOP")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
if __name__ == "__main__":
|
|
81
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example MCP Agent application showing router workflow with decorator syntax.
|
|
3
|
+
Demonstrates router's ability to either:
|
|
4
|
+
1. Use tools directly to handle requests
|
|
5
|
+
2. Delegate requests to specialized agents
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
from mcp_agent.core.fastagent import FastAgent
|
|
10
|
+
|
|
11
|
+
# Create the application
|
|
12
|
+
agent_app = FastAgent(
|
|
13
|
+
"Router Workflow Example",
|
|
14
|
+
)
|
|
15
|
+
agent_app.app._human_input_callback = None
|
|
16
|
+
|
|
17
|
+
# Sample requests demonstrating direct tool use vs agent delegation
|
|
18
|
+
SAMPLE_REQUESTS = [
|
|
19
|
+
"Download and summarize https://llmindset.co.uk/posts/2024/12/mcp-build-notes/", # Router handles directly with fetch
|
|
20
|
+
"Analyze the quality of the Python codebase in the current working directory", # Delegated to code expert
|
|
21
|
+
"What are the key principles of effective beekeeping?", # Delegated to general assistant
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@agent_app.agent(
|
|
26
|
+
name="fetcher",
|
|
27
|
+
instruction="""You are an agent, with a tool enabling you to fetch URLs.""",
|
|
28
|
+
servers=["fetch"],
|
|
29
|
+
model="haiku",
|
|
30
|
+
)
|
|
31
|
+
@agent_app.agent(
|
|
32
|
+
name="code_expert",
|
|
33
|
+
instruction="""You are an expert in code analysis and software engineering.
|
|
34
|
+
When asked about code, architecture, or development practices,
|
|
35
|
+
you provide thorough and practical insights.""",
|
|
36
|
+
servers=["filesystem"],
|
|
37
|
+
model="gpt-4o",
|
|
38
|
+
)
|
|
39
|
+
@agent_app.agent(
|
|
40
|
+
name="general_assistant",
|
|
41
|
+
instruction="""You are a knowledgeable assistant that provides clear,
|
|
42
|
+
well-reasoned responses about general topics, concepts, and principles.""",
|
|
43
|
+
)
|
|
44
|
+
@agent_app.router(
|
|
45
|
+
name="llm_router",
|
|
46
|
+
model="sonnet",
|
|
47
|
+
agents=["code_expert", "general_assistant", "fetcher"],
|
|
48
|
+
)
|
|
49
|
+
async def main():
|
|
50
|
+
async with agent_app.run() as agent:
|
|
51
|
+
for request in SAMPLE_REQUESTS:
|
|
52
|
+
await agent.send("llm_router", request)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
if __name__ == "__main__":
|
|
56
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Example MCP Agent application showing simplified agent access.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from mcp_agent.core.fastagent import FastAgent
|
|
7
|
+
|
|
8
|
+
# Create the application
|
|
9
|
+
agent_app = FastAgent("Interactive Agent Example")
|
|
10
|
+
# agent_app.app._human_input_callback = None
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Define the agent
|
|
14
|
+
@agent_app.agent()
|
|
15
|
+
async def main():
|
|
16
|
+
# use the --model= command line switch to specify model
|
|
17
|
+
async with agent_app.run() as agent:
|
|
18
|
+
await agent()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
if __name__ == "__main__":
|
|
22
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
from mcp_agent.core.fastagent import FastAgent
|
|
4
|
+
|
|
5
|
+
agents = FastAgent(name="Researcher")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@agents.agent(
|
|
9
|
+
name="Researcher",
|
|
10
|
+
instruction="""
|
|
11
|
+
You are a research assistant, with access to internet search (via Brave),
|
|
12
|
+
website fetch, a python interpreter (you can install packages with uv) and a filesystem.
|
|
13
|
+
Use the current working directory to save and create files with both the Interpreter and Filesystem tools.
|
|
14
|
+
The interpreter has numpy, pandas, matplotlib and seaborn already installed.
|
|
15
|
+
|
|
16
|
+
You must always provide a summary of the specific sources you have used in your research.
|
|
17
|
+
""",
|
|
18
|
+
servers=["brave", "interpreter", "filesystem", "fetch"],
|
|
19
|
+
)
|
|
20
|
+
@agents.agent(
|
|
21
|
+
name="Evaluator",
|
|
22
|
+
model="sonnet",
|
|
23
|
+
instruction="""
|
|
24
|
+
Evaluate the response from the researcher based on the criteria:
|
|
25
|
+
- Sources cited. Has the researcher provided a summary of the specific sources used in the research?
|
|
26
|
+
- Validity. Has the researcher cross-checked and validated data and assumptions.
|
|
27
|
+
- Alignment. Has the researher acted and addressed feedback from any previous assessments?
|
|
28
|
+
|
|
29
|
+
For each criterion:
|
|
30
|
+
- Provide a rating (EXCELLENT, GOOD, FAIR, or POOR).
|
|
31
|
+
- Offer specific feedback or suggestions for improvement.
|
|
32
|
+
|
|
33
|
+
Summarize your evaluation as a structured response with:
|
|
34
|
+
- Overall quality rating.
|
|
35
|
+
- Specific feedback and areas for improvement.""",
|
|
36
|
+
)
|
|
37
|
+
@agents.evaluator_optimizer(
|
|
38
|
+
optimizer="Researcher",
|
|
39
|
+
evaluator="Evaluator",
|
|
40
|
+
max_refinements=5,
|
|
41
|
+
min_rating="EXCELLENT",
|
|
42
|
+
name="Researcher_Evaluator",
|
|
43
|
+
)
|
|
44
|
+
async def main():
|
|
45
|
+
async with agents.run() as agent:
|
|
46
|
+
await agent.prompt("Researcher_Evaluator")
|
|
47
|
+
|
|
48
|
+
print("Ask follow up quesions to the Researcher?")
|
|
49
|
+
await agent.prompt("Researcher", default="STOP")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
if __name__ == "__main__":
|
|
53
|
+
asyncio.run(main())
|