mail-swarms 1.3.2__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.
- mail/__init__.py +35 -0
- mail/api.py +1964 -0
- mail/cli.py +432 -0
- mail/client.py +1657 -0
- mail/config/__init__.py +8 -0
- mail/config/client.py +87 -0
- mail/config/server.py +165 -0
- mail/core/__init__.py +72 -0
- mail/core/actions.py +69 -0
- mail/core/agents.py +73 -0
- mail/core/message.py +366 -0
- mail/core/runtime.py +3537 -0
- mail/core/tasks.py +311 -0
- mail/core/tools.py +1206 -0
- mail/db/__init__.py +0 -0
- mail/db/init.py +182 -0
- mail/db/types.py +65 -0
- mail/db/utils.py +523 -0
- mail/examples/__init__.py +27 -0
- mail/examples/analyst_dummy/__init__.py +15 -0
- mail/examples/analyst_dummy/agent.py +136 -0
- mail/examples/analyst_dummy/prompts.py +44 -0
- mail/examples/consultant_dummy/__init__.py +15 -0
- mail/examples/consultant_dummy/agent.py +136 -0
- mail/examples/consultant_dummy/prompts.py +42 -0
- mail/examples/data_analysis/__init__.py +40 -0
- mail/examples/data_analysis/analyst/__init__.py +9 -0
- mail/examples/data_analysis/analyst/agent.py +67 -0
- mail/examples/data_analysis/analyst/prompts.py +53 -0
- mail/examples/data_analysis/processor/__init__.py +13 -0
- mail/examples/data_analysis/processor/actions.py +293 -0
- mail/examples/data_analysis/processor/agent.py +67 -0
- mail/examples/data_analysis/processor/prompts.py +48 -0
- mail/examples/data_analysis/reporter/__init__.py +10 -0
- mail/examples/data_analysis/reporter/actions.py +187 -0
- mail/examples/data_analysis/reporter/agent.py +67 -0
- mail/examples/data_analysis/reporter/prompts.py +49 -0
- mail/examples/data_analysis/statistics/__init__.py +18 -0
- mail/examples/data_analysis/statistics/actions.py +343 -0
- mail/examples/data_analysis/statistics/agent.py +67 -0
- mail/examples/data_analysis/statistics/prompts.py +60 -0
- mail/examples/mafia/__init__.py +0 -0
- mail/examples/mafia/game.py +1537 -0
- mail/examples/mafia/narrator_tools.py +396 -0
- mail/examples/mafia/personas.py +240 -0
- mail/examples/mafia/prompts.py +489 -0
- mail/examples/mafia/roles.py +147 -0
- mail/examples/mafia/spec.md +350 -0
- mail/examples/math_dummy/__init__.py +23 -0
- mail/examples/math_dummy/actions.py +252 -0
- mail/examples/math_dummy/agent.py +136 -0
- mail/examples/math_dummy/prompts.py +46 -0
- mail/examples/math_dummy/types.py +5 -0
- mail/examples/research/__init__.py +39 -0
- mail/examples/research/researcher/__init__.py +9 -0
- mail/examples/research/researcher/agent.py +67 -0
- mail/examples/research/researcher/prompts.py +54 -0
- mail/examples/research/searcher/__init__.py +10 -0
- mail/examples/research/searcher/actions.py +324 -0
- mail/examples/research/searcher/agent.py +67 -0
- mail/examples/research/searcher/prompts.py +53 -0
- mail/examples/research/summarizer/__init__.py +18 -0
- mail/examples/research/summarizer/actions.py +255 -0
- mail/examples/research/summarizer/agent.py +67 -0
- mail/examples/research/summarizer/prompts.py +55 -0
- mail/examples/research/verifier/__init__.py +10 -0
- mail/examples/research/verifier/actions.py +337 -0
- mail/examples/research/verifier/agent.py +67 -0
- mail/examples/research/verifier/prompts.py +52 -0
- mail/examples/supervisor/__init__.py +11 -0
- mail/examples/supervisor/agent.py +4 -0
- mail/examples/supervisor/prompts.py +93 -0
- mail/examples/support/__init__.py +33 -0
- mail/examples/support/classifier/__init__.py +10 -0
- mail/examples/support/classifier/actions.py +307 -0
- mail/examples/support/classifier/agent.py +68 -0
- mail/examples/support/classifier/prompts.py +56 -0
- mail/examples/support/coordinator/__init__.py +9 -0
- mail/examples/support/coordinator/agent.py +67 -0
- mail/examples/support/coordinator/prompts.py +48 -0
- mail/examples/support/faq/__init__.py +10 -0
- mail/examples/support/faq/actions.py +182 -0
- mail/examples/support/faq/agent.py +67 -0
- mail/examples/support/faq/prompts.py +42 -0
- mail/examples/support/sentiment/__init__.py +15 -0
- mail/examples/support/sentiment/actions.py +341 -0
- mail/examples/support/sentiment/agent.py +67 -0
- mail/examples/support/sentiment/prompts.py +54 -0
- mail/examples/weather_dummy/__init__.py +23 -0
- mail/examples/weather_dummy/actions.py +75 -0
- mail/examples/weather_dummy/agent.py +136 -0
- mail/examples/weather_dummy/prompts.py +35 -0
- mail/examples/weather_dummy/types.py +5 -0
- mail/factories/__init__.py +27 -0
- mail/factories/action.py +223 -0
- mail/factories/base.py +1531 -0
- mail/factories/supervisor.py +241 -0
- mail/net/__init__.py +7 -0
- mail/net/registry.py +712 -0
- mail/net/router.py +728 -0
- mail/net/server_utils.py +114 -0
- mail/net/types.py +247 -0
- mail/server.py +1605 -0
- mail/stdlib/__init__.py +0 -0
- mail/stdlib/anthropic/__init__.py +0 -0
- mail/stdlib/fs/__init__.py +15 -0
- mail/stdlib/fs/actions.py +209 -0
- mail/stdlib/http/__init__.py +19 -0
- mail/stdlib/http/actions.py +333 -0
- mail/stdlib/interswarm/__init__.py +11 -0
- mail/stdlib/interswarm/actions.py +208 -0
- mail/stdlib/mcp/__init__.py +19 -0
- mail/stdlib/mcp/actions.py +294 -0
- mail/stdlib/openai/__init__.py +13 -0
- mail/stdlib/openai/agents.py +451 -0
- mail/summarizer.py +234 -0
- mail/swarms_json/__init__.py +27 -0
- mail/swarms_json/types.py +87 -0
- mail/swarms_json/utils.py +255 -0
- mail/url_scheme.py +51 -0
- mail/utils/__init__.py +53 -0
- mail/utils/auth.py +194 -0
- mail/utils/context.py +17 -0
- mail/utils/logger.py +73 -0
- mail/utils/openai.py +212 -0
- mail/utils/parsing.py +89 -0
- mail/utils/serialize.py +292 -0
- mail/utils/store.py +49 -0
- mail/utils/string_builder.py +119 -0
- mail/utils/version.py +20 -0
- mail_swarms-1.3.2.dist-info/METADATA +237 -0
- mail_swarms-1.3.2.dist-info/RECORD +137 -0
- mail_swarms-1.3.2.dist-info/WHEEL +4 -0
- mail_swarms-1.3.2.dist-info/entry_points.txt +2 -0
- mail_swarms-1.3.2.dist-info/licenses/LICENSE +202 -0
- mail_swarms-1.3.2.dist-info/licenses/NOTICE +10 -0
- mail_swarms-1.3.2.dist-info/licenses/THIRD_PARTY_NOTICES.md +12334 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Copyright (c) 2025 Addison Kline
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import aiohttp
|
|
7
|
+
|
|
8
|
+
from mail import action
|
|
9
|
+
|
|
10
|
+
SWARM_URL_PARAMETERS = {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"properties": {
|
|
13
|
+
"url": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "The URL of the remote swarm.",
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
"required": ["url"],
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@action(
|
|
23
|
+
name="ping_swarm",
|
|
24
|
+
description="Check if a remote URL hosts an active MAIL swarm.",
|
|
25
|
+
parameters=SWARM_URL_PARAMETERS,
|
|
26
|
+
)
|
|
27
|
+
async def ping_swarm(args: dict[str, Any]) -> str:
|
|
28
|
+
"""
|
|
29
|
+
Check if a remote URL is a valid, active MAIL swarm.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
url: The URL of the remote swarm.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
"pong" if the URL represents an active MAIL swarm, otherwise an error message.
|
|
36
|
+
"""
|
|
37
|
+
url = args.get("url")
|
|
38
|
+
if url is None:
|
|
39
|
+
return "Error: `url` is required"
|
|
40
|
+
if not isinstance(url, str):
|
|
41
|
+
return "Error: `url` must be a string"
|
|
42
|
+
|
|
43
|
+
# attempt to ping (`GET /`) the remote swarm
|
|
44
|
+
try:
|
|
45
|
+
async with aiohttp.ClientSession() as session:
|
|
46
|
+
async with session.get(url) as response:
|
|
47
|
+
if response.status == 200:
|
|
48
|
+
if await _is_valid_mail_root_response(response):
|
|
49
|
+
return "pong"
|
|
50
|
+
else:
|
|
51
|
+
return "Error: remote URL is not a valid MAIL swarm"
|
|
52
|
+
else:
|
|
53
|
+
return f"Error: remote URL returned status code {response.status}"
|
|
54
|
+
except Exception as e:
|
|
55
|
+
return f"Error: {e}"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@action(
|
|
59
|
+
name="get_swarm_health",
|
|
60
|
+
description="Get the health status of a remote MAIL swarm.",
|
|
61
|
+
parameters=SWARM_URL_PARAMETERS,
|
|
62
|
+
)
|
|
63
|
+
async def get_swarm_health(args: dict[str, Any]) -> str:
|
|
64
|
+
"""
|
|
65
|
+
Get the health of a remote MAIL swarm.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
url: The URL of the remote swarm.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
The health status string of the swarm, otherwise an error message.
|
|
72
|
+
"""
|
|
73
|
+
url = args.get("url")
|
|
74
|
+
if url is None:
|
|
75
|
+
return "Error: `url` is required"
|
|
76
|
+
if not isinstance(url, str):
|
|
77
|
+
return "Error: `url` must be a string"
|
|
78
|
+
|
|
79
|
+
# attempt to `GET /health` on the remote swarm
|
|
80
|
+
try:
|
|
81
|
+
async with aiohttp.ClientSession() as session:
|
|
82
|
+
async with session.get(url + "/health") as response:
|
|
83
|
+
if response.status == 200:
|
|
84
|
+
if await _is_valid_mail_health_response(response):
|
|
85
|
+
json = await response.json()
|
|
86
|
+
return json.get("status")
|
|
87
|
+
else:
|
|
88
|
+
return "Error: remote URL is not a valid MAIL swarm"
|
|
89
|
+
else:
|
|
90
|
+
return f"Error: remote URL returned status code {response.status}"
|
|
91
|
+
except Exception as e:
|
|
92
|
+
return f"Error: {e}"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@action(
|
|
96
|
+
name="get_swarm_registry",
|
|
97
|
+
description="Fetch the registry listing for a remote MAIL swarm.",
|
|
98
|
+
parameters=SWARM_URL_PARAMETERS,
|
|
99
|
+
)
|
|
100
|
+
async def get_swarm_registry(args: dict[str, Any]) -> str:
|
|
101
|
+
"""
|
|
102
|
+
Get the registry of the remote MAIL swarm.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
url: The URL of the remote swarm.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
The registry of the remote MAIL swarm, otherwise an error message.
|
|
109
|
+
"""
|
|
110
|
+
url = args.get("url")
|
|
111
|
+
if url is None:
|
|
112
|
+
return "Error: `url` is required"
|
|
113
|
+
if not isinstance(url, str):
|
|
114
|
+
return "Error: `url` must be a string"
|
|
115
|
+
|
|
116
|
+
# attempt to `GET /swarms` on the remote swarm
|
|
117
|
+
try:
|
|
118
|
+
async with aiohttp.ClientSession() as session:
|
|
119
|
+
async with session.get(url + "/swarms") as response:
|
|
120
|
+
if response.status == 200:
|
|
121
|
+
if await _is_valid_mail_swarms_response(response):
|
|
122
|
+
return await _swarm_registry_response_str(response)
|
|
123
|
+
else:
|
|
124
|
+
return "Error: remote URL is not a valid MAIL swarm"
|
|
125
|
+
else:
|
|
126
|
+
return f"Error: remote URL returned status code {response.status}"
|
|
127
|
+
except Exception as e:
|
|
128
|
+
return f"Error: {e}"
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
async def _is_valid_mail_root_response(response: aiohttp.ClientResponse) -> bool:
|
|
132
|
+
"""
|
|
133
|
+
Based on the response to `GET /`, determine if the remote URL is in fact a valid MAIL swarm.
|
|
134
|
+
"""
|
|
135
|
+
json = await response.json()
|
|
136
|
+
name = json.get("name")
|
|
137
|
+
swarm = json.get("swarm")
|
|
138
|
+
status = json.get("status")
|
|
139
|
+
|
|
140
|
+
if not name or not swarm or not status:
|
|
141
|
+
return False
|
|
142
|
+
if (
|
|
143
|
+
not isinstance(name, str)
|
|
144
|
+
or not isinstance(swarm, dict)
|
|
145
|
+
or not isinstance(status, str)
|
|
146
|
+
):
|
|
147
|
+
return False
|
|
148
|
+
if name != "mail" or status != "running":
|
|
149
|
+
return False
|
|
150
|
+
if not isinstance(swarm.get("name"), str) or not isinstance(
|
|
151
|
+
swarm.get("entrypoint"), str
|
|
152
|
+
):
|
|
153
|
+
return False
|
|
154
|
+
|
|
155
|
+
return True
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
async def _is_valid_mail_health_response(response: aiohttp.ClientResponse) -> bool:
|
|
159
|
+
"""
|
|
160
|
+
Based on the response to `GET /health`, determine if the remote URL is in fact a valid MAIL swarm.
|
|
161
|
+
"""
|
|
162
|
+
json = await response.json()
|
|
163
|
+
status = json.get("status")
|
|
164
|
+
swarm_name = json.get("swarm_name")
|
|
165
|
+
|
|
166
|
+
if not status or not swarm_name:
|
|
167
|
+
return False
|
|
168
|
+
if not isinstance(status, str) or not isinstance(swarm_name, str):
|
|
169
|
+
return False
|
|
170
|
+
|
|
171
|
+
return True
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
async def _is_valid_mail_swarms_response(response: aiohttp.ClientResponse) -> bool:
|
|
175
|
+
"""
|
|
176
|
+
Based on the response to `GET /swarms`, determine if the remote URL is in fact a valid MAIL swarm.
|
|
177
|
+
"""
|
|
178
|
+
json = await response.json()
|
|
179
|
+
swarms = json.get("swarms")
|
|
180
|
+
|
|
181
|
+
if not swarms:
|
|
182
|
+
return False
|
|
183
|
+
if not isinstance(swarms, list):
|
|
184
|
+
return False
|
|
185
|
+
for swarm in swarms:
|
|
186
|
+
if not isinstance(swarm, dict):
|
|
187
|
+
return False
|
|
188
|
+
if not isinstance(swarm.get("swarm_name"), str) or not isinstance(
|
|
189
|
+
swarm.get("base_url"), str
|
|
190
|
+
):
|
|
191
|
+
return False
|
|
192
|
+
|
|
193
|
+
return True
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
async def _swarm_registry_response_str(response: aiohttp.ClientResponse) -> str:
|
|
197
|
+
"""
|
|
198
|
+
Convert the response to `GET /swarms` to a string representation of the registry.
|
|
199
|
+
"""
|
|
200
|
+
json = await response.json()
|
|
201
|
+
swarms = json.get("swarms")
|
|
202
|
+
|
|
203
|
+
if not swarms:
|
|
204
|
+
return "No swarms found"
|
|
205
|
+
if not isinstance(swarms, list):
|
|
206
|
+
return "Error: `swarms` is not a list"
|
|
207
|
+
|
|
208
|
+
return "\n".join([f"{swarm['swarm_name']}@{swarm['base_url']}" for swarm in swarms])
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from .actions import (
|
|
2
|
+
mcp_call_tool,
|
|
3
|
+
mcp_get_prompt,
|
|
4
|
+
mcp_list_prompts,
|
|
5
|
+
mcp_list_resources,
|
|
6
|
+
mcp_list_tools,
|
|
7
|
+
mcp_ping,
|
|
8
|
+
mcp_read_resource,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"mcp_ping",
|
|
13
|
+
"mcp_list_tools",
|
|
14
|
+
"mcp_call_tool",
|
|
15
|
+
"mcp_get_prompt",
|
|
16
|
+
"mcp_list_prompts",
|
|
17
|
+
"mcp_list_resources",
|
|
18
|
+
"mcp_read_resource",
|
|
19
|
+
]
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Copyright (c) 2025 Addison Kline
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from fastmcp import Client
|
|
7
|
+
|
|
8
|
+
from mail import action
|
|
9
|
+
|
|
10
|
+
MCP_PING_PARAMETERS = {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"properties": {
|
|
13
|
+
"server_url": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "The URL of the remote MCP server.",
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
"required": ["server_url"],
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@action(
|
|
23
|
+
name="mcp_ping",
|
|
24
|
+
description="Ping a remote MCP server.",
|
|
25
|
+
parameters=MCP_PING_PARAMETERS,
|
|
26
|
+
)
|
|
27
|
+
async def mcp_ping(args: dict[str, Any]) -> str:
|
|
28
|
+
"""
|
|
29
|
+
Ping a remote MCP server.
|
|
30
|
+
"""
|
|
31
|
+
server_url = args.get("server_url")
|
|
32
|
+
|
|
33
|
+
if server_url is None:
|
|
34
|
+
return "Error: `server_url` is required"
|
|
35
|
+
if not isinstance(server_url, str):
|
|
36
|
+
return "Error: `server_url` must be a string"
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
client = Client(server_url)
|
|
40
|
+
async with client:
|
|
41
|
+
result = await client.ping()
|
|
42
|
+
return str(result)
|
|
43
|
+
except Exception as e:
|
|
44
|
+
return f"Error: {e}"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
MCP_LIST_TOOLS_PARAMETERS = {
|
|
48
|
+
"type": "object",
|
|
49
|
+
"properties": {
|
|
50
|
+
"server_url": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"description": "The URL of the remote MCP server.",
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
"required": ["server_url"],
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@action(
|
|
60
|
+
name="mcp_list_tools",
|
|
61
|
+
description="List the tools on a remote MCP server.",
|
|
62
|
+
parameters=MCP_LIST_TOOLS_PARAMETERS,
|
|
63
|
+
)
|
|
64
|
+
async def mcp_list_tools(args: dict[str, Any]) -> str:
|
|
65
|
+
"""
|
|
66
|
+
List the tools on a remote MCP server.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
server_url = args.get("server_url")
|
|
70
|
+
|
|
71
|
+
if server_url is None:
|
|
72
|
+
return "Error: `server_url` is required"
|
|
73
|
+
if not isinstance(server_url, str):
|
|
74
|
+
return "Error: `server_url` must be a string"
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
client = Client(server_url)
|
|
78
|
+
async with client:
|
|
79
|
+
result = await client.list_tools()
|
|
80
|
+
return str(result)
|
|
81
|
+
except Exception as e:
|
|
82
|
+
return f"Error: {e}"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
MCP_CALL_TOOL_PARAMETERS = {
|
|
86
|
+
"type": "object",
|
|
87
|
+
"properties": {
|
|
88
|
+
"server_url": {
|
|
89
|
+
"type": "string",
|
|
90
|
+
"description": "The URL of the remote MCP server.",
|
|
91
|
+
},
|
|
92
|
+
"tool_name": {
|
|
93
|
+
"type": "string",
|
|
94
|
+
"description": "The name of the tool to call.",
|
|
95
|
+
},
|
|
96
|
+
"tool_input": {
|
|
97
|
+
"type": "object",
|
|
98
|
+
"description": "The input to the tool.",
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
"required": ["server_url", "tool_name", "tool_input"],
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@action(
|
|
106
|
+
name="mcp_call_tool",
|
|
107
|
+
description="Call a tool on a remote MCP server.",
|
|
108
|
+
parameters=MCP_CALL_TOOL_PARAMETERS,
|
|
109
|
+
)
|
|
110
|
+
async def mcp_call_tool(args: dict[str, Any]) -> str:
|
|
111
|
+
"""
|
|
112
|
+
Call a tool on a remote MCP server.
|
|
113
|
+
"""
|
|
114
|
+
server_url = args.get("server_url")
|
|
115
|
+
tool_name = args.get("tool_name")
|
|
116
|
+
tool_input = args.get("tool_input")
|
|
117
|
+
|
|
118
|
+
if server_url is None:
|
|
119
|
+
return "Error: `server_url` is required"
|
|
120
|
+
if tool_name is None:
|
|
121
|
+
return "Error: `tool_name` is required"
|
|
122
|
+
if tool_input is None:
|
|
123
|
+
return "Error: `tool_input` is required"
|
|
124
|
+
|
|
125
|
+
if not isinstance(server_url, str):
|
|
126
|
+
return "Error: `server_url` must be a string"
|
|
127
|
+
if not isinstance(tool_name, str):
|
|
128
|
+
return "Error: `tool_name` must be a string"
|
|
129
|
+
if not isinstance(tool_input, dict):
|
|
130
|
+
return "Error: `tool_input` must be a dictionary"
|
|
131
|
+
|
|
132
|
+
try:
|
|
133
|
+
client = Client(server_url)
|
|
134
|
+
async with client:
|
|
135
|
+
result = await client.call_tool(tool_name, tool_input)
|
|
136
|
+
return str(result)
|
|
137
|
+
except Exception as e:
|
|
138
|
+
return f"Error: {e}"
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
MCP_GET_PROMPT_PARAMETERS = {
|
|
142
|
+
"type": "object",
|
|
143
|
+
"properties": {
|
|
144
|
+
"server_url": {
|
|
145
|
+
"type": "string",
|
|
146
|
+
"description": "The URL of the remote MCP server.",
|
|
147
|
+
},
|
|
148
|
+
"prompt_name": {
|
|
149
|
+
"type": "string",
|
|
150
|
+
"description": "The name of the prompt to get.",
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
"required": ["server_url", "prompt_name"],
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@action(
|
|
158
|
+
name="mcp_get_prompt",
|
|
159
|
+
description="Get a prompt from a remote MCP server.",
|
|
160
|
+
parameters=MCP_GET_PROMPT_PARAMETERS,
|
|
161
|
+
)
|
|
162
|
+
async def mcp_get_prompt(args: dict[str, Any]) -> str:
|
|
163
|
+
"""
|
|
164
|
+
Get a prompt from a remote MCP server.
|
|
165
|
+
"""
|
|
166
|
+
server_url = args.get("server_url")
|
|
167
|
+
prompt_name = args.get("prompt_name")
|
|
168
|
+
|
|
169
|
+
if server_url is None:
|
|
170
|
+
return "Error: `server_url` is required"
|
|
171
|
+
if prompt_name is None:
|
|
172
|
+
return "Error: `prompt_name` is required"
|
|
173
|
+
|
|
174
|
+
if not isinstance(server_url, str):
|
|
175
|
+
return "Error: `server_url` must be a string"
|
|
176
|
+
if not isinstance(prompt_name, str):
|
|
177
|
+
return "Error: `prompt_name` must be a string"
|
|
178
|
+
|
|
179
|
+
try:
|
|
180
|
+
client = Client(server_url)
|
|
181
|
+
async with client:
|
|
182
|
+
result = await client.get_prompt(prompt_name)
|
|
183
|
+
return str(result)
|
|
184
|
+
except Exception as e:
|
|
185
|
+
return f"Error: {e}"
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@action(
|
|
189
|
+
name="mcp_list_prompts",
|
|
190
|
+
description="List the prompts on a remote MCP server.",
|
|
191
|
+
parameters=MCP_PING_PARAMETERS,
|
|
192
|
+
)
|
|
193
|
+
async def mcp_list_prompts(args: dict[str, Any]) -> str:
|
|
194
|
+
"""
|
|
195
|
+
List the prompts on a remote MCP server.
|
|
196
|
+
"""
|
|
197
|
+
server_url = args.get("server_url")
|
|
198
|
+
|
|
199
|
+
if server_url is None:
|
|
200
|
+
return "Error: `server_url` is required"
|
|
201
|
+
if not isinstance(server_url, str):
|
|
202
|
+
return "Error: `server_url` must be a string"
|
|
203
|
+
|
|
204
|
+
try:
|
|
205
|
+
client = Client(server_url)
|
|
206
|
+
async with client:
|
|
207
|
+
result = await client.list_prompts()
|
|
208
|
+
return str(result)
|
|
209
|
+
except Exception as e:
|
|
210
|
+
return f"Error: {e}"
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
MCP_READ_RESOURCE_PARAMETERS = {
|
|
214
|
+
"type": "object",
|
|
215
|
+
"properties": {
|
|
216
|
+
"server_url": {
|
|
217
|
+
"type": "string",
|
|
218
|
+
"description": "The URL of the remote MCP server.",
|
|
219
|
+
},
|
|
220
|
+
"resource_uri": {
|
|
221
|
+
"type": "string",
|
|
222
|
+
"description": "The URI of the resource to read.",
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
"required": ["server_url", "resource_uri"],
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
@action(
|
|
230
|
+
name="mcp_read_resource",
|
|
231
|
+
description="Read a resource from a remote MCP server.",
|
|
232
|
+
parameters=MCP_READ_RESOURCE_PARAMETERS,
|
|
233
|
+
)
|
|
234
|
+
async def mcp_read_resource(args: dict[str, Any]) -> str:
|
|
235
|
+
"""
|
|
236
|
+
Read a resource from a remote MCP server.
|
|
237
|
+
"""
|
|
238
|
+
server_url = args.get("server_url")
|
|
239
|
+
resource_uri = args.get("resource_uri")
|
|
240
|
+
|
|
241
|
+
if server_url is None:
|
|
242
|
+
return "Error: `server_url` is required"
|
|
243
|
+
if resource_uri is None:
|
|
244
|
+
return "Error: `resource_uri` is required"
|
|
245
|
+
|
|
246
|
+
if not isinstance(server_url, str):
|
|
247
|
+
return "Error: `server_url` must be a string"
|
|
248
|
+
if not isinstance(resource_uri, str):
|
|
249
|
+
return "Error: `resource_uri` must be a string"
|
|
250
|
+
|
|
251
|
+
try:
|
|
252
|
+
client = Client(server_url)
|
|
253
|
+
async with client:
|
|
254
|
+
result = await client.read_resource(resource_uri)
|
|
255
|
+
return str(result)
|
|
256
|
+
except Exception as e:
|
|
257
|
+
return f"Error: {e}"
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
MCP_LIST_RESOURCES_PARAMETERS = {
|
|
261
|
+
"type": "object",
|
|
262
|
+
"properties": {
|
|
263
|
+
"server_url": {
|
|
264
|
+
"type": "string",
|
|
265
|
+
"description": "The URL of the remote MCP server.",
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
"required": ["server_url"],
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
@action(
|
|
273
|
+
name="mcp_list_resources",
|
|
274
|
+
description="List the resources on a remote MCP server.",
|
|
275
|
+
parameters=MCP_LIST_RESOURCES_PARAMETERS,
|
|
276
|
+
)
|
|
277
|
+
async def mcp_list_resources(args: dict[str, Any]) -> str:
|
|
278
|
+
"""
|
|
279
|
+
List the resources on a remote MCP server.
|
|
280
|
+
"""
|
|
281
|
+
server_url = args.get("server_url")
|
|
282
|
+
|
|
283
|
+
if server_url is None:
|
|
284
|
+
return "Error: `server_url` is required"
|
|
285
|
+
if not isinstance(server_url, str):
|
|
286
|
+
return "Error: `server_url` must be a string"
|
|
287
|
+
|
|
288
|
+
try:
|
|
289
|
+
client = Client(server_url)
|
|
290
|
+
async with client:
|
|
291
|
+
result = await client.list_resources()
|
|
292
|
+
return str(result)
|
|
293
|
+
except Exception as e:
|
|
294
|
+
return f"Error: {e}"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .agents import (
|
|
2
|
+
OpenAIChatCompletionsAgentFunction,
|
|
3
|
+
OpenAIChatCompletionsSupervisorFunction,
|
|
4
|
+
OpenAIResponsesAgentFunction,
|
|
5
|
+
OpenAIResponsesSupervisorFunction,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"OpenAIChatCompletionsAgentFunction",
|
|
10
|
+
"OpenAIChatCompletionsSupervisorFunction",
|
|
11
|
+
"OpenAIResponsesAgentFunction",
|
|
12
|
+
"OpenAIResponsesSupervisorFunction",
|
|
13
|
+
]
|