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.
- {fast_agent_mcp-0.2.21.dist-info → fast_agent_mcp-0.2.23.dist-info}/METADATA +10 -8
- {fast_agent_mcp-0.2.21.dist-info → fast_agent_mcp-0.2.23.dist-info}/RECORD +25 -22
- mcp_agent/agents/workflow/orchestrator_agent.py +2 -2
- mcp_agent/cli/commands/go.py +136 -33
- mcp_agent/cli/commands/url_parser.py +185 -0
- mcp_agent/config.py +16 -1
- mcp_agent/core/fastagent.py +2 -2
- mcp_agent/core/request_params.py +11 -7
- mcp_agent/event_progress.py +1 -1
- mcp_agent/llm/augmented_llm.py +3 -9
- mcp_agent/llm/model_factory.py +8 -0
- mcp_agent/llm/provider_types.py +1 -0
- mcp_agent/llm/providers/augmented_llm_anthropic.py +1 -0
- mcp_agent/llm/providers/augmented_llm_openai.py +14 -1
- mcp_agent/llm/providers/augmented_llm_tensorzero.py +442 -0
- mcp_agent/llm/providers/multipart_converter_tensorzero.py +200 -0
- mcp_agent/mcp/mcp_connection_manager.py +78 -10
- mcp_agent/mcp/prompts/prompt_server.py +12 -4
- mcp_agent/mcp_server/agent_server.py +13 -10
- mcp_agent/mcp_server_registry.py +51 -9
- mcp_agent/resources/examples/mcp/state-transfer/fastagent.config.yaml +2 -2
- mcp_agent/ui/console_display.py +7 -6
- {fast_agent_mcp-0.2.21.dist-info → fast_agent_mcp-0.2.23.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.2.21.dist-info → fast_agent_mcp-0.2.23.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.2.21.dist-info → fast_agent_mcp-0.2.23.dist-info}/licenses/LICENSE +0 -0
@@ -1,8 +1,8 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: fast-agent-mcp
|
3
|
-
Version: 0.2.
|
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
|
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
|
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
|
282
|
+
uv pip install fast-agent-mcp # install fast-agent!
|
281
283
|
|
282
|
-
fast-agent setup
|
283
|
-
uv run agent.py
|
284
|
-
uv run agent.py --model=o3-mini.low
|
285
|
-
fast-agent quickstart workflow
|
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=
|
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=
|
8
|
-
mcp_agent/mcp_server_registry.py,sha256=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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
|
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=
|
146
|
-
fast_agent_mcp-0.2.
|
147
|
-
fast_agent_mcp-0.2.
|
148
|
-
fast_agent_mcp-0.2.
|
149
|
-
fast_agent_mcp-0.2.
|
150
|
-
fast_agent_mcp-0.2.
|
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.
|
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, "
|
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
|
mcp_agent/cli/commands/go.py
CHANGED
@@ -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
|
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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
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(
|
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(
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
-
|
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
|
124
|
-
--quiet
|
125
|
-
--servers
|
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
|
-
|
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
|