The

The meta.mcp.public Requirement: Why Your Tools Aren't Showing Up

⏱ 12 min

If there’s one issue that generates more confused bug reports than everything else in this migration combined, it’s this one: an ability registers without error, shows up in wp_get_abilities(), and then simply never appears to a connected AI agent. The cause is almost always the same single missing field, and this lesson exists to make sure you never lose an afternoon to it.

What you'll learn in this lesson
What meta.mcp.public actually is
A boolean inside an ability's meta argument, unrelated to whether the ability is registered at all.
Why migrated code is especially prone to missing it
The archived package didn't require it either, so old code you're migrating may never have set it.
A fast diagnostic script
One wp eval command that tells you exactly what the MCP Adapter currently exposes.
The full checklist
Every place this can silently fail, not just the obvious one.
Prerequisites

An ability already registered (Lesson 2 covers migrating registration itself). The MCP Adapter plugin installed and active, since the field has no effect without it.

Step 1: Confirm what the field actually does

meta.mcp.public is not part of registering an ability. It’s a value inside the meta argument you pass to wp_register_ability(), and it is read specifically by the MCP Adapter plugin to decide whether to include that ability in the tool list it serves to external MCP clients:

// File: my-plugin.php
add_action( 'wp_abilities_api_init', function () {
	wp_register_ability(
		'my-plugin/create-draft-post',
		array(
			'label'               => __( 'Create draft post', 'my-plugin' ),
			'description'         => __( 'Creates a new draft post from a title and body.', 'my-plugin' ),
			'input_schema'        => array(
				'type'       => 'object',
				'properties' => array(
					'title' => array( 'type' => 'string' ),
					'body'  => array( 'type' => 'string' ),
				),
				'required'   => array( 'title' ),
			),
			'permission_callback' => function () {
				return current_user_can( 'edit_posts' );
			},
			'execute_callback'    => 'my_plugin_create_draft_post',
			'meta'                => array(
				'mcp' => array( 'public' => true ),
			),
		)
	);
} );

Without that meta block, or with public set to false, the ability still fully exists, your own code can still call it with wp_get_ability( 'my-plugin/create-draft-post' )->execute(), but the MCP Adapter will not hand it to any connected agent.

Step 2: Why migrated abilities are especially likely to miss this

The archived, pre-core wordpress/abilities-api package never depended on the MCP Adapter existing at all, it only handled registration. If your original code was written and tested purely against that package, before you’d installed the MCP Adapter, there was never a moment where a missing meta.mcp.public would have caused a visible failure. It’s an easy field to have simply never added, and migrating your registration code line-for-line onto core doesn’t fix it, since the omission was never a core-versus-package issue in the first place.

This is the single most common real support issue

If you migrate a batch of abilities and only one class of bug shows up afterward, it’s this one: agents reporting “the tool isn’t available” for something you know is registered. Check meta.mcp.public first, before assuming a deeper migration problem.

Step 3: Run the diagnostic

This one wp eval command tells you, for every registered ability on the site, exactly what the MCP Adapter would currently expose:

wp-env run cli wp eval '
$abilities = wp_get_abilities();
foreach ( $abilities as $ability ) {
	$public = $ability->get_meta( "mcp" )["public"] ?? false;
	echo $ability->get_name() . ": " . ( $public ? "public" : "private" ) . "\n";
}
'

Run this immediately after any migration batch. Anything printing private that you expected an agent to be able to call is your list of abilities still needing the meta.mcp.public fix from Step 1.

Step 4: Work through the full checklist, not just the obvious cause

meta.mcp.public being unset is the most common cause, but it isn’t the only thing that can make an ability invisible to an agent. Work through all of these before assuming a deeper bug:

Full visibility checklist
meta.mcp.public is true
Confirmed via Step 3's script, not assumed from reading the registration code.
The MCP Adapter plugin is active
wp plugin is-active mcp-adapter. A public ability is still invisible with no adapter running at all.
The ability registered on the right hook
wp_abilities_api_init, not a hook that fires too late or too early to be picked up.
permission_callback passes for the connecting agent's user context
A public ability with a permission_callback that always returns false is technically exposed but will reject every call.
The agent's MCP client cache is fresh
Some MCP clients cache a server's tool list; reconnecting or restarting the client after a registration change is sometimes necessary.

Test it: connect a real client and compare

Once the diagnostic script and checklist agree an ability should be visible, confirm against an actual connected MCP client (Claude Desktop, Cursor, or whichever client Course 3 walked you through), and check its tool list matches your wp eval output exactly. A mismatch at this point almost always points back to the client-side cache item in Step 4’s checklist, not a server-side registration problem.

'Registered' and 'exposed' are not the same claim

It’s tempting to treat wp_get_abilities() returning your ability as proof an agent can use it. It isn’t. Registration and MCP exposure are two separate, independently controlled things, and conflating them is exactly what generates the confused “it’s registered but doesn’t work” bug reports this lesson exists to prevent.

Recap

meta.mcp.public is the actual gate between an ability existing and an ability being usable by an external AI agent, and it’s a field the archived standalone package never required you to think about, which is exactly why migrated abilities are especially prone to missing it. Run the diagnostic script in Step 3 after every migration batch, work the full checklist in Step 4 before assuming a deeper bug, and always confirm against a real connected client before calling an ability “done.”

Resources & further reading

← Updating .wp-env.json and Local Environments for Core Abilities Handling Deprecations From the Standalone Plugin Era →