Im Übersichtsartikel zu WordPress 7.0 steht die Abilities API als eine der drei Architekturachsen, neben dem AI Client und der PHP-Renaissance im Block-System. Sie ist die unauffälligste der drei und vermutlich die folgenreichste, weil sie das Verhältnis zwischen WordPress und externer Software grundsätzlich verschiebt. Was sie konkret leistet, wie sie aufgebaut ist und welche Konsequenzen sie für Plugin- und Eigenentwicklungen hat, lohnt einen eigenen Blick.
Der Begriff ist tückisch, weil WordPress seit Jahren mit Capabilities arbeitet. Eine Capability ist eine Berechtigung: edit_posts, manage_options, upload_files. Sie regelt, was eine Rolle grundsätzlich darf. Eine Ability liegt eine Ebene höher: Sie beschreibt eine ausführbare Funktion mit Name, Schema, Berechtigungsprüfung und Kategorie, und sie nutzt Capabilities intern, um zu prüfen, ob ein Aufruf erlaubt ist. Zwischen beiden besteht keine Konkurrenz; die Ability ergänzt das klassische Berechtigungssystem um eine Beschreibungsdimension, die ihm bislang fehlte.
Die naheliegende Anschlussfrage liegt eine Schicht weiter: Warum dafür eine eigene Schnittstelle, wo die REST-API seit Version 4.7 ausgereift verfügbar ist? Die REST-API beschreibt Endpunkte, also Adressen, unter denen Daten gelesen oder verändert werden. Eine Ability beschreibt eine Operation mit ihrem Schema, ihrer Kategorie und der Information, ob sie lesend oder verändernd arbeitet. Für menschliche Entwicklung mag der Unterschied marginal wirken; für Sprachmodelle, die ohne strukturierte Beschreibung nicht zuverlässig zwischen Funktionen wählen können, sind die zusätzlichen Metadaten entscheidend. Hooks wiederum sind Erweiterungspunkte innerhalb des Systems und haben keine Schnittstelle nach außen. Die drei Mechanismen ergänzen einander statt sich zu ersetzen.
Eine Ability wird in zwei Hooks registriert. Zuerst muss eine Kategorie existieren, weil WordPress eine Ability ohne Kategorie nicht akzeptiert. Anschließend wird die Ability selbst angemeldet. Die Trennung wirkt zunächst umständlich, ergibt aber Sinn, sobald mehrere Plugins parallel Abilities registrieren: Kategorien sind global, Abilities namespaced.
<?php
/**
* Schritt 1: Kategorie registrieren.
* Ohne registrierte Kategorie wird keine Ability akzeptiert.
*/
add_action( 'wp_abilities_api_categories_init', function() {
wp_register_ability_category(
'content-retrieval',
array(
'label' => __( 'Inhalte abrufen', 'urbanstudio' ),
'description' => __( 'Abilities zum Abrufen veröffentlichter Inhalte.', 'urbanstudio' ),
)
);
} );
/**
* Schritt 2: Ability registrieren.
* Namenskonvention: plugin-slug/ability-name
*/
add_action( 'wp_abilities_api_init', function() {
wp_register_ability(
'urbanstudio/get-press-releases',
array(
'label' => __( 'Pressemeldungen abrufen', 'urbanstudio' ),
'description' => __(
'Liefert die zuletzt veröffentlichten Pressemeldungen einer Kategorie.',
'urbanstudio'
),
'category' => 'content-retrieval',
// Eingabe-Schema: definiert die erwarteten Parameter und validiert sie.
'input_schema' => array(
'type' => 'object',
'properties' => array(
'category' => array(
'type' => 'string',
'description' => 'Slug der Kategorie, deren Pressemeldungen abgerufen werden sollen.',
'minLength' => 1,
),
'limit' => array(
'type' => 'integer',
'description' => 'Maximale Anzahl der zurückgegebenen Pressemeldungen.',
'minimum' => 1,
'maximum' => 20,
'default' => 5,
),
),
'required' => array( 'category' ),
'additionalProperties' => false,
),
// Ausgabe-Schema: Pflichtfeld, beschreibt das Rückgabeformat.
'output_schema' => array(
'type' => 'array',
'items' => array(
'type' => 'object',
'properties' => array(
'title' => array( 'type' => 'string' ),
'date' => array( 'type' => 'string', 'format' => 'date-time' ),
'excerpt' => array( 'type' => 'string' ),
'url' => array( 'type' => 'string', 'format' => 'uri' ),
),
),
),
// Berechtigungsprüfung: einzige Schicht zwischen Ability und Ausführung.
'permission_callback' => function() {
return current_user_can( 'read' );
},
// Eigentliche Funktionslogik der Ability.
'execute_callback' => function( $input ) {
// Eingaben säubern und Grenzwerte erzwingen.
$category = sanitize_key( $input['category'] );
$limit = isset( $input['limit'] ) ? absint( $input['limit'] ) : 5;
$limit = min( max( $limit, 1 ), 20 );
// Beiträge der gewünschten Kategorie laden.
$posts = get_posts( array(
'category_name' => $category,
'posts_per_page' => $limit,
'post_status' => 'publish',
) );
// Ergebnis in das Ausgabe-Schema überführen.
return array_map( function( $post ) {
return array(
'title' => get_the_title( $post ),
'date' => get_the_date( 'c', $post ),
'excerpt' => get_the_excerpt( $post ),
'url' => get_permalink( $post ),
);
}, $posts );
},
// Annotations: Hinweise für konsumierende Systeme zum Verhalten der Ability.
'meta' => array(
'annotations' => array(
'readonly' => true,
'destructive' => false,
'idempotent' => true,
),
),
// Macht die Ability zusätzlich über die REST-API erreichbar.
'show_in_rest' => true,
)
);
} );
Die Konstruktion hat sechs Pflichtfelder. Drei davon, label, description und category, sorgen für die Verzeichnis-Funktion. Die anderen drei tragen die operative Logik. Das output_schema beschreibt in JSON-Schema das Rückgabeformat. Im permission_callback entscheidet sich, ob ein Aufruf überhaupt ausgeführt wird; er ist die einzige Schicht zwischen einer Ability und ihrer Ausführung. Die eigentliche Funktionslogik steht im execute_callback . Hinzu kommt das optionale, in der Praxis aber meist gesetzte input_schema für die Validierung der Eingaben, dazu die Annotations readonly, destructive und idempotent, die konsumierenden Systemen mitteilen, ob die Ability gefahrlos wiederholt aufgerufen werden kann. Über show_in_rest => true wird sie zusätzlich über die REST-API erreichbar, was bei lesenden Funktionen üblicherweise gewünscht und bei verändernden mit Bedacht zu setzen ist.
Bei klassischen PHP-Funktionen interessiert der Name allenfalls Entwickler. Bei Abilities wird die description selbst Teil der öffentlichen Schnittstelle. Sie wird im Adminbereich angezeigt, über die REST-API ausgeliefert und, sobald der MCP Adapter im Spiel ist, an Sprachmodelle übergeben, die anhand dieser Beschreibung entscheiden, ob sie die Funktion für eine Aufgabe heranziehen.
Diese Verschiebung hat eine sprachliche Konsequenz. Marketing-Sprache, Mehrdeutigkeit und Pseudo-Präzision verlieren in der description ihre Wirkung. Ein Agent, der zwischen urbanstudio/get-press-releases und einer hypothetischen urbanstudio/get-events wählen muss, liest die beiden Felder als technische Klassifizierung. Eine Formulierung wie Findet die perfekten Inhalte für jede Kommunikationssituation ist deshalb schlechter als Returns the latest published press releases for a given category slug. Die zweite Version ist trockener und dafür eindeutig zuordenbar. Wer Abilities formuliert, lernt eine Form von Sprache, die abgrenzt statt erzählt.
Bei oberflächlicher Lektüre wirkt der permission_callback wie ein Implementierungsdetail. Tatsächlich entscheidet allein dieser Callback darüber, ob ein externer Aufrufer eine Funktion ausführen kann oder eine WP_Error-Antwort erhält. Sobald eine Ability über REST oder MCP erreichbar ist, gibt es keine zweite Schicht.
Bei lesenden Funktionen wie dem gezeigten Beispiel kann current_user_can('read') ausreichen, solange die Inhalte ohnehin öffentlich sind. Bei verändernden Funktionen ist diese Prüfung zu locker. Wer Optionen ändert, Inhalte veröffentlicht oder Daten exportiert, braucht eine Prüfung, die auf das konkrete Objekt und die konkrete Operation zugeschnitten ist. Die Konvention current_user_can( 'edit_post', $post_id ) ist hier präziser als ein generisches edit_posts, weil sie die Berechtigung an die spezifische Ressource bindet.
Wichtiger als die einzelne Prüfung ist die Modellierungsebene. Eine Ability sollte klein, prüfbar und in ihrer Funktion eindeutig sein. Ein urbanstudio/manage-content, das alles und nichts tut, ist sicherheitstechnisch deutlich schwerer zu kontrollieren als ein Bündel kleinerer Abilities, die jeweils eine Operation klar umreißen. Die API legt diese Disziplin nahe, ohne sie zu erzwingen.
Mit der Client-Side Abilities API, die WordPress 7.0 zusätzlich zur serverseitigen Schicht aus 6.9 mitbringt, ist eine im PHP registrierte Ability in drei Kontexten verfügbar. Im PHP selbst über wp_get_ability(), über die REST-Endpunkte der Abilities API (sofern show_in_rest gesetzt ist) und im JavaScript-Frontend über @wordpress/core-abilities, das server- und clientseitige Fähigkeiten gemeinsam lädt. Reine Frontend-Abilities lassen sich isoliert über @wordpress/abilities einbinden. Die praktische Konsequenz ist subtil und folgenreich: Funktionen müssen nicht mehr pro Ausführungskontext neu implementiert werden, was redundante Logik vermeidet und die Wahrscheinlichkeit reduziert, dass eine REST-Variante anders prüft als ihre interne Schwester.
Der MCP Adapter ist die Brücke zwischen der Abilities API und der Welt agentenbasierter Werkzeuge. Das Model Context Protocol beschreibt, wie Sprachmodelle und externe Werkzeuge miteinander kommunizieren; der WordPress-Adapter übersetzt registrierte Abilities in MCP-Tools, die Clients wie Claude Desktop, Cursor oder Claude Code direkt aufrufen können. Das Plugin wird separat installiert und liegt als offizielles Repository unter WordPress/mcp-adapter vor. Im Core selbst ist nichts davon enthalten.
Die Reihenfolge ist wichtiger, als die kurze Beschreibung nahelegt. Die Abilities API beschreibt, was eine WordPress-Installation kann; der Adapter macht diese Fähigkeiten für externe Sprachmodelle nutzbar. Beide Schichten arbeiten unabhängig. Eine Installation mit sauber registrierten Abilities ist auch ohne MCP Adapter eine strukturiert ansprechbare Anwendung mit dokumentierter Schnittstelle. Mit aktiviertem Adapter wird sie zusätzlich an die Werkzeuglandschaft der Sprachmodelle angeschlossen. Authentifizierung läuft dabei über WordPress Application Passwords, was bei der Konfiguration Sorgfalt verlangt: Jede über den Adapter erreichbare Ability ist eine potenzielle Angriffsfläche.
Den Aufbau des Adapters, die Authentifizierung und die Sicherheitserwägungen führt der nachfolgende Snippet aus. Hier reicht der Hinweis, dass der Adapter ohne registrierte Abilities nichts ausrichtet. Die Qualität der Abilities entscheidet über die Qualität der MCP-Anbindung.
Für Verbände, Fachgesellschaften und Kongress-Plattformen ist die Abilities API vor allem deshalb relevant, weil sie zu einer Differenzierung zwingt, die in klassischen WordPress-Setups oft unterbleibt. Eine Pressemeldung als Entwurf anzulegen ist eine andere Operation als eine Pressemeldung zu veröffentlichen. Das Filtern einer Abstract-Liste nach Status hat andere Konsequenzen als ein Export des Mitgliederverzeichnisses. Solange diese Operationen in einer generischen Admin-Maske zusammenfallen, ist das eine Frage des Workflows; sobald sie als Abilities einzeln registriert werden müssen, wird daraus eine Frage des Sicherheitsmodells.
Diese Differenzierung ist der eigentliche Gewinn der API für komplexe Plattformen. Die API verlangt klare Modellierung von Funktionen, und genau das ist in vielen gewachsenen Verbandssystemen das, was am dringendsten fehlt. Wer heute Eigenentwicklungen plant, sollte jede neue Funktion auf ihre Ability-Tauglichkeit prüfen. Manches gehört in das Verzeichnis, manches nicht. Vieles, was bisher als interner Helper geschrieben wurde, ließe sich als kleine, klar umrissene Ability präziser modellieren und nebenbei dokumentieren.
Die Abilities API ist die Schicht, auf der die nächsten Bausteine aufsetzen: der MCP Adapter für die agentische Erreichbarkeit von außen, das AI Client SDK für die KI-Anbindung von innen, die Client-Side Abilities API für die Verbindung von Server- und Frontend-Logik. Die nachfolgenden Beiträge dieses Clusters setzen voraus, was hier beschrieben ist. Wer eine kleine eigene Ability registriert, hat das Prinzip schneller verstanden, als jede Doku es erklären könnte.