Support newsletter creating and updating

This commit is contained in:
Fanyang Meng
2025-02-11 22:40:57 -05:00
parent 146c3ffcfd
commit b44165a043
5 changed files with 214 additions and 4 deletions

View File

@@ -100,6 +100,8 @@ GHOST_API_URL=your_ghost_api_url GHOST_STAFF_API_KEY=your_staff_api_key npx @mod
### Newsletters Management ### Newsletters Management
- `list_newsletters`: List all newsletters associated with the blog - `list_newsletters`: List all newsletters associated with the blog
- `read_newsletter`: Retrieve detailed settings and information for a specific newsletter - `read_newsletter`: Retrieve detailed settings and information for a specific newsletter
- `create_newsletter`: Create a new newsletter with specified details
- `update_newsletter`: Update an existing newsletter with new information
## Available Resources ## Available Resources

View File

@@ -59,6 +59,8 @@ def create_server() -> FastMCP:
mcp.tool()(tools.read_offer) mcp.tool()(tools.read_offer)
mcp.tool()(tools.list_newsletters) mcp.tool()(tools.list_newsletters)
mcp.tool()(tools.read_newsletter) mcp.tool()(tools.read_newsletter)
mcp.tool()(tools.create_newsletter)
mcp.tool()(tools.update_newsletter)
# Register prompts # Register prompts
@mcp.prompt() @mcp.prompt()

View File

@@ -29,7 +29,9 @@ from .tools.offers import (
) )
from .tools.newsletters import ( from .tools.newsletters import (
list_newsletters, list_newsletters,
read_newsletter read_newsletter,
create_newsletter,
update_newsletter
) )
__all__ = [ __all__ = [
@@ -50,5 +52,7 @@ __all__ = [
'list_offers', 'list_offers',
'read_offer', 'read_offer',
'list_newsletters', 'list_newsletters',
'read_newsletter' 'read_newsletter',
'create_newsletter',
'update_newsletter'
] ]

View File

@@ -5,7 +5,7 @@ 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, create_tier, update_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, create_newsletter, update_newsletter
__all__ = [ __all__ = [
'search_posts_by_title', 'search_posts_by_title',
@@ -25,5 +25,7 @@ __all__ = [
'list_offers', 'list_offers',
'read_offer', 'read_offer',
'list_newsletters', 'list_newsletters',
'read_newsletter' 'read_newsletter',
'create_newsletter',
'update_newsletter'
] ]

View File

@@ -126,3 +126,203 @@ Updated: {newsletter.get('updated_at', 'Unknown')}
if ctx: if ctx:
ctx.error(f"Failed to read newsletter: {str(e)}") ctx.error(f"Failed to read newsletter: {str(e)}")
return str(e) return str(e)
async def create_newsletter(
name: str,
description: str = None,
status: str = "active",
subscribe_on_signup: bool = True,
opt_in_existing: bool = False,
sender_reply_to: str = "newsletter",
show_header_icon: bool = True,
show_header_title: bool = True,
show_header_name: bool = True,
show_feature_image: bool = True,
title_font_category: str = "sans_serif",
title_alignment: str = "center",
body_font_category: str = "sans_serif",
show_badge: bool = True,
ctx: Context = None
) -> str:
"""Create a new newsletter.
Args:
name: Name of the newsletter (required)
description: Newsletter description
status: Newsletter status ("active" or "archived")
subscribe_on_signup: Whether to subscribe new members automatically
opt_in_existing: Whether to subscribe existing members
sender_reply_to: Reply-to setting ("newsletter" or "support")
show_header_icon: Whether to show header icon
show_header_title: Whether to show header title
show_header_name: Whether to show header name
show_feature_image: Whether to show feature image
title_font_category: Font category for titles
title_alignment: Title alignment
body_font_category: Font category for body text
show_badge: Whether to show badge
ctx: Optional context for logging
Returns:
Formatted string containing the created newsletter details
"""
if ctx:
ctx.info(f"Creating new newsletter: {name}")
try:
if ctx:
ctx.debug("Getting auth headers")
headers = await get_auth_headers(STAFF_API_KEY)
newsletter_data = {
"newsletters": [{
"name": name,
"description": description,
"status": status,
"subscribe_on_signup": subscribe_on_signup,
"sender_reply_to": sender_reply_to,
"show_header_icon": show_header_icon,
"show_header_title": show_header_title,
"show_header_name": show_header_name,
"show_feature_image": show_feature_image,
"title_font_category": title_font_category,
"title_alignment": title_alignment,
"body_font_category": body_font_category,
"show_badge": show_badge
}]
}
if ctx:
ctx.debug("Making API request to create newsletter")
endpoint = f"newsletters/?opt_in_existing={'true' if opt_in_existing else 'false'}"
data = await make_ghost_request(
endpoint,
headers,
ctx,
http_method="POST",
json_data=newsletter_data
)
if ctx:
ctx.debug("Processing create newsletter response")
newsletter = data["newsletters"][0]
return f"""
Newsletter created successfully!
Name: {newsletter.get('name')}
Description: {newsletter.get('description', 'No description')}
Status: {newsletter.get('status')}
ID: {newsletter.get('id')}
"""
except GhostError as e:
if ctx:
ctx.error(f"Failed to create newsletter: {str(e)}")
return str(e)
async def update_newsletter(
newsletter_id: str,
name: str = None,
description: str = None,
sender_name: str = None,
sender_email: str = None,
sender_reply_to: str = None,
status: str = None,
subscribe_on_signup: bool = None,
sort_order: int = None,
header_image: str = None,
show_header_icon: bool = None,
show_header_title: bool = None,
show_header_name: bool = None,
title_font_category: str = None,
title_alignment: str = None,
show_feature_image: bool = None,
body_font_category: str = None,
footer_content: str = None,
show_badge: bool = None,
ctx: Context = None
) -> str:
"""Update an existing newsletter.
Args:
newsletter_id: ID of the newsletter to update (required)
name: New newsletter name
description: New newsletter description
sender_name: Name shown in email clients
sender_email: Email address newsletters are sent from
sender_reply_to: Reply-to setting ("newsletter" or "support")
status: Newsletter status ("active" or "archived")
subscribe_on_signup: Whether to subscribe new members automatically
sort_order: Order in lists
header_image: URL of header image
show_header_icon: Whether to show header icon
show_header_title: Whether to show header title
show_header_name: Whether to show header name
title_font_category: Font category for titles
title_alignment: Title alignment
show_feature_image: Whether to show feature image
body_font_category: Font category for body text
footer_content: Custom footer content
show_badge: Whether to show badge
ctx: Optional context for logging
Returns:
Formatted string containing the updated newsletter details
"""
if ctx:
ctx.info(f"Updating newsletter with ID: {newsletter_id}")
try:
if ctx:
ctx.debug("Getting auth headers")
headers = await get_auth_headers(STAFF_API_KEY)
# Build update data with only provided fields
update_data = {"newsletters": [{"id": newsletter_id}]}
# Add non-None values to the update data
fields = locals()
for field in [
"name", "description", "sender_name", "sender_email",
"sender_reply_to", "status", "subscribe_on_signup",
"sort_order", "header_image", "show_header_icon",
"show_header_title", "show_header_name", "title_font_category",
"title_alignment", "show_feature_image", "body_font_category",
"footer_content", "show_badge"
]:
if fields[field] is not None:
update_data["newsletters"][0][field] = fields[field]
if ctx:
ctx.debug(f"Making API request to update newsletter {newsletter_id}")
data = await make_ghost_request(
f"newsletters/{newsletter_id}/",
headers,
ctx,
http_method="PUT",
json_data=update_data
)
if ctx:
ctx.debug("Processing update newsletter response")
newsletter = data["newsletters"][0]
return f"""
Newsletter updated successfully!
Name: {newsletter.get('name')}
Description: {newsletter.get('description', 'No description')}
Status: {newsletter.get('status')}
Sender Name: {newsletter.get('sender_name', 'Not set')}
Sender Email: {newsletter.get('sender_email', 'Not set')}
Sort Order: {newsletter.get('sort_order', 0)}
ID: {newsletter.get('id')}
"""
except GhostError as e:
if ctx:
ctx.error(f"Failed to update newsletter: {str(e)}")
return str(e)