fast-agent-mcp 0.2.58__py3-none-any.whl → 0.3.1__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 +75 -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 +10 -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 +52 -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 +127 -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. {mcp_agent/llm/providers → fast_agent/llm/provider/bedrock}/bedrock_utils.py +3 -1
  64. mcp_agent/llm/providers/augmented_llm_bedrock.py → fast_agent/llm/provider/bedrock/llm_bedrock.py +833 -717
  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 -207
  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 +54 -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/forms_demo.py +3 -3
  118. {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/game_character.py +2 -2
  119. {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/game_character_handler.py +1 -1
  120. {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/tool_call.py +1 -1
  121. {mcp_agent → fast_agent}/resources/examples/mcp/state-transfer/agent_one.py +1 -1
  122. {mcp_agent → fast_agent}/resources/examples/mcp/state-transfer/agent_two.py +1 -1
  123. {mcp_agent → fast_agent}/resources/examples/researcher/researcher-eval.py +1 -1
  124. {mcp_agent → fast_agent}/resources/examples/researcher/researcher-imp.py +1 -1
  125. {mcp_agent → fast_agent}/resources/examples/researcher/researcher.py +1 -1
  126. {mcp_agent → fast_agent}/resources/examples/tensorzero/agent.py +2 -2
  127. {mcp_agent → fast_agent}/resources/examples/tensorzero/image_demo.py +3 -3
  128. {mcp_agent → fast_agent}/resources/examples/tensorzero/simple_agent.py +1 -1
  129. {mcp_agent → fast_agent}/resources/examples/workflows/chaining.py +1 -1
  130. {mcp_agent → fast_agent}/resources/examples/workflows/evaluator.py +3 -3
  131. {mcp_agent → fast_agent}/resources/examples/workflows/human_input.py +5 -3
  132. {mcp_agent → fast_agent}/resources/examples/workflows/orchestrator.py +1 -1
  133. {mcp_agent → fast_agent}/resources/examples/workflows/parallel.py +2 -2
  134. {mcp_agent → fast_agent}/resources/examples/workflows/router.py +5 -2
  135. fast_agent/resources/setup/.gitignore +24 -0
  136. fast_agent/resources/setup/agent.py +18 -0
  137. fast_agent/resources/setup/fastagent.config.yaml +44 -0
  138. fast_agent/resources/setup/fastagent.secrets.yaml.example +38 -0
  139. fast_agent/resources/setup/pyproject.toml.tmpl +17 -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 +17 -12
  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.58.dist-info → fast_agent_mcp-0.3.1.dist-info}/METADATA +7 -7
  154. fast_agent_mcp-0.3.1.dist-info/RECORD +203 -0
  155. fast_agent_mcp-0.3.1.dist-info/entry_points.txt +5 -0
  156. fast_agent_mcp-0.2.58.dist-info/RECORD +0 -193
  157. fast_agent_mcp-0.2.58.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 -718
  172. mcp_agent/llm/providers/augmented_llm_google_native.py +0 -496
  173. mcp_agent/llm/providers/sampling_converter_anthropic.py +0 -57
  174. mcp_agent/llm/providers/sampling_converter_openai.py +0 -26
  175. mcp_agent/llm/sampling_format_converter.py +0 -37
  176. mcp_agent/logging/__init__.py +0 -0
  177. mcp_agent/mcp/__init__.py +0 -50
  178. mcp_agent/mcp/helpers/__init__.py +0 -25
  179. mcp_agent/mcp/helpers/content_helpers.py +0 -187
  180. mcp_agent/mcp/interfaces.py +0 -266
  181. mcp_agent/mcp/prompts/__init__.py +0 -0
  182. mcp_agent/mcp/prompts/__main__.py +0 -10
  183. mcp_agent/mcp_server_registry.py +0 -343
  184. mcp_agent/tools/tool_definition.py +0 -14
  185. mcp_agent/ui/console_display.py +0 -790
  186. mcp_agent/ui/console_display_legacy.py +0 -401
  187. {mcp_agent → fast_agent}/agents/workflow/orchestrator_prompts.py +0 -0
  188. {mcp_agent/agents → fast_agent/cli}/__init__.py +0 -0
  189. {mcp_agent → fast_agent}/cli/constants.py +0 -0
  190. {mcp_agent → fast_agent}/core/error_handling.py +0 -0
  191. {mcp_agent → fast_agent}/core/exceptions.py +0 -0
  192. {mcp_agent/cli → fast_agent/core/executor}/__init__.py +0 -0
  193. {mcp_agent → fast_agent/core}/executor/task_registry.py +0 -0
  194. {mcp_agent → fast_agent/core}/executor/workflow_signal.py +0 -0
  195. {mcp_agent → fast_agent}/human_input/form_fields.py +0 -0
  196. {mcp_agent → fast_agent}/llm/prompt_utils.py +0 -0
  197. {mcp_agent/core → fast_agent/llm}/request_params.py +0 -0
  198. {mcp_agent → fast_agent}/mcp/common.py +0 -0
  199. {mcp_agent/executor → fast_agent/mcp/prompts}/__init__.py +0 -0
  200. {mcp_agent → fast_agent}/mcp/prompts/prompt_constants.py +0 -0
  201. {mcp_agent → fast_agent}/py.typed +0 -0
  202. {mcp_agent → fast_agent}/resources/examples/data-analysis/fastagent.config.yaml +0 -0
  203. {mcp_agent → fast_agent}/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +0 -0
  204. {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/elicitation_account_server.py +0 -0
  205. {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/elicitation_forms_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.58.dist-info → fast_agent_mcp-0.3.1.dist-info}/WHEEL +0 -0
  234. {fast_agent_mcp-0.2.58.dist-info → fast_agent_mcp-0.3.1.dist-info}/licenses/LICENSE +0 -0
@@ -22,8 +22,7 @@ from prompt_toolkit.widgets import (
22
22
  from pydantic import AnyUrl, EmailStr
23
23
  from pydantic import ValidationError as PydanticValidationError
24
24
 
25
- from mcp_agent.human_input.elicitation_forms import ELICITATION_STYLE
26
- from mcp_agent.human_input.elicitation_state import elicitation_state
25
+ from fast_agent.ui.elicitation_style import ELICITATION_STYLE
27
26
 
28
27
 
29
28
  class SimpleNumberValidator(Validator):
@@ -280,10 +279,15 @@ class ElicitationForm:
280
279
  ]
281
280
  )
282
281
 
283
- # Create dialog frame with title
282
+ # Choose dialog title: prefer schema.title if provided
283
+ dialog_title = self.schema.get("title") if isinstance(self.schema, dict) else None
284
+ if not dialog_title or not isinstance(dialog_title, str):
285
+ dialog_title = "Elicitation Request"
286
+
287
+ # Create dialog frame with dynamic title
284
288
  dialog = Frame(
285
289
  body=full_content,
286
- title="Elicitation Request",
290
+ title=dialog_title,
287
291
  style="class:dialog",
288
292
  )
289
293
 
@@ -334,7 +338,7 @@ class ElicitationForm:
334
338
  @kb.add("c-j")
335
339
  def insert_newline(event):
336
340
  # Insert a newline at the cursor position
337
- event.current_buffer.insert_text('\n')
341
+ event.current_buffer.insert_text("\n")
338
342
  # Mark this field as multiline when user adds a newline
339
343
  for field_name, widget in self.field_widgets.items():
340
344
  if isinstance(widget, Buffer) and widget == event.current_buffer:
@@ -438,7 +442,6 @@ class ElicitationForm:
438
442
 
439
443
  return constraints
440
444
 
441
-
442
445
  def _create_field(self, field_name: str, field_def: Dict[str, Any]):
443
446
  """Create a field widget."""
444
447
 
@@ -558,11 +561,11 @@ class ElicitationForm:
558
561
  max_length = None
559
562
 
560
563
  # Check if default value contains newlines
561
- if field_type == "string" and default_value is not None and '\n' in str(default_value):
564
+ if field_type == "string" and default_value is not None and "\n" in str(default_value):
562
565
  multiline = True
563
566
  self.multiline_fields.add(field_name) # Track multiline fields
564
567
  # Set height to actual line count for fields with newlines in default
565
- initial_height = str(default_value).count('\n') + 1
568
+ initial_height = str(default_value).count("\n") + 1
566
569
  elif max_length and max_length > 100:
567
570
  # Use multiline for longer fields
568
571
  multiline = True
@@ -605,10 +608,10 @@ class ElicitationForm:
605
608
  if not buffer.text:
606
609
  return initial_height
607
610
  # Calculate height based on number of newlines in buffer
608
- line_count = buffer.text.count('\n') + 1
611
+ line_count = buffer.text.count("\n") + 1
609
612
  # Use initial height as minimum, grow up to 20 lines
610
613
  return min(max(line_count, initial_height), 20)
611
-
614
+
612
615
  text_input = Window(
613
616
  BufferControl(buffer=buffer),
614
617
  height=get_dynamic_height, # Use dynamic height function
@@ -727,8 +730,10 @@ class ElicitationForm:
727
730
  self.app.exit()
728
731
 
729
732
  def _cancel_all(self):
730
- """Handle cancel all - cancels and disables future elicitations."""
731
- elicitation_state.disable_server(self.server_name)
733
+ """Handle cancel all: signal disable; no side effects here.
734
+
735
+ UI emits an action; handler/orchestration is responsible for updating state.
736
+ """
732
737
  self.action = "disable"
733
738
  self._clear_status_bar()
734
739
  self.app.exit()
@@ -1,4 +1,4 @@
1
- """Shared styling configuration for MCP elicitation forms."""
1
+ """Shared styling configuration for elicitation UIs (prompt_toolkit)."""
2
2
 
3
3
  from prompt_toolkit.styles import Style
4
4
 
@@ -19,8 +19,9 @@ from prompt_toolkit.key_binding import KeyBindings
19
19
  from prompt_toolkit.styles import Style
20
20
  from rich import print as rich_print
21
21
 
22
- from mcp_agent.core.agent_types import AgentType
23
- from mcp_agent.core.exceptions import PromptExitError
22
+ from fast_agent.agents.agent_types import AgentType
23
+ from fast_agent.core.exceptions import PromptExitError
24
+ from fast_agent.llm.model_info import get_model_info
24
25
 
25
26
  # Get the application version
26
27
  try:
@@ -59,9 +60,12 @@ async def _display_agent_info_helper(agent_name: str, agent_provider: object) ->
59
60
  # This is a single agent
60
61
  agent = agent_provider
61
62
 
62
- # Get counts
63
- servers = await agent.list_servers()
64
- server_count = len(servers) if servers else 0
63
+ # Get counts TODO -- add this to the type library or adjust the way aggregator/reporting works
64
+ server_count = 0
65
+ if hasattr(agent, "_aggregator") and hasattr(agent._aggregator, "server_names"):
66
+ server_count = (
67
+ len(agent._aggregator.server_names) if agent._aggregator.server_names else 0
68
+ )
65
69
 
66
70
  tools_result = await agent.list_tools()
67
71
  tool_count = (
@@ -290,12 +294,13 @@ class AgentCompleter(Completer):
290
294
  self.agents = agents
291
295
  # Map commands to their descriptions for better completion hints
292
296
  self.commands = {
293
- "tools": "List and call MCP tools",
294
- "prompt": "List and select MCP prompts, or apply specific prompt (/prompt <name>)",
297
+ "tools": "List available MCP tools",
298
+ "prompt": "List and choose MCP prompts, or apply specific prompt (/prompt <name>)",
295
299
  "agents": "List available agents",
296
300
  "usage": "Show current usage statistics",
297
301
  "markdown": "Show last assistant message without markdown formatting",
298
- "help": "Show available commands",
302
+ "save_history": "Save history; .json = MCP JSON, others = Markdown",
303
+ "help": "Show commands and shortcuts",
299
304
  "clear": "Clear the screen",
300
305
  "STOP": "Stop this prompting session and move to next workflow step",
301
306
  "EXIT": "Exit fast-agent, terminating any running workflows",
@@ -578,32 +583,91 @@ async def get_enhanced_input(
578
583
  if in_multiline_mode:
579
584
  mode_style = "ansired" # More noticeable for multiline mode
580
585
  mode_text = "MULTILINE"
581
- toggle_text = "Normal"
586
+ # toggle_text = "Normal"
582
587
  else:
583
588
  mode_style = "ansigreen"
584
589
  mode_text = "NORMAL"
585
- toggle_text = "Multiline"
590
+ # toggle_text = "Multiline"
586
591
 
587
- shortcuts = [
588
- ("Ctrl+T", toggle_text),
589
- ("Ctrl+J", "Newline" if not in_multiline_mode else None),
590
- ("Ctrl+E", "External"),
591
- ("Ctrl+Y", "Copy"),
592
- ("Ctrl+L", "Clear"),
593
- ("↑/↓", "History"),
594
- ("EXIT", "Exit")
595
- ]
596
-
597
- newline = "Ctrl+J:Submit" if in_multiline_mode else "&lt;Enter&gt;:Submit"
592
+ # No shortcut hints in the toolbar for now
593
+ shortcuts = []
598
594
 
599
595
  # Only show relevant shortcuts based on mode
600
596
  shortcuts = [(k, v) for k, v in shortcuts if v]
601
597
 
602
598
  shortcut_text = " | ".join(f"{key}:{action}" for key, action in shortcuts)
603
599
 
604
- return HTML(
605
- f" <style fg='{toolbar_color}' bg='ansiblack'> {agent_name} </style> Mode: <style fg='{mode_style}' bg='ansiblack'> {mode_text} </style> {newline} | {shortcut_text} | v{app_version}"
606
- )
600
+ # Resolve model name and TDV from the current agent if available
601
+ model_display = None
602
+ tdv_segment = None
603
+ try:
604
+ agent_obj = (
605
+ agent_provider._agent(agent_name)
606
+ if agent_provider and hasattr(agent_provider, "_agent")
607
+ else agent_provider
608
+ )
609
+ if agent_obj and hasattr(agent_obj, "llm") and agent_obj.llm:
610
+ model_name = getattr(agent_obj.llm, "model_name", None)
611
+ if model_name:
612
+ # Truncate model name to max 25 characters with ellipsis
613
+ max_len = 25
614
+ if len(model_name) > max_len:
615
+ # Keep total length at max_len including ellipsis
616
+ model_display = model_name[: max_len - 1] + "…"
617
+ else:
618
+ model_display = model_name
619
+
620
+ # Build TDV capability segment based on model database
621
+ info = get_model_info(agent_obj)
622
+ # Default to text-only if info resolution fails for any reason
623
+ t, d, v = (True, False, False)
624
+ if info:
625
+ t, d, v = info.tdv_flags
626
+
627
+ def _style_flag(letter: str, supported: bool) -> str:
628
+ # Enabled uses the same color as NORMAL mode (ansigreen), disabled is dim
629
+ enabled_color = "ansigreen"
630
+ return (
631
+ f"<style fg='{enabled_color}' bg='ansiblack'>{letter}</style>"
632
+ if supported
633
+ else f"<style fg='ansiblack' bg='ansiwhite'>{letter}</style>"
634
+ )
635
+
636
+ tdv_segment = f"{_style_flag('T', t)}{_style_flag('D', d)}{_style_flag('V', v)}"
637
+ except Exception:
638
+ # If anything goes wrong determining the model, omit it gracefully
639
+ model_display = None
640
+ tdv_segment = None
641
+
642
+ # Build dynamic middle segments: model (in green) and optional shortcuts
643
+ middle_segments = []
644
+ if model_display:
645
+ # Model chip + inline TDV flags
646
+ if tdv_segment:
647
+ middle_segments.append(
648
+ f"{tdv_segment} <style bg='ansigreen'>{model_display}</style>"
649
+ )
650
+ else:
651
+ middle_segments.append(f"<style bg='ansigreen'>{model_display}</style>")
652
+ if shortcut_text:
653
+ middle_segments.append(shortcut_text)
654
+ middle = " | ".join(middle_segments)
655
+
656
+ # Version/app label in green (dynamic version)
657
+ version_segment = f"fast-agent {app_version}"
658
+
659
+ if middle:
660
+ return HTML(
661
+ f" <style fg='{toolbar_color}' bg='ansiblack'> {agent_name} </style> "
662
+ f" {middle} | <style fg='{mode_style}' bg='ansiblack'> {mode_text} </style> | "
663
+ f"{version_segment}"
664
+ )
665
+ else:
666
+ return HTML(
667
+ f" <style fg='{toolbar_color}' bg='ansiblack'> {agent_name} </style> "
668
+ f"Mode: <style fg='{mode_style}' bg='ansiblack'> {mode_text} </style> | "
669
+ f"{version_segment}"
670
+ )
607
671
 
608
672
  # A more terminal-agnostic style that should work across themes
609
673
  custom_style = Style.from_dict(
@@ -676,7 +740,7 @@ async def get_enhanced_input(
676
740
  def pre_process_input(text):
677
741
  # Command processing
678
742
  if text and text.startswith("/"):
679
- if text == "/":
743
+ if text == "/":
680
744
  return ""
681
745
  cmd_parts = text[1:].strip().split(maxsplit=1)
682
746
  cmd = cmd_parts[0].lower()
@@ -691,6 +755,11 @@ async def get_enhanced_input(
691
755
  return "SHOW_USAGE"
692
756
  elif cmd == "markdown":
693
757
  return "MARKDOWN"
758
+ elif cmd in ("save_history", "save"):
759
+ # Return a structured action for the interactive loop to handle
760
+ # Prefer programmatic saving via HistoryExporter; fall back to magic-string there if needed
761
+ filename = cmd_parts[1].strip() if len(cmd_parts) > 1 and cmd_parts[1].strip() else None
762
+ return {"save_history": True, "filename": filename}
694
763
  elif cmd == "prompt":
695
764
  # Handle /prompt with no arguments as interactive mode
696
765
  if len(cmd_parts) > 1:
@@ -867,6 +936,8 @@ async def handle_special_commands(command, agent_app=None):
867
936
  rich_print(" /prompt <name> - Apply a specific prompt by name")
868
937
  rich_print(" /usage - Show current usage statistics")
869
938
  rich_print(" /markdown - Show last assistant message without markdown formatting")
939
+ rich_print(" /save_history <filename> - Save current chat history to a file")
940
+ rich_print(" [dim]Tip: Use a .json extension for MCP-compatible JSON; any other extension saves Markdown.[/dim]")
870
941
  rich_print(" @agent_name - Switch to agent")
871
942
  rich_print(" STOP - Return control back to the workflow")
872
943
  rich_print(" EXIT - Exit fast-agent, terminating any running workflows")