fast-agent-mcp 0.1.3__py3-none-any.whl → 0.1.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. {fast_agent_mcp-0.1.3.dist-info → fast_agent_mcp-0.1.5.dist-info}/METADATA +5 -1
  2. {fast_agent_mcp-0.1.3.dist-info → fast_agent_mcp-0.1.5.dist-info}/RECORD +28 -17
  3. mcp_agent/agents/agent.py +46 -0
  4. mcp_agent/core/agent_app.py +373 -9
  5. mcp_agent/core/decorators.py +455 -0
  6. mcp_agent/core/enhanced_prompt.py +70 -4
  7. mcp_agent/core/factory.py +501 -0
  8. mcp_agent/core/fastagent.py +140 -1059
  9. mcp_agent/core/proxies.py +83 -47
  10. mcp_agent/core/validation.py +221 -0
  11. mcp_agent/human_input/handler.py +5 -2
  12. mcp_agent/mcp/mcp_aggregator.py +537 -47
  13. mcp_agent/mcp/mcp_connection_manager.py +13 -2
  14. mcp_agent/mcp_server/__init__.py +4 -0
  15. mcp_agent/mcp_server/agent_server.py +121 -0
  16. mcp_agent/resources/examples/internal/fastagent.config.yaml +52 -0
  17. mcp_agent/resources/examples/internal/prompt_category.py +21 -0
  18. mcp_agent/resources/examples/internal/prompt_sizing.py +53 -0
  19. mcp_agent/resources/examples/internal/sizer.py +24 -0
  20. mcp_agent/resources/examples/researcher/fastagent.config.yaml +14 -1
  21. mcp_agent/resources/examples/workflows/sse.py +23 -0
  22. mcp_agent/ui/console_display.py +278 -0
  23. mcp_agent/workflows/llm/augmented_llm.py +245 -179
  24. mcp_agent/workflows/llm/augmented_llm_anthropic.py +49 -3
  25. mcp_agent/workflows/llm/augmented_llm_openai.py +52 -4
  26. {fast_agent_mcp-0.1.3.dist-info → fast_agent_mcp-0.1.5.dist-info}/WHEEL +0 -0
  27. {fast_agent_mcp-0.1.3.dist-info → fast_agent_mcp-0.1.5.dist-info}/entry_points.txt +0 -0
  28. {fast_agent_mcp-0.1.3.dist-info → fast_agent_mcp-0.1.5.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.1.3
3
+ Version: 0.1.5
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
@@ -519,6 +519,10 @@ agent["greeter"].send("Good Evening!") # Dictionary access is supported
519
519
  )
520
520
  ```
521
521
 
522
+ ### Prompts
523
+
524
+ MCP Prompts are supported with `apply_prompt(name)`. Prompts can be
525
+
522
526
  #### Chain
523
527
 
524
528
  ```python
@@ -8,7 +8,7 @@ mcp_agent/event_progress.py,sha256=25iz0yyg-O4glMmtijcYpDdUmtUIKsCmR_8A52GgeC4,2
8
8
  mcp_agent/mcp_server_registry.py,sha256=5x30L1IlmC18JASl7NQbZYHMqPWS3ay0f_3U3uleaMM,9884
9
9
  mcp_agent/progress_display.py,sha256=GeJU9VUt6qKsFVymG688hCMVCsAygG9ifiiEb5IcbN4,361
10
10
  mcp_agent/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- mcp_agent/agents/agent.py,sha256=losanPSdZXZzmeiX-J6ctOinLlkhNZsxwi3Swr8lnxA,11482
11
+ mcp_agent/agents/agent.py,sha256=DpsqvrQYsyHrn5nFnQ7wcB7fDRhV22LSVPWhVGLp93M,13497
12
12
  mcp_agent/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  mcp_agent/cli/__main__.py,sha256=AVZ7tQFhU_sDOGuUGJq8ujgKtcxsYJBJwHbVaaiRDlI,166
14
14
  mcp_agent/cli/main.py,sha256=DE6EZzspfzHwPK59x8vL4AIDHRQkVQ1Ja70XRGU1IQs,2753
@@ -17,17 +17,20 @@ mcp_agent/cli/commands/bootstrap.py,sha256=Rmwbuwl52eHfnya7fnwKk2J7nCsHpSh6irka4
17
17
  mcp_agent/cli/commands/config.py,sha256=32YTS5jmsYAs9QzAhjkG70_daAHqOemf4XbZBBSMz6g,204
18
18
  mcp_agent/cli/commands/setup.py,sha256=_SCpd6_PrixqbSaE72JQ7erIRkZnJGmh_3TvvwSzEiE,6392
19
19
  mcp_agent/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- mcp_agent/core/agent_app.py,sha256=6U3HLYAJOfyVuUpZVELWT5lOo64-b_sfWp0yn88s7Wo,6085
20
+ mcp_agent/core/agent_app.py,sha256=PkD1ihpeDaAgJt6AsaU5hkE1AR9azKdXPcio9HBVYdU,26881
21
21
  mcp_agent/core/agent_types.py,sha256=yKiMbv9QO2dduq4zXmoMZlOZpXJZhM4oNwIq1-134FE,318
22
22
  mcp_agent/core/agent_utils.py,sha256=QMvwmxZyCqYhBzSyL9xARsxTuwdmlyjQvrPpsH36HnQ,1888
23
- mcp_agent/core/enhanced_prompt.py,sha256=Zh1I_PZtQmH_v70dJ-gK5lnLOc42h6_Ai8FGi3aLBvU,13400
23
+ mcp_agent/core/decorators.py,sha256=gYqXvwF0pFL-FFU2Vkq5-HsqBWJsXy3YeEdesL8o-jM,16110
24
+ mcp_agent/core/enhanced_prompt.py,sha256=gyYwDfxzSCdrwKt0Gv1Ap8neOpT-z6gAiKxDyKi4n-w,16365
24
25
  mcp_agent/core/error_handling.py,sha256=D3HMW5odrbJvaKqcpCGj6eDXrbFcuqYaCZz7fyYiTu4,623
25
26
  mcp_agent/core/exceptions.py,sha256=a2-JGRwFFRoQEPuAq0JC5PhAJ5TO3xVJfdS4-VN29cw,2225
26
- mcp_agent/core/fastagent.py,sha256=G0GNOD1JMD37rAaZdYRNsa7gXYjvYiV6Cg39laBKRW8,59714
27
- mcp_agent/core/proxies.py,sha256=5eIqi63wcJMG9aLu_8QF78mS9w4hYas-bcdJqcWZ-fU,6638
27
+ mcp_agent/core/factory.py,sha256=cmsv6qhdxdYoQ2Xjc0bIBKFbg3yQPe2bAjDovx4Wrus,20447
28
+ mcp_agent/core/fastagent.py,sha256=3amBjjsVtFaqOz6jtiPC2xUh1IivxuWzWXYiy8lPjFw,19319
29
+ mcp_agent/core/proxies.py,sha256=yP5H89niJmyV17ekIO9NeI2o07XPr1neWAx8GwZ9rzE,7978
28
30
  mcp_agent/core/server_validation.py,sha256=_59cn16nNT4HGPwg19HgxMtHK4MsdWYDUw_CuL-5xek,1696
29
31
  mcp_agent/core/simulator_registry.py,sha256=rcd1cyFGx8MAnN5O0UgwElmVKU_uoIBh9s24pxP33Jc,573
30
32
  mcp_agent/core/types.py,sha256=Zhi9iW7uiOfdpSt9NC0FCtGRFtJPg4mpZPK2aYi7a7M,817
33
+ mcp_agent/core/validation.py,sha256=x0fsx5eLTawASFm9MDtEukwGOj_RTdY1OW064UihMR8,8309
31
34
  mcp_agent/eval/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
35
  mcp_agent/executor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
36
  mcp_agent/executor/decorator_registry.py,sha256=eONv5WvIcjKd43jVqeP7iB2EkAK-ErhdmXt6ogN0K_w,3848
@@ -37,7 +40,7 @@ mcp_agent/executor/temporal.py,sha256=U-wyltgWlVmzJoyivT6rR0Z1U3S6TbMXpeCxyuXako
37
40
  mcp_agent/executor/workflow.py,sha256=lA6r7PNEvxCVFHp4XkEJkaR0QCTf-J6iw9JwNx-tzNY,6727
38
41
  mcp_agent/executor/workflow_signal.py,sha256=3PWwSgXhz3PhkA8SRX3u0BDVoSlQqRGqC9d1qLC25vE,11210
39
42
  mcp_agent/human_input/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
- mcp_agent/human_input/handler.py,sha256=37ERNg9OXaFgm4Ew-6aaU4y2jKre4VpekSnYZtRYFFI,2904
43
+ mcp_agent/human_input/handler.py,sha256=fF2qSSG4snIYOodrIuy_t2qOYssOYqAygw3qq9XsmXU,3158
41
44
  mcp_agent/human_input/types.py,sha256=ZvuDHvI0-wO2tFoS0bzrv8U5B83zYdxAG7g9G9jCxug,1489
42
45
  mcp_agent/logging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
46
  mcp_agent/logging/events.py,sha256=qfYJnrqgXdujV-nl-iOwBEBh6HMraowBI4zeAWPPU4A,3461
@@ -52,18 +55,24 @@ mcp_agent/mcp/gen_client.py,sha256=u0HwdJiw9YCerS5JC7JDuGgBh9oTcLd7vv9vPjwibXc,3
52
55
  mcp_agent/mcp/mcp_activity.py,sha256=CajXCFWZ2cKEX9s4-HfNVAj471ePTVs4NOkvmIh65tE,592
53
56
  mcp_agent/mcp/mcp_agent_client_session.py,sha256=NtWcQhjmnnaR3yYcYj2d2lh-m563NexZUa57K1tAjeM,9477
54
57
  mcp_agent/mcp/mcp_agent_server.py,sha256=xP09HZTeguJi4Fq0p3fjLBP55uSYe5AdqM90xCgn9Ho,1639
55
- mcp_agent/mcp/mcp_aggregator.py,sha256=RVsgNnSJ1IPBkqKgF_Gp-Cpv97FVBIdppPey6FRoHB0,14751
56
- mcp_agent/mcp/mcp_connection_manager.py,sha256=WLli0w3TVcsszyD9M7zP7vLKPetnQLTf_0PGhvMm9YM,13145
58
+ mcp_agent/mcp/mcp_aggregator.py,sha256=qY9rCy14diC4QJbZormqeuOjJnK2ReK-kpY7y_LfIL0,35805
59
+ mcp_agent/mcp/mcp_connection_manager.py,sha256=EPJTKiEMKnFYpC37SOXiLriQL2YyhH0s6vvZWQRb_Mo,13663
57
60
  mcp_agent/mcp/stdio.py,sha256=tW075R5rQ-UlflXWFKIFDgCbWbuhKqxhiYolWvyEkFs,3985
61
+ mcp_agent/mcp_server/__init__.py,sha256=SEWyU7aSFzdSk6iTYnrQu-llji5_P5dp3TaztCt_rzo,154
62
+ mcp_agent/mcp_server/agent_server.py,sha256=SUBggPyrzWtBRUC5xIMpCxu6ei-6Vah3q9Si12BQ-zY,4444
58
63
  mcp_agent/resources/examples/data-analysis/analysis-campaign.py,sha256=EG-HhaDHltZ4hHAqhgfX_pHM2wem48aYhSIKJxyWHKc,7269
59
64
  mcp_agent/resources/examples/data-analysis/analysis.py,sha256=5zLoioZQNKUfXt1EXLrGX3TU06-0N06-L9Gtp9BIr6k,2611
60
65
  mcp_agent/resources/examples/data-analysis/fastagent.config.yaml,sha256=ini94PHyJCfgpjcjHKMMbGuHs6LIj46F1NwY0ll5HVk,1609
61
66
  mcp_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv,sha256=pcMeOL1_r8m8MziE6xgbBrQbjl5Ijo98yycZn7O-dlk,227977
62
67
  mcp_agent/resources/examples/internal/agent.py,sha256=f-jTgYabV3nWCQm0ZP9NtSEWjx3nQbRngzArRufcELg,384
68
+ mcp_agent/resources/examples/internal/fastagent.config.yaml,sha256=98vKDiGLnpshPBqrkbPPaeKrGZ13RWGBxXhJBb9Hv6M,1850
63
69
  mcp_agent/resources/examples/internal/job.py,sha256=WEKIAANMEAuKr13__rYf3PqJeTAsNB_kqYqbqVYQlUM,4093
70
+ mcp_agent/resources/examples/internal/prompt_category.py,sha256=b3tjkfrVIW1EPoDjr4mG87wlZ7D0Uju9eg6asXAYYpI,551
71
+ mcp_agent/resources/examples/internal/prompt_sizing.py,sha256=UtQ_jvwS4yMh80PHhUQXJ9WXk-fqNYlqUMNTNkZosKM,2003
72
+ mcp_agent/resources/examples/internal/sizer.py,sha256=FC9zTscPRStlaaeDFVUODnrD5ytGUa3sD0NkfrhiQOc,707
64
73
  mcp_agent/resources/examples/internal/social.py,sha256=Cot2lg3PLhLm13gPdVFvFEN28-mm6x3-jHu2YsV4N3s,1707
65
74
  mcp_agent/resources/examples/mcp_researcher/researcher-eval.py,sha256=kNPjIU-JwE0oIBQKwhv6lZsUF_SPtYVkiEEbY1ZVZxk,1807
66
- mcp_agent/resources/examples/researcher/fastagent.config.yaml,sha256=2_VXZneckR6zk6RWzzL-smV_oWmgg4uSkLWqZv8jF0I,1995
75
+ mcp_agent/resources/examples/researcher/fastagent.config.yaml,sha256=bNOnID9OgdSBTUEhdimKB8LjaZLa1B6igmp-nxx8nr4,2271
67
76
  mcp_agent/resources/examples/researcher/researcher-eval.py,sha256=kNPjIU-JwE0oIBQKwhv6lZsUF_SPtYVkiEEbY1ZVZxk,1807
68
77
  mcp_agent/resources/examples/researcher/researcher-imp.py,sha256=Xfw2YAyjXd47pQz-uljgG5ii5x77fVuCP2XCivRDI48,7885
69
78
  mcp_agent/resources/examples/researcher/researcher.py,sha256=jPRafm7jbpHKkX_dQiYGG3Sw-e1Dm86q-JZT-WZDhM0,1425
@@ -75,8 +84,10 @@ mcp_agent/resources/examples/workflows/human_input.py,sha256=c8cBdLEPbaMXddFwsfN
75
84
  mcp_agent/resources/examples/workflows/orchestrator.py,sha256=oyKzmLA1z00wbAwDwBCthJ_qJx4fai6GAJpeOXDR-bE,2569
76
85
  mcp_agent/resources/examples/workflows/parallel.py,sha256=pLbQrtXfbdYqMVddxtg5dZnBnm5Wo2mXlIa1Vf2F1FQ,3096
77
86
  mcp_agent/resources/examples/workflows/router.py,sha256=XT_ewCrxPxdUTMCYQGw34qZQ3GGu8TYY_v5Lige8By4,1707
87
+ mcp_agent/resources/examples/workflows/sse.py,sha256=tdmmh7p87YNfcF_fCq3evAmc1Nek0oY0YOqLRKBLqKg,570
78
88
  mcp_agent/telemetry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
89
  mcp_agent/telemetry/usage_tracking.py,sha256=ePujKMSjPxB7k6X34DGaVlnsV1728mcWZq38OqahiCU,501
90
+ mcp_agent/ui/console_display.py,sha256=6LiZLPrxVvDjslNR6cwxdrx1XnW5NT5qFYkEHO0mFKM,9746
80
91
  mcp_agent/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
81
92
  mcp_agent/workflows/embedding/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
82
93
  mcp_agent/workflows/embedding/embedding_base.py,sha256=-c20ggQ8s7XhMxRX-WEhOgHE7vP_Ca6wtdoXlse-AAA,1681
@@ -93,9 +104,9 @@ mcp_agent/workflows/intent_classifier/intent_classifier_llm.py,sha256=WSLUv2Casb
93
104
  mcp_agent/workflows/intent_classifier/intent_classifier_llm_anthropic.py,sha256=Hp4454IniWFxV4ml50Ml8ip9rS1La5FBn5pd7vm1FHA,1964
94
105
  mcp_agent/workflows/intent_classifier/intent_classifier_llm_openai.py,sha256=zj76WlTYnSCYjBQ_IDi5vFBQGmNwYaoUq1rT730sY98,1940
95
106
  mcp_agent/workflows/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
96
- mcp_agent/workflows/llm/augmented_llm.py,sha256=Hyx-jwgbMjE_WQ--YjIUvdj6HAgX36IvXBesGy6uic0,25884
97
- mcp_agent/workflows/llm/augmented_llm_anthropic.py,sha256=q-WWkD59b_aYMn5Dh1Vaz1pknBgFnacvfbdhRd-u9Kk,23183
98
- mcp_agent/workflows/llm/augmented_llm_openai.py,sha256=a95Q4AFiVw36bXMgYNLFrC2zyDmHERWwkjxJFHlL6JU,25061
107
+ mcp_agent/workflows/llm/augmented_llm.py,sha256=BF2HupWTdvN7B91HhNfeb1btrcO3lGijvYezIAV2f2M,29407
108
+ mcp_agent/workflows/llm/augmented_llm_anthropic.py,sha256=NjOYcDvQZ82hbyehSj4-42mpzqBXLY2qZvWJihd7Afc,25012
109
+ mcp_agent/workflows/llm/augmented_llm_openai.py,sha256=yV0FdPTgRsmNiCcssX4en7WW2pkB8LWjIG_qSev_goM,27055
99
110
  mcp_agent/workflows/llm/enhanced_passthrough.py,sha256=rHNbb6pYllIuVMOhuzUbt63_6WlUnjm57Y7r59N1pnk,2388
100
111
  mcp_agent/workflows/llm/llm_selector.py,sha256=G7pIybuBDwtmyxUDov_QrNYH2FoI0qFRu2JfoxWUF5Y,11045
101
112
  mcp_agent/workflows/llm/model_factory.py,sha256=ZyO3FpiIiM2EiFgE8Y5PhdzLglKENfgknmF9unmKWJ8,7075
@@ -118,8 +129,8 @@ mcp_agent/workflows/swarm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
118
129
  mcp_agent/workflows/swarm/swarm.py,sha256=-lAIeSWDqbGHGRPTvjiP9nIKWvxxy9DAojl9yQzO1Pw,11050
119
130
  mcp_agent/workflows/swarm/swarm_anthropic.py,sha256=pW8zFx5baUWGd5Vw3nIDF2oVOOGNorij4qvGJKdYPcs,1624
120
131
  mcp_agent/workflows/swarm/swarm_openai.py,sha256=wfteywvAGkT5bLmIxX_StHJq8144whYmCRnJASAjOes,1596
121
- fast_agent_mcp-0.1.3.dist-info/METADATA,sha256=Hxlhct68x9F9beQ16vrdjC_AkzP9ex6-VuAuCkNpl-0,27924
122
- fast_agent_mcp-0.1.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
123
- fast_agent_mcp-0.1.3.dist-info/entry_points.txt,sha256=2IXtSmDK9XjWN__RWuRIJTgWyW17wJnJ_h-pb0pZAxo,174
124
- fast_agent_mcp-0.1.3.dist-info/licenses/LICENSE,sha256=cN3FxDURL9XuzE5mhK9L2paZo82LTfjwCYVT7e3j0e4,10939
125
- fast_agent_mcp-0.1.3.dist-info/RECORD,,
132
+ fast_agent_mcp-0.1.5.dist-info/METADATA,sha256=bgvTRasBYWqrvTCI24b4wriirVl5irj7EeKPYIRr54M,28006
133
+ fast_agent_mcp-0.1.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
134
+ fast_agent_mcp-0.1.5.dist-info/entry_points.txt,sha256=2IXtSmDK9XjWN__RWuRIJTgWyW17wJnJ_h-pb0pZAxo,174
135
+ fast_agent_mcp-0.1.5.dist-info/licenses/LICENSE,sha256=cN3FxDURL9XuzE5mhK9L2paZo82LTfjwCYVT7e3j0e4,10939
136
+ fast_agent_mcp-0.1.5.dist-info/RECORD,,
mcp_agent/agents/agent.py CHANGED
@@ -319,3 +319,49 @@ class Agent(MCPAggregator):
319
319
  )
320
320
  ],
321
321
  )
322
+
323
+ async def apply_prompt(self, prompt_name: str, arguments: dict[str, str] = None) -> str:
324
+ """
325
+ Apply an MCP Server Prompt by name and return the assistant's response.
326
+ Will search all available servers for the prompt if not namespaced.
327
+
328
+ If the last message in the prompt is from a user, this will automatically
329
+ generate an assistant response to ensure we always end with an assistant message.
330
+
331
+ Args:
332
+ prompt_name: The name of the prompt to apply
333
+ arguments: Optional dictionary of string arguments to pass to the prompt template
334
+
335
+ Returns:
336
+ The assistant's response or error message
337
+ """
338
+ # If we don't have an LLM, we can't apply the prompt
339
+ if not hasattr(self, "_llm") or not self._llm:
340
+ raise RuntimeError("Agent has no attached LLM")
341
+
342
+ # Get the prompt - this will search all servers if needed
343
+ self.logger.debug(f"Loading prompt '{prompt_name}'")
344
+ prompt_result = await self.get_prompt(prompt_name, arguments)
345
+
346
+ if not prompt_result or not prompt_result.messages:
347
+ error_msg = (
348
+ f"Prompt '{prompt_name}' could not be found or contains no messages"
349
+ )
350
+ self.logger.warning(error_msg)
351
+ return error_msg
352
+
353
+ # Get the display name (namespaced version)
354
+ display_name = getattr(prompt_result, "namespaced_name", prompt_name)
355
+
356
+ # Apply the prompt template and get the result
357
+ # The LLM will automatically generate a response if needed
358
+ result = await self._llm.apply_prompt_template(prompt_result, display_name)
359
+ return result
360
+
361
+ # For backward compatibility
362
+ async def load_prompt(self, prompt_name: str, arguments: dict[str, str] = None) -> str:
363
+ """
364
+ Legacy method - use apply_prompt instead.
365
+ This is maintained for backward compatibility.
366
+ """
367
+ return await self.apply_prompt(prompt_name, arguments)
@@ -15,7 +15,6 @@ from mcp_agent.workflows.evaluator_optimizer.evaluator_optimizer import (
15
15
  # Import proxies directly - they handle their own circular imports
16
16
  from mcp_agent.core.proxies import (
17
17
  BaseAgentProxy,
18
- AgentProxy,
19
18
  LLMAgentProxy,
20
19
  RouterProxy,
21
20
  ChainProxy,
@@ -112,12 +111,377 @@ class AgentApp:
112
111
  command_result = await handle_special_commands(user_input, self)
113
112
 
114
113
  # Check if we should switch agents
115
- if (
116
- isinstance(command_result, dict)
117
- and "switch_agent" in command_result
118
- ):
119
- agent = command_result["switch_agent"]
120
- continue
114
+ if isinstance(command_result, dict):
115
+ if "switch_agent" in command_result:
116
+ agent = command_result["switch_agent"]
117
+ continue
118
+ elif "list_prompts" in command_result:
119
+ # Handle listing of prompts
120
+ from rich import print as rich_print
121
+
122
+ try:
123
+ # Check if we have any agents with aggregator capabilities
124
+ found_prompts = False
125
+ for agent_name, agent_proxy in self._agents.items():
126
+ # Check if agent has an mcp_aggregator (agent instance)
127
+ if hasattr(agent_proxy, "_agent") and hasattr(
128
+ agent_proxy._agent, "list_prompts"
129
+ ):
130
+ rich_print(
131
+ f"\n[bold]Fetching prompts for agent [cyan]{agent_name}[/cyan]...[/bold]"
132
+ )
133
+ prompt_servers = (
134
+ await agent_proxy._agent.list_prompts()
135
+ )
136
+
137
+ if prompt_servers:
138
+ found_prompts = True
139
+ for (
140
+ server_name,
141
+ prompts_info,
142
+ ) in prompt_servers.items():
143
+ if (
144
+ prompts_info
145
+ and hasattr(prompts_info, "prompts")
146
+ and prompts_info.prompts
147
+ ):
148
+ rich_print(
149
+ f"\n[bold cyan]{server_name}:[/bold cyan]"
150
+ )
151
+ for prompt in prompts_info.prompts:
152
+ rich_print(f" {prompt.name}")
153
+ elif (
154
+ isinstance(prompts_info, list)
155
+ and prompts_info
156
+ ):
157
+ rich_print(
158
+ f"\n[bold cyan]{server_name}:[/bold cyan]"
159
+ )
160
+ for prompt in prompts_info:
161
+ if (
162
+ isinstance(prompt, dict)
163
+ and "name" in prompt
164
+ ):
165
+ rich_print(
166
+ f" {prompt['name']}"
167
+ )
168
+ else:
169
+ rich_print(f" {prompt}")
170
+
171
+ if not found_prompts:
172
+ rich_print("[yellow]No prompts available[/yellow]")
173
+ except Exception as e:
174
+ rich_print(f"[red]Error listing prompts: {e}[/red]")
175
+ continue
176
+ elif "select_prompt" in command_result:
177
+ from rich import print as rich_print
178
+ from rich.table import Table
179
+ from rich.console import Console
180
+ from prompt_toolkit import PromptSession
181
+ from prompt_toolkit.formatted_text import HTML
182
+ from prompt_toolkit.completion import WordCompleter
183
+
184
+ console = Console()
185
+
186
+ # Get the current agent proxy
187
+ current_proxy = self._agents[agent]
188
+
189
+ # Check if the agent has prompt capabilities
190
+ if not hasattr(current_proxy, "_agent") or not hasattr(
191
+ current_proxy._agent, "list_prompts"
192
+ ):
193
+ rich_print(
194
+ f"[red]Current agent '{agent}' does not support prompts[/red]"
195
+ )
196
+ continue
197
+
198
+ try:
199
+ # Create a list to store prompt data for selection
200
+ all_prompts = []
201
+
202
+ # Get prompts from the current agent
203
+ rich_print(
204
+ f"\n[bold]Fetching prompts for agent [cyan]{agent}[/cyan]...[/bold]"
205
+ )
206
+ prompt_servers = await current_proxy._agent.list_prompts()
207
+
208
+ if not prompt_servers:
209
+ rich_print(
210
+ "[yellow]No prompts available for this agent[/yellow]"
211
+ )
212
+ continue
213
+
214
+ # Process retrieved prompts
215
+ for server_name, prompts_info in prompt_servers.items():
216
+ # Skip servers with no prompts
217
+ if not prompts_info:
218
+ continue
219
+
220
+ # Extract prompts from the response
221
+ prompts = []
222
+ if hasattr(prompts_info, "prompts"):
223
+ prompts = prompts_info.prompts
224
+ elif isinstance(prompts_info, list):
225
+ prompts = prompts_info
226
+
227
+ # Process each prompt
228
+ for prompt in prompts:
229
+ # Basic prompt information
230
+ prompt_name = getattr(prompt, "name", "Unknown")
231
+ description = getattr(
232
+ prompt, "description", "No description"
233
+ )
234
+
235
+ # Extract argument information
236
+ arg_names = []
237
+ required_args = []
238
+ optional_args = []
239
+ arg_descriptions = {}
240
+
241
+ # Get arguments list from prompt (MCP SDK Prompt.arguments)
242
+ arguments = getattr(prompt, "arguments", None)
243
+ if arguments:
244
+ for arg in arguments:
245
+ # Each arg is a PromptArgument with name and required fields
246
+ name = getattr(arg, "name", None)
247
+ if name:
248
+ arg_names.append(name)
249
+
250
+ # Store description if available
251
+ description = getattr(
252
+ arg, "description", None
253
+ )
254
+ if description:
255
+ arg_descriptions[name] = description
256
+
257
+ # Check if required (default to False per MCP spec)
258
+ if getattr(arg, "required", False):
259
+ required_args.append(name)
260
+ else:
261
+ optional_args.append(name)
262
+
263
+ # Create a namespaced version with the server
264
+ namespaced_name = f"{server_name}-{prompt_name}"
265
+
266
+ # Add to our collection
267
+ all_prompts.append(
268
+ {
269
+ "server": server_name,
270
+ "name": prompt_name,
271
+ "namespaced_name": namespaced_name,
272
+ "description": description,
273
+ "arg_count": len(arg_names),
274
+ "arg_names": arg_names,
275
+ "required_args": required_args,
276
+ "optional_args": optional_args,
277
+ "arg_descriptions": arg_descriptions,
278
+ }
279
+ )
280
+
281
+ # If no prompts were found
282
+ if not all_prompts:
283
+ rich_print(
284
+ "[yellow]No prompts available for this agent[/yellow]"
285
+ )
286
+ continue
287
+
288
+ # Sort prompts by server then name
289
+ all_prompts.sort(key=lambda p: (p["server"], p["name"]))
290
+
291
+ # Check if a specific prompt was requested
292
+ if (
293
+ "prompt_name" in command_result
294
+ and command_result["prompt_name"]
295
+ ):
296
+ requested_name = command_result["prompt_name"]
297
+ # Find the prompt in our list (either by name or namespaced name)
298
+ matching_prompts = [
299
+ p
300
+ for p in all_prompts
301
+ if p["name"] == requested_name
302
+ or p["namespaced_name"] == requested_name
303
+ ]
304
+
305
+ if not matching_prompts:
306
+ rich_print(
307
+ f"[red]Prompt '{requested_name}' not found[/red]"
308
+ )
309
+ rich_print("[yellow]Available prompts:[/yellow]")
310
+ for p in all_prompts:
311
+ rich_print(f" {p['namespaced_name']}")
312
+ continue
313
+
314
+ # If we found exactly one match, use it
315
+ if len(matching_prompts) == 1:
316
+ selected_prompt = matching_prompts[0]
317
+ else:
318
+ # If multiple matches, show them and ask user to be more specific
319
+ rich_print(
320
+ f"[yellow]Multiple prompts match '{requested_name}':[/yellow]"
321
+ )
322
+ for i, p in enumerate(matching_prompts):
323
+ rich_print(
324
+ f" {i + 1}. {p['namespaced_name']} - {p['description']}"
325
+ )
326
+
327
+ # Ask user to select one
328
+ prompt_session = PromptSession()
329
+ selection = await prompt_session.prompt_async(
330
+ "Enter prompt number to select: ", default="1"
331
+ )
332
+
333
+ try:
334
+ idx = int(selection) - 1
335
+ if 0 <= idx < len(matching_prompts):
336
+ selected_prompt = matching_prompts[idx]
337
+ else:
338
+ rich_print("[red]Invalid selection[/red]")
339
+ continue
340
+ except ValueError:
341
+ rich_print(
342
+ "[red]Invalid input, please enter a number[/red]"
343
+ )
344
+ continue
345
+ else:
346
+ # Display prompt selection UI
347
+ table = Table(title="Available MCP Prompts")
348
+ table.add_column("#", justify="right", style="cyan")
349
+ table.add_column("Server", style="green")
350
+ table.add_column("Prompt Name", style="bright_blue")
351
+ table.add_column("Description")
352
+ table.add_column("Args", justify="center")
353
+
354
+ # Add all prompts to the table
355
+ for i, prompt in enumerate(all_prompts):
356
+ # Get argument counts
357
+ required_args = prompt["required_args"]
358
+ optional_args = prompt["optional_args"]
359
+
360
+ # Format args column nicely
361
+ if required_args and optional_args:
362
+ args_display = f"[bold]{len(required_args)}[/bold]+{len(optional_args)}"
363
+ elif required_args:
364
+ args_display = (
365
+ f"[bold]{len(required_args)}[/bold]"
366
+ )
367
+ elif optional_args:
368
+ args_display = f"{len(optional_args)} opt"
369
+ else:
370
+ args_display = "0"
371
+
372
+ table.add_row(
373
+ str(i + 1),
374
+ prompt["server"],
375
+ prompt["name"],
376
+ prompt["description"] or "No description",
377
+ args_display,
378
+ )
379
+
380
+ console.print(table)
381
+ prompt_names = [
382
+ str(i + 1) for i in range(len(all_prompts))
383
+ ]
384
+ completer = WordCompleter(prompt_names)
385
+
386
+ # Ask user to select a prompt
387
+ prompt_session = PromptSession(completer=completer)
388
+ selection = await prompt_session.prompt_async(
389
+ "Enter prompt number to select (or press Enter to cancel): "
390
+ )
391
+
392
+ # Make cancellation easier
393
+ if not selection or selection.strip() == "":
394
+ rich_print(
395
+ "[yellow]Prompt selection cancelled[/yellow]"
396
+ )
397
+ continue
398
+
399
+ try:
400
+ idx = int(selection) - 1
401
+ if 0 <= idx < len(all_prompts):
402
+ selected_prompt = all_prompts[idx]
403
+ else:
404
+ rich_print("[red]Invalid selection[/red]")
405
+ continue
406
+ except ValueError:
407
+ rich_print(
408
+ "[red]Invalid input, please enter a number[/red]"
409
+ )
410
+ continue
411
+
412
+ # Get our prompt arguments
413
+ required_args = selected_prompt["required_args"]
414
+ optional_args = selected_prompt["optional_args"]
415
+ arg_descriptions = selected_prompt.get(
416
+ "arg_descriptions", {}
417
+ )
418
+
419
+ # Always initialize arg_values
420
+ arg_values = {}
421
+
422
+ # Show argument info if we have any
423
+ if required_args or optional_args:
424
+ # Display information about the arguments
425
+ if required_args and optional_args:
426
+ rich_print(
427
+ f"\n[bold]Prompt [cyan]{selected_prompt['name']}[/cyan] requires {len(required_args)} arguments and has {len(optional_args)} optional arguments:[/bold]"
428
+ )
429
+ elif required_args:
430
+ rich_print(
431
+ f"\n[bold]Prompt [cyan]{selected_prompt['name']}[/cyan] requires {len(required_args)} arguments:[/bold]"
432
+ )
433
+ elif optional_args:
434
+ rich_print(
435
+ f"\n[bold]Prompt [cyan]{selected_prompt['name']}[/cyan] has {len(optional_args)} optional arguments:[/bold]"
436
+ )
437
+
438
+ # Collect required arguments
439
+ for arg_name in required_args:
440
+ # Show description if available
441
+ description = arg_descriptions.get(arg_name, "")
442
+ if description:
443
+ rich_print(
444
+ f" [dim]{arg_name}: {description}[/dim]"
445
+ )
446
+
447
+ # Only include non-empty values for optional arguments
448
+ if optional_args:
449
+ # Collect optional arguments
450
+ for arg_name in optional_args:
451
+ # Show description if available
452
+ description = arg_descriptions.get(arg_name, "")
453
+ if description:
454
+ rich_print(
455
+ f" [dim]{arg_name}: {description}[/dim]"
456
+ )
457
+
458
+ arg_value = await PromptSession().prompt_async(
459
+ HTML(
460
+ f"Enter value for <ansibrightcyan>{arg_name}</ansibrightcyan> (optional, press Enter to skip): "
461
+ )
462
+ )
463
+ # Only include non-empty values for optional arguments
464
+ if arg_value:
465
+ arg_values[arg_name] = arg_value
466
+
467
+ # Apply the prompt with or without arguments
468
+ rich_print(
469
+ f"\n[bold]Applying prompt [cyan]{selected_prompt['namespaced_name']}[/cyan]...[/bold]"
470
+ )
471
+
472
+ # Call apply_prompt on the agent - always pass arg_values (empty dict if no args)
473
+ await current_proxy._agent.apply_prompt(
474
+ selected_prompt["namespaced_name"], arg_values
475
+ )
476
+
477
+ except Exception as e:
478
+ import traceback
479
+
480
+ rich_print(
481
+ f"[red]Error selecting or applying prompt: {e}[/red]"
482
+ )
483
+ rich_print(f"[dim]{traceback.format_exc()}[/dim]")
484
+ continue
121
485
 
122
486
  # Skip further processing if command was handled
123
487
  if command_result:
@@ -145,13 +509,13 @@ class AgentApp:
145
509
  """Support: agent.researcher"""
146
510
  if name not in self._agents:
147
511
  raise AttributeError(f"No agent named '{name}'")
148
- return AgentProxy(self, name)
512
+ return self._agents[name]
149
513
 
150
514
  def __getitem__(self, name: str) -> BaseAgentProxy:
151
515
  """Support: agent['researcher']"""
152
516
  if name not in self._agents:
153
517
  raise KeyError(f"No agent named '{name}'")
154
- return AgentProxy(self, name)
518
+ return self._agents[name]
155
519
 
156
520
  async def __call__(
157
521
  self, message: Optional[str] = "", agent_name: Optional[str] = None