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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fast-agent-mcp
3
- Version: 0.0.12
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=utMR_QWKD1_MqWE_fYY-xqUMKtGlekW0laJfduU6Ckw,9831
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=h_TqBlpIMGhsJr6pp_oxUl00x3F9d1R-JQVwJ9uAreA,2449
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=Q55I2gL-K3Ja8c6MmbLZMVQQ_MaOTxnEC5se09XTI2s,10742
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=dI_01B5nye707Rcd15gvZZCYlZGSiKajlnuLf6hJf2A,6197
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=U4hbAbm5WFUwTiwmBlNR7nadbMD9oHYgKuNNQVrGdvc,11047
21
- mcp_agent/core/exceptions.py,sha256=xDdhYh83ni3t0NiXQTEL0_Yyx0qQxBPQL1gSwRToeaw,1469
22
- mcp_agent/core/fastagent.py,sha256=Wn5uHR6DIzMXu4S93Ll8oFD7VJUcSBH4wMm4c0aofpw,48858
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=_gVIyvjDo53Aj8NFoKCiM8nBdQIuCxiStvgEtRBwYv8,1812
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=LH9ZmK-fXC-_7exAFclzWEjfFjwwdPqO_ZERqoHI_JM,13166
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=QD_r_PKIoDedWqOTzg7IBnTY8OVoDSMot5WnArJubnc,751
64
- mcp_agent/resources/examples/workflows/evaluator.py,sha256=kC8uBcCMoeDROip4B_X6jLr-1QXXvcUB0fZ6elun7k4,3147
65
- mcp_agent/resources/examples/workflows/fastagent.py,sha256=lkO3waYLt_zQtAVqGjirmIsG73jpHA5ad1WSm4BXv2I,532
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=kHUDDALqjA8TRjkbsDP2MwspEj1a5DdSUOPAiI17izQ,2545
68
- mcp_agent/resources/examples/workflows/parallel.py,sha256=cNYcIcsdo0-KK-S7KEPCc11aWELeVlQJdJ2LIC9xgDs,3090
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=AjYxTn2XdBDHnibmjlCKwaVfQQlQRES9sRBMIU6NaPQ,23258
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=5JrMXZ5jbE8isiteF2A912gGuCyomGpjtC_BCVSAM9s,6806
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.12.dist-info/METADATA,sha256=mgH0wJrtVRReqKTn2a-ANc_reC_quUXY-texT2zGdVI,16583
112
- fast_agent_mcp-0.0.12.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
113
- fast_agent_mcp-0.0.12.dist-info/entry_points.txt,sha256=2IXtSmDK9XjWN__RWuRIJTgWyW17wJnJ_h-pb0pZAxo,174
114
- fast_agent_mcp-0.0.12.dist-info/licenses/LICENSE,sha256=cN3FxDURL9XuzE5mhK9L2paZo82LTfjwCYVT7e3j0e4,10939
115
- fast_agent_mcp-0.0.12.dist-info/RECORD,,
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
- request = HumanInputRequest(**arguments.get("request"))
257
- result: HumanInputResponse = await self.request_human_input(request=request)
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: {result.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=[
@@ -26,6 +26,7 @@ EXAMPLE_TYPES = {
26
26
  "orchestrator.py",
27
27
  "parallel.py",
28
28
  "router.py",
29
+ "fastagent.config.yaml",
29
30
  ],
30
31
  "create_subdir": False,
31
32
  },
@@ -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 (decorator, researcher, etc.)"
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 decorator")
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__(self, agents: List[str], commands: List[str] = None, agent_types: dict = None):
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
- self.commands = commands or ["help", "clear", "STOP"]
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="Command",
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
- @kb.add("escape", "enter")
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
- """Alt+Enter: always submit even in multiline mode."""
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+&lt;Enter&gt;:Submit" if in_multiline_mode else "&lt;Enter&gt;: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" <b>Agent:</b> <ansiblue> {agent_name} </ansiblue> | <b>Mode:</b> <{mode_style}> {mode_text} </{mode_style}> | {shortcut_text}"
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=True,
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
- rich_print(
224
- "[dim]Tip: Type /help for commands, press F1 for keyboard shortcuts. Ctrl+T toggles multiline mode. @Agent to switch agent[/dim]"
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 - End session")
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(" Alt+Enter - Always submit (even in multiline mode)")
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]")
@@ -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)
@@ -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 List, Optional, Dict, Callable, TypeVar, Any, Union, TypeAlias
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, # Use same model as orchestrator
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="full", # TODO -- support iterative plan type properly
889
- verb=ProgressAction.PLANNING, # Using PLANNING instead of ORCHESTRATING
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(
@@ -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 rich panel and prompt."""
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
- if request.timeout_seconds:
36
- try:
37
- loop = asyncio.get_event_loop()
38
- response = await asyncio.wait_for(
39
- loop.run_in_executor(
40
- None, lambda: Prompt.ask("Provide your response ")
41
- ),
42
- request.timeout_seconds,
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
- except asyncio.TimeoutError:
45
- console.print("\n[red]Timeout waiting for input[/red]")
46
- raise TimeoutError("No response received within timeout period")
47
- else:
48
- loop = asyncio.get_event_loop()
49
- response = await loop.run_in_executor(
50
- None, lambda: Prompt.ask("Provide your response ")
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(f"{server_name}: Server exists but is unhealthy, recreating...")
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 server: {error_msg}"
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
- # alternative syntax for above is agent["social_media"].send(message)
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 optimizer agent
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 optimizer agent
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
- model="sonnet",
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
- # await agent.style_enforcer.prompt(default="STOP")
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 ValueError(f"Unknown model: {model_name}")
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())