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
@@ -5,9 +5,7 @@ from typing import Any, Dict, Optional
5
5
 
6
6
  from mcp.types import ElicitRequestedSchema
7
7
  from prompt_toolkit import Application
8
- from prompt_toolkit.application.current import get_app
9
8
  from prompt_toolkit.buffer import Buffer
10
- from prompt_toolkit.filters import Condition
11
9
  from prompt_toolkit.formatted_text import FormattedText
12
10
  from prompt_toolkit.key_binding import KeyBindings
13
11
  from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous
@@ -24,8 +22,7 @@ from prompt_toolkit.widgets import (
24
22
  from pydantic import AnyUrl, EmailStr
25
23
  from pydantic import ValidationError as PydanticValidationError
26
24
 
27
- from mcp_agent.human_input.elicitation_forms import ELICITATION_STYLE
28
- from mcp_agent.human_input.elicitation_state import elicitation_state
25
+ from fast_agent.ui.elicitation_style import ELICITATION_STYLE
29
26
 
30
27
 
31
28
  class SimpleNumberValidator(Validator):
@@ -282,10 +279,15 @@ class ElicitationForm:
282
279
  ]
283
280
  )
284
281
 
285
- # 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
286
288
  dialog = Frame(
287
289
  body=full_content,
288
- title="Elicitation Request",
290
+ title=dialog_title,
289
291
  style="class:dialog",
290
292
  )
291
293
 
@@ -327,16 +329,21 @@ class ElicitationForm:
327
329
  def focus_previous_left(event):
328
330
  focus_previous(event)
329
331
 
330
- # Create filter for non-multiline fields
331
- not_in_multiline = Condition(lambda: not self._is_in_multiline_field())
332
-
333
- @kb.add("c-m", filter=not_in_multiline) # Enter to submit only when not in multiline
332
+ # Enter always submits
333
+ @kb.add("c-m")
334
334
  def submit(event):
335
335
  self._accept()
336
336
 
337
- @kb.add("c-j") # Ctrl+J as alternative submit for multiline fields
338
- def submit_alt(event):
339
- self._accept()
337
+ # Ctrl+J inserts newlines
338
+ @kb.add("c-j")
339
+ def insert_newline(event):
340
+ # Insert a newline at the cursor position
341
+ event.current_buffer.insert_text("\n")
342
+ # Mark this field as multiline when user adds a newline
343
+ for field_name, widget in self.field_widgets.items():
344
+ if isinstance(widget, Buffer) and widget == event.current_buffer:
345
+ self.multiline_fields.add(field_name)
346
+ break
340
347
 
341
348
  # ESC should ALWAYS cancel immediately, no matter what
342
349
  @kb.add("escape", eager=True, is_global=True)
@@ -353,7 +360,7 @@ class ElicitationForm:
353
360
  [
354
361
  (
355
362
  "class:bottom-toolbar.text",
356
- " <TAB> or ↑↓→← to navigate. <ENTER> submit (<Ctrl+J> in multiline). <ESC> to cancel. ",
363
+ " <TAB>/↑↓→← navigate. <ENTER> submit. <Ctrl+J> insert new line. <ESC> cancel. ",
357
364
  ),
358
365
  (
359
366
  "class:bottom-toolbar.text",
@@ -510,7 +517,8 @@ class ElicitationForm:
510
517
  enum_names = field_def.get("enumNames", enum_values)
511
518
  values = [(val, name) for val, name in zip(enum_values, enum_names)]
512
519
 
513
- radio_list = RadioList(values=values)
520
+ default_value = field_def.get("default")
521
+ radio_list = RadioList(values=values, default=default_value)
514
522
  self.field_widgets[field_name] = radio_list
515
523
 
516
524
  return HSplit([label, Frame(radio_list, height=min(len(values) + 2, 6))])
@@ -541,23 +549,35 @@ class ElicitationForm:
541
549
  else:
542
550
  constraints = {}
543
551
 
544
- # Determine if field should be multiline based on max_length
552
+ default_value = field_def.get("default")
553
+
554
+ # Determine if field should be multiline based on max_length or default value length
545
555
  if field_type == "string":
546
556
  max_length = constraints.get("maxLength")
557
+ # Check default value length if maxLength not specified
558
+ if not max_length and default_value is not None:
559
+ max_length = len(str(default_value))
547
560
  else:
548
561
  max_length = None
549
- if max_length and max_length > 100:
562
+
563
+ # Check if default value contains newlines
564
+ if field_type == "string" and default_value is not None and "\n" in str(default_value):
565
+ multiline = True
566
+ self.multiline_fields.add(field_name) # Track multiline fields
567
+ # Set height to actual line count for fields with newlines in default
568
+ initial_height = str(default_value).count("\n") + 1
569
+ elif max_length and max_length > 100:
550
570
  # Use multiline for longer fields
551
571
  multiline = True
552
572
  self.multiline_fields.add(field_name) # Track multiline fields
553
573
  if max_length <= 300:
554
- height = 3
574
+ initial_height = 3
555
575
  else:
556
- height = 5
576
+ initial_height = 5
557
577
  else:
558
578
  # Single line for shorter fields
559
579
  multiline = False
560
- height = 1
580
+ initial_height = 1
561
581
 
562
582
  buffer = Buffer(
563
583
  validator=validator,
@@ -566,6 +586,8 @@ class ElicitationForm:
566
586
  complete_while_typing=False, # Disable completion for cleaner experience
567
587
  enable_history_search=False, # Disable history for cleaner experience
568
588
  )
589
+ if default_value is not None:
590
+ buffer.text = str(default_value)
569
591
  self.field_widgets[field_name] = buffer
570
592
 
571
593
  # Create dynamic style function for focus highlighting and validation errors
@@ -581,31 +603,24 @@ class ElicitationForm:
581
603
  else:
582
604
  return "class:input-field"
583
605
 
606
+ # Create a dynamic height function based on content
607
+ def get_dynamic_height():
608
+ if not buffer.text:
609
+ return initial_height
610
+ # Calculate height based on number of newlines in buffer
611
+ line_count = buffer.text.count("\n") + 1
612
+ # Use initial height as minimum, grow up to 20 lines
613
+ return min(max(line_count, initial_height), 20)
614
+
584
615
  text_input = Window(
585
616
  BufferControl(buffer=buffer),
586
- height=height,
617
+ height=get_dynamic_height, # Use dynamic height function
587
618
  style=get_field_style, # Use dynamic style function
588
619
  wrap_lines=True if multiline else False, # Enable word wrap for multiline
589
620
  )
590
621
 
591
622
  return HSplit([label, Frame(text_input)])
592
623
 
593
- def _is_in_multiline_field(self) -> bool:
594
- """Check if currently focused field is a multiline field."""
595
-
596
- focused = get_app().layout.current_control
597
-
598
- # Find which field this control belongs to
599
- # Only Buffer widgets can be multiline, so only check those
600
- for field_name, widget in self.field_widgets.items():
601
- if (
602
- isinstance(widget, Buffer)
603
- and hasattr(focused, "buffer")
604
- and widget == focused.buffer
605
- ):
606
- return field_name in self.multiline_fields
607
- return False
608
-
609
624
  def _validate_form(self) -> tuple[bool, Optional[str]]:
610
625
  """Validate the entire form."""
611
626
 
@@ -715,8 +730,10 @@ class ElicitationForm:
715
730
  self.app.exit()
716
731
 
717
732
  def _cancel_all(self):
718
- """Handle cancel all - cancels and disables future elicitations."""
719
- 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
+ """
720
737
  self.action = "disable"
721
738
  self._clear_status_bar()
722
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")