fast-agent-mcp 0.1.4__tar.gz → 0.1.5__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.
Files changed (139) hide show
  1. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/PKG-INFO +5 -1
  2. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/README.md +4 -0
  3. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/pyproject.toml +1 -1
  4. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/agents/agent.py +46 -0
  5. fast_agent_mcp-0.1.5/src/mcp_agent/core/agent_app.py +527 -0
  6. fast_agent_mcp-0.1.5/src/mcp_agent/core/decorators.py +455 -0
  7. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/core/enhanced_prompt.py +70 -4
  8. fast_agent_mcp-0.1.5/src/mcp_agent/core/factory.py +501 -0
  9. fast_agent_mcp-0.1.5/src/mcp_agent/core/fastagent.py +556 -0
  10. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/core/proxies.py +51 -11
  11. fast_agent_mcp-0.1.5/src/mcp_agent/core/validation.py +221 -0
  12. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/human_input/handler.py +5 -2
  13. fast_agent_mcp-0.1.5/src/mcp_agent/mcp/mcp_aggregator.py +885 -0
  14. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/mcp/mcp_connection_manager.py +13 -2
  15. fast_agent_mcp-0.1.5/src/mcp_agent/mcp_server/__init__.py +4 -0
  16. fast_agent_mcp-0.1.5/src/mcp_agent/mcp_server/agent_server.py +121 -0
  17. fast_agent_mcp-0.1.5/src/mcp_agent/resources/examples/internal/fastagent.config.yaml +52 -0
  18. fast_agent_mcp-0.1.5/src/mcp_agent/resources/examples/internal/prompt_category.py +21 -0
  19. fast_agent_mcp-0.1.5/src/mcp_agent/resources/examples/internal/prompt_sizing.py +53 -0
  20. fast_agent_mcp-0.1.5/src/mcp_agent/resources/examples/internal/sizer.py +24 -0
  21. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/resources/examples/researcher/fastagent.config.yaml +14 -1
  22. fast_agent_mcp-0.1.5/src/mcp_agent/resources/examples/workflows/sse.py +23 -0
  23. fast_agent_mcp-0.1.5/src/mcp_agent/ui/console_display.py +278 -0
  24. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/llm/augmented_llm.py +245 -179
  25. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/llm/augmented_llm_anthropic.py +49 -3
  26. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/llm/augmented_llm_openai.py +52 -4
  27. fast_agent_mcp-0.1.4/src/mcp_agent/core/agent_app.py +0 -163
  28. fast_agent_mcp-0.1.4/src/mcp_agent/core/fastagent.py +0 -1475
  29. fast_agent_mcp-0.1.4/src/mcp_agent/mcp/mcp_aggregator.py +0 -395
  30. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/.gitignore +0 -0
  31. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/LICENSE +0 -0
  32. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/__init__.py +0 -0
  33. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/agents/__init__.py +0 -0
  34. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/app.py +0 -0
  35. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/cli/__init__.py +0 -0
  36. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/cli/__main__.py +0 -0
  37. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/cli/commands/bootstrap.py +0 -0
  38. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/cli/commands/config.py +0 -0
  39. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/cli/commands/setup.py +0 -0
  40. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/cli/main.py +0 -0
  41. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/cli/terminal.py +0 -0
  42. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/config.py +0 -0
  43. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/console.py +0 -0
  44. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/context.py +0 -0
  45. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/context_dependent.py +0 -0
  46. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/core/__init__.py +0 -0
  47. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/core/agent_types.py +0 -0
  48. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/core/agent_utils.py +0 -0
  49. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/core/error_handling.py +0 -0
  50. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/core/exceptions.py +0 -0
  51. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/core/server_validation.py +0 -0
  52. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/core/simulator_registry.py +0 -0
  53. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/core/types.py +0 -0
  54. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/eval/__init__.py +0 -0
  55. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/event_progress.py +0 -0
  56. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/executor/__init__.py +0 -0
  57. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/executor/decorator_registry.py +0 -0
  58. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/executor/executor.py +0 -0
  59. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/executor/task_registry.py +0 -0
  60. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/executor/temporal.py +0 -0
  61. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/executor/workflow.py +0 -0
  62. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/executor/workflow_signal.py +0 -0
  63. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/human_input/__init__.py +0 -0
  64. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/human_input/types.py +0 -0
  65. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/logging/__init__.py +0 -0
  66. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/logging/events.py +0 -0
  67. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/logging/json_serializer.py +0 -0
  68. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/logging/listeners.py +0 -0
  69. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/logging/logger.py +0 -0
  70. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/logging/rich_progress.py +0 -0
  71. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/logging/tracing.py +0 -0
  72. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/logging/transport.py +0 -0
  73. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/mcp/__init__.py +0 -0
  74. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/mcp/gen_client.py +0 -0
  75. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/mcp/mcp_activity.py +0 -0
  76. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/mcp/mcp_agent_client_session.py +0 -0
  77. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/mcp/mcp_agent_server.py +0 -0
  78. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/mcp/stdio.py +0 -0
  79. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/mcp_server_registry.py +0 -0
  80. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/progress_display.py +0 -0
  81. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/resources/examples/data-analysis/analysis-campaign.py +0 -0
  82. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/resources/examples/data-analysis/analysis.py +0 -0
  83. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/resources/examples/data-analysis/fastagent.config.yaml +0 -0
  84. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +0 -0
  85. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/resources/examples/internal/agent.py +0 -0
  86. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/resources/examples/internal/job.py +0 -0
  87. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/resources/examples/internal/social.py +0 -0
  88. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/resources/examples/mcp_researcher/researcher-eval.py +0 -0
  89. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/resources/examples/researcher/researcher-eval.py +0 -0
  90. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/resources/examples/researcher/researcher-imp.py +0 -0
  91. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/resources/examples/researcher/researcher.py +0 -0
  92. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/resources/examples/workflows/agent_build.py +0 -0
  93. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/resources/examples/workflows/chaining.py +0 -0
  94. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/resources/examples/workflows/evaluator.py +0 -0
  95. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/resources/examples/workflows/fastagent.config.yaml +0 -0
  96. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/resources/examples/workflows/human_input.py +0 -0
  97. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/resources/examples/workflows/orchestrator.py +0 -0
  98. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/resources/examples/workflows/parallel.py +0 -0
  99. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/resources/examples/workflows/router.py +0 -0
  100. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/telemetry/__init__.py +0 -0
  101. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/telemetry/usage_tracking.py +0 -0
  102. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/__init__.py +0 -0
  103. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/embedding/__init__.py +0 -0
  104. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/embedding/embedding_base.py +0 -0
  105. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/embedding/embedding_cohere.py +0 -0
  106. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/embedding/embedding_openai.py +0 -0
  107. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/evaluator_optimizer/__init__.py +0 -0
  108. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/evaluator_optimizer/evaluator_optimizer.py +0 -0
  109. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/intent_classifier/__init__.py +0 -0
  110. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/intent_classifier/intent_classifier_base.py +0 -0
  111. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/intent_classifier/intent_classifier_embedding.py +0 -0
  112. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/intent_classifier/intent_classifier_embedding_cohere.py +0 -0
  113. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/intent_classifier/intent_classifier_embedding_openai.py +0 -0
  114. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/intent_classifier/intent_classifier_llm.py +0 -0
  115. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/intent_classifier/intent_classifier_llm_anthropic.py +0 -0
  116. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/intent_classifier/intent_classifier_llm_openai.py +0 -0
  117. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/llm/__init__.py +0 -0
  118. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/llm/enhanced_passthrough.py +0 -0
  119. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/llm/llm_selector.py +0 -0
  120. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/llm/model_factory.py +0 -0
  121. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/llm/prompt_utils.py +0 -0
  122. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/orchestrator/__init__.py +0 -0
  123. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/orchestrator/orchestrator.py +0 -0
  124. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/orchestrator/orchestrator_models.py +0 -0
  125. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/orchestrator/orchestrator_prompts.py +0 -0
  126. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/parallel/__init__.py +0 -0
  127. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/parallel/fan_in.py +0 -0
  128. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/parallel/fan_out.py +0 -0
  129. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/parallel/parallel_llm.py +0 -0
  130. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/router/__init__.py +0 -0
  131. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/router/router_base.py +0 -0
  132. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/router/router_embedding.py +0 -0
  133. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/router/router_embedding_cohere.py +0 -0
  134. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/router/router_embedding_openai.py +0 -0
  135. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/router/router_llm.py +0 -0
  136. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/swarm/__init__.py +0 -0
  137. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/swarm/swarm.py +0 -0
  138. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/swarm/swarm_anthropic.py +0 -0
  139. {fast_agent_mcp-0.1.4 → fast_agent_mcp-0.1.5}/src/mcp_agent/workflows/swarm/swarm_openai.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fast-agent-mcp
3
- Version: 0.1.4
3
+ Version: 0.1.5
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
@@ -519,6 +519,10 @@ agent["greeter"].send("Good Evening!") # Dictionary access is supported
519
519
  )
520
520
  ```
521
521
 
522
+ ### Prompts
523
+
524
+ MCP Prompts are supported with `apply_prompt(name)`. Prompts can be
525
+
522
526
  #### Chain
523
527
 
524
528
  ```python
@@ -282,6 +282,10 @@ agent["greeter"].send("Good Evening!") # Dictionary access is supported
282
282
  )
283
283
  ```
284
284
 
285
+ ### Prompts
286
+
287
+ MCP Prompts are supported with `apply_prompt(name)`. Prompts can be
288
+
285
289
  #### Chain
286
290
 
287
291
  ```python
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fast-agent-mcp"
3
- version = "0.1.4"
3
+ version = "0.1.5"
4
4
  description = "Define, Prompt and Test MCP enabled Agents and Workflows"
5
5
  readme = "README.md"
6
6
  license = { file = "LICENSE" }
@@ -319,3 +319,49 @@ class Agent(MCPAggregator):
319
319
  )
320
320
  ],
321
321
  )
322
+
323
+ async def apply_prompt(self, prompt_name: str, arguments: dict[str, str] = None) -> str:
324
+ """
325
+ Apply an MCP Server Prompt by name and return the assistant's response.
326
+ Will search all available servers for the prompt if not namespaced.
327
+
328
+ If the last message in the prompt is from a user, this will automatically
329
+ generate an assistant response to ensure we always end with an assistant message.
330
+
331
+ Args:
332
+ prompt_name: The name of the prompt to apply
333
+ arguments: Optional dictionary of string arguments to pass to the prompt template
334
+
335
+ Returns:
336
+ The assistant's response or error message
337
+ """
338
+ # If we don't have an LLM, we can't apply the prompt
339
+ if not hasattr(self, "_llm") or not self._llm:
340
+ raise RuntimeError("Agent has no attached LLM")
341
+
342
+ # Get the prompt - this will search all servers if needed
343
+ self.logger.debug(f"Loading prompt '{prompt_name}'")
344
+ prompt_result = await self.get_prompt(prompt_name, arguments)
345
+
346
+ if not prompt_result or not prompt_result.messages:
347
+ error_msg = (
348
+ f"Prompt '{prompt_name}' could not be found or contains no messages"
349
+ )
350
+ self.logger.warning(error_msg)
351
+ return error_msg
352
+
353
+ # Get the display name (namespaced version)
354
+ display_name = getattr(prompt_result, "namespaced_name", prompt_name)
355
+
356
+ # Apply the prompt template and get the result
357
+ # The LLM will automatically generate a response if needed
358
+ result = await self._llm.apply_prompt_template(prompt_result, display_name)
359
+ return result
360
+
361
+ # For backward compatibility
362
+ async def load_prompt(self, prompt_name: str, arguments: dict[str, str] = None) -> str:
363
+ """
364
+ Legacy method - use apply_prompt instead.
365
+ This is maintained for backward compatibility.
366
+ """
367
+ return await self.apply_prompt(prompt_name, arguments)
@@ -0,0 +1,527 @@
1
+ """
2
+ Main application wrapper for interacting with agents.
3
+ """
4
+
5
+ from typing import Optional, Dict, TYPE_CHECKING
6
+
7
+ from mcp_agent.app import MCPApp
8
+ from mcp_agent.progress_display import progress_display
9
+ from mcp_agent.workflows.orchestrator.orchestrator import Orchestrator
10
+ from mcp_agent.workflows.parallel.parallel_llm import ParallelLLM
11
+ from mcp_agent.workflows.evaluator_optimizer.evaluator_optimizer import (
12
+ EvaluatorOptimizerLLM,
13
+ )
14
+
15
+ # Import proxies directly - they handle their own circular imports
16
+ from mcp_agent.core.proxies import (
17
+ BaseAgentProxy,
18
+ LLMAgentProxy,
19
+ RouterProxy,
20
+ ChainProxy,
21
+ WorkflowProxy,
22
+ )
23
+
24
+ # Handle possible circular imports with types
25
+ if TYPE_CHECKING:
26
+ from mcp_agent.core.types import ProxyDict
27
+ else:
28
+ ProxyDict = Dict[str, BaseAgentProxy]
29
+
30
+
31
+ class AgentApp:
32
+ """Main application wrapper"""
33
+
34
+ def __init__(self, app: MCPApp, agents: ProxyDict):
35
+ self._app = app
36
+ self._agents = agents
37
+ # Optional: set default agent for direct calls
38
+ self._default = next(iter(agents)) if agents else None
39
+
40
+ async def send(self, agent_name: str, message: Optional[str]) -> str:
41
+ """Core message sending"""
42
+ if agent_name not in self._agents:
43
+ raise ValueError(f"No agent named '{agent_name}'")
44
+
45
+ if not message or "" == message:
46
+ return await self.prompt(agent_name)
47
+
48
+ proxy = self._agents[agent_name]
49
+ return await proxy.generate_str(message)
50
+
51
+ async def prompt(self, agent_name: Optional[str] = None, default: str = "") -> str:
52
+ """
53
+ Interactive prompt for sending messages with advanced features.
54
+
55
+ Args:
56
+ agent_name: Optional target agent name (uses default if not specified)
57
+ default: Default message to use when user presses enter
58
+ """
59
+ from mcp_agent.core.enhanced_prompt import (
60
+ get_enhanced_input,
61
+ handle_special_commands,
62
+ )
63
+
64
+ agent = agent_name or self._default
65
+
66
+ if agent not in self._agents:
67
+ raise ValueError(f"No agent named '{agent}'")
68
+
69
+ # Pass all available agent names for auto-completion
70
+ available_agents = list(self._agents.keys())
71
+
72
+ # Create agent_types dictionary mapping agent names to their types
73
+ agent_types = {}
74
+ for name, proxy in self._agents.items():
75
+ # Determine agent type based on the proxy type
76
+ if isinstance(proxy, LLMAgentProxy):
77
+ # Convert AgentType.BASIC.value ("agent") to "Agent"
78
+ agent_types[name] = "Agent"
79
+ elif isinstance(proxy, RouterProxy):
80
+ agent_types[name] = "Router"
81
+ elif isinstance(proxy, ChainProxy):
82
+ agent_types[name] = "Chain"
83
+ elif isinstance(proxy, WorkflowProxy):
84
+ # For workflow proxies, check the workflow type
85
+ workflow = proxy._workflow
86
+ if isinstance(workflow, Orchestrator):
87
+ agent_types[name] = "Orchestrator"
88
+ elif isinstance(workflow, ParallelLLM):
89
+ agent_types[name] = "Parallel"
90
+ elif isinstance(workflow, EvaluatorOptimizerLLM):
91
+ agent_types[name] = "Evaluator"
92
+ else:
93
+ agent_types[name] = "Workflow"
94
+
95
+ result = ""
96
+ while True:
97
+ with progress_display.paused():
98
+ # Use the enhanced input method with advanced features
99
+ user_input = await get_enhanced_input(
100
+ agent_name=agent,
101
+ default=default,
102
+ show_default=(default != ""),
103
+ show_stop_hint=True,
104
+ multiline=False, # Default to single-line mode
105
+ available_agent_names=available_agents,
106
+ syntax=None, # Can enable syntax highlighting for code input
107
+ agent_types=agent_types, # Pass agent types for display
108
+ )
109
+
110
+ # Handle special commands
111
+ command_result = await handle_special_commands(user_input, self)
112
+
113
+ # Check if we should switch agents
114
+ if isinstance(command_result, dict):
115
+ if "switch_agent" in command_result:
116
+ agent = command_result["switch_agent"]
117
+ continue
118
+ elif "list_prompts" in command_result:
119
+ # Handle listing of prompts
120
+ from rich import print as rich_print
121
+
122
+ try:
123
+ # Check if we have any agents with aggregator capabilities
124
+ found_prompts = False
125
+ for agent_name, agent_proxy in self._agents.items():
126
+ # Check if agent has an mcp_aggregator (agent instance)
127
+ if hasattr(agent_proxy, "_agent") and hasattr(
128
+ agent_proxy._agent, "list_prompts"
129
+ ):
130
+ rich_print(
131
+ f"\n[bold]Fetching prompts for agent [cyan]{agent_name}[/cyan]...[/bold]"
132
+ )
133
+ prompt_servers = (
134
+ await agent_proxy._agent.list_prompts()
135
+ )
136
+
137
+ if prompt_servers:
138
+ found_prompts = True
139
+ for (
140
+ server_name,
141
+ prompts_info,
142
+ ) in prompt_servers.items():
143
+ if (
144
+ prompts_info
145
+ and hasattr(prompts_info, "prompts")
146
+ and prompts_info.prompts
147
+ ):
148
+ rich_print(
149
+ f"\n[bold cyan]{server_name}:[/bold cyan]"
150
+ )
151
+ for prompt in prompts_info.prompts:
152
+ rich_print(f" {prompt.name}")
153
+ elif (
154
+ isinstance(prompts_info, list)
155
+ and prompts_info
156
+ ):
157
+ rich_print(
158
+ f"\n[bold cyan]{server_name}:[/bold cyan]"
159
+ )
160
+ for prompt in prompts_info:
161
+ if (
162
+ isinstance(prompt, dict)
163
+ and "name" in prompt
164
+ ):
165
+ rich_print(
166
+ f" {prompt['name']}"
167
+ )
168
+ else:
169
+ rich_print(f" {prompt}")
170
+
171
+ if not found_prompts:
172
+ rich_print("[yellow]No prompts available[/yellow]")
173
+ except Exception as e:
174
+ rich_print(f"[red]Error listing prompts: {e}[/red]")
175
+ continue
176
+ elif "select_prompt" in command_result:
177
+ from rich import print as rich_print
178
+ from rich.table import Table
179
+ from rich.console import Console
180
+ from prompt_toolkit import PromptSession
181
+ from prompt_toolkit.formatted_text import HTML
182
+ from prompt_toolkit.completion import WordCompleter
183
+
184
+ console = Console()
185
+
186
+ # Get the current agent proxy
187
+ current_proxy = self._agents[agent]
188
+
189
+ # Check if the agent has prompt capabilities
190
+ if not hasattr(current_proxy, "_agent") or not hasattr(
191
+ current_proxy._agent, "list_prompts"
192
+ ):
193
+ rich_print(
194
+ f"[red]Current agent '{agent}' does not support prompts[/red]"
195
+ )
196
+ continue
197
+
198
+ try:
199
+ # Create a list to store prompt data for selection
200
+ all_prompts = []
201
+
202
+ # Get prompts from the current agent
203
+ rich_print(
204
+ f"\n[bold]Fetching prompts for agent [cyan]{agent}[/cyan]...[/bold]"
205
+ )
206
+ prompt_servers = await current_proxy._agent.list_prompts()
207
+
208
+ if not prompt_servers:
209
+ rich_print(
210
+ "[yellow]No prompts available for this agent[/yellow]"
211
+ )
212
+ continue
213
+
214
+ # Process retrieved prompts
215
+ for server_name, prompts_info in prompt_servers.items():
216
+ # Skip servers with no prompts
217
+ if not prompts_info:
218
+ continue
219
+
220
+ # Extract prompts from the response
221
+ prompts = []
222
+ if hasattr(prompts_info, "prompts"):
223
+ prompts = prompts_info.prompts
224
+ elif isinstance(prompts_info, list):
225
+ prompts = prompts_info
226
+
227
+ # Process each prompt
228
+ for prompt in prompts:
229
+ # Basic prompt information
230
+ prompt_name = getattr(prompt, "name", "Unknown")
231
+ description = getattr(
232
+ prompt, "description", "No description"
233
+ )
234
+
235
+ # Extract argument information
236
+ arg_names = []
237
+ required_args = []
238
+ optional_args = []
239
+ arg_descriptions = {}
240
+
241
+ # Get arguments list from prompt (MCP SDK Prompt.arguments)
242
+ arguments = getattr(prompt, "arguments", None)
243
+ if arguments:
244
+ for arg in arguments:
245
+ # Each arg is a PromptArgument with name and required fields
246
+ name = getattr(arg, "name", None)
247
+ if name:
248
+ arg_names.append(name)
249
+
250
+ # Store description if available
251
+ description = getattr(
252
+ arg, "description", None
253
+ )
254
+ if description:
255
+ arg_descriptions[name] = description
256
+
257
+ # Check if required (default to False per MCP spec)
258
+ if getattr(arg, "required", False):
259
+ required_args.append(name)
260
+ else:
261
+ optional_args.append(name)
262
+
263
+ # Create a namespaced version with the server
264
+ namespaced_name = f"{server_name}-{prompt_name}"
265
+
266
+ # Add to our collection
267
+ all_prompts.append(
268
+ {
269
+ "server": server_name,
270
+ "name": prompt_name,
271
+ "namespaced_name": namespaced_name,
272
+ "description": description,
273
+ "arg_count": len(arg_names),
274
+ "arg_names": arg_names,
275
+ "required_args": required_args,
276
+ "optional_args": optional_args,
277
+ "arg_descriptions": arg_descriptions,
278
+ }
279
+ )
280
+
281
+ # If no prompts were found
282
+ if not all_prompts:
283
+ rich_print(
284
+ "[yellow]No prompts available for this agent[/yellow]"
285
+ )
286
+ continue
287
+
288
+ # Sort prompts by server then name
289
+ all_prompts.sort(key=lambda p: (p["server"], p["name"]))
290
+
291
+ # Check if a specific prompt was requested
292
+ if (
293
+ "prompt_name" in command_result
294
+ and command_result["prompt_name"]
295
+ ):
296
+ requested_name = command_result["prompt_name"]
297
+ # Find the prompt in our list (either by name or namespaced name)
298
+ matching_prompts = [
299
+ p
300
+ for p in all_prompts
301
+ if p["name"] == requested_name
302
+ or p["namespaced_name"] == requested_name
303
+ ]
304
+
305
+ if not matching_prompts:
306
+ rich_print(
307
+ f"[red]Prompt '{requested_name}' not found[/red]"
308
+ )
309
+ rich_print("[yellow]Available prompts:[/yellow]")
310
+ for p in all_prompts:
311
+ rich_print(f" {p['namespaced_name']}")
312
+ continue
313
+
314
+ # If we found exactly one match, use it
315
+ if len(matching_prompts) == 1:
316
+ selected_prompt = matching_prompts[0]
317
+ else:
318
+ # If multiple matches, show them and ask user to be more specific
319
+ rich_print(
320
+ f"[yellow]Multiple prompts match '{requested_name}':[/yellow]"
321
+ )
322
+ for i, p in enumerate(matching_prompts):
323
+ rich_print(
324
+ f" {i + 1}. {p['namespaced_name']} - {p['description']}"
325
+ )
326
+
327
+ # Ask user to select one
328
+ prompt_session = PromptSession()
329
+ selection = await prompt_session.prompt_async(
330
+ "Enter prompt number to select: ", default="1"
331
+ )
332
+
333
+ try:
334
+ idx = int(selection) - 1
335
+ if 0 <= idx < len(matching_prompts):
336
+ selected_prompt = matching_prompts[idx]
337
+ else:
338
+ rich_print("[red]Invalid selection[/red]")
339
+ continue
340
+ except ValueError:
341
+ rich_print(
342
+ "[red]Invalid input, please enter a number[/red]"
343
+ )
344
+ continue
345
+ else:
346
+ # Display prompt selection UI
347
+ table = Table(title="Available MCP Prompts")
348
+ table.add_column("#", justify="right", style="cyan")
349
+ table.add_column("Server", style="green")
350
+ table.add_column("Prompt Name", style="bright_blue")
351
+ table.add_column("Description")
352
+ table.add_column("Args", justify="center")
353
+
354
+ # Add all prompts to the table
355
+ for i, prompt in enumerate(all_prompts):
356
+ # Get argument counts
357
+ required_args = prompt["required_args"]
358
+ optional_args = prompt["optional_args"]
359
+
360
+ # Format args column nicely
361
+ if required_args and optional_args:
362
+ args_display = f"[bold]{len(required_args)}[/bold]+{len(optional_args)}"
363
+ elif required_args:
364
+ args_display = (
365
+ f"[bold]{len(required_args)}[/bold]"
366
+ )
367
+ elif optional_args:
368
+ args_display = f"{len(optional_args)} opt"
369
+ else:
370
+ args_display = "0"
371
+
372
+ table.add_row(
373
+ str(i + 1),
374
+ prompt["server"],
375
+ prompt["name"],
376
+ prompt["description"] or "No description",
377
+ args_display,
378
+ )
379
+
380
+ console.print(table)
381
+ prompt_names = [
382
+ str(i + 1) for i in range(len(all_prompts))
383
+ ]
384
+ completer = WordCompleter(prompt_names)
385
+
386
+ # Ask user to select a prompt
387
+ prompt_session = PromptSession(completer=completer)
388
+ selection = await prompt_session.prompt_async(
389
+ "Enter prompt number to select (or press Enter to cancel): "
390
+ )
391
+
392
+ # Make cancellation easier
393
+ if not selection or selection.strip() == "":
394
+ rich_print(
395
+ "[yellow]Prompt selection cancelled[/yellow]"
396
+ )
397
+ continue
398
+
399
+ try:
400
+ idx = int(selection) - 1
401
+ if 0 <= idx < len(all_prompts):
402
+ selected_prompt = all_prompts[idx]
403
+ else:
404
+ rich_print("[red]Invalid selection[/red]")
405
+ continue
406
+ except ValueError:
407
+ rich_print(
408
+ "[red]Invalid input, please enter a number[/red]"
409
+ )
410
+ continue
411
+
412
+ # Get our prompt arguments
413
+ required_args = selected_prompt["required_args"]
414
+ optional_args = selected_prompt["optional_args"]
415
+ arg_descriptions = selected_prompt.get(
416
+ "arg_descriptions", {}
417
+ )
418
+
419
+ # Always initialize arg_values
420
+ arg_values = {}
421
+
422
+ # Show argument info if we have any
423
+ if required_args or optional_args:
424
+ # Display information about the arguments
425
+ if required_args and optional_args:
426
+ rich_print(
427
+ f"\n[bold]Prompt [cyan]{selected_prompt['name']}[/cyan] requires {len(required_args)} arguments and has {len(optional_args)} optional arguments:[/bold]"
428
+ )
429
+ elif required_args:
430
+ rich_print(
431
+ f"\n[bold]Prompt [cyan]{selected_prompt['name']}[/cyan] requires {len(required_args)} arguments:[/bold]"
432
+ )
433
+ elif optional_args:
434
+ rich_print(
435
+ f"\n[bold]Prompt [cyan]{selected_prompt['name']}[/cyan] has {len(optional_args)} optional arguments:[/bold]"
436
+ )
437
+
438
+ # Collect required arguments
439
+ for arg_name in required_args:
440
+ # Show description if available
441
+ description = arg_descriptions.get(arg_name, "")
442
+ if description:
443
+ rich_print(
444
+ f" [dim]{arg_name}: {description}[/dim]"
445
+ )
446
+
447
+ # Only include non-empty values for optional arguments
448
+ if optional_args:
449
+ # Collect optional arguments
450
+ for arg_name in optional_args:
451
+ # Show description if available
452
+ description = arg_descriptions.get(arg_name, "")
453
+ if description:
454
+ rich_print(
455
+ f" [dim]{arg_name}: {description}[/dim]"
456
+ )
457
+
458
+ arg_value = await PromptSession().prompt_async(
459
+ HTML(
460
+ f"Enter value for <ansibrightcyan>{arg_name}</ansibrightcyan> (optional, press Enter to skip): "
461
+ )
462
+ )
463
+ # Only include non-empty values for optional arguments
464
+ if arg_value:
465
+ arg_values[arg_name] = arg_value
466
+
467
+ # Apply the prompt with or without arguments
468
+ rich_print(
469
+ f"\n[bold]Applying prompt [cyan]{selected_prompt['namespaced_name']}[/cyan]...[/bold]"
470
+ )
471
+
472
+ # Call apply_prompt on the agent - always pass arg_values (empty dict if no args)
473
+ await current_proxy._agent.apply_prompt(
474
+ selected_prompt["namespaced_name"], arg_values
475
+ )
476
+
477
+ except Exception as e:
478
+ import traceback
479
+
480
+ rich_print(
481
+ f"[red]Error selecting or applying prompt: {e}[/red]"
482
+ )
483
+ rich_print(f"[dim]{traceback.format_exc()}[/dim]")
484
+ continue
485
+
486
+ # Skip further processing if command was handled
487
+ if command_result:
488
+ continue
489
+
490
+ if user_input.upper() == "STOP":
491
+ return result
492
+ if user_input == "":
493
+ continue
494
+
495
+ result = await self.send(agent, user_input)
496
+
497
+ # Check if current agent is a chain that should continue with final agent
498
+ if agent_types.get(agent) == "Chain":
499
+ proxy = self._agents[agent]
500
+ if isinstance(proxy, ChainProxy) and proxy._continue_with_final:
501
+ # Get the last agent in the sequence
502
+ last_agent = proxy._sequence[-1]
503
+ # Switch to that agent for the next iteration
504
+ agent = last_agent
505
+
506
+ return result
507
+
508
+ def __getattr__(self, name: str) -> BaseAgentProxy:
509
+ """Support: agent.researcher"""
510
+ if name not in self._agents:
511
+ raise AttributeError(f"No agent named '{name}'")
512
+ return self._agents[name]
513
+
514
+ def __getitem__(self, name: str) -> BaseAgentProxy:
515
+ """Support: agent['researcher']"""
516
+ if name not in self._agents:
517
+ raise KeyError(f"No agent named '{name}'")
518
+ return self._agents[name]
519
+
520
+ async def __call__(
521
+ self, message: Optional[str] = "", agent_name: Optional[str] = None
522
+ ) -> str:
523
+ """Support: agent('message')"""
524
+ target = agent_name or self._default
525
+ if not target:
526
+ raise ValueError("No default agent available")
527
+ return await self.send(target, message)