sitebay-mcp 0.1.1751179164__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.
@@ -0,0 +1,5 @@
1
+ """
2
+ SiteBay MCP Tools
3
+
4
+ This package contains all the MCP tool implementations for SiteBay functionality.
5
+ """
@@ -0,0 +1,285 @@
1
+ """
2
+ Site operations tools for SiteBay MCP Server
3
+ """
4
+
5
+ from typing import Optional, List, Dict, Any
6
+ from ..client import SiteBayClient
7
+ from ..exceptions import SiteBayError
8
+
9
+
10
+ async def sitebay_site_shell_command(
11
+ client: SiteBayClient,
12
+ fqdn: str,
13
+ command: str
14
+ ) -> str:
15
+ """
16
+ Execute a shell command on a WordPress site (including WP-CLI commands).
17
+
18
+ Args:
19
+ fqdn: The fully qualified domain name of the site
20
+ command: The shell command to execute (e.g., "wp plugin list")
21
+
22
+ Returns:
23
+ Command output or error message
24
+ """
25
+ try:
26
+ result = await client.execute_shell_command(fqdn, command)
27
+
28
+ # Handle different response formats
29
+ if isinstance(result, dict):
30
+ if 'output' in result:
31
+ output = result['output']
32
+ elif 'result' in result:
33
+ output = result['result']
34
+ else:
35
+ output = str(result)
36
+ else:
37
+ output = str(result)
38
+
39
+ response = f"**Command executed on {fqdn}:**\n"
40
+ response += f"```bash\n{command}\n```\n\n"
41
+ response += f"**Output:**\n```\n{output}\n```"
42
+
43
+ return response
44
+
45
+ except SiteBayError as e:
46
+ return f"Error executing command on {fqdn}: {str(e)}"
47
+
48
+
49
+ async def sitebay_site_edit_file(
50
+ client: SiteBayClient,
51
+ fqdn: str,
52
+ file_path: str,
53
+ content: str
54
+ ) -> str:
55
+ """
56
+ Edit a file in the site's wp-content directory.
57
+
58
+ Args:
59
+ fqdn: The fully qualified domain name of the site
60
+ file_path: Path to the file relative to wp-content (e.g., "themes/mytheme/style.css")
61
+ content: New content for the file
62
+
63
+ Returns:
64
+ Success message or error
65
+ """
66
+ try:
67
+ result = await client.edit_file(fqdn, file_path, content)
68
+
69
+ response = f"✅ **File Updated Successfully**\n\n"
70
+ response += f"• **Site**: {fqdn}\n"
71
+ response += f"• **File**: wp-content/{file_path}\n"
72
+ response += f"• **Content Length**: {len(content)} characters\n"
73
+
74
+ if isinstance(result, str) and result:
75
+ response += f"\n**Server Response:**\n```\n{result}\n```"
76
+
77
+ return response
78
+
79
+ except SiteBayError as e:
80
+ return f"Error editing file on {fqdn}: {str(e)}"
81
+
82
+
83
+ async def sitebay_site_get_events(
84
+ client: SiteBayClient,
85
+ fqdn: str,
86
+ after_datetime: Optional[str] = None,
87
+ limit: int = 20
88
+ ) -> str:
89
+ """
90
+ Get recent events for a site (deployments, updates, restores, etc.).
91
+
92
+ Args:
93
+ fqdn: The fully qualified domain name of the site
94
+ after_datetime: Optional datetime to filter events after (ISO format)
95
+ limit: Maximum number of events to return (default: 20)
96
+
97
+ Returns:
98
+ Formatted list of site events
99
+ """
100
+ try:
101
+ events = await client.get_site_events(fqdn, after_datetime)
102
+
103
+ if not events:
104
+ return f"No events found for {fqdn}."
105
+
106
+ # Limit the results
107
+ events = events[:limit]
108
+
109
+ result = f"**Recent Events for {fqdn}** (showing {len(events)} events):\n\n"
110
+
111
+ for event in events:
112
+ result += f"• **{event.get('event_type', 'Unknown Event')}**\n"
113
+ result += f" - Time: {event.get('created_at', 'Unknown')}\n"
114
+ result += f" - Status: {event.get('status', 'Unknown')}\n"
115
+
116
+ if event.get('description'):
117
+ result += f" - Description: {event.get('description')}\n"
118
+
119
+ if event.get('metadata'):
120
+ metadata = event.get('metadata')
121
+ for key, value in metadata.items():
122
+ result += f" - {key.title()}: {value}\n"
123
+
124
+ result += "\n"
125
+
126
+ return result
127
+
128
+ except SiteBayError as e:
129
+ return f"Error getting events for {fqdn}: {str(e)}"
130
+
131
+
132
+ async def sitebay_site_external_path_list(
133
+ client: SiteBayClient,
134
+ fqdn: str
135
+ ) -> str:
136
+ """
137
+ List external path configurations for a site.
138
+
139
+ Args:
140
+ fqdn: The fully qualified domain name of the site
141
+
142
+ Returns:
143
+ List of external path configurations
144
+ """
145
+ try:
146
+ paths = await client.list_external_paths(fqdn)
147
+
148
+ if not paths:
149
+ return f"No external paths configured for {fqdn}."
150
+
151
+ result = f"**External Paths for {fqdn}**:\n\n"
152
+
153
+ for path in paths:
154
+ result += f"• **Path**: {path.get('path', 'Unknown')}\n"
155
+ result += f" - Target URL: {path.get('target_url', 'Unknown')}\n"
156
+ result += f" - Status: {path.get('status', 'Unknown')}\n"
157
+ result += f" - Created: {path.get('created_at', 'Unknown')}\n"
158
+ result += f" - ID: {path.get('id', 'Unknown')}\n"
159
+ result += "\n"
160
+
161
+ return result
162
+
163
+ except SiteBayError as e:
164
+ return f"Error listing external paths for {fqdn}: {str(e)}"
165
+
166
+
167
+ async def sitebay_site_external_path_create(
168
+ client: SiteBayClient,
169
+ fqdn: str,
170
+ path: str,
171
+ target_url: str,
172
+ description: Optional[str] = None
173
+ ) -> str:
174
+ """
175
+ Create an external path configuration for a site.
176
+
177
+ Args:
178
+ fqdn: The fully qualified domain name of the site
179
+ path: The path on your site (e.g., "/api")
180
+ target_url: The external URL to proxy to
181
+ description: Optional description for the path
182
+
183
+ Returns:
184
+ Success message with path details
185
+ """
186
+ try:
187
+ path_data = {
188
+ "path": path,
189
+ "target_url": target_url,
190
+ }
191
+
192
+ if description:
193
+ path_data["description"] = description
194
+
195
+ external_path = await client.create_external_path(fqdn, path_data)
196
+
197
+ result = f"✅ **External Path Created Successfully**\n\n"
198
+ result += f"• **Site**: {fqdn}\n"
199
+ result += f"• **Path**: {external_path.get('path')}\n"
200
+ result += f"• **Target URL**: {external_path.get('target_url')}\n"
201
+ result += f"• **Status**: {external_path.get('status')}\n"
202
+ result += f"• **ID**: {external_path.get('id')}\n"
203
+
204
+ if description:
205
+ result += f"• **Description**: {description}\n"
206
+
207
+ result += f"\n🔗 Your site path {fqdn}{path} now proxies to {target_url}"
208
+
209
+ return result
210
+
211
+ except SiteBayError as e:
212
+ return f"Error creating external path for {fqdn}: {str(e)}"
213
+
214
+
215
+ async def sitebay_site_external_path_update(
216
+ client: SiteBayClient,
217
+ fqdn: str,
218
+ path_id: str,
219
+ path: Optional[str] = None,
220
+ target_url: Optional[str] = None,
221
+ description: Optional[str] = None
222
+ ) -> str:
223
+ """
224
+ Update an external path configuration.
225
+
226
+ Args:
227
+ fqdn: The fully qualified domain name of the site
228
+ path_id: The ID of the external path to update
229
+ path: New path value (optional)
230
+ target_url: New target URL (optional)
231
+ description: New description (optional)
232
+
233
+ Returns:
234
+ Update confirmation message
235
+ """
236
+ try:
237
+ path_data = {}
238
+
239
+ if path:
240
+ path_data["path"] = path
241
+ if target_url:
242
+ path_data["target_url"] = target_url
243
+ if description:
244
+ path_data["description"] = description
245
+
246
+ if not path_data:
247
+ return "No updates specified. Please provide at least one field to update."
248
+
249
+ external_path = await client.update_external_path(fqdn, path_id, path_data)
250
+
251
+ result = f"✅ **External Path Updated Successfully**\n\n"
252
+ result += f"• **Site**: {fqdn}\n"
253
+ result += f"• **Path**: {external_path.get('path')}\n"
254
+ result += f"• **Target URL**: {external_path.get('target_url')}\n"
255
+ result += f"• **Status**: {external_path.get('status')}\n"
256
+ result += f"• **ID**: {external_path.get('id')}\n"
257
+
258
+ return result
259
+
260
+ except SiteBayError as e:
261
+ return f"Error updating external path for {fqdn}: {str(e)}"
262
+
263
+
264
+ async def sitebay_site_external_path_delete(
265
+ client: SiteBayClient,
266
+ fqdn: str,
267
+ path_id: str
268
+ ) -> str:
269
+ """
270
+ Delete an external path configuration.
271
+
272
+ Args:
273
+ fqdn: The fully qualified domain name of the site
274
+ path_id: The ID of the external path to delete
275
+
276
+ Returns:
277
+ Deletion confirmation message
278
+ """
279
+ try:
280
+ await client.delete_external_path(fqdn, path_id)
281
+
282
+ return f"✅ **External Path Deleted Successfully**\n\nExternal path {path_id} has been removed from {fqdn}."
283
+
284
+ except SiteBayError as e:
285
+ return f"Error deleting external path for {fqdn}: {str(e)}"
@@ -0,0 +1,248 @@
1
+ """
2
+ Site management tools for SiteBay MCP Server
3
+ """
4
+
5
+ from typing import Optional, Dict, Any, List
6
+ from ..client import SiteBayClient
7
+ from ..exceptions import SiteBayError
8
+
9
+
10
+ async def sitebay_list_sites(
11
+ client: SiteBayClient,
12
+ team_id: Optional[str] = None
13
+ ) -> str:
14
+ """
15
+ List all WordPress sites for the authenticated user.
16
+
17
+ Args:
18
+ team_id: Optional team ID to filter sites by team
19
+
20
+ Returns:
21
+ Formatted string with site details
22
+ """
23
+ try:
24
+ sites = await client.list_sites(team_id=team_id)
25
+
26
+ if not sites:
27
+ return "No sites found for your account."
28
+
29
+ result = f"Found {len(sites)} site(s):\n\n"
30
+
31
+ for site in sites:
32
+ result += f"• **{site.get('fqdn', 'Unknown')}**\n"
33
+ result += f" - Status: {site.get('status', 'Unknown')}\n"
34
+ result += f" - Region: {site.get('region_name', 'Unknown')}\n"
35
+ result += f" - WordPress Version: {site.get('wp_version', 'Unknown')}\n"
36
+ result += f" - PHP Version: {site.get('php_version', 'Unknown')}\n"
37
+ result += f" - Created: {site.get('created_at', 'Unknown')}\n"
38
+ if site.get('staging_site'):
39
+ result += f" - Has Staging Site: Yes\n"
40
+ result += "\n"
41
+
42
+ return result
43
+
44
+ except SiteBayError as e:
45
+ return f"Error listing sites: {str(e)}"
46
+
47
+
48
+ async def sitebay_get_site(
49
+ client: SiteBayClient,
50
+ fqdn: str
51
+ ) -> str:
52
+ """
53
+ Get detailed information about a specific WordPress site.
54
+
55
+ Args:
56
+ fqdn: The fully qualified domain name of the site
57
+
58
+ Returns:
59
+ Formatted string with detailed site information
60
+ """
61
+ try:
62
+ site = await client.get_site(fqdn)
63
+
64
+ result = f"**Site Details for {fqdn}**\n\n"
65
+ result += f"• **Status**: {site.get('status', 'Unknown')}\n"
66
+ result += f"• **Region**: {site.get('region_name', 'Unknown')}\n"
67
+ result += f"• **WordPress Version**: {site.get('wp_version', 'Unknown')}\n"
68
+ result += f"• **PHP Version**: {site.get('php_version', 'Unknown')}\n"
69
+ result += f"• **MySQL Version**: {site.get('mysql_version', 'Unknown')}\n"
70
+ result += f"• **Site URL**: {site.get('site_url', 'Unknown')}\n"
71
+ result += f"• **Admin URL**: {site.get('admin_url', 'Unknown')}\n"
72
+ result += f"• **Created**: {site.get('created_at', 'Unknown')}\n"
73
+ result += f"• **Updated**: {site.get('updated_at', 'Unknown')}\n"
74
+
75
+ if site.get('staging_site'):
76
+ result += f"• **Staging Site**: Available\n"
77
+ else:
78
+ result += f"• **Staging Site**: Not created\n"
79
+
80
+ if site.get('git_enabled'):
81
+ result += f"• **Git Integration**: Enabled\n"
82
+ if site.get('git_repo'):
83
+ result += f"• **Git Repository**: {site.get('git_repo')}\n"
84
+
85
+ return result
86
+
87
+ except SiteBayError as e:
88
+ return f"Error getting site details: {str(e)}"
89
+
90
+
91
+ async def sitebay_create_site(
92
+ client: SiteBayClient,
93
+ fqdn: str,
94
+ wp_title: str,
95
+ wp_username: str,
96
+ wp_password: str,
97
+ wp_email: str,
98
+ region_name: Optional[str] = None,
99
+ template_id: Optional[str] = None,
100
+ team_id: Optional[str] = None
101
+ ) -> str:
102
+ """
103
+ Create a new WordPress site.
104
+
105
+ Args:
106
+ fqdn: The fully qualified domain name for the new site
107
+ wp_title: WordPress site title
108
+ wp_username: WordPress admin username
109
+ wp_password: WordPress admin password
110
+ wp_email: WordPress admin email
111
+ region_name: Optional region name (uses default if not specified)
112
+ template_id: Optional template ID to use for site creation
113
+ team_id: Optional team ID to create site under
114
+
115
+ Returns:
116
+ Formatted string with new site details
117
+ """
118
+ try:
119
+ site_data = {
120
+ "fqdn": fqdn,
121
+ "wp_title": wp_title,
122
+ "wp_username": wp_username,
123
+ "wp_password": wp_password,
124
+ "wp_email": wp_email,
125
+ }
126
+
127
+ if region_name:
128
+ site_data["region_name"] = region_name
129
+ if template_id:
130
+ site_data["template_id"] = template_id
131
+ if team_id:
132
+ site_data["team_id"] = team_id
133
+
134
+ site = await client.create_site(site_data)
135
+
136
+ result = f"✅ **Site Created Successfully!**\n\n"
137
+ result += f"• **Domain**: {site.get('fqdn')}\n"
138
+ result += f"• **Status**: {site.get('status')}\n"
139
+ result += f"• **Region**: {site.get('region_name')}\n"
140
+ result += f"• **Site URL**: {site.get('site_url')}\n"
141
+ result += f"• **Admin URL**: {site.get('admin_url')}\n"
142
+ result += f"• **WordPress Admin**: {wp_username}\n"
143
+ result += f"• **WordPress Email**: {wp_email}\n"
144
+ result += f"\n🚀 Your WordPress site is being deployed and will be ready shortly!"
145
+
146
+ return result
147
+
148
+ except SiteBayError as e:
149
+ return f"Error creating site: {str(e)}"
150
+
151
+
152
+ async def sitebay_update_site(
153
+ client: SiteBayClient,
154
+ fqdn: str,
155
+ wp_title: Optional[str] = None,
156
+ wp_username: Optional[str] = None,
157
+ wp_password: Optional[str] = None,
158
+ wp_email: Optional[str] = None,
159
+ php_version: Optional[str] = None
160
+ ) -> str:
161
+ """
162
+ Update an existing WordPress site configuration.
163
+
164
+ Args:
165
+ fqdn: The fully qualified domain name of the site to update
166
+ wp_title: New WordPress site title
167
+ wp_username: New WordPress admin username
168
+ wp_password: New WordPress admin password
169
+ wp_email: New WordPress admin email
170
+ php_version: New PHP version (e.g., "8.1", "8.2")
171
+
172
+ Returns:
173
+ Formatted string with update confirmation
174
+ """
175
+ try:
176
+ site_data = {}
177
+
178
+ if wp_title:
179
+ site_data["wp_title"] = wp_title
180
+ if wp_username:
181
+ site_data["wp_username"] = wp_username
182
+ if wp_password:
183
+ site_data["wp_password"] = wp_password
184
+ if wp_email:
185
+ site_data["wp_email"] = wp_email
186
+ if php_version:
187
+ site_data["php_version"] = php_version
188
+
189
+ if not site_data:
190
+ return "No updates specified. Please provide at least one field to update."
191
+
192
+ site = await client.update_site(fqdn, site_data)
193
+
194
+ result = f"✅ **Site Updated Successfully!**\n\n"
195
+ result += f"• **Domain**: {site.get('fqdn')}\n"
196
+ result += f"• **Status**: {site.get('status')}\n"
197
+
198
+ if wp_title:
199
+ result += f"• **Title**: Updated to '{wp_title}'\n"
200
+ if wp_username:
201
+ result += f"• **Admin Username**: Updated to '{wp_username}'\n"
202
+ if wp_password:
203
+ result += f"• **Admin Password**: Updated\n"
204
+ if wp_email:
205
+ result += f"• **Admin Email**: Updated to '{wp_email}'\n"
206
+ if php_version:
207
+ result += f"• **PHP Version**: Updated to {php_version}\n"
208
+
209
+ return result
210
+
211
+ except SiteBayError as e:
212
+ return f"Error updating site: {str(e)}"
213
+
214
+
215
+ async def sitebay_delete_site(
216
+ client: SiteBayClient,
217
+ fqdn: str,
218
+ confirm: bool = False
219
+ ) -> str:
220
+ """
221
+ Delete a WordPress site permanently.
222
+
223
+ Args:
224
+ fqdn: The fully qualified domain name of the site to delete
225
+ confirm: Must be True to actually delete the site (safety check)
226
+
227
+ Returns:
228
+ Confirmation message or error
229
+ """
230
+ if not confirm:
231
+ return (
232
+ f"⚠️ **CONFIRMATION REQUIRED**\n\n"
233
+ f"You are about to permanently delete the site: **{fqdn}**\n\n"
234
+ f"This action will:\n"
235
+ f"• Delete all website files and content\n"
236
+ f"• Delete the database and all data\n"
237
+ f"• Remove any staging sites\n"
238
+ f"• Cannot be undone\n\n"
239
+ f"To proceed with deletion, call this function again with confirm=True"
240
+ )
241
+
242
+ try:
243
+ await client.delete_site(fqdn)
244
+
245
+ return f"✅ **Site Deleted Successfully**\n\nThe site {fqdn} has been permanently deleted."
246
+
247
+ except SiteBayError as e:
248
+ return f"Error deleting site: {str(e)}"