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.
Files changed (137) hide show
  1. mail/__init__.py +35 -0
  2. mail/api.py +1964 -0
  3. mail/cli.py +432 -0
  4. mail/client.py +1657 -0
  5. mail/config/__init__.py +8 -0
  6. mail/config/client.py +87 -0
  7. mail/config/server.py +165 -0
  8. mail/core/__init__.py +72 -0
  9. mail/core/actions.py +69 -0
  10. mail/core/agents.py +73 -0
  11. mail/core/message.py +366 -0
  12. mail/core/runtime.py +3537 -0
  13. mail/core/tasks.py +311 -0
  14. mail/core/tools.py +1206 -0
  15. mail/db/__init__.py +0 -0
  16. mail/db/init.py +182 -0
  17. mail/db/types.py +65 -0
  18. mail/db/utils.py +523 -0
  19. mail/examples/__init__.py +27 -0
  20. mail/examples/analyst_dummy/__init__.py +15 -0
  21. mail/examples/analyst_dummy/agent.py +136 -0
  22. mail/examples/analyst_dummy/prompts.py +44 -0
  23. mail/examples/consultant_dummy/__init__.py +15 -0
  24. mail/examples/consultant_dummy/agent.py +136 -0
  25. mail/examples/consultant_dummy/prompts.py +42 -0
  26. mail/examples/data_analysis/__init__.py +40 -0
  27. mail/examples/data_analysis/analyst/__init__.py +9 -0
  28. mail/examples/data_analysis/analyst/agent.py +67 -0
  29. mail/examples/data_analysis/analyst/prompts.py +53 -0
  30. mail/examples/data_analysis/processor/__init__.py +13 -0
  31. mail/examples/data_analysis/processor/actions.py +293 -0
  32. mail/examples/data_analysis/processor/agent.py +67 -0
  33. mail/examples/data_analysis/processor/prompts.py +48 -0
  34. mail/examples/data_analysis/reporter/__init__.py +10 -0
  35. mail/examples/data_analysis/reporter/actions.py +187 -0
  36. mail/examples/data_analysis/reporter/agent.py +67 -0
  37. mail/examples/data_analysis/reporter/prompts.py +49 -0
  38. mail/examples/data_analysis/statistics/__init__.py +18 -0
  39. mail/examples/data_analysis/statistics/actions.py +343 -0
  40. mail/examples/data_analysis/statistics/agent.py +67 -0
  41. mail/examples/data_analysis/statistics/prompts.py +60 -0
  42. mail/examples/mafia/__init__.py +0 -0
  43. mail/examples/mafia/game.py +1537 -0
  44. mail/examples/mafia/narrator_tools.py +396 -0
  45. mail/examples/mafia/personas.py +240 -0
  46. mail/examples/mafia/prompts.py +489 -0
  47. mail/examples/mafia/roles.py +147 -0
  48. mail/examples/mafia/spec.md +350 -0
  49. mail/examples/math_dummy/__init__.py +23 -0
  50. mail/examples/math_dummy/actions.py +252 -0
  51. mail/examples/math_dummy/agent.py +136 -0
  52. mail/examples/math_dummy/prompts.py +46 -0
  53. mail/examples/math_dummy/types.py +5 -0
  54. mail/examples/research/__init__.py +39 -0
  55. mail/examples/research/researcher/__init__.py +9 -0
  56. mail/examples/research/researcher/agent.py +67 -0
  57. mail/examples/research/researcher/prompts.py +54 -0
  58. mail/examples/research/searcher/__init__.py +10 -0
  59. mail/examples/research/searcher/actions.py +324 -0
  60. mail/examples/research/searcher/agent.py +67 -0
  61. mail/examples/research/searcher/prompts.py +53 -0
  62. mail/examples/research/summarizer/__init__.py +18 -0
  63. mail/examples/research/summarizer/actions.py +255 -0
  64. mail/examples/research/summarizer/agent.py +67 -0
  65. mail/examples/research/summarizer/prompts.py +55 -0
  66. mail/examples/research/verifier/__init__.py +10 -0
  67. mail/examples/research/verifier/actions.py +337 -0
  68. mail/examples/research/verifier/agent.py +67 -0
  69. mail/examples/research/verifier/prompts.py +52 -0
  70. mail/examples/supervisor/__init__.py +11 -0
  71. mail/examples/supervisor/agent.py +4 -0
  72. mail/examples/supervisor/prompts.py +93 -0
  73. mail/examples/support/__init__.py +33 -0
  74. mail/examples/support/classifier/__init__.py +10 -0
  75. mail/examples/support/classifier/actions.py +307 -0
  76. mail/examples/support/classifier/agent.py +68 -0
  77. mail/examples/support/classifier/prompts.py +56 -0
  78. mail/examples/support/coordinator/__init__.py +9 -0
  79. mail/examples/support/coordinator/agent.py +67 -0
  80. mail/examples/support/coordinator/prompts.py +48 -0
  81. mail/examples/support/faq/__init__.py +10 -0
  82. mail/examples/support/faq/actions.py +182 -0
  83. mail/examples/support/faq/agent.py +67 -0
  84. mail/examples/support/faq/prompts.py +42 -0
  85. mail/examples/support/sentiment/__init__.py +15 -0
  86. mail/examples/support/sentiment/actions.py +341 -0
  87. mail/examples/support/sentiment/agent.py +67 -0
  88. mail/examples/support/sentiment/prompts.py +54 -0
  89. mail/examples/weather_dummy/__init__.py +23 -0
  90. mail/examples/weather_dummy/actions.py +75 -0
  91. mail/examples/weather_dummy/agent.py +136 -0
  92. mail/examples/weather_dummy/prompts.py +35 -0
  93. mail/examples/weather_dummy/types.py +5 -0
  94. mail/factories/__init__.py +27 -0
  95. mail/factories/action.py +223 -0
  96. mail/factories/base.py +1531 -0
  97. mail/factories/supervisor.py +241 -0
  98. mail/net/__init__.py +7 -0
  99. mail/net/registry.py +712 -0
  100. mail/net/router.py +728 -0
  101. mail/net/server_utils.py +114 -0
  102. mail/net/types.py +247 -0
  103. mail/server.py +1605 -0
  104. mail/stdlib/__init__.py +0 -0
  105. mail/stdlib/anthropic/__init__.py +0 -0
  106. mail/stdlib/fs/__init__.py +15 -0
  107. mail/stdlib/fs/actions.py +209 -0
  108. mail/stdlib/http/__init__.py +19 -0
  109. mail/stdlib/http/actions.py +333 -0
  110. mail/stdlib/interswarm/__init__.py +11 -0
  111. mail/stdlib/interswarm/actions.py +208 -0
  112. mail/stdlib/mcp/__init__.py +19 -0
  113. mail/stdlib/mcp/actions.py +294 -0
  114. mail/stdlib/openai/__init__.py +13 -0
  115. mail/stdlib/openai/agents.py +451 -0
  116. mail/summarizer.py +234 -0
  117. mail/swarms_json/__init__.py +27 -0
  118. mail/swarms_json/types.py +87 -0
  119. mail/swarms_json/utils.py +255 -0
  120. mail/url_scheme.py +51 -0
  121. mail/utils/__init__.py +53 -0
  122. mail/utils/auth.py +194 -0
  123. mail/utils/context.py +17 -0
  124. mail/utils/logger.py +73 -0
  125. mail/utils/openai.py +212 -0
  126. mail/utils/parsing.py +89 -0
  127. mail/utils/serialize.py +292 -0
  128. mail/utils/store.py +49 -0
  129. mail/utils/string_builder.py +119 -0
  130. mail/utils/version.py +20 -0
  131. mail_swarms-1.3.2.dist-info/METADATA +237 -0
  132. mail_swarms-1.3.2.dist-info/RECORD +137 -0
  133. mail_swarms-1.3.2.dist-info/WHEEL +4 -0
  134. mail_swarms-1.3.2.dist-info/entry_points.txt +2 -0
  135. mail_swarms-1.3.2.dist-info/licenses/LICENSE +202 -0
  136. mail_swarms-1.3.2.dist-info/licenses/NOTICE +10 -0
  137. 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
+ ]