Giving the chatbot a new capability means registering a tool: a description the model reads, a schema for its input, and a callback that does the work.
The shape
add_action( 'sohaychat_abilities_api_init', function () {
sohaychat_register_ability(
'acme/check-order-status',
array(
'label' => 'Check order status',
'description' => 'Look up the delivery status of an order by its number.',
'category' => 'acme',
'input_schema' => array(
'type' => 'object',
'properties' => array(
'order_number' => array(
'type' => 'string',
'description' => 'The order number, as printed on the confirmation email.',
),
),
'required' => array( 'order_number' ),
'additionalProperties' => false,
),
'permission_callback' => function () {
return is_user_logged_in();
},
'execute_callback' => function ( $input ) {
return array( 'status' => acme_lookup( $input['order_number'] ) );
},
)
);
} );
The description is the interface
The model chooses tools by reading their descriptions. That makes the description the most important line you write — more than the schema, more than the code.
Describe when to use it, not what it does internally. “Look up the delivery status of an order by its number” gets called at the right moment; “Order lookup” does not.
The same goes for each property’s description. A model that knows an order number is on the confirmation email can ask the visitor for it properly.
Take no input? Omit properties entirely
A tool with no input should declare:
'input_schema' => array(
'type' => 'object',
'additionalProperties' => false,
),
Do not pass an empty object as properties. WordPress’s schema validator
iterates it as an array and fatals on an object. Leaving the key out is both
correct and sufficient to reject unexpected input.
What the callback should return
An array, which is serialised for the model. Keep it small and flat — every field costs tokens on every call, and a field the model does not need is a field it can misread.
Return only what answers the question. A full record where a status string would do makes replies worse, not better.
Two things that decide whether it is ever called
The permission callback runs before execution, but the tool is still
described to the model. If a capability should not even be advertised, remove
it from sohaychat_tools instead — see The tools registry for the
distinction.
Names are constrained by the providers. Stick to letters, numbers, underscores and hyphens, plus one slash as a namespace separator. The slash is rewritten before the model sees it.
Testing it
Ask the chatbot something that should trigger it, then check Sohay → Diagnostics and the tool activity log to see whether it was actually called.
A tool that never fires is almost always a description problem, not a registration one. Rewrite the description as the sentence a visitor would say, and try again.
Where to go next
The tools registry for the surrounding hooks.