fast-agent-mcp 0.0.12__py3-none-any.whl → 0.0.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fast-agent-mcp might be problematic. Click here for more details.
- {fast_agent_mcp-0.0.12.dist-info → fast_agent_mcp-0.0.13.dist-info}/METADATA +7 -1
- {fast_agent_mcp-0.0.12.dist-info → fast_agent_mcp-0.0.13.dist-info}/RECORD +21 -23
- mcp_agent/agents/agent.py +48 -8
- mcp_agent/cli/commands/bootstrap.py +1 -0
- mcp_agent/cli/commands/setup.py +1 -1
- mcp_agent/cli/main.py +3 -3
- mcp_agent/core/enhanced_prompt.py +59 -16
- mcp_agent/core/exceptions.py +17 -0
- mcp_agent/core/fastagent.py +43 -7
- mcp_agent/human_input/handler.py +43 -18
- mcp_agent/mcp/mcp_connection_manager.py +14 -12
- mcp_agent/resources/examples/workflows/chaining.py +5 -1
- mcp_agent/resources/examples/workflows/evaluator.py +2 -2
- mcp_agent/resources/examples/workflows/fastagent.config.yaml +24 -0
- mcp_agent/resources/examples/workflows/orchestrator.py +3 -2
- mcp_agent/resources/examples/workflows/parallel.py +2 -1
- mcp_agent/workflows/llm/augmented_llm.py +3 -0
- mcp_agent/workflows/llm/model_factory.py +7 -4
- mcp_agent/resources/examples/mcp_researcher/researcher.py +0 -38
- mcp_agent/resources/examples/workflows/agent.py +0 -17
- mcp_agent/resources/examples/workflows/fastagent.py +0 -22
- {fast_agent_mcp-0.0.12.dist-info → fast_agent_mcp-0.0.13.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.0.12.dist-info → fast_agent_mcp-0.0.13.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.0.12.dist-info → fast_agent_mcp-0.0.13.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fast-agent-mcp
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.13
|
|
4
4
|
Summary: Define, Prompt and Test MCP enabled Agents and Workflows
|
|
5
5
|
Author-email: Shaun Smith <fastagent@llmindset.co.uk>, Sarmad Qadri <sarmad@lastmileai.dev>
|
|
6
6
|
License: Apache License
|
|
@@ -269,6 +269,12 @@ Other bootstrap examples include a Researcher (with Evaluator-Optimizer workflow
|
|
|
269
269
|
|
|
270
270
|
> Windows Users - there are a couple of configuration changes needed for the Filesystem and Docker MCP Servers - necessary changes are detailed within the configuration files.
|
|
271
271
|
|
|
272
|
+
## Agent Development
|
|
273
|
+
|
|
274
|
+
FastAgent lets you interact with Agents during a workflow, enabling "warm-up" and diagnostic prompting to improve behaviour and refine prompts.
|
|
275
|
+
|
|
276
|
+
## MCP Server Development
|
|
277
|
+
|
|
272
278
|
### llmindset.co.uk fork:
|
|
273
279
|
|
|
274
280
|
- "FastAgent" style prototyping, with per-agent models
|
|
@@ -8,18 +8,18 @@ mcp_agent/event_progress.py,sha256=25iz0yyg-O4glMmtijcYpDdUmtUIKsCmR_8A52GgeC4,2
|
|
|
8
8
|
mcp_agent/mcp_server_registry.py,sha256=5x30L1IlmC18JASl7NQbZYHMqPWS3ay0f_3U3uleaMM,9884
|
|
9
9
|
mcp_agent/progress_display.py,sha256=GeJU9VUt6qKsFVymG688hCMVCsAygG9ifiiEb5IcbN4,361
|
|
10
10
|
mcp_agent/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
-
mcp_agent/agents/agent.py,sha256=
|
|
11
|
+
mcp_agent/agents/agent.py,sha256=losanPSdZXZzmeiX-J6ctOinLlkhNZsxwi3Swr8lnxA,11482
|
|
12
12
|
mcp_agent/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
mcp_agent/cli/__main__.py,sha256=bhxe66GYqy0q78OQhi7dkuubY1Tn0bQL6hU5Nn47E34,73
|
|
14
|
-
mcp_agent/cli/main.py,sha256=
|
|
14
|
+
mcp_agent/cli/main.py,sha256=cqRxYTpeZ656lzf9qLR3LPnQXrFVDxlWm5gRuqyzUQg,2456
|
|
15
15
|
mcp_agent/cli/terminal.py,sha256=5fqrKlJvIpKEuvpvZ653OueQSYFFktBEbosjr2ucMUc,1026
|
|
16
|
-
mcp_agent/cli/commands/bootstrap.py,sha256=
|
|
16
|
+
mcp_agent/cli/commands/bootstrap.py,sha256=N7pGP7IWjL5HI99glk4BXzaAYwHGkJjQcDTBMOmDAGQ,10779
|
|
17
17
|
mcp_agent/cli/commands/config.py,sha256=32YTS5jmsYAs9QzAhjkG70_daAHqOemf4XbZBBSMz6g,204
|
|
18
|
-
mcp_agent/cli/commands/setup.py,sha256=
|
|
18
|
+
mcp_agent/cli/commands/setup.py,sha256=8ofxUAF2nUSu1IarDZSAsTt6_6PoEht3TGbz9N6WSbs,6239
|
|
19
19
|
mcp_agent/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
-
mcp_agent/core/enhanced_prompt.py,sha256=
|
|
21
|
-
mcp_agent/core/exceptions.py,sha256=
|
|
22
|
-
mcp_agent/core/fastagent.py,sha256=
|
|
20
|
+
mcp_agent/core/enhanced_prompt.py,sha256=0V5q0xcCk8PBwtc0p62B8JJ1VvqxN_wuJiXC2QPqv1M,12750
|
|
21
|
+
mcp_agent/core/exceptions.py,sha256=pcI-JNn5Eql4weIKsDYnIwhPyzkc7CBJBJHOUT0w6kQ,2005
|
|
22
|
+
mcp_agent/core/fastagent.py,sha256=_9PRnxZZJWgLxvcjF-KnibsxqYs2lVs-BD_gj9o_Neo,49733
|
|
23
23
|
mcp_agent/core/server_validation.py,sha256=_59cn16nNT4HGPwg19HgxMtHK4MsdWYDUw_CuL-5xek,1696
|
|
24
24
|
mcp_agent/eval/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
25
|
mcp_agent/executor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -30,7 +30,7 @@ mcp_agent/executor/temporal.py,sha256=U-wyltgWlVmzJoyivT6rR0Z1U3S6TbMXpeCxyuXako
|
|
|
30
30
|
mcp_agent/executor/workflow.py,sha256=lA6r7PNEvxCVFHp4XkEJkaR0QCTf-J6iw9JwNx-tzNY,6727
|
|
31
31
|
mcp_agent/executor/workflow_signal.py,sha256=3PWwSgXhz3PhkA8SRX3u0BDVoSlQqRGqC9d1qLC25vE,11210
|
|
32
32
|
mcp_agent/human_input/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
|
-
mcp_agent/human_input/handler.py,sha256=
|
|
33
|
+
mcp_agent/human_input/handler.py,sha256=37ERNg9OXaFgm4Ew-6aaU4y2jKre4VpekSnYZtRYFFI,2904
|
|
34
34
|
mcp_agent/human_input/types.py,sha256=ZvuDHvI0-wO2tFoS0bzrv8U5B83zYdxAG7g9G9jCxug,1489
|
|
35
35
|
mcp_agent/logging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
36
|
mcp_agent/logging/events.py,sha256=qfYJnrqgXdujV-nl-iOwBEBh6HMraowBI4zeAWPPU4A,3461
|
|
@@ -46,7 +46,7 @@ mcp_agent/mcp/mcp_activity.py,sha256=CajXCFWZ2cKEX9s4-HfNVAj471ePTVs4NOkvmIh65tE
|
|
|
46
46
|
mcp_agent/mcp/mcp_agent_client_session.py,sha256=NtWcQhjmnnaR3yYcYj2d2lh-m563NexZUa57K1tAjeM,9477
|
|
47
47
|
mcp_agent/mcp/mcp_agent_server.py,sha256=xP09HZTeguJi4Fq0p3fjLBP55uSYe5AdqM90xCgn9Ho,1639
|
|
48
48
|
mcp_agent/mcp/mcp_aggregator.py,sha256=RVsgNnSJ1IPBkqKgF_Gp-Cpv97FVBIdppPey6FRoHB0,14751
|
|
49
|
-
mcp_agent/mcp/mcp_connection_manager.py,sha256=
|
|
49
|
+
mcp_agent/mcp/mcp_connection_manager.py,sha256=WLli0w3TVcsszyD9M7zP7vLKPetnQLTf_0PGhvMm9YM,13145
|
|
50
50
|
mcp_agent/mcp/stdio.py,sha256=tW075R5rQ-UlflXWFKIFDgCbWbuhKqxhiYolWvyEkFs,3985
|
|
51
51
|
mcp_agent/resources/examples/data-analysis/analysis.py,sha256=Sp-umPPfwVjG3yNrHdQA6blGtG6jc5of1e_0oS4njYc,1379
|
|
52
52
|
mcp_agent/resources/examples/data-analysis/fastagent.config.yaml,sha256=eTKGbjnTHhDTeNRPQvG_fr9OQpEZ5Y9v7X2NyCj0V70,530
|
|
@@ -54,18 +54,16 @@ mcp_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-A
|
|
|
54
54
|
mcp_agent/resources/examples/internal/agent.py,sha256=f-jTgYabV3nWCQm0ZP9NtSEWjx3nQbRngzArRufcELg,384
|
|
55
55
|
mcp_agent/resources/examples/internal/job.py,sha256=WEKIAANMEAuKr13__rYf3PqJeTAsNB_kqYqbqVYQlUM,4093
|
|
56
56
|
mcp_agent/resources/examples/mcp_researcher/researcher-eval.py,sha256=kNPjIU-JwE0oIBQKwhv6lZsUF_SPtYVkiEEbY1ZVZxk,1807
|
|
57
|
-
mcp_agent/resources/examples/mcp_researcher/researcher.py,sha256=jPRafm7jbpHKkX_dQiYGG3Sw-e1Dm86q-JZT-WZDhM0,1425
|
|
58
57
|
mcp_agent/resources/examples/researcher/fastagent.config.yaml,sha256=2_VXZneckR6zk6RWzzL-smV_oWmgg4uSkLWqZv8jF0I,1995
|
|
59
58
|
mcp_agent/resources/examples/researcher/researcher-eval.py,sha256=kNPjIU-JwE0oIBQKwhv6lZsUF_SPtYVkiEEbY1ZVZxk,1807
|
|
60
59
|
mcp_agent/resources/examples/researcher/researcher.py,sha256=jPRafm7jbpHKkX_dQiYGG3Sw-e1Dm86q-JZT-WZDhM0,1425
|
|
61
|
-
mcp_agent/resources/examples/workflows/agent.py,sha256=f-jTgYabV3nWCQm0ZP9NtSEWjx3nQbRngzArRufcELg,384
|
|
62
60
|
mcp_agent/resources/examples/workflows/agent_build.py,sha256=vdjS02rZR88RU53WYzXxPscfFNEFFe_niHYE_i49I8Q,2396
|
|
63
|
-
mcp_agent/resources/examples/workflows/chaining.py,sha256=
|
|
64
|
-
mcp_agent/resources/examples/workflows/evaluator.py,sha256=
|
|
65
|
-
mcp_agent/resources/examples/workflows/fastagent.
|
|
61
|
+
mcp_agent/resources/examples/workflows/chaining.py,sha256=sDymBRuU5V_tlnDuclOaMYHPbHOk8eyaWsya5vxzrGo,817
|
|
62
|
+
mcp_agent/resources/examples/workflows/evaluator.py,sha256=FZy-ciZafdqSHUW67LKdHw0t9rvX6X67waMOoeIN3GY,3147
|
|
63
|
+
mcp_agent/resources/examples/workflows/fastagent.config.yaml,sha256=k2AiapOcK42uqG2nWDVvnSLqN4okQIQZK0FTbZufBpY,809
|
|
66
64
|
mcp_agent/resources/examples/workflows/human_input.py,sha256=c8cBdLEPbaMXddFwsfN3Z7RFs5PZXsdrjANfvq1VTPM,605
|
|
67
|
-
mcp_agent/resources/examples/workflows/orchestrator.py,sha256=
|
|
68
|
-
mcp_agent/resources/examples/workflows/parallel.py,sha256=
|
|
65
|
+
mcp_agent/resources/examples/workflows/orchestrator.py,sha256=pRJqB-ok79_iEj8aG4FysHyXz6wAHLUX-5tS8khUI7k,2574
|
|
66
|
+
mcp_agent/resources/examples/workflows/parallel.py,sha256=pLbQrtXfbdYqMVddxtg5dZnBnm5Wo2mXlIa1Vf2F1FQ,3096
|
|
69
67
|
mcp_agent/resources/examples/workflows/router.py,sha256=XT_ewCrxPxdUTMCYQGw34qZQ3GGu8TYY_v5Lige8By4,1707
|
|
70
68
|
mcp_agent/telemetry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
71
69
|
mcp_agent/telemetry/usage_tracking.py,sha256=ePujKMSjPxB7k6X34DGaVlnsV1728mcWZq38OqahiCU,501
|
|
@@ -85,11 +83,11 @@ mcp_agent/workflows/intent_classifier/intent_classifier_llm.py,sha256=WSLUv2Casb
|
|
|
85
83
|
mcp_agent/workflows/intent_classifier/intent_classifier_llm_anthropic.py,sha256=Hp4454IniWFxV4ml50Ml8ip9rS1La5FBn5pd7vm1FHA,1964
|
|
86
84
|
mcp_agent/workflows/intent_classifier/intent_classifier_llm_openai.py,sha256=zj76WlTYnSCYjBQ_IDi5vFBQGmNwYaoUq1rT730sY98,1940
|
|
87
85
|
mcp_agent/workflows/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
88
|
-
mcp_agent/workflows/llm/augmented_llm.py,sha256=
|
|
86
|
+
mcp_agent/workflows/llm/augmented_llm.py,sha256=8D0hUePn47ChZrmUL8nt60u-iw09EFnJIV7wiQDd2fo,23362
|
|
89
87
|
mcp_agent/workflows/llm/augmented_llm_anthropic.py,sha256=yrOv1V6rOfm2TTDR58fnf8YU8hBnTIpOZhB2sUgZw6o,21246
|
|
90
88
|
mcp_agent/workflows/llm/augmented_llm_openai.py,sha256=5PwTh0QJSQ29EtK0UuiltgX6snRSBoau75C35S4xQcQ,24477
|
|
91
89
|
mcp_agent/workflows/llm/llm_selector.py,sha256=G7pIybuBDwtmyxUDov_QrNYH2FoI0qFRu2JfoxWUF5Y,11045
|
|
92
|
-
mcp_agent/workflows/llm/model_factory.py,sha256=
|
|
90
|
+
mcp_agent/workflows/llm/model_factory.py,sha256=7zTJrO2ReHa_6dfh_gY6xO8dTySqGFCKlOG9-AMJ-i8,6920
|
|
93
91
|
mcp_agent/workflows/orchestrator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
94
92
|
mcp_agent/workflows/orchestrator/orchestrator.py,sha256=Fn5r0uUGNAiUq5NLFDjaJ04t19MnGEgr9iknbUj0DSA,14733
|
|
95
93
|
mcp_agent/workflows/orchestrator/orchestrator_models.py,sha256=UWn7_HFLcqFGlcjZ1Rn2SYQfm5k9seS6QJN_FRST5Kc,4513
|
|
@@ -108,8 +106,8 @@ mcp_agent/workflows/swarm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
|
|
|
108
106
|
mcp_agent/workflows/swarm/swarm.py,sha256=-lAIeSWDqbGHGRPTvjiP9nIKWvxxy9DAojl9yQzO1Pw,11050
|
|
109
107
|
mcp_agent/workflows/swarm/swarm_anthropic.py,sha256=pW8zFx5baUWGd5Vw3nIDF2oVOOGNorij4qvGJKdYPcs,1624
|
|
110
108
|
mcp_agent/workflows/swarm/swarm_openai.py,sha256=wfteywvAGkT5bLmIxX_StHJq8144whYmCRnJASAjOes,1596
|
|
111
|
-
fast_agent_mcp-0.0.
|
|
112
|
-
fast_agent_mcp-0.0.
|
|
113
|
-
fast_agent_mcp-0.0.
|
|
114
|
-
fast_agent_mcp-0.0.
|
|
115
|
-
fast_agent_mcp-0.0.
|
|
109
|
+
fast_agent_mcp-0.0.13.dist-info/METADATA,sha256=5Mu-PczPn7wfDWw_j_UXpYWlUYhDHOQEcGRvHyxg5MA,16777
|
|
110
|
+
fast_agent_mcp-0.0.13.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
111
|
+
fast_agent_mcp-0.0.13.dist-info/entry_points.txt,sha256=2IXtSmDK9XjWN__RWuRIJTgWyW17wJnJ_h-pb0pZAxo,174
|
|
112
|
+
fast_agent_mcp-0.0.13.dist-info/licenses/LICENSE,sha256=cN3FxDURL9XuzE5mhK9L2paZo82LTfjwCYVT7e3j0e4,10939
|
|
113
|
+
fast_agent_mcp-0.0.13.dist-info/RECORD,,
|
mcp_agent/agents/agent.py
CHANGED
|
@@ -11,6 +11,7 @@ from mcp.types import (
|
|
|
11
11
|
Tool,
|
|
12
12
|
)
|
|
13
13
|
|
|
14
|
+
from mcp_agent.core.exceptions import PromptExitError
|
|
14
15
|
from mcp_agent.mcp.mcp_aggregator import MCPAggregator
|
|
15
16
|
from mcp_agent.workflows.llm.augmented_llm import RequestParams
|
|
16
17
|
from mcp_agent.human_input.types import (
|
|
@@ -24,6 +25,7 @@ from mcp_agent.logging.logger import get_logger
|
|
|
24
25
|
|
|
25
26
|
if TYPE_CHECKING:
|
|
26
27
|
from mcp_agent.context import Context
|
|
28
|
+
import traceback
|
|
27
29
|
|
|
28
30
|
logger = get_logger(__name__)
|
|
29
31
|
|
|
@@ -148,10 +150,7 @@ class Agent(MCPAggregator):
|
|
|
148
150
|
"""
|
|
149
151
|
await super().close()
|
|
150
152
|
|
|
151
|
-
async def request_human_input(
|
|
152
|
-
self,
|
|
153
|
-
request: HumanInputRequest,
|
|
154
|
-
) -> str:
|
|
153
|
+
async def request_human_input(self, request: HumanInputRequest) -> str:
|
|
155
154
|
"""
|
|
156
155
|
Request input from a human user. Pauses the workflow until input is received.
|
|
157
156
|
|
|
@@ -170,14 +169,23 @@ class Agent(MCPAggregator):
|
|
|
170
169
|
# Generate a unique ID for this request to avoid signal collisions
|
|
171
170
|
request_id = f"{HUMAN_INPUT_SIGNAL_NAME}_{self.name}_{uuid.uuid4()}"
|
|
172
171
|
request.request_id = request_id
|
|
173
|
-
|
|
172
|
+
# Use metadata as a dictionary to pass agent name
|
|
173
|
+
request.metadata = {"agent_name": self.name}
|
|
174
174
|
self.logger.debug("Requesting human input:", data=request)
|
|
175
175
|
|
|
176
176
|
async def call_callback_and_signal():
|
|
177
177
|
try:
|
|
178
178
|
user_input = await self.human_input_callback(request)
|
|
179
|
+
|
|
179
180
|
self.logger.debug("Received human input:", data=user_input)
|
|
180
181
|
await self.executor.signal(signal_name=request_id, payload=user_input)
|
|
182
|
+
except PromptExitError as e:
|
|
183
|
+
# Propagate the exit error through the signal system
|
|
184
|
+
self.logger.info("User requested to exit session")
|
|
185
|
+
await self.executor.signal(
|
|
186
|
+
signal_name=request_id,
|
|
187
|
+
payload={"exit_requested": True, "error": str(e)},
|
|
188
|
+
)
|
|
181
189
|
except Exception as e:
|
|
182
190
|
await self.executor.signal(
|
|
183
191
|
request_id, payload=f"Error getting human input: {str(e)}"
|
|
@@ -197,6 +205,10 @@ class Agent(MCPAggregator):
|
|
|
197
205
|
signal_type=HumanInputResponse, # TODO: saqadri - should this be HumanInputResponse?
|
|
198
206
|
)
|
|
199
207
|
|
|
208
|
+
if isinstance(result, dict) and result.get("exit_requested", False):
|
|
209
|
+
raise PromptExitError(
|
|
210
|
+
result.get("error", "User requested to exit FastAgent session")
|
|
211
|
+
)
|
|
200
212
|
self.logger.debug("Received human input signal", data=result)
|
|
201
213
|
return result
|
|
202
214
|
|
|
@@ -253,13 +265,39 @@ class Agent(MCPAggregator):
|
|
|
253
265
|
) -> CallToolResult:
|
|
254
266
|
# Handle human input request
|
|
255
267
|
try:
|
|
256
|
-
|
|
257
|
-
|
|
268
|
+
# Make sure arguments is not None
|
|
269
|
+
if arguments is None:
|
|
270
|
+
arguments = {}
|
|
271
|
+
|
|
272
|
+
# Extract request data
|
|
273
|
+
request_data = arguments.get("request")
|
|
274
|
+
|
|
275
|
+
# Handle both string and dict request formats
|
|
276
|
+
if isinstance(request_data, str):
|
|
277
|
+
request = HumanInputRequest(prompt=request_data)
|
|
278
|
+
elif isinstance(request_data, dict):
|
|
279
|
+
request = HumanInputRequest(**request_data)
|
|
280
|
+
else:
|
|
281
|
+
# Fallback for invalid or missing request data
|
|
282
|
+
request = HumanInputRequest(prompt="Please provide input:")
|
|
283
|
+
|
|
284
|
+
result = await self.request_human_input(request=request)
|
|
285
|
+
|
|
286
|
+
# Use response attribute if available, otherwise use the result directly
|
|
287
|
+
response_text = (
|
|
288
|
+
result.response
|
|
289
|
+
if isinstance(result, HumanInputResponse)
|
|
290
|
+
else str(result)
|
|
291
|
+
)
|
|
292
|
+
|
|
258
293
|
return CallToolResult(
|
|
259
294
|
content=[
|
|
260
|
-
TextContent(type="text", text=f"Human response: {
|
|
295
|
+
TextContent(type="text", text=f"Human response: {response_text}")
|
|
261
296
|
]
|
|
262
297
|
)
|
|
298
|
+
|
|
299
|
+
except PromptExitError:
|
|
300
|
+
raise
|
|
263
301
|
except TimeoutError as e:
|
|
264
302
|
return CallToolResult(
|
|
265
303
|
isError=True,
|
|
@@ -271,6 +309,8 @@ class Agent(MCPAggregator):
|
|
|
271
309
|
],
|
|
272
310
|
)
|
|
273
311
|
except Exception as e:
|
|
312
|
+
print(f"Error in _call_human_input_tool: {traceback.format_exc()}")
|
|
313
|
+
|
|
274
314
|
return CallToolResult(
|
|
275
315
|
isError=True,
|
|
276
316
|
content=[
|
mcp_agent/cli/commands/setup.py
CHANGED
|
@@ -119,7 +119,7 @@ fast = FastAgent("FastAgent Example")
|
|
|
119
119
|
|
|
120
120
|
|
|
121
121
|
# Define the agent
|
|
122
|
-
@fast.agent(servers=["fetch"])
|
|
122
|
+
@fast.agent(instruction="You are a helpful AI Agent", servers=["fetch"])
|
|
123
123
|
async def main():
|
|
124
124
|
# use the --model command line switch or agent arguments to change model
|
|
125
125
|
async with fast.run() as agent:
|
mcp_agent/cli/main.py
CHANGED
|
@@ -32,7 +32,7 @@ def show_welcome():
|
|
|
32
32
|
|
|
33
33
|
table.add_row("setup", "Set up a new agent project with configuration files")
|
|
34
34
|
table.add_row(
|
|
35
|
-
"bootstrap", "Create example applications (
|
|
35
|
+
"bootstrap", "Create example applications (workflow, researcher, etc.)"
|
|
36
36
|
)
|
|
37
37
|
# table.add_row("config", "Manage agent configuration settings")
|
|
38
38
|
|
|
@@ -41,8 +41,8 @@ def show_welcome():
|
|
|
41
41
|
console.print("\n[bold]Getting Started:[/bold]")
|
|
42
42
|
console.print("1. Set up a new project:")
|
|
43
43
|
console.print(" fastagent setup")
|
|
44
|
-
console.print("\n2. Try an example:")
|
|
45
|
-
console.print(" fastagent bootstrap create
|
|
44
|
+
console.print("\n2. Try an example workflow:")
|
|
45
|
+
console.print(" fastagent bootstrap create workflow")
|
|
46
46
|
console.print("\nUse --help with any command for more information")
|
|
47
47
|
console.print("Example: fastagent bootstrap --help")
|
|
48
48
|
|
|
@@ -13,6 +13,8 @@ from prompt_toolkit.filters import Condition
|
|
|
13
13
|
from pygments.lexers.python import PythonLexer
|
|
14
14
|
from rich import print as rich_print
|
|
15
15
|
|
|
16
|
+
from mcp_agent.core.exceptions import PromptExitError
|
|
17
|
+
|
|
16
18
|
# Map of agent names to their history
|
|
17
19
|
agent_histories = {}
|
|
18
20
|
|
|
@@ -29,9 +31,25 @@ agent_messages_shown = set()
|
|
|
29
31
|
class AgentCompleter(Completer):
|
|
30
32
|
"""Provide completion for agent names and common commands."""
|
|
31
33
|
|
|
32
|
-
def __init__(
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
agents: List[str],
|
|
37
|
+
commands: List[str] = None,
|
|
38
|
+
agent_types: dict = None,
|
|
39
|
+
is_human_input: bool = False,
|
|
40
|
+
):
|
|
33
41
|
self.agents = agents
|
|
34
|
-
|
|
42
|
+
# Map commands to their descriptions for better completion hints
|
|
43
|
+
self.commands = {
|
|
44
|
+
"help": "Show available commands",
|
|
45
|
+
"clear": "Clear the screen",
|
|
46
|
+
"agents": "List available agents",
|
|
47
|
+
"STOP": "Stop this prompting session and move to next workflow step",
|
|
48
|
+
"EXIT": "Exit FastAgent, terminating any running workflows",
|
|
49
|
+
**(commands or {}), # Allow custom commands to be passed in
|
|
50
|
+
}
|
|
51
|
+
if is_human_input:
|
|
52
|
+
self.commands.pop("agents")
|
|
35
53
|
self.agent_types = agent_types or {}
|
|
36
54
|
|
|
37
55
|
def get_completions(self, document, complete_event):
|
|
@@ -41,13 +59,13 @@ class AgentCompleter(Completer):
|
|
|
41
59
|
# Complete commands
|
|
42
60
|
if text.startswith("/"):
|
|
43
61
|
cmd = text[1:]
|
|
44
|
-
for command in self.commands:
|
|
62
|
+
for command, description in self.commands.items():
|
|
45
63
|
if command.lower().startswith(cmd):
|
|
46
64
|
yield Completion(
|
|
47
65
|
command,
|
|
48
66
|
start_position=-len(cmd),
|
|
49
67
|
display=command,
|
|
50
|
-
display_meta=
|
|
68
|
+
display_meta=description,
|
|
51
69
|
)
|
|
52
70
|
|
|
53
71
|
# Complete agent names for agent-related commands
|
|
@@ -62,6 +80,7 @@ class AgentCompleter(Completer):
|
|
|
62
80
|
start_position=-len(agent_name),
|
|
63
81
|
display=agent,
|
|
64
82
|
display_meta=agent_type,
|
|
83
|
+
style="bg:ansiblack fg:ansiblue",
|
|
65
84
|
)
|
|
66
85
|
|
|
67
86
|
|
|
@@ -79,9 +98,10 @@ def create_keybindings(on_toggle_multiline=None, app=None):
|
|
|
79
98
|
"""Enter: insert newline when in multiline mode."""
|
|
80
99
|
event.current_buffer.insert_text("\n")
|
|
81
100
|
|
|
82
|
-
|
|
101
|
+
# Use c-j (Ctrl+J) as an alternative to represent Ctrl+Enter in multiline mode
|
|
102
|
+
@kb.add("c-j", filter=Condition(lambda: in_multiline_mode))
|
|
83
103
|
def _(event):
|
|
84
|
-
"""
|
|
104
|
+
"""Ctrl+J (equivalent to Ctrl+Enter): Submit in multiline mode."""
|
|
85
105
|
event.current_buffer.validate_and_handle()
|
|
86
106
|
|
|
87
107
|
@kb.add("c-t")
|
|
@@ -105,7 +125,7 @@ def create_keybindings(on_toggle_multiline=None, app=None):
|
|
|
105
125
|
|
|
106
126
|
@kb.add("c-l")
|
|
107
127
|
def _(event):
|
|
108
|
-
"""Ctrl+L: Clear input."""
|
|
128
|
+
"""Ctrl+L: Clear the input buffer."""
|
|
109
129
|
event.current_buffer.text = ""
|
|
110
130
|
|
|
111
131
|
return kb
|
|
@@ -120,6 +140,8 @@ async def get_enhanced_input(
|
|
|
120
140
|
available_agent_names: List[str] = None,
|
|
121
141
|
syntax: str = None,
|
|
122
142
|
agent_types: dict = None,
|
|
143
|
+
is_human_input: bool = False,
|
|
144
|
+
toolbar_color: str = "ansiblue",
|
|
123
145
|
) -> str:
|
|
124
146
|
"""
|
|
125
147
|
Enhanced input with advanced prompt_toolkit features.
|
|
@@ -133,6 +155,8 @@ async def get_enhanced_input(
|
|
|
133
155
|
available_agent_names: List of agent names for auto-completion
|
|
134
156
|
syntax: Syntax highlighting (e.g., 'python', 'sql')
|
|
135
157
|
agent_types: Dictionary mapping agent names to their types for display
|
|
158
|
+
is_human_input: Whether this is a human input request (disables agent selection features)
|
|
159
|
+
toolbar_color: Color to use for the agent name in the toolbar (default: "ansiblue")
|
|
136
160
|
|
|
137
161
|
Returns:
|
|
138
162
|
User input string
|
|
@@ -167,16 +191,20 @@ async def get_enhanced_input(
|
|
|
167
191
|
|
|
168
192
|
shortcuts = [
|
|
169
193
|
("Ctrl+T", toggle_text),
|
|
170
|
-
("Alt+Enter", "Submit" if in_multiline_mode else ""),
|
|
171
194
|
("Ctrl+L", "Clear"),
|
|
172
195
|
("↑/↓", "History"),
|
|
173
196
|
]
|
|
197
|
+
|
|
198
|
+
newline = (
|
|
199
|
+
"Ctrl+<Enter>:Submit" if in_multiline_mode else "<Enter>:Submit"
|
|
200
|
+
)
|
|
201
|
+
|
|
174
202
|
# Only show relevant shortcuts based on mode
|
|
175
203
|
shortcuts = [(k, v) for k, v in shortcuts if v]
|
|
176
204
|
|
|
177
205
|
shortcut_text = " | ".join(f"{key}:{action}" for key, action in shortcuts)
|
|
178
206
|
return HTML(
|
|
179
|
-
f" <
|
|
207
|
+
f" <{toolbar_color}> {agent_name} </{toolbar_color}> | <b>Mode:</b> <{mode_style}> {mode_text} </{mode_style}> {newline} | {shortcut_text}"
|
|
180
208
|
)
|
|
181
209
|
|
|
182
210
|
# Create session with history and completions
|
|
@@ -185,12 +213,13 @@ async def get_enhanced_input(
|
|
|
185
213
|
completer=AgentCompleter(
|
|
186
214
|
agents=list(available_agents) if available_agents else [],
|
|
187
215
|
agent_types=agent_types or {},
|
|
216
|
+
is_human_input=is_human_input,
|
|
188
217
|
),
|
|
189
218
|
complete_while_typing=True,
|
|
190
219
|
lexer=PygmentsLexer(PythonLexer) if syntax == "python" else None,
|
|
191
220
|
multiline=Condition(lambda: in_multiline_mode),
|
|
192
221
|
complete_in_thread=True,
|
|
193
|
-
mouse_support=
|
|
222
|
+
mouse_support=False,
|
|
194
223
|
bottom_toolbar=get_toolbar, # Pass the function here
|
|
195
224
|
)
|
|
196
225
|
|
|
@@ -220,9 +249,14 @@ async def get_enhanced_input(
|
|
|
220
249
|
|
|
221
250
|
# Mention available features but only on first usage for this agent
|
|
222
251
|
if agent_name not in agent_messages_shown:
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
252
|
+
if is_human_input:
|
|
253
|
+
rich_print(
|
|
254
|
+
"[dim]Tip: Type /help for commands. Ctrl+T toggles multiline mode. Ctrl+Enter to submit in multiline mode.[/dim]"
|
|
255
|
+
)
|
|
256
|
+
else:
|
|
257
|
+
rich_print(
|
|
258
|
+
"[dim]Tip: Type /help for commands, @Agent to switch agent. Ctrl+T toggles multiline mode. [/dim]"
|
|
259
|
+
)
|
|
226
260
|
agent_messages_shown.add(agent_name)
|
|
227
261
|
|
|
228
262
|
# Process special commands
|
|
@@ -236,6 +270,10 @@ async def get_enhanced_input(
|
|
|
236
270
|
return "CLEAR"
|
|
237
271
|
elif cmd == "agents":
|
|
238
272
|
return "LIST_AGENTS"
|
|
273
|
+
elif cmd == "exit":
|
|
274
|
+
return "EXIT"
|
|
275
|
+
elif cmd == "stop":
|
|
276
|
+
return "STOP"
|
|
239
277
|
|
|
240
278
|
# Agent switching
|
|
241
279
|
if text and text.startswith("@"):
|
|
@@ -272,16 +310,18 @@ async def handle_special_commands(command, agent_app=None):
|
|
|
272
310
|
rich_print(" /clear - Clear screen")
|
|
273
311
|
rich_print(" /agents - List available agents")
|
|
274
312
|
rich_print(" @agent_name - Switch to agent")
|
|
275
|
-
rich_print(" STOP -
|
|
313
|
+
rich_print(" STOP - Return control back to the workflow")
|
|
314
|
+
rich_print(
|
|
315
|
+
" EXIT - Exit FastAgent, terminating any running workflows"
|
|
316
|
+
)
|
|
276
317
|
rich_print("\n[bold]Keyboard Shortcuts:[/bold]")
|
|
277
318
|
rich_print(
|
|
278
319
|
" Enter - Submit (normal mode) / New line (multiline mode)"
|
|
279
320
|
)
|
|
280
|
-
rich_print("
|
|
321
|
+
rich_print(" Ctrl+Enter - Always submit (even in multiline mode)")
|
|
281
322
|
rich_print(" Ctrl+T - Toggle multiline mode")
|
|
282
323
|
rich_print(" Ctrl+L - Clear input")
|
|
283
324
|
rich_print(" Up/Down - Navigate history")
|
|
284
|
-
rich_print(" F1 - Show help")
|
|
285
325
|
return True
|
|
286
326
|
|
|
287
327
|
elif command == "CLEAR":
|
|
@@ -289,6 +329,9 @@ async def handle_special_commands(command, agent_app=None):
|
|
|
289
329
|
print("\033c", end="")
|
|
290
330
|
return True
|
|
291
331
|
|
|
332
|
+
elif command == "EXIT":
|
|
333
|
+
raise PromptExitError("User requested to exit FastAgent session")
|
|
334
|
+
|
|
292
335
|
elif command == "LIST_AGENTS":
|
|
293
336
|
if available_agents:
|
|
294
337
|
rich_print("\n[bold]Available Agents:[/bold]")
|
mcp_agent/core/exceptions.py
CHANGED
|
@@ -45,3 +45,20 @@ class ServerInitializationError(FastAgentError):
|
|
|
45
45
|
|
|
46
46
|
def __init__(self, message: str, details: str = ""):
|
|
47
47
|
super().__init__(message, details)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class ModelConfigError(FastAgentError):
|
|
51
|
+
"""Raised when there are issues with LLM model configuration
|
|
52
|
+
Example: Unknown model name in model specification string
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def __init__(self, message: str, details: str = ""):
|
|
56
|
+
super().__init__(message, details)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class PromptExitError(FastAgentError):
|
|
60
|
+
"""Raised from enhanced_prompt when the user requests hard exits"""
|
|
61
|
+
|
|
62
|
+
# TODO an exception for flow control :(
|
|
63
|
+
def __init__(self, message: str, details: str = ""):
|
|
64
|
+
super().__init__(message, details)
|
mcp_agent/core/fastagent.py
CHANGED
|
@@ -3,7 +3,17 @@ Decorator-based interface for MCP Agent applications.
|
|
|
3
3
|
Provides a simplified way to create and manage agents using decorators.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
-
from typing import
|
|
6
|
+
from typing import (
|
|
7
|
+
List,
|
|
8
|
+
Optional,
|
|
9
|
+
Dict,
|
|
10
|
+
Callable,
|
|
11
|
+
TypeVar,
|
|
12
|
+
Any,
|
|
13
|
+
Union,
|
|
14
|
+
TypeAlias,
|
|
15
|
+
Literal,
|
|
16
|
+
)
|
|
7
17
|
from enum import Enum
|
|
8
18
|
import yaml
|
|
9
19
|
import argparse
|
|
@@ -11,6 +21,8 @@ from contextlib import asynccontextmanager
|
|
|
11
21
|
|
|
12
22
|
from mcp_agent.core.exceptions import (
|
|
13
23
|
AgentConfigError,
|
|
24
|
+
ModelConfigError,
|
|
25
|
+
PromptExitError,
|
|
14
26
|
ServerConfigError,
|
|
15
27
|
ProviderKeyError,
|
|
16
28
|
ServerInitializationError,
|
|
@@ -175,7 +187,7 @@ class AgentApp:
|
|
|
175
187
|
|
|
176
188
|
# Pass all available agent names for auto-completion
|
|
177
189
|
available_agents = list(self._agents.keys())
|
|
178
|
-
|
|
190
|
+
|
|
179
191
|
# Create agent_types dictionary mapping agent names to their types
|
|
180
192
|
agent_types = {}
|
|
181
193
|
for name, proxy in self._agents.items():
|
|
@@ -633,6 +645,7 @@ class FastAgent(ContextDependent):
|
|
|
633
645
|
use_history: bool = False,
|
|
634
646
|
request_params: Optional[Dict] = None,
|
|
635
647
|
human_input: bool = False,
|
|
648
|
+
plan_type: Literal["full", "iterative"] = "full",
|
|
636
649
|
) -> Callable:
|
|
637
650
|
"""
|
|
638
651
|
Decorator to create and register an orchestrator.
|
|
@@ -645,6 +658,7 @@ class FastAgent(ContextDependent):
|
|
|
645
658
|
use_history: Whether to maintain conversation history (forced false)
|
|
646
659
|
request_params: Additional request parameters for the LLM
|
|
647
660
|
human_input: Whether to enable human input capabilities
|
|
661
|
+
plan_type: Planning approach - "full" generates entire plan first, "iterative" plans one step at a time
|
|
648
662
|
"""
|
|
649
663
|
default_instruction = """
|
|
650
664
|
You are an expert planner. Given an objective task and a list of MCP servers (which are collections of tools)
|
|
@@ -666,6 +680,7 @@ class FastAgent(ContextDependent):
|
|
|
666
680
|
use_history=use_history,
|
|
667
681
|
request_params=request_params,
|
|
668
682
|
human_input=human_input,
|
|
683
|
+
plan_type=plan_type,
|
|
669
684
|
)
|
|
670
685
|
return decorator
|
|
671
686
|
|
|
@@ -864,7 +879,7 @@ class FastAgent(ContextDependent):
|
|
|
864
879
|
which can be performed by LLMs with access to the servers or agents.
|
|
865
880
|
""",
|
|
866
881
|
servers=[], # Planner doesn't need server access
|
|
867
|
-
model=config.model,
|
|
882
|
+
model=config.model,
|
|
868
883
|
default_request_params=base_params,
|
|
869
884
|
)
|
|
870
885
|
planner_agent = Agent(
|
|
@@ -885,8 +900,10 @@ class FastAgent(ContextDependent):
|
|
|
885
900
|
available_agents=child_agents,
|
|
886
901
|
context=agent_app.context,
|
|
887
902
|
request_params=planner.default_request_params, # Base params already include model settings
|
|
888
|
-
plan_type=
|
|
889
|
-
|
|
903
|
+
plan_type=agent_data.get(
|
|
904
|
+
"plan_type", "full"
|
|
905
|
+
), # Get plan_type from agent_data
|
|
906
|
+
verb=ProgressAction.PLANNING,
|
|
890
907
|
)
|
|
891
908
|
|
|
892
909
|
elif agent_type == AgentType.EVALUATOR_OPTIMIZER:
|
|
@@ -1228,10 +1245,28 @@ class FastAgent(ContextDependent):
|
|
|
1228
1245
|
had_error = True
|
|
1229
1246
|
self._handle_error(
|
|
1230
1247
|
e,
|
|
1231
|
-
"Server Startup Error",
|
|
1248
|
+
"MCP Server Startup Error",
|
|
1232
1249
|
"There was an error starting up the MCP Server.",
|
|
1233
1250
|
)
|
|
1234
1251
|
raise SystemExit(1)
|
|
1252
|
+
|
|
1253
|
+
except ModelConfigError as e:
|
|
1254
|
+
had_error = True
|
|
1255
|
+
self._handle_error(
|
|
1256
|
+
e,
|
|
1257
|
+
"Model Configuration Error",
|
|
1258
|
+
"Common models: gpt-4o, o3-mini, sonnet, haiku. for o3, set reasoning effort with o3-mini.high",
|
|
1259
|
+
)
|
|
1260
|
+
raise SystemExit(1)
|
|
1261
|
+
|
|
1262
|
+
except PromptExitError as e:
|
|
1263
|
+
had_error = True
|
|
1264
|
+
self._handle_error(
|
|
1265
|
+
e,
|
|
1266
|
+
"User requested exit",
|
|
1267
|
+
)
|
|
1268
|
+
raise SystemExit(1)
|
|
1269
|
+
|
|
1235
1270
|
finally:
|
|
1236
1271
|
# Clean up any active agents without re-raising errors
|
|
1237
1272
|
if active_agents and not had_error:
|
|
@@ -1239,7 +1274,8 @@ class FastAgent(ContextDependent):
|
|
|
1239
1274
|
if isinstance(proxy, LLMAgentProxy):
|
|
1240
1275
|
try:
|
|
1241
1276
|
await proxy._agent.__aexit__(None, None, None)
|
|
1242
|
-
except Exception:
|
|
1277
|
+
except Exception as e:
|
|
1278
|
+
print(f"DEBUG {e.message}")
|
|
1243
1279
|
pass # Ignore cleanup errors
|
|
1244
1280
|
|
|
1245
1281
|
def _handle_error(
|
mcp_agent/human_input/handler.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
from rich.panel import Panel
|
|
3
|
-
from rich.prompt import Prompt
|
|
4
3
|
|
|
5
4
|
from mcp_agent.console import console
|
|
6
5
|
from mcp_agent.human_input.types import (
|
|
@@ -8,10 +7,11 @@ from mcp_agent.human_input.types import (
|
|
|
8
7
|
HumanInputResponse,
|
|
9
8
|
)
|
|
10
9
|
from mcp_agent.progress_display import progress_display
|
|
10
|
+
from mcp_agent.core.enhanced_prompt import get_enhanced_input, handle_special_commands
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
async def console_input_callback(request: HumanInputRequest) -> HumanInputResponse:
|
|
14
|
-
"""Request input from a human user via console using
|
|
14
|
+
"""Request input from a human user via console using prompt_toolkit."""
|
|
15
15
|
|
|
16
16
|
# Prepare the prompt text
|
|
17
17
|
prompt_text = request.prompt
|
|
@@ -28,26 +28,51 @@ async def console_input_callback(request: HumanInputRequest) -> HumanInputRespon
|
|
|
28
28
|
padding=(1, 2),
|
|
29
29
|
)
|
|
30
30
|
|
|
31
|
+
# Extract agent name from metadata dictionary
|
|
32
|
+
agent_name = (
|
|
33
|
+
request.metadata.get("agent_name", "Unknown Agent")
|
|
34
|
+
if request.metadata
|
|
35
|
+
else "Unknown Agent"
|
|
36
|
+
)
|
|
37
|
+
|
|
31
38
|
# Use the context manager to pause the progress display while getting input
|
|
32
39
|
with progress_display.paused():
|
|
33
40
|
console.print(panel)
|
|
34
41
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
try:
|
|
43
|
+
if request.timeout_seconds:
|
|
44
|
+
try:
|
|
45
|
+
# Use get_enhanced_input with empty agent list to disable agent switching
|
|
46
|
+
response = await asyncio.wait_for(
|
|
47
|
+
get_enhanced_input(
|
|
48
|
+
agent_name=agent_name,
|
|
49
|
+
available_agent_names=[], # No agents for selection
|
|
50
|
+
show_stop_hint=False,
|
|
51
|
+
is_human_input=True,
|
|
52
|
+
toolbar_color="ansimagenta",
|
|
53
|
+
),
|
|
54
|
+
request.timeout_seconds,
|
|
55
|
+
)
|
|
56
|
+
except asyncio.TimeoutError:
|
|
57
|
+
console.print("\n[red]Timeout waiting for input[/red]")
|
|
58
|
+
raise TimeoutError("No response received within timeout period")
|
|
59
|
+
else:
|
|
60
|
+
response = await get_enhanced_input(
|
|
61
|
+
agent_name=agent_name,
|
|
62
|
+
available_agent_names=[], # No agents for selection
|
|
63
|
+
show_stop_hint=False,
|
|
64
|
+
is_human_input=True,
|
|
65
|
+
toolbar_color="ansimagenta",
|
|
43
66
|
)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
67
|
+
|
|
68
|
+
# if response and (response.startswith("/") or response.startswith("@")):
|
|
69
|
+
await handle_special_commands(response)
|
|
70
|
+
|
|
71
|
+
except KeyboardInterrupt:
|
|
72
|
+
console.print("\n[yellow]Input interrupted[/yellow]")
|
|
73
|
+
response = ""
|
|
74
|
+
except EOFError:
|
|
75
|
+
console.print("\n[yellow]Input terminated[/yellow]")
|
|
76
|
+
response = ""
|
|
52
77
|
|
|
53
78
|
return HumanInputResponse(request_id=request.request_id, response=response.strip())
|
|
@@ -75,15 +75,15 @@ class ServerConnection:
|
|
|
75
75
|
|
|
76
76
|
# Signal we want to shut down
|
|
77
77
|
self._shutdown_event = Event()
|
|
78
|
-
|
|
78
|
+
|
|
79
79
|
# Track error state
|
|
80
80
|
self._error_occurred = False
|
|
81
81
|
self._error_message = None
|
|
82
|
-
|
|
82
|
+
|
|
83
83
|
def is_healthy(self) -> bool:
|
|
84
84
|
"""Check if the server connection is healthy and ready to use."""
|
|
85
85
|
return self.session is not None and not self._error_occurred
|
|
86
|
-
|
|
86
|
+
|
|
87
87
|
def reset_error_state(self) -> None:
|
|
88
88
|
"""Reset the error state, allowing reconnection attempts."""
|
|
89
89
|
self._error_occurred = False
|
|
@@ -216,10 +216,10 @@ class MCPConnectionManager(ContextDependent):
|
|
|
216
216
|
try:
|
|
217
217
|
# First request all servers to shutdown
|
|
218
218
|
await self.disconnect_all()
|
|
219
|
-
|
|
219
|
+
|
|
220
220
|
# Add a small delay to allow for clean shutdown
|
|
221
221
|
await asyncio.sleep(0.5)
|
|
222
|
-
|
|
222
|
+
|
|
223
223
|
# Then close the task group if it's active
|
|
224
224
|
if self._task_group_active:
|
|
225
225
|
await self._task_group.__aexit__(exc_type, exc_val, exc_tb)
|
|
@@ -305,10 +305,12 @@ class MCPConnectionManager(ContextDependent):
|
|
|
305
305
|
server_conn = self.running_servers.get(server_name)
|
|
306
306
|
if server_conn and server_conn.is_healthy():
|
|
307
307
|
return server_conn
|
|
308
|
-
|
|
308
|
+
|
|
309
309
|
# If server exists but isn't healthy, remove it so we can create a new one
|
|
310
310
|
if server_conn:
|
|
311
|
-
logger.info(
|
|
311
|
+
logger.info(
|
|
312
|
+
f"{server_name}: Server exists but is unhealthy, recreating..."
|
|
313
|
+
)
|
|
312
314
|
self.running_servers.pop(server_name)
|
|
313
315
|
server_conn.request_shutdown()
|
|
314
316
|
|
|
@@ -326,9 +328,9 @@ class MCPConnectionManager(ContextDependent):
|
|
|
326
328
|
if not server_conn.is_healthy():
|
|
327
329
|
error_msg = server_conn._error_message or "Unknown error"
|
|
328
330
|
raise ServerInitializationError(
|
|
329
|
-
f"{server_name}: Failed to initialize
|
|
331
|
+
f"MCP Server: '{server_name}': Failed to initialize with error: '{error_msg}'. Check fastagent.config.yaml"
|
|
330
332
|
)
|
|
331
|
-
|
|
333
|
+
|
|
332
334
|
return server_conn
|
|
333
335
|
|
|
334
336
|
async def disconnect_server(self, server_name: str) -> None:
|
|
@@ -353,16 +355,16 @@ class MCPConnectionManager(ContextDependent):
|
|
|
353
355
|
"""Disconnect all servers that are running under this connection manager."""
|
|
354
356
|
# Get a copy of servers to shutdown
|
|
355
357
|
servers_to_shutdown = []
|
|
356
|
-
|
|
358
|
+
|
|
357
359
|
async with self._lock:
|
|
358
360
|
if not self.running_servers:
|
|
359
361
|
return
|
|
360
|
-
|
|
362
|
+
|
|
361
363
|
# Make a copy of the servers to shut down
|
|
362
364
|
servers_to_shutdown = list(self.running_servers.items())
|
|
363
365
|
# Clear the dict immediately to prevent any new access
|
|
364
366
|
self.running_servers.clear()
|
|
365
|
-
|
|
367
|
+
|
|
366
368
|
# Release the lock before waiting for servers to shut down
|
|
367
369
|
for name, conn in servers_to_shutdown:
|
|
368
370
|
logger.info(f"{name}: Requesting shutdown...")
|
|
@@ -23,7 +23,11 @@ async def main():
|
|
|
23
23
|
await agent.url_fetcher("http://llmindset.co.uk/resources/mcp-hfspace/")
|
|
24
24
|
)
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
|
|
27
|
+
# uncomment below to interact with agents
|
|
28
|
+
# await agent()
|
|
29
|
+
|
|
30
|
+
# alternative syntax for above is agent["social_media"].send(message)
|
|
27
31
|
|
|
28
32
|
|
|
29
33
|
if __name__ == "__main__":
|
|
@@ -9,7 +9,7 @@ from mcp_agent.core.fastagent import FastAgent
|
|
|
9
9
|
fast = FastAgent("Evaluator-Optimizer")
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
# Define
|
|
12
|
+
# Define generator agent
|
|
13
13
|
@fast.agent(
|
|
14
14
|
name="generator",
|
|
15
15
|
instruction="""You are a career coach specializing in cover letter writing.
|
|
@@ -44,7 +44,7 @@ fast = FastAgent("Evaluator-Optimizer")
|
|
|
44
44
|
# Define the evaluator-optimizer workflow
|
|
45
45
|
@fast.evaluator_optimizer(
|
|
46
46
|
name="cover_letter_writer",
|
|
47
|
-
generator="generator", # Reference to
|
|
47
|
+
generator="generator", # Reference to generator agent
|
|
48
48
|
evaluator="evaluator", # Reference to evaluator agent
|
|
49
49
|
min_rating="EXCELLENT", # Strive for excellence
|
|
50
50
|
max_refinements=3, # Maximum iterations
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Please edit this configuration file to match your environment (on Windows).
|
|
2
|
+
# Examples in comments below - check/change the paths.
|
|
3
|
+
#
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
execution_engine: asyncio
|
|
7
|
+
logger:
|
|
8
|
+
type: file
|
|
9
|
+
level: error
|
|
10
|
+
truncate_tools: true
|
|
11
|
+
|
|
12
|
+
mcp:
|
|
13
|
+
servers:
|
|
14
|
+
filesystem:
|
|
15
|
+
# On windows update the command and arguments to use `node` and the absolute path to the server.
|
|
16
|
+
# Use `npm i -g @modelcontextprotocol/server-filesystem` to install the server globally.
|
|
17
|
+
# Use `npm -g root` to find the global node_modules path.`
|
|
18
|
+
# command: "node"
|
|
19
|
+
# args: ["c:/Program Files/nodejs/node_modules/@modelcontextprotocol/server-filesystem/dist/index.js","."]
|
|
20
|
+
command: "npx"
|
|
21
|
+
args: ["-y", "@modelcontextprotocol/server-filesystem", "."]
|
|
22
|
+
fetch:
|
|
23
|
+
command: "uvx"
|
|
24
|
+
args: ["mcp-server-fetch"]
|
|
@@ -45,10 +45,11 @@ fast = FastAgent("Orchestrator-Workers")
|
|
|
45
45
|
@fast.orchestrator(
|
|
46
46
|
name="orchestrate",
|
|
47
47
|
agents=["finder", "writer", "proofreader"],
|
|
48
|
-
|
|
48
|
+
plan_type="iterative",
|
|
49
49
|
)
|
|
50
50
|
async def main():
|
|
51
51
|
async with fast.run() as agent:
|
|
52
|
+
|
|
52
53
|
await agent.author(
|
|
53
54
|
"write a 250 word short story about kittens discovering a castle, and save it to short_story.md"
|
|
54
55
|
)
|
|
@@ -65,7 +66,7 @@ async def main():
|
|
|
65
66
|
|
|
66
67
|
# Send the task
|
|
67
68
|
await agent.orchestrate(task)
|
|
68
|
-
|
|
69
|
+
await agent()
|
|
69
70
|
|
|
70
71
|
if __name__ == "__main__":
|
|
71
72
|
asyncio.run(main())
|
|
@@ -69,8 +69,9 @@ async def main():
|
|
|
69
69
|
# Use the app's context manager
|
|
70
70
|
async with fast.run() as agent:
|
|
71
71
|
await agent.parallel(f"student short story submission: {SHORT_STORY}")
|
|
72
|
+
|
|
72
73
|
# follow-on prompt to task agent
|
|
73
|
-
|
|
74
|
+
await agent.style_enforcer.prompt(default_prompt="STOP")
|
|
74
75
|
|
|
75
76
|
|
|
76
77
|
if __name__ == "__main__":
|
|
@@ -15,6 +15,7 @@ from mcp.types import (
|
|
|
15
15
|
)
|
|
16
16
|
|
|
17
17
|
from mcp_agent.context_dependent import ContextDependent
|
|
18
|
+
from mcp_agent.core.exceptions import PromptExitError
|
|
18
19
|
from mcp_agent.event_progress import ProgressAction
|
|
19
20
|
from mcp_agent.mcp.mcp_aggregator import MCPAggregator, SEP
|
|
20
21
|
from mcp_agent.workflows.llm.llm_selector import ModelSelector
|
|
@@ -608,6 +609,8 @@ class AugmentedLLM(ContextDependent, AugmentedLLMProtocol[MessageParamT, Message
|
|
|
608
609
|
result = postprocess
|
|
609
610
|
|
|
610
611
|
return result
|
|
612
|
+
except PromptExitError:
|
|
613
|
+
raise
|
|
611
614
|
except Exception as e:
|
|
612
615
|
return CallToolResult(
|
|
613
616
|
isError=True,
|
|
@@ -3,6 +3,7 @@ from enum import Enum, auto
|
|
|
3
3
|
from typing import Optional, Type, Dict, Union, Callable
|
|
4
4
|
|
|
5
5
|
from mcp_agent.agents.agent import Agent
|
|
6
|
+
from mcp_agent.core.exceptions import ModelConfigError
|
|
6
7
|
from mcp_agent.workflows.llm.augmented_llm_anthropic import AnthropicAugmentedLLM
|
|
7
8
|
from mcp_agent.workflows.llm.augmented_llm_openai import OpenAIAugmentedLLM
|
|
8
9
|
from mcp_agent.workflows.llm.augmented_llm import RequestParams
|
|
@@ -53,6 +54,7 @@ class ModelFactory:
|
|
|
53
54
|
|
|
54
55
|
# TODO -- add context window size information for display/mmanagement
|
|
55
56
|
# TODO -- add audio supporting got-4o-audio-preview
|
|
57
|
+
# TODO -- bring model parameter configuration here
|
|
56
58
|
# Mapping of model names to their default providers
|
|
57
59
|
DEFAULT_PROVIDERS = {
|
|
58
60
|
"gpt-4o": Provider.OPENAI,
|
|
@@ -80,6 +82,7 @@ class ModelFactory:
|
|
|
80
82
|
"claude": "claude-3-5-sonnet-latest",
|
|
81
83
|
"haiku": "claude-3-5-haiku-latest",
|
|
82
84
|
"haiku3": "claude-3-haiku-20240307",
|
|
85
|
+
"haiku35": "claude-3-5-haiku-latest",
|
|
83
86
|
"opus": "claude-3-opus-latest",
|
|
84
87
|
"opus3": "claude-3-opus-latest",
|
|
85
88
|
}
|
|
@@ -121,7 +124,7 @@ class ModelFactory:
|
|
|
121
124
|
if provider is None:
|
|
122
125
|
provider = cls.DEFAULT_PROVIDERS.get(model_name)
|
|
123
126
|
if provider is None:
|
|
124
|
-
raise
|
|
127
|
+
raise ModelConfigError(f"Unknown model: {model_name}")
|
|
125
128
|
|
|
126
129
|
return ModelConfig(
|
|
127
130
|
provider=provider, model_name=model_name, reasoning_effort=reasoning_effort
|
|
@@ -173,16 +176,16 @@ class ModelFactory:
|
|
|
173
176
|
"request_params": factory_params,
|
|
174
177
|
"name": kwargs.get("name"),
|
|
175
178
|
}
|
|
176
|
-
|
|
179
|
+
|
|
177
180
|
# Add reasoning effort if available
|
|
178
181
|
if config.reasoning_effort:
|
|
179
182
|
llm_args["reasoning_effort"] = config.reasoning_effort.value
|
|
180
|
-
|
|
183
|
+
|
|
181
184
|
# Forward all other kwargs (including verb)
|
|
182
185
|
for key, value in kwargs.items():
|
|
183
186
|
if key not in ["agent", "default_request_params", "name"]:
|
|
184
187
|
llm_args[key] = value
|
|
185
|
-
|
|
188
|
+
|
|
186
189
|
llm = llm_class(**llm_args)
|
|
187
190
|
return llm
|
|
188
191
|
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
|
|
3
|
-
from mcp_agent.core.fastagent import FastAgent
|
|
4
|
-
# from rich import print
|
|
5
|
-
|
|
6
|
-
agents = FastAgent(name="Researcher")
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@agents.agent(
|
|
10
|
-
"Researcher",
|
|
11
|
-
instruction="""
|
|
12
|
-
You are a research assistant, with access to internet search (via Brave),
|
|
13
|
-
website fetch, a python interpreter (you can install packages with uv) and a filesystem.
|
|
14
|
-
Use the current working directory to save and create files with both the Interpreter and Filesystem tools.
|
|
15
|
-
The interpreter has numpy, pandas, matplotlib and seaborn already installed
|
|
16
|
-
""",
|
|
17
|
-
servers=["brave", "interpreter", "filesystem", "fetch"],
|
|
18
|
-
)
|
|
19
|
-
async def main():
|
|
20
|
-
research_prompt = """
|
|
21
|
-
Produce an investment report for the company Eutelsat. The final report should be saved in the filesystem in markdown format, and
|
|
22
|
-
contain at least the following:
|
|
23
|
-
1 - A brief description of the company
|
|
24
|
-
2 - Current financial position (find data, create and incorporate charts)
|
|
25
|
-
3 - A PESTLE analysis
|
|
26
|
-
4 - An investment thesis for the next 3 years. Include both 'buy side' and 'sell side' arguments, and a final
|
|
27
|
-
summary and recommendation.
|
|
28
|
-
Todays date is 15 February 2025. Include the main data sources consulted in presenting the report.""" # noqa: F841
|
|
29
|
-
|
|
30
|
-
async with agents.run() as agent:
|
|
31
|
-
await agent.prompt()
|
|
32
|
-
|
|
33
|
-
# await agent.prompt(default="STOP")
|
|
34
|
-
# await agent.prompt(default=research_prompt)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if __name__ == "__main__":
|
|
38
|
-
asyncio.run(main())
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
from mcp_agent.core.fastagent import FastAgent
|
|
3
|
-
|
|
4
|
-
# Create the application
|
|
5
|
-
fast = FastAgent("FastAgent Example")
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
# Define the agent
|
|
9
|
-
@fast.agent(servers=["fetch"])
|
|
10
|
-
async def main():
|
|
11
|
-
# use the --model command line switch or agent arguments to change model
|
|
12
|
-
async with fast.run() as agent:
|
|
13
|
-
await agent()
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if __name__ == "__main__":
|
|
17
|
-
asyncio.run(main())
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
from mcp_agent.core.fastagent import FastAgent
|
|
3
|
-
|
|
4
|
-
# Create the application
|
|
5
|
-
agent_app = FastAgent("FastAgent Example")
|
|
6
|
-
# Uncomment the below to disable human input callback tool
|
|
7
|
-
# agent_app.app._human_input_callback = None
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
# Define the agent
|
|
11
|
-
@agent_app.agent(
|
|
12
|
-
instruction="You are a helpful AI Agent",
|
|
13
|
-
servers=[],
|
|
14
|
-
)
|
|
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())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|