fast-agent-mcp 0.0.11__tar.gz → 0.0.13__tar.gz

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.

Files changed (114) hide show
  1. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/.gitignore +1 -0
  2. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/PKG-INFO +9 -1
  3. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/README.md +7 -0
  4. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/pyproject.toml +2 -1
  5. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/agents/agent.py +48 -8
  6. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/cli/commands/bootstrap.py +2 -5
  7. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/cli/commands/setup.py +1 -1
  8. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/cli/main.py +6 -6
  9. fast_agent_mcp-0.0.13/src/mcp_agent/core/enhanced_prompt.py +358 -0
  10. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/core/exceptions.py +17 -0
  11. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/core/fastagent.py +108 -34
  12. fast_agent_mcp-0.0.13/src/mcp_agent/human_input/handler.py +78 -0
  13. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/mcp/mcp_connection_manager.py +14 -12
  14. fast_agent_mcp-0.0.13/src/mcp_agent/resources/examples/internal/agent.py +17 -0
  15. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/resources/examples/internal/job.py +1 -1
  16. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/resources/examples/mcp_researcher/researcher-eval.py +1 -1
  17. fast_agent_mcp-0.0.13/src/mcp_agent/resources/examples/researcher/fastagent.config.yaml +53 -0
  18. fast_agent_mcp-0.0.13/src/mcp_agent/resources/examples/researcher/researcher-eval.py +53 -0
  19. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/resources/examples/workflows/chaining.py +5 -1
  20. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/resources/examples/workflows/evaluator.py +7 -4
  21. fast_agent_mcp-0.0.13/src/mcp_agent/resources/examples/workflows/fastagent.config.yaml +24 -0
  22. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/resources/examples/workflows/orchestrator.py +3 -2
  23. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/resources/examples/workflows/parallel.py +2 -1
  24. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/evaluator_optimizer/evaluator_optimizer.py +31 -30
  25. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/llm/augmented_llm.py +8 -2
  26. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/llm/augmented_llm_anthropic.py +3 -1
  27. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/llm/augmented_llm_openai.py +20 -9
  28. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/llm/model_factory.py +7 -4
  29. fast_agent_mcp-0.0.11/src/mcp_agent/human_input/handler.py +0 -53
  30. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/LICENSE +0 -0
  31. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/__init__.py +0 -0
  32. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/agents/__init__.py +0 -0
  33. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/app.py +0 -0
  34. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/cli/__init__.py +0 -0
  35. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/cli/__main__.py +0 -0
  36. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/cli/commands/config.py +0 -0
  37. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/cli/terminal.py +0 -0
  38. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/config.py +0 -0
  39. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/console.py +0 -0
  40. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/context.py +0 -0
  41. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/context_dependent.py +0 -0
  42. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/core/__init__.py +0 -0
  43. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/core/server_validation.py +0 -0
  44. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/eval/__init__.py +0 -0
  45. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/event_progress.py +0 -0
  46. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/executor/__init__.py +0 -0
  47. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/executor/decorator_registry.py +0 -0
  48. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/executor/executor.py +0 -0
  49. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/executor/task_registry.py +0 -0
  50. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/executor/temporal.py +0 -0
  51. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/executor/workflow.py +0 -0
  52. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/executor/workflow_signal.py +0 -0
  53. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/human_input/__init__.py +0 -0
  54. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/human_input/types.py +0 -0
  55. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/logging/__init__.py +0 -0
  56. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/logging/events.py +0 -0
  57. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/logging/json_serializer.py +0 -0
  58. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/logging/listeners.py +0 -0
  59. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/logging/logger.py +0 -0
  60. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/logging/rich_progress.py +0 -0
  61. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/logging/tracing.py +0 -0
  62. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/logging/transport.py +0 -0
  63. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/mcp/__init__.py +0 -0
  64. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/mcp/gen_client.py +0 -0
  65. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/mcp/mcp_activity.py +0 -0
  66. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/mcp/mcp_agent_client_session.py +0 -0
  67. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/mcp/mcp_agent_server.py +0 -0
  68. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/mcp/mcp_aggregator.py +0 -0
  69. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/mcp/stdio.py +0 -0
  70. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/mcp_server_registry.py +0 -0
  71. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/progress_display.py +0 -0
  72. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/resources/examples/data-analysis/analysis.py +0 -0
  73. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/resources/examples/data-analysis/fastagent.config.yaml +0 -0
  74. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +0 -0
  75. {fast_agent_mcp-0.0.11/src/mcp_agent/resources/examples/mcp_researcher → fast_agent_mcp-0.0.13/src/mcp_agent/resources/examples/researcher}/researcher.py +0 -0
  76. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/resources/examples/workflows/agent_build.py +0 -0
  77. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/resources/examples/workflows/human_input.py +0 -0
  78. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/resources/examples/workflows/router.py +0 -0
  79. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/telemetry/__init__.py +0 -0
  80. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/telemetry/usage_tracking.py +0 -0
  81. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/__init__.py +0 -0
  82. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/embedding/__init__.py +0 -0
  83. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/embedding/embedding_base.py +0 -0
  84. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/embedding/embedding_cohere.py +0 -0
  85. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/embedding/embedding_openai.py +0 -0
  86. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/evaluator_optimizer/__init__.py +0 -0
  87. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/intent_classifier/__init__.py +0 -0
  88. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/intent_classifier/intent_classifier_base.py +0 -0
  89. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/intent_classifier/intent_classifier_embedding.py +0 -0
  90. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/intent_classifier/intent_classifier_embedding_cohere.py +0 -0
  91. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/intent_classifier/intent_classifier_embedding_openai.py +0 -0
  92. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/intent_classifier/intent_classifier_llm.py +0 -0
  93. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/intent_classifier/intent_classifier_llm_anthropic.py +0 -0
  94. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/intent_classifier/intent_classifier_llm_openai.py +0 -0
  95. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/llm/__init__.py +0 -0
  96. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/llm/llm_selector.py +0 -0
  97. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/orchestrator/__init__.py +0 -0
  98. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/orchestrator/orchestrator.py +0 -0
  99. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/orchestrator/orchestrator_models.py +0 -0
  100. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/orchestrator/orchestrator_prompts.py +0 -0
  101. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/parallel/__init__.py +0 -0
  102. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/parallel/fan_in.py +0 -0
  103. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/parallel/fan_out.py +0 -0
  104. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/parallel/parallel_llm.py +0 -0
  105. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/router/__init__.py +0 -0
  106. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/router/router_base.py +0 -0
  107. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/router/router_embedding.py +0 -0
  108. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/router/router_embedding_cohere.py +0 -0
  109. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/router/router_embedding_openai.py +0 -0
  110. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/router/router_llm.py +0 -0
  111. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/swarm/__init__.py +0 -0
  112. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/swarm/swarm.py +0 -0
  113. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/swarm/swarm_anthropic.py +0 -0
  114. {fast_agent_mcp-0.0.11 → fast_agent_mcp-0.0.13}/src/mcp_agent/workflows/swarm/swarm_openai.py +0 -0
@@ -179,3 +179,4 @@ examples/**/*.jsonl
179
179
  mcp_agent.config.yaml
180
180
  mcp_agent.secrets.yaml
181
181
  fastagent.secrets.yaml
182
+ CLAUDE.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fast-agent-mcp
3
- Version: 0.0.11
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
@@ -217,6 +217,7 @@ Requires-Dist: numpy>=2.2.1
217
217
  Requires-Dist: openai>=1.63.2
218
218
  Requires-Dist: opentelemetry-distro>=0.50b0
219
219
  Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.29.0
220
+ Requires-Dist: prompt-toolkit>=3.0.50
220
221
  Requires-Dist: pydantic-settings>=2.7.0
221
222
  Requires-Dist: pydantic>=2.10.4
222
223
  Requires-Dist: pyyaml>=6.0.2
@@ -268,9 +269,16 @@ Other bootstrap examples include a Researcher (with Evaluator-Optimizer workflow
268
269
 
269
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.
270
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
+
271
278
  ### llmindset.co.uk fork:
272
279
 
273
280
  - "FastAgent" style prototyping, with per-agent models
281
+ - Api keys through Environment Variables
274
282
  - Warm-up / Post-Workflow Agent Interactions
275
283
  - Quick Setup
276
284
  - Interactive Prompt Mode
@@ -32,9 +32,16 @@ Other bootstrap examples include a Researcher (with Evaluator-Optimizer workflow
32
32
 
33
33
  > 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.
34
34
 
35
+ ## Agent Development
36
+
37
+ FastAgent lets you interact with Agents during a workflow, enabling "warm-up" and diagnostic prompting to improve behaviour and refine prompts.
38
+
39
+ ## MCP Server Development
40
+
35
41
  ### llmindset.co.uk fork:
36
42
 
37
43
  - "FastAgent" style prototyping, with per-agent models
44
+ - Api keys through Environment Variables
38
45
  - Warm-up / Post-Workflow Agent Interactions
39
46
  - Quick Setup
40
47
  - Interactive Prompt Mode
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fast-agent-mcp"
3
- version = "0.0.11"
3
+ version = "0.0.13"
4
4
  description = "Define, Prompt and Test MCP enabled Agents and Workflows"
5
5
  readme = "README.md"
6
6
  license = { file = "LICENSE" }
@@ -29,6 +29,7 @@ dependencies = [
29
29
  "scikit-learn>=1.6.0",
30
30
  "anthropic>=0.42.0",
31
31
  "openai>=1.63.2",
32
+ "prompt-toolkit>=3.0.50",
32
33
  ]
33
34
 
34
35
  [project.optional-dependencies]
@@ -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
  },
@@ -33,11 +34,7 @@ EXAMPLE_TYPES = {
33
34
  "description": "Research agent example with additional evaluation/optimization\n"
34
35
  "example. Uses Brave Search and Docker MCP Servers.\n"
35
36
  "Creates examples in a 'researcher' subdirectory.",
36
- "files": [
37
- "researcher.py",
38
- "researcher-eval.py",
39
- "mcp_agent.secrets.yaml.example",
40
- ],
37
+ "files": ["researcher.py", "researcher-eval.py", "fastagent.config.yaml"],
41
38
  "create_subdir": True,
42
39
  },
43
40
  "data-analysis": {
@@ -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:
@@ -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
 
@@ -40,11 +40,11 @@ def show_welcome():
40
40
 
41
41
  console.print("\n[bold]Getting Started:[/bold]")
42
42
  console.print("1. Set up a new project:")
43
- console.print(" mcp-agent setup")
44
- console.print("\n2. Try an example:")
45
- console.print(" mcp-agent bootstrap create decorator")
43
+ console.print(" fastagent setup")
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
- console.print("Example: mcp-agent bootstrap --help")
47
+ console.print("Example: fastagent bootstrap --help")
48
48
 
49
49
 
50
50
  @app.callback(invoke_without_command=True)
@@ -56,7 +56,7 @@ def main(
56
56
  True, "--color/--no-color", help="Enable/disable color output"
57
57
  ),
58
58
  ):
59
- """MCP Agent CLI - Build effective agents using Model Context Protocol (MCP).
59
+ """FastAgent CLI - Build effective agents using Model Context Protocol (MCP).
60
60
 
61
61
  Use --help with any command for detailed usage information.
62
62
  """
@@ -0,0 +1,358 @@
1
+ """
2
+ Enhanced prompt functionality with advanced prompt_toolkit features.
3
+ """
4
+
5
+ from typing import List
6
+ from prompt_toolkit import PromptSession
7
+ from prompt_toolkit.formatted_text import HTML
8
+ from prompt_toolkit.history import InMemoryHistory
9
+ from prompt_toolkit.key_binding import KeyBindings
10
+ from prompt_toolkit.completion import Completer, Completion
11
+ from prompt_toolkit.lexers import PygmentsLexer
12
+ from prompt_toolkit.filters import Condition
13
+ from pygments.lexers.python import PythonLexer
14
+ from rich import print as rich_print
15
+
16
+ from mcp_agent.core.exceptions import PromptExitError
17
+
18
+ # Map of agent names to their history
19
+ agent_histories = {}
20
+
21
+ # Store available agents for auto-completion
22
+ available_agents = set()
23
+
24
+ # Keep track of multi-line mode state
25
+ in_multiline_mode = False
26
+
27
+ # Track which agents have already shown welcome messages
28
+ agent_messages_shown = set()
29
+
30
+
31
+ class AgentCompleter(Completer):
32
+ """Provide completion for agent names and common commands."""
33
+
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
+ ):
41
+ self.agents = agents
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")
53
+ self.agent_types = agent_types or {}
54
+
55
+ def get_completions(self, document, complete_event):
56
+ """Synchronous completions method - this is what prompt_toolkit expects by default"""
57
+ text = document.text_before_cursor.lower()
58
+
59
+ # Complete commands
60
+ if text.startswith("/"):
61
+ cmd = text[1:]
62
+ for command, description in self.commands.items():
63
+ if command.lower().startswith(cmd):
64
+ yield Completion(
65
+ command,
66
+ start_position=-len(cmd),
67
+ display=command,
68
+ display_meta=description,
69
+ )
70
+
71
+ # Complete agent names for agent-related commands
72
+ elif text.startswith("@"):
73
+ agent_name = text[1:]
74
+ for agent in self.agents:
75
+ if agent.lower().startswith(agent_name.lower()):
76
+ # Get agent type or default to "Agent"
77
+ agent_type = self.agent_types.get(agent, "Agent")
78
+ yield Completion(
79
+ agent,
80
+ start_position=-len(agent_name),
81
+ display=agent,
82
+ display_meta=agent_type,
83
+ style="bg:ansiblack fg:ansiblue",
84
+ )
85
+
86
+
87
+ def create_keybindings(on_toggle_multiline=None, app=None):
88
+ """Create custom key bindings."""
89
+ kb = KeyBindings()
90
+
91
+ @kb.add("c-m", filter=Condition(lambda: not in_multiline_mode))
92
+ def _(event):
93
+ """Enter: accept input when not in multiline mode."""
94
+ event.current_buffer.validate_and_handle()
95
+
96
+ @kb.add("c-m", filter=Condition(lambda: in_multiline_mode))
97
+ def _(event):
98
+ """Enter: insert newline when in multiline mode."""
99
+ event.current_buffer.insert_text("\n")
100
+
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))
103
+ def _(event):
104
+ """Ctrl+J (equivalent to Ctrl+Enter): Submit in multiline mode."""
105
+ event.current_buffer.validate_and_handle()
106
+
107
+ @kb.add("c-t")
108
+ def _(event):
109
+ """Ctrl+T: Toggle multiline mode."""
110
+ global in_multiline_mode
111
+ in_multiline_mode = not in_multiline_mode
112
+
113
+ # Force redraw the app to update toolbar
114
+ if event.app:
115
+ event.app.invalidate()
116
+ elif app:
117
+ app.invalidate()
118
+
119
+ # Call the toggle callback if provided
120
+ if on_toggle_multiline:
121
+ on_toggle_multiline(in_multiline_mode)
122
+
123
+ # Instead of printing, we'll just update the toolbar
124
+ # The toolbar will show the current mode
125
+
126
+ @kb.add("c-l")
127
+ def _(event):
128
+ """Ctrl+L: Clear the input buffer."""
129
+ event.current_buffer.text = ""
130
+
131
+ return kb
132
+
133
+
134
+ async def get_enhanced_input(
135
+ agent_name: str,
136
+ default: str = "",
137
+ show_default: bool = False,
138
+ show_stop_hint: bool = False,
139
+ multiline: bool = False,
140
+ available_agent_names: List[str] = None,
141
+ syntax: str = None,
142
+ agent_types: dict = None,
143
+ is_human_input: bool = False,
144
+ toolbar_color: str = "ansiblue",
145
+ ) -> str:
146
+ """
147
+ Enhanced input with advanced prompt_toolkit features.
148
+
149
+ Args:
150
+ agent_name: Name of the agent (used for prompt and history)
151
+ default: Default value if user presses enter
152
+ show_default: Whether to show the default value in the prompt
153
+ show_stop_hint: Whether to show the STOP hint
154
+ multiline: Start in multiline mode
155
+ available_agent_names: List of agent names for auto-completion
156
+ syntax: Syntax highlighting (e.g., 'python', 'sql')
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")
160
+
161
+ Returns:
162
+ User input string
163
+ """
164
+ global in_multiline_mode, available_agents
165
+
166
+ # Update global state
167
+ in_multiline_mode = multiline
168
+ if available_agent_names:
169
+ available_agents = set(available_agent_names)
170
+
171
+ # Get or create history object for this agent
172
+ if agent_name not in agent_histories:
173
+ agent_histories[agent_name] = InMemoryHistory()
174
+
175
+ # Define callback for multiline toggle
176
+ def on_multiline_toggle(enabled):
177
+ nonlocal session
178
+ if hasattr(session, "app") and session.app:
179
+ session.app.invalidate()
180
+
181
+ # Define toolbar function that will update dynamically
182
+ def get_toolbar():
183
+ if in_multiline_mode:
184
+ mode_style = "ansired" # More noticeable for multiline mode
185
+ mode_text = "MULTILINE"
186
+ toggle_text = "Normal Editing"
187
+ else:
188
+ mode_style = "ansigreen"
189
+ mode_text = "NORMAL"
190
+ toggle_text = "Multiline Editing"
191
+
192
+ shortcuts = [
193
+ ("Ctrl+T", toggle_text),
194
+ ("Ctrl+L", "Clear"),
195
+ ("↑/↓", "History"),
196
+ ]
197
+
198
+ newline = (
199
+ "Ctrl+&lt;Enter&gt;:Submit" if in_multiline_mode else "&lt;Enter&gt;:Submit"
200
+ )
201
+
202
+ # Only show relevant shortcuts based on mode
203
+ shortcuts = [(k, v) for k, v in shortcuts if v]
204
+
205
+ shortcut_text = " | ".join(f"{key}:{action}" for key, action in shortcuts)
206
+ return HTML(
207
+ f" <{toolbar_color}> {agent_name} </{toolbar_color}> | <b>Mode:</b> <{mode_style}> {mode_text} </{mode_style}> {newline} | {shortcut_text}"
208
+ )
209
+
210
+ # Create session with history and completions
211
+ session = PromptSession(
212
+ history=agent_histories[agent_name],
213
+ completer=AgentCompleter(
214
+ agents=list(available_agents) if available_agents else [],
215
+ agent_types=agent_types or {},
216
+ is_human_input=is_human_input,
217
+ ),
218
+ complete_while_typing=True,
219
+ lexer=PygmentsLexer(PythonLexer) if syntax == "python" else None,
220
+ multiline=Condition(lambda: in_multiline_mode),
221
+ complete_in_thread=True,
222
+ mouse_support=False,
223
+ bottom_toolbar=get_toolbar, # Pass the function here
224
+ )
225
+
226
+ # Create key bindings with a reference to the app
227
+ bindings = create_keybindings(
228
+ on_toggle_multiline=on_multiline_toggle, app=session.app
229
+ )
230
+ session.app.key_bindings = bindings
231
+
232
+ # Create formatted prompt text
233
+ prompt_text = f"<ansicyan>{agent_name}</ansicyan> > "
234
+
235
+ # Add default value display if requested
236
+ if show_default and default and default != "STOP":
237
+ prompt_text = f"{prompt_text} [<ansigreen>{default}</ansigreen>] "
238
+
239
+ # Only show hints at startup if requested
240
+ if show_stop_hint:
241
+ if default == "STOP":
242
+ rich_print("[yellow]Press <ENTER> to finish.[/yellow]")
243
+ else:
244
+ rich_print("Enter a prompt, or [red]STOP[/red] to finish")
245
+ if default:
246
+ rich_print(
247
+ f"Press <ENTER> to use the default prompt:\n[cyan]{default}[/cyan]"
248
+ )
249
+
250
+ # Mention available features but only on first usage for this agent
251
+ if agent_name not in agent_messages_shown:
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
+ )
260
+ agent_messages_shown.add(agent_name)
261
+
262
+ # Process special commands
263
+ def pre_process_input(text):
264
+ # Command processing
265
+ if text and text.startswith("/"):
266
+ cmd = text[1:].strip().lower()
267
+ if cmd == "help":
268
+ return "HELP"
269
+ elif cmd == "clear":
270
+ return "CLEAR"
271
+ elif cmd == "agents":
272
+ return "LIST_AGENTS"
273
+ elif cmd == "exit":
274
+ return "EXIT"
275
+ elif cmd == "stop":
276
+ return "STOP"
277
+
278
+ # Agent switching
279
+ if text and text.startswith("@"):
280
+ return f"SWITCH:{text[1:].strip()}"
281
+
282
+ return text
283
+
284
+ # Get the input - using async version
285
+ try:
286
+ result = await session.prompt_async(HTML(prompt_text), default=default)
287
+ return pre_process_input(result)
288
+ except KeyboardInterrupt:
289
+ # Handle Ctrl+C gracefully
290
+ return "STOP"
291
+ except EOFError:
292
+ # Handle Ctrl+D gracefully
293
+ return "STOP"
294
+ except Exception as e:
295
+ # Log and gracefully handle other exceptions
296
+ print(f"\nInput error: {type(e).__name__}: {e}")
297
+ return "STOP"
298
+
299
+
300
+ async def handle_special_commands(command, agent_app=None):
301
+ """Handle special input commands."""
302
+ # Quick guard for empty or None commands
303
+ if not command:
304
+ return False
305
+
306
+ # Check for special commands
307
+ if command == "HELP":
308
+ rich_print("\n[bold]Available Commands:[/bold]")
309
+ rich_print(" /help - Show this help")
310
+ rich_print(" /clear - Clear screen")
311
+ rich_print(" /agents - List available agents")
312
+ rich_print(" @agent_name - Switch to agent")
313
+ rich_print(" STOP - Return control back to the workflow")
314
+ rich_print(
315
+ " EXIT - Exit FastAgent, terminating any running workflows"
316
+ )
317
+ rich_print("\n[bold]Keyboard Shortcuts:[/bold]")
318
+ rich_print(
319
+ " Enter - Submit (normal mode) / New line (multiline mode)"
320
+ )
321
+ rich_print(" Ctrl+Enter - Always submit (even in multiline mode)")
322
+ rich_print(" Ctrl+T - Toggle multiline mode")
323
+ rich_print(" Ctrl+L - Clear input")
324
+ rich_print(" Up/Down - Navigate history")
325
+ return True
326
+
327
+ elif command == "CLEAR":
328
+ # Clear screen (ANSI escape sequence)
329
+ print("\033c", end="")
330
+ return True
331
+
332
+ elif command == "EXIT":
333
+ raise PromptExitError("User requested to exit FastAgent session")
334
+
335
+ elif command == "LIST_AGENTS":
336
+ if available_agents:
337
+ rich_print("\n[bold]Available Agents:[/bold]")
338
+ for agent in sorted(available_agents):
339
+ rich_print(f" @{agent}")
340
+ else:
341
+ rich_print("[yellow]No agents available[/yellow]")
342
+ return True
343
+
344
+ elif isinstance(command, str) and command.startswith("SWITCH:"):
345
+ agent_name = command.split(":", 1)[1]
346
+ if agent_name in available_agents:
347
+ if agent_app:
348
+ rich_print(f"[green]Switching to agent: {agent_name}[/green]")
349
+ return {"switch_agent": agent_name}
350
+ else:
351
+ rich_print(
352
+ "[yellow]Agent switching not available in this context[/yellow]"
353
+ )
354
+ else:
355
+ rich_print(f"[red]Unknown agent: {agent_name}[/red]")
356
+ return True
357
+
358
+ return False
@@ -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)