fast-agent-mcp 0.2.21__py3-none-any.whl → 0.2.23__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.
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fast-agent-mcp
3
- Version: 0.2.21
3
+ Version: 0.2.23
4
4
  Summary: Define, Prompt and Test MCP enabled Agents and Workflows
5
- Author-email: Shaun Smith <fastagent@llmindset.co.uk>, Sarmad Qadri <sarmad@lastmileai.dev>
5
+ Author-email: Shaun Smith <fastagent@llmindset.co.uk>
6
6
  License: Apache License
7
7
  Version 2.0, January 2004
8
8
  http://www.apache.org/licenses/
@@ -213,7 +213,7 @@ Requires-Dist: a2a-types>=0.1.0
213
213
  Requires-Dist: aiohttp>=3.11.13
214
214
  Requires-Dist: anthropic>=0.49.0
215
215
  Requires-Dist: fastapi>=0.115.6
216
- Requires-Dist: mcp~=1.7.0
216
+ Requires-Dist: mcp>=1.8.0
217
217
  Requires-Dist: openai>=1.63.2
218
218
  Requires-Dist: opentelemetry-distro>=0.50b0
219
219
  Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.29.0
@@ -224,6 +224,7 @@ Requires-Dist: pydantic-settings>=2.7.0
224
224
  Requires-Dist: pydantic>=2.10.4
225
225
  Requires-Dist: pyyaml>=6.0.2
226
226
  Requires-Dist: rich>=13.9.4
227
+ Requires-Dist: tensorzero>=2025.4.7
227
228
  Requires-Dist: typer>=0.15.1
228
229
  Provides-Extra: dev
229
230
  Requires-Dist: anthropic>=0.42.0; extra == 'dev'
@@ -260,6 +261,7 @@ The simple declarative syntax lets you concentrate on composing your Prompts and
260
261
  `fast-agent` is multi-modal, supporting Images and PDFs for both Anthropic and OpenAI endpoints via Prompts, Resources and MCP Tool Call results. The inclusion of passthrough and playback LLMs enable rapid development and test of Python glue-code for your applications.
261
262
 
262
263
  > [!IMPORTANT]
264
+ >
263
265
  > `fast-agent` The fast-agent documentation repo is here: https://github.com/evalstate/fast-agent-docs. Please feel free to submit PRs for documentation, experience reports or other content you think others may find helpful. All help and feedback warmly received.
264
266
 
265
267
  ### Agent Application Development
@@ -277,12 +279,12 @@ Simple model selection makes testing Model <-> MCP Server interaction painless.
277
279
  Start by installing the [uv package manager](https://docs.astral.sh/uv/) for Python. Then:
278
280
 
279
281
  ```bash
280
- uv pip install fast-agent-mcp # install fast-agent!
282
+ uv pip install fast-agent-mcp # install fast-agent!
281
283
 
282
- fast-agent setup # create an example agent and config files
283
- uv run agent.py # run your first agent
284
- uv run agent.py --model=o3-mini.low # specify a model
285
- fast-agent quickstart workflow # create "building effective agents" examples
284
+ uv run fast-agent setup # create an example agent and config files
285
+ uv run agent.py # run your first agent
286
+ uv run agent.py --model=o3-mini.low # specify a model
287
+ uv run fast-agent quickstart workflow # create "building effective agents" examples
286
288
  ```
287
289
 
288
290
  Other quickstart examples include a Researcher Agent (with Evaluator-Optimizer workflow) and Data Analysis Agent (similar to the ChatGPT experience), demonstrating MCP Roots support.
@@ -1,11 +1,11 @@
1
1
  mcp_agent/__init__.py,sha256=18T0AG0W9sJhTY38O9GFFOzliDhxx9p87CvRyti9zbw,1620
2
2
  mcp_agent/app.py,sha256=WRsiUdwy_9IAnaGRDwuLm7pzgQpt2wgsg10vBOpfcwM,5539
3
- mcp_agent/config.py,sha256=_b5JeS2nWHScSUUTu6wYxXzdfKefoqII305ecKcw7Gs,12248
3
+ mcp_agent/config.py,sha256=L_wUWTdqFXaRTBA5tL_j2l_9dufWE_MHHPut5e89lBk,12773
4
4
  mcp_agent/console.py,sha256=Gjf2QLFumwG1Lav__c07X_kZxxEUSkzV-1_-YbAwcwo,813
5
5
  mcp_agent/context.py,sha256=Kb3s_0MolHx7AeTs1NVcY3ly-xFBd35o8LT7Srpx9is,7334
6
6
  mcp_agent/context_dependent.py,sha256=QXfhw3RaQCKfscEEBRGuZ3sdMWqkgShz2jJ1ivGGX1I,1455
7
- mcp_agent/event_progress.py,sha256=3dqk5Pn1tAG_m_wn4IPNwLWLyzm7CyKIidqHN-4l-JY,2736
8
- mcp_agent/mcp_server_registry.py,sha256=jUmCdfcpTitXm1-3TxpWsdRWY_8phdKNYgXwB16ZSVU,10100
7
+ mcp_agent/event_progress.py,sha256=b1VKlQQF2AgPMb6XHjlJAVoPdx8GuxRTUk2g-4lBNm0,2749
8
+ mcp_agent/mcp_server_registry.py,sha256=QTzu0elBWzqXks6u5nI5n8uN5CX8CpyV6ybxnyt5LZM,11531
9
9
  mcp_agent/progress_display.py,sha256=GeJU9VUt6qKsFVymG688hCMVCsAygG9ifiiEb5IcbN4,361
10
10
  mcp_agent/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  mcp_agent/agents/agent.py,sha256=GgaUHoilgqzh9PQYr5k2WiPj4pagwicf9-ZLFsHkNNo,3848
@@ -13,7 +13,7 @@ mcp_agent/agents/base_agent.py,sha256=fjDr01-hZ9sB3ghI4DlXYVePP0s5f9pmtLH-N3X8bR
13
13
  mcp_agent/agents/workflow/__init__.py,sha256=HloteEW6kalvgR0XewpiFAqaQlMPlPJYg5p3K33IUzI,25
14
14
  mcp_agent/agents/workflow/chain_agent.py,sha256=eIlImirrSXkqBJmPuAJgOKis81Cl6lZEGM0-6IyaUV8,6105
15
15
  mcp_agent/agents/workflow/evaluator_optimizer.py,sha256=ysUMGM2NzeCIutgr_vXH6kUPpZMw0cX4J_Wl1r8eT84,13296
16
- mcp_agent/agents/workflow/orchestrator_agent.py,sha256=byZe4bx7D_7BSZZ3hN8BNUWVFPYeqeUwDUCLTRC8mlI,21583
16
+ mcp_agent/agents/workflow/orchestrator_agent.py,sha256=lArV7wHwPYepSuxe0ybTGJRJv85iebRI4ZOY_m8kMZQ,21593
17
17
  mcp_agent/agents/workflow/orchestrator_models.py,sha256=5P_aXADVT4Et8qT4e1cb9RelmHX5dCRrzu8j8T41Kdg,7230
18
18
  mcp_agent/agents/workflow/orchestrator_prompts.py,sha256=EXKEI174sshkZyPPEnWbwwNafzSPuA39MXL7iqG9cWc,9106
19
19
  mcp_agent/agents/workflow/parallel_agent.py,sha256=JaQFp35nmAdoBRLAwx8BfnK7kirVq9PMw24LQ3ZEzoc,7705
@@ -23,9 +23,10 @@ mcp_agent/cli/__main__.py,sha256=AVZ7tQFhU_sDOGuUGJq8ujgKtcxsYJBJwHbVaaiRDlI,166
23
23
  mcp_agent/cli/main.py,sha256=XjrgXMBaPKkVqAFo8T9LJz6Tp1-ivrKDOuNYWke99YA,3090
24
24
  mcp_agent/cli/terminal.py,sha256=GRwD-RGW7saIz2IOWZn5vD6JjiArscELBThm1GTFkuI,1065
25
25
  mcp_agent/cli/commands/check_config.py,sha256=9Ryxo_fLInm3YKdYv46yLrAJgnQtMisGreu6Kkriw2g,16677
26
- mcp_agent/cli/commands/go.py,sha256=DJpmq4n-p5r8BXH10UqBOexmLND-zSODl5f-w4noR5Q,4304
26
+ mcp_agent/cli/commands/go.py,sha256=LIsOJQuTdfCUcNm7JT-NQDU8cI-GCnYwYjN2VOWxvqs,8658
27
27
  mcp_agent/cli/commands/quickstart.py,sha256=SM3CHMzDgvTxIpKjFuX9BrS_N1vRoXNBDaO90aWx1Rk,14586
28
28
  mcp_agent/cli/commands/setup.py,sha256=eOEd4TL-b0DaDeSJMGOfNOsTEItoZ67W88eTP4aP-bo,6482
29
+ mcp_agent/cli/commands/url_parser.py,sha256=7QL9bp9tO7w0cPnwhbpt8GwjbOJ1Rrry1o71uVJhSss,5655
29
30
  mcp_agent/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
31
  mcp_agent/core/agent_app.py,sha256=5nQJNo8DocIRWiX4pVKAHUZF8s6HWpc-hJnfzl_1v1c,9697
31
32
  mcp_agent/core/agent_types.py,sha256=bQVQMTwKH7qHIJsNglj4C_d6PNFBBzC_0RIkcENSII4,1459
@@ -34,11 +35,11 @@ mcp_agent/core/direct_factory.py,sha256=d96OM1yS3eIocIiaA9FQt6C2zr6VDUyCJBTZCp_D
34
35
  mcp_agent/core/enhanced_prompt.py,sha256=bzvcengS7XzHWB7NWhyxHM3hhO2HI4zP5DbGXAOw0Jw,19155
35
36
  mcp_agent/core/error_handling.py,sha256=xoyS2kLe0eG0bj2eSJCJ2odIhGUve2SbDR7jP-A-uRw,624
36
37
  mcp_agent/core/exceptions.py,sha256=ENAD_qGG67foxy6vDkIvc-lgopIUQy6O7zvNPpPXaQg,2289
37
- mcp_agent/core/fastagent.py,sha256=WEEGz2WBAddDGNeWJwqwFIPLiQnLjaNxZLoMR0peyyU,22884
38
+ mcp_agent/core/fastagent.py,sha256=uS_NSXeniUYFu6xce8OHGJ9PbEYNU-gm1XVpa1r0rZc,22893
38
39
  mcp_agent/core/interactive_prompt.py,sha256=w3VyRzW4hzn0xhWZRwo_qRRAD5WVSrJYe8QDe1XZ55Y,24252
39
40
  mcp_agent/core/mcp_content.py,sha256=2D7KHY9mG_vxoDwFLKvsPQV9VRIzHItM7V-jcEnACh8,8878
40
41
  mcp_agent/core/prompt.py,sha256=qnintOUGEoDPYLI9bu9G2OlgVMCe5ZPUZilgMzydXhc,7919
41
- mcp_agent/core/request_params.py,sha256=loYf13DN7e-DsdYRd37jWkJWJGwVBL-iFkcANP1J60Q,1366
42
+ mcp_agent/core/request_params.py,sha256=qmFWZXeYEJyYw2IwonyrTnZWxQG7qX6bKpOPcqETa60,1603
42
43
  mcp_agent/core/validation.py,sha256=RIBKFlh0GJg4rTcFQXoXp8A0sK1HpsCigKcYSK3gFaY,12090
43
44
  mcp_agent/executor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
45
  mcp_agent/executor/executor.py,sha256=E44p6d-o3OMRoP_dNs_cDnyti91LQ3P9eNU88mSi1kc,9462
@@ -48,26 +49,28 @@ mcp_agent/human_input/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
48
49
  mcp_agent/human_input/handler.py,sha256=s712Z5ssTCwjL9-VKoIdP5CtgMh43YvepynYisiWTTA,3144
49
50
  mcp_agent/human_input/types.py,sha256=RtWBOVzy8vnYoQrc36jRLn8z8N3C4pDPMBN5vF6qM5Y,1476
50
51
  mcp_agent/llm/__init__.py,sha256=d8zgwG-bRFuwiMNMYkywg_qytk4P8lawyld_meuUmHI,68
51
- mcp_agent/llm/augmented_llm.py,sha256=ASe604OhrMZ9dVoGEUEpUQaY6fFamz4gL8ttzWP_9m0,24212
52
+ mcp_agent/llm/augmented_llm.py,sha256=CqtSGo_QrHE73tz_DHMd0wdt2F41gwuUu5Bue51FNm4,24199
52
53
  mcp_agent/llm/augmented_llm_passthrough.py,sha256=zHcctNpwg4EFJvD1x9Eg443SVX-uyzFphLikwF_yVE0,6288
53
54
  mcp_agent/llm/augmented_llm_playback.py,sha256=6L_RWIK__R67oZK7u3Xt3hWy1T2LnHXIO-efqgP3tPw,4177
54
55
  mcp_agent/llm/memory.py,sha256=HQ_c1QemOUjrkY6Z2omE6BG5fXga7y4jN7KCMOuGjPs,3345
55
- mcp_agent/llm/model_factory.py,sha256=QK3RrEhpvafKRlTFYR8Z9oNJRGjO7J9wIx5TGcDu6As,7649
56
+ mcp_agent/llm/model_factory.py,sha256=h3NJSa0yPa9iiLojEqBhIm9wgEBB46ZBibe44MnskHM,8089
56
57
  mcp_agent/llm/prompt_utils.py,sha256=yWQHykoK13QRF7evHUKxVF0SpVLN-Bsft0Yixzvn0g0,4825
57
58
  mcp_agent/llm/provider_key_manager.py,sha256=-K_FuibN6hdSnweT32lB8mKTfCARnbja6zYYs0ErTKg,2802
58
- mcp_agent/llm/provider_types.py,sha256=3IRRt9UbIHZHVJq1KF1XYfzsQtF9gj_gBNtU3hukIaY,308
59
+ mcp_agent/llm/provider_types.py,sha256=oWwXTlyr6hIzU_QLJ5T-UwxZGo5e4Pjwtahz2cr1PHg,364
59
60
  mcp_agent/llm/sampling_converter.py,sha256=C7wPBlmT0eD90XWabC22zkxsrVHKCrjwIwg6cG628cI,2926
60
61
  mcp_agent/llm/sampling_format_converter.py,sha256=xGz4odHpOcP7--eFaJaFtUR8eR9jxZS7MnLH6J7n0EU,1263
61
62
  mcp_agent/llm/providers/__init__.py,sha256=heVxtmuqFJOnjjxHz4bWSqTAxXoN1E8twC_gQ_yJpHk,265
62
63
  mcp_agent/llm/providers/anthropic_utils.py,sha256=vYDN5G5jKMhD2CQg8veJYab7tvvzYkDMq8M1g_hUAQg,3275
63
- mcp_agent/llm/providers/augmented_llm_anthropic.py,sha256=RQ4r5Q84VJ_dyuNo23b-EMzvq6RrpspzIQWtfVUfw6M,15468
64
+ mcp_agent/llm/providers/augmented_llm_anthropic.py,sha256=gK_IvllVBNJUUrSfpgFpdhM-d4liCt0MLq7d2lXS7RI,15510
64
65
  mcp_agent/llm/providers/augmented_llm_deepseek.py,sha256=NiZK5nv91ZS2VgVFXpbsFNFYLsLcppcbo_RstlRMd7I,1145
65
66
  mcp_agent/llm/providers/augmented_llm_generic.py,sha256=5Uq8ZBhcFuQTt7koP_5ykolREh2iWu8zKhNbh3pM9lQ,1210
66
67
  mcp_agent/llm/providers/augmented_llm_google.py,sha256=N0a2fphVtkvNYxKQpEX6J4tlO1C_mRw4sw3LBXnrOeI,1130
67
- mcp_agent/llm/providers/augmented_llm_openai.py,sha256=TumZs1y678IvyvYIehf8xSDqYWqC44dWrIbqFGtz03g,14085
68
+ mcp_agent/llm/providers/augmented_llm_openai.py,sha256=0C7BOB7i3xo0HsMCTagRSQ8Hsywb-31mot26OfohzCU,14478
68
69
  mcp_agent/llm/providers/augmented_llm_openrouter.py,sha256=V_TlVKm92GHBxYIo6gpvH_6cAaIdppS25Tz6x5T7LW0,2341
70
+ mcp_agent/llm/providers/augmented_llm_tensorzero.py,sha256=Mol_Wzj_ZtccW-LMw0oFwWUt1m1yfofloay9QYNP23c,20729
69
71
  mcp_agent/llm/providers/multipart_converter_anthropic.py,sha256=t5lHYGfFUacJldnrVtMNW-8gEMoto8Y7hJkDrnyZR-Y,16650
70
72
  mcp_agent/llm/providers/multipart_converter_openai.py,sha256=XPIulWntNpZWNGWrc240StPzok2RqrDAV7OigDwQ1uU,15850
73
+ mcp_agent/llm/providers/multipart_converter_tensorzero.py,sha256=BFTdyVk42HZskDAuTHicfDTUJq89d1fz8C9nAOuHxlE,8646
71
74
  mcp_agent/llm/providers/openai_multipart.py,sha256=qKBn7d3jSabnJmVgWweVzqh8q9mBqr09fsPmP92niAQ,6899
72
75
  mcp_agent/llm/providers/openai_utils.py,sha256=T4bTCL9f7DsoS_zoKgQKv_FUv_4n98vgbvaUpdWZJr8,1875
73
76
  mcp_agent/llm/providers/sampling_converter_anthropic.py,sha256=35WzBWkPklnuMlu5S6XsQIq0YL58NOy8Ja6A_l4m6eM,1612
@@ -85,7 +88,7 @@ mcp_agent/mcp/interfaces.py,sha256=PAou8znAl2HgtvfCpLQOZFbKra9F72OcVRfBJbboNX8,6
85
88
  mcp_agent/mcp/logger_textio.py,sha256=vljC1BtNTCxBAda9ExqNB-FwVNUZIuJT3h1nWmCjMws,3172
86
89
  mcp_agent/mcp/mcp_agent_client_session.py,sha256=Ng7epBXq8BEA_3m1GX5LqwafgNUAMSzBugwN6N0VUWQ,4364
87
90
  mcp_agent/mcp/mcp_aggregator.py,sha256=lVSt0yp0CnaYjcHCWmluwBeFgl8JXHYEZk0MzXgrQzA,40110
88
- mcp_agent/mcp/mcp_connection_manager.py,sha256=L5Dk4cyarN_v2rfktkrfZJR4xUuD3yN_hUyQnKHBWgM,14044
91
+ mcp_agent/mcp/mcp_connection_manager.py,sha256=jlqaAdS4zc1UfVBHQU0TkTbVr0-rOkbN9bkrLPrZVLk,17159
89
92
  mcp_agent/mcp/mime_utils.py,sha256=difepNR_gpb4MpMLkBRAoyhDk-AjXUHTiqKvT_VwS1o,1805
90
93
  mcp_agent/mcp/prompt_message_multipart.py,sha256=BDwRdNwyWHb2q2bccDb2iR2VlORqVvkvoG3xYzcMpCE,4403
91
94
  mcp_agent/mcp/prompt_render.py,sha256=k3v4BZDThGE2gGiOYVQtA6x8WTEdOuXIEnRafANhN1U,2996
@@ -99,10 +102,10 @@ mcp_agent/mcp/prompts/__main__.py,sha256=gr1Tdz9fcK0EXjEuZg_BOnKUmvhYq5AH2lFZicV
99
102
  mcp_agent/mcp/prompts/prompt_constants.py,sha256=Q9W0t3rOXl2LHIG9wcghApUV2QZ1iICuo7SwVwHUf3c,566
100
103
  mcp_agent/mcp/prompts/prompt_helpers.py,sha256=Joqo2t09pTKDP-Wge3G-ozPEHikzjaqwV6GVk8hNR50,7534
101
104
  mcp_agent/mcp/prompts/prompt_load.py,sha256=Zo0FogqWFEG5FtF1d9ZH-RWsCSSMsi5FIEQHpJD8N7M,5404
102
- mcp_agent/mcp/prompts/prompt_server.py,sha256=DbuDcYCMbsbqwBeebpNGInAQ4-DP1Jjp49y8uZ-0XlY,18872
105
+ mcp_agent/mcp/prompts/prompt_server.py,sha256=VAKS4rHTE5Mp7e0NV6qADslR_5vSLab8RUhNxCkAdJE,19234
103
106
  mcp_agent/mcp/prompts/prompt_template.py,sha256=EejiqGkau8OizORNyKTUwUjrPof5V-hH1H_MBQoQfXw,15732
104
107
  mcp_agent/mcp_server/__init__.py,sha256=zBU51ITHIEPScd9nRafnhEddsWqXRPAAvHhkrbRI2_4,155
105
- mcp_agent/mcp_server/agent_server.py,sha256=s-nI0uTNWx4nYDDM_5GmuY5x6ZeFkymfNoCSuwuBRd8,19891
108
+ mcp_agent/mcp_server/agent_server.py,sha256=df3UbPLg52e_SS98F3lc4T8BqqzvQRBl6kplODsaq-M,20096
106
109
  mcp_agent/resources/examples/data-analysis/analysis-campaign.py,sha256=16gxrQ5kM8fb8tPwSCMXaitonk3PSEhz28njWwPxXrw,7269
107
110
  mcp_agent/resources/examples/data-analysis/analysis.py,sha256=M9z8Q4YC5OGuqSa5uefYmmfmctqMn-WqCSfg5LI407o,2609
108
111
  mcp_agent/resources/examples/data-analysis/fastagent.config.yaml,sha256=ini94PHyJCfgpjcjHKMMbGuHs6LIj46F1NwY0ll5HVk,1609
@@ -121,7 +124,7 @@ mcp_agent/resources/examples/internal/sizer.py,sha256=xP1TBJkp4xIdtJnyk2MP4BufTh
121
124
  mcp_agent/resources/examples/internal/social.py,sha256=pTKcpHAcvA-vQYgjVfDuU1FivCR004Nq4N2GXd5OMs0,1716
122
125
  mcp_agent/resources/examples/mcp/state-transfer/agent_one.py,sha256=HR-Igr8k68HU0tqIpaXujtJxnKSUwwtZqTdZk8QHNgo,455
123
126
  mcp_agent/resources/examples/mcp/state-transfer/agent_two.py,sha256=TY9SPzJZFv3TL6VEP3IpdJvTjYup5txF_DjpvEzlmbw,476
124
- mcp_agent/resources/examples/mcp/state-transfer/fastagent.config.yaml,sha256=-IIocaehANnWiwFHSNNzDRdV80ApicNmxAY4flKETpk,797
127
+ mcp_agent/resources/examples/mcp/state-transfer/fastagent.config.yaml,sha256=e3Esqw850p9GcapVVhPhAAHWwtt2gH2ivsVIULD9n6Q,798
125
128
  mcp_agent/resources/examples/mcp/state-transfer/fastagent.secrets.yaml.example,sha256=0n3F2S_Z2CeLHZueZqCGy37mxiMDHLplvxUHYiCpD2A,421
126
129
  mcp_agent/resources/examples/prompting/__init__.py,sha256=2GSrs9MSDIKo-uDrUI0O311F0UH0RW02ZNdvItJzjfI,50
127
130
  mcp_agent/resources/examples/prompting/agent.py,sha256=HxzUsidfxoc7Th0Ws55ppQCHNLkdZvcbiAcc2fMd4KI,490
@@ -142,9 +145,9 @@ mcp_agent/resources/examples/workflows/orchestrator.py,sha256=rOGilFTliWWnZ3Jx5w
142
145
  mcp_agent/resources/examples/workflows/parallel.py,sha256=DQ5vY5-h8Qa5QHcYjsWXhZ_FYrYoloVWOdgeXV9p2gI,1890
143
146
  mcp_agent/resources/examples/workflows/router.py,sha256=E4x_-c3l4YW9w1i4ARcDtkdeqIdbWEGfsMzwLYpdbVc,1677
144
147
  mcp_agent/resources/examples/workflows/short_story.txt,sha256=X3y_1AyhLFN2AKzCKvucJtDgAFIJfnlbsbGZO5bBWu0,1187
145
- mcp_agent/ui/console_display.py,sha256=TVGDtJ37hc6UG0ei9g7ZPZZfFNeS1MYozt-Mx8HsPCk,9752
146
- fast_agent_mcp-0.2.21.dist-info/METADATA,sha256=c-Aq5664t9xi4iW2mcDHUGaDL_jRUznn_nyeMVFJzgY,30142
147
- fast_agent_mcp-0.2.21.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
148
- fast_agent_mcp-0.2.21.dist-info/entry_points.txt,sha256=bRniFM5zk3Kix5z7scX0gf9VnmGQ2Cz_Q1Gh7Ir4W00,186
149
- fast_agent_mcp-0.2.21.dist-info/licenses/LICENSE,sha256=cN3FxDURL9XuzE5mhK9L2paZo82LTfjwCYVT7e3j0e4,10939
150
- fast_agent_mcp-0.2.21.dist-info/RECORD,,
148
+ mcp_agent/ui/console_display.py,sha256=EUeMJ7yqtxJ0-hAjXNZFJFTlmfyKlENdfzTlw0e5ETg,9949
149
+ fast_agent_mcp-0.2.23.dist-info/METADATA,sha256=Vtk96ocWT3Xk_Y8f3ZLUILhh2aybr8OebCPn5jZeZOY,30156
150
+ fast_agent_mcp-0.2.23.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
151
+ fast_agent_mcp-0.2.23.dist-info/entry_points.txt,sha256=bRniFM5zk3Kix5z7scX0gf9VnmGQ2Cz_Q1Gh7Ir4W00,186
152
+ fast_agent_mcp-0.2.23.dist-info/licenses/LICENSE,sha256=cN3FxDURL9XuzE5mhK9L2paZo82LTfjwCYVT7e3j0e4,10939
153
+ fast_agent_mcp-0.2.23.dist-info/RECORD,,
@@ -229,7 +229,7 @@ class OrchestratorAgent(BaseAgent):
229
229
  self.logger.warning(
230
230
  f"Reached maximum step limit ({max_steps}) without completing objective"
231
231
  )
232
- plan_result.max_steps_reached = True
232
+ plan_result.max_iterations_reached = True
233
233
  break
234
234
 
235
235
  # Execute the step and collect results
@@ -239,7 +239,7 @@ class OrchestratorAgent(BaseAgent):
239
239
  total_steps_executed += 1
240
240
 
241
241
  # Check if we need to break due to hitting max steps
242
- if getattr(plan_result, "max_steps_reached", False):
242
+ if getattr(plan_result, "max_iterations_reached", False):
243
243
  break
244
244
 
245
245
  # If the plan is marked complete, finalize the result
@@ -2,60 +2,133 @@
2
2
 
3
3
  import asyncio
4
4
  import sys
5
- from typing import List, Optional
5
+ from typing import Dict, List, Optional
6
6
 
7
7
  import typer
8
8
 
9
+ from mcp_agent.cli.commands.url_parser import generate_server_configs, parse_server_urls
9
10
  from mcp_agent.core.fastagent import FastAgent
10
11
 
11
12
  app = typer.Typer(
12
13
  help="Run an interactive agent directly from the command line without creating an agent.py file"
13
14
  )
14
15
 
16
+
15
17
  async def _run_agent(
16
18
  name: str = "FastAgent CLI",
17
19
  instruction: str = "You are a helpful AI Agent.",
18
20
  config_path: Optional[str] = None,
19
21
  server_list: Optional[List[str]] = None,
20
22
  model: Optional[str] = None,
23
+ message: Optional[str] = None,
24
+ prompt_file: Optional[str] = None,
25
+ url_servers: Optional[Dict[str, Dict[str, str]]] = None,
21
26
  ) -> None:
22
27
  """Async implementation to run an interactive agent."""
28
+ from pathlib import Path
29
+
30
+ from mcp_agent.config import MCPServerSettings, MCPSettings
31
+ from mcp_agent.mcp.prompts.prompt_load import load_prompt_multipart
23
32
 
24
- # Create the FastAgent instance with CLI arg parsing enabled
25
- # It will automatically parse args like --model, --quiet, etc.
33
+ # Create the FastAgent instance
26
34
  fast_kwargs = {
27
35
  "name": name,
28
36
  "config_path": config_path,
29
37
  "ignore_unknown_args": True,
38
+ "parse_cli_args": False, # Don't parse CLI args, we're handling it ourselves
30
39
  }
31
-
40
+
32
41
  fast = FastAgent(**fast_kwargs)
33
42
 
43
+ # Add URL-based servers to the context configuration
44
+ if url_servers:
45
+ # Initialize the app to ensure context is ready
46
+ await fast.app.initialize()
47
+
48
+ # Initialize mcp settings if needed
49
+ if not hasattr(fast.app.context.config, "mcp"):
50
+ fast.app.context.config.mcp = MCPSettings()
51
+
52
+ # Initialize servers dictionary if needed
53
+ if (
54
+ not hasattr(fast.app.context.config.mcp, "servers")
55
+ or fast.app.context.config.mcp.servers is None
56
+ ):
57
+ fast.app.context.config.mcp.servers = {}
58
+
59
+ # Add each URL server to the config
60
+ for server_name, server_config in url_servers.items():
61
+ server_settings = {"transport": server_config["transport"], "url": server_config["url"]}
62
+
63
+ # Add headers if present in the server config
64
+ if "headers" in server_config:
65
+ server_settings["headers"] = server_config["headers"]
66
+
67
+ fast.app.context.config.mcp.servers[server_name] = MCPServerSettings(**server_settings)
68
+
34
69
  # Define the agent with specified parameters
35
70
  agent_kwargs = {"instruction": instruction}
36
71
  if server_list:
37
72
  agent_kwargs["servers"] = server_list
38
73
  if model:
39
74
  agent_kwargs["model"] = model
40
-
41
- @fast.agent(**agent_kwargs)
42
- async def cli_agent():
43
- async with fast.run() as agent:
44
- await agent.interactive()
75
+
76
+ # Handle prompt file and message options
77
+ if message or prompt_file:
78
+
79
+ @fast.agent(**agent_kwargs)
80
+ async def cli_agent():
81
+ async with fast.run() as agent:
82
+ if message:
83
+ response = await agent.send(message)
84
+ # Print the response and exit
85
+ print(response)
86
+ elif prompt_file:
87
+ prompt = load_prompt_multipart(Path(prompt_file))
88
+ response = await agent.default.generate(prompt)
89
+ # Print the response text and exit
90
+ print(response.last_text())
91
+ else:
92
+ # Standard interactive mode
93
+ @fast.agent(**agent_kwargs)
94
+ async def cli_agent():
95
+ async with fast.run() as agent:
96
+ await agent.interactive()
45
97
 
46
98
  # Run the agent
47
99
  await cli_agent()
48
100
 
101
+
49
102
  def run_async_agent(
50
- name: str,
51
- instruction: str,
52
- config_path: Optional[str] = None,
103
+ name: str,
104
+ instruction: str,
105
+ config_path: Optional[str] = None,
53
106
  servers: Optional[str] = None,
54
- model: Optional[str] = None
107
+ urls: Optional[str] = None,
108
+ auth: Optional[str] = None,
109
+ model: Optional[str] = None,
110
+ message: Optional[str] = None,
111
+ prompt_file: Optional[str] = None,
55
112
  ):
56
113
  """Run the async agent function with proper loop handling."""
57
- server_list = servers.split(',') if servers else None
58
-
114
+ server_list = servers.split(",") if servers else None
115
+
116
+ # Parse URLs and generate server configurations if provided
117
+ url_servers = None
118
+ if urls:
119
+ try:
120
+ parsed_urls = parse_server_urls(urls, auth)
121
+ url_servers = generate_server_configs(parsed_urls)
122
+ # If we have servers from URLs, add their names to the server_list
123
+ if url_servers and not server_list:
124
+ server_list = list(url_servers.keys())
125
+ elif url_servers and server_list:
126
+ # Merge both lists
127
+ server_list.extend(list(url_servers.keys()))
128
+ except ValueError as e:
129
+ print(f"Error parsing URLs: {e}")
130
+ return
131
+
59
132
  # Check if we're already in an event loop
60
133
  try:
61
134
  loop = asyncio.get_event_loop()
@@ -68,22 +141,27 @@ def run_async_agent(
68
141
  # No event loop exists, so we'll create one
69
142
  loop = asyncio.new_event_loop()
70
143
  asyncio.set_event_loop(loop)
71
-
144
+
72
145
  try:
73
- loop.run_until_complete(_run_agent(
74
- name=name,
75
- instruction=instruction,
76
- config_path=config_path,
77
- server_list=server_list,
78
- model=model
79
- ))
146
+ loop.run_until_complete(
147
+ _run_agent(
148
+ name=name,
149
+ instruction=instruction,
150
+ config_path=config_path,
151
+ server_list=server_list,
152
+ model=model,
153
+ message=message,
154
+ prompt_file=prompt_file,
155
+ url_servers=url_servers,
156
+ )
157
+ )
80
158
  finally:
81
159
  try:
82
160
  # Clean up the loop
83
161
  tasks = asyncio.all_tasks(loop)
84
162
  for task in tasks:
85
163
  task.cancel()
86
-
164
+
87
165
  # Run the event loop until all tasks are done
88
166
  if sys.version_info >= (3, 7):
89
167
  loop.run_until_complete(asyncio.gather(*tasks, return_exceptions=True))
@@ -92,6 +170,7 @@ def run_async_agent(
92
170
  except Exception:
93
171
  pass
94
172
 
173
+
95
174
  @app.callback(invoke_without_command=True)
96
175
  def go(
97
176
  ctx: typer.Context,
@@ -105,29 +184,53 @@ def go(
105
184
  servers: Optional[str] = typer.Option(
106
185
  None, "--servers", help="Comma-separated list of server names to enable from config"
107
186
  ),
187
+ urls: Optional[str] = typer.Option(
188
+ None, "--url", help="Comma-separated list of HTTP/SSE URLs to connect to"
189
+ ),
190
+ auth: Optional[str] = typer.Option(
191
+ None, "--auth", help="Bearer token for authorization with URL-based servers"
192
+ ),
108
193
  model: Optional[str] = typer.Option(
109
194
  None, "--model", help="Override the default model (e.g., haiku, sonnet, gpt-4)"
110
195
  ),
196
+ message: Optional[str] = typer.Option(
197
+ None, "--message", "-m", help="Message to send to the agent (skips interactive mode)"
198
+ ),
199
+ prompt_file: Optional[str] = typer.Option(
200
+ None, "--prompt-file", "-p", help="Path to a prompt file to use (either text or JSON)"
201
+ ),
111
202
  ) -> None:
112
203
  """
113
204
  Run an interactive agent directly from the command line.
114
205
 
115
- Example:
206
+ Examples:
116
207
  fast-agent go --model=haiku --instruction="You are a coding assistant" --servers=fetch,filesystem
208
+ fast-agent go --message="What is the weather today?" --model=haiku
209
+ fast-agent go --prompt-file=my-prompt.txt --model=haiku
210
+ fast-agent go --url=http://localhost:8001/mcp,http://api.example.com/sse
211
+ fast-agent go --url=https://api.example.com/mcp --auth=YOUR_API_TOKEN
117
212
 
118
213
  This will start an interactive session with the agent, using the specified model
119
214
  and instruction. It will use the default configuration from fastagent.config.yaml
120
215
  unless --config-path is specified.
121
216
 
122
217
  Common options:
123
- --model: Override the default model (e.g., --model=haiku)
124
- --quiet: Disable progress display and logging
125
- --servers: Comma-separated list of server names to enable from config
218
+ --model Override the default model (e.g., --model=haiku)
219
+ --quiet Disable progress display and logging
220
+ --servers Comma-separated list of server names to enable from config
221
+ --url Comma-separated list of HTTP/SSE URLs to connect to
222
+ --auth Bearer token for authorization with URL-based servers
223
+ --message, -m Send a single message and exit
224
+ --prompt-file, -p Use a prompt file instead of interactive mode
126
225
  """
127
226
  run_async_agent(
128
- name=name,
129
- instruction=instruction,
130
- config_path=config_path,
227
+ name=name,
228
+ instruction=instruction,
229
+ config_path=config_path,
131
230
  servers=servers,
132
- model=model
133
- )
231
+ urls=urls,
232
+ auth=auth,
233
+ model=model,
234
+ message=message,
235
+ prompt_file=prompt_file,
236
+ )
@@ -0,0 +1,185 @@
1
+ """
2
+ URL parsing utility for the fast-agent CLI.
3
+ Provides functions to parse URLs and determine MCP server configurations.
4
+ """
5
+
6
+ import hashlib
7
+ import re
8
+ from typing import Dict, List, Literal, Tuple
9
+ from urllib.parse import urlparse
10
+
11
+
12
+ def parse_server_url(
13
+ url: str,
14
+ ) -> Tuple[str, Literal["http", "sse"], str]:
15
+ """
16
+ Parse a server URL and determine the transport type and server name.
17
+
18
+ Args:
19
+ url: The URL to parse
20
+
21
+ Returns:
22
+ Tuple containing:
23
+ - server_name: A generated name for the server
24
+ - transport_type: Either "http" or "sse" based on URL
25
+ - url: The parsed and validated URL
26
+
27
+ Raises:
28
+ ValueError: If the URL is invalid or unsupported
29
+ """
30
+ # Basic URL validation
31
+ if not url:
32
+ raise ValueError("URL cannot be empty")
33
+
34
+ # Parse the URL
35
+ parsed_url = urlparse(url)
36
+
37
+ # Ensure scheme is present and is either http or https
38
+ if not parsed_url.scheme or parsed_url.scheme not in ("http", "https"):
39
+ raise ValueError(f"URL must have http or https scheme: {url}")
40
+
41
+ # Ensure netloc (hostname) is present
42
+ if not parsed_url.netloc:
43
+ raise ValueError(f"URL must include a hostname: {url}")
44
+
45
+ # Determine transport type based on URL path
46
+ transport_type: Literal["http", "sse"] = "http"
47
+ if parsed_url.path.endswith("/sse"):
48
+ transport_type = "sse"
49
+ elif not parsed_url.path.endswith("/mcp"):
50
+ # If path doesn't end with /mcp or /sse, append /mcp
51
+ url = url if url.endswith("/") else f"{url}/"
52
+ url = f"{url}mcp"
53
+
54
+ # Generate a server name based on hostname and port
55
+ server_name = generate_server_name(url)
56
+
57
+ return server_name, transport_type, url
58
+
59
+
60
+ def generate_server_name(url: str) -> str:
61
+ """
62
+ Generate a unique and readable server name from a URL.
63
+
64
+ Args:
65
+ url: The URL to generate a name for
66
+
67
+ Returns:
68
+ A server name string
69
+ """
70
+ parsed_url = urlparse(url)
71
+
72
+ # Extract hostname and port
73
+ hostname = parsed_url.netloc.split(":")[0]
74
+
75
+ # Clean the hostname for use in a server name
76
+ # Replace non-alphanumeric characters with underscores
77
+ clean_hostname = re.sub(r"[^a-zA-Z0-9]", "_", hostname)
78
+
79
+ if len(clean_hostname) > 15:
80
+ clean_hostname = clean_hostname[:9] + clean_hostname[-5:]
81
+
82
+ # If it's localhost or an IP, add a more unique identifier
83
+ if clean_hostname in ("localhost", "127_0_0_1") or re.match(r"^(\d+_){3}\d+$", clean_hostname):
84
+ # Use the path as part of the name for uniqueness
85
+ path = parsed_url.path.strip("/")
86
+ path = re.sub(r"[^a-zA-Z0-9]", "_", path)
87
+
88
+ # Include port if specified
89
+ port = ""
90
+ if ":" in parsed_url.netloc:
91
+ port = f"_{parsed_url.netloc.split(':')[1]}"
92
+
93
+ if path:
94
+ return f"{clean_hostname}{port}_{path[:20]}" # Limit path length
95
+ else:
96
+ # Use a hash if no path for uniqueness
97
+ url_hash = hashlib.md5(url.encode()).hexdigest()[:8]
98
+ return f"{clean_hostname}{port}_{url_hash}"
99
+
100
+ return clean_hostname
101
+
102
+
103
+ def parse_server_urls(
104
+ urls_param: str, auth_token: str = None
105
+ ) -> List[Tuple[str, Literal["http", "sse"], str, Dict[str, str] | None]]:
106
+ """
107
+ Parse a comma-separated list of URLs into server configurations.
108
+
109
+ Args:
110
+ urls_param: Comma-separated list of URLs
111
+ auth_token: Optional bearer token for authorization
112
+
113
+ Returns:
114
+ List of tuples containing (server_name, transport_type, url, headers)
115
+
116
+ Raises:
117
+ ValueError: If any URL is invalid
118
+ """
119
+ if not urls_param:
120
+ return []
121
+
122
+ # Split by comma and strip whitespace
123
+ url_list = [url.strip() for url in urls_param.split(",")]
124
+
125
+ # Prepare headers if auth token is provided
126
+ headers = None
127
+ if auth_token:
128
+ headers = {"Authorization": f"Bearer {auth_token}"}
129
+
130
+ # Parse each URL
131
+ result = []
132
+ for url in url_list:
133
+ server_name, transport_type, parsed_url = parse_server_url(url)
134
+ result.append((server_name, transport_type, parsed_url, headers))
135
+
136
+ return result
137
+
138
+
139
+ def generate_server_configs(
140
+ parsed_urls: List[Tuple[str, Literal["http", "sse"], str, Dict[str, str] | None]],
141
+ ) -> Dict[str, Dict[str, str | Dict[str, str]]]:
142
+ """
143
+ Generate server configurations from parsed URLs.
144
+
145
+ Args:
146
+ parsed_urls: List of tuples containing (server_name, transport_type, url, headers)
147
+
148
+ Returns:
149
+ Dictionary of server configurations
150
+ """
151
+ server_configs = {}
152
+ # Keep track of server name occurrences to handle collisions
153
+ name_counts = {}
154
+
155
+ for server_name, transport_type, url, headers in parsed_urls:
156
+ # Handle name collisions by adding a suffix
157
+ final_name = server_name
158
+ if server_name in server_configs:
159
+ # Initialize counter if we haven't seen this name yet
160
+ if server_name not in name_counts:
161
+ name_counts[server_name] = 1
162
+
163
+ # Generate a new name with suffix
164
+ suffix = name_counts[server_name]
165
+ final_name = f"{server_name}_{suffix}"
166
+ name_counts[server_name] += 1
167
+
168
+ # Ensure the new name is also unique
169
+ while final_name in server_configs:
170
+ suffix = name_counts[server_name]
171
+ final_name = f"{server_name}_{suffix}"
172
+ name_counts[server_name] += 1
173
+
174
+ config = {
175
+ "transport": transport_type,
176
+ "url": url,
177
+ }
178
+
179
+ # Add headers if provided
180
+ if headers:
181
+ config["headers"] = headers
182
+
183
+ server_configs[final_name] = config
184
+
185
+ return server_configs