sitebay-mcp 0.1.1751285665__py3-none-any.whl → 0.1.1757492007__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.
@@ -10,7 +10,9 @@ from ..exceptions import SiteBayError
10
10
  async def sitebay_site_shell_command(
11
11
  client: SiteBayClient,
12
12
  fqdn: str,
13
- command: str
13
+ command: str,
14
+ cwd: Optional[str] = None,
15
+ auto_track_dir: Optional[bool] = None,
14
16
  ) -> str:
15
17
  """
16
18
  Execute a shell command on a WordPress site (including WP-CLI commands).
@@ -23,7 +25,9 @@ async def sitebay_site_shell_command(
23
25
  Command output or error message
24
26
  """
25
27
  try:
26
- result = await client.execute_shell_command(fqdn, command)
28
+ result = await client.execute_shell_command(
29
+ fqdn, cmd=command, cwd=cwd, auto_track_dir=auto_track_dir
30
+ )
27
31
 
28
32
  # Handle different response formats
29
33
  if isinstance(result, dict):
@@ -38,6 +42,8 @@ async def sitebay_site_shell_command(
38
42
 
39
43
  response = f"**Command executed on {fqdn}:**\n"
40
44
  response += f"```bash\n{command}\n```\n\n"
45
+ if cwd is not None:
46
+ response += f"Working dir: {cwd}\n\n"
41
47
  response += f"**Output:**\n```\n{output}\n```"
42
48
 
43
49
  return response
@@ -50,26 +56,52 @@ async def sitebay_site_edit_file(
50
56
  client: SiteBayClient,
51
57
  fqdn: str,
52
58
  file_path: str,
53
- content: str
59
+ file_edit_using_search_replace_blocks: str,
54
60
  ) -> str:
55
61
  """
56
- Edit a file in the site's wp-content directory.
62
+ Edit a file in the site's wp-content directory using diff-edit blocks.
57
63
 
58
64
  Args:
59
65
  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
66
+ file_path: Path to the file relative to wp-content (e.g., "wp-content/themes/mytheme/style.css")
67
+ file_edit_using_search_replace_blocks: A single string containing blocks in the form:
68
+ <<<<<< SEARCH\nold text\n=======\nnew text\n>>>>>> REPLACE
62
69
 
63
70
  Returns:
64
71
  Success message or error
65
72
  """
66
73
  try:
67
- result = await client.edit_file(fqdn, file_path, content)
74
+ # Normalize path (server also handles this, but we pre-normalize to help users)
75
+ normalized_path = file_path.replace(
76
+ "/bitnami/wordpress/wp-content", "wp-content"
77
+ )
78
+
79
+ if not normalized_path.startswith("wp-content"):
80
+ return (
81
+ "❌ Invalid file_path: must start with 'wp-content/'. "
82
+ "Example: wp-content/themes/mytheme/style.css"
83
+ )
84
+
85
+ text = file_edit_using_search_replace_blocks
86
+ if (
87
+ "<<<<<<< SEARCH" not in text
88
+ or "=======" not in text
89
+ or ">>>>>> REPLACE" not in text
90
+ ):
91
+ return (
92
+ "❌ Invalid diff-edit block format. Make sure blocks are in correct sequence, "
93
+ "and the markers are on separate lines:\n\n"
94
+ "<<<<<< SEARCH\n example old\n=======\n example new\n>>>>>> REPLACE\n"
95
+ )
96
+
97
+ result = await client.edit_file(
98
+ fqdn, normalized_path, text
99
+ )
68
100
 
69
101
  response = f"✅ **File Updated Successfully**\n\n"
70
102
  response += f"• **Site**: {fqdn}\n"
71
103
  response += f"• **File**: wp-content/{file_path}\n"
72
- response += f"• **Content Length**: {len(content)} characters\n"
104
+ response += f"• **Content Length**: {len(text)} characters\n"
73
105
 
74
106
  if isinstance(result, str) and result:
75
107
  response += f"\n**Server Response:**\n```\n{result}\n```"
@@ -86,47 +118,7 @@ async def sitebay_site_get_events(
86
118
  after_datetime: Optional[str] = None,
87
119
  limit: int = 20
88
120
  ) -> 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') or {}
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)}"
121
+ return "Site events are not available via schema endpoints."
130
122
 
131
123
 
132
124
  async def sitebay_site_external_path_list(
@@ -142,26 +134,7 @@ async def sitebay_site_external_path_list(
142
134
  Returns:
143
135
  List of external path configurations
144
136
  """
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)}"
137
+ return "External path tools are no longer supported."
165
138
 
166
139
 
167
140
  async def sitebay_site_external_path_create(
@@ -183,33 +156,7 @@ async def sitebay_site_external_path_create(
183
156
  Returns:
184
157
  Success message with path details
185
158
  """
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)}"
159
+ return "External path tools are no longer supported."
213
160
 
214
161
 
215
162
  async def sitebay_site_external_path_update(
@@ -233,32 +180,7 @@ async def sitebay_site_external_path_update(
233
180
  Returns:
234
181
  Update confirmation message
235
182
  """
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)}"
183
+ return "External path tools are no longer supported."
262
184
 
263
185
 
264
186
  async def sitebay_site_external_path_delete(
@@ -276,10 +198,4 @@ async def sitebay_site_external_path_delete(
276
198
  Returns:
277
199
  Deletion confirmation message
278
200
  """
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)}"
201
+ return "External path tools are no longer supported."
@@ -21,9 +21,9 @@ async def sitebay_list_sites(
21
21
  Formatted string with site details
22
22
  """
23
23
  try:
24
- if team_id is not None and not isinstance(team_id, str):
25
- msg = "team_id must be a string if provided"
26
- raise ValueError(msg)
24
+ # Normalize team_id to string (UUID4 expected by API)
25
+ if team_id is not None:
26
+ team_id = str(team_id)
27
27
 
28
28
  sites = await client.list_sites(team_id=team_id)
29
29
 
@@ -102,61 +102,76 @@ async def sitebay_get_site(
102
102
 
103
103
  async def sitebay_create_site(
104
104
  client: SiteBayClient,
105
+ team_id: str,
105
106
  fqdn: str,
106
- wp_title: str,
107
- wp_username: str,
108
- wp_password: str,
109
- wp_email: str,
110
- region_name: Optional[str] = None,
111
- template_id: Optional[str] = None,
112
- team_id: Optional[str] = None
107
+ wordpress_blog_name: str,
108
+ wordpress_first_name: str,
109
+ wordpress_last_name: str,
110
+ wordpress_email: str,
111
+ wordpress_username: str,
112
+ wordpress_password: str,
113
+ git_url: Optional[str] = None,
114
+ ready_made_site_name: Optional[str] = None,
115
+ is_free: Optional[bool] = None,
113
116
  ) -> str:
114
117
  """
115
- Create a new WordPress site.
118
+ Create a new WordPress site (SiteLiveCreate schema).
116
119
 
117
120
  Args:
118
- fqdn: The fully qualified domain name for the new site
119
- wp_title: WordPress site title
120
- wp_username: WordPress admin username
121
- wp_password: WordPress admin password
122
- wp_email: WordPress admin email
123
- region_name: Optional region name (uses default if not specified)
124
- template_id: Optional template ID to use for site creation
125
- team_id: Optional team ID to create site under
121
+ team_id: Team UUID
122
+ fqdn: New site domain
123
+ wordpress_blog_name: Blog/site title
124
+ wordpress_first_name: Admin first name
125
+ wordpress_last_name: Admin last name
126
+ wordpress_email: Admin email
127
+ wordpress_username: Admin username
128
+ wordpress_password: Admin password
129
+ git_url: Optional repository URL
130
+ ready_made_site_name: Optional ready-made site name
131
+ is_free: Optional free plan flag
126
132
 
127
133
  Returns:
128
134
  Formatted string with new site details
129
135
  """
130
136
  try:
131
- site_data = {
137
+ site_data: Dict[str, Any] = {
138
+ "team_id": team_id,
132
139
  "fqdn": fqdn,
133
- "wp_title": wp_title,
134
- "wp_username": wp_username,
135
- "wp_password": wp_password,
136
- "wp_email": wp_email,
140
+ "wordpress_blog_name": wordpress_blog_name,
141
+ "wordpress_first_name": wordpress_first_name,
142
+ "wordpress_last_name": wordpress_last_name,
143
+ "wordpress_email": wordpress_email,
144
+ "wordpress_username": wordpress_username,
145
+ "wordpress_password": wordpress_password,
137
146
  }
138
-
139
- if region_name:
140
- site_data["region_name"] = region_name
141
- if template_id:
142
- site_data["template_id"] = template_id
143
- if team_id:
144
- site_data["team_id"] = team_id
145
-
147
+ if git_url:
148
+ site_data["git_url"] = git_url
149
+ if ready_made_site_name:
150
+ site_data["ready_made_site_name"] = ready_made_site_name
151
+ if is_free is not None:
152
+ site_data["is_free"] = is_free
153
+
146
154
  site = await client.create_site(site_data)
147
-
155
+
148
156
  result = f"✅ **Site Created Successfully!**\n\n"
149
157
  result += f"• **Domain**: {site.get('fqdn')}\n"
150
158
  result += f"• **Status**: {site.get('status')}\n"
151
- result += f"• **Region**: {site.get('region_name')}\n"
159
+ if site.get('region_name'):
160
+ result += f"• **Region**: {site.get('region_name')}\n"
152
161
  result += f"• **Site URL**: {site.get('site_url')}\n"
153
162
  result += f"• **Admin URL**: {site.get('admin_url')}\n"
154
- result += f"• **WordPress Admin**: {wp_username}\n"
155
- result += f"• **WordPress Email**: {wp_email}\n"
156
- result += f"\n🚀 Your WordPress site is being deployed and will be ready shortly!"
157
-
163
+ result += f"• **Admin Username**: {wordpress_username}\n"
164
+ result += f"• **Admin Email**: {wordpress_email}\n"
165
+ if git_url:
166
+ result += f"• **Git URL**: {git_url}\n"
167
+ if ready_made_site_name:
168
+ result += f"• **Ready-made**: {ready_made_site_name}\n"
169
+ if is_free is not None:
170
+ result += f"• **Plan**: {'Free' if is_free else 'Paid'}\n"
171
+ result += "\n🚀 Your WordPress site is being deployed and will be ready shortly!"
172
+
158
173
  return result
159
-
174
+
160
175
  except SiteBayError as e:
161
176
  return f"Error creating site: {str(e)}"
162
177
 
@@ -164,42 +179,46 @@ async def sitebay_create_site(
164
179
  async def sitebay_update_site(
165
180
  client: SiteBayClient,
166
181
  fqdn: str,
167
- wp_title: Optional[str] = None,
168
- wp_username: Optional[str] = None,
169
- wp_password: Optional[str] = None,
170
- wp_email: Optional[str] = None,
171
- php_version: Optional[str] = None
182
+ cf_dev_mode_enabled: Optional[bool] = None,
183
+ new_fqdn: Optional[str] = None,
184
+ git_url: Optional[str] = None,
185
+ http_auth_enabled: Optional[bool] = None,
186
+ team_id: Optional[str] = None,
187
+ is_free: Optional[bool] = None,
172
188
  ) -> str:
173
189
  """
174
- Update an existing WordPress site configuration.
190
+ Update an existing SiteBay site configuration.
175
191
 
176
192
  Args:
177
193
  fqdn: The fully qualified domain name of the site to update
178
- wp_title: New WordPress site title
179
- wp_username: New WordPress admin username
180
- wp_password: New WordPress admin password
181
- wp_email: New WordPress admin email
182
- php_version: New PHP version (e.g., "8.1", "8.2")
194
+ cf_dev_mode_enabled: Enable/disable Cloudflare dev mode
195
+ new_fqdn: New domain for the site
196
+ git_url: Git repository URL
197
+ http_auth_enabled: Enable/disable HTTP basic auth
198
+ team_id: Move site to a different team
199
+ is_free: Toggle free plan flag
183
200
 
184
201
  Returns:
185
202
  Formatted string with update confirmation
186
203
  """
187
204
  try:
188
- site_data = {}
205
+ site_data: Dict[str, Any] = {}
189
206
 
190
- if wp_title:
191
- site_data["wp_title"] = wp_title
192
- if wp_username:
193
- site_data["wp_username"] = wp_username
194
- if wp_password:
195
- site_data["wp_password"] = wp_password
196
- if wp_email:
197
- site_data["wp_email"] = wp_email
198
- if php_version:
199
- site_data["php_version"] = php_version
207
+ if cf_dev_mode_enabled is not None:
208
+ site_data["cf_dev_mode_enabled"] = cf_dev_mode_enabled
209
+ if new_fqdn:
210
+ site_data["new_fqdn"] = new_fqdn
211
+ if git_url:
212
+ site_data["git_url"] = git_url
213
+ if http_auth_enabled is not None:
214
+ site_data["http_auth_enabled"] = http_auth_enabled
215
+ if team_id:
216
+ site_data["team_id"] = team_id
217
+ if is_free is not None:
218
+ site_data["is_free"] = is_free
200
219
 
201
220
  if not site_data:
202
- return "No updates specified. Please provide at least one field to update."
221
+ return "No updates specified. Provide at least one supported field."
203
222
 
204
223
  site = await client.update_site(fqdn, site_data)
205
224
 
@@ -207,16 +226,18 @@ async def sitebay_update_site(
207
226
  result += f"• **Domain**: {site.get('fqdn')}\n"
208
227
  result += f"• **Status**: {site.get('status')}\n"
209
228
 
210
- if wp_title:
211
- result += f"• **Title**: Updated to '{wp_title}'\n"
212
- if wp_username:
213
- result += f"• **Admin Username**: Updated to '{wp_username}'\n"
214
- if wp_password:
215
- result += f"• **Admin Password**: Updated\n"
216
- if wp_email:
217
- result += f"• **Admin Email**: Updated to '{wp_email}'\n"
218
- if php_version:
219
- result += f"• **PHP Version**: Updated to {php_version}\n"
229
+ if cf_dev_mode_enabled is not None:
230
+ result += f"• **Cloudflare Dev Mode**: {'Enabled' if cf_dev_mode_enabled else 'Disabled'}\n"
231
+ if new_fqdn:
232
+ result += f"• **New Domain**: {new_fqdn}\n"
233
+ if git_url:
234
+ result += f"• **Git URL**: {git_url}\n"
235
+ if http_auth_enabled is not None:
236
+ result += f"• **HTTP Auth**: {'Enabled' if http_auth_enabled else 'Disabled'}\n"
237
+ if team_id:
238
+ result += f"• **Team ID**: Moved to {team_id}\n"
239
+ if is_free is not None:
240
+ result += f"• **Plan**: {'Free' if is_free else 'Paid'}\n"
220
241
 
221
242
  return result
222
243
 
@@ -227,30 +248,16 @@ async def sitebay_update_site(
227
248
  async def sitebay_delete_site(
228
249
  client: SiteBayClient,
229
250
  fqdn: str,
230
- confirm: bool = False
231
251
  ) -> str:
232
252
  """
233
253
  Delete a WordPress site permanently.
234
254
 
235
255
  Args:
236
256
  fqdn: The fully qualified domain name of the site to delete
237
- confirm: Must be True to actually delete the site (safety check)
238
257
 
239
258
  Returns:
240
259
  Confirmation message or error
241
260
  """
242
- if not confirm:
243
- return (
244
- f"⚠️ **CONFIRMATION REQUIRED**\n\n"
245
- f"You are about to permanently delete the site: **{fqdn}**\n\n"
246
- f"This action will:\n"
247
- f"• Delete all website files and content\n"
248
- f"• Delete the database and all data\n"
249
- f"• Remove any staging sites\n"
250
- f"• Cannot be undone\n\n"
251
- f"To proceed with deletion, call this function again with confirm=True"
252
- )
253
-
254
261
  try:
255
262
  await client.delete_site(fqdn)
256
263
 
@@ -260,4 +267,4 @@ async def sitebay_delete_site(
260
267
  )
261
268
 
262
269
  except SiteBayError as e:
263
- return f"Error deleting site: {str(e)}"
270
+ return f"Error deleting site: {str(e)}"