fast-agent-mcp 0.2.57__py3-none-any.whl → 0.3.0__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.

Files changed (234) hide show
  1. fast_agent/__init__.py +127 -0
  2. fast_agent/agents/__init__.py +36 -0
  3. {mcp_agent/core → fast_agent/agents}/agent_types.py +2 -1
  4. fast_agent/agents/llm_agent.py +217 -0
  5. fast_agent/agents/llm_decorator.py +486 -0
  6. mcp_agent/agents/base_agent.py → fast_agent/agents/mcp_agent.py +377 -385
  7. fast_agent/agents/tool_agent.py +168 -0
  8. {mcp_agent → fast_agent}/agents/workflow/chain_agent.py +43 -33
  9. {mcp_agent → fast_agent}/agents/workflow/evaluator_optimizer.py +31 -35
  10. {mcp_agent → fast_agent}/agents/workflow/iterative_planner.py +56 -47
  11. {mcp_agent → fast_agent}/agents/workflow/orchestrator_models.py +4 -4
  12. {mcp_agent → fast_agent}/agents/workflow/parallel_agent.py +34 -41
  13. {mcp_agent → fast_agent}/agents/workflow/router_agent.py +54 -39
  14. {mcp_agent → fast_agent}/cli/__main__.py +5 -3
  15. {mcp_agent → fast_agent}/cli/commands/check_config.py +95 -66
  16. {mcp_agent → fast_agent}/cli/commands/go.py +20 -11
  17. {mcp_agent → fast_agent}/cli/commands/quickstart.py +4 -4
  18. {mcp_agent → fast_agent}/cli/commands/server_helpers.py +1 -1
  19. {mcp_agent → fast_agent}/cli/commands/setup.py +64 -134
  20. {mcp_agent → fast_agent}/cli/commands/url_parser.py +9 -8
  21. {mcp_agent → fast_agent}/cli/main.py +36 -16
  22. {mcp_agent → fast_agent}/cli/terminal.py +2 -2
  23. {mcp_agent → fast_agent}/config.py +13 -2
  24. fast_agent/constants.py +8 -0
  25. {mcp_agent → fast_agent}/context.py +24 -19
  26. {mcp_agent → fast_agent}/context_dependent.py +9 -5
  27. fast_agent/core/__init__.py +17 -0
  28. {mcp_agent → fast_agent}/core/agent_app.py +39 -36
  29. fast_agent/core/core_app.py +135 -0
  30. {mcp_agent → fast_agent}/core/direct_decorators.py +12 -26
  31. {mcp_agent → fast_agent}/core/direct_factory.py +95 -73
  32. {mcp_agent → fast_agent/core}/executor/executor.py +4 -5
  33. {mcp_agent → fast_agent}/core/fastagent.py +32 -32
  34. fast_agent/core/logging/__init__.py +5 -0
  35. {mcp_agent → fast_agent/core}/logging/events.py +3 -3
  36. {mcp_agent → fast_agent/core}/logging/json_serializer.py +1 -1
  37. {mcp_agent → fast_agent/core}/logging/listeners.py +85 -7
  38. {mcp_agent → fast_agent/core}/logging/logger.py +7 -7
  39. {mcp_agent → fast_agent/core}/logging/transport.py +10 -11
  40. fast_agent/core/prompt.py +9 -0
  41. {mcp_agent → fast_agent}/core/validation.py +4 -4
  42. fast_agent/event_progress.py +61 -0
  43. fast_agent/history/history_exporter.py +44 -0
  44. {mcp_agent → fast_agent}/human_input/__init__.py +9 -12
  45. {mcp_agent → fast_agent}/human_input/elicitation_handler.py +26 -8
  46. {mcp_agent → fast_agent}/human_input/elicitation_state.py +7 -7
  47. {mcp_agent → fast_agent}/human_input/simple_form.py +6 -4
  48. {mcp_agent → fast_agent}/human_input/types.py +1 -18
  49. fast_agent/interfaces.py +228 -0
  50. fast_agent/llm/__init__.py +9 -0
  51. mcp_agent/llm/augmented_llm.py → fast_agent/llm/fastagent_llm.py +128 -218
  52. fast_agent/llm/internal/passthrough.py +137 -0
  53. mcp_agent/llm/augmented_llm_playback.py → fast_agent/llm/internal/playback.py +29 -25
  54. mcp_agent/llm/augmented_llm_silent.py → fast_agent/llm/internal/silent.py +10 -17
  55. fast_agent/llm/internal/slow.py +38 -0
  56. {mcp_agent → fast_agent}/llm/memory.py +40 -30
  57. {mcp_agent → fast_agent}/llm/model_database.py +35 -2
  58. {mcp_agent → fast_agent}/llm/model_factory.py +103 -77
  59. fast_agent/llm/model_info.py +126 -0
  60. {mcp_agent/llm/providers → fast_agent/llm/provider/anthropic}/anthropic_utils.py +7 -7
  61. fast_agent/llm/provider/anthropic/llm_anthropic.py +603 -0
  62. {mcp_agent/llm/providers → fast_agent/llm/provider/anthropic}/multipart_converter_anthropic.py +79 -86
  63. fast_agent/llm/provider/bedrock/bedrock_utils.py +218 -0
  64. fast_agent/llm/provider/bedrock/llm_bedrock.py +2192 -0
  65. {mcp_agent/llm/providers → fast_agent/llm/provider/google}/google_converter.py +66 -14
  66. fast_agent/llm/provider/google/llm_google_native.py +431 -0
  67. mcp_agent/llm/providers/augmented_llm_aliyun.py → fast_agent/llm/provider/openai/llm_aliyun.py +6 -7
  68. mcp_agent/llm/providers/augmented_llm_azure.py → fast_agent/llm/provider/openai/llm_azure.py +4 -4
  69. mcp_agent/llm/providers/augmented_llm_deepseek.py → fast_agent/llm/provider/openai/llm_deepseek.py +10 -11
  70. mcp_agent/llm/providers/augmented_llm_generic.py → fast_agent/llm/provider/openai/llm_generic.py +4 -4
  71. mcp_agent/llm/providers/augmented_llm_google_oai.py → fast_agent/llm/provider/openai/llm_google_oai.py +4 -4
  72. mcp_agent/llm/providers/augmented_llm_groq.py → fast_agent/llm/provider/openai/llm_groq.py +14 -16
  73. mcp_agent/llm/providers/augmented_llm_openai.py → fast_agent/llm/provider/openai/llm_openai.py +133 -206
  74. mcp_agent/llm/providers/augmented_llm_openrouter.py → fast_agent/llm/provider/openai/llm_openrouter.py +6 -6
  75. mcp_agent/llm/providers/augmented_llm_tensorzero_openai.py → fast_agent/llm/provider/openai/llm_tensorzero_openai.py +17 -16
  76. mcp_agent/llm/providers/augmented_llm_xai.py → fast_agent/llm/provider/openai/llm_xai.py +6 -6
  77. {mcp_agent/llm/providers → fast_agent/llm/provider/openai}/multipart_converter_openai.py +125 -63
  78. {mcp_agent/llm/providers → fast_agent/llm/provider/openai}/openai_multipart.py +12 -12
  79. {mcp_agent/llm/providers → fast_agent/llm/provider/openai}/openai_utils.py +18 -16
  80. {mcp_agent → fast_agent}/llm/provider_key_manager.py +2 -2
  81. {mcp_agent → fast_agent}/llm/provider_types.py +2 -0
  82. {mcp_agent → fast_agent}/llm/sampling_converter.py +15 -12
  83. {mcp_agent → fast_agent}/llm/usage_tracking.py +23 -5
  84. fast_agent/mcp/__init__.py +43 -0
  85. {mcp_agent → fast_agent}/mcp/elicitation_factory.py +3 -3
  86. {mcp_agent → fast_agent}/mcp/elicitation_handlers.py +19 -10
  87. {mcp_agent → fast_agent}/mcp/gen_client.py +3 -3
  88. fast_agent/mcp/helpers/__init__.py +36 -0
  89. fast_agent/mcp/helpers/content_helpers.py +183 -0
  90. {mcp_agent → fast_agent}/mcp/helpers/server_config_helpers.py +8 -8
  91. {mcp_agent → fast_agent}/mcp/hf_auth.py +25 -23
  92. fast_agent/mcp/interfaces.py +93 -0
  93. {mcp_agent → fast_agent}/mcp/logger_textio.py +4 -4
  94. {mcp_agent → fast_agent}/mcp/mcp_agent_client_session.py +49 -44
  95. {mcp_agent → fast_agent}/mcp/mcp_aggregator.py +66 -115
  96. {mcp_agent → fast_agent}/mcp/mcp_connection_manager.py +16 -23
  97. {mcp_agent/core → fast_agent/mcp}/mcp_content.py +23 -15
  98. {mcp_agent → fast_agent}/mcp/mime_utils.py +39 -0
  99. fast_agent/mcp/prompt.py +159 -0
  100. mcp_agent/mcp/prompt_message_multipart.py → fast_agent/mcp/prompt_message_extended.py +27 -20
  101. {mcp_agent → fast_agent}/mcp/prompt_render.py +21 -19
  102. {mcp_agent → fast_agent}/mcp/prompt_serialization.py +46 -46
  103. fast_agent/mcp/prompts/__main__.py +7 -0
  104. {mcp_agent → fast_agent}/mcp/prompts/prompt_helpers.py +31 -30
  105. {mcp_agent → fast_agent}/mcp/prompts/prompt_load.py +8 -8
  106. {mcp_agent → fast_agent}/mcp/prompts/prompt_server.py +11 -19
  107. {mcp_agent → fast_agent}/mcp/prompts/prompt_template.py +18 -18
  108. {mcp_agent → fast_agent}/mcp/resource_utils.py +1 -1
  109. {mcp_agent → fast_agent}/mcp/sampling.py +31 -26
  110. {mcp_agent/mcp_server → fast_agent/mcp/server}/__init__.py +1 -1
  111. {mcp_agent/mcp_server → fast_agent/mcp/server}/agent_server.py +5 -6
  112. fast_agent/mcp/ui_agent.py +48 -0
  113. fast_agent/mcp/ui_mixin.py +209 -0
  114. fast_agent/mcp_server_registry.py +90 -0
  115. {mcp_agent → fast_agent}/resources/examples/data-analysis/analysis-campaign.py +5 -4
  116. {mcp_agent → fast_agent}/resources/examples/data-analysis/analysis.py +1 -1
  117. {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/elicitation_forms_server.py +25 -3
  118. {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/forms_demo.py +3 -3
  119. {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/game_character.py +2 -2
  120. {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/game_character_handler.py +1 -1
  121. {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/tool_call.py +1 -1
  122. {mcp_agent → fast_agent}/resources/examples/mcp/state-transfer/agent_one.py +1 -1
  123. {mcp_agent → fast_agent}/resources/examples/mcp/state-transfer/agent_two.py +1 -1
  124. {mcp_agent → fast_agent}/resources/examples/researcher/researcher-eval.py +1 -1
  125. {mcp_agent → fast_agent}/resources/examples/researcher/researcher-imp.py +1 -1
  126. {mcp_agent → fast_agent}/resources/examples/researcher/researcher.py +1 -1
  127. {mcp_agent → fast_agent}/resources/examples/tensorzero/agent.py +2 -2
  128. {mcp_agent → fast_agent}/resources/examples/tensorzero/image_demo.py +3 -3
  129. {mcp_agent → fast_agent}/resources/examples/tensorzero/simple_agent.py +1 -1
  130. {mcp_agent → fast_agent}/resources/examples/workflows/chaining.py +1 -1
  131. {mcp_agent → fast_agent}/resources/examples/workflows/evaluator.py +3 -3
  132. {mcp_agent → fast_agent}/resources/examples/workflows/human_input.py +5 -3
  133. {mcp_agent → fast_agent}/resources/examples/workflows/orchestrator.py +1 -1
  134. {mcp_agent → fast_agent}/resources/examples/workflows/parallel.py +2 -2
  135. {mcp_agent → fast_agent}/resources/examples/workflows/router.py +5 -2
  136. fast_agent/resources/setup/.gitignore +24 -0
  137. fast_agent/resources/setup/agent.py +18 -0
  138. fast_agent/resources/setup/fastagent.config.yaml +44 -0
  139. fast_agent/resources/setup/fastagent.secrets.yaml.example +38 -0
  140. fast_agent/tools/elicitation.py +369 -0
  141. fast_agent/types/__init__.py +32 -0
  142. fast_agent/types/llm_stop_reason.py +77 -0
  143. fast_agent/ui/__init__.py +38 -0
  144. fast_agent/ui/console_display.py +1005 -0
  145. {mcp_agent/human_input → fast_agent/ui}/elicitation_form.py +56 -39
  146. mcp_agent/human_input/elicitation_forms.py → fast_agent/ui/elicitation_style.py +1 -1
  147. {mcp_agent/core → fast_agent/ui}/enhanced_prompt.py +96 -25
  148. {mcp_agent/core → fast_agent/ui}/interactive_prompt.py +330 -125
  149. fast_agent/ui/mcp_ui_utils.py +224 -0
  150. {mcp_agent → fast_agent/ui}/progress_display.py +2 -2
  151. {mcp_agent/logging → fast_agent/ui}/rich_progress.py +4 -4
  152. {mcp_agent/core → fast_agent/ui}/usage_display.py +3 -8
  153. {fast_agent_mcp-0.2.57.dist-info → fast_agent_mcp-0.3.0.dist-info}/METADATA +7 -7
  154. fast_agent_mcp-0.3.0.dist-info/RECORD +202 -0
  155. fast_agent_mcp-0.3.0.dist-info/entry_points.txt +5 -0
  156. fast_agent_mcp-0.2.57.dist-info/RECORD +0 -192
  157. fast_agent_mcp-0.2.57.dist-info/entry_points.txt +0 -6
  158. mcp_agent/__init__.py +0 -114
  159. mcp_agent/agents/agent.py +0 -92
  160. mcp_agent/agents/workflow/__init__.py +0 -1
  161. mcp_agent/agents/workflow/orchestrator_agent.py +0 -597
  162. mcp_agent/app.py +0 -175
  163. mcp_agent/core/__init__.py +0 -26
  164. mcp_agent/core/prompt.py +0 -191
  165. mcp_agent/event_progress.py +0 -134
  166. mcp_agent/human_input/handler.py +0 -81
  167. mcp_agent/llm/__init__.py +0 -2
  168. mcp_agent/llm/augmented_llm_passthrough.py +0 -232
  169. mcp_agent/llm/augmented_llm_slow.py +0 -53
  170. mcp_agent/llm/providers/__init__.py +0 -8
  171. mcp_agent/llm/providers/augmented_llm_anthropic.py +0 -717
  172. mcp_agent/llm/providers/augmented_llm_bedrock.py +0 -1788
  173. mcp_agent/llm/providers/augmented_llm_google_native.py +0 -495
  174. mcp_agent/llm/providers/sampling_converter_anthropic.py +0 -57
  175. mcp_agent/llm/providers/sampling_converter_openai.py +0 -26
  176. mcp_agent/llm/sampling_format_converter.py +0 -37
  177. mcp_agent/logging/__init__.py +0 -0
  178. mcp_agent/mcp/__init__.py +0 -50
  179. mcp_agent/mcp/helpers/__init__.py +0 -25
  180. mcp_agent/mcp/helpers/content_helpers.py +0 -187
  181. mcp_agent/mcp/interfaces.py +0 -266
  182. mcp_agent/mcp/prompts/__init__.py +0 -0
  183. mcp_agent/mcp/prompts/__main__.py +0 -10
  184. mcp_agent/mcp_server_registry.py +0 -343
  185. mcp_agent/tools/tool_definition.py +0 -14
  186. mcp_agent/ui/console_display.py +0 -790
  187. mcp_agent/ui/console_display_legacy.py +0 -401
  188. {mcp_agent → fast_agent}/agents/workflow/orchestrator_prompts.py +0 -0
  189. {mcp_agent/agents → fast_agent/cli}/__init__.py +0 -0
  190. {mcp_agent → fast_agent}/cli/constants.py +0 -0
  191. {mcp_agent → fast_agent}/core/error_handling.py +0 -0
  192. {mcp_agent → fast_agent}/core/exceptions.py +0 -0
  193. {mcp_agent/cli → fast_agent/core/executor}/__init__.py +0 -0
  194. {mcp_agent → fast_agent/core}/executor/task_registry.py +0 -0
  195. {mcp_agent → fast_agent/core}/executor/workflow_signal.py +0 -0
  196. {mcp_agent → fast_agent}/human_input/form_fields.py +0 -0
  197. {mcp_agent → fast_agent}/llm/prompt_utils.py +0 -0
  198. {mcp_agent/core → fast_agent/llm}/request_params.py +0 -0
  199. {mcp_agent → fast_agent}/mcp/common.py +0 -0
  200. {mcp_agent/executor → fast_agent/mcp/prompts}/__init__.py +0 -0
  201. {mcp_agent → fast_agent}/mcp/prompts/prompt_constants.py +0 -0
  202. {mcp_agent → fast_agent}/py.typed +0 -0
  203. {mcp_agent → fast_agent}/resources/examples/data-analysis/fastagent.config.yaml +0 -0
  204. {mcp_agent → fast_agent}/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +0 -0
  205. {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/elicitation_account_server.py +0 -0
  206. {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/elicitation_game_server.py +0 -0
  207. {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/fastagent.config.yaml +0 -0
  208. {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/fastagent.secrets.yaml.example +0 -0
  209. {mcp_agent → fast_agent}/resources/examples/mcp/state-transfer/fastagent.config.yaml +0 -0
  210. {mcp_agent → fast_agent}/resources/examples/mcp/state-transfer/fastagent.secrets.yaml.example +0 -0
  211. {mcp_agent → fast_agent}/resources/examples/researcher/fastagent.config.yaml +0 -0
  212. {mcp_agent → fast_agent}/resources/examples/tensorzero/.env.sample +0 -0
  213. {mcp_agent → fast_agent}/resources/examples/tensorzero/Makefile +0 -0
  214. {mcp_agent → fast_agent}/resources/examples/tensorzero/README.md +0 -0
  215. {mcp_agent → fast_agent}/resources/examples/tensorzero/demo_images/clam.jpg +0 -0
  216. {mcp_agent → fast_agent}/resources/examples/tensorzero/demo_images/crab.png +0 -0
  217. {mcp_agent → fast_agent}/resources/examples/tensorzero/demo_images/shrimp.png +0 -0
  218. {mcp_agent → fast_agent}/resources/examples/tensorzero/docker-compose.yml +0 -0
  219. {mcp_agent → fast_agent}/resources/examples/tensorzero/fastagent.config.yaml +0 -0
  220. {mcp_agent → fast_agent}/resources/examples/tensorzero/mcp_server/Dockerfile +0 -0
  221. {mcp_agent → fast_agent}/resources/examples/tensorzero/mcp_server/entrypoint.sh +0 -0
  222. {mcp_agent → fast_agent}/resources/examples/tensorzero/mcp_server/mcp_server.py +0 -0
  223. {mcp_agent → fast_agent}/resources/examples/tensorzero/mcp_server/pyproject.toml +0 -0
  224. {mcp_agent → fast_agent}/resources/examples/tensorzero/tensorzero_config/system_schema.json +0 -0
  225. {mcp_agent → fast_agent}/resources/examples/tensorzero/tensorzero_config/system_template.minijinja +0 -0
  226. {mcp_agent → fast_agent}/resources/examples/tensorzero/tensorzero_config/tensorzero.toml +0 -0
  227. {mcp_agent → fast_agent}/resources/examples/workflows/fastagent.config.yaml +0 -0
  228. {mcp_agent → fast_agent}/resources/examples/workflows/graded_report.md +0 -0
  229. {mcp_agent → fast_agent}/resources/examples/workflows/short_story.md +0 -0
  230. {mcp_agent → fast_agent}/resources/examples/workflows/short_story.txt +0 -0
  231. {mcp_agent → fast_agent/ui}/console.py +0 -0
  232. {mcp_agent/core → fast_agent/ui}/mermaid_utils.py +0 -0
  233. {fast_agent_mcp-0.2.57.dist-info → fast_agent_mcp-0.3.0.dist-info}/WHEEL +0 -0
  234. {fast_agent_mcp-0.2.57.dist-info → fast_agent_mcp-0.3.0.dist-info}/licenses/LICENSE +0 -0
@@ -18,24 +18,24 @@ from typing import Awaitable, Callable, Dict, List, Mapping, Optional, Protocol,
18
18
 
19
19
  from mcp.types import Prompt, PromptMessage
20
20
  from rich import print as rich_print
21
- from rich.console import Console
22
- from rich.table import Table
23
21
 
24
- from mcp_agent.core.agent_types import AgentType
25
- from mcp_agent.core.enhanced_prompt import (
22
+ from fast_agent.agents.agent_types import AgentType
23
+ from fast_agent.history.history_exporter import HistoryExporter
24
+ from fast_agent.interfaces import AgentProtocol
25
+ from fast_agent.mcp.mcp_aggregator import SEP
26
+ from fast_agent.types import PromptMessageExtended
27
+ from fast_agent.ui.enhanced_prompt import (
26
28
  _display_agent_info_helper,
27
29
  get_argument_input,
28
30
  get_enhanced_input,
29
31
  get_selection_input,
30
32
  handle_special_commands,
31
33
  )
32
- from mcp_agent.core.usage_display import collect_agents_from_provider, display_usage_report
33
- from mcp_agent.mcp.mcp_aggregator import SEP
34
- from mcp_agent.mcp.prompt_message_multipart import PromptMessageMultipart
35
- from mcp_agent.progress_display import progress_display
34
+ from fast_agent.ui.progress_display import progress_display
35
+ from fast_agent.ui.usage_display import collect_agents_from_provider, display_usage_report
36
36
 
37
37
  # Type alias for the send function
38
- SendFunc = Callable[[Union[str, PromptMessage, PromptMessageMultipart], str], Awaitable[str]]
38
+ SendFunc = Callable[[Union[str, PromptMessage, PromptMessageExtended], str], Awaitable[str]]
39
39
 
40
40
  # Type alias for the agent getter function
41
41
  AgentGetter = Callable[[str], Optional[object]]
@@ -45,7 +45,7 @@ class PromptProvider(Protocol):
45
45
  """Protocol for objects that can provide prompt functionality."""
46
46
 
47
47
  async def list_prompts(
48
- self, server_name: Optional[str] = None, agent_name: Optional[str] = None
48
+ self, namespace: Optional[str] = None, agent_name: Optional[str] = None
49
49
  ) -> Mapping[str, List[Prompt]]:
50
50
  """List available prompts."""
51
51
  ...
@@ -62,6 +62,14 @@ class PromptProvider(Protocol):
62
62
  """Apply a prompt."""
63
63
  ...
64
64
 
65
+ def _agent(self, agent_name: str) -> AgentProtocol:
66
+ """Return the concrete agent by name (AgentApp provides this)."""
67
+ ...
68
+
69
+ def _show_turn_usage(self, agent_name: str) -> None:
70
+ """Display usage for a given agent after a turn."""
71
+ ...
72
+
65
73
 
66
74
  class InteractivePrompt:
67
75
  """
@@ -83,7 +91,7 @@ class InteractivePrompt:
83
91
  send_func: SendFunc,
84
92
  default_agent: str,
85
93
  available_agents: List[str],
86
- prompt_provider: Optional[PromptProvider] = None,
94
+ prompt_provider: PromptProvider,
87
95
  default: str = "",
88
96
  ) -> str:
89
97
  """
@@ -144,11 +152,11 @@ class InteractivePrompt:
144
152
  rich_print(f"[red]Agent '{new_agent}' not found[/red]")
145
153
  continue
146
154
  # Keep the existing list_prompts handler for backward compatibility
147
- elif "list_prompts" in command_result and prompt_provider:
155
+ elif "list_prompts" in command_result:
148
156
  # Use the prompt_provider directly
149
157
  await self._list_prompts(prompt_provider, agent)
150
158
  continue
151
- elif "select_prompt" in command_result and prompt_provider:
159
+ elif "select_prompt" in command_result:
152
160
  # Handle prompt selection, using both list_prompts and apply_prompt
153
161
  prompt_name = command_result.get("prompt_name")
154
162
  prompt_index = command_result.get("prompt_index")
@@ -181,7 +189,7 @@ class InteractivePrompt:
181
189
  # Use the name-based selection
182
190
  await self._select_prompt(prompt_provider, agent, prompt_name)
183
191
  continue
184
- elif "list_tools" in command_result and prompt_provider:
192
+ elif "list_tools" in command_result:
185
193
  # Handle tools list display
186
194
  await self._list_tools(prompt_provider, agent)
187
195
  continue
@@ -193,6 +201,22 @@ class InteractivePrompt:
193
201
  # Handle markdown display
194
202
  await self._show_markdown(prompt_provider, agent)
195
203
  continue
204
+ elif "save_history" in command_result:
205
+ # Save history for the current agent
206
+ filename = command_result.get("filename")
207
+ try:
208
+ agent_obj = prompt_provider._agent(agent)
209
+
210
+ # Prefer type-safe exporter over magic string
211
+ saved_path = await HistoryExporter.save(agent_obj, filename)
212
+ rich_print(f"[green]History saved to {saved_path}[/green]")
213
+ except Exception:
214
+ # Fallback to magic string path for maximum compatibility
215
+ control = "***SAVE_HISTORY" + (f" {filename}" if filename else "")
216
+ result = await send_func(control, agent)
217
+ if result:
218
+ rich_print(f"[green]{result}[/green]")
219
+ continue
196
220
 
197
221
  # Skip further processing if:
198
222
  # 1. The command was handled (command_result is truthy)
@@ -216,6 +240,51 @@ class InteractivePrompt:
216
240
 
217
241
  return result
218
242
 
243
+ def _create_combined_separator_status(
244
+ self, left_content: str, right_info: str, console
245
+ ) -> None:
246
+ """
247
+ Create a combined separator and status line using the new visual style.
248
+
249
+ Args:
250
+ left_content: The main content (block, arrow, name) - left justified with color
251
+ right_info: Supplementary information to show in brackets - right aligned
252
+ console: Rich console instance to use
253
+ """
254
+ from rich.text import Text
255
+
256
+ width = console.size.width
257
+
258
+ # Create left text
259
+ left_text = Text.from_markup(left_content)
260
+
261
+ # Create right text if we have info
262
+ if right_info and right_info.strip():
263
+ # Add dim brackets around the right info
264
+ right_text = Text()
265
+ right_text.append("[", style="dim")
266
+ right_text.append_text(Text.from_markup(right_info))
267
+ right_text.append("]", style="dim")
268
+ # Calculate separator count
269
+ separator_count = width - left_text.cell_len - right_text.cell_len
270
+ if separator_count < 1:
271
+ separator_count = 1 # Always at least 1 separator
272
+ else:
273
+ right_text = Text("")
274
+ separator_count = width - left_text.cell_len
275
+
276
+ # Build the combined line
277
+ combined = Text()
278
+ combined.append_text(left_text)
279
+ combined.append(" ", style="default")
280
+ combined.append("─" * (separator_count - 1), style="dim")
281
+ combined.append_text(right_text)
282
+
283
+ # Print with empty line before
284
+ rich_print()
285
+ console.print(combined)
286
+ rich_print()
287
+
219
288
  async def _get_all_prompts(
220
289
  self, prompt_provider: PromptProvider, agent_name: Optional[str] = None
221
290
  ):
@@ -232,7 +301,7 @@ class InteractivePrompt:
232
301
  try:
233
302
  # Call list_prompts on the provider
234
303
  prompt_servers = await prompt_provider.list_prompts(
235
- server_name=None, agent_name=agent_name
304
+ namespace=None, agent_name=agent_name
236
305
  )
237
306
 
238
307
  all_prompts = []
@@ -305,44 +374,94 @@ class InteractivePrompt:
305
374
  prompt_provider: Provider that implements list_prompts
306
375
  agent_name: Name of the agent
307
376
  """
308
- console = Console()
309
-
310
377
  try:
311
- # Directly call the list_prompts function for this agent
312
- rich_print(f"\n[bold]Fetching prompts for agent [cyan]{agent_name}[/cyan]...[/bold]")
313
-
314
378
  # Get all prompts using the helper function
315
379
  all_prompts = await self._get_all_prompts(prompt_provider, agent_name)
316
380
 
317
- if all_prompts:
318
- # Create a table for better display
319
- table = Table(title="Available MCP Prompts")
320
- table.add_column("#", justify="right", style="cyan")
321
- table.add_column("Server", style="green")
322
- table.add_column("Prompt Name", style="bright_blue")
323
- table.add_column("Title")
324
- table.add_column("Description")
325
- table.add_column("Args", justify="center")
326
-
327
- # Add prompts to table
328
- for i, prompt in enumerate(all_prompts):
329
- table.add_row(
330
- str(i + 1),
331
- prompt["server"],
332
- prompt["name"],
333
- prompt["title"],
334
- prompt["description"],
335
- str(prompt["arg_count"]),
336
- )
381
+ rich_print(f"\n[bold]Prompts for agent [cyan]{agent_name}[/cyan]:[/bold]")
337
382
 
338
- console.print(table)
383
+ if not all_prompts:
384
+ rich_print("[yellow]No prompts available for this agent[/yellow]")
385
+ return
386
+
387
+ rich_print()
388
+
389
+ # Display prompts using clean compact format
390
+ for i, prompt in enumerate(all_prompts, 1):
391
+ # Main line: [ 1] server•prompt_name Title
392
+ from rich.text import Text
393
+
394
+ prompt_line = Text()
395
+ prompt_line.append(f"[{i:2}] ", style="dim cyan")
396
+ prompt_line.append(f"{prompt['server']}•", style="dim green")
397
+ prompt_line.append(prompt["name"], style="bright_blue bold")
398
+
399
+ # Add title if available
400
+ if prompt["title"] and prompt["title"].strip():
401
+ prompt_line.append(f" {prompt['title']}", style="default")
402
+
403
+ rich_print(prompt_line)
404
+
405
+ # Description lines - show 2-3 rows if needed
406
+ if prompt["description"] and prompt["description"].strip():
407
+ description = prompt["description"].strip()
408
+ # Calculate rough character limit for 2-3 lines (assuming ~80 chars per line with indent)
409
+ char_limit = 240 # About 3 lines worth
410
+
411
+ if len(description) > char_limit:
412
+ # Find a good break point near the limit (prefer sentence/word boundaries)
413
+ truncate_pos = char_limit
414
+ # Look back for sentence end
415
+ sentence_break = description.rfind(". ", 0, char_limit + 20)
416
+ if sentence_break > char_limit - 50: # If we found a nearby sentence break
417
+ truncate_pos = sentence_break + 1
418
+ else:
419
+ # Look for word boundary
420
+ word_break = description.rfind(" ", 0, char_limit + 10)
421
+ if word_break > char_limit - 30: # If we found a nearby word break
422
+ truncate_pos = word_break
423
+
424
+ description = description[:truncate_pos].rstrip() + "..."
425
+
426
+ # Split into lines and wrap
427
+ import textwrap
428
+
429
+ wrapped_lines = textwrap.wrap(description, width=72, subsequent_indent=" ")
430
+ for line in wrapped_lines:
431
+ if line.startswith(" "): # Already indented continuation line
432
+ rich_print(f" [white]{line[5:]}[/white]")
433
+ else: # First line needs indent
434
+ rich_print(f" [white]{line}[/white]")
435
+
436
+ # Arguments line - show argument names if available
437
+ if prompt["arg_count"] > 0:
438
+ arg_names = prompt.get("arg_names", [])
439
+ required_args = prompt.get("required_args", [])
440
+
441
+ if arg_names:
442
+ arg_list = []
443
+ for arg_name in arg_names:
444
+ if arg_name in required_args:
445
+ arg_list.append(f"{arg_name}*")
446
+ else:
447
+ arg_list.append(arg_name)
448
+
449
+ args_text = ", ".join(arg_list)
450
+ if len(args_text) > 80:
451
+ args_text = args_text[:77] + "..."
452
+ rich_print(f" [dim magenta]args: {args_text}[/dim magenta]")
453
+ else:
454
+ rich_print(
455
+ f" [dim magenta]args: {prompt['arg_count']} parameter{'s' if prompt['arg_count'] != 1 else ''}[/dim magenta]"
456
+ )
457
+
458
+ rich_print() # Space between prompts
459
+
460
+ # Add usage instructions
461
+ rich_print(
462
+ "[dim]Usage: /prompt <number> to select by number, or /prompts for interactive selection[/dim]"
463
+ )
339
464
 
340
- # Add usage instructions
341
- rich_print("\n[bold]Usage:[/bold]")
342
- rich_print(" • Use [cyan]/prompt <number>[/cyan] to select a prompt by number")
343
- rich_print(" • Or use [cyan]/prompts[/cyan] to open the prompt selection UI")
344
- else:
345
- rich_print("[yellow]No prompts available[/yellow]")
346
465
  except Exception as e:
347
466
  import traceback
348
467
 
@@ -364,15 +483,13 @@ class InteractivePrompt:
364
483
  agent_name: Name of the agent
365
484
  requested_name: Optional name of the prompt to apply
366
485
  """
367
- console = Console()
368
-
369
486
  try:
370
487
  # Get all available prompts directly from the prompt provider
371
488
  rich_print(f"\n[bold]Fetching prompts for agent [cyan]{agent_name}[/cyan]...[/bold]")
372
489
 
373
490
  # Call list_prompts on the provider
374
491
  prompt_servers = await prompt_provider.list_prompts(
375
- server_name=None, agent_name=agent_name
492
+ namespace=None, agent_name=agent_name
376
493
  )
377
494
 
378
495
  if not prompt_servers:
@@ -487,40 +604,85 @@ class InteractivePrompt:
487
604
  rich_print("[red]Invalid input, please enter a number[/red]")
488
605
  return
489
606
  else:
490
- # Show prompt selection UI
491
- table = Table(title="Available MCP Prompts")
492
- table.add_column("#", justify="right", style="cyan")
493
- table.add_column("Server", style="green")
494
- table.add_column("Prompt Name", style="bright_blue")
495
- table.add_column("Title")
496
- table.add_column("Description")
497
- table.add_column("Args", justify="center")
498
-
499
- # Add prompts to table
500
- for i, prompt in enumerate(all_prompts):
501
- required_args = prompt["required_args"]
502
- optional_args = prompt["optional_args"]
503
-
504
- # Format args column
505
- if required_args and optional_args:
506
- args_display = f"[bold]{len(required_args)}[/bold]+{len(optional_args)}"
507
- elif required_args:
508
- args_display = f"[bold]{len(required_args)}[/bold]"
509
- elif optional_args:
510
- args_display = f"{len(optional_args)} opt"
511
- else:
512
- args_display = "0"
513
-
514
- table.add_row(
515
- str(i + 1),
516
- prompt["server"],
517
- prompt["name"],
518
- prompt["title"] or "No title",
519
- prompt["description"] or "No description",
520
- args_display,
521
- )
607
+ # Show prompt selection UI using clean compact format
608
+ rich_print(f"\n[bold]Select a prompt for agent [cyan]{agent_name}[/cyan]:[/bold]")
609
+ rich_print()
610
+
611
+ # Display prompts using the same format as _list_prompts
612
+ for i, prompt in enumerate(all_prompts, 1):
613
+ # Main line: [ 1] server•prompt_name Title
614
+ from rich.text import Text
615
+
616
+ prompt_line = Text()
617
+ prompt_line.append(f"[{i:2}] ", style="dim cyan")
618
+ prompt_line.append(f"{prompt['server']}•", style="dim green")
619
+ prompt_line.append(prompt["name"], style="bright_blue bold")
620
+
621
+ # Add title if available
622
+ if prompt["title"] and prompt["title"].strip():
623
+ prompt_line.append(f" {prompt['title']}", style="default")
624
+
625
+ rich_print(prompt_line)
626
+
627
+ # Description lines - show 2-3 rows if needed
628
+ if prompt["description"] and prompt["description"].strip():
629
+ description = prompt["description"].strip()
630
+ # Calculate rough character limit for 2-3 lines (assuming ~80 chars per line with indent)
631
+ char_limit = 240 # About 3 lines worth
632
+
633
+ if len(description) > char_limit:
634
+ # Find a good break point near the limit (prefer sentence/word boundaries)
635
+ truncate_pos = char_limit
636
+ # Look back for sentence end
637
+ sentence_break = description.rfind(". ", 0, char_limit + 20)
638
+ if (
639
+ sentence_break > char_limit - 50
640
+ ): # If we found a nearby sentence break
641
+ truncate_pos = sentence_break + 1
642
+ else:
643
+ # Look for word boundary
644
+ word_break = description.rfind(" ", 0, char_limit + 10)
645
+ if word_break > char_limit - 30: # If we found a nearby word break
646
+ truncate_pos = word_break
647
+
648
+ description = description[:truncate_pos].rstrip() + "..."
649
+
650
+ # Split into lines and wrap
651
+ import textwrap
652
+
653
+ wrapped_lines = textwrap.wrap(
654
+ description, width=72, subsequent_indent=" "
655
+ )
656
+ for line in wrapped_lines:
657
+ if line.startswith(" "): # Already indented continuation line
658
+ rich_print(f" [white]{line[5:]}[/white]")
659
+ else: # First line needs indent
660
+ rich_print(f" [white]{line}[/white]")
661
+
662
+ # Arguments line - show argument names if available
663
+ if prompt["arg_count"] > 0:
664
+ arg_names = prompt.get("arg_names", [])
665
+ required_args = prompt.get("required_args", [])
666
+
667
+ if arg_names:
668
+ arg_list = []
669
+ for arg_name in arg_names:
670
+ if arg_name in required_args:
671
+ arg_list.append(f"{arg_name}*")
672
+ else:
673
+ arg_list.append(arg_name)
674
+
675
+ args_text = ", ".join(arg_list)
676
+ if len(args_text) > 80:
677
+ args_text = args_text[:77] + "..."
678
+ rich_print(f" [dim magenta]args: {args_text}[/dim magenta]")
679
+ else:
680
+ rich_print(
681
+ f" [dim magenta]args: {prompt['arg_count']} parameter{'s' if prompt['arg_count'] != 1 else ''}[/dim magenta]"
682
+ )
683
+
684
+ rich_print() # Space between prompts
522
685
 
523
- console.print(table)
524
686
  prompt_names = [str(i + 1) for i in range(len(all_prompts))]
525
687
 
526
688
  # Get user selection
@@ -595,12 +757,10 @@ class InteractivePrompt:
595
757
  rich_print(f"\n[bold]Applying prompt [cyan]{namespaced_name}[/cyan]...[/bold]")
596
758
 
597
759
  # Get the agent directly for generate() call
598
- if hasattr(prompt_provider, "_agent"):
599
- # This is an AgentApp - get the specific agent
600
- agent = prompt_provider._agent(agent_name)
601
- else:
602
- # This is a single agent
603
- agent = prompt_provider
760
+ assert hasattr(prompt_provider, "_agent"), (
761
+ "Interactive prompt expects an AgentApp with _agent()"
762
+ )
763
+ agent = prompt_provider._agent(agent_name)
604
764
 
605
765
  try:
606
766
  # Use agent.apply_prompt() which handles everything properly:
@@ -620,9 +780,9 @@ class InteractivePrompt:
620
780
  return
621
781
 
622
782
  # Convert to multipart format
623
- from mcp_agent.mcp.prompt_message_multipart import PromptMessageMultipart
783
+ from fast_agent.types import PromptMessageExtended
624
784
 
625
- multipart_messages = PromptMessageMultipart.from_get_prompt_result(prompt_result)
785
+ multipart_messages = PromptMessageExtended.from_get_prompt_result(prompt_result)
626
786
 
627
787
  # Now start progress display for the actual generation
628
788
  progress_display.resume()
@@ -633,8 +793,7 @@ class InteractivePrompt:
633
793
  progress_display.pause()
634
794
 
635
795
  # Show usage info after the turn (same as send_wrapper does)
636
- if hasattr(prompt_provider, "_show_turn_usage"):
637
- prompt_provider._show_turn_usage(agent_name)
796
+ prompt_provider._show_turn_usage(agent_name)
638
797
 
639
798
  except Exception as e:
640
799
  rich_print(f"[red]Error applying prompt: {e}[/red]")
@@ -653,18 +812,14 @@ class InteractivePrompt:
653
812
  prompt_provider: Provider that implements list_tools
654
813
  agent_name: Name of the agent
655
814
  """
656
- console = Console()
657
-
658
815
  try:
659
816
  # Get agent to list tools from
660
- if hasattr(prompt_provider, "_agent"):
661
- # This is an AgentApp - get the specific agent
662
- agent = prompt_provider._agent(agent_name)
663
- else:
664
- # This is a single agent
665
- agent = prompt_provider
817
+ assert hasattr(prompt_provider, "_agent"), (
818
+ "Interactive prompt expects an AgentApp with _agent()"
819
+ )
820
+ agent = prompt_provider._agent(agent_name)
666
821
 
667
- rich_print(f"\n[bold]Fetching tools for agent [cyan]{agent_name}[/cyan]...[/bold]")
822
+ rich_print(f"\n[bold]Tools for agent [cyan]{agent_name}[/cyan]:[/bold]")
668
823
 
669
824
  # Get tools using list_tools
670
825
  tools_result = await agent.list_tools()
@@ -673,23 +828,75 @@ class InteractivePrompt:
673
828
  rich_print("[yellow]No tools available for this agent[/yellow]")
674
829
  return
675
830
 
676
- # Create a table for better display
677
- table = Table(title="Available MCP Tools")
678
- table.add_column("#", justify="right", style="cyan")
679
- table.add_column("Tool Name", style="bright_blue")
680
- table.add_column("Title")
681
- table.add_column("Description")
682
-
683
- # Add tools to table
684
- for i, tool in enumerate(tools_result.tools):
685
- table.add_row(
686
- str(i + 1),
687
- tool.name,
688
- tool.title or "No title",
689
- tool.description or "No description",
690
- )
831
+ rich_print()
691
832
 
692
- console.print(table)
833
+ # Display tools using clean compact format
834
+ for i, tool in enumerate(tools_result.tools, 1):
835
+ # Main line: [ 1] tool_name Title
836
+ from rich.text import Text
837
+
838
+ tool_line = Text()
839
+ tool_line.append(f"[{i:2}] ", style="dim cyan")
840
+ tool_line.append(tool.name, style="bright_blue bold")
841
+
842
+ # Add title if available
843
+ if tool.title and tool.title.strip():
844
+ tool_line.append(f" {tool.title}", style="default")
845
+
846
+ rich_print(tool_line)
847
+
848
+ # Description lines - show 2-3 rows if needed
849
+ if tool.description and tool.description.strip():
850
+ description = tool.description.strip()
851
+ # Calculate rough character limit for 2-3 lines (assuming ~80 chars per line with indent)
852
+ char_limit = 240 # About 3 lines worth
853
+
854
+ if len(description) > char_limit:
855
+ # Find a good break point near the limit (prefer sentence/word boundaries)
856
+ truncate_pos = char_limit
857
+ # Look back for sentence end
858
+ sentence_break = description.rfind(". ", 0, char_limit + 20)
859
+ if sentence_break > char_limit - 50: # If we found a nearby sentence break
860
+ truncate_pos = sentence_break + 1
861
+ else:
862
+ # Look for word boundary
863
+ word_break = description.rfind(" ", 0, char_limit + 10)
864
+ if word_break > char_limit - 30: # If we found a nearby word break
865
+ truncate_pos = word_break
866
+
867
+ description = description[:truncate_pos].rstrip() + "..."
868
+
869
+ # Split into lines and wrap
870
+ import textwrap
871
+
872
+ wrapped_lines = textwrap.wrap(description, width=72, subsequent_indent=" ")
873
+ for line in wrapped_lines:
874
+ if line.startswith(" "): # Already indented continuation line
875
+ rich_print(f" [white]{line[5:]}[/white]")
876
+ else: # First line needs indent
877
+ rich_print(f" [white]{line}[/white]")
878
+
879
+ # Arguments line - show schema info if available
880
+ if hasattr(tool, "inputSchema") and tool.inputSchema:
881
+ schema = tool.inputSchema
882
+ if "properties" in schema:
883
+ properties = schema["properties"]
884
+ required = schema.get("required", [])
885
+
886
+ arg_list = []
887
+ for prop_name, prop_info in properties.items():
888
+ if prop_name in required:
889
+ arg_list.append(f"{prop_name}*")
890
+ else:
891
+ arg_list.append(prop_name)
892
+
893
+ if arg_list:
894
+ args_text = ", ".join(arg_list)
895
+ if len(args_text) > 80:
896
+ args_text = args_text[:77] + "..."
897
+ rich_print(f" [dim magenta]args: {args_text}[/dim magenta]")
898
+
899
+ rich_print() # Space between tools
693
900
 
694
901
  except Exception as e:
695
902
  import traceback
@@ -729,12 +936,10 @@ class InteractivePrompt:
729
936
  """
730
937
  try:
731
938
  # Get agent to display from
732
- if hasattr(prompt_provider, "_agent"):
733
- # This is an AgentApp - get the specific agent
734
- agent = prompt_provider._agent(agent_name)
735
- else:
736
- # This is a single agent
737
- agent = prompt_provider
939
+ assert hasattr(prompt_provider, "_agent"), (
940
+ "Interactive prompt expects an AgentApp with _agent()"
941
+ )
942
+ agent = prompt_provider._agent(agent_name)
738
943
 
739
944
  # Check if agent has message history
740
945
  if not hasattr(agent, "_llm") or not agent._llm:
@@ -764,7 +969,7 @@ class InteractivePrompt:
764
969
  rich_print("\n[bold blue]Last Assistant Response (Plain Text):[/bold blue]")
765
970
  rich_print("─" * 60)
766
971
  # Use console.print with markup=False to display raw text
767
- from mcp_agent import console
972
+ from fast_agent.ui import console
768
973
 
769
974
  console.console.print(content, markup=False)
770
975
  rich_print("─" * 60)