fast-agent-mcp 0.4.7__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.
Files changed (261) hide show
  1. fast_agent/__init__.py +183 -0
  2. fast_agent/acp/__init__.py +19 -0
  3. fast_agent/acp/acp_aware_mixin.py +304 -0
  4. fast_agent/acp/acp_context.py +437 -0
  5. fast_agent/acp/content_conversion.py +136 -0
  6. fast_agent/acp/filesystem_runtime.py +427 -0
  7. fast_agent/acp/permission_store.py +269 -0
  8. fast_agent/acp/server/__init__.py +5 -0
  9. fast_agent/acp/server/agent_acp_server.py +1472 -0
  10. fast_agent/acp/slash_commands.py +1050 -0
  11. fast_agent/acp/terminal_runtime.py +408 -0
  12. fast_agent/acp/tool_permission_adapter.py +125 -0
  13. fast_agent/acp/tool_permissions.py +474 -0
  14. fast_agent/acp/tool_progress.py +814 -0
  15. fast_agent/agents/__init__.py +85 -0
  16. fast_agent/agents/agent_types.py +64 -0
  17. fast_agent/agents/llm_agent.py +350 -0
  18. fast_agent/agents/llm_decorator.py +1139 -0
  19. fast_agent/agents/mcp_agent.py +1337 -0
  20. fast_agent/agents/tool_agent.py +271 -0
  21. fast_agent/agents/workflow/agents_as_tools_agent.py +849 -0
  22. fast_agent/agents/workflow/chain_agent.py +212 -0
  23. fast_agent/agents/workflow/evaluator_optimizer.py +380 -0
  24. fast_agent/agents/workflow/iterative_planner.py +652 -0
  25. fast_agent/agents/workflow/maker_agent.py +379 -0
  26. fast_agent/agents/workflow/orchestrator_models.py +218 -0
  27. fast_agent/agents/workflow/orchestrator_prompts.py +248 -0
  28. fast_agent/agents/workflow/parallel_agent.py +250 -0
  29. fast_agent/agents/workflow/router_agent.py +353 -0
  30. fast_agent/cli/__init__.py +0 -0
  31. fast_agent/cli/__main__.py +73 -0
  32. fast_agent/cli/commands/acp.py +159 -0
  33. fast_agent/cli/commands/auth.py +404 -0
  34. fast_agent/cli/commands/check_config.py +783 -0
  35. fast_agent/cli/commands/go.py +514 -0
  36. fast_agent/cli/commands/quickstart.py +557 -0
  37. fast_agent/cli/commands/serve.py +143 -0
  38. fast_agent/cli/commands/server_helpers.py +114 -0
  39. fast_agent/cli/commands/setup.py +174 -0
  40. fast_agent/cli/commands/url_parser.py +190 -0
  41. fast_agent/cli/constants.py +40 -0
  42. fast_agent/cli/main.py +115 -0
  43. fast_agent/cli/terminal.py +24 -0
  44. fast_agent/config.py +798 -0
  45. fast_agent/constants.py +41 -0
  46. fast_agent/context.py +279 -0
  47. fast_agent/context_dependent.py +50 -0
  48. fast_agent/core/__init__.py +92 -0
  49. fast_agent/core/agent_app.py +448 -0
  50. fast_agent/core/core_app.py +137 -0
  51. fast_agent/core/direct_decorators.py +784 -0
  52. fast_agent/core/direct_factory.py +620 -0
  53. fast_agent/core/error_handling.py +27 -0
  54. fast_agent/core/exceptions.py +90 -0
  55. fast_agent/core/executor/__init__.py +0 -0
  56. fast_agent/core/executor/executor.py +280 -0
  57. fast_agent/core/executor/task_registry.py +32 -0
  58. fast_agent/core/executor/workflow_signal.py +324 -0
  59. fast_agent/core/fastagent.py +1186 -0
  60. fast_agent/core/logging/__init__.py +5 -0
  61. fast_agent/core/logging/events.py +138 -0
  62. fast_agent/core/logging/json_serializer.py +164 -0
  63. fast_agent/core/logging/listeners.py +309 -0
  64. fast_agent/core/logging/logger.py +278 -0
  65. fast_agent/core/logging/transport.py +481 -0
  66. fast_agent/core/prompt.py +9 -0
  67. fast_agent/core/prompt_templates.py +183 -0
  68. fast_agent/core/validation.py +326 -0
  69. fast_agent/event_progress.py +62 -0
  70. fast_agent/history/history_exporter.py +49 -0
  71. fast_agent/human_input/__init__.py +47 -0
  72. fast_agent/human_input/elicitation_handler.py +123 -0
  73. fast_agent/human_input/elicitation_state.py +33 -0
  74. fast_agent/human_input/form_elements.py +59 -0
  75. fast_agent/human_input/form_fields.py +256 -0
  76. fast_agent/human_input/simple_form.py +113 -0
  77. fast_agent/human_input/types.py +40 -0
  78. fast_agent/interfaces.py +310 -0
  79. fast_agent/llm/__init__.py +9 -0
  80. fast_agent/llm/cancellation.py +22 -0
  81. fast_agent/llm/fastagent_llm.py +931 -0
  82. fast_agent/llm/internal/passthrough.py +161 -0
  83. fast_agent/llm/internal/playback.py +129 -0
  84. fast_agent/llm/internal/silent.py +41 -0
  85. fast_agent/llm/internal/slow.py +38 -0
  86. fast_agent/llm/memory.py +275 -0
  87. fast_agent/llm/model_database.py +490 -0
  88. fast_agent/llm/model_factory.py +388 -0
  89. fast_agent/llm/model_info.py +102 -0
  90. fast_agent/llm/prompt_utils.py +155 -0
  91. fast_agent/llm/provider/anthropic/anthropic_utils.py +84 -0
  92. fast_agent/llm/provider/anthropic/cache_planner.py +56 -0
  93. fast_agent/llm/provider/anthropic/llm_anthropic.py +796 -0
  94. fast_agent/llm/provider/anthropic/multipart_converter_anthropic.py +462 -0
  95. fast_agent/llm/provider/bedrock/bedrock_utils.py +218 -0
  96. fast_agent/llm/provider/bedrock/llm_bedrock.py +2207 -0
  97. fast_agent/llm/provider/bedrock/multipart_converter_bedrock.py +84 -0
  98. fast_agent/llm/provider/google/google_converter.py +466 -0
  99. fast_agent/llm/provider/google/llm_google_native.py +681 -0
  100. fast_agent/llm/provider/openai/llm_aliyun.py +31 -0
  101. fast_agent/llm/provider/openai/llm_azure.py +143 -0
  102. fast_agent/llm/provider/openai/llm_deepseek.py +76 -0
  103. fast_agent/llm/provider/openai/llm_generic.py +35 -0
  104. fast_agent/llm/provider/openai/llm_google_oai.py +32 -0
  105. fast_agent/llm/provider/openai/llm_groq.py +42 -0
  106. fast_agent/llm/provider/openai/llm_huggingface.py +85 -0
  107. fast_agent/llm/provider/openai/llm_openai.py +1195 -0
  108. fast_agent/llm/provider/openai/llm_openai_compatible.py +138 -0
  109. fast_agent/llm/provider/openai/llm_openrouter.py +45 -0
  110. fast_agent/llm/provider/openai/llm_tensorzero_openai.py +128 -0
  111. fast_agent/llm/provider/openai/llm_xai.py +38 -0
  112. fast_agent/llm/provider/openai/multipart_converter_openai.py +561 -0
  113. fast_agent/llm/provider/openai/openai_multipart.py +169 -0
  114. fast_agent/llm/provider/openai/openai_utils.py +67 -0
  115. fast_agent/llm/provider/openai/responses.py +133 -0
  116. fast_agent/llm/provider_key_manager.py +139 -0
  117. fast_agent/llm/provider_types.py +34 -0
  118. fast_agent/llm/request_params.py +61 -0
  119. fast_agent/llm/sampling_converter.py +98 -0
  120. fast_agent/llm/stream_types.py +9 -0
  121. fast_agent/llm/usage_tracking.py +445 -0
  122. fast_agent/mcp/__init__.py +56 -0
  123. fast_agent/mcp/common.py +26 -0
  124. fast_agent/mcp/elicitation_factory.py +84 -0
  125. fast_agent/mcp/elicitation_handlers.py +164 -0
  126. fast_agent/mcp/gen_client.py +83 -0
  127. fast_agent/mcp/helpers/__init__.py +36 -0
  128. fast_agent/mcp/helpers/content_helpers.py +352 -0
  129. fast_agent/mcp/helpers/server_config_helpers.py +25 -0
  130. fast_agent/mcp/hf_auth.py +147 -0
  131. fast_agent/mcp/interfaces.py +92 -0
  132. fast_agent/mcp/logger_textio.py +108 -0
  133. fast_agent/mcp/mcp_agent_client_session.py +411 -0
  134. fast_agent/mcp/mcp_aggregator.py +2175 -0
  135. fast_agent/mcp/mcp_connection_manager.py +723 -0
  136. fast_agent/mcp/mcp_content.py +262 -0
  137. fast_agent/mcp/mime_utils.py +108 -0
  138. fast_agent/mcp/oauth_client.py +509 -0
  139. fast_agent/mcp/prompt.py +159 -0
  140. fast_agent/mcp/prompt_message_extended.py +155 -0
  141. fast_agent/mcp/prompt_render.py +84 -0
  142. fast_agent/mcp/prompt_serialization.py +580 -0
  143. fast_agent/mcp/prompts/__init__.py +0 -0
  144. fast_agent/mcp/prompts/__main__.py +7 -0
  145. fast_agent/mcp/prompts/prompt_constants.py +18 -0
  146. fast_agent/mcp/prompts/prompt_helpers.py +238 -0
  147. fast_agent/mcp/prompts/prompt_load.py +186 -0
  148. fast_agent/mcp/prompts/prompt_server.py +552 -0
  149. fast_agent/mcp/prompts/prompt_template.py +438 -0
  150. fast_agent/mcp/resource_utils.py +215 -0
  151. fast_agent/mcp/sampling.py +200 -0
  152. fast_agent/mcp/server/__init__.py +4 -0
  153. fast_agent/mcp/server/agent_server.py +613 -0
  154. fast_agent/mcp/skybridge.py +44 -0
  155. fast_agent/mcp/sse_tracking.py +287 -0
  156. fast_agent/mcp/stdio_tracking_simple.py +59 -0
  157. fast_agent/mcp/streamable_http_tracking.py +309 -0
  158. fast_agent/mcp/tool_execution_handler.py +137 -0
  159. fast_agent/mcp/tool_permission_handler.py +88 -0
  160. fast_agent/mcp/transport_tracking.py +634 -0
  161. fast_agent/mcp/types.py +24 -0
  162. fast_agent/mcp/ui_agent.py +48 -0
  163. fast_agent/mcp/ui_mixin.py +209 -0
  164. fast_agent/mcp_server_registry.py +89 -0
  165. fast_agent/py.typed +0 -0
  166. fast_agent/resources/examples/data-analysis/analysis-campaign.py +189 -0
  167. fast_agent/resources/examples/data-analysis/analysis.py +68 -0
  168. fast_agent/resources/examples/data-analysis/fastagent.config.yaml +41 -0
  169. fast_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +1471 -0
  170. fast_agent/resources/examples/mcp/elicitations/elicitation_account_server.py +88 -0
  171. fast_agent/resources/examples/mcp/elicitations/elicitation_forms_server.py +297 -0
  172. fast_agent/resources/examples/mcp/elicitations/elicitation_game_server.py +164 -0
  173. fast_agent/resources/examples/mcp/elicitations/fastagent.config.yaml +35 -0
  174. fast_agent/resources/examples/mcp/elicitations/fastagent.secrets.yaml.example +17 -0
  175. fast_agent/resources/examples/mcp/elicitations/forms_demo.py +107 -0
  176. fast_agent/resources/examples/mcp/elicitations/game_character.py +65 -0
  177. fast_agent/resources/examples/mcp/elicitations/game_character_handler.py +256 -0
  178. fast_agent/resources/examples/mcp/elicitations/tool_call.py +21 -0
  179. fast_agent/resources/examples/mcp/state-transfer/agent_one.py +18 -0
  180. fast_agent/resources/examples/mcp/state-transfer/agent_two.py +18 -0
  181. fast_agent/resources/examples/mcp/state-transfer/fastagent.config.yaml +27 -0
  182. fast_agent/resources/examples/mcp/state-transfer/fastagent.secrets.yaml.example +15 -0
  183. fast_agent/resources/examples/researcher/fastagent.config.yaml +61 -0
  184. fast_agent/resources/examples/researcher/researcher-eval.py +53 -0
  185. fast_agent/resources/examples/researcher/researcher-imp.py +189 -0
  186. fast_agent/resources/examples/researcher/researcher.py +36 -0
  187. fast_agent/resources/examples/tensorzero/.env.sample +2 -0
  188. fast_agent/resources/examples/tensorzero/Makefile +31 -0
  189. fast_agent/resources/examples/tensorzero/README.md +56 -0
  190. fast_agent/resources/examples/tensorzero/agent.py +35 -0
  191. fast_agent/resources/examples/tensorzero/demo_images/clam.jpg +0 -0
  192. fast_agent/resources/examples/tensorzero/demo_images/crab.png +0 -0
  193. fast_agent/resources/examples/tensorzero/demo_images/shrimp.png +0 -0
  194. fast_agent/resources/examples/tensorzero/docker-compose.yml +105 -0
  195. fast_agent/resources/examples/tensorzero/fastagent.config.yaml +19 -0
  196. fast_agent/resources/examples/tensorzero/image_demo.py +67 -0
  197. fast_agent/resources/examples/tensorzero/mcp_server/Dockerfile +25 -0
  198. fast_agent/resources/examples/tensorzero/mcp_server/entrypoint.sh +35 -0
  199. fast_agent/resources/examples/tensorzero/mcp_server/mcp_server.py +31 -0
  200. fast_agent/resources/examples/tensorzero/mcp_server/pyproject.toml +11 -0
  201. fast_agent/resources/examples/tensorzero/simple_agent.py +25 -0
  202. fast_agent/resources/examples/tensorzero/tensorzero_config/system_schema.json +29 -0
  203. fast_agent/resources/examples/tensorzero/tensorzero_config/system_template.minijinja +11 -0
  204. fast_agent/resources/examples/tensorzero/tensorzero_config/tensorzero.toml +35 -0
  205. fast_agent/resources/examples/workflows/agents_as_tools_extended.py +73 -0
  206. fast_agent/resources/examples/workflows/agents_as_tools_simple.py +50 -0
  207. fast_agent/resources/examples/workflows/chaining.py +37 -0
  208. fast_agent/resources/examples/workflows/evaluator.py +77 -0
  209. fast_agent/resources/examples/workflows/fastagent.config.yaml +26 -0
  210. fast_agent/resources/examples/workflows/graded_report.md +89 -0
  211. fast_agent/resources/examples/workflows/human_input.py +28 -0
  212. fast_agent/resources/examples/workflows/maker.py +156 -0
  213. fast_agent/resources/examples/workflows/orchestrator.py +70 -0
  214. fast_agent/resources/examples/workflows/parallel.py +56 -0
  215. fast_agent/resources/examples/workflows/router.py +69 -0
  216. fast_agent/resources/examples/workflows/short_story.md +13 -0
  217. fast_agent/resources/examples/workflows/short_story.txt +19 -0
  218. fast_agent/resources/setup/.gitignore +30 -0
  219. fast_agent/resources/setup/agent.py +28 -0
  220. fast_agent/resources/setup/fastagent.config.yaml +65 -0
  221. fast_agent/resources/setup/fastagent.secrets.yaml.example +38 -0
  222. fast_agent/resources/setup/pyproject.toml.tmpl +23 -0
  223. fast_agent/skills/__init__.py +9 -0
  224. fast_agent/skills/registry.py +235 -0
  225. fast_agent/tools/elicitation.py +369 -0
  226. fast_agent/tools/shell_runtime.py +402 -0
  227. fast_agent/types/__init__.py +59 -0
  228. fast_agent/types/conversation_summary.py +294 -0
  229. fast_agent/types/llm_stop_reason.py +78 -0
  230. fast_agent/types/message_search.py +249 -0
  231. fast_agent/ui/__init__.py +38 -0
  232. fast_agent/ui/console.py +59 -0
  233. fast_agent/ui/console_display.py +1080 -0
  234. fast_agent/ui/elicitation_form.py +946 -0
  235. fast_agent/ui/elicitation_style.py +59 -0
  236. fast_agent/ui/enhanced_prompt.py +1400 -0
  237. fast_agent/ui/history_display.py +734 -0
  238. fast_agent/ui/interactive_prompt.py +1199 -0
  239. fast_agent/ui/markdown_helpers.py +104 -0
  240. fast_agent/ui/markdown_truncator.py +1004 -0
  241. fast_agent/ui/mcp_display.py +857 -0
  242. fast_agent/ui/mcp_ui_utils.py +235 -0
  243. fast_agent/ui/mermaid_utils.py +169 -0
  244. fast_agent/ui/message_primitives.py +50 -0
  245. fast_agent/ui/notification_tracker.py +205 -0
  246. fast_agent/ui/plain_text_truncator.py +68 -0
  247. fast_agent/ui/progress_display.py +10 -0
  248. fast_agent/ui/rich_progress.py +195 -0
  249. fast_agent/ui/streaming.py +774 -0
  250. fast_agent/ui/streaming_buffer.py +449 -0
  251. fast_agent/ui/tool_display.py +422 -0
  252. fast_agent/ui/usage_display.py +204 -0
  253. fast_agent/utils/__init__.py +5 -0
  254. fast_agent/utils/reasoning_stream_parser.py +77 -0
  255. fast_agent/utils/time.py +22 -0
  256. fast_agent/workflow_telemetry.py +261 -0
  257. fast_agent_mcp-0.4.7.dist-info/METADATA +788 -0
  258. fast_agent_mcp-0.4.7.dist-info/RECORD +261 -0
  259. fast_agent_mcp-0.4.7.dist-info/WHEEL +4 -0
  260. fast_agent_mcp-0.4.7.dist-info/entry_points.txt +7 -0
  261. fast_agent_mcp-0.4.7.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,84 @@
1
+ """
2
+ Factory for resolving elicitation handlers with proper precedence.
3
+ """
4
+
5
+ from typing import Any
6
+
7
+ from mcp.client.session import ElicitationFnT
8
+
9
+ from fast_agent.agents.agent_types import AgentConfig
10
+ from fast_agent.core.logging.logger import get_logger
11
+ from fast_agent.mcp.elicitation_handlers import (
12
+ auto_cancel_elicitation_handler,
13
+ forms_elicitation_handler,
14
+ )
15
+
16
+ logger = get_logger(__name__)
17
+
18
+
19
+ def resolve_elicitation_handler(
20
+ agent_config: AgentConfig, app_config: Any, server_config: Any = None
21
+ ) -> ElicitationFnT | None:
22
+ """Resolve elicitation handler with proper precedence.
23
+
24
+ Precedence order:
25
+ 1. Agent decorator supplied (highest precedence)
26
+ 2. Server-specific config file setting
27
+ 3. Global config file setting
28
+ 4. Default forms handler (lowest precedence)
29
+
30
+ Args:
31
+ agent_config: Agent configuration from decorator
32
+ app_config: Application configuration from YAML
33
+ server_config: Server-specific configuration (optional)
34
+
35
+ Returns:
36
+ ElicitationFnT handler or None (no elicitation capability)
37
+ """
38
+
39
+ # 1. Decorator takes highest precedence
40
+ if agent_config.elicitation_handler:
41
+ logger.debug(f"Using decorator-provided elicitation handler for agent {agent_config.name}")
42
+ return agent_config.elicitation_handler
43
+
44
+ # 2. Check server-specific config first
45
+ if server_config:
46
+ elicitation_config = getattr(server_config, "elicitation", {})
47
+ if isinstance(elicitation_config, dict):
48
+ mode = elicitation_config.get("mode")
49
+ else:
50
+ mode = getattr(elicitation_config, "mode", None)
51
+
52
+ if mode:
53
+ if mode == "none":
54
+ logger.debug(f"Elicitation disabled by server config for agent {agent_config.name}")
55
+ return None # Don't advertise elicitation capability
56
+ elif mode == "auto-cancel":
57
+ logger.debug(
58
+ f"Using auto-cancel elicitation handler (server config) for agent {agent_config.name}"
59
+ )
60
+ return auto_cancel_elicitation_handler
61
+ else: # "forms" or other
62
+ logger.debug(
63
+ f"Using forms elicitation handler (server config) for agent {agent_config.name}"
64
+ )
65
+ return forms_elicitation_handler
66
+
67
+ # 3. Check global config file
68
+ elicitation_config = getattr(app_config, "elicitation", {})
69
+ if isinstance(elicitation_config, dict):
70
+ mode = elicitation_config.get("mode", "forms")
71
+ else:
72
+ mode = getattr(elicitation_config, "mode", "forms")
73
+
74
+ if mode == "none":
75
+ logger.debug(f"Elicitation disabled by global config for agent {agent_config.name}")
76
+ return None # Don't advertise elicitation capability
77
+ elif mode == "auto-cancel":
78
+ logger.debug(
79
+ f"Using auto-cancel elicitation handler (global config) for agent {agent_config.name}"
80
+ )
81
+ return auto_cancel_elicitation_handler
82
+ else: # "forms" or default
83
+ logger.debug(f"Using default forms elicitation handler for agent {agent_config.name}")
84
+ return forms_elicitation_handler
@@ -0,0 +1,164 @@
1
+ """
2
+ Predefined elicitation handlers for different use cases.
3
+ """
4
+
5
+ import json
6
+ from typing import TYPE_CHECKING, Any
7
+
8
+ from mcp.shared.context import RequestContext
9
+ from mcp.types import ElicitRequestParams, ElicitResult, ErrorData
10
+
11
+ from fast_agent.core.logging.logger import get_logger
12
+ from fast_agent.human_input.elicitation_handler import elicitation_input_callback
13
+ from fast_agent.human_input.types import HumanInputRequest
14
+ from fast_agent.mcp.helpers.server_config_helpers import get_server_config
15
+
16
+ if TYPE_CHECKING:
17
+ from mcp import ClientSession
18
+
19
+ logger = get_logger(__name__)
20
+
21
+
22
+ async def auto_cancel_elicitation_handler(
23
+ context: RequestContext["ClientSession", Any],
24
+ params: ElicitRequestParams,
25
+ ) -> ElicitResult | ErrorData:
26
+ """Handler that automatically cancels all elicitation requests.
27
+
28
+ Useful for production deployments where you want to advertise elicitation
29
+ capability but automatically decline all requests.
30
+ """
31
+ logger.info(f"Auto-cancelling elicitation request: {params.message}")
32
+ return ElicitResult(action="cancel")
33
+
34
+
35
+ async def forms_elicitation_handler(
36
+ context: RequestContext["ClientSession", Any], params: ElicitRequestParams
37
+ ) -> ElicitResult:
38
+ """
39
+ Interactive forms-based elicitation handler using enhanced input handler.
40
+ """
41
+ logger.info(f"Eliciting response for params: {params}")
42
+
43
+ # Get server config for additional context
44
+ server_config = get_server_config(context)
45
+ server_name = server_config.name if server_config else "Unknown Server"
46
+ server_info = (
47
+ {"command": server_config.command} if server_config and server_config.command else None
48
+ )
49
+
50
+ # Get agent name - try multiple sources in order of preference
51
+ agent_name: str | None = None
52
+
53
+ # 1. Check if we have an MCPAgentClientSession in the context
54
+ from fast_agent.mcp.mcp_agent_client_session import MCPAgentClientSession
55
+
56
+ if hasattr(context, "session") and isinstance(context.session, MCPAgentClientSession):
57
+ agent_name = context.session.agent_name
58
+
59
+ # 2. If no agent name yet, use a sensible default
60
+ if not agent_name:
61
+ agent_name = "Unknown Agent"
62
+
63
+ # Create human input request
64
+ request = HumanInputRequest(
65
+ prompt=params.message,
66
+ description=f"Schema: {params.requestedSchema}" if params.requestedSchema else None,
67
+ request_id=f"elicit_{id(params)}",
68
+ metadata={
69
+ "agent_name": agent_name,
70
+ "server_name": server_name,
71
+ "elicitation": True,
72
+ "requested_schema": params.requestedSchema,
73
+ },
74
+ )
75
+
76
+ try:
77
+ # Call the enhanced elicitation handler
78
+ response = await elicitation_input_callback(
79
+ request=request,
80
+ agent_name=agent_name,
81
+ server_name=server_name,
82
+ server_info=server_info,
83
+ )
84
+
85
+ # Check for special action responses
86
+ response_data = response.response.strip()
87
+
88
+ # Handle special responses
89
+ if response_data == "__DECLINED__":
90
+ return ElicitResult(action="decline")
91
+ elif response_data == "__CANCELLED__":
92
+ return ElicitResult(action="cancel")
93
+ elif response_data == "__DISABLE_SERVER__":
94
+ # Respect user's request: disable elicitation for this server for this session
95
+ logger.warning(
96
+ f"User requested to disable elicitation for server: {server_name} — disabling for session"
97
+ )
98
+ try:
99
+ from fast_agent.human_input.elicitation_state import elicitation_state
100
+
101
+ elicitation_state.disable_server(server_name)
102
+ except Exception:
103
+ # Do not fail the flow if state update fails
104
+ pass
105
+ return ElicitResult(action="cancel")
106
+
107
+ # Parse response based on schema if provided
108
+ if params.requestedSchema:
109
+ # Check if the response is already JSON (from our form)
110
+ try:
111
+ # Try to parse as JSON first (from schema-driven form)
112
+ content = json.loads(response_data)
113
+ # Validate that all required fields are present
114
+ required_fields = params.requestedSchema.get("required", [])
115
+ for field in required_fields:
116
+ if field not in content:
117
+ logger.warning(f"Missing required field '{field}' in elicitation response")
118
+ return ElicitResult(action="decline")
119
+ except json.JSONDecodeError:
120
+ # Not JSON, try to handle as simple text response
121
+ # This is a fallback for simple schemas or text-based responses
122
+ properties = params.requestedSchema.get("properties", {})
123
+ if len(properties) == 1:
124
+ # Single field schema - try to parse based on type
125
+ field_name = next(iter(properties))
126
+ field_def = properties[field_name]
127
+ field_type = field_def.get("type")
128
+
129
+ if field_type == "boolean":
130
+ # Parse boolean values
131
+ if response_data.lower() in ["yes", "y", "true", "1"]:
132
+ content = {field_name: True}
133
+ elif response_data.lower() in ["no", "n", "false", "0"]:
134
+ content = {field_name: False}
135
+ else:
136
+ return ElicitResult(action="decline")
137
+ elif field_type == "string":
138
+ content = {field_name: response_data}
139
+ elif field_type in ["number", "integer"]:
140
+ try:
141
+ value = (
142
+ int(response_data)
143
+ if field_type == "integer"
144
+ else float(response_data)
145
+ )
146
+ content = {field_name: value}
147
+ except ValueError:
148
+ return ElicitResult(action="decline")
149
+ else:
150
+ # Unknown type, just pass as string
151
+ content = {field_name: response_data}
152
+ else:
153
+ # Multiple fields but text response - can't parse reliably
154
+ logger.warning("Text response provided for multi-field schema")
155
+ return ElicitResult(action="decline")
156
+ else:
157
+ # No schema, just return the raw response
158
+ content = {"response": response_data}
159
+
160
+ # Return the response wrapped in ElicitResult with accept action
161
+ return ElicitResult(action="accept", content=content)
162
+ except (KeyboardInterrupt, EOFError, TimeoutError):
163
+ # User cancelled or timeout
164
+ return ElicitResult(action="cancel")
@@ -0,0 +1,83 @@
1
+ from contextlib import asynccontextmanager
2
+ from datetime import timedelta
3
+ from typing import AsyncGenerator, Callable
4
+
5
+ from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
6
+ from mcp import ClientSession
7
+
8
+ from fast_agent.core.logging.logger import get_logger
9
+ from fast_agent.mcp.interfaces import ServerRegistryProtocol
10
+ from fast_agent.mcp.mcp_agent_client_session import MCPAgentClientSession
11
+
12
+ logger = get_logger(__name__)
13
+
14
+
15
+ @asynccontextmanager
16
+ async def gen_client(
17
+ server_name: str,
18
+ server_registry: ServerRegistryProtocol,
19
+ client_session_factory: Callable[
20
+ [MemoryObjectReceiveStream, MemoryObjectSendStream, timedelta | None],
21
+ ClientSession,
22
+ ] = MCPAgentClientSession,
23
+ ) -> AsyncGenerator[ClientSession, None]:
24
+ """
25
+ Create a client session to the specified server.
26
+ Handles server startup, initialization, and message receive loop setup.
27
+ If required, callers can specify their own message receive loop and ClientSession class constructor to customize further.
28
+ For persistent connections, use connect() or MCPConnectionManager instead.
29
+ """
30
+ if not server_registry:
31
+ raise ValueError(
32
+ "Server registry not found in the context. Please specify one either on this method, or in the context."
33
+ )
34
+
35
+ async with server_registry.initialize_server(
36
+ server_name=server_name,
37
+ client_session_factory=client_session_factory,
38
+ ) as session:
39
+ yield session
40
+
41
+
42
+ async def connect(
43
+ server_name: str,
44
+ server_registry: ServerRegistryProtocol,
45
+ client_session_factory: Callable[
46
+ [MemoryObjectReceiveStream, MemoryObjectSendStream, timedelta | None],
47
+ ClientSession,
48
+ ] = MCPAgentClientSession,
49
+ ) -> ClientSession:
50
+ """
51
+ Create a persistent client session to the specified server.
52
+ Handles server startup, initialization, and message receive loop setup.
53
+ If required, callers can specify their own message receive loop and ClientSession class constructor to customize further.
54
+ """
55
+ if not server_registry:
56
+ raise ValueError(
57
+ "Server registry not found in the context. Please specify one either on this method, or in the context."
58
+ )
59
+
60
+ server_connection = await server_registry.connection_manager.get_server(
61
+ server_name=server_name,
62
+ client_session_factory=client_session_factory,
63
+ )
64
+
65
+ return server_connection.session
66
+
67
+
68
+ async def disconnect(
69
+ server_name: str | None,
70
+ server_registry: ServerRegistryProtocol,
71
+ ) -> None:
72
+ """
73
+ Disconnect from the specified server. If server_name is None, disconnect from all servers.
74
+ """
75
+ if not server_registry:
76
+ raise ValueError(
77
+ "Server registry not found in the context. Please specify one either on this method, or in the context."
78
+ )
79
+
80
+ if server_name:
81
+ await server_registry.connection_manager.disconnect_server(server_name=server_name)
82
+ else:
83
+ await server_registry.connection_manager.disconnect_all_servers()
@@ -0,0 +1,36 @@
1
+ """
2
+ Helper modules for working with MCP content (Fast Agent namespace).
3
+
4
+ This mirrors the legacy fast_agent.mcp.helpers API to provide a stable,
5
+ cycle-free import path now that PromptMessageExtended lives in fast_agent.mcp.
6
+ """
7
+
8
+ from .content_helpers import (
9
+ ensure_multipart_messages,
10
+ get_image_data,
11
+ get_resource_text,
12
+ get_resource_uri,
13
+ get_text,
14
+ is_image_content,
15
+ is_resource_content,
16
+ is_resource_link,
17
+ is_text_content,
18
+ normalize_to_extended_list,
19
+ split_thinking_content,
20
+ text_content,
21
+ )
22
+
23
+ __all__ = [
24
+ "get_text",
25
+ "get_image_data",
26
+ "get_resource_uri",
27
+ "is_text_content",
28
+ "is_image_content",
29
+ "is_resource_content",
30
+ "is_resource_link",
31
+ "get_resource_text",
32
+ "ensure_multipart_messages",
33
+ "normalize_to_extended_list",
34
+ "split_thinking_content",
35
+ "text_content",
36
+ ]