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.
- sitebay_mcp/__init__.py +2 -2
- sitebay_mcp/client.py +33 -48
- sitebay_mcp/resources.py +23 -76
- sitebay_mcp/server.py +268 -351
- sitebay_mcp/tools/operations.py +44 -151
- sitebay_mcp/tools/sites.py +102 -107
- {sitebay_mcp-0.1.1751286041.dist-info → sitebay_mcp-0.1.1757498234.dist-info}/METADATA +70 -34
- sitebay_mcp-0.1.1757498234.dist-info/RECORD +14 -0
- sitebay_mcp-0.1.1751286041.dist-info/RECORD +0 -14
- {sitebay_mcp-0.1.1751286041.dist-info → sitebay_mcp-0.1.1757498234.dist-info}/WHEEL +0 -0
- {sitebay_mcp-0.1.1751286041.dist-info → sitebay_mcp-0.1.1757498234.dist-info}/entry_points.txt +0 -0
- {sitebay_mcp-0.1.1751286041.dist-info → sitebay_mcp-0.1.1757498234.dist-info}/licenses/LICENSE +0 -0
sitebay_mcp/__init__.py
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
SiteBay MCP Server
|
3
3
|
|
4
4
|
Provides Model Context Protocol (MCP) integration for SiteBay WordPress hosting platform.
|
5
|
-
Allows Claude Code users to manage WordPress sites, execute commands,
|
5
|
+
Allows Claude Code users to manage WordPress sites, execute commands,
|
6
6
|
backups, and more through natural language interactions.
|
7
7
|
"""
|
8
8
|
|
@@ -12,4 +12,4 @@ __email__ = "support@sitebay.org"
|
|
12
12
|
|
13
13
|
from .server import main
|
14
14
|
|
15
|
-
__all__ = ["main"]
|
15
|
+
__all__ = ["main"]
|
sitebay_mcp/client.py
CHANGED
@@ -267,38 +267,42 @@ class SiteBayClient:
|
|
267
267
|
return await self.delete(f"/site/{fqdn}")
|
268
268
|
|
269
269
|
# Site Operations Methods
|
270
|
-
async def execute_shell_command(
|
270
|
+
async def execute_shell_command(
|
271
|
+
self,
|
272
|
+
fqdn: str,
|
273
|
+
cmd: str,
|
274
|
+
cwd: Optional[str] = None,
|
275
|
+
auto_track_dir: Optional[bool] = None,
|
276
|
+
) -> Any:
|
271
277
|
"""Execute a shell command on a site"""
|
272
|
-
|
273
|
-
|
274
|
-
|
278
|
+
payload: Dict[str, Any] = {"cmd": cmd}
|
279
|
+
if cwd is not None:
|
280
|
+
payload["cwd"] = cwd
|
281
|
+
if auto_track_dir is not None:
|
282
|
+
payload["auto_track_dir"] = auto_track_dir
|
283
|
+
return await self.post(f"/site/{fqdn}/cmd", json_data=payload)
|
284
|
+
|
285
|
+
async def edit_file(
|
286
|
+
self,
|
287
|
+
fqdn: str,
|
288
|
+
file_path: str,
|
289
|
+
file_edit_using_search_replace_blocks: str,
|
290
|
+
) -> str:
|
275
291
|
"""Edit a file in the site's wp-content directory"""
|
276
292
|
return await self.post(
|
277
293
|
f"/site/{fqdn}/wpfile_diff_edit",
|
278
|
-
json_data={
|
294
|
+
json_data={
|
295
|
+
"file_path": file_path,
|
296
|
+
"file_edit_using_search_replace_blocks": file_edit_using_search_replace_blocks,
|
297
|
+
},
|
279
298
|
)
|
280
299
|
|
281
|
-
|
282
|
-
"""Get site events"""
|
283
|
-
params = {"after_datetime": after_datetime} if after_datetime else None
|
284
|
-
response = await self.get(f"/site/{fqdn}/event", params=params)
|
285
|
-
return response.get("results", [])
|
286
|
-
|
287
|
-
# Staging Methods
|
288
|
-
async def create_staging_site(self, fqdn: str, staging_data: Dict[str, Any]) -> Dict[str, Any]:
|
289
|
-
"""Create a staging site"""
|
290
|
-
return await self.post(f"/site/{fqdn}/stage", json_data=staging_data)
|
300
|
+
# Site events endpoint removed (not present in schema)
|
291
301
|
|
292
|
-
|
293
|
-
"""Delete a staging site"""
|
294
|
-
return await self.delete(f"/site/{fqdn}/stage")
|
295
|
-
|
296
|
-
async def commit_staging_site(self, fqdn: str) -> Dict[str, Any]:
|
297
|
-
"""Commit staging site to live"""
|
298
|
-
return await self.post(f"/site/{fqdn}/stage/commit")
|
302
|
+
# Staging methods removed (no longer supported)
|
299
303
|
|
300
304
|
# Backup/Restore Methods
|
301
|
-
async def get_backup_commits(self, fqdn: str, number_to_fetch: int =
|
305
|
+
async def get_backup_commits(self, fqdn: str, number_to_fetch: int = 1) -> List[Dict[str, Any]]:
|
302
306
|
"""Get backup commits for a site"""
|
303
307
|
params = {"number_to_fetch": number_to_fetch}
|
304
308
|
return await self.get(f"/site/{fqdn}/pit_restore/commits", params=params)
|
@@ -313,22 +317,7 @@ class SiteBayClient:
|
|
313
317
|
return response.get("results", [])
|
314
318
|
|
315
319
|
# External Path Methods
|
316
|
-
|
317
|
-
"""List external paths for a site"""
|
318
|
-
response = await self.get(f"/site/{fqdn}/external_path")
|
319
|
-
return response.get("results", [])
|
320
|
-
|
321
|
-
async def create_external_path(self, fqdn: str, path_data: Dict[str, Any]) -> Dict[str, Any]:
|
322
|
-
"""Create an external path"""
|
323
|
-
return await self.post(f"/site/{fqdn}/external_path", json_data=path_data)
|
324
|
-
|
325
|
-
async def update_external_path(self, fqdn: str, path_id: str, path_data: Dict[str, Any]) -> Dict[str, Any]:
|
326
|
-
"""Update an external path"""
|
327
|
-
return await self.patch(f"/site/{fqdn}/external_path/{path_id}", json_data=path_data)
|
328
|
-
|
329
|
-
async def delete_external_path(self, fqdn: str, path_id: str) -> Dict[str, Any]:
|
330
|
-
"""Delete an external path"""
|
331
|
-
return await self.delete(f"/site/{fqdn}/external_path/{path_id}")
|
320
|
+
# External path methods removed (no longer supported)
|
332
321
|
|
333
322
|
# Proxy Methods
|
334
323
|
async def wordpress_proxy(self, proxy_data: Dict[str, Any]) -> Any:
|
@@ -349,16 +338,12 @@ class SiteBayClient:
|
|
349
338
|
response = await self.get("/team")
|
350
339
|
return response.get("results", [])
|
351
340
|
|
352
|
-
#
|
353
|
-
async def
|
354
|
-
"""List available
|
355
|
-
response = await self.get("/
|
341
|
+
# Ready-made site catalog
|
342
|
+
async def list_ready_made_sites(self) -> List[Dict[str, Any]]:
|
343
|
+
"""List available ready-made sites"""
|
344
|
+
response = await self.get("/ready_made_site")
|
356
345
|
return response.get("results", [])
|
357
346
|
|
358
|
-
async def list_regions(self) -> List[Dict[str, Any]]:
|
359
|
-
"""List available regions"""
|
360
|
-
return await self.get("/region")
|
361
|
-
|
362
347
|
# Account Methods
|
363
348
|
async def get_affiliate_referrals(self) -> List[Dict[str, Any]]:
|
364
349
|
"""Get affiliate referrals"""
|
@@ -367,4 +352,4 @@ class SiteBayClient:
|
|
367
352
|
|
368
353
|
async def create_checkout_session(self, checkout_data: Dict[str, Any]) -> Dict[str, Any]:
|
369
354
|
"""Create Stripe checkout session"""
|
370
|
-
return await self.post("/create_checkout_session", json_data=checkout_data)
|
355
|
+
return await self.post("/create_checkout_session", json_data=checkout_data)
|
sitebay_mcp/resources.py
CHANGED
@@ -30,30 +30,18 @@ async def get_site_config_resource(ctx: Context, site_fqdn: str) -> str:
|
|
30
30
|
|
31
31
|
site = await client.get_site(site_fqdn)
|
32
32
|
|
33
|
-
# Format as readable configuration
|
33
|
+
# Format as readable configuration (schema-aligned fields)
|
34
34
|
config = {
|
35
35
|
"site_info": {
|
36
36
|
"domain": site.get("fqdn"),
|
37
|
-
"
|
38
|
-
"
|
39
|
-
"region": site.get("region_name"),
|
37
|
+
"active": site.get("active"),
|
38
|
+
"team_id": site.get("team_id"),
|
40
39
|
"created": site.get("created_at"),
|
41
|
-
"updated": site.get("updated_at")
|
42
|
-
},
|
43
|
-
"technical_specs": {
|
44
|
-
"php_version": site.get("php_version"),
|
45
|
-
"mysql_version": site.get("mysql_version"),
|
46
|
-
"wordpress_version": site.get("wp_version"),
|
47
|
-
"git_enabled": site.get("git_enabled", False)
|
48
|
-
},
|
49
|
-
"urls": {
|
50
|
-
"site_url": site.get("site_url"),
|
51
|
-
"admin_url": site.get("admin_url")
|
52
40
|
},
|
53
41
|
"features": {
|
54
|
-
"
|
55
|
-
"
|
56
|
-
"
|
42
|
+
"http_auth_enabled": site.get("http_auth_enabled", False),
|
43
|
+
"is_free": site.get("is_free", False),
|
44
|
+
"git_url": site.get("git_url"),
|
57
45
|
}
|
58
46
|
}
|
59
47
|
|
@@ -64,50 +52,7 @@ async def get_site_config_resource(ctx: Context, site_fqdn: str) -> str:
|
|
64
52
|
return f"Error: {str(e)}"
|
65
53
|
|
66
54
|
|
67
|
-
|
68
|
-
"""
|
69
|
-
Get site events/logs as a resource.
|
70
|
-
|
71
|
-
Args:
|
72
|
-
ctx: FastMCP context
|
73
|
-
site_fqdn: Site domain name
|
74
|
-
limit: Maximum number of events to fetch
|
75
|
-
|
76
|
-
Returns:
|
77
|
-
JSON formatted site events
|
78
|
-
"""
|
79
|
-
try:
|
80
|
-
await ctx.info(f"Fetching events resource for: {site_fqdn}")
|
81
|
-
|
82
|
-
from .server import initialize_client
|
83
|
-
client = await initialize_client()
|
84
|
-
|
85
|
-
events = await client.get_site_events(site_fqdn)
|
86
|
-
|
87
|
-
# Limit and format events
|
88
|
-
limited_events = events[:limit]
|
89
|
-
|
90
|
-
formatted_events = {
|
91
|
-
"site": site_fqdn,
|
92
|
-
"total_events": len(events),
|
93
|
-
"showing": len(limited_events),
|
94
|
-
"events": [
|
95
|
-
{
|
96
|
-
"timestamp": event.get("created_at"),
|
97
|
-
"type": event.get("event_type"),
|
98
|
-
"status": event.get("status"),
|
99
|
-
"description": event.get("description"),
|
100
|
-
"metadata": event.get("metadata", {})
|
101
|
-
}
|
102
|
-
for event in limited_events
|
103
|
-
]
|
104
|
-
}
|
105
|
-
|
106
|
-
return json.dumps(formatted_events, indent=2)
|
107
|
-
|
108
|
-
except SiteBayError as e:
|
109
|
-
await ctx.error(f"Error fetching events for {site_fqdn}: {str(e)}")
|
110
|
-
return f"Error: {str(e)}"
|
55
|
+
# Site events resource removed (not present in schema)
|
111
56
|
|
112
57
|
|
113
58
|
async def get_account_summary_resource(ctx: Context) -> str:
|
@@ -129,37 +74,39 @@ async def get_account_summary_resource(ctx: Context) -> str:
|
|
129
74
|
# Get sites and teams in parallel
|
130
75
|
sites = await client.list_sites()
|
131
76
|
teams = await client.list_teams()
|
132
|
-
|
133
|
-
|
77
|
+
# Regions endpoint removed; ready-made sites replaces templates
|
78
|
+
ready_made_sites = await client.list_ready_made_sites()
|
134
79
|
|
135
80
|
summary: dict[str, Any] = {
|
136
81
|
"account_overview": {
|
137
82
|
"total_sites": len(sites),
|
138
83
|
"total_teams": len(teams),
|
139
|
-
"
|
140
|
-
|
84
|
+
"available_ready_made_sites": len(ready_made_sites),
|
85
|
+
# Filled below
|
86
|
+
"active_sites": 0,
|
87
|
+
"inactive_sites": 0,
|
141
88
|
},
|
142
|
-
"sites_by_status": {},
|
143
|
-
"sites_by_region": {},
|
144
89
|
"recent_sites": []
|
145
90
|
}
|
146
91
|
|
147
92
|
# Analyze sites
|
93
|
+
active_count = 0
|
94
|
+
inactive_count = 0
|
148
95
|
for site in sites:
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
96
|
+
if bool(site.get("active", False)):
|
97
|
+
active_count += 1
|
98
|
+
else:
|
99
|
+
inactive_count += 1
|
100
|
+
summary["account_overview"]["active_sites"] = active_count
|
101
|
+
summary["account_overview"]["inactive_sites"] = inactive_count
|
154
102
|
|
155
103
|
# Get 5 most recent sites
|
156
104
|
sorted_sites = sorted(sites, key=lambda x: x.get("created_at", ""), reverse=True)
|
157
105
|
summary["recent_sites"] = [
|
158
106
|
{
|
159
107
|
"domain": site.get("fqdn"),
|
160
|
-
"
|
108
|
+
"active": site.get("active"),
|
161
109
|
"created": site.get("created_at"),
|
162
|
-
"region": site.get("region_name")
|
163
110
|
}
|
164
111
|
for site in sorted_sites[:5]
|
165
112
|
]
|
@@ -168,4 +115,4 @@ async def get_account_summary_resource(ctx: Context) -> str:
|
|
168
115
|
|
169
116
|
except SiteBayError as e:
|
170
117
|
await ctx.error(f"Error fetching account summary: {str(e)}")
|
171
|
-
return f"Error: {str(e)}"
|
118
|
+
return f"Error: {str(e)}"
|