mirror of
https://github.com/jlengrand/ghost-mcp.git
synced 2026-03-10 08:21:19 +00:00
✨ Support tier creating and updating
This commit is contained in:
@@ -90,6 +90,8 @@ GHOST_API_URL=your_ghost_api_url GHOST_STAFF_API_KEY=your_staff_api_key npx @mod
|
|||||||
### Tiers Management
|
### Tiers Management
|
||||||
- `list_tiers`: List all available membership tiers
|
- `list_tiers`: List all available membership tiers
|
||||||
- `read_tier`: Retrieve detailed information about a specific tier, including benefits and pricing
|
- `read_tier`: Retrieve detailed information about a specific tier, including benefits and pricing
|
||||||
|
- `create_tier`: Create a new membership tier with specified details
|
||||||
|
- `update_tier`: Update an existing tier with new information
|
||||||
|
|
||||||
### Offers Management
|
### Offers Management
|
||||||
- `list_offers`: List promotional offers with relevant details
|
- `list_offers`: List promotional offers with relevant details
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ def create_server() -> FastMCP:
|
|||||||
mcp.tool()(tools.read_member)
|
mcp.tool()(tools.read_member)
|
||||||
mcp.tool()(tools.list_tiers)
|
mcp.tool()(tools.list_tiers)
|
||||||
mcp.tool()(tools.read_tier)
|
mcp.tool()(tools.read_tier)
|
||||||
|
mcp.tool()(tools.create_tier)
|
||||||
|
mcp.tool()(tools.update_tier)
|
||||||
mcp.tool()(tools.list_offers)
|
mcp.tool()(tools.list_offers)
|
||||||
mcp.tool()(tools.read_offer)
|
mcp.tool()(tools.read_offer)
|
||||||
mcp.tool()(tools.list_newsletters)
|
mcp.tool()(tools.list_newsletters)
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ from .tools.members import (
|
|||||||
)
|
)
|
||||||
from .tools.tiers import (
|
from .tools.tiers import (
|
||||||
list_tiers,
|
list_tiers,
|
||||||
read_tier
|
read_tier,
|
||||||
|
create_tier,
|
||||||
|
update_tier
|
||||||
)
|
)
|
||||||
from .tools.offers import (
|
from .tools.offers import (
|
||||||
list_offers,
|
list_offers,
|
||||||
@@ -43,6 +45,8 @@ __all__ = [
|
|||||||
'read_member',
|
'read_member',
|
||||||
'list_tiers',
|
'list_tiers',
|
||||||
'read_tier',
|
'read_tier',
|
||||||
|
'create_tier',
|
||||||
|
'update_tier',
|
||||||
'list_offers',
|
'list_offers',
|
||||||
'read_offer',
|
'read_offer',
|
||||||
'list_newsletters',
|
'list_newsletters',
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
from .posts import search_posts_by_title, list_posts, read_post, create_post, update_post, delete_post
|
from .posts import search_posts_by_title, list_posts, read_post, create_post, update_post, delete_post
|
||||||
from .users import list_users, read_user
|
from .users import list_users, read_user
|
||||||
from .members import list_members, read_member
|
from .members import list_members, read_member
|
||||||
from .tiers import list_tiers, read_tier
|
from .tiers import list_tiers, read_tier, create_tier, update_tier
|
||||||
from .offers import list_offers, read_offer
|
from .offers import list_offers, read_offer
|
||||||
from .newsletters import list_newsletters, read_newsletter
|
from .newsletters import list_newsletters, read_newsletter
|
||||||
|
|
||||||
@@ -20,6 +20,8 @@ __all__ = [
|
|||||||
'read_member',
|
'read_member',
|
||||||
'list_tiers',
|
'list_tiers',
|
||||||
'read_tier',
|
'read_tier',
|
||||||
|
'create_tier',
|
||||||
|
'update_tier',
|
||||||
'list_offers',
|
'list_offers',
|
||||||
'read_offer',
|
'read_offer',
|
||||||
'list_newsletters',
|
'list_newsletters',
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
"""Tier-related MCP tools for Ghost API."""
|
"""Tier-related MCP tools for Ghost API."""
|
||||||
|
|
||||||
import json
|
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 mcp.server.fastmcp import Context
|
||||||
|
|
||||||
from ..api import make_ghost_request, get_auth_headers
|
from ..api import make_ghost_request, get_auth_headers
|
||||||
@@ -125,3 +130,201 @@ Benefits:
|
|||||||
if ctx:
|
if ctx:
|
||||||
ctx.error(f"Failed to read tier: {str(e)}")
|
ctx.error(f"Failed to read tier: {str(e)}")
|
||||||
return 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
|
||||||
Reference in New Issue
Block a user