Ghost MCP Server
by MFYDev
Verified
"""Tier-related MCP tools for Ghost API."""
import json
from typing import Optional, List
from mcp.server.fastmcp import Context
from ..api import make_ghost_request, get_auth_headers
from ..config import STAFF_API_KEY
from mcp.server.fastmcp import Context
from ..api import make_ghost_request, get_auth_headers
from ..config import STAFF_API_KEY
from ..exceptions import GhostError
async def list_tiers(
format: str = "text",
page: int = 1,
limit: int = 15,
ctx: Context = None
) -> str:
"""Get the list of tiers from your Ghost blog.
Args:
format: Output format - either "text" or "json" (default: "text")
page: Page number for pagination (default: 1)
limit: Number of tiers per page (default: 15)
ctx: Optional context for logging
Returns:
Formatted string containing tier information
"""
if ctx:
ctx.info(f"Listing tiers (page {page}, limit {limit}, format {format})")
try:
if ctx:
ctx.debug("Getting auth headers")
headers = await get_auth_headers(STAFF_API_KEY)
if ctx:
ctx.debug("Making API request to /tiers/ with pagination")
data = await make_ghost_request(
f"tiers/?page={page}&limit={limit}&include=monthly_price,yearly_price,benefits",
headers,
ctx
)
if ctx:
ctx.debug("Processing tiers list response")
tiers = data.get("tiers", [])
if not tiers:
if ctx:
ctx.info("No tiers found in response")
return "No tiers found."
if format.lower() == "json":
if ctx:
ctx.debug("Returning JSON format")
return json.dumps(tiers, indent=2)
formatted_tiers = []
for tier in tiers:
benefits = tier.get('benefits', [])
formatted_tier = f"""
Name: {tier.get('name', 'Unknown')}
Description: {tier.get('description', 'No description')}
Type: {tier.get('type', 'Unknown')}
Active: {tier.get('active', False)}
Monthly Price: {tier.get('monthly_price', 'N/A')}
Yearly Price: {tier.get('yearly_price', 'N/A')}
Benefits: {', '.join(benefits) if benefits else 'None'}
ID: {tier.get('id', 'Unknown')}
"""
formatted_tiers.append(formatted_tier)
return "\n---\n".join(formatted_tiers)
except GhostError as e:
if ctx:
ctx.error(f"Failed to list tiers: {str(e)}")
return str(e)
async def read_tier(tier_id: str, ctx: Context = None) -> str:
"""Get the details of a specific tier.
Args:
tier_id: The ID of the tier to retrieve
ctx: Optional context for logging
Returns:
Formatted string containing the tier details
"""
if ctx:
ctx.info(f"Reading tier details for ID: {tier_id}")
try:
if ctx:
ctx.debug("Getting auth headers")
headers = await get_auth_headers(STAFF_API_KEY)
if ctx:
ctx.debug(f"Making API request to /tiers/{tier_id}/")
data = await make_ghost_request(
f"tiers/{tier_id}/?include=monthly_price,yearly_price,benefits",
headers,
ctx
)
if ctx:
ctx.debug("Processing tier response data")
tier = data["tiers"][0]
benefits = tier.get('benefits', [])
return f"""
Name: {tier.get('name', 'Unknown')}
Description: {tier.get('description', 'No description')}
Type: {tier.get('type', 'Unknown')}
Active: {tier.get('active', False)}
Welcome Page URL: {tier.get('welcome_page_url', 'None')}
Created: {tier.get('created_at', 'Unknown')}
Updated: {tier.get('updated_at', 'Unknown')}
Monthly Price: {tier.get('monthly_price', 'N/A')}
Yearly Price: {tier.get('yearly_price', 'N/A')}
Currency: {tier.get('currency', 'Unknown')}
Benefits:
{chr(10).join(f'- {benefit}' for benefit in benefits) if benefits else 'No benefits listed'}
"""
except GhostError as e:
if ctx:
ctx.error(f"Failed to read tier: {str(e)}")
return str(e)
async def create_tier(
name: str,
monthly_price: Optional[int] = None,
yearly_price: Optional[int] = None,
description: Optional[str] = None,
benefits: Optional[List[str]] = None,
welcome_page_url: Optional[str] = None,
visibility: str = "public",
currency: str = "usd",
ctx: Context = None
) -> str:
"""Create a new tier in Ghost.
Args:
name: Name of the tier (required)
monthly_price: Optional monthly price in cents (e.g. 500 for $5.00)
yearly_price: Optional yearly price in cents (e.g. 5000 for $50.00)
description: Optional description of the tier
benefits: Optional list of benefits for the tier
welcome_page_url: Optional URL for the welcome page
visibility: Visibility of tier, either "public" or "none" (default: "public")
currency: Currency for prices (default: "usd")
ctx: Optional context for logging
Returns:
String representation of the created tier
Raises:
GhostError: If the Ghost API request fails
"""
if not name:
raise ValueError("Name is required for creating a tier")
if ctx:
ctx.info(f"Creating new tier: {name}")
# Construct tier data
tier_data = {
"tiers": [{
"name": name,
"description": description,
"type": "paid" if (monthly_price or yearly_price) else "free",
"active": True,
"visibility": visibility,
"welcome_page_url": welcome_page_url,
"benefits": benefits or [],
"currency": currency
}]
}
# Add pricing if provided
if monthly_price is not None:
tier_data["tiers"][0]["monthly_price"] = monthly_price
if yearly_price is not None:
tier_data["tiers"][0]["yearly_price"] = yearly_price
try:
if ctx:
ctx.debug("Getting auth headers")
headers = await get_auth_headers(STAFF_API_KEY)
if ctx:
ctx.debug("Making API request to create tier")
response = await make_ghost_request(
"tiers/",
headers,
ctx,
http_method="POST",
json_data=tier_data
)
if ctx:
ctx.debug("Processing created tier response")
tier = response.get("tiers", [{}])[0]
# Format response
benefits_text = "\n- ".join(tier.get('benefits', [])) if tier.get('benefits') else "None"
return f"""
Tier created successfully:
Name: {tier.get('name')}
Type: {tier.get('type')}
Description: {tier.get('description', 'No description')}
Active: {tier.get('active', False)}
Visibility: {tier.get('visibility', 'public')}
Monthly Price: {tier.get('monthly_price', 'N/A')} {tier.get('currency', 'usd').upper()}
Yearly Price: {tier.get('yearly_price', 'N/A')} {tier.get('currency', 'usd').upper()}
Currency: {tier.get('currency', 'usd').upper()}
Benefits:
- {benefits_text}
ID: {tier.get('id', 'Unknown')}
"""
except Exception as e:
if ctx:
ctx.error(f"Failed to create tier: {str(e)}")
raise
async def update_tier(
tier_id: str,
name: Optional[str] = None,
description: Optional[str] = None,
monthly_price: Optional[int] = None,
yearly_price: Optional[int] = None,
benefits: Optional[List[str]] = None,
welcome_page_url: Optional[str] = None,
visibility: Optional[str] = None,
currency: Optional[str] = None,
active: Optional[bool] = None,
ctx: Context = None
) -> str:
"""Update an existing tier in Ghost.
Args:
tier_id: ID of the tier to update (required)
name: New name for the tier
description: New description for the tier
monthly_price: New monthly price in cents (e.g. 500 for $5.00)
yearly_price: New yearly price in cents (e.g. 5000 for $50.00)
benefits: New list of benefits for the tier
welcome_page_url: New URL for the welcome page
visibility: New visibility setting ("public" or "none")
currency: New currency for prices
active: New active status
ctx: Optional context for logging
Returns:
String representation of the updated tier
Raises:
GhostError: If the Ghost API request fails
"""
if ctx:
ctx.info(f"Updating tier with ID: {tier_id}")
# Construct update data with only provided fields
update_data = {"tiers": [{}]}
tier_updates = update_data["tiers"][0]
if name is not None:
tier_updates["name"] = name
if description is not None:
tier_updates["description"] = description
if monthly_price is not None:
tier_updates["monthly_price"] = monthly_price
if yearly_price is not None:
tier_updates["yearly_price"] = yearly_price
if benefits is not None:
tier_updates["benefits"] = benefits
if welcome_page_url is not None:
tier_updates["welcome_page_url"] = welcome_page_url
if visibility is not None:
tier_updates["visibility"] = visibility
if currency is not None:
tier_updates["currency"] = currency
if active is not None:
tier_updates["active"] = active
try:
if ctx:
ctx.debug("Getting auth headers")
headers = await get_auth_headers(STAFF_API_KEY)
if ctx:
ctx.debug(f"Making API request to update tier {tier_id}")
response = await make_ghost_request(
f"tiers/{tier_id}/",
headers,
ctx,
http_method="PUT",
json_data=update_data
)
if ctx:
ctx.debug("Processing updated tier response")
tier = response.get("tiers", [{}])[0]
# Format response
benefits_text = "\n- ".join(tier.get('benefits', [])) if tier.get('benefits') else "None"
return f"""
Tier updated successfully:
Name: {tier.get('name')}
Type: {tier.get('type')}
Description: {tier.get('description', 'No description')}
Active: {tier.get('active', False)}
Visibility: {tier.get('visibility', 'public')}
Monthly Price: {tier.get('monthly_price', 'N/A')} {tier.get('currency', 'usd').upper()}
Yearly Price: {tier.get('yearly_price', 'N/A')} {tier.get('currency', 'usd').upper()}
Currency: {tier.get('currency', 'usd').upper()}
Benefits:
- {benefits_text}
ID: {tier.get('id')}
"""
except Exception as e:
if ctx:
ctx.error(f"Failed to update tier: {str(e)}")
raise