Install
$ agentstack add skill-mralaminahamed-wp-dev-skills-wp-admin-browser ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
WordPress Admin via Browser (Chrome DevTools MCP)
> Model note: Primarily MCP tool calls — navigate, fill, click. haiku works for simple CRUD flows. Complex UI sequences (multi-step forms, dynamic AJAX state) use sonnet to handle unexpected DOM states.
When to use
- "Log in to the WordPress admin", "navigate to Settings > General", "create a test user".
- "Submit a form in WP admin", "upload a file via the media library", "save plugin settings".
- "Create a temporary admin account for testing instead of using the main admin user".
- "Verify a feature works end-to-end through the browser UI".
Not for: Headless automated testing without a real browser — use wp-plugin-testing. PHP code changes and plugin logic that don't require browser interaction.
Core Rules — Non-Negotiable
- Never touch the main admin user. Always create a temporary admin for testing.
- All data operations go through the browser UI. No WP-CLI, no direct DB, no REST API calls to mutate data — use WordPress forms.
- Navigate via menus, not hardcoded URLs. Click the menu item; don't jump straight to
/wp-admin/users.php?action=.... - Use
fill_form+ click for all inputs. Never skip the form and post directly.
Step 1 — Log In
// POST to wp-login.php via fetch (fastest, no UI needed for login itself)
async () => {
const res = await fetch('/wp-login.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
log: 'YOUR_USER',
pwd: 'YOUR_PASS',
'wp-submit': 'Log In',
redirect_to: '/wp-admin/',
testcookie: '1',
}),
credentials: 'include',
redirect: 'follow',
});
return { ok: res.ok, url: res.url };
}
Login via fetch is acceptable because it's a pure auth step — no data mutation.
Step 2 — Create a Temporary Admin User
Always create a temp user before any testing that requires admin actions.
Navigate to Users → Add New via menu clicks, not direct URL.
Admin menu → Users → Add New
Fill the form using fill_form:
| Field | Value | |-------|-------| | Username | tmp_admin_ | | Email | tmp+@example.com | | First Name | Temp | | Last Name | Admin | | Role | Administrator | | Password | strong generated password | | Send notification | unchecked |
After testing: delete the temp user via Users list → hover → Delete.
Step 3 — Get a REST Nonce (for read-only API calls only)
async () => {
return fetch('/wp-admin/admin-ajax.php?action=rest-nonce', {
credentials: 'include',
}).then(r => r.text());
}
Use nonce only for GET requests to read data. All mutations go through WP forms.
Browser Interaction Rules
Navigation
✅ Click: Admin menu → Submenu item
❌ Never: navigate_page to hardcoded /wp-admin/edit.php?post_type=...
Forms
✅ fill_form on visible fields → click Submit button
❌ Never: fetch POST directly to admin-post.php / admin-ajax.php for data changes
❌ Never: wp eval or wp post create via CLI for browser-visible operations
Menus
Always wait for the page to load after each menu click before interacting with the next element.
Data Operation Checklist
| Operation | Method | |-----------|--------| | Create post/page | Posts → Add New → fill form → Publish | | Update user | Users → find user → Edit → fill form → Update | | Delete item | List view → hover row → Delete (confirm dialog) | | Change setting | Settings menu → fill field → Save Changes | | Install plugin | Plugins → Add New → Search → Install → Activate |
Common Mistakes
| Mistake | Fix | |---------|-----| | Using main admin for destructive tests | Create temp admin first | | Navigating directly to ?action=delete&id=X | Use list UI → Delete link | | Using wp user create CLI to seed browser session | Use Add New User form | | Skipping fill_form and posting via fetch | Always fill the visible form | | Leaving temp admin after testing | Delete via Users list when done |
JS State Verification
Use evaluate_script to inspect JavaScript state without touching the UI.
Check scripts loaded + globals present
() => ({
// Verify a localized WP global is available
myPlugin: typeof window.MY_PLUGIN,
keys: Object.keys(window.MY_PLUGIN || {}),
// Verify a library bundle loaded
driverLoaded: !!window.driver?.js?.driver,
// Check current URL
url: location.href,
hash: location.hash,
})
If typeof window.MY_PLUGIN === 'undefined' the script may not be enqueued for this screen, or the page is in maintenance mode (document.title === 'Maintenance').
SPA pages — always wait for React to render
// Wrong: query DOM immediately after navigate
() => document.querySelector('.spa-element') // returns null
// Right: wrap in setTimeout
() => new Promise(r => setTimeout(() => r(
!!document.querySelector('.spa-element')
), 2000))
Verify CSS selectors before committing
() => {
const selectors = [
'#my-id',
'.class-one.class-two',
'.border-\\[\\#F0EDFB\\]', // Tailwind escaped
];
return selectors.map(s => ({
selector: s,
found: !!document.querySelector(s),
count: document.querySelectorAll(s).length,
}));
}
Always test on the page where the selector is expected to exist. A selector for the orders page returns false on the dashboard — that's not a bug.
localStorage testing
// Clear feature flags / completion state before testing
() => {
Object.keys(localStorage)
.filter(k => k.startsWith('myplugin_'))
.forEach(k => localStorage.removeItem(k));
return 'cleared';
}
// Read a specific key after an action
() => localStorage.getItem('myplugin_feature_completed') // "true" or null
Detect maintenance mode / redirect loop
If navigate_page lands on a blank page or unexpected content:
() => ({ title: document.title, url: location.href })
// "Maintenance" title = WP in maintenance mode (.maintenance file or plugin)
// Redirected to /wp-login.php = session expired — re-login via fetch
Re-login when session expires
async () => {
const res = await fetch('/wp-login.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
log: 'admin', pwd: 'admin',
'wp-submit': 'Log In',
redirect_to: '/wp-admin/',
testcookie: '1',
}),
credentials: 'include',
redirect: 'follow',
});
return { ok: res.ok, url: res.url };
}
References
- [Chrome DevTools MCP tools](references/chrome-devtools-tools.md) — full tool params, interaction sequence template, priority rules
- [WordPress admin navigation](references/wp-admin-navigation.md) — menu paths, form submit patterns, success strings
- [Browser debug patterns](references/browser-debug-patterns.md) — console error capture, network request inspection, evaluate_script debug snippets, screenshot strategy, PHP error detection
Cleanup
After every test session:
- Log in as main admin (or existing admin).
- Users → find all
tmp_admin_*users → bulk delete. - Do not reassign their content (temp users have no content).
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: mralaminahamed
- Source: mralaminahamed/wp-dev-skills
- License: MIT
- Homepage: https://github.com/mralaminahamed/wp-dev-skills/blob/trunk/README.md
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.