Drupal CMS AI Agent: 9 Actions, Retry Logic, and Production Error Recovery
The original POC had three actions -- create a node, read a node, list nodes. Enough to prove the concept, not enough to be useful. A real content operations agent needs to modify, delete, query users, browse taxonomies, and handle media. It also needs to survive flaky networks without crashing.
What Changed
The agent now supports 9 actions -- the original 3 plus 5 new ones:
updateNodesends a PATCH request to update existing content.deleteNodesends a DELETE request to remove content.listUsersqueries the Drupal JSON:API users endpoint.listTaxonomyTermsqueries taxonomy term collections for vocabulary browsing.uploadMediahandles file uploads through Drupal's media system.
Each action follows the same pattern: validate inputs, build the request, call the Drupal client, return structured results. The OpenAI planner sees all 9 actions as available tools and selects the right one based on the user's intent.
Tech Stack
| Component | Technology | Why |
|---|---|---|
| Planner | OpenAI GPT | Tool selection from natural language intent |
| Runtime | Node.js | Async HTTP, JSON:API client |
| API | Drupal JSON:API | Standard RESTful interface for all content operations |
| Retry | Exponential backoff | Survives deployment windows and cache rebuilds |
| Testing | Jest (24 tests, 4 files) | Mocked Drupal responses, full pipeline coverage |
| License | MIT | Open for adoption |
Error Recovery
Exponential backoff with retry is now built into the Drupal client. The agent retries automatically on HTTP status codes 408, 429, 500, 502, 503, 504 -- the standard set of transient server errors. Network-level failures like DNS resolution errors and connection resets also trigger retries. Each retry doubles the wait time. If all retries exhaust, the agent surfaces a clear error instead of hanging or throwing an unhandled exception.
Drupal sites behind reverse proxies and CDNs return 502 and 503 during deployments and cache rebuilds. An agent that gives up on the first transient failure is unusable in production. Retry logic belongs in the HTTP client layer, not scattered across individual actions.
Every action goes through the same Drupal client, so transient error handling is defined once. When you add action number 10 or 15, you get retry behavior for free. Put resilience at the transport layer and keep the business logic naive about network conditions.
- All 9 Actions
- Retry Logic
const actions = {
// Original 3
createNode: require('./createNode'),
readNode: require('./readNode'),
listNodes: require('./listNodes'),
// New 5
updateNode: require('./updateNode'),
deleteNode: require('./deleteNode'),
listUsers: require('./listUsers'),
listTaxonomyTerms: require('./listTaxonomyTerms'),
uploadMedia: require('./uploadMedia'),
};
async function requestWithRetry(config, retries = 3) {
for (let attempt = 0; attempt <= retries; attempt++) {
try {
const response = await axios(config);
return response;
} catch (error) {
const status = error.response?.status;
const retryable = [408, 429, 500, 502, 503, 504].includes(status)
|| error.code === 'ECONNRESET'
|| error.code === 'ENOTFOUND';
if (!retryable || attempt === retries) throw error;
const delay = Math.pow(2, attempt) * 1000;
await new Promise(r => setTimeout(r, delay));
}
}
}
Test Coverage
24 tests across 4 test files: drupalClient.test.js, actions.test.js, agent.test.js, and openAiPlanner.test.js. The tests validate HTTP method selection per action, retry behavior on transient errors, planner tool registration, and end-to-end agent orchestration with mocked Drupal responses. Every new action has dedicated assertions.
Test file breakdown
| Test file | Coverage area |
|---|---|
drupalClient.test.js | Retry logic, transient error handling, HTTP method selection |
actions.test.js | All 9 actions: input validation, request building |
agent.test.js | End-to-end orchestration with mocked Drupal |
openAiPlanner.test.js | Tool registration, intent-to-action mapping |
Project Hygiene
The repo now includes a .env.example documenting every required environment variable, a MIT LICENSE, and a comprehensive README with an ASCII architecture diagram showing the flow from user prompt through the OpenAI planner to Drupal API actions. The README includes 7 usage examples covering each action type and common multi-step workflows.
Why this matters for Drupal and WordPress
Drupal's JSON:API is the natural transport for agent-driven content operations, and the retry logic here directly addresses the 502/503 errors that Drupal sites behind Varnish or CDN layers produce during deployments. WordPress developers can apply the same agent architecture against the WP REST API -- the action pattern (create, read, update, delete, list) maps one-to-one, and the exponential backoff strategy is equally critical for WordPress sites on managed hosting where brief downtime during updates is common.
Technical Takeaway
Retry logic belongs in the HTTP client, not in the action layer. Every action goes through the same Drupal client, so transient error handling is defined once. The actions stay clean -- they describe what to do, not how to survive failure. When you add action number 10 or 15, you get retry behavior for free. Put resilience at the transport layer and keep the business logic naive about network conditions.
References
Looking for an Architect who doesn't just write code, but builds the AI systems that multiply your team's output? View my enterprise CMS case studies at victorjimenezdev.github.io or connect with me on LinkedIn.
