sitebay-mcp 0.1.1751189381__tar.gz → 0.1.1751285665__tar.gz

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 (25) hide show
  1. {sitebay_mcp-0.1.1751189381 → sitebay_mcp-0.1.1751285665}/PKG-INFO +1 -1
  2. {sitebay_mcp-0.1.1751189381 → sitebay_mcp-0.1.1751285665}/pyproject.toml +1 -1
  3. {sitebay_mcp-0.1.1751189381 → sitebay_mcp-0.1.1751285665}/src/sitebay_mcp/client.py +14 -4
  4. {sitebay_mcp-0.1.1751189381 → sitebay_mcp-0.1.1751285665}/src/sitebay_mcp/resources.py +1 -1
  5. {sitebay_mcp-0.1.1751189381 → sitebay_mcp-0.1.1751285665}/src/sitebay_mcp/server.py +4 -4
  6. {sitebay_mcp-0.1.1751189381 → sitebay_mcp-0.1.1751285665}/src/sitebay_mcp/tools/operations.py +1 -1
  7. sitebay_mcp-0.1.1751285665/tests/unit/__init__.py +0 -0
  8. sitebay_mcp-0.1.1751285665/tests/unit/test_auth.py +53 -0
  9. sitebay_mcp-0.1.1751285665/tests/unit/test_client.py +55 -0
  10. {sitebay_mcp-0.1.1751189381 → sitebay_mcp-0.1.1751285665}/.flake8 +0 -0
  11. {sitebay_mcp-0.1.1751189381 → sitebay_mcp-0.1.1751285665}/.github/workflows/python-pytest.yml +0 -0
  12. {sitebay_mcp-0.1.1751189381 → sitebay_mcp-0.1.1751285665}/.github/workflows/testpypi.yaml +0 -0
  13. {sitebay_mcp-0.1.1751189381 → sitebay_mcp-0.1.1751285665}/.gitignore +0 -0
  14. {sitebay_mcp-0.1.1751189381 → sitebay_mcp-0.1.1751285665}/Dockerfile +0 -0
  15. {sitebay_mcp-0.1.1751189381 → sitebay_mcp-0.1.1751285665}/LICENSE +0 -0
  16. {sitebay_mcp-0.1.1751189381 → sitebay_mcp-0.1.1751285665}/PLAN.md +0 -0
  17. {sitebay_mcp-0.1.1751189381 → sitebay_mcp-0.1.1751285665}/README.md +0 -0
  18. {sitebay_mcp-0.1.1751189381 → sitebay_mcp-0.1.1751285665}/example-config.json +0 -0
  19. {sitebay_mcp-0.1.1751189381 → sitebay_mcp-0.1.1751285665}/smithery.yaml +0 -0
  20. {sitebay_mcp-0.1.1751189381 → sitebay_mcp-0.1.1751285665}/src/sitebay_mcp/__init__.py +0 -0
  21. {sitebay_mcp-0.1.1751189381 → sitebay_mcp-0.1.1751285665}/src/sitebay_mcp/auth.py +0 -0
  22. {sitebay_mcp-0.1.1751189381 → sitebay_mcp-0.1.1751285665}/src/sitebay_mcp/exceptions.py +0 -0
  23. {sitebay_mcp-0.1.1751189381 → sitebay_mcp-0.1.1751285665}/src/sitebay_mcp/tools/__init__.py +0 -0
  24. {sitebay_mcp-0.1.1751189381 → sitebay_mcp-0.1.1751285665}/src/sitebay_mcp/tools/sites.py +0 -0
  25. {sitebay_mcp-0.1.1751189381 → sitebay_mcp-0.1.1751285665}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sitebay-mcp
3
- Version: 0.1.1751189381
3
+ Version: 0.1.1751285665
4
4
  Summary: SiteBay MCP Server - WordPress hosting management through Claude Code
5
5
  Author-email: SiteBay <support@sitebay.org>
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sitebay-mcp"
3
- version = "0.1.1751189381"
3
+ version = "0.1.1751285665"
4
4
  description = "SiteBay MCP Server - WordPress hosting management through Claude Code"
5
5
  authors = [{name = "SiteBay", email = "support@sitebay.org"}]
6
6
  readme = "README.md"
@@ -103,7 +103,7 @@ class SiteBayClient:
103
103
 
104
104
  return f"Validation Error: {str(error_data)}"
105
105
 
106
- def _extract_field_errors(self, error_data: dict) -> dict:
106
+ def _extract_field_errors(self, error_data: dict) -> dict[str, str]:
107
107
  """
108
108
  Extract field-specific errors for programmatic access
109
109
 
@@ -113,7 +113,7 @@ class SiteBayClient:
113
113
  Returns:
114
114
  Dictionary mapping field names to error messages
115
115
  """
116
- field_errors = {}
116
+ field_errors: dict[str, str] = {}
117
117
 
118
118
  if not error_data:
119
119
  return field_errors
@@ -233,11 +233,21 @@ class SiteBayClient:
233
233
  return await self._request("DELETE", endpoint, params=params)
234
234
 
235
235
  # Site Management Methods
236
- async def list_sites(self, team_id: Optional[str] = None) -> List[Dict[str, Any]]:
236
+ async def list_sites(self, team_id: Optional[str] = None) -> Union[List[Dict[str, Any]], str]:
237
237
  """List all sites for the user"""
238
238
  params = {"team_id": team_id} if team_id else None
239
239
  response = await self.get("/site", params=params)
240
- return response.get("results", [])
240
+
241
+ # Handle case where API returns error as string
242
+ if isinstance(response, str):
243
+ return response
244
+
245
+ # Handle normal dict response
246
+ if isinstance(response, dict):
247
+ return response.get("results", [])
248
+
249
+ # Handle unexpected response format
250
+ return f"Unexpected response format: {type(response).__name__}"
241
251
 
242
252
  async def get_site(self, fqdn: str) -> Dict[str, Any]:
243
253
  """Get details for a specific site"""
@@ -132,7 +132,7 @@ async def get_account_summary_resource(ctx: Context) -> str:
132
132
  regions = await client.list_regions()
133
133
  templates = await client.list_templates()
134
134
 
135
- summary = {
135
+ summary: dict[str, Any] = {
136
136
  "account_overview": {
137
137
  "total_sites": len(sites),
138
138
  "total_teams": len(teams),
@@ -6,7 +6,7 @@ Main server implementation that provides MCP tools for SiteBay WordPress hosting
6
6
 
7
7
  import asyncio
8
8
  import sys
9
- from typing import Optional
9
+ from typing import Any, Optional
10
10
 
11
11
  from fastmcp import FastMCP
12
12
  from fastmcp.server import Context
@@ -454,7 +454,7 @@ async def sitebay_wordpress_proxy(
454
454
  await ctx.info(f"WordPress proxy request to {site_fqdn}{endpoint}")
455
455
 
456
456
  client = await initialize_client()
457
- proxy_data = {
457
+ proxy_data: dict[str, Any] = {
458
458
  "site_fqdn": site_fqdn,
459
459
  "endpoint": endpoint,
460
460
  "method": method
@@ -499,7 +499,7 @@ async def sitebay_shopify_proxy(
499
499
  await ctx.info(f"Shopify proxy request to {shop_domain}{endpoint}")
500
500
 
501
501
  client = await initialize_client()
502
- proxy_data = {
502
+ proxy_data: dict[str, Any] = {
503
503
  "shop_domain": shop_domain,
504
504
  "endpoint": endpoint,
505
505
  "access_token": access_token,
@@ -541,7 +541,7 @@ async def sitebay_posthog_proxy(
541
541
  await ctx.info(f"PostHog proxy request to {endpoint}")
542
542
 
543
543
  client = await initialize_client()
544
- proxy_data = {
544
+ proxy_data: dict[str, Any] = {
545
545
  "endpoint": endpoint,
546
546
  "data": data
547
547
  }
@@ -117,7 +117,7 @@ async def sitebay_site_get_events(
117
117
  result += f" - Description: {event.get('description')}\n"
118
118
 
119
119
  if event.get('metadata'):
120
- metadata = event.get('metadata')
120
+ metadata = event.get('metadata') or {}
121
121
  for key, value in metadata.items():
122
122
  result += f" - {key.title()}: {value}\n"
123
123
 
File without changes
@@ -0,0 +1,53 @@
1
+ import os
2
+ import pathlib
3
+ import sys
4
+ import types
5
+ import pytest
6
+
7
+ sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / "src"))
8
+
9
+ # Provide a minimal stub for the fastmcp package used in sitebay_mcp.__init__
10
+ fastmcp_stub = types.ModuleType("fastmcp")
11
+
12
+ class _FastMCP:
13
+ def __init__(self, *args, **kwargs):
14
+ pass
15
+
16
+ fastmcp_stub.FastMCP = _FastMCP
17
+ server_stub = types.ModuleType("fastmcp.server")
18
+ server_stub.Context = object
19
+ sys.modules.setdefault("fastmcp", fastmcp_stub)
20
+ sys.modules.setdefault("fastmcp.server", server_stub)
21
+
22
+ # Stub sitebay_mcp.server to avoid importing the real server during tests
23
+ server_module = types.ModuleType("sitebay_mcp.server")
24
+ server_module.main = lambda: None
25
+ sys.modules.setdefault("sitebay_mcp.server", server_module)
26
+
27
+ from sitebay_mcp.auth import SiteBayAuth
28
+ from sitebay_mcp.exceptions import ConfigurationError
29
+
30
+
31
+ def test_validate_token_length():
32
+ auth = SiteBayAuth(api_token="x" * 25)
33
+ assert auth.validate_token() is True
34
+
35
+
36
+ def test_validate_token_short():
37
+ auth = SiteBayAuth(api_token="short")
38
+ assert auth.validate_token() is False
39
+
40
+
41
+ def test_get_headers():
42
+ token = "x" * 25
43
+ auth = SiteBayAuth(api_token=token)
44
+ headers = auth.get_headers()
45
+ assert headers["Authorization"] == f"Bearer {token}"
46
+ assert headers["Content-Type"] == "application/json"
47
+ assert headers["Accept"] == "application/json"
48
+
49
+
50
+ def test_missing_token_raises(monkeypatch):
51
+ monkeypatch.delenv("SITEBAY_API_TOKEN", raising=False)
52
+ with pytest.raises(ConfigurationError):
53
+ SiteBayAuth()
@@ -0,0 +1,55 @@
1
+ import pathlib
2
+ import sys
3
+ import types
4
+ import pytest
5
+
6
+ sys.path.append(str(pathlib.Path(__file__).resolve().parents[2] / "src"))
7
+
8
+ # Stub out fastmcp to satisfy sitebay_mcp package imports
9
+ fastmcp_stub = types.ModuleType("fastmcp")
10
+
11
+ class _FastMCP:
12
+ def __init__(self, *args, **kwargs):
13
+ pass
14
+
15
+ fastmcp_stub.FastMCP = _FastMCP
16
+ server_stub = types.ModuleType("fastmcp.server")
17
+ server_stub.Context = object
18
+ sys.modules.setdefault("fastmcp", fastmcp_stub)
19
+ sys.modules.setdefault("fastmcp.server", server_stub)
20
+
21
+ # Stub sitebay_mcp.server module
22
+ server_module = types.ModuleType("sitebay_mcp.server")
23
+ server_module.main = lambda: None
24
+ sys.modules.setdefault("sitebay_mcp.server", server_module)
25
+
26
+ from sitebay_mcp.client import SiteBayClient, SiteBayAuth
27
+
28
+
29
+ def test_get_url_slash():
30
+ auth = SiteBayAuth(api_token="x" * 25)
31
+ client = SiteBayClient(auth)
32
+ assert client._get_url("/test") == "/f/api/v1/test"
33
+
34
+
35
+ def test_get_url_no_slash():
36
+ auth = SiteBayAuth(api_token="x" * 25)
37
+ client = SiteBayClient(auth)
38
+ assert client._get_url("test") == "/f/api/v1/test"
39
+
40
+
41
+ def test_format_validation_error_basic():
42
+ auth = SiteBayAuth(api_token="x" * 25)
43
+ client = SiteBayClient(auth)
44
+ data = {"detail": [{"loc": ["field"], "msg": "required"}]}
45
+ msg = client._format_validation_error(data)
46
+ assert "field" in msg
47
+ assert "required" in msg
48
+
49
+
50
+ def test_extract_field_errors():
51
+ auth = SiteBayAuth(api_token="x" * 25)
52
+ client = SiteBayClient(auth)
53
+ data = {"detail": [{"loc": ["field"], "msg": "required"}]}
54
+ errors = client._extract_field_errors(data)
55
+ assert errors == {"field": "required"}