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,514 @@
1
+ """Run an interactive agent directly from the command line."""
2
+
3
+ import asyncio
4
+ import logging
5
+ import shlex
6
+ import sys
7
+ from pathlib import Path
8
+ from typing import Literal
9
+
10
+ import typer
11
+
12
+ from fast_agent import FastAgent
13
+ from fast_agent.agents.llm_agent import LlmAgent
14
+ from fast_agent.cli.commands.server_helpers import add_servers_to_config, generate_server_name
15
+ from fast_agent.cli.commands.url_parser import generate_server_configs, parse_server_urls
16
+ from fast_agent.constants import DEFAULT_AGENT_INSTRUCTION
17
+ from fast_agent.ui.console_display import ConsoleDisplay
18
+
19
+ app = typer.Typer(
20
+ help="Run an interactive agent directly from the command line without creating an agent.py file",
21
+ context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
22
+ )
23
+
24
+ default_instruction = DEFAULT_AGENT_INSTRUCTION
25
+
26
+
27
+ def resolve_instruction_option(instruction: str | None) -> tuple[str, str]:
28
+ """
29
+ Resolve the instruction option (file or URL) to the instruction string and agent name.
30
+ Returns (resolved_instruction, agent_name).
31
+ """
32
+ resolved_instruction = default_instruction
33
+ agent_name = "agent"
34
+
35
+ if instruction:
36
+ try:
37
+ from pathlib import Path
38
+
39
+ from pydantic import AnyUrl
40
+
41
+ from fast_agent.core.direct_decorators import _resolve_instruction
42
+
43
+ if instruction.startswith(("http://", "https://")):
44
+ resolved_instruction = _resolve_instruction(AnyUrl(instruction))
45
+ else:
46
+ resolved_instruction = _resolve_instruction(Path(instruction))
47
+ instruction_path = Path(instruction)
48
+ if instruction_path.exists() and instruction_path.is_file():
49
+ agent_name = instruction_path.stem
50
+ except Exception as e:
51
+ typer.echo(f"Error loading instruction from {instruction}: {e}", err=True)
52
+ raise typer.Exit(1)
53
+
54
+ return resolved_instruction, agent_name
55
+
56
+
57
+ def collect_stdio_commands(npx: str | None, uvx: str | None, stdio: str | None) -> list[str]:
58
+ """Collect STDIO command definitions from convenience options."""
59
+ stdio_commands: list[str] = []
60
+
61
+ if npx:
62
+ stdio_commands.append(f"npx {npx}")
63
+ if uvx:
64
+ stdio_commands.append(f"uvx {uvx}")
65
+ if stdio:
66
+ stdio_commands.append(stdio)
67
+
68
+ return stdio_commands
69
+
70
+
71
+ def _set_asyncio_exception_handler(loop: asyncio.AbstractEventLoop) -> None:
72
+ """Attach a detailed exception handler to the provided event loop."""
73
+
74
+ logger = logging.getLogger("fast_agent.asyncio")
75
+
76
+ def _handler(_loop: asyncio.AbstractEventLoop, context: dict) -> None:
77
+ message = context.get("message", "(no message)")
78
+ task = context.get("task")
79
+ future = context.get("future")
80
+ handle = context.get("handle")
81
+ source_traceback = context.get("source_traceback")
82
+ exception = context.get("exception")
83
+
84
+ details = {
85
+ "message": message,
86
+ "task": repr(task) if task else None,
87
+ "future": repr(future) if future else None,
88
+ "handle": repr(handle) if handle else None,
89
+ "source_traceback": [str(frame) for frame in source_traceback]
90
+ if source_traceback
91
+ else None,
92
+ }
93
+
94
+ logger.error("Unhandled asyncio error: %s", message)
95
+ logger.error("Asyncio context: %s", details)
96
+
97
+ if exception:
98
+ logger.exception("Asyncio exception", exc_info=exception)
99
+
100
+ try:
101
+ loop.set_exception_handler(_handler)
102
+ except Exception:
103
+ logger = logging.getLogger("fast_agent.asyncio")
104
+ logger.exception("Failed to set asyncio exception handler")
105
+
106
+
107
+ async def _run_agent(
108
+ name: str = "fast-agent cli",
109
+ instruction: str = default_instruction,
110
+ config_path: str | None = None,
111
+ server_list: list[str] | None = None,
112
+ model: str | None = None,
113
+ message: str | None = None,
114
+ prompt_file: str | None = None,
115
+ url_servers: dict[str, dict[str, str]] | None = None,
116
+ stdio_servers: dict[str, dict[str, str]] | None = None,
117
+ agent_name: str | None = "agent",
118
+ skills_directory: Path | None = None,
119
+ shell_runtime: bool = False,
120
+ mode: Literal["interactive", "serve"] = "interactive",
121
+ transport: str = "http",
122
+ host: str = "0.0.0.0",
123
+ port: int = 8000,
124
+ tool_description: str | None = None,
125
+ instance_scope: str = "shared",
126
+ permissions_enabled: bool = True,
127
+ ) -> None:
128
+ """Async implementation to run an interactive agent."""
129
+ from fast_agent.mcp.prompts.prompt_load import load_prompt
130
+
131
+ # Create the FastAgent instance
132
+
133
+ fast_kwargs = {
134
+ "name": name,
135
+ "config_path": config_path,
136
+ "ignore_unknown_args": True,
137
+ "parse_cli_args": False, # Don't parse CLI args, we're handling it ourselves
138
+ }
139
+ if mode == "serve":
140
+ fast_kwargs["quiet"] = True
141
+ if skills_directory is not None:
142
+ fast_kwargs["skills_directory"] = skills_directory
143
+
144
+ fast = FastAgent(**fast_kwargs)
145
+
146
+ # Set model on args so model source detection works correctly
147
+ if model:
148
+ fast.args.model = model
149
+
150
+ if shell_runtime:
151
+ await fast.app.initialize()
152
+ setattr(fast.app.context, "shell_runtime", True)
153
+
154
+ # Add all dynamic servers to the configuration
155
+ await add_servers_to_config(fast, url_servers)
156
+ await add_servers_to_config(fast, stdio_servers)
157
+
158
+ # Check if we have multiple models (comma-delimited)
159
+ if model and "," in model:
160
+ # Parse multiple models
161
+ models = [m.strip() for m in model.split(",") if m.strip()]
162
+
163
+ # Create an agent for each model
164
+ fan_out_agents = []
165
+ for i, model_name in enumerate(models):
166
+ agent_name = f"{model_name}"
167
+
168
+ # Define the agent with specified parameters
169
+ agent_kwargs = {"instruction": instruction, "name": agent_name}
170
+ if server_list:
171
+ agent_kwargs["servers"] = server_list
172
+ agent_kwargs["model"] = model_name
173
+
174
+ @fast.agent(**agent_kwargs)
175
+ async def model_agent():
176
+ pass
177
+
178
+ fan_out_agents.append(agent_name)
179
+
180
+ # Create a silent fan-in agent (suppresses display output)
181
+ class SilentFanInAgent(LlmAgent):
182
+ async def show_assistant_message(self, *args, **kwargs): # type: ignore[override]
183
+ return None
184
+
185
+ def show_user_message(self, *args, **kwargs): # type: ignore[override]
186
+ return None
187
+
188
+ @fast.custom(
189
+ SilentFanInAgent,
190
+ name="aggregate",
191
+ model="passthrough",
192
+ instruction="You aggregate parallel outputs without displaying intermediate messages.",
193
+ )
194
+ async def aggregate():
195
+ pass
196
+
197
+ # Create a parallel agent with silent fan_in
198
+ @fast.parallel(
199
+ name="parallel",
200
+ fan_out=fan_out_agents,
201
+ fan_in="aggregate",
202
+ include_request=True,
203
+ default=True,
204
+ )
205
+ async def cli_agent():
206
+ async with fast.run() as agent:
207
+ if message:
208
+ await agent.parallel.send(message)
209
+ display = ConsoleDisplay(config=None)
210
+ display.show_parallel_results(agent.parallel)
211
+ elif prompt_file:
212
+ prompt = load_prompt(Path(prompt_file))
213
+ await agent.parallel.generate(prompt)
214
+ display = ConsoleDisplay(config=None)
215
+ display.show_parallel_results(agent.parallel)
216
+ else:
217
+ await agent.interactive(pretty_print_parallel=True)
218
+ else:
219
+ # Single model - use original behavior
220
+ # Define the agent with specified parameters
221
+ agent_kwargs = {"instruction": instruction}
222
+ if agent_name:
223
+ agent_kwargs["name"] = agent_name
224
+ if server_list:
225
+ agent_kwargs["servers"] = server_list
226
+ if model:
227
+ agent_kwargs["model"] = model
228
+
229
+ @fast.agent(**agent_kwargs)
230
+ async def cli_agent():
231
+ async with fast.run() as agent:
232
+ if message:
233
+ response = await agent.send(message)
234
+ # Print the response and exit
235
+ print(response)
236
+ elif prompt_file:
237
+ prompt = load_prompt(Path(prompt_file))
238
+ response = await agent.agent.generate(prompt)
239
+ print(f"\nLoaded {len(prompt)} messages from prompt file '{prompt_file}'")
240
+ await agent.interactive()
241
+ else:
242
+ await agent.interactive()
243
+
244
+ # Run the agent
245
+ if mode == "serve":
246
+ await fast.start_server(
247
+ transport=transport,
248
+ host=host,
249
+ port=port,
250
+ tool_description=tool_description,
251
+ instance_scope=instance_scope,
252
+ permissions_enabled=permissions_enabled,
253
+ )
254
+ else:
255
+ await cli_agent()
256
+
257
+
258
+ def run_async_agent(
259
+ name: str,
260
+ instruction: str,
261
+ config_path: str | None = None,
262
+ servers: str | None = None,
263
+ urls: str | None = None,
264
+ auth: str | None = None,
265
+ model: str | None = None,
266
+ message: str | None = None,
267
+ prompt_file: str | None = None,
268
+ stdio_commands: list[str] | None = None,
269
+ agent_name: str | None = None,
270
+ skills_directory: Path | None = None,
271
+ shell_enabled: bool = False,
272
+ mode: Literal["interactive", "serve"] = "interactive",
273
+ transport: str = "http",
274
+ host: str = "0.0.0.0",
275
+ port: int = 8000,
276
+ tool_description: str | None = None,
277
+ instance_scope: str = "shared",
278
+ permissions_enabled: bool = True,
279
+ ):
280
+ """Run the async agent function with proper loop handling."""
281
+ server_list = servers.split(",") if servers else None
282
+
283
+ # Parse URLs and generate server configurations if provided
284
+ url_servers = None
285
+ if urls:
286
+ try:
287
+ parsed_urls = parse_server_urls(urls, auth)
288
+ url_servers = generate_server_configs(parsed_urls)
289
+ # If we have servers from URLs, add their names to the server_list
290
+ if url_servers and not server_list:
291
+ server_list = list(url_servers.keys())
292
+ elif url_servers and server_list:
293
+ # Merge both lists
294
+ server_list.extend(list(url_servers.keys()))
295
+ except ValueError as e:
296
+ print(f"Error parsing URLs: {e}", file=sys.stderr)
297
+ sys.exit(1)
298
+
299
+ # Generate STDIO server configurations if provided
300
+ stdio_servers = None
301
+
302
+ if stdio_commands:
303
+ stdio_servers = {}
304
+ for i, stdio_cmd in enumerate(stdio_commands):
305
+ # Parse the stdio command string
306
+ try:
307
+ parsed_command = shlex.split(stdio_cmd)
308
+ if not parsed_command:
309
+ print(f"Error: Empty stdio command: {stdio_cmd}", file=sys.stderr)
310
+ continue
311
+
312
+ command = parsed_command[0]
313
+ initial_args = parsed_command[1:] if len(parsed_command) > 1 else []
314
+
315
+ # Generate a server name from the command
316
+ if initial_args:
317
+ # Try to extract a meaningful name from the args
318
+ for arg in initial_args:
319
+ if arg.endswith(".py") or arg.endswith(".js") or arg.endswith(".ts"):
320
+ base_name = generate_server_name(arg)
321
+ break
322
+ else:
323
+ # Fallback to command name
324
+ base_name = generate_server_name(command)
325
+ else:
326
+ base_name = generate_server_name(command)
327
+
328
+ # Ensure unique server names when multiple servers
329
+ server_name = base_name
330
+ if len(stdio_commands) > 1:
331
+ server_name = f"{base_name}_{i + 1}"
332
+
333
+ # Build the complete args list
334
+ stdio_command_args = initial_args.copy()
335
+
336
+ # Add this server to the configuration
337
+ stdio_servers[server_name] = {
338
+ "transport": "stdio",
339
+ "command": command,
340
+ "args": stdio_command_args,
341
+ }
342
+
343
+ # Add STDIO server to the server list
344
+ if not server_list:
345
+ server_list = [server_name]
346
+ else:
347
+ server_list.append(server_name)
348
+
349
+ except ValueError as e:
350
+ print(f"Error parsing stdio command '{stdio_cmd}': {e}", file=sys.stderr)
351
+ continue
352
+
353
+ # Check if we're already in an event loop
354
+ try:
355
+ loop = asyncio.get_event_loop()
356
+ if loop.is_running():
357
+ # We're inside a running event loop, so we can't use asyncio.run
358
+ # Instead, create a new loop
359
+ loop = asyncio.new_event_loop()
360
+ asyncio.set_event_loop(loop)
361
+ _set_asyncio_exception_handler(loop)
362
+ except RuntimeError:
363
+ # No event loop exists, so we'll create one
364
+ loop = asyncio.new_event_loop()
365
+ asyncio.set_event_loop(loop)
366
+ _set_asyncio_exception_handler(loop)
367
+
368
+ try:
369
+ loop.run_until_complete(
370
+ _run_agent(
371
+ name=name,
372
+ instruction=instruction,
373
+ config_path=config_path,
374
+ server_list=server_list,
375
+ model=model,
376
+ message=message,
377
+ prompt_file=prompt_file,
378
+ url_servers=url_servers,
379
+ stdio_servers=stdio_servers,
380
+ agent_name=agent_name,
381
+ skills_directory=skills_directory,
382
+ shell_runtime=shell_enabled,
383
+ mode=mode,
384
+ transport=transport,
385
+ host=host,
386
+ port=port,
387
+ tool_description=tool_description,
388
+ instance_scope=instance_scope,
389
+ permissions_enabled=permissions_enabled,
390
+ )
391
+ )
392
+ finally:
393
+ try:
394
+ # Clean up the loop
395
+ tasks = asyncio.all_tasks(loop)
396
+ for task in tasks:
397
+ task.cancel()
398
+
399
+ # Run the event loop until all tasks are done
400
+ if sys.version_info >= (3, 7):
401
+ loop.run_until_complete(asyncio.gather(*tasks, return_exceptions=True))
402
+ loop.run_until_complete(loop.shutdown_asyncgens())
403
+ loop.close()
404
+ except Exception:
405
+ pass
406
+
407
+
408
+ @app.callback(invoke_without_command=True, no_args_is_help=False)
409
+ def go(
410
+ ctx: typer.Context,
411
+ name: str = typer.Option("fast-agent", "--name", help="Name for the agent"),
412
+ instruction: str | None = typer.Option(
413
+ None, "--instruction", "-i", help="Path to file or URL containing instruction for the agent"
414
+ ),
415
+ config_path: str | None = typer.Option(None, "--config-path", "-c", help="Path to config file"),
416
+ servers: str | None = typer.Option(
417
+ None, "--servers", help="Comma-separated list of server names to enable from config"
418
+ ),
419
+ urls: str | None = typer.Option(
420
+ None, "--url", help="Comma-separated list of HTTP/SSE URLs to connect to"
421
+ ),
422
+ auth: str | None = typer.Option(
423
+ None, "--auth", help="Bearer token for authorization with URL-based servers"
424
+ ),
425
+ model: str | None = typer.Option(
426
+ None, "--model", "--models", help="Override the default model (e.g., haiku, sonnet, gpt-4)"
427
+ ),
428
+ message: str | None = typer.Option(
429
+ None, "--message", "-m", help="Message to send to the agent (skips interactive mode)"
430
+ ),
431
+ prompt_file: str | None = typer.Option(
432
+ None, "--prompt-file", "-p", help="Path to a prompt file to use (either text or JSON)"
433
+ ),
434
+ skills_dir: Path | None = typer.Option(
435
+ None,
436
+ "--skills-dir",
437
+ "--skills",
438
+ help="Override the default skills directory",
439
+ ),
440
+ npx: str | None = typer.Option(
441
+ None, "--npx", help="NPX package and args to run as MCP server (quoted)"
442
+ ),
443
+ uvx: str | None = typer.Option(
444
+ None, "--uvx", help="UVX package and args to run as MCP server (quoted)"
445
+ ),
446
+ stdio: str | None = typer.Option(
447
+ None, "--stdio", help="Command to run as STDIO MCP server (quoted)"
448
+ ),
449
+ shell: bool = typer.Option(
450
+ False,
451
+ "--shell",
452
+ "-x",
453
+ help="Enable a local shell runtime and expose the execute tool (bash or pwsh).",
454
+ ),
455
+ ) -> None:
456
+ """
457
+ Run an interactive agent directly from the command line.
458
+
459
+ Examples:
460
+ fast-agent go --model=haiku --instruction=./instruction.md --servers=fetch,filesystem
461
+ fast-agent go --instruction=https://raw.githubusercontent.com/user/repo/prompt.md
462
+ fast-agent go --message="What is the weather today?" --model=haiku
463
+ fast-agent go --prompt-file=my-prompt.txt --model=haiku
464
+ fast-agent go --url=http://localhost:8001/mcp,http://api.example.com/sse
465
+ fast-agent go --url=https://api.example.com/mcp --auth=YOUR_API_TOKEN
466
+ fast-agent go --npx "@modelcontextprotocol/server-filesystem /path/to/data"
467
+ fast-agent go --uvx "mcp-server-fetch --verbose"
468
+ fast-agent go --stdio "python my_server.py --debug"
469
+ fast-agent go --stdio "uv run server.py --config=settings.json"
470
+ fast-agent go --skills /path/to/myskills -x
471
+
472
+ This will start an interactive session with the agent, using the specified model
473
+ and instruction. It will use the default configuration from fastagent.config.yaml
474
+ unless --config-path is specified.
475
+
476
+ Common options:
477
+ --model Override the default model (e.g., --model=haiku)
478
+ --quiet Disable progress display and logging
479
+ --servers Comma-separated list of server names to enable from config
480
+ --url Comma-separated list of HTTP/SSE URLs to connect to
481
+ --auth Bearer token for authorization with URL-based servers
482
+ --message, -m Send a single message and exit
483
+ --prompt-file, -p Use a prompt file instead of interactive mode
484
+ --skills Override the default skills folder
485
+ --shell, -x Enable local shell runtime
486
+ --npx NPX package and args to run as MCP server (quoted)
487
+ --uvx UVX package and args to run as MCP server (quoted)
488
+ --stdio Command to run as STDIO MCP server (quoted)
489
+ """
490
+ # Collect all stdio commands from convenience options
491
+ stdio_commands = collect_stdio_commands(npx, uvx, stdio)
492
+ shell_enabled = shell
493
+
494
+ # When shell is enabled we don't add an MCP stdio server; handled inside the agent
495
+
496
+ # Resolve instruction from file/URL or use default
497
+ resolved_instruction, agent_name = resolve_instruction_option(instruction)
498
+
499
+ run_async_agent(
500
+ name=name,
501
+ instruction=resolved_instruction,
502
+ config_path=config_path,
503
+ servers=servers,
504
+ urls=urls,
505
+ auth=auth,
506
+ model=model,
507
+ message=message,
508
+ prompt_file=prompt_file,
509
+ stdio_commands=stdio_commands,
510
+ agent_name=agent_name,
511
+ skills_directory=skills_dir,
512
+ shell_enabled=shell_enabled,
513
+ instance_scope="shared",
514
+ )