Skip to content

Commit 1eed106

Browse files
authored
Added new tools for site and location discovery (#3)
* Added new tools for site and location discovery * Updated tests for new tools
1 parent e1615e3 commit 1eed106

3 files changed

Lines changed: 248 additions & 31 deletions

File tree

README.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@ You can add the `devhub-cms-mcp` by updating the environment variables below
4646

4747
```
4848
claude mcp add devhub-cms-mcp \
49-
-e DEVHUB_API_KEY=YOUR_KEY_HERE \
50-
-e DEVHUB_API_SECRET=YOUR_SECRET_HERE \
51-
-e DEVHUB_BASE_URL=https://yourbrand.cloudfrontend.net \
52-
-- uvx devhub-cms-mcp
49+
-e DEVHUB_API_KEY=YOUR_KEY_HERE \
50+
-e DEVHUB_API_SECRET=YOUR_SECRET_HERE \
51+
-e DEVHUB_BASE_URL=https://yourbrand.cloudfrontend.net \
52+
-- uvx devhub-cms-mcp
5353
```
5454

5555
### Installing via Smithery
@@ -115,11 +115,14 @@ uv run main.py
115115

116116
This MCP provides the following tools for interacting with DevHub CMS:
117117

118-
### Location Management
118+
### Business and Location Management
119119

120-
- **get_hours_of_operation(location_id)**: Gets the hours of operation for a specific DevHub location. Returns a structured list of time ranges for each day of the week.
120+
- **get_businesses()**: Gets all businesses within the DevHub account. Returns a list of businesses with their IDs and names.
121+
- **get_locations(business_id)**: Gets all locations for a specific business. Returns detailed location information including address, coordinates, and URLs.
122+
- **get_hours_of_operation(location_id, hours_type='primary')**: Gets the hours of operation for a specific DevHub location. Returns a structured list of time ranges for each day of the week.
121123
- **update_hours(location_id, new_hours, hours_type='primary')**: Updates the hours of operation for a DevHub location.
122124
- **get_nearest_location(business_id, latitude, longitude)**: Finds the nearest DevHub location based on geographic coordinates.
125+
- **site_from_url(url)**: Gets the DevHub site ID and details from a URL. Returns site ID, URL, and associated location IDs.
123126

124127
### Content Management
125128

@@ -150,13 +153,13 @@ uv pip install -e ".[test]"
150153
Run the tests with pytest:
151154

152155
```bash
153-
pytest
156+
uv run pytest
154157
```
155158

156159
For more detailed output and test coverage information:
157160

158161
```bash
159-
pytest -v --cov=devhub_cms_mcp
162+
uv run pytest -v --cov=devhub_cms_mcp
160163
```
161164

162165
### Test Structure

src/devhub_cms_mcp/server.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import json
22
import os
3+
from urllib.parse import urlparse
34

45
from mcp.server.fastmcp import FastMCP
56
from requests_oauthlib import OAuth1Session
@@ -45,6 +46,66 @@ def get_hours_of_operation(location_id: int, hours_type: str = 'primary') -> lis
4546
return content['hours_by_type'].get(hours_type, [])
4647

4748

49+
@mcp.tool()
50+
def get_businesses() -> list:
51+
"""Get all businesses within the DevHub account
52+
53+
Returns a list of businesses with the following fields:
54+
- id: Business ID that can be used in the other tools
55+
- business_name: Business name
56+
57+
If only one business exists in the account, you can assume that the user wants to use that business for any business_id related tools.
58+
"""
59+
client, base_url = get_client()
60+
params = {
61+
'deleted': 0,
62+
'limit': 20,
63+
'order_by': 'business_name',
64+
'project_type': 'default',
65+
}
66+
r = client.get('{}businesses/'.format(base_url), params=params)
67+
content = json.loads(r.content)
68+
return content['objects']
69+
70+
71+
@mcp.tool()
72+
def get_locations(business_id: int) -> list:
73+
"""Get all locations for a business
74+
75+
Returns a list of locations with the following fields:
76+
- id: Location ID that can be used in the other tools
77+
- location_name: Location name
78+
- location_url: Location URL in DevHub
79+
- street: Street address
80+
- city: City
81+
- state: State
82+
- country: Country
83+
- postal_code: Postal code
84+
- lat: Latitude
85+
- lon: Longitude
86+
"""
87+
client, base_url = get_client()
88+
params = {
89+
'business_id': business_id,
90+
'limit': 600,
91+
'order_by': 'location_name',
92+
}
93+
r = client.get('{}locations/'.format(base_url), params=params)
94+
content = json.loads(r.content)
95+
return [{
96+
'id': location['id'],
97+
'location_name': location['location_name'],
98+
'location_url': location['location_url'],
99+
'street': location['street'],
100+
'city': location['city'],
101+
'state': location['state'],
102+
'country': location['country'],
103+
'postal_code': location['postal_code'],
104+
'lat': location['lat'],
105+
'lon': location['lon'],
106+
} for location in content['objects']]
107+
108+
48109
@mcp.tool()
49110
def update_hours(location_id: int, new_hours: list, hours_type: str = 'primary') -> str:
50111
"""Update the hours of operation for a DevHub location
@@ -81,6 +142,42 @@ def update_hours(location_id: int, new_hours: list, hours_type: str = 'primary')
81142
return 'Updated successfully'
82143

83144

145+
@mcp.tool()
146+
def site_from_url(url: str) -> str:
147+
"""Get the DevHub site ID from a URL.
148+
149+
Can prompt the user for the URL instead of passing a site_id.
150+
151+
Returns details about the Site matches the URL that can be used in the other tools.
152+
- Site ID: ID of the DevHub site
153+
- Site URL: URL of the DevHub site
154+
- Site Location IDs: List of location IDs associated with the site
155+
156+
Args:
157+
url: URL of the DevHub site, all lowercase and ends with a slash
158+
"""
159+
parsed_url = urlparse(url)
160+
subdomain = parsed_url.netloc.split('.', 1)[0] or 'www'
161+
domain = parsed_url.netloc.split('.', 1)[1]
162+
base_directory = parsed_url.path
163+
client, base_url = get_client()
164+
r = client.get('{}sites/'.format(base_url), params={
165+
'base_directory': base_directory,
166+
'deleted': 0,
167+
'domain': domain,
168+
'subdomain': subdomain,
169+
})
170+
content = json.loads(r.content)
171+
if len(content['objects']) == 0:
172+
return 'No site found'
173+
site = content['objects'][0]
174+
return f"""
175+
Site ID: {site['id']}
176+
Site URL: {site['formatted_url']}
177+
Site Location IDs: {", ".join([str(location_id) for location_id in site['location_ids']])}
178+
"""
179+
180+
84181
@mcp.tool()
85182
def upload_image(base64_image_content: str, filename: str) -> str:
86183
"""Upload an image to the DevHub media gallery

0 commit comments

Comments
 (0)