fast-agent-mcp 0.2.9__py3-none-any.whl → 0.2.11__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.9.dist-info → fast_agent_mcp-0.2.11.dist-info}/METADATA +7 -6
- {fast_agent_mcp-0.2.9.dist-info → fast_agent_mcp-0.2.11.dist-info}/RECORD +26 -17
- {fast_agent_mcp-0.2.9.dist-info → fast_agent_mcp-0.2.11.dist-info}/entry_points.txt +0 -1
- mcp_agent/cli/commands/{bootstrap.py → quickstart.py} +88 -21
- mcp_agent/cli/commands/setup.py +10 -4
- mcp_agent/cli/main.py +8 -6
- mcp_agent/config.py +3 -0
- mcp_agent/llm/providers/augmented_llm_generic.py +2 -2
- mcp_agent/llm/providers/augmented_llm_openrouter.py +2 -2
- mcp_agent/mcp/mcp_connection_manager.py +5 -1
- mcp_agent/mcp/prompt_serialization.py +62 -37
- mcp_agent/mcp/prompts/prompt_load.py +27 -27
- mcp_agent/mcp/prompts/prompt_server.py +50 -1
- mcp_agent/mcp_server/agent_server.py +6 -20
- mcp_agent/mcp_server_registry.py +5 -1
- mcp_agent/resources/examples/in_dev/css-LICENSE.txt +21 -0
- mcp_agent/resources/examples/internal/simple.txt +2 -0
- mcp_agent/resources/examples/mcp/state-transfer/agent_one.py +18 -0
- mcp_agent/resources/examples/mcp/state-transfer/agent_two.py +18 -0
- mcp_agent/resources/examples/mcp/state-transfer/fastagent.config.yaml +27 -0
- mcp_agent/resources/examples/mcp/state-transfer/fastagent.secrets.yaml.example +14 -0
- mcp_agent/resources/examples/prompting/delimited_prompt.txt +14 -0
- mcp_agent/resources/examples/prompting/prompt1.txt +6 -0
- mcp_agent/resources/examples/workflows/short_story.txt +19 -0
- {fast_agent_mcp-0.2.9.dist-info → fast_agent_mcp-0.2.11.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.2.9.dist-info → fast_agent_mcp-0.2.11.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: fast-agent-mcp
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.11
|
4
4
|
Summary: Define, Prompt and Test MCP enabled Agents and Workflows
|
5
5
|
Author-email: Shaun Smith <fastagent@llmindset.co.uk>, Sarmad Qadri <sarmad@lastmileai.dev>
|
6
6
|
License: Apache License
|
@@ -209,6 +209,7 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
209
209
|
Classifier: Operating System :: OS Independent
|
210
210
|
Classifier: Programming Language :: Python :: 3
|
211
211
|
Requires-Python: >=3.10
|
212
|
+
Requires-Dist: a2a-types>=0.1.0
|
212
213
|
Requires-Dist: aiohttp>=3.11.13
|
213
214
|
Requires-Dist: anthropic>=0.49.0
|
214
215
|
Requires-Dist: fastapi>=0.115.6
|
@@ -252,7 +253,7 @@ Description-Content-Type: text/markdown
|
|
252
253
|
## Overview
|
253
254
|
|
254
255
|
> [!TIP]
|
255
|
-
> Documentation site is in production here : https://fast-agent.ai. Feel free to feed back what's helpful and what's not.
|
256
|
+
> Documentation site is in production here : https://fast-agent.ai. Feel free to feed back what's helpful and what's not. llms.txt link is here: https://fast-agent.ai/llms.txt
|
256
257
|
|
257
258
|
**`fast-agent`** enables you to create and interact with sophisticated Agents and Workflows in minutes. It is the first framework with complete, end-to-end tested MCP Feature support including Sampling. Both Anthropic (Haiku, Sonnet, Opus) and OpenAI models (gpt-4o family, o1/o3 family) are supported.
|
258
259
|
|
@@ -282,10 +283,10 @@ uv pip install fast-agent-mcp # install fast-agent!
|
|
282
283
|
fast-agent setup # create an example agent and config files
|
283
284
|
uv run agent.py # run your first agent
|
284
285
|
uv run agent.py --model=o3-mini.low # specify a model
|
285
|
-
fast-agent
|
286
|
+
fast-agent quickstart workflow # create "building effective agents" examples
|
286
287
|
```
|
287
288
|
|
288
|
-
Other
|
289
|
+
Other quickstart examples include a Researcher Agent (with Evaluator-Optimizer workflow) and Data Analysis Agent (similar to the ChatGPT experience), demonstrating MCP Roots support.
|
289
290
|
|
290
291
|
> [!TIP]
|
291
292
|
> Windows Users - there are a couple of configuration changes needed for the Filesystem and Docker MCP Servers - necessary changes are detailed within the configuration files.
|
@@ -341,7 +342,7 @@ Specify a model with the `--model` switch - for example `uv run sizer.py --model
|
|
341
342
|
|
342
343
|
### Combining Agents and using MCP Servers
|
343
344
|
|
344
|
-
_To generate examples use `fast-agent
|
345
|
+
_To generate examples use `fast-agent quickstart workflow`. This example can be run with `uv run workflow/chaining.py`. fast-agent looks for configuration files in the current directory before checking parent directories recursively._
|
345
346
|
|
346
347
|
Agents can be chained to build a workflow, using MCP Servers defined in the `fastagent.config.yaml` file:
|
347
348
|
|
@@ -463,7 +464,7 @@ async with fast.run() as agent:
|
|
463
464
|
|
464
465
|
When used in a workflow, it returns the last `generator` message as the result.
|
465
466
|
|
466
|
-
See the `evaluator.py` workflow example, or `fast-agent
|
467
|
+
See the `evaluator.py` workflow example, or `fast-agent quickstart researcher` for a more complete example.
|
467
468
|
|
468
469
|
### Router
|
469
470
|
|
@@ -1,11 +1,11 @@
|
|
1
1
|
mcp_agent/__init__.py,sha256=-AIoeL4c9UAp_P4U0z-uIWTTmQWdihOis5nbQ5L_eao,1664
|
2
2
|
mcp_agent/app.py,sha256=jBmzYM_o50g8vhlTgkkf5TGiBWNbXWViYnd0WANbpzo,10276
|
3
|
-
mcp_agent/config.py,sha256=
|
3
|
+
mcp_agent/config.py,sha256=z3EtltnwdEM03WevxSHusymMvHB02BN4brDsLYQ-M68,12268
|
4
4
|
mcp_agent/console.py,sha256=Gjf2QLFumwG1Lav__c07X_kZxxEUSkzV-1_-YbAwcwo,813
|
5
5
|
mcp_agent/context.py,sha256=pp_F1Q1jgAxGrRccSZJutn1JUxYfVue-St3S8tUyptM,7903
|
6
6
|
mcp_agent/context_dependent.py,sha256=QXfhw3RaQCKfscEEBRGuZ3sdMWqkgShz2jJ1ivGGX1I,1455
|
7
7
|
mcp_agent/event_progress.py,sha256=25iz0yyg-O4glMmtijcYpDdUmtUIKsCmR_8A52GgeC4,2716
|
8
|
-
mcp_agent/mcp_server_registry.py,sha256=
|
8
|
+
mcp_agent/mcp_server_registry.py,sha256=pSD3euU-Oc2LAVenqkLU7UmutAzk6A9liYVLjCj4J70,10068
|
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=Tn2YKw_ytx9b8jC-65WYQmrnD43kYiZsLa4sVHxn9d4,3854
|
@@ -20,11 +20,11 @@ mcp_agent/agents/workflow/parallel_agent.py,sha256=SgIXJx2X_MSlLOv6WXYRezwjDYjU9
|
|
20
20
|
mcp_agent/agents/workflow/router_agent.py,sha256=c4MU55U6q1DRayP0sDoyxdlnKX-N0LPbRv-MFlwbwrY,11165
|
21
21
|
mcp_agent/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
22
22
|
mcp_agent/cli/__main__.py,sha256=AVZ7tQFhU_sDOGuUGJq8ujgKtcxsYJBJwHbVaaiRDlI,166
|
23
|
-
mcp_agent/cli/main.py,sha256=
|
23
|
+
mcp_agent/cli/main.py,sha256=_yMv39nP5faJ8rsp-4xKofX1Mnj7rTKnhBJnC2taNJU,3241
|
24
24
|
mcp_agent/cli/terminal.py,sha256=GRwD-RGW7saIz2IOWZn5vD6JjiArscELBThm1GTFkuI,1065
|
25
|
-
mcp_agent/cli/commands/bootstrap.py,sha256=UiTt0fO_IpaHqFCKAW5tg7BIH04vPXf8xH0hJjPM52M,11602
|
26
25
|
mcp_agent/cli/commands/config.py,sha256=jU2gl4d5YESrdUboh3u6mxf7CxVT-_DT_sK8Vuh3ajw,231
|
27
|
-
mcp_agent/cli/commands/
|
26
|
+
mcp_agent/cli/commands/quickstart.py,sha256=SM3CHMzDgvTxIpKjFuX9BrS_N1vRoXNBDaO90aWx1Rk,14586
|
27
|
+
mcp_agent/cli/commands/setup.py,sha256=YoZM5DiYj-4CaB5cOPistH8UHCTzZGnOsFhaFcqosB0,6470
|
28
28
|
mcp_agent/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
29
29
|
mcp_agent/core/agent_app.py,sha256=5nQJNo8DocIRWiX4pVKAHUZF8s6HWpc-hJnfzl_1v1c,9697
|
30
30
|
mcp_agent/core/agent_types.py,sha256=LuWslu9YI6JRnAWwh_A1ZejK72-e839wH7tf2MHxSIU,1389
|
@@ -62,9 +62,9 @@ mcp_agent/llm/providers/__init__.py,sha256=heVxtmuqFJOnjjxHz4bWSqTAxXoN1E8twC_gQ
|
|
62
62
|
mcp_agent/llm/providers/anthropic_utils.py,sha256=vYDN5G5jKMhD2CQg8veJYab7tvvzYkDMq8M1g_hUAQg,3275
|
63
63
|
mcp_agent/llm/providers/augmented_llm_anthropic.py,sha256=CNKpTEvWqjOteACUx_Vha0uFpPt32C17JrkSXg_allM,14445
|
64
64
|
mcp_agent/llm/providers/augmented_llm_deepseek.py,sha256=SdYDqZZ9hM9sBvW1FSItNn_ENEKQXGNKwVHGnjqjyAA,1927
|
65
|
-
mcp_agent/llm/providers/augmented_llm_generic.py,sha256=
|
65
|
+
mcp_agent/llm/providers/augmented_llm_generic.py,sha256=v6r8mbDO69fpQnvFoy62QyQTnq2qXR2DBbn5o3pyhvg,1533
|
66
66
|
mcp_agent/llm/providers/augmented_llm_openai.py,sha256=Wso9GVgsq8y3sqlOzTk_iQqrkCOL3LyuG07nA1PWDng,17913
|
67
|
-
mcp_agent/llm/providers/augmented_llm_openrouter.py,sha256=
|
67
|
+
mcp_agent/llm/providers/augmented_llm_openrouter.py,sha256=jcnbjMO2NHETOOwc3BPZUcfv8gObczpnYDRZeKlV6eY,3552
|
68
68
|
mcp_agent/llm/providers/multipart_converter_anthropic.py,sha256=t5lHYGfFUacJldnrVtMNW-8gEMoto8Y7hJkDrnyZR-Y,16650
|
69
69
|
mcp_agent/llm/providers/multipart_converter_openai.py,sha256=zCj0LBgd9FDG8aL_GeTrPo2ssloYnmC_Uj3ENWVUJAg,16753
|
70
70
|
mcp_agent/llm/providers/openai_multipart.py,sha256=qKBn7d3jSabnJmVgWweVzqh8q9mBqr09fsPmP92niAQ,6899
|
@@ -85,11 +85,11 @@ mcp_agent/mcp/interfaces.py,sha256=vma7bbWbY3zp1RM6hMYxVO4aV6Vfaygm-nLwzK2jFKI,6
|
|
85
85
|
mcp_agent/mcp/logger_textio.py,sha256=vljC1BtNTCxBAda9ExqNB-FwVNUZIuJT3h1nWmCjMws,3172
|
86
86
|
mcp_agent/mcp/mcp_agent_client_session.py,sha256=RMYNltc2pDIzxwEJSS5589RbvPO0KWV4Y3jSyAmhKf0,4181
|
87
87
|
mcp_agent/mcp/mcp_aggregator.py,sha256=jaWbOvb3wioECohZ47CubyxfJ5QkfNSshu1hwhZksG4,40486
|
88
|
-
mcp_agent/mcp/mcp_connection_manager.py,sha256=
|
88
|
+
mcp_agent/mcp/mcp_connection_manager.py,sha256=FGFF3DruVcHD_8J-VadrRyyrOiiq-N9-_ZzIdx4NUOA,13973
|
89
89
|
mcp_agent/mcp/mime_utils.py,sha256=difepNR_gpb4MpMLkBRAoyhDk-AjXUHTiqKvT_VwS1o,1805
|
90
90
|
mcp_agent/mcp/prompt_message_multipart.py,sha256=IpIndd75tAcCbJbfqjpAF0tOUUP1TQceDbWoxO5gvpo,3684
|
91
91
|
mcp_agent/mcp/prompt_render.py,sha256=k3v4BZDThGE2gGiOYVQtA6x8WTEdOuXIEnRafANhN1U,2996
|
92
|
-
mcp_agent/mcp/prompt_serialization.py,sha256=
|
92
|
+
mcp_agent/mcp/prompt_serialization.py,sha256=MQY6QxnhQTiq0oBDsyRzFtX8sBiovUjzUFX78As8q60,17974
|
93
93
|
mcp_agent/mcp/resource_utils.py,sha256=K4XY8bihmBMleRTZ2viMPiD2Y2HWxFnlgIJi6dd_PYE,6588
|
94
94
|
mcp_agent/mcp/sampling.py,sha256=vzWrIdI1CyFSxDWO-O69TpD6RwQcCM694BqMlYPVtaw,4584
|
95
95
|
mcp_agent/mcp/helpers/__init__.py,sha256=sKqwlUR3jSsd9PVJKjXtxHgZA1YOdzPtsSW4xVey77Q,52
|
@@ -98,16 +98,17 @@ mcp_agent/mcp/prompts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
|
|
98
98
|
mcp_agent/mcp/prompts/__main__.py,sha256=gr1Tdz9fcK0EXjEuZg_BOnKUmvhYq5AH2lFZicVyNb0,237
|
99
99
|
mcp_agent/mcp/prompts/prompt_constants.py,sha256=Q9W0t3rOXl2LHIG9wcghApUV2QZ1iICuo7SwVwHUf3c,566
|
100
100
|
mcp_agent/mcp/prompts/prompt_helpers.py,sha256=Joqo2t09pTKDP-Wge3G-ozPEHikzjaqwV6GVk8hNR50,7534
|
101
|
-
mcp_agent/mcp/prompts/prompt_load.py,sha256=
|
102
|
-
mcp_agent/mcp/prompts/prompt_server.py,sha256=
|
101
|
+
mcp_agent/mcp/prompts/prompt_load.py,sha256=Zo0FogqWFEG5FtF1d9ZH-RWsCSSMsi5FIEQHpJD8N7M,5404
|
102
|
+
mcp_agent/mcp/prompts/prompt_server.py,sha256=SiUR2xYfd3vEpghnYRdzz2rFEMtAbCKx2xzUXgvz1g8,18501
|
103
103
|
mcp_agent/mcp/prompts/prompt_template.py,sha256=EejiqGkau8OizORNyKTUwUjrPof5V-hH1H_MBQoQfXw,15732
|
104
104
|
mcp_agent/mcp_server/__init__.py,sha256=zBU51ITHIEPScd9nRafnhEddsWqXRPAAvHhkrbRI2_4,155
|
105
|
-
mcp_agent/mcp_server/agent_server.py,sha256=
|
105
|
+
mcp_agent/mcp_server/agent_server.py,sha256=Nzws9h-itEqx9ml87Wx_mKhyoHehAvcAgsve0PZgghI,5905
|
106
106
|
mcp_agent/resources/examples/data-analysis/analysis-campaign.py,sha256=QdNdo0-7LR4Uzw61hEU_jVKmWyk6A9YpGo81kMwVobM,7267
|
107
107
|
mcp_agent/resources/examples/data-analysis/analysis.py,sha256=M9z8Q4YC5OGuqSa5uefYmmfmctqMn-WqCSfg5LI407o,2609
|
108
108
|
mcp_agent/resources/examples/data-analysis/fastagent.config.yaml,sha256=ini94PHyJCfgpjcjHKMMbGuHs6LIj46F1NwY0ll5HVk,1609
|
109
109
|
mcp_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv,sha256=pcMeOL1_r8m8MziE6xgbBrQbjl5Ijo98yycZn7O-dlk,227977
|
110
110
|
mcp_agent/resources/examples/in_dev/agent_build.py,sha256=eetMEdYDbmnRH4CLO7chpQucAar3OE7iVzD_pnMjIGs,2854
|
111
|
+
mcp_agent/resources/examples/in_dev/css-LICENSE.txt,sha256=zv0cyruZlRdeDUploPpO54kV1x1fFAc90X7vXVPrUIY,1073
|
111
112
|
mcp_agent/resources/examples/in_dev/slides.py,sha256=-SEFeGIg9SLF253NIxmA0NjlanLe8CR1yjDBBp2LXgs,4904
|
112
113
|
mcp_agent/resources/examples/internal/agent.py,sha256=JDfb-64gKoEG6ihsyyVWkuY9XNOdC1-P3r9qwG_qN28,497
|
113
114
|
mcp_agent/resources/examples/internal/fastagent.config.yaml,sha256=EPJW8vsRfYq57w2U2gPF0rTAxGhkaLgiYXw0mnpXsdM,2060
|
@@ -115,12 +116,19 @@ mcp_agent/resources/examples/internal/history_transfer.py,sha256=ETyX2wMMvUnMpUh
|
|
115
116
|
mcp_agent/resources/examples/internal/job.py,sha256=ANF3c01gHJ4O4pIxaAtC3rdgYqVObMySaCUBS4dApW4,4102
|
116
117
|
mcp_agent/resources/examples/internal/prompt_category.py,sha256=kMvqNX_zu0sV-kTaAR3skc_tsq9t8QSEofciK0m4aJc,551
|
117
118
|
mcp_agent/resources/examples/internal/prompt_sizing.py,sha256=bskgxulN57hVkc0V9W0fnjnqSRCK5Tkw9Ggf2MmGIVU,1989
|
119
|
+
mcp_agent/resources/examples/internal/simple.txt,sha256=lcWo9h4NJE1OshBhRgg2QBHhHHqcrVj5j1LWdlyYlAk,14
|
118
120
|
mcp_agent/resources/examples/internal/sizer.py,sha256=xP1TBJkp4xIdtJnyk2MP4BufThauzULaMmgnt5Y5Iw4,365
|
119
121
|
mcp_agent/resources/examples/internal/social.py,sha256=pTKcpHAcvA-vQYgjVfDuU1FivCR004Nq4N2GXd5OMs0,1716
|
122
|
+
mcp_agent/resources/examples/mcp/state-transfer/agent_one.py,sha256=HR-Igr8k68HU0tqIpaXujtJxnKSUwwtZqTdZk8QHNgo,455
|
123
|
+
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=yxrBBvE3icol3mFrIQ5FLWPWCci4S5LDHQGXy8Mcbnk,796
|
125
|
+
mcp_agent/resources/examples/mcp/state-transfer/fastagent.secrets.yaml.example,sha256=0n3F2S_Z2CeLHZueZqCGy37mxiMDHLplvxUHYiCpD2A,421
|
120
126
|
mcp_agent/resources/examples/prompting/__init__.py,sha256=2GSrs9MSDIKo-uDrUI0O311F0UH0RW02ZNdvItJzjfI,50
|
121
127
|
mcp_agent/resources/examples/prompting/agent.py,sha256=yuONn6xkQfk6hjanC9j5_zsIK6JtRP7N-tlgVYiKJnE,616
|
128
|
+
mcp_agent/resources/examples/prompting/delimited_prompt.txt,sha256=T_RRcJVXbrI6fXUIatQxx-8vALJ64qQqh-2WPeRS1b8,305
|
122
129
|
mcp_agent/resources/examples/prompting/fastagent.config.yaml,sha256=UR6LtCpeSIzkHsCrHJW1z-wE7AgmgKozS_IYcfcSAkc,1270
|
123
130
|
mcp_agent/resources/examples/prompting/image_server.py,sha256=vRDRGi68BqTWcldZ4-sd8j41M3e5TtWIUSzIROK8uFo,1667
|
131
|
+
mcp_agent/resources/examples/prompting/prompt1.txt,sha256=H3Is5WvS9xeB7eP2-4bWF5PfZhZ6B0BICKGdGeKL9WE,64
|
124
132
|
mcp_agent/resources/examples/prompting/work_with_image.py,sha256=2MctSPXZsmIyCYvsxsRc1_v_8v0ZKorHH0gWZxLW8Tc,507
|
125
133
|
mcp_agent/resources/examples/researcher/fastagent.config.yaml,sha256=bNOnID9OgdSBTUEhdimKB8LjaZLa1B6igmp-nxx8nr4,2271
|
126
134
|
mcp_agent/resources/examples/researcher/researcher-eval.py,sha256=CR9m4lyoXijS1whvsBDuk6IA-RmNc6iOYbtloETkITY,1833
|
@@ -133,9 +141,10 @@ mcp_agent/resources/examples/workflows/human_input.py,sha256=_I6nS6xYo8IHAmvzsUY
|
|
133
141
|
mcp_agent/resources/examples/workflows/orchestrator.py,sha256=rOGilFTliWWnZ3Jx5wZOH6AQMBKwaGqSMI4PR9MKcZw,2507
|
134
142
|
mcp_agent/resources/examples/workflows/parallel.py,sha256=n0dFN26QvYd2wjgohcaUBflac2SzXYx-bCyxMSousJE,1884
|
135
143
|
mcp_agent/resources/examples/workflows/router.py,sha256=E4x_-c3l4YW9w1i4ARcDtkdeqIdbWEGfsMzwLYpdbVc,1677
|
144
|
+
mcp_agent/resources/examples/workflows/short_story.txt,sha256=X3y_1AyhLFN2AKzCKvucJtDgAFIJfnlbsbGZO5bBWu0,1187
|
136
145
|
mcp_agent/ui/console_display.py,sha256=TVGDtJ37hc6UG0ei9g7ZPZZfFNeS1MYozt-Mx8HsPCk,9752
|
137
|
-
fast_agent_mcp-0.2.
|
138
|
-
fast_agent_mcp-0.2.
|
139
|
-
fast_agent_mcp-0.2.
|
140
|
-
fast_agent_mcp-0.2.
|
141
|
-
fast_agent_mcp-0.2.
|
146
|
+
fast_agent_mcp-0.2.11.dist-info/METADATA,sha256=1kdkV2W3MbPI2IkokeFwfA-rEdmkTZUR6C8nfFMmVrk,29940
|
147
|
+
fast_agent_mcp-0.2.11.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
148
|
+
fast_agent_mcp-0.2.11.dist-info/entry_points.txt,sha256=bRniFM5zk3Kix5z7scX0gf9VnmGQ2Cz_Q1Gh7Ir4W00,186
|
149
|
+
fast_agent_mcp-0.2.11.dist-info/licenses/LICENSE,sha256=cN3FxDURL9XuzE5mhK9L2paZo82LTfjwCYVT7e3j0e4,10939
|
150
|
+
fast_agent_mcp-0.2.11.dist-info/RECORD,,
|
@@ -47,6 +47,18 @@ EXAMPLE_TYPES = {
|
|
47
47
|
"mount_point_files": ["WA_Fn-UseC_-HR-Employee-Attrition.csv"],
|
48
48
|
"create_subdir": True,
|
49
49
|
},
|
50
|
+
"state-transfer": {
|
51
|
+
"description": "Example demonstrating state transfer between multiple agents.\n"
|
52
|
+
"Shows how state can be passed between agent runs to maintain context.\n"
|
53
|
+
"Creates examples in a 'state-transfer' subdirectory.",
|
54
|
+
"files": [
|
55
|
+
"agent_one.py",
|
56
|
+
"agent_two.py",
|
57
|
+
"fastagent.config.yaml",
|
58
|
+
"fastagent.secrets.yaml.example",
|
59
|
+
],
|
60
|
+
"create_subdir": True,
|
61
|
+
},
|
50
62
|
}
|
51
63
|
|
52
64
|
|
@@ -75,28 +87,54 @@ def copy_example_files(example_type: str, target_dir: Path, force: bool = False)
|
|
75
87
|
|
76
88
|
try:
|
77
89
|
# First try to find examples in the package resources
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
90
|
+
if example_type == "state-transfer":
|
91
|
+
# The state-transfer example is in the mcp subdirectory
|
92
|
+
source_dir = (
|
93
|
+
files("mcp_agent")
|
94
|
+
.joinpath("resources")
|
95
|
+
.joinpath("examples")
|
96
|
+
.joinpath("mcp")
|
97
|
+
.joinpath("state-transfer")
|
98
|
+
)
|
99
|
+
else:
|
100
|
+
# Other examples are at the top level of examples
|
101
|
+
source_dir = (
|
102
|
+
files("mcp_agent")
|
103
|
+
.joinpath("resources")
|
104
|
+
.joinpath("examples")
|
105
|
+
.joinpath("workflows" if example_type == "workflow" else f"{example_type}")
|
106
|
+
)
|
107
|
+
|
108
|
+
# Check if we found a valid directory
|
84
109
|
if not source_dir.is_dir():
|
110
|
+
console.print(
|
111
|
+
f"[yellow]Resource directory not found: {source_dir}. Falling back to development mode.[/yellow]"
|
112
|
+
)
|
85
113
|
# Fall back to the top-level directory for development mode
|
86
114
|
package_dir = Path(__file__).parent.parent.parent.parent.parent
|
115
|
+
if example_type == "state-transfer":
|
116
|
+
source_dir = package_dir / "examples" / "mcp" / "state-transfer"
|
117
|
+
else:
|
118
|
+
source_dir = (
|
119
|
+
package_dir
|
120
|
+
/ "examples"
|
121
|
+
/ ("workflows" if example_type == "workflow" else f"{example_type}")
|
122
|
+
)
|
123
|
+
console.print(f"[blue]Using development directory: {source_dir}[/blue]")
|
124
|
+
except (ImportError, ModuleNotFoundError, ValueError) as e:
|
125
|
+
console.print(
|
126
|
+
f"[yellow]Error accessing resources: {e}. Falling back to development mode.[/yellow]"
|
127
|
+
)
|
128
|
+
# Fall back to the top-level directory if the resource finding fails
|
129
|
+
package_dir = Path(__file__).parent.parent.parent.parent.parent
|
130
|
+
if example_type == "state-transfer":
|
131
|
+
source_dir = package_dir / "examples" / "mcp" / "state-transfer"
|
132
|
+
else:
|
87
133
|
source_dir = (
|
88
134
|
package_dir
|
89
135
|
/ "examples"
|
90
136
|
/ ("workflows" if example_type == "workflow" else f"{example_type}")
|
91
137
|
)
|
92
|
-
except (ImportError, ModuleNotFoundError, ValueError):
|
93
|
-
# Fall back to the top-level directory if the resource finding fails
|
94
|
-
package_dir = Path(__file__).parent.parent.parent.parent.parent
|
95
|
-
source_dir = (
|
96
|
-
package_dir
|
97
|
-
/ "examples"
|
98
|
-
/ ("workflows" if example_type == "workflow" else f"{example_type}")
|
99
|
-
)
|
100
138
|
|
101
139
|
if not source_dir.exists():
|
102
140
|
console.print(f"[red]Error: Source directory not found: {source_dir}[/red]")
|
@@ -116,7 +154,14 @@ def copy_example_files(example_type: str, target_dir: Path, force: bool = False)
|
|
116
154
|
continue
|
117
155
|
|
118
156
|
shutil.copy2(source, target)
|
119
|
-
|
157
|
+
try:
|
158
|
+
# This can fail in test environments where the target is not relative to target_dir.parent
|
159
|
+
rel_path = str(target.relative_to(target_dir.parent))
|
160
|
+
except ValueError:
|
161
|
+
# Fallback to just the filename
|
162
|
+
rel_path = f"{example_type}/{filename}"
|
163
|
+
|
164
|
+
created.append(rel_path)
|
120
165
|
console.print(f"[green]Created[/green] {created[-1]}")
|
121
166
|
|
122
167
|
except Exception as e:
|
@@ -174,15 +219,17 @@ def show_overview() -> None:
|
|
174
219
|
# Show usage instructions in a panel
|
175
220
|
usage_text = (
|
176
221
|
"[bold]Commands:[/bold]\n"
|
177
|
-
" fastagent
|
178
|
-
" fastagent
|
179
|
-
" fastagent
|
222
|
+
" fastagent quickstart workflow DIR Create workflow examples in DIR\n"
|
223
|
+
" fastagent quickstart researcher DIR Create researcher example in 'researcher' subdirectory\n"
|
224
|
+
" fastagent quickstart data-analysis DIR Create data analysis examples in 'data-analysis' subdirectory\n"
|
225
|
+
" fastagent quickstart state-transfer DIR Create state transfer examples in 'state-transfer' subdirectory\n\n"
|
180
226
|
"[bold]Options:[/bold]\n"
|
181
227
|
" --force Overwrite existing files\n\n"
|
182
228
|
"[bold]Examples:[/bold]\n"
|
183
|
-
" fastagent
|
184
|
-
" fastagent
|
185
|
-
" fastagent
|
229
|
+
" fastagent quickstart workflow . Create in current directory\n"
|
230
|
+
" fastagent quickstart researcher . Create in researcher subdirectory\n"
|
231
|
+
" fastagent quickstart data-analysis . --force Force overwrite files in data-analysis subdirectory\n"
|
232
|
+
" fastagent quickstart state-transfer . Create state transfer examples"
|
186
233
|
)
|
187
234
|
console.print(Panel(usage_text, title="Usage", border_style="blue"))
|
188
235
|
|
@@ -241,6 +288,24 @@ def data_analysis(
|
|
241
288
|
_show_completion_message("data-analysis", created)
|
242
289
|
|
243
290
|
|
291
|
+
@app.command()
|
292
|
+
def state_transfer(
|
293
|
+
directory: Path = typer.Argument(
|
294
|
+
Path("."),
|
295
|
+
help="Directory where state transfer examples will be created (in 'state-transfer' subdirectory)",
|
296
|
+
),
|
297
|
+
force: bool = typer.Option(False, "--force", "-f", help="Force overwrite existing files"),
|
298
|
+
) -> None:
|
299
|
+
"""Create state transfer example showing state passing between agents."""
|
300
|
+
target_dir = directory.resolve()
|
301
|
+
if not target_dir.exists():
|
302
|
+
target_dir.mkdir(parents=True)
|
303
|
+
console.print(f"Created directory: {target_dir}")
|
304
|
+
|
305
|
+
created = copy_example_files("state-transfer", target_dir, force)
|
306
|
+
_show_completion_message("state-transfer", created)
|
307
|
+
|
308
|
+
|
244
309
|
def _show_completion_message(example_type: str, created: list[str]) -> None:
|
245
310
|
"""Show completion message and next steps."""
|
246
311
|
if created:
|
@@ -275,6 +340,8 @@ def _show_completion_message(example_type: str, created: list[str]) -> None:
|
|
275
340
|
console.print(
|
276
341
|
"On Windows platforms, please edit the fastagent.config.yaml and adjust the volume mount point."
|
277
342
|
)
|
343
|
+
elif example_type == "state-transfer":
|
344
|
+
console.print("Check https://fast-agent.ai for quick start walkthroughs")
|
278
345
|
else:
|
279
346
|
console.print("\n[yellow]No files were created.[/yellow]")
|
280
347
|
|
mcp_agent/cli/commands/setup.py
CHANGED
@@ -55,12 +55,18 @@ FASTAGENT_SECRETS_TEMPLATE = """
|
|
55
55
|
# FastAgent Secrets Configuration
|
56
56
|
# WARNING: Keep this file secure and never commit to version control
|
57
57
|
|
58
|
-
# Alternatively set OPENAI_API_KEY
|
58
|
+
# Alternatively set OPENAI_API_KEY, ANTHROPIC_API_KEY or other environment variables.
|
59
|
+
# Keys in the configuration file override environment variables.
|
59
60
|
|
60
61
|
openai:
|
61
62
|
api_key: <your-api-key-here>
|
62
63
|
anthropic:
|
63
64
|
api_key: <your-api-key-here>
|
65
|
+
deepseek:
|
66
|
+
api_key: <your-api-key-here>
|
67
|
+
openrouter:
|
68
|
+
api_key: <your-api-key-here>
|
69
|
+
|
64
70
|
|
65
71
|
# Example of setting an MCP Server environment variable
|
66
72
|
mcp:
|
@@ -116,15 +122,15 @@ import asyncio
|
|
116
122
|
from mcp_agent.core.fastagent import FastAgent
|
117
123
|
|
118
124
|
# Create the application
|
119
|
-
fast = FastAgent("
|
125
|
+
fast = FastAgent("fast-agent example")
|
120
126
|
|
121
127
|
|
122
128
|
# Define the agent
|
123
|
-
@fast.agent(instruction="You are a helpful AI Agent"
|
129
|
+
@fast.agent(instruction="You are a helpful AI Agent")
|
124
130
|
async def main():
|
125
131
|
# use the --model command line switch or agent arguments to change model
|
126
132
|
async with fast.run() as agent:
|
127
|
-
await agent()
|
133
|
+
await agent.interactive()
|
128
134
|
|
129
135
|
|
130
136
|
if __name__ == "__main__":
|
mcp_agent/cli/main.py
CHANGED
@@ -4,7 +4,7 @@ import typer
|
|
4
4
|
from rich.console import Console
|
5
5
|
from rich.table import Table
|
6
6
|
|
7
|
-
from mcp_agent.cli.commands import
|
7
|
+
from mcp_agent.cli.commands import quickstart, setup
|
8
8
|
from mcp_agent.cli.terminal import Application
|
9
9
|
|
10
10
|
app = typer.Typer(
|
@@ -14,7 +14,8 @@ app = typer.Typer(
|
|
14
14
|
|
15
15
|
# Subcommands
|
16
16
|
app.add_typer(setup.app, name="setup", help="Set up a new agent project")
|
17
|
-
app.add_typer(
|
17
|
+
app.add_typer(quickstart.app, name="bootstrap", help="Create example applications")
|
18
|
+
app.add_typer(quickstart.app, name="quickstart", help="Create example applications")
|
18
19
|
|
19
20
|
# Shared application context
|
20
21
|
application = Application()
|
@@ -39,7 +40,7 @@ def show_welcome() -> None:
|
|
39
40
|
table.add_column("Description")
|
40
41
|
|
41
42
|
table.add_row("setup", "Set up a new agent project with configuration files")
|
42
|
-
table.add_row("
|
43
|
+
table.add_row("quickstart", "Create example applications (workflow, researcher, etc.)")
|
43
44
|
# table.add_row("config", "Manage agent configuration settings")
|
44
45
|
|
45
46
|
console.print(table)
|
@@ -48,12 +49,12 @@ def show_welcome() -> None:
|
|
48
49
|
console.print("1. Set up a new project:")
|
49
50
|
console.print(" fastagent setup")
|
50
51
|
console.print("\n2. Create Building Effective Agents workflow examples:")
|
51
|
-
console.print(" fastagent
|
52
|
+
console.print(" fastagent quickstart workflow")
|
52
53
|
console.print("\n3. Explore other examples:")
|
53
|
-
console.print(" fastagent
|
54
|
+
console.print(" fastagent quickstart")
|
54
55
|
|
55
56
|
console.print("\nUse --help with any command for more information")
|
56
|
-
console.print("Example: fastagent
|
57
|
+
console.print("Example: fastagent quickstart --help")
|
57
58
|
|
58
59
|
|
59
60
|
@app.callback(invoke_without_command=True)
|
@@ -74,6 +75,7 @@ def main(
|
|
74
75
|
# Handle version flag
|
75
76
|
if version:
|
76
77
|
from importlib.metadata import version as get_version
|
78
|
+
|
77
79
|
try:
|
78
80
|
app_version = get_version("fast-agent-mcp")
|
79
81
|
except: # noqa: E722
|
mcp_agent/config.py
CHANGED
@@ -70,6 +70,9 @@ class MCPServerSettings(BaseModel):
|
|
70
70
|
"""The arguments for the server command."""
|
71
71
|
|
72
72
|
read_timeout_seconds: int | None = None
|
73
|
+
"""The timeout in seconds for the session."""
|
74
|
+
|
75
|
+
read_transport_sse_timeout_seconds: int = 300
|
73
76
|
"""The timeout in seconds for the server connection."""
|
74
77
|
|
75
78
|
url: str | None = None
|
@@ -40,8 +40,8 @@ class GenericAugmentedLLM(OpenAIAugmentedLLM):
|
|
40
40
|
return api_key or "ollama"
|
41
41
|
|
42
42
|
def _base_url(self) -> str:
|
43
|
-
base_url =
|
43
|
+
base_url = os.getenv("GENERIC_BASE_URL", DEFAULT_OLLAMA_BASE_URL)
|
44
44
|
if self.context.config and self.context.config.generic:
|
45
45
|
base_url = self.context.config.generic.base_url
|
46
46
|
|
47
|
-
return base_url
|
47
|
+
return base_url
|
@@ -4,7 +4,7 @@ from mcp_agent.core.exceptions import ProviderKeyError
|
|
4
4
|
from mcp_agent.core.request_params import RequestParams
|
5
5
|
from mcp_agent.llm.providers.augmented_llm_openai import OpenAIAugmentedLLM
|
6
6
|
|
7
|
-
|
7
|
+
DEFAULT_OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1"
|
8
8
|
# No single default model for OpenRouter, users must specify full path
|
9
9
|
DEFAULT_OPENROUTER_MODEL = None
|
10
10
|
|
@@ -63,7 +63,7 @@ class OpenRouterAugmentedLLM(OpenAIAugmentedLLM):
|
|
63
63
|
|
64
64
|
def _base_url(self) -> str:
|
65
65
|
"""Retrieve the OpenRouter base URL from config or use the default."""
|
66
|
-
base_url = OPENROUTER_BASE_URL # Default
|
66
|
+
base_url = os.getenv("OPENROUTER_BASE_URL", DEFAULT_OPENROUTER_BASE_URL) # Default
|
67
67
|
config = self.context.config
|
68
68
|
|
69
69
|
# Check config file for override
|
@@ -271,7 +271,11 @@ class MCPConnectionManager(ContextDependent):
|
|
271
271
|
logger.debug(f"{server_name}: Creating stdio client with custom error handler")
|
272
272
|
return stdio_client(server_params, errlog=error_handler)
|
273
273
|
elif config.transport == "sse":
|
274
|
-
return sse_client(
|
274
|
+
return sse_client(
|
275
|
+
config.url,
|
276
|
+
config.headers,
|
277
|
+
sse_read_timeout=config.read_transport_sse_timeout_seconds,
|
278
|
+
)
|
275
279
|
else:
|
276
280
|
raise ValueError(f"Unsupported transport: {config.transport}")
|
277
281
|
|
@@ -5,9 +5,9 @@ This module provides utilities for converting between different serialization fo
|
|
5
5
|
and PromptMessageMultipart objects. It includes functionality for:
|
6
6
|
|
7
7
|
1. JSON Serialization:
|
8
|
-
- Converting PromptMessageMultipart objects to
|
9
|
-
- Parsing JSON into PromptMessageMultipart objects
|
10
|
-
- This is ideal for programmatic use and ensures
|
8
|
+
- Converting PromptMessageMultipart objects to MCP-compatible GetPromptResult JSON format
|
9
|
+
- Parsing GetPromptResult JSON into PromptMessageMultipart objects
|
10
|
+
- This is ideal for programmatic use and ensures full MCP compatibility
|
11
11
|
|
12
12
|
2. Delimited Text Format:
|
13
13
|
- Converting PromptMessageMultipart objects to delimited text (---USER, ---ASSISTANT)
|
@@ -19,7 +19,13 @@ and PromptMessageMultipart objects. It includes functionality for:
|
|
19
19
|
import json
|
20
20
|
from typing import List
|
21
21
|
|
22
|
-
from mcp.types import
|
22
|
+
from mcp.types import (
|
23
|
+
EmbeddedResource,
|
24
|
+
GetPromptResult,
|
25
|
+
ImageContent,
|
26
|
+
TextContent,
|
27
|
+
TextResourceContents,
|
28
|
+
)
|
23
29
|
|
24
30
|
from mcp_agent.mcp.prompt_message_multipart import PromptMessageMultipart
|
25
31
|
from mcp_agent.mcp.prompts.prompt_constants import (
|
@@ -33,51 +39,68 @@ from mcp_agent.mcp.prompts.prompt_constants import (
|
|
33
39
|
# -------------------------------------------------------------------------
|
34
40
|
|
35
41
|
|
42
|
+
def multipart_messages_to_get_prompt_result(
|
43
|
+
messages: List[PromptMessageMultipart],
|
44
|
+
) -> GetPromptResult:
|
45
|
+
"""
|
46
|
+
Convert PromptMessageMultipart objects to a GetPromptResult container.
|
47
|
+
|
48
|
+
Args:
|
49
|
+
messages: List of PromptMessageMultipart objects
|
50
|
+
|
51
|
+
Returns:
|
52
|
+
GetPromptResult object containing flattened messages
|
53
|
+
"""
|
54
|
+
# Convert multipart messages to regular PromptMessage objects
|
55
|
+
flat_messages = []
|
56
|
+
for message in messages:
|
57
|
+
flat_messages.extend(message.from_multipart())
|
58
|
+
|
59
|
+
# Create a GetPromptResult with the flattened messages
|
60
|
+
return GetPromptResult(messages=flat_messages)
|
61
|
+
|
62
|
+
|
36
63
|
def multipart_messages_to_json(messages: List[PromptMessageMultipart]) -> str:
|
37
64
|
"""
|
38
|
-
Convert PromptMessageMultipart objects to a pure JSON string.
|
65
|
+
Convert PromptMessageMultipart objects to a pure JSON string in GetPromptResult format.
|
39
66
|
|
40
|
-
This approach preserves all data and structure exactly as is,
|
41
|
-
|
67
|
+
This approach preserves all data and structure exactly as is, compatible with
|
68
|
+
the MCP GetPromptResult type.
|
42
69
|
|
43
70
|
Args:
|
44
71
|
messages: List of PromptMessageMultipart objects
|
45
72
|
|
46
73
|
Returns:
|
47
|
-
JSON string representation
|
74
|
+
JSON string representation with GetPromptResult container
|
48
75
|
"""
|
49
|
-
#
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
76
|
+
# First convert to GetPromptResult
|
77
|
+
result = multipart_messages_to_get_prompt_result(messages)
|
78
|
+
|
79
|
+
# Convert to dictionary using model_dump with proper JSON mode
|
80
|
+
result_dict = result.model_dump(by_alias=True, mode="json", exclude_none=True)
|
54
81
|
|
55
82
|
# Convert to JSON string
|
56
|
-
return json.dumps(
|
83
|
+
return json.dumps(result_dict, indent=2)
|
57
84
|
|
58
85
|
|
59
86
|
def json_to_multipart_messages(json_str: str) -> List[PromptMessageMultipart]:
|
60
87
|
"""
|
61
|
-
Parse a JSON string into PromptMessageMultipart objects.
|
88
|
+
Parse a JSON string in GetPromptResult format into PromptMessageMultipart objects.
|
62
89
|
|
63
90
|
Args:
|
64
|
-
json_str: JSON string representation of
|
91
|
+
json_str: JSON string representation of GetPromptResult
|
65
92
|
|
66
93
|
Returns:
|
67
94
|
List of PromptMessageMultipart objects
|
68
95
|
"""
|
69
|
-
# Parse JSON to
|
70
|
-
|
96
|
+
# Parse JSON to dictionary
|
97
|
+
result_dict = json.loads(json_str)
|
71
98
|
|
72
|
-
#
|
73
|
-
|
74
|
-
for message_dict in message_dicts:
|
75
|
-
# Parse message using Pydantic's model_validate method (Pydantic v2)
|
76
|
-
# For Pydantic v1, this would use parse_obj instead
|
77
|
-
message = PromptMessageMultipart.model_validate(message_dict)
|
78
|
-
messages.append(message)
|
99
|
+
# Parse as GetPromptResult
|
100
|
+
result = GetPromptResult.model_validate(result_dict)
|
79
101
|
|
80
|
-
|
102
|
+
# Convert to multipart messages
|
103
|
+
return PromptMessageMultipart.to_multipart(result.messages)
|
81
104
|
|
82
105
|
|
83
106
|
def save_messages_to_json_file(messages: List[PromptMessageMultipart], file_path: str) -> None:
|
@@ -113,17 +136,18 @@ def load_messages_from_json_file(file_path: str) -> List[PromptMessageMultipart]
|
|
113
136
|
def save_messages_to_file(messages: List[PromptMessageMultipart], file_path: str) -> None:
|
114
137
|
"""
|
115
138
|
Save PromptMessageMultipart objects to a file, with format determined by file extension.
|
116
|
-
|
117
|
-
Uses JSON format for .json files
|
118
|
-
|
139
|
+
|
140
|
+
Uses GetPromptResult JSON format for .json files (fully MCP compatible) and
|
141
|
+
delimited text format for other extensions.
|
142
|
+
|
119
143
|
Args:
|
120
144
|
messages: List of PromptMessageMultipart objects
|
121
145
|
file_path: Path to save the file
|
122
146
|
"""
|
123
147
|
path_str = str(file_path).lower()
|
124
|
-
|
148
|
+
|
125
149
|
if path_str.endswith(".json"):
|
126
|
-
# Use JSON format for .json files (MCP
|
150
|
+
# Use GetPromptResult JSON format for .json files (fully MCP compatible)
|
127
151
|
save_messages_to_json_file(messages, file_path)
|
128
152
|
else:
|
129
153
|
# Use delimited text format for other extensions
|
@@ -133,19 +157,20 @@ def save_messages_to_file(messages: List[PromptMessageMultipart], file_path: str
|
|
133
157
|
def load_messages_from_file(file_path: str) -> List[PromptMessageMultipart]:
|
134
158
|
"""
|
135
159
|
Load PromptMessageMultipart objects from a file, with format determined by file extension.
|
136
|
-
|
137
|
-
Uses JSON format for .json files
|
138
|
-
|
160
|
+
|
161
|
+
Uses GetPromptResult JSON format for .json files (fully MCP compatible) and
|
162
|
+
delimited text format for other extensions.
|
163
|
+
|
139
164
|
Args:
|
140
165
|
file_path: Path to the file
|
141
|
-
|
166
|
+
|
142
167
|
Returns:
|
143
168
|
List of PromptMessageMultipart objects
|
144
169
|
"""
|
145
170
|
path_str = str(file_path).lower()
|
146
|
-
|
171
|
+
|
147
172
|
if path_str.endswith(".json"):
|
148
|
-
# Use JSON format for .json files (MCP
|
173
|
+
# Use GetPromptResult JSON format for .json files (fully MCP compatible)
|
149
174
|
return load_messages_from_json_file(file_path)
|
150
175
|
else:
|
151
176
|
# Use delimited text format for other extensions
|
@@ -103,29 +103,34 @@ def create_resource_message(
|
|
103
103
|
def load_prompt(file: Path) -> List[PromptMessage]:
|
104
104
|
"""
|
105
105
|
Load a prompt from a file and return as PromptMessage objects.
|
106
|
-
|
106
|
+
|
107
107
|
The loader uses file extension to determine the format:
|
108
|
-
- .json files are loaded as MCP SDK compatible JSON format
|
108
|
+
- .json files are loaded as MCP SDK compatible GetPromptResult JSON format
|
109
109
|
- All other files are loaded using the template-based delimited format
|
110
|
-
|
110
|
+
|
111
111
|
Args:
|
112
112
|
file: Path to the prompt file
|
113
|
-
|
113
|
+
|
114
114
|
Returns:
|
115
115
|
List of PromptMessage objects
|
116
116
|
"""
|
117
117
|
file_str = str(file).lower()
|
118
|
-
|
118
|
+
|
119
119
|
if file_str.endswith(".json"):
|
120
|
-
# JSON format
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
120
|
+
# Handle JSON format as GetPromptResult
|
121
|
+
import json
|
122
|
+
|
123
|
+
from mcp.types import GetPromptResult
|
124
|
+
|
125
|
+
# Load JSON directly into GetPromptResult
|
126
|
+
with open(file, "r", encoding="utf-8") as f:
|
127
|
+
json_data = json.load(f)
|
128
|
+
|
129
|
+
# Parse as GetPromptResult object
|
130
|
+
result = GetPromptResult.model_validate(json_data)
|
131
|
+
|
132
|
+
# Return the messages directly
|
133
|
+
return result.messages
|
129
134
|
else:
|
130
135
|
# Template-based format (delimited text)
|
131
136
|
template: PromptTemplate = PromptTemplateLoader().load_from_file(file)
|
@@ -135,23 +140,18 @@ def load_prompt(file: Path) -> List[PromptMessage]:
|
|
135
140
|
def load_prompt_multipart(file: Path) -> List[PromptMessageMultipart]:
|
136
141
|
"""
|
137
142
|
Load a prompt from a file and return as PromptMessageMultipart objects.
|
138
|
-
|
143
|
+
|
139
144
|
The loader uses file extension to determine the format:
|
140
|
-
- .json files are loaded as MCP SDK compatible JSON format
|
145
|
+
- .json files are loaded as MCP SDK compatible GetPromptResult JSON format
|
141
146
|
- All other files are loaded using the template-based delimited format
|
142
|
-
|
147
|
+
|
143
148
|
Args:
|
144
149
|
file: Path to the prompt file
|
145
|
-
|
150
|
+
|
146
151
|
Returns:
|
147
152
|
List of PromptMessageMultipart objects
|
148
153
|
"""
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
from mcp_agent.mcp.prompt_serialization import load_messages_from_json_file
|
154
|
-
return load_messages_from_json_file(str(file))
|
155
|
-
else:
|
156
|
-
# Template-based format (delimited text)
|
157
|
-
return PromptMessageMultipart.to_multipart(load_prompt(file))
|
154
|
+
# First load as regular PromptMessage objects
|
155
|
+
messages = load_prompt(file)
|
156
|
+
# Then convert to multipart messages
|
157
|
+
return PromptMessageMultipart.to_multipart(messages)
|
@@ -140,10 +140,59 @@ def get_delimiter_config(
|
|
140
140
|
def register_prompt(file_path: Path, config: Optional[PromptConfig] = None) -> None:
|
141
141
|
"""Register a prompt file"""
|
142
142
|
try:
|
143
|
+
# Check if it's a JSON file for ultra-minimal path
|
144
|
+
file_str = str(file_path).lower()
|
145
|
+
if file_str.endswith(".json"):
|
146
|
+
# Simple JSON handling - just load and register directly
|
147
|
+
from mcp.server.fastmcp.prompts.base import Prompt, PromptArgument
|
148
|
+
|
149
|
+
from mcp_agent.mcp.prompts.prompt_load import load_prompt
|
150
|
+
|
151
|
+
# Create metadata with minimal information
|
152
|
+
metadata = PromptMetadata(
|
153
|
+
name=file_path.stem,
|
154
|
+
description=f"JSON prompt: {file_path.stem}",
|
155
|
+
template_variables=set(),
|
156
|
+
resource_paths=[], # Skip resource handling
|
157
|
+
file_path=file_path,
|
158
|
+
)
|
159
|
+
|
160
|
+
# Ensure unique name
|
161
|
+
prompt_name = metadata.name
|
162
|
+
if prompt_name in prompt_registry:
|
163
|
+
base_name = prompt_name
|
164
|
+
suffix = 1
|
165
|
+
while prompt_name in prompt_registry:
|
166
|
+
prompt_name = f"{base_name}_{suffix}"
|
167
|
+
suffix += 1
|
168
|
+
metadata.name = prompt_name
|
169
|
+
|
170
|
+
prompt_registry[metadata.name] = metadata
|
171
|
+
|
172
|
+
# Create a simple handler that directly loads the JSON file each time
|
173
|
+
async def json_prompt_handler():
|
174
|
+
# Load the messages from the JSON file
|
175
|
+
messages = load_prompt(file_path)
|
176
|
+
# Convert to FastMCP format
|
177
|
+
return convert_to_fastmcp_messages(messages)
|
178
|
+
|
179
|
+
# Register directly with MCP
|
180
|
+
prompt = Prompt(
|
181
|
+
name=metadata.name,
|
182
|
+
description=metadata.description,
|
183
|
+
arguments=[], # No arguments for JSON prompts
|
184
|
+
fn=json_prompt_handler,
|
185
|
+
)
|
186
|
+
mcp._prompt_manager.add_prompt(prompt)
|
187
|
+
|
188
|
+
logger.info(f"Registered JSON prompt: {metadata.name} ({file_path})")
|
189
|
+
return # Early return - we're done with JSON files
|
190
|
+
|
191
|
+
# For non-JSON files, continue with the standard approach
|
143
192
|
# Get delimiter configuration
|
144
193
|
config_values = get_delimiter_config(config, file_path)
|
145
194
|
|
146
|
-
# Use
|
195
|
+
# Use standard template loader for text files
|
147
196
|
loader = PromptTemplateLoader(
|
148
197
|
{
|
149
198
|
config_values["user_delimiter"]: "user",
|
@@ -21,7 +21,7 @@ class AgentMCPServer:
|
|
21
21
|
server_description: str | None = None,
|
22
22
|
) -> None:
|
23
23
|
self.agent_app = agent_app
|
24
|
-
self.mcp_server = FastMCP(
|
24
|
+
self.mcp_server: FastMCP = FastMCP(
|
25
25
|
name=server_name,
|
26
26
|
instructions=server_description
|
27
27
|
or f"This server provides access to {len(agent_app._agents)} agents",
|
@@ -95,16 +95,15 @@ class AgentMCPServer:
|
|
95
95
|
try:
|
96
96
|
await self.mcp_server.run_sse_async()
|
97
97
|
except (asyncio.CancelledError, KeyboardInterrupt):
|
98
|
-
|
99
|
-
|
100
|
-
pass
|
98
|
+
print("Server Stopped (CTRL+C)")
|
99
|
+
return
|
101
100
|
else: # stdio
|
102
101
|
try:
|
103
102
|
await self.mcp_server.run_stdio_async()
|
104
103
|
except (asyncio.CancelledError, KeyboardInterrupt):
|
105
104
|
# Gracefully handle cancellation during shutdown
|
106
|
-
|
107
|
-
|
105
|
+
print("Server Stopped (CTRL+C)")
|
106
|
+
return
|
108
107
|
|
109
108
|
async def with_bridged_context(self, agent_context, mcp_context, func, *args, **kwargs):
|
110
109
|
"""
|
@@ -150,17 +149,4 @@ class AgentMCPServer:
|
|
150
149
|
async def shutdown(self):
|
151
150
|
"""Gracefully shutdown the MCP server and its resources."""
|
152
151
|
# Your MCP server may have additional cleanup code here
|
153
|
-
|
154
|
-
# If your MCP server has a shutdown method, call it
|
155
|
-
if hasattr(self.mcp_server, "shutdown"):
|
156
|
-
await self.mcp_server.shutdown()
|
157
|
-
|
158
|
-
# Clean up any other resources
|
159
|
-
import asyncio
|
160
|
-
|
161
|
-
# Allow any pending tasks to clean up
|
162
|
-
await asyncio.sleep(0.5)
|
163
|
-
except Exception as e:
|
164
|
-
# Just log exceptions during shutdown, don't raise
|
165
|
-
print(f"Error during MCP server shutdown: {e}")
|
166
|
-
pass
|
152
|
+
pass
|
mcp_agent/mcp_server_registry.py
CHANGED
@@ -152,7 +152,11 @@ class ServerRegistry:
|
|
152
152
|
raise ValueError(f"URL is required for SSE transport: {server_name}")
|
153
153
|
|
154
154
|
# Use sse_client to get the read and write streams
|
155
|
-
async with sse_client(
|
155
|
+
async with sse_client(
|
156
|
+
config.url,
|
157
|
+
config.headers,
|
158
|
+
sse_read_timeout=config.read_transport_sse_timeout_seconds,
|
159
|
+
) as (read_stream, write_stream):
|
156
160
|
session = client_session_factory(
|
157
161
|
read_stream,
|
158
162
|
write_stream,
|
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2021-2024 Paulo Cunha
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import asyncio
|
2
|
+
|
3
|
+
from mcp_agent.core.fastagent import FastAgent
|
4
|
+
|
5
|
+
# Create the application
|
6
|
+
fast = FastAgent("fast-agent agent_one (mcp server)")
|
7
|
+
|
8
|
+
|
9
|
+
# Define the agent
|
10
|
+
@fast.agent(name="agent_one", instruction="You are a helpful AI Agent.")
|
11
|
+
async def main():
|
12
|
+
# use the --model command line switch or agent arguments to change model
|
13
|
+
async with fast.run() as agent:
|
14
|
+
await agent.interactive()
|
15
|
+
|
16
|
+
|
17
|
+
if __name__ == "__main__":
|
18
|
+
asyncio.run(main())
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import asyncio
|
2
|
+
|
3
|
+
from mcp_agent.core.fastagent import FastAgent
|
4
|
+
|
5
|
+
# Create the application
|
6
|
+
fast = FastAgent("fast-agent agent_two (mcp host)")
|
7
|
+
|
8
|
+
|
9
|
+
# Define the agent
|
10
|
+
@fast.agent(name="agent_two", instruction="You are a helpful AI Agent.", servers=["agent_one"])
|
11
|
+
async def main():
|
12
|
+
# use the --model command line switch or agent arguments to change model
|
13
|
+
async with fast.run() as agent:
|
14
|
+
await agent.interactive()
|
15
|
+
|
16
|
+
|
17
|
+
if __name__ == "__main__":
|
18
|
+
asyncio.run(main())
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Model string takes format:
|
2
|
+
# <provider>.<model_string>.<reasoning_effort?> (e.g. anthropic.claude-3-5-sonnet-20241022 or openai.o3-mini.low)
|
3
|
+
#
|
4
|
+
# Can be overriden with a command line switch --model=<model>, or within the Agent decorator.
|
5
|
+
# Check here for current details: https://fast-agent.ai/models/
|
6
|
+
|
7
|
+
# set the default model for fast-agent below:
|
8
|
+
default_model: gpt-4o
|
9
|
+
|
10
|
+
# Logging and Console Configuration:
|
11
|
+
logger:
|
12
|
+
# Switched off to avoid cluttering the console
|
13
|
+
progress_display: false
|
14
|
+
|
15
|
+
# Show chat User/Assistant messages on the console
|
16
|
+
show_chat: true
|
17
|
+
# Show tool calls on the console
|
18
|
+
show_tools: true
|
19
|
+
# Truncate long tool responses on the console
|
20
|
+
truncate_tools: true
|
21
|
+
|
22
|
+
# MCP Servers
|
23
|
+
mcp:
|
24
|
+
servers:
|
25
|
+
agent_one:
|
26
|
+
transport: sse
|
27
|
+
url: http://localhost:8001/sse
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# FastAgent Secrets Configuration
|
2
|
+
# WARNING: Keep this file secure and never commit to version control
|
3
|
+
|
4
|
+
# Alternatively set OPENAI_API_KEY, ANTHROPIC_API_KEY or other environment variables.
|
5
|
+
# Keys in the configuration file override environment variables.
|
6
|
+
|
7
|
+
openai:
|
8
|
+
api_key: <your-api-key-here>
|
9
|
+
anthropic:
|
10
|
+
api_key: <your-api-key-here>
|
11
|
+
deepseek:
|
12
|
+
api_key: <your-api-key-here>
|
13
|
+
openrouter:
|
14
|
+
api_key: <your-api-key-here>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
---USER
|
2
|
+
I want to learn about {{topic}}.
|
3
|
+
|
4
|
+
Can you tell me about it in detail?
|
5
|
+
|
6
|
+
---ASSISTANT
|
7
|
+
I'd be happy to tell you about {{topic}}!
|
8
|
+
|
9
|
+
Here are some key facts about {{topic}}:
|
10
|
+
1. It's very interesting
|
11
|
+
2. It has a rich history
|
12
|
+
3. Many people study it
|
13
|
+
|
14
|
+
Would you like me to elaborate on any specific aspect?
|
@@ -0,0 +1,19 @@
|
|
1
|
+
The Battle of Glimmerwood
|
2
|
+
|
3
|
+
In the heart of Glimmerwood, a mystical forest knowed for its radiant trees, a small village thrived.
|
4
|
+
The villagers, who were live peacefully, shared their home with the forest's magical creatures,
|
5
|
+
especially the Glimmerfoxes whose fur shimmer like moonlight.
|
6
|
+
|
7
|
+
One fateful evening, the peace was shaterred when the infamous Dark Marauders attack.
|
8
|
+
Lead by the cunning Captain Thorn, the bandits aim to steal the precious Glimmerstones which was believed to grant immortality.
|
9
|
+
|
10
|
+
Amidst the choas, a young girl named Elara stood her ground, she rallied the villagers and devised a clever plan.
|
11
|
+
Using the forests natural defenses they lured the marauders into a trap.
|
12
|
+
As the bandits aproached the village square, a herd of Glimmerfoxes emerged, blinding them with their dazzling light,
|
13
|
+
the villagers seized the opportunity to captured the invaders.
|
14
|
+
|
15
|
+
Elara's bravery was celebrated and she was hailed as the "Guardian of Glimmerwood".
|
16
|
+
The Glimmerstones were secured in a hidden grove protected by an ancient spell.
|
17
|
+
|
18
|
+
However, not all was as it seemed. The Glimmerstones true power was never confirm,
|
19
|
+
and whispers of a hidden agenda linger among the villagers.
|
File without changes
|
File without changes
|