Agent 3: Creating enrolment agent (Part 1)
Today we built the building blocks of a system where Ghost member events feed into genuine intelligence, with every signup, cancellation or upgrade reasoned about in context rather than pattern-matched against a rules table.
Building an Event-Driven AI Agent System on Ghost CMS
Modern SaaS platforms generate a continuous stream of meaningful user signals like signups, cancellations and payment events, but most treat these as passive data points rather than triggers for intelligent action. The naive solution is webhooks-to-code: Ghost fires a webhook, a function catches it, runs some logic and calls an API. This works at small scale but breaks down quickly. Business logic becomes brittle, adding behaviour means new deployments, and there's no way to involve an AI model as a genuine decision-maker rather than a glorified if-statement.
What we considered
We evaluated several options before settling on our architecture. Kafka offered exceptional throughput and replay capability but is operationally heavy, and the complexity buys you little when your bottleneck is model inference rather than ingestion speed. Redis Streams was appealing for its simplicity but its memory-first model introduced durability tradeoffs we weren't comfortable with for business-critical events. RabbitMQ was a genuine contender but meant owning broker infrastructure. EventBridge had strong routing capabilities but is designed for push-based fan-out to Lambda targets, which sits awkwardly against an AI agent that needs to pull work on its own schedule.
We chose SQS for pragmatic reasons: fully managed, durably persistent and natively pull-based. Visibility timeouts handle retries automatically when a model call times out. Dead-letter queues catch repeated failures without custom logic. Operational overhead is essentially zero, which is the right tradeoff when the complex work happens in the agent layer, not the messaging layer.
What we built
When a user event happens in Ghost, it is captured by a webhook service, normalised and pushed into SQS. A dedicated MCP server exposes the queue as tools including fetching the next event, acknowledging completion and inspecting the dead-letter queue, allowing Claude, running in Cowork, to interact as an agent rather than a passive API caller. Claude reads events, reasons about what should happen next and outputs structured actions like sending a welcome sequence, flagging a churn risk or updating a CRM record. An action worker then executes those decisions against downstream systems.
Each layer has a clear responsibility. Ghost generates events. SQS guarantees durability. MCP acts as the interface through which the agent perceives and interacts with the system. And Claude becomes the decision-making layer: not a text generator responding to prompts, but an agent with a queue of work, a set of tools and the judgment to act.
Prompt used to create a MCP server using Claude Code
- Event Ingestion (Ghost → SQS)
Build a Node.js service that receives Ghost CMS webhook events (member.added), normalises them into this format, and pushes them into AWS SQS:
{
"id": "evt_123",
"type": "member.created",
"timestamp": "...",
"data": {
"email": "user@example.com",
"name": "John Doe"
}
}
Requirements:
Validate webhook payload
Transform to standard event format
Send to AWS SQS queue
Ensure retry-safe ingestion
2. MCP Server (SQS → Claude Tool Layer)
Build an MCP-compatible server that exposes SQS as tools for Claude Cowork.
Implement these tools:
get_next_event → reads and locks next SQS message
ack_event → deletes processed message from SQS
publish_result → stores agent output actions
retry_event → re-queues failed events
Requirements:
Stateless server
All state stored in SQS
Must prevent duplicate processing (visibility timeout)
Must return JSON only
3. Claude Cowork Agent (Event Loop)
Create an agent prompt for Claude Cowork:
You are an event-driven automation agent.
Loop:
1. Call get_next_event
2. If no event → wait and retry
3. If event exists → process it
4. Decide actions based on event
5. Return structured JSON only
6. Call publish_result
7. Call ack_event
Rules:
- Always output valid JSON
- Never hallucinate external data
- Treat each event independently
Agent output example:
{
"segment": "new_user",
"actions": [
{
"type": "send_email",
"template": "welcome_v1"
},
{
"type": "notify_slack",
"channel": "growth"
}
]
}
Production Safety Reminder
Before merging any AI-generated or agent-produced code to your production branch, always review and manually approve every change yourself. Do not rely solely on automated tests or the agent's own output — read the diff, understand what it does, and take personal ownership of it.
- Run the code in a staging or local environment first
- Check for unintended side effects, data mutations, or scope creep
- Ensure secrets, credentials, and API keys are never committed
- You are the last line of defence — own the merge, own the outcome
To deploy, you need to:
- Set env vars on
ghost-ingestionandmcp-server(SQS_EVENTS_QUEUE_URL,SQS_RESULTS_QUEUE_URL,GHOST_WEBHOOK_SECRET) - Install ghost-ingestion deps and run it somewhere (
npm install && npm start) - Register the webhook in Ghost admin → Settings → Integrations → Custom → add webhook for
member.addedpointing at yourghost-ingestionURL - Point Claude Cowork at the
/eventsMCP endpoint with the agent prompt from agent-prompt.md
Create two SQS queues (one-time):
aws sqs create-queue --queue-name ghost-events --profile oxcomply-dev --region eu-west-1
aws sqs create-queue --queue-name ghost-results --profile oxcomply-dev --region eu-west-1
Refresh AWS session:
aws sso login --profile oxcomply-dev
Today we built the building blocks of a system where Ghost member events feed into genuine intelligence, with every signup, cancellation or upgrade reasoned about in context rather than pattern-matched against a rules table. SQS handles durability, MCP provides the interface, and Claude will make smart context-aware decisions at the moment they matter most.