Skip to content

Connectors: Backport Gutenberg PR #75833 PHP integration#11056

Open
jorgefilipecosta wants to merge 7 commits intoWordPress:trunkfrom
jorgefilipecosta:backport/connectors-screen
Open

Connectors: Backport Gutenberg PR #75833 PHP integration#11056
jorgefilipecosta wants to merge 7 commits intoWordPress:trunkfrom
jorgefilipecosta:backport/connectors-screen

Conversation

@jorgefilipecosta
Copy link
Member

@jorgefilipecosta jorgefilipecosta commented Feb 26, 2026

Trac ticket: https://core.trac.wordpress.org/ticket/64730

Summary

  • Backport the PHP integration pieces from Gutenberg PR Add: Connectors screen and API gutenberg#75833.
  • Register the Settings > Connectors submenu (options-general.php?page=connectors-wp-admin).
  • Register default connector settings for:
    • connectors_openai_api_key
    • connectors_anthropic_api_key
    • connectors_gemini_api_key
  • Add API key masking and server-side validation wiring for settings reads/writes.
  • Expose invalid_key in /wp/v2/settings responses for requested connector fields when validation fails.
  • Pass stored connector keys into the WP AI client provider registry on init.

Notes

  • JS/TS/runtime artifacts for Connectors will be included on the Gutenberg sync; this PR covers the missing PHP glue.
  • Includes guard checks so behavior is a no-op when the AI client (or Connectors render callback) is unavailable.

Testing Instructions

Visit wp-admin/options-general.php?page=connectors-wp-admin and verify the Connectors page appears under Settings.
Verify Connectors appears between General and Writing in the Settings submenu.
Install all AI provider plugins.
Set API keys for every supported provider.
Remove the key and set it again for every supported provider.
When a incorrect API key is provided then it isn't possible to save it.
API keys are properly masked on the frontend (including REST API response).
Deactivate the AI provider plugin from the Plugins screen and activate the plugin again from the Connectors screen.
Uninstall the AI provider plugin from the Plugins screen and install the plugin again from the Connectors screen.
Revoke a key that was valid on the provider website and veify the screen does not says connected anymore.

@github-actions
Copy link

github-actions bot commented Feb 26, 2026

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props jorgefilipecosta, gziolo, flixos90, noruzzaman.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@github-actions
Copy link

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

},
"gutenberg": {
"ref": "23b566c72e9c4a36219ef5d6e62890f05551f6cb"
"ref": "336a47b80b566256ce5035cae56b2ab16f583dad"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is here just for testing it will not happen on this PR, it will happen during the Gutenberg package sync.

Copy link
Member

@felixarntz felixarntz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jorgefilipecosta Thanks for preparing this!

There are of course still several things in here that need iteration, but given it's all private and we need this in Beta 2 tomorrow, I think we can proceed.

I left a few minor comments, that (other than the registry related ones) would be great to address before commit. But overall I think it's good to go in.

We'll need to iron out the whole registry bit and improve DX of registering a connector for Beta 3.

Comment on lines 68 to 78
return null;
}

$registry->setProviderRequestAuthentication(
$provider_id,
new ApiKeyRequestAuthentication( $key )
);

return $registry->isProviderConfigured( $provider_id );
} catch ( \Error $e ) {
return null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for both of the return null cases, we'll want to add some kind of messaging, at least for debugging.

could use _doing_it_wrong for the first one (because that would be doing it wrong on the developer side) and wp_trigger_error for the second one.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done with 31ab71c. I still need to look into it, because we most likely will need to pass here some generic translatable message instead of the code-generated error message.

* @param string $key The API key.
* @param string $provider_id The WP AI client provider ID.
*/
function _wp_connectors_set_provider_api_key( string $key, string $provider_id ): void {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this function should return bool, to have an idea whether it worked or not.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the method in 31ab71c. We might want to revisit this method if we are looking at supporting multiple auth methods. In that case, we might want something more like:

function _wp_connectors_set_provider_auth( string $provider_id, string $auth_type, array $auth_params ): void {

This way we would have a general purpose util that devs can use elsewhere, too.

Comment on lines 244 to 260
return array(
'connectors_gemini_api_key' => array(
'provider' => 'google',
'mask' => '_wp_connectors_mask_gemini_api_key',
'sanitize' => '_wp_connectors_sanitize_gemini_api_key',
),
'connectors_openai_api_key' => array(
'provider' => 'openai',
'mask' => '_wp_connectors_mask_openai_api_key',
'sanitize' => '_wp_connectors_sanitize_openai_api_key',
),
'connectors_anthropic_api_key' => array(
'provider' => 'anthropic',
'mask' => '_wp_connectors_mask_anthropic_api_key',
'sanitize' => '_wp_connectors_sanitize_anthropic_api_key',
),
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine for now (i.e. Beta 2), but as discussed earlier today we should enhance this to allow properly registering a provider with all its base information in PHP, and notably reduce the barrier of entry.

First of all, of course this will require a proper registry.

In terms of this function, we would then want to use the registry, and for the 3 ones here, they would continue to be hard-coded, but overridable by the registry. This will allow us to make updates to the provider plugins (which is more appropriate for timing, since we can't quickly ship a new Core release when some of the metadata might be worth a change), and the hard-coded Core references would only be "starting points" / "fallbacks" for when the provider plugins are not active yet.

For the AI connectors specifically, we should as much as possible rely on what the WordPress\AiClient\AiClient::defaultRegistry() already offers:

Ideally we can simply iterate through those providers and then register the connector for it based on this information. We could support a "merge" behavior, where it would still be allowed to pass in additional information for the provider that may not (yet) have a 1:1 mapping in the WordPress\AiClient provider registry.

We should be able to avoid the need to provide all these callbacks, ideally this is handled centrally in Core, based on the authenticationMethod given.

And as for React UI for the fields, there should be a built-in component that is automatically used based on the given authenticationMethod. For flexibility with more advanced processes (e.g. Jetpack might add some kind of one-button click flow for better UX), we should of course still allow using a custom render component. But for the regular AI provider connector, we need to simplify this so that a single PHP array is all that's needed.

None of this is a blocker for this PR. But wanted to capture it here.

cc @gziolo

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, as noted in #11056 (comment), I plan to work on all the described changes. It seems like a viable path forward, which would make the dev experience for AI provider authors very compelling 💯

'connectors',
$option_name,
array(
'type' => 'string',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works for now, but is problematic and needs to be improved before stable release: Not every credential is a single string. A connector may require two fields (e.g. ID and secret, username and password, ...). We need to abstract this in a way that it's not enforcing a single option per provider usage - or at the very least, if we use a single option, it also needs to allow for more complex data, e.g. an object / associative array with multiple fields in it.

Once we have the connector registry, I think a good idea might be to have an settings field in there that allows an array and then handles these things. Or it could even be a register_settings PHP callback.

(Again, for some predefined authentication methods like "api_key" we could auto-populate this.)

cc @gziolo

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I refactored _wp_connectors_get_provider_settings(), so it generates all necessary settings for registered (hardcoded for now) providers. The current shape will allow us to iterate further while moving most of the logic to that function. I will continue working on it based on the proposed design after Beta 2.

- Change catch blocks from `\Error` to `Exception` (global namespace).
- Add `_doing_it_wrong` when an unregistered provider ID is passed.
- Add `wp_trigger_error` to surface exception messages in catch blocks.
- Change `_wp_connectors_set_provider_api_key` to return `bool`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
gziolo and others added 2 commits February 26, 2026 10:26
…cks.

Replace six identical per-provider mask/sanitize wrapper functions with
`_wp_connectors_mask_api_key` used directly and sanitize closures built
from a provider map in `_wp_connectors_get_connectors()`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… boilerplate.

Rename `_wp_connectors_get_connectors` to `_wp_connectors_get_provider_settings`
and dynamically assemble setting names, labels, and descriptions from a minimal
provider slug/name map. Add `label` and `description` to `register_setting` calls
so they appear in the REST API schema.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
mockedApiResponse.settings = {
"connectors_gemini_api_key": "",
"connectors_openai_api_key": "",
"connectors_anthropic_api_key": "",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if that matters that much, but we use Claude in the UI instead of Anthropic, similar to Gemini instead of Google. We could be more consistent, but it isn't a blocker to me.

* @access private
*/
function _wp_register_default_connector_settings(): void {
if ( ! class_exists( '\WordPress\AiClient\AiClient' ) ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jorgefilipecosta, do we need this check in WP core? I bet we don't.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well we don't excatly "need" but I would prefer to be defensive here, in the same way code normally checks for the inclusion of build generated functions function_exists( 'wp_connectors_wp_admin_render_page' ), AI client also seems like an external lib we had conversations in the past about some constant that makes AI client not even load etc, so I think being defensive here may be good.

@gziolo
Copy link
Member

gziolo commented Feb 26, 2026

I need to re-test the branch after all the refactoring applied based on the feedback provided, but it looks like we are good to go if it still works as expected 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants