sitebay-mcp 0.1.1751180311__tar.gz → 0.1.1751196041__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.1751180311 → sitebay_mcp-0.1.1751196041}/PKG-INFO +1 -1
  2. {sitebay_mcp-0.1.1751180311 → sitebay_mcp-0.1.1751196041}/pyproject.toml +1 -1
  3. {sitebay_mcp-0.1.1751180311 → sitebay_mcp-0.1.1751196041}/src/sitebay_mcp/client.py +2 -2
  4. {sitebay_mcp-0.1.1751180311 → sitebay_mcp-0.1.1751196041}/src/sitebay_mcp/resources.py +1 -1
  5. {sitebay_mcp-0.1.1751180311 → sitebay_mcp-0.1.1751196041}/src/sitebay_mcp/server.py +7 -4
  6. {sitebay_mcp-0.1.1751180311 → sitebay_mcp-0.1.1751196041}/src/sitebay_mcp/tools/operations.py +1 -1
  7. {sitebay_mcp-0.1.1751180311 → sitebay_mcp-0.1.1751196041}/src/sitebay_mcp/tools/sites.py +18 -3
  8. sitebay_mcp-0.1.1751196041/tests/unit/__init__.py +0 -0
  9. sitebay_mcp-0.1.1751196041/tests/unit/test_auth.py +53 -0
  10. sitebay_mcp-0.1.1751196041/tests/unit/test_client.py +55 -0
  11. {sitebay_mcp-0.1.1751180311 → sitebay_mcp-0.1.1751196041}/.flake8 +0 -0
  12. {sitebay_mcp-0.1.1751180311 → sitebay_mcp-0.1.1751196041}/.github/workflows/python-pytest.yml +0 -0
  13. {sitebay_mcp-0.1.1751180311 → sitebay_mcp-0.1.1751196041}/.github/workflows/testpypi.yaml +0 -0
  14. {sitebay_mcp-0.1.1751180311 → sitebay_mcp-0.1.1751196041}/.gitignore +0 -0
  15. {sitebay_mcp-0.1.1751180311 → sitebay_mcp-0.1.1751196041}/Dockerfile +0 -0
  16. {sitebay_mcp-0.1.1751180311 → sitebay_mcp-0.1.1751196041}/LICENSE +0 -0
  17. {sitebay_mcp-0.1.1751180311 → sitebay_mcp-0.1.1751196041}/PLAN.md +0 -0
  18. {sitebay_mcp-0.1.1751180311 → sitebay_mcp-0.1.1751196041}/README.md +0 -0
  19. {sitebay_mcp-0.1.1751180311 → sitebay_mcp-0.1.1751196041}/example-config.json +0 -0
  20. {sitebay_mcp-0.1.1751180311 → sitebay_mcp-0.1.1751196041}/smithery.yaml +0 -0
  21. {sitebay_mcp-0.1.1751180311 → sitebay_mcp-0.1.1751196041}/src/sitebay_mcp/__init__.py +0 -0
  22. {sitebay_mcp-0.1.1751180311 → sitebay_mcp-0.1.1751196041}/src/sitebay_mcp/auth.py +0 -0
  23. {sitebay_mcp-0.1.1751180311 → sitebay_mcp-0.1.1751196041}/src/sitebay_mcp/exceptions.py +0 -0
  24. {sitebay_mcp-0.1.1751180311 → sitebay_mcp-0.1.1751196041}/src/sitebay_mcp/tools/__init__.py +0 -0
  25. {sitebay_mcp-0.1.1751180311 → sitebay_mcp-0.1.1751196041}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sitebay-mcp
3
- Version: 0.1.1751180311
3
+ Version: 0.1.1751196041
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.1751180311"
3
+ version = "0.1.1751196041"
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
@@ -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
@@ -69,6 +69,9 @@ async def sitebay_list_sites(ctx: Context, team_id: Optional[str] = None) -> str
69
69
  except SiteBayError as e:
70
70
  await ctx.error(f"SiteBay API error: {str(e)}")
71
71
  return f"❌ SiteBay Error: {str(e)}"
72
+ except ValueError as e:
73
+ await ctx.error(f"Validation error listing sites: {str(e)}")
74
+ return f"❌ Validation Error: {str(e)}"
72
75
  except Exception as e:
73
76
  await ctx.error(f"Unexpected error listing sites: {str(e)}")
74
77
  return f"❌ Unexpected error: {str(e)}"
@@ -451,7 +454,7 @@ async def sitebay_wordpress_proxy(
451
454
  await ctx.info(f"WordPress proxy request to {site_fqdn}{endpoint}")
452
455
 
453
456
  client = await initialize_client()
454
- proxy_data = {
457
+ proxy_data: dict[str, Any] = {
455
458
  "site_fqdn": site_fqdn,
456
459
  "endpoint": endpoint,
457
460
  "method": method
@@ -496,7 +499,7 @@ async def sitebay_shopify_proxy(
496
499
  await ctx.info(f"Shopify proxy request to {shop_domain}{endpoint}")
497
500
 
498
501
  client = await initialize_client()
499
- proxy_data = {
502
+ proxy_data: dict[str, Any] = {
500
503
  "shop_domain": shop_domain,
501
504
  "endpoint": endpoint,
502
505
  "access_token": access_token,
@@ -538,7 +541,7 @@ async def sitebay_posthog_proxy(
538
541
  await ctx.info(f"PostHog proxy request to {endpoint}")
539
542
 
540
543
  client = await initialize_client()
541
- proxy_data = {
544
+ proxy_data: dict[str, Any] = {
542
545
  "endpoint": endpoint,
543
546
  "data": data
544
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
 
@@ -3,8 +3,8 @@ Site management tools for SiteBay MCP Server
3
3
  """
4
4
 
5
5
  from typing import Optional, Dict, Any, List
6
- from ..client import SiteBayClient
7
- from ..exceptions import SiteBayError
6
+ from sitebay_mcp.client import SiteBayClient
7
+ from sitebay_mcp.exceptions import SiteBayError
8
8
 
9
9
 
10
10
  async def sitebay_list_sites(
@@ -21,7 +21,17 @@ 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)
27
+
24
28
  sites = await client.list_sites(team_id=team_id)
29
+
30
+ if isinstance(sites, str):
31
+ return f"Error listing sites: {sites}"
32
+
33
+ if not isinstance(sites, list) or not all(isinstance(s, dict) for s in sites):
34
+ return f"Unexpected response format when listing sites: {sites}"
25
35
 
26
36
  if not sites:
27
37
  return "No sites found for your account."
@@ -43,6 +53,8 @@ async def sitebay_list_sites(
43
53
 
44
54
  except SiteBayError as e:
45
55
  return f"Error listing sites: {str(e)}"
56
+ except ValueError as e:
57
+ return f"Error listing sites: {str(e)}"
46
58
 
47
59
 
48
60
  async def sitebay_get_site(
@@ -242,7 +254,10 @@ async def sitebay_delete_site(
242
254
  try:
243
255
  await client.delete_site(fqdn)
244
256
 
245
- return f"✅ **Site Deleted Successfully**\n\nThe site {fqdn} has been permanently deleted."
257
+ return (
258
+ "✅ **Site Deleted Successfully**\n\n"
259
+ f"The site {fqdn} has been permanently deleted."
260
+ )
246
261
 
247
262
  except SiteBayError as e:
248
263
  return f"Error deleting site: {str(e)}"
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"}