sitebay-mcp 0.1.1751286041__py3-none-any.whl → 0.1.1757498234__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,83 +156,9 @@ 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
- 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
162
 
264
163
  async def sitebay_site_external_path_delete(
265
164
  client: SiteBayClient,
@@ -276,10 +175,4 @@ async def sitebay_site_external_path_delete(
276
175
  Returns:
277
176
  Deletion confirmation message
278
177
  """
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)}"
178
+ 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
 
@@ -40,13 +40,10 @@ async def sitebay_list_sites(
40
40
 
41
41
  for site in sites:
42
42
  result += f"• **{site.get('fqdn', 'Unknown')}**\n"
43
- result += f" - Status: {site.get('status', 'Unknown')}\n"
44
- result += f" - Region: {site.get('region_name', 'Unknown')}\n"
45
- result += f" - WordPress Version: {site.get('wp_version', 'Unknown')}\n"
46
- result += f" - PHP Version: {site.get('php_version', 'Unknown')}\n"
43
+ result += f" - Active: {site.get('active', 'Unknown')}\n"
44
+ result += f" - HTTP Auth Enabled: {site.get('http_auth_enabled', 'Unknown')}\n"
45
+ result += f" - Is Free: {site.get('is_free', 'Unknown')}\n"
47
46
  result += f" - Created: {site.get('created_at', 'Unknown')}\n"
48
- if site.get('staging_site'):
49
- result += f" - Has Staging Site: Yes\n"
50
47
  result += "\n"
51
48
 
52
49
  return result
@@ -74,20 +71,14 @@ async def sitebay_get_site(
74
71
  site = await client.get_site(fqdn)
75
72
 
76
73
  result = f"**Site Details for {fqdn}**\n\n"
77
- result += f"• **Status**: {site.get('status', 'Unknown')}\n"
78
- result += f"• **Region**: {site.get('region_name', 'Unknown')}\n"
79
- result += f"• **WordPress Version**: {site.get('wp_version', 'Unknown')}\n"
80
- result += f"• **PHP Version**: {site.get('php_version', 'Unknown')}\n"
81
- result += f"• **MySQL Version**: {site.get('mysql_version', 'Unknown')}\n"
82
- result += f"• **Site URL**: {site.get('site_url', 'Unknown')}\n"
83
- result += f"• **Admin URL**: {site.get('admin_url', 'Unknown')}\n"
74
+ result += f"• **Active**: {site.get('active', 'Unknown')}\n"
75
+ result += f"• **HTTP Auth Enabled**: {site.get('http_auth_enabled', 'Unknown')}\n"
76
+ result += f"• **Is Free**: {site.get('is_free', 'Unknown')}\n"
77
+ result += f"• **Git URL**: {site.get('git_url', '')}\n"
84
78
  result += f"• **Created**: {site.get('created_at', 'Unknown')}\n"
85
79
  result += f"• **Updated**: {site.get('updated_at', 'Unknown')}\n"
86
80
 
87
- if site.get('staging_site'):
88
- result += f"• **Staging Site**: Available\n"
89
- else:
90
- result += f"• **Staging Site**: Not created\n"
81
+
91
82
 
92
83
  if site.get('git_enabled'):
93
84
  result += f"• **Git Integration**: Enabled\n"
@@ -102,61 +93,73 @@ async def sitebay_get_site(
102
93
 
103
94
  async def sitebay_create_site(
104
95
  client: SiteBayClient,
96
+ team_id: str,
105
97
  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
98
+ wordpress_blog_name: str,
99
+ wordpress_first_name: str,
100
+ wordpress_last_name: str,
101
+ wordpress_email: str,
102
+ wordpress_username: str,
103
+ wordpress_password: str,
104
+ git_url: Optional[str] = None,
105
+ ready_made_site_name: Optional[str] = None,
106
+ is_free: Optional[bool] = None,
113
107
  ) -> str:
114
108
  """
115
- Create a new WordPress site.
109
+ Create a new WordPress site (SiteLiveCreate schema).
116
110
 
117
111
  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
112
+ team_id: Team UUID
113
+ fqdn: New site domain
114
+ wordpress_blog_name: Blog/site title
115
+ wordpress_first_name: Admin first name
116
+ wordpress_last_name: Admin last name
117
+ wordpress_email: Admin email
118
+ wordpress_username: Admin username
119
+ wordpress_password: Admin password
120
+ git_url: Optional repository URL
121
+ ready_made_site_name: Optional ready-made site name
122
+ is_free: Optional free plan flag
126
123
 
127
124
  Returns:
128
125
  Formatted string with new site details
129
126
  """
130
127
  try:
131
- site_data = {
128
+ site_data: Dict[str, Any] = {
129
+ "team_id": team_id,
132
130
  "fqdn": fqdn,
133
- "wp_title": wp_title,
134
- "wp_username": wp_username,
135
- "wp_password": wp_password,
136
- "wp_email": wp_email,
131
+ "wordpress_blog_name": wordpress_blog_name,
132
+ "wordpress_first_name": wordpress_first_name,
133
+ "wordpress_last_name": wordpress_last_name,
134
+ "wordpress_email": wordpress_email,
135
+ "wordpress_username": wordpress_username,
136
+ "wordpress_password": wordpress_password,
137
137
  }
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
-
138
+ if git_url:
139
+ site_data["git_url"] = git_url
140
+ if ready_made_site_name:
141
+ site_data["ready_made_site_name"] = ready_made_site_name
142
+ if is_free is not None:
143
+ site_data["is_free"] = is_free
144
+
146
145
  site = await client.create_site(site_data)
147
-
146
+
148
147
  result = f"✅ **Site Created Successfully!**\n\n"
149
148
  result += f"• **Domain**: {site.get('fqdn')}\n"
150
- result += f"• **Status**: {site.get('status')}\n"
151
- result += f"• **Region**: {site.get('region_name')}\n"
152
- result += f"• **Site URL**: {site.get('site_url')}\n"
153
- 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
-
149
+ result += f"• **Active**: {site.get('active', 'Unknown')}\n"
150
+ result += f"• **HTTP Auth Enabled**: {site.get('http_auth_enabled', 'Unknown')}\n"
151
+ result += f"• **Admin Username**: {wordpress_username}\n"
152
+ result += f"• **Admin Email**: {wordpress_email}\n"
153
+ if git_url:
154
+ result += f"• **Git URL**: {git_url}\n"
155
+ if ready_made_site_name:
156
+ result += f"• **Ready-made**: {ready_made_site_name}\n"
157
+ if is_free is not None:
158
+ result += f"• **Plan**: {'Free' if is_free else 'Paid'}\n"
159
+ result += "\n🚀 Your WordPress site is being deployed and will be ready shortly!"
160
+
158
161
  return result
159
-
162
+
160
163
  except SiteBayError as e:
161
164
  return f"Error creating site: {str(e)}"
162
165
 
@@ -164,42 +167,46 @@ async def sitebay_create_site(
164
167
  async def sitebay_update_site(
165
168
  client: SiteBayClient,
166
169
  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
170
+ cf_dev_mode_enabled: Optional[bool] = None,
171
+ new_fqdn: Optional[str] = None,
172
+ git_url: Optional[str] = None,
173
+ http_auth_enabled: Optional[bool] = None,
174
+ team_id: Optional[str] = None,
175
+ is_free: Optional[bool] = None,
172
176
  ) -> str:
173
177
  """
174
- Update an existing WordPress site configuration.
178
+ Update an existing SiteBay site configuration.
175
179
 
176
180
  Args:
177
181
  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")
182
+ cf_dev_mode_enabled: Enable/disable Cloudflare dev mode
183
+ new_fqdn: New domain for the site
184
+ git_url: Git repository URL
185
+ http_auth_enabled: Enable/disable HTTP basic auth
186
+ team_id: Move site to a different team
187
+ is_free: Toggle free plan flag
183
188
 
184
189
  Returns:
185
190
  Formatted string with update confirmation
186
191
  """
187
192
  try:
188
- site_data = {}
193
+ site_data: Dict[str, Any] = {}
189
194
 
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
195
+ if cf_dev_mode_enabled is not None:
196
+ site_data["cf_dev_mode_enabled"] = cf_dev_mode_enabled
197
+ if new_fqdn:
198
+ site_data["new_fqdn"] = new_fqdn
199
+ if git_url:
200
+ site_data["git_url"] = git_url
201
+ if http_auth_enabled is not None:
202
+ site_data["http_auth_enabled"] = http_auth_enabled
203
+ if team_id:
204
+ site_data["team_id"] = team_id
205
+ if is_free is not None:
206
+ site_data["is_free"] = is_free
200
207
 
201
208
  if not site_data:
202
- return "No updates specified. Please provide at least one field to update."
209
+ return "No updates specified. Provide at least one supported field."
203
210
 
204
211
  site = await client.update_site(fqdn, site_data)
205
212
 
@@ -207,16 +214,18 @@ async def sitebay_update_site(
207
214
  result += f"• **Domain**: {site.get('fqdn')}\n"
208
215
  result += f"• **Status**: {site.get('status')}\n"
209
216
 
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"
217
+ if cf_dev_mode_enabled is not None:
218
+ result += f"• **Cloudflare Dev Mode**: {'Enabled' if cf_dev_mode_enabled else 'Disabled'}\n"
219
+ if new_fqdn:
220
+ result += f"• **New Domain**: {new_fqdn}\n"
221
+ if git_url:
222
+ result += f"• **Git URL**: {git_url}\n"
223
+ if http_auth_enabled is not None:
224
+ result += f"• **HTTP Auth**: {'Enabled' if http_auth_enabled else 'Disabled'}\n"
225
+ if team_id:
226
+ result += f"• **Team ID**: Moved to {team_id}\n"
227
+ if is_free is not None:
228
+ result += f"• **Plan**: {'Free' if is_free else 'Paid'}\n"
220
229
 
221
230
  return result
222
231
 
@@ -227,30 +236,16 @@ async def sitebay_update_site(
227
236
  async def sitebay_delete_site(
228
237
  client: SiteBayClient,
229
238
  fqdn: str,
230
- confirm: bool = False
231
239
  ) -> str:
232
240
  """
233
241
  Delete a WordPress site permanently.
234
242
 
235
243
  Args:
236
244
  fqdn: The fully qualified domain name of the site to delete
237
- confirm: Must be True to actually delete the site (safety check)
238
245
 
239
246
  Returns:
240
247
  Confirmation message or error
241
248
  """
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
249
  try:
255
250
  await client.delete_site(fqdn)
256
251
 
@@ -260,4 +255,4 @@ async def sitebay_delete_site(
260
255
  )
261
256
 
262
257
  except SiteBayError as e:
263
- return f"Error deleting site: {str(e)}"
258
+ return f"Error deleting site: {str(e)}"