SYSTEM TEST RESULTS
This page documents what happens when you point an MCP-compatible AI agent at a WordPress site running AgenticWP Pro. Every block below is a real tool invocation from an external Python MCP server (running on a developer machine in Slovenia) talking to this WordPress install over the REST API. Each test run is its own dated session — use the sticky nav on the left to jump between sessions and individual tests.
Session 1 — Initial test battery
check_mcp_reachability
First call any agent should make. Probes plugin /status + site root, returns latency + a hint if anything’s wrong.
{
"plugin": { "reachable": true, "plugin_version": "3.9.5", "latency_ms": 286.4 },
"root": { "reachable": true, "http_status": 200, "latency_ms": 438.3 },
"hint": null
}
get_wp_status
{
"wordpress_version": "6.9.4",
"php_version": "8.4.19",
"theme": "Astra 4.13.0",
"agenticwp_version": "3.9.5",
"edition": "pro",
"licensed": true,
"mcp_version": "3.9.5",
"version_parity": true
}
Options round-trip
Three calls: write, read, delete. Verifies protected-options blocklist doesn’t false-positive.
update_option("agenticwp_test_marker", { ... }) → { success: true }
get_option("agenticwp_test_marker") → { exists: true, value: {...} }
delete_option("agenticwp_test_marker") → { success: true }
Post meta round-trip
update_postmeta(43, "_agenticwp_test_marker", "test-write-then-delete") → ✓ get_postmeta(43, "_agenticwp_test_marker") → ✓ delete_postmeta(43, "_agenticwp_test_marker") → ✓
update_postmeta_multi
3 meta keys written across 2 posts in a single round-trip.
update_postmeta_multi([
{ post_id: 43, meta: { _agenticwp_test_a: "value-a", _agenticwp_test_b: "value-b" } },
{ post_id: 8, meta: { _agenticwp_test_a: "home-a" } }
])
→ { success: true, results: [
{ post_id: 43, updated_count: 2 },
{ post_id: 8, updated_count: 1 }
] }
list_elementor_pages
{
"pages": [
{ "id": 41, "title": "Build Log", "element_count": 0 },
{ "id": 8, "title": "Home", "element_count": 9 }
],
"total": 2
}
get_elementor_outline (max_depth=1)
Lightweight tree of Home page — IDs and types only. Saves ~100x context vs get_elementor_page.
{
"page_id": 8,
"element_count": 9,
"outline": [
{ "id": "hero0001", "elType": "container", "children": [...] },
{ "id": "prob0001", "elType": "container", "children": [...] },
{ "id": "feat0001", "elType": "container", "children": [...] },
{ "id": "flow0001", "elType": "container", "children": [...] },
{ "id": "tool0001", "elType": "container", "children": [...] },
{ "id": "pric0001", "elType": "container", "children": [...] },
{ "id": "faq00001", "elType": "container", "children": [...] },
{ "id": "cta0001", "elType": "container", "children": [
{ "id": "cta0002", "widgetType": "heading", "label": "...Correctly." },
{ "id": "cta0003", "widgetType": "button", "label": "Download AgenticWP" }
]}
],
"truncated_count": 24
}
get_elementor_element
get_elementor_element(page_id=8, element_id="cta0002")
→ {
"widgetType": "heading",
"settings": {
"title": "Let your AI agent manage WordPress. Correctly.",
"align": "center",
"typography_font_family": "Inter",
"typography_font_size": { "unit": "rem", "size": 2.4 }
}
}
get_elementor_components
11 component templates the plugin can build, with parameters each accepts.
["hero", "heading", "text", "image", "button", "video_bg", "products_grid", "icon_list", "category_cards", "cta_banner", "spacer"]
list_elementor_revisions
{
"page_id": 8,
"revision_count": 31,
"revisions": [
{ "revision_id": 71, "date": "2026-04-27 10:53:45", "element_count": 9 },
{ "revision_id": 9, "date": "2026-04-22 23:44:52", "element_count": 1 }
/* … 29 more */
]
}
patch_css + css_has_selector
patch_css(block: "testing_page_marker", rules: ".awp-testing-marker { display: none; }")
→ { before_bytes: 81220, after_bytes: 81418, delta: +198 }
css_has_selector(".awp-testing-marker")
→ { exists: true, count: 1 }
patch_css(block: "testing_page_marker", mode: "remove")
→ { before_bytes: 81418, after_bytes: 81220, delta: -198 }
seo_audit
{
"total_posts": 11,
"summary": {
"missing_title": 0, "missing_description": 0, "missing_focus_keyword": 0,
"missing_og_image": 0, "thin_content": 0, "noindex": 0
}
}
{
"locations": [
{ "location": "primary", "menu_name": "Primary", "item_count": 7 },
{ "location": "mobile_menu", "menu_name": "Primary", "item_count": 7 },
{ "location": "footer_menu", "menu_name": null, "item_count": 0 }
],
"all_menus": [
{ "id": 2, "name": "Primary", "count": 7 },
{ "id": 3, "name": "Footer Legal", "count": 5 }
]
}
flush_cache
{ "success": true,
"flushed": ["object_cache", "elementor_css", "astra_css", "rewrite_rules"] }
get_debug_log
{ "exists": false,
"message": "debug.log does not exist. Enable WP_DEBUG_LOG to start capturing errors." }
list_elementor_widgets — 500
The endpoint failed during this run. Investigation → root cause → fix applied. See Session 2 below for verification.
GET /elementor/widgets failed (500):
{ "code": "internal_server_error",
"message": "There has been a critical error on this website." }
$widget->init_controls() is a protected method on Elementor\Widget_Base in current Elementor versions. Calling it from outside the class throws a fatal Error. Fixed by switching to the public get_controls() API. See Session 2 for verification.Session 2 — list_elementor_widgets fix verification
What changed: the endpoint was rewritten in two phases. (1) Default mode flipped from heavy-introspection to names only — same data path that 500’d is now opt-in via ?mode=full. (2) The protected-method bug was fixed by switching from init_controls() + get_stack() to the public get_controls() API. ?mode=meta is the new middle ground returning {name, title, icon, categories}.
mode=names (new default)
{
"widgets": [
"accordion", "alert", "animated-headline", "audio", "author-box",
"button", "call-to-action", "countdown", "counter", "divider",
/* … 142 more */
],
"total": 152,
"mode": "names"
}
init_controls() walk — just array_keys() on the already-loaded registry. Ridiculously fast and never fatals.mode=meta
{
"widgets": [
{ "name": "button", "title": "Button", "icon": "eicon-button", "categories": ["basic"] },
{ "name": "heading", "title": "Heading", "icon": "eicon-t-letter", "categories": ["basic"] },
{ "name": "form", "title": "Form", "icon": "eicon-apps", "categories": ["general", "pro-elements"] }
/* … 149 more */
],
"total": 152,
"mode": "meta"
}
basic, general, pro-elements, link-in-bio, v4-elements, wordpress.mode=full — the original failing path, after fix
{
"widgets": [
{
"name": "button",
"title": "Button",
"icon": "eicon-button",
"categories": ["basic"],
"controls": {
"button_section": { "type": "section", "label": "Button", "default": null },
"text": { "type": "text", "label": "Text", "default": "Click here" },
"link": { "type": "url", "label": "Link", "default": {...} },
"size": { "type": "select", "label": "Size", "default": "sm" }
}
}
/* … 151 more widgets */
],
"total": 152,
"mode": "full",
"skipped": [],
"skipped_count": 0
}
mode=full only when you need controls; otherwise stick to meta or names.Session 3 — Free-tier battery (license deliberately disabled)
What this session proves: every free-tier endpoint works without a license. Every paid endpoint returns a structured 402 instead of leaking. License was disabled in WP Admin between Sessions 2 and 3, then re-enabled at the end so the page could be saved. Side-effect bonus: the SEO audit caught that this Testing page had no Rank Math meta, and update_postmeta_bulk set it in the same session.
check_mcp_reachability
{
"plugin": { "reachable": true, "plugin_version": "3.9.5", "latency_ms": 161.5 },
"root": { "reachable": true, "http_status": 200, "latency_ms": 672.1 },
"hint": null
}
get_wp_status — unlicensed
{
"agenticwp_version": "3.9.5",
"edition": "pro",
"licensed": false,
"version_parity": true,
"readonly_mode": false
}
licensed: false tells the MCP to expect 402s on paid endpoints.Paid endpoints — gating verified (402)
Two representative paid endpoints called with no license. Both return a structured 402, not a 5xx — the front-door auth layer rejects cleanly before any business logic runs.
list_elementor_pages()
→ GET /elementor/pages failed (402):
{ "code": "agenticwp_unlicensed",
"message": "This endpoint requires an AgenticWP license. Add one under Settings → AgenticWP." }
media_upload({ url: "https://example.com/test.png" })
→ POST /media/upload failed (402):
{ "code": "agenticwp_unlicensed",
"message": "This endpoint requires an AgenticWP license. Add one under Settings → AgenticWP." }
Options round-trip
update_option("agenticwp_test_session_3", { timestamp, purpose, license_state: "disabled" })
→ { success: true, updated: true }
get_option("agenticwp_test_session_3")
→ { exists: true, value: { ... } }
delete_option("agenticwp_test_session_3")
→ { success: true }
Post meta CRUD
update_postmeta(97, "_agenticwp_session3_marker", "free-tier-test-2026-04-27") → ✓ delete_postmeta(97, "_agenticwp_session3_marker") → ✓
Term meta CRUD
Same shape as post meta but for taxonomy terms (categories, tags, custom taxonomies). Useful for setting Rank Math SEO on category archive pages.
update_termmeta(1, "_agenticwp_session3_term_marker", "term-meta-test") → ✓
get_termmeta(1, "_agenticwp_session3_term_marker") → { exists: true, value: "term-meta-test" }
delete_termmeta(1, "_agenticwp_session3_term_marker") → ✓
update_postmeta_bulk — free-tier (single post, multi key)
Different from update_postmeta_multi (paid, many posts). Free tier writes N keys to ONE post atomically.
update_postmeta_bulk(97, {
_agenticwp_s3_a: "alpha",
_agenticwp_s3_b: "bravo",
_agenticwp_s3_c: "charlie"
})
→ { success: true, updated_count: 3 }
update_postmeta calls. Cleaned up after.SEO meta bulk-write — closed-loop fix
The SEO audit (test 10 below) found this page had no Rank Math meta. Same session used update_postmeta_bulk to fix it.
update_postmeta_bulk(97, {
rank_math_title: "Live Test Range — MCP Tool Battery Results | AgenticWP",
rank_math_description: "Live test results from running the AgenticWP MCP tool battery against this WordPress demo. Every block on the page is a real tool call. Multiple test sessions documented.",
rank_math_focus_keyword: "agenticwp test results"
})
→ { success: true, updated_count: 3 }
patch_css + css_has_selector
patch_css(block: "session3_marker", rules: ".awp-s3-marker { display: none; }")
→ { before_bytes: 85839, after_bytes: 86010, delta: +171 }
css_has_selector(".awp-s3-marker")
→ { exists: true, count: 1 }
patch_css(block: "session3_marker", mode: "remove")
→ { before_bytes: 86010, after_bytes: 85839, delta: -171 }
seo_audit — caught one real issue
{
"total_posts": 12,
"summary": {
"missing_title": 1,
"missing_description": 1,
"missing_focus_keyword": 1,
"missing_og_image": 1
},
"items[0]": {
"id": 97,
"title": "Testing",
"issues": ["missing_title", "missing_description", "missing_focus_keyword", "missing_og_image"]
}
}
update_postmeta_bulk in the same session.{
"locations": [
{ "location": "primary", "menu_name": "Primary", "item_count": 8 },
{ "location": "mobile_menu", "menu_name": "Primary", "item_count": 8 },
{ "location": "footer_menu", "menu_name": null, "item_count": 0 }
],
"all_menus": [
{ "id": 2, "name": "Primary", "count": 8 }
]
}
flush_cache
{ "success": true,
"flushed": ["object_cache", "elementor_css", "astra_css", "rewrite_rules"] }
get_debug_log
{ "exists": false,
"message": "debug.log does not exist. Enable WP_DEBUG_LOG to start capturing errors." }
find_pages by title
find_pages(title="Testing")
→ { count: 1, pages: [
{ id: 97, slug: "testing", link: ".../testing/", is_elementor: false }
] }
update_postmeta_bulk, update_elementor_page, or get_elementor_outline.wp_rest_call — generic REST passthrough
For any WordPress REST endpoint that doesn’t have a dedicated AgenticWP wrapper. Free-tier escape hatch.
wp_rest_call(method="GET", path="/users/me", query={ _fields: "id,name" })
→ { status: 200, data: { id: 1, name: "glava" } }
render_page — unauthenticated public fetch
Python-side fetch that returns the visitor’s-eye view of a page. No auth, no plugin endpoint — useful to verify what visitors actually see after a save.
render_page(url="https://demo-agenticwp.racunalnicar.eu/manual/")
→ {
"title": "Operations Manual — Install, Configure, Deploy | AgenticWP",
"size_bytes": 190551,
"stylesheets_count": 3,
"inline_style_blocks": 5,
"inline_css_total_bytes": 150033
}
Range clear · 3 sessions
✓ Session 1 (paid · 21:23): 17 tests · 16 passed · 1 found-and-fixed
✓ Session 2 (fix verify · 22:00): 3 tests · 3 passed
✓ Session 3 (free-tier · 22:30): 17 tests · 17 passed · 2 paid endpoints correctly returned 402
test data: all written + cleaned up across all 3 sessions
css patches: 3 round-trips, all reverted byte-perfect
real fixes: Testing page Rank Math SEO meta set via update_postmeta_bulk
# every block above came from a real network call. nothing on this page is mocked.
