gdamian.pl
← Wszystkie wpisy
15 min

WordPress + AI 2026: Abilities API i rejestr możliwości — jak agent bezpiecznie korzysta z Twojej strony 

Damian GębalaAutor

Abilities API siedzi w rdzeniu WordPressa od wersji 6.9 „Gene”, która wyszła 2 grudnia 2025. To standardowy rejestr możliwości witryny: każda funkcja udostępniona przez wtyczkę dostaje nazwę, opis, schemat danych wejściowych i wyjściowych oraz własne uprawnienia. Bezpieczeństwo agenta AI nie opiera się na jednej blokadzie, tylko na trzech niezależnych przepustnicach, które trzeba przejść łącznie.

Dwie z tych przepustnic są domyślnie zamknięte - zdolność nie wychodzi do REST API i nie pokazuje się w MCP, dopóki ktoś jej świadomie nie otworzy. Trzecia zależy od autora wtyczki. W praktyce możliwości w WordPressie nie włącza się jednym przełącznikiem. Poniżej: czym jest rejestr, jak go wypełnić w PHP, czym agent wchodzi na stronę i gdzie leżą granice bezpieczeństwa.

Czym jest Abilities API i po co komu rejestr możliwości

Oficjalna dokumentacja definiuje Abilities API jako „standardized way to register and discover distinct units of functionality within a WordPress site”. Jednostką jest ability - pojedynczy kawałek funkcjonalności o unikalnej nazwie w wzorcu namespace/ability-name, z nazwą i opisem dla człowieka, schematem wejścia i wyjścia, kategorią oraz funkcją PHP, która się wykona po wywołaniu.

Warto znać cztery pojęcia rdzenia. Category to pojemnik na powiązane zdolności - każda należy do dokładnie jednej kategorii. Rejestr to centralna, jedna dla witryny lista, po której można szukać i pytać. Schema to opis danych w standardzie JSON Schema: pozwala sprawdzić, czy wejście jest poprawne, i mówi agentowi z góry, czego zdolność oczekuje, bez czytania kodu. Callback to funkcja, która wykonuje się przy wywołaniu.

Sedno różnicy wobec „po prostu REST API” opisuje wpis na blogu deweloperskim WordPressa: WordPress nie narzuca jednego sposobu budowania publicznego API, więc dostępne opcje to mogą być hooki, globalne funkcje, endpointy REST albo dowolna mieszanka. Abilities daje jeden ustandaryzowany sposób wystawienia funkcji - dostępny z PHP, z JavaScriptu i z zewnątrz przez REST, tak samo.

Rejestr przy tym nie wykonuje Twojej logiki. Zakres jest opisany wprost: „discovery, permissioning, and execution metadata only. Actual business logic stays inside the registering component”. Rejestr opisuje zdolność, waliduje dane i pilnuje uprawnień, a efekt uboczny robi kod wtyczki. Własnego systemu ról Abilities API nie wprowadza - opiera się na istniejącym current_user_can().

Jeśli dopiero myślisz o agentach na stronie, zacznij od pętli obserwacja, plan, narzędzie i wynik. Rejestr możliwości po prostu daje tej pętli narzędzia z opisem.

Kiedy to wchodziło i co się z WordPressem stało

Historia jest krótka. 17 lipca 2025 na blogu Make/AI pojawiły się wpisy o Abilities API i o MCP Adapterze, opisane jako projekt „AI Building Blocks”. Pierwsza wersja API, v0.1.0, wyszła 29 sierpnia 2025, a 14 listopada 2025 Jonathan Bossenger ogłosił na blogu deweloperskim, że warstwa serwerowa wraz z obsługą REST została zatwierdzona do rdzenia.

Rdzeń dostał ją razem z WordPressem 6.9 „Gene” z 2 grudnia 2025. Dokumentacja jest jednoznaczna: „The Abilities API was added to WordPress in version 6.9”, a korzystanie z niej wymaga 6.9 albo nowszego.

Repozytorium WordPress/abilities-api zarchiwizowano 5 lutego 2026, ostatni push wypadł 17 stycznia 2026, a lista wydań kończy się na sześciu pozycjach, ostatniej oznaczonej v0.5.0-rc. To fakt, nie interpretacja: repozytorium rozwojowe przełączono w tryb tylko do odczytu, bo kod przeniesiono do rdzenia.

Uwaga na dokumentację: plik Getting Started w repozytorium wciąż opisuje Abilities API jako coś, co trzeba doinstalować. To ta część instrukcji jest nieaktualna - reszta zgadza się z rdzeniem.

Aktywnym elementem został MCP Adapter: 1774 gwiazdki, 198 forków, push 29 września 2026, osiem wydań od v0.1.0 (14 sierpnia 2025) do v0.6.1 (13 sierpnia 2026).

Najbliżej Twojej stronie jest wtyczka „Enable Abilities for MCP” w wersji 2.13.0: 4 000 aktywnych instalacji, 112 abilities w 21 kategoriach, ocena 100% z 12 głosów. Autor to Fabio Montenegro, w katalogu jest od 24 marca 2026, wymaga WordPressa 6.9 i PHP 8.0.

WordPress 7.0 „Armstrong” z 20 maja 2026 dołożył AI Client w rdzeniu, czyli możliwość, żeby sam WordPress rozmawiał z modelami generatywnymi, oraz pakiet Client-Side Abilities - odpowiednik zdolności po stronie przeglądarki.

Jak zarejestrować pierwszą ability (PHP, dosłownie z dokumentacji)

Rejestracja wygląda tak: najpierw kategoria, potem zdolność, każda na swoim hooku - czyli punkcie zaczepienia, w którym WordPress mówi Twojemu kodowi „tu się zaczynaj”. Kategorie wpinasz w wp_abilities_api_categories_init, zdolności w wp_abilities_api_init. Są też filtry wp_register_ability_args i wp_register_ability_category_args, które poprawiają argumenty przed walidacją.

add_action( 'wp_abilities_api_categories_init', 'wporg_register_category' );

function wporg_register_category() {
	wp_register_ability_category(
		'site-information',
		array(
			'label'       => __( 'Site Information', 'textdomain' ),
			'description' => __( 'Abilities that provide information about the WordPress site.', 'textdomain' ),
		)
	);
}

add_action( 'wp_abilities_api_init', 'wporg_register_ability' );

function wporg_register_ability() {
	wp_register_ability(
		'my-plugin/site-info',
		array(
			'label'         => __( 'Site Info', 'textdomain' ),
			'description'   => __( 'Returns information about this WordPress site', 'textdomain' ),
			'category'      => 'site-information',
			'input_schema'  => array(),
			'output_schema' => array(
				'type'       => 'object',
				'properties' => array(
					'site_name' => array(
						'type'        => 'string',
						'description' => __( 'The name of the WordPress site', 'textdomain' ),
					),
					'site_url'  => array(
						'type'        => 'string',
						'description' => __( 'The URL of the WordPress site', 'textdomain' ),
					),
				),
			),
			'execute_callback'   => 'wporg_get_siteinfo',
			'permission_callback' => function () {
				return current_user_can( 'manage_options' );
			},
			'meta' => array(
				'show_in_rest' => true,
			),
		)
	);
}

Najważniejsze pola to input_schema i output_schema (opis danych w JSON Schema), execute_callback (funkcja, która robi robotę), permission_callback (czy ten użytkownik może to wywołać) oraz meta.show_in_rest, czyli decyzja, czy zdolność wychodzi przez REST. Slug kategorii: małe litery, cyfry i myślniki.

W rdzeniu zestaw jest mały. Funkcja wp_register_core_abilities() rejestruje trzy zdolności: core/get-site-info, core/get-user-info i core/get-environment-info, w kategoriach site i user. Reszta zależy od wtyczek i od tego, kto w nich pisze permission_callback.

Jak agent wchodzi na Twoją stronę

REST API WordPressa działa pod adresem /wp-json. To zwykły korytarz HTTP do funkcji witryny. Abilities API ma w nim własną przestrzeń: /wp-abilities/v1. Trzy wywołania prosto z dokumentacji:

  # lista zdolności
curl https://example.com/wp-json/wp-abilities/v1/abilities

  # odczyt z danymi w adresie (URL-encoded)
curl "https://example.com/wp-json/wp-abilities/v1/my-plugin/get-user-info/run?input=%7B%22user_id%22%3A1%7D"

  # zapis ustawienia
curl -X POST -H "Content-Type: application/json" \
-d '{"input":{"option_name":"blogname","option_value":"New Site Name"}}' \
https://example.com/wp-json/wp-abilities/v1/my-plugin/update-option/run

Lista siedzi pod GET /wp-abilities/v1/abilities i przyjmuje page (domyślnie 1), per_page (domyślnie 50, maksymalnie 100) oraz category do filtrowania po slugu kategorii. Są też endpointy listy kategorii, pojedynczej kategorii i pojedynczej zdolności.

Wykonanie zawsze idzie przez /run, a metodę HTTP wybiera nie klient, tylko opis zdolności. To najmocniejszy wyróżnik całego mechanizmu. Zdolność oznaczona readonly: true musi być wywołana przez GET, zwykła zdolność przyjmująca dane wejściowe przez POST, a zdolność oznaczona destructive: true przez DELETE. Dokumentacja tłumaczy to jednym zdaniem: odczyty używają bezpiecznej metody, którą da się cache’ować i która nie zmienia stanu serwera. Zła metoda kończy się błędem rest_ability_invalid_method, a nie cichym wykonaniem.

W odpowiedzi agent dostaje opis zdolności razem z adnotacjami w meta.annotations: instructions, readonly, destructive, idempotent. Zanim cokolwiek odpali, wie z tego, co dana operacja robi.

Po stronie JavaScriptu działa pakiet @wordpress/abilities z funkcjami getAbilities, getAbility i executeAbility. Jedno zastrzeżenie, bo tu łatwo o błąd: w 6.9 ten klient był dostępny tylko w repozytorium, a pakiet zdolności po stronie klienta trafił do rdzenia dopiero w 7.0.

Trzy przepustnice, które decydują, czy agent w ogóle dotrze do Twojej strony

Przepustnica 1: uwierzytelnienie na poziomie endpointu

Dokumentacja jest wprost: dostęp do wszystkich endpointów Abilities API wymaga zalogowanego użytkownika. Obsługiwane są metody, które WordPress zna od lat - cookie dla żądań z tej samej domeny, application passwords (hasła aplikacji, rekomendowane przy dostępie zewnętrznym) i wtyczki uwierzytelniające.

Application password to osobne, długie hasło do jednej maszyny, które można w każdej chwili cofnąć bez ruszania hasła administratora. Praktycznie: agent dostaje własne drzwi, które odcinasz jednym kliknięciem, a w logu widać, kto się nimi posłużył.

curl -u 'USERNAME:APPLICATION_PASSWORD' \
https://example.com/wp-json/wp-abilities/v1

Przepustnica 2: show_in_rest, domyślnie false

Druga przepustnica brzmi: „By default, registered abilities are not exposed via the REST API”. Zarejestrowana zdolność jest domyślnie niewidoczna - nie pojawi się na liście i nie da się jej wywołać przez REST. Zostaje dostępna wewnątrz WordPressa przez wp_get_ability() i $ability->execute(). Ktoś musi świadomie ustawić meta.show_in_rest na true, jak w przykładzie wyżej.

Próba dostępu do zamkniętej zdolności kończy się błędem rest_ability_not_found. Świadomie wybrany kod, który wygląda jak „nie ma takiej rzeczy”, zamiast komunikatu o braku uprawnień.

Przepustnica 3: meta.public dla MCP, domyślnie false

Trzecia przepustnica chroni drogę, którą przyjdzie asystent z Model Context Protocol. Dokumentacja MCP Adaptera mówi wprost: „WordPress abilities are private by default”. Aby wystawić zdolność przez MCP, trzeba ustawić meta.public (albo meta.mcp.public) na true i dopiero wtedy da się do niej dosięgnąć przez trzy meta-narzędzia serwera domyślnego.

Przełączniki są od siebie niezależne. Można wystawić zdolność przez REST, nie wystawiając jej przez MCP, albo zostawić ją w pełni zamkniętą i używać tylko wewnątrz PHP.

Czwarte miejsce: permission_callback i dokumentacja, która się nie zgadza

Na poziomie pojedynczej zdolności działa permission_callback - funkcja sprawdzająca, czy zalogowany użytkownik może wywołać właśnie tę zdolność. W rdzeniu jest to current_user_can( 'manage_options' ) dla dwóch z trzech i is_user_logged_in() dla core/get-user-info.

Jest tu realna niespójność dokumentacji: Handbook określa ten callback jako opcjonalny, a plik php-api.md w tym samym repozytorium jako wymagany. Nie zgaduję, która wersja jest poprawna. Praktycznie traktuj go jako obowiązkowy - bez niego nie ma decyzji, komu wolno.

Warto też wiedzieć o kolejności: sprawdzenie schematu wejścia leży przed sprawdzeniem uprawnień. Przy wejściu niezgodnym ze schematem permission_callback w ogóle nie zostanie wywołany, a wykonanie zakończy się błędem walidacji.

Kody błędów w logach są konkretne: ability_invalid_permissions (brak uprawnień, zwykle 403), rest_ability_not_found (żądana zdolność nie jest zarejestrowana), ability_invalid_input (wejście nie przeszło walidacją według schematu) i rest_ability_cannot_execute (brak uprawnień do wykonania).

{
  "code": "ability_invalid_permissions",
  "message": "Ability \"my-plugin/update-option\" does not have necessary permission.",
  "data": {
    "status": 403
  }
}

Dane, które przy okazji wracają, bywają wrażliwe, a lecą do zewnętrznych narzędzi. Uporządkowanie tego, co wolno wysyłać i kto to przechowuje, opisałem przy okazji RODO i AI w firmie.

Pułapka, o której mało kto pisze: destructive domyślnie true

Najbardziej zaskakujący parametr całego API to destructive. Domyślna wartość wynosi true. Nie false - true. Pełny zestaw adnotacji wygląda tak:

AdnotacjaDomyślnieCo znaczy
instructions''własna instrukcja dla agenta, jak używać tej zdolności
readonlyfalseczy zdolność tylko czyta, czy zmienia otoczenie
destructivetrueczy może robić zmiany niszczące, w tym usunięcia
idempotentfalseczy powtórzenie z tymi samymi argumentami nie zmienia efektu

Znaczenie jest takie, że brak jawnego ustawienia tej flagi nie oznacza bezpiecznej operacji. Autor wtyczki, który nie pomyślał o adnotacjach, automatycznie deklaruje, że jego zdolność może kasować. Dokumentacja podaje to obojętnie, bez oceny ryzyka - ja traktuję to jako realną pułapkę, bo większość twórców kopiuje przykłady i zapomina o polu, którego nie widać w interfejsie.

Druga rzecz, którą łatwo źle zrozumieć: adnotacje to deklaracja intencji, a nie egzekucja. To moja analiza, nie cytat z dokumentacji. Wiem o tym stąd, że opisane wpływają na dobór metody HTTP przy wywołaniu. Nie blokują samego wykonania, bo efekt uboczny robi execute_callback wtyczki. Agent dostaje więc informację, na której może polegać, ale nikt go nie pyta „czy na pewno chcesz usunąć ten wpis?”.

Najlepszy wzorzec do skopiowania jest w samym rdzeniu. Wszystkie trzy rdzeniowe zdolności mają readonly: true, destructive: false, idempotent: true, public: true. Autorzy WordPressa zaznaczyli flagi jawnie, mimo że destructive: true byłoby bezpieczniejszym ustawieniem domyślnym. Zajrzyj do ich kodu, zanim napiszesz pierwszy callback.

Jest jeszcze jedna rzecz, której Abilities API nie ma: sandboxu. Ani izolacji wykonania, ani ograniczenia zasobów - w żadnym z moich źródeł to pojęcie się nie pojawia. W praktyce zakres możliwych skutków ubocznych ogranicza permission_callback i hooki, a nie sam rejestr. Dlatego ustawianie hamulców opisuję osobno przy 4 poziomach swobody agenta AI.

Jedyne potwierdzone miejsce na własny dziennik zdarzeń to hooki wp_before_execute_ability (odpala się po przejściu kontroli uprawnień, przed callbackiem) i wp_after_execute_ability (po udanym wykonaniu i walidacji wyniku). Podpięcie własnego logu do drugiego z nich daje tabelę: kto, co i kiedy uruchomił - pod prawdziwym użytkownikiem, a nie pod anonimowym „agentem”.

Druga sprawa: agent łączący się z Twoją stroną to niezaufane wejście, tak jak każdy formularz na stronie. Prompt injection, czyli wstrzyknięcie instrukcji przez treść, którą agent czyta, jest realnym wektorem - wzory i zabezpieczenia opisałem w tekście o atakach na agentów AI.

MCP Adapter: drogą z WordPressa do popularnych asystentów

Samo Abilities API to rejestr. Do popularnych asystentów prowadzi most: MCP Adapter, opisany w repozytorium jako „An MCP adapter that bridges the Abilities API to the Model Context Protocol, enabling MCP clients to discover and invoke WordPress plugin, theme, and core abilities programmatically”. Model Context Protocol to standard, przez który asystent zewnętrzny widzi narzędzia. Tool calling kontra MCP różni się tym, że tool calling jest wbudowanym mechanizmem modelu, a MCP osobnym protokołem do podłączania zewnętrznych usług i rejestrów funkcji.

WordPress może działać w obie strony: jako serwer MCP i jako klient łączący się z innymi narzędziami. Jako serwer obsługuje transport Streamable HTTP przez istniejące REST API oraz transport stdio przez WP-CLI, czyli wiersz poleceń WordPressa.

Serwer domyślny wystawia trzy meta-narzędzia, przez które agent odkrywa i wywołuje zdolności: mcp-adapter/discover-abilities, mcp-adapter/get-ability-info i mcp-adapter/execute-ability. Klient HTTP wskazujesz na /wp-json/mcp/mcp-adapter-default-server.

Co dokładnie dostajesz w pakiecie: automatyczną konwersję zdolności na narzędzia, zasoby i prompty MCP; możliwość uruchomienia wielu serwerów, każdy z własnymi transportami, zdolnościami i handlerami; transporty HTTP i STDIO oraz interfejs McpTransportInterface dla własnych protokołów; podmienialną obsługę błędów i obserwowalności przez McpErrorHandlerInterface i McpObservabilityHandlerInterface; oraz uprawnienia granularne - osobno dla transportu serwera i osobno dla każdej zdolności.

Wersje: v0.1.0 - 14 sierpnia 2025, v0.3.0 - 6 listopada 2025, v0.4.0 - 4 grudnia 2025, v0.5.0 - 15 kwietnia 2026, v0.6.0 - 12 sierpnia 2026, v0.6.1 - 13 sierpnia 2026. Wszystkie stabilne, w numeracji jest jedna luka: wersji 0.2.0 nie było.

Po stronie praktyki warto zajrzeć do wtyczki „Enable Abilities for MCP” 2.13.0, bo rozwiązuje realny problem: jak zalogować asystenta zewnętrznego. Od wersji 2.1 ma wbudowany serwer OAuth 2.1 zbudowany pod custom connectory claude.ai, więc podłączasz się samym adresem URL. ChatGPT rejestruje się dynamicznie dla każdego konektora (RFC 7591), więc potrzebuje osobnego przełącznika na zakładce Connection. Jeśli wolisz tokeny, są application passwords per użytkownik oraz pojedynczy token admina w Bearer - te obsługują Claude Desktop i Claude Code, OpenAI Codex CLI oraz Google Antigravity. To wzorzec praktyczny wtyczki, nie standard rdzenia; przed włączeniem sprawdź, co dokładnie dajesz swojej stronie.

Praktyczna checklista zanim włączysz agentowi dostęp

Krótka lista, którą przechodzę z klientem przed uruchomieniem czegokolwiek:

  1. Sprawdź wersję. Abilities API wymaga WordPressa 6.9 albo nowszego. Starsza wersja nie ma rejestru, a wtyczka „Enable Abilities for MCP” tego nie obsługuje.
  2. Zacznij od odczytu. Pierwsze zdolności oznacz readonly: true i destructive: false. Rdzeń robi tak dla wszystkich trzech swoich zdolności i warto mu się podporządkować.
  3. Ustaw destructive jawnie w każdej zdolności. Domyślna wartość to true, więc brak flagi to deklaracja możliwości usuwania.
  4. Przejrzyj permission_callback jednej po drugiej. Nie ma własnego systemu ról - wszystko stoi na current_user_can(). Domyślne manage_options jest szerokie.
  5. Nie wystawiaj wszystkiego naraz. show_in_rest i meta.public zostaw na false tam, gdzie agent nie musi działać. Każdy otwarty przełącznik to jedna trzecia przepustnica z trzech.
  6. Daj agentowi application password, nie hasło administratora. W razie problemu cofasz jedno hasło, a nie całe konto.
  7. Załóż osobne konto testowe z własnym loginem, żeby zobaczyć dokładnie to, co zobaczy agent.
  8. Podepnij wp_after_execute_ability do własnego logu. Bez tego nie masz odpowiedzi na pytanie, co agent robił na stronie w weekend.

Jedno zastrzeżenie o źródłach: dokumentacja MCP Adaptera ma luki. README linkuje przewodniki pod docs/guides/* - między innymi o transportach, uprawnieniach i serwerze domyślnym - a tych plików w repozytorium nie ma. Przy skomplikowanych integracjach zakładaj więcej testów własnych. Jak wygląda podpięcie takiego mostu do realnej automatyzacji, pokazałem na przykładzie n8n z MCP.

Co z tym zrobić, jeśli prowadzisz firmę bez działu IT

Praktyczna zmiana jest mniejsza, niż sugeruje nagłówek w stylu newsa. WordPress dostał standardowy, udokumentowany rejestr tego, co Twoja strona umie. Wcześniej, żeby asystent miał cokolwiek, trzeba było mu zrobić REST API ręcznie, endpoint po endpoincie, każdy z własnym uwierzytelnianiem i formatem odpowiedzi. Teraz wystawiasz zdolność i ona sama deklaruje, czego potrzebuje i jakie ma ograniczenia.

Przełożenie na Twój dzień pracy: agent dostaje dostęp do powtarzalnych czynności, które dziś wykonuje człowiek - na stronie opartej na WordPressie to mogą być pobranie listy ostatnich wpisów, przygotowanie wersji roboczej, uzupełnienie metadanych czy odczyt stanu środowiska. Wtyczka „Enable Abilities for MCP” wystawia ich 112 w 21 kategoriach, od SEO przez WooCommerce po Elementor i dostępność.

To najprostsza droga, żeby AI na stronie naprawdę pomagało tam, gdzie ma to sens, a nie tam, gdzie tylko szkodzi. Zamiast dorabiać własne integracje, korzystasz z możliwości, które wtyczka już opisuje.

Ale to nadal dostęp do Twojej strony. Zmiana nazwy wpisu, publikacja, usuwanie treści - wszystko przechodzi przez permission_callback i log zdarzeń. Zanim agent dostanie cokolwiek, warto ustalić, kto zatwierdza operacje zmieniające treść i gdzie leżą dane osobowe klientów. Zdolności typu core/get-user-info zwracają między innymi login, role i dane profilu zalogowanego użytkownika, więc zakres tego, co agent odczyta, warto zapisać przed włączeniem.

Przy pierwszej rozmowie nie pytam „czy chcesz AI?”. Pytam o cztery rzeczy. Które powtarzalne czynności na stronie faktycznie kosztują godziny w tygodniu. Co agent może tylko czytać, a co zmieniać. Kto zatwierdza te zmiany. I co z danymi osobowymi: co wychodzi na zewnątrz, a co zostaje na serwerze.

Co do ceny: nie podaję stawek, bo cena zależy od tego, które wtyczki chcesz wystawić i jak głęboko sięgasz. Wycenę daję po audycie i rozmowie - dopiero wtedy widać, ile uprawnień trzeba zaprojektować i ile testów napisać, żeby udowodnić, że agent nie przekroczy wyznaczonych granic.

Napisz do mnie i powiedz, co na Twojej stronie agent powinien robić. Odpowiadam zwykle tego samego dnia.

Robisz coś podobnego u siebie?Napisz do mnie

Masz proces, który zjada tydzień pracy? Odzyskajmy ten tydzień.

Napisz do mnie