Agent 3: Creating enrolment agent (Part 2)
Creating enrolment agent - Part 2
AI-Run Business · Series: Enrolment Agent · Part 2 of 3
What we're building
In Part 1 we built the MCP server and drafted the Claude coworker, which is the foundation that lets Claude connect to external services and act on member events. What we don't yet have is the execution layer: the non-intelligent workers that carry out provisioning actions reliably when Claude tells them to.
Before we can put the Claude enrolment agent to work, those workers need to exist. A worker has no opinion about what to do or when. It receives an explicit instruction, executes it against an API, and returns a structured result. That's all it does. The intelligence stays with Claude and the workers just need to be fast, reliable, and predictable.
In this part we build the provisioning worker, then show how the Claude enrolment agent directs it. Claude provides the reasoning. The worker provides the execution.
The provisioning worker prompt: the full instructions used to build the worker are published in our Prompt Library.
📄 Prompt Library: Creating the Provisioning Worker
Why intelligence and execution are kept separate
A common instinct when building AI systems is to give the AI model direct access to APIs and let it figure everything out. That works fine for simple tasks. It breaks down when reliability, auditability, and safety matter, which they do the moment real accounts and credentials are involved.
We split the system into two layers deliberately:
| Property | Claude enrolment agent | Provisioning worker |
|---|---|---|
| Has intelligence? | Yes. Reasons about context, chooses actions, handles edge cases | No. Receives an explicit instruction and executes it |
| Makes decisions? | Yes. Decides what needs provisioning and in what order | No. The decision has already been made upstream |
| Touches APIs directly? | No. Calls the worker via MCP tools only | Yes. Owns all direct API calls to Ghost and InAgentic |
| Logged and auditable? | Yes. Publishes a structured result for every event | Yes. Appends to an append-only log for every action |
| Replaceable independently? | Yes. Swap the model or prompt without touching the worker | Yes. Change the API implementation without touching Claude |
The worker is a tool. Claude is the person deciding which tool to pick up, when, and why.
How the system fits together
The event queue gives Claude the raw member data. Claude reasons about it, issues MCP tool calls to the worker in order, collects the results, and publishes a final structured outcome. The event is only acknowledged (marked as done) once that outcome is published successfully.
How Claude instructs the worker
The key insight is that Claude doesn't call Ghost or InAgentic directly. It calls the worker's MCP tools: predefined, validated functions that each do exactly one thing. Claude's job is to choose which tools to call, in what order, with the right arguments.
This is what it looks like in practice when a new consumer member signs up:
// Event received: member.created for alice@gmail.com // Domain: gmail.com → not corporate // Plan: free → standard consumer onboarding // Actions needed: grant Ghost access, then create MCP API key Step 1 → call grant_ghost_access({ "email": "alice@gmail.com" }) Step 2 → if Step 1 succeeds, call create_mcp_api_key({ "email": "alice@gmail.com" }) Step 3 → collect both results, publish structured outcome Step 4 → acknowledge the event to close the queue entry
Order matters. Claude is instructed to grant Ghost access before creating an MCP API key. If access provisioning fails, the API key step is skipped. There is no point creating credentials for an account that does not have access yet. This sequencing logic lives in Claude's system prompt, not in the worker.
For a corporate member, identified by a business email domain, Claude adds a third step: posting a Slack notification to the growth channel so a human can follow up. The worker doesn't know it's a corporate signup. Claude does. Claude tells the worker what to do.
What the worker sends back
Every tool call returns a structured JSON response. Claude reads these to decide whether to continue, skip a step, or flag a failure. Here's what a successful create_mcp_api_key response looks like:
{
"status": "success",
"actions": [
{
"action": "create_mcp_api_key",
"target": "alice@gmail.com",
"result": "success",
"detail": "API key created with full MCP scopes",
"credentials": {
"api_key": "sk-ina-xxxxxxxxxxxxxxxxxxxx",
"scopes": ["email", "articles", "mcp_full"],
"created_at": "2026-06-08T14:30:00Z"
}
}
],
"summary": "MCP API key created successfully for alice@gmail.com"
}Claude receives this, extracts the API key from the credentials field, and includes it in the final result it publishes back to the event queue so downstream systems (like a welcome email tool) can pass the key on to the new member.
The key is only visible once. The worker returns the raw API key value exactly once: in this response. It never stores the key value, only metadata. Claude passes it through to the published result, after which it travels to the welcome email and is gone from the system. This is by design.
What happens when something fails
Claude's system prompt defines explicit failure behaviour. It doesn't guess. If the worker returns a failure, Claude follows a defined path:
Worker returns failure on first attempt
Claude calls the same tool again once, after a short pause. This handles transient network or API errors without any special retry logic needing to exist in Claude itself. The worker also has its own internal retry, so there are two layers of resilience.
Retry also fails
Claude publishes a partial or failure result to the event queue, including the specific step that failed and a retry_suggested: true flag. The event is still acknowledged and won't be re-queued automatically, but the failure is visible to any monitoring system watching the result queue.
Step 1 fails, Step 2 is skipped
If Ghost access provisioning fails, Claude does not attempt to create an MCP API key. Creating credentials for an account without access is pointless and a security risk. Claude knows this because its system prompt says so explicitly. The worker doesn't need to know.
publish_result itself fails
Claude retries the publish once. If that also fails, it still acknowledges the event so the queue doesn't block, and logs the publish failure as a distinct error. An unpublished result is recoverable; a blocked queue is not.
Claude decides, the worker executes, the queue moves on. Every path through the system, whether success, partial failure, or full failure, ends with the event acknowledged and a result published. Nothing gets silently dropped.
How Claude knows what to do
Everything described above, including the sequencing, the corporate detection logic, the failure handling, and the output format, is defined in Claude's system prompt. Claude doesn't invent rules. It follows the ones we wrote.
The system prompt tells Claude:
- Which MCP tools are available and what each one does
- What a
member.createdevent looks like and which fields to read - How to identify a corporate member from an email domain
- The exact sequence of actions for each member type
- What to do when a tool call succeeds, fails, or returns a partial result
- The exact JSON structure to use when publishing a result
- When to acknowledge an event and when to retry
The system prompt is the specification. Claude is the runtime that executes it intelligently, handling variation, ambiguity, and unexpected inputs without needing a new code path for every edge case.
📄 Read the full system prompt: the complete instructions given to the Claude enrolment agent are published in our Prompt Library.
Prompt Library: Provisioning Worker →
The enrolment agent prompt will be published in the next instalment of this series.
What's next: Part 3
We now have two things: a worker that executes provisioning actions reliably, and an agent that reasons about member events and directs the worker. The final part of this series puts them into production by connecting the event queue, running the agent loop, and wiring up monitoring so we can see what's happening in real time.
Part 3 will cover: deploying the agent loop, testing with live Ghost member events, reading the result queue, and what a full end-to-end enrolment looks like from signup to provisioned account in under 30 seconds.
Part of the AI-Run Business series. InAgentic · June 2026
Today Claude Code got stuck while building a provisioning worker — and instead of waiting, I reset and guided it. When this happens, start a new chat with a clear, minimal goal and strict output rules.
Break big tasks into small steps and request one step at a time. Force deterministic output by asking for only code with no explanations or placeholders.
If it stalls, prompt it to summarise progress, identify blockers, and continue.
Reduce context by removing unnecessary files or past messages. Use a spec-first approach: ask for a plan, then implement step by step. Add checkpoints to confirm each part works before moving on.
If needed, change its role to a strict engineer or debugger. You stay in control — you are orchestrating, not delegating blindly. Clear inputs produce reliable outputs.