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.
- {fast_agent_mcp-0.1.3.dist-info → fast_agent_mcp-0.1.5.dist-info}/METADATA +5 -1
- {fast_agent_mcp-0.1.3.dist-info → fast_agent_mcp-0.1.5.dist-info}/RECORD +28 -17
- mcp_agent/agents/agent.py +46 -0
- mcp_agent/core/agent_app.py +373 -9
- mcp_agent/core/decorators.py +455 -0
- mcp_agent/core/enhanced_prompt.py +70 -4
- mcp_agent/core/factory.py +501 -0
- mcp_agent/core/fastagent.py +140 -1059
- mcp_agent/core/proxies.py +83 -47
- mcp_agent/core/validation.py +221 -0
- mcp_agent/human_input/handler.py +5 -2
- mcp_agent/mcp/mcp_aggregator.py +537 -47
- mcp_agent/mcp/mcp_connection_manager.py +13 -2
- mcp_agent/mcp_server/__init__.py +4 -0
- mcp_agent/mcp_server/agent_server.py +121 -0
- mcp_agent/resources/examples/internal/fastagent.config.yaml +52 -0
- mcp_agent/resources/examples/internal/prompt_category.py +21 -0
- mcp_agent/resources/examples/internal/prompt_sizing.py +53 -0
- mcp_agent/resources/examples/internal/sizer.py +24 -0
- mcp_agent/resources/examples/researcher/fastagent.config.yaml +14 -1
- mcp_agent/resources/examples/workflows/sse.py +23 -0
- mcp_agent/ui/console_display.py +278 -0
- mcp_agent/workflows/llm/augmented_llm.py +245 -179
- mcp_agent/workflows/llm/augmented_llm_anthropic.py +49 -3
- mcp_agent/workflows/llm/augmented_llm_openai.py +52 -4
- {fast_agent_mcp-0.1.3.dist-info → fast_agent_mcp-0.1.5.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.1.3.dist-info → fast_agent_mcp-0.1.5.dist-info}/entry_points.txt +0 -0
- {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
|
+
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=
|
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=
|
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/
|
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/
|
27
|
-
mcp_agent/core/
|
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=
|
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=
|
56
|
-
mcp_agent/mcp/mcp_connection_manager.py,sha256=
|
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=
|
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=
|
97
|
-
mcp_agent/workflows/llm/augmented_llm_anthropic.py,sha256=
|
98
|
-
mcp_agent/workflows/llm/augmented_llm_openai.py,sha256=
|
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.
|
122
|
-
fast_agent_mcp-0.1.
|
123
|
-
fast_agent_mcp-0.1.
|
124
|
-
fast_agent_mcp-0.1.
|
125
|
-
fast_agent_mcp-0.1.
|
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)
|
mcp_agent/core/agent_app.py
CHANGED
@@ -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
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
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
|
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
|
518
|
+
return self._agents[name]
|
155
519
|
|
156
520
|
async def __call__(
|
157
521
|
self, message: Optional[str] = "", agent_name: Optional[str] = None
|