Servidor MCP para el asistente del equipo y API para idioma, traducciones y necesidades

- mcp/: servidor MCP (stdio) con list/get/create/update animal, set_status, upload_photo,
  get/set_needs, sobre la API REST con contraseña de aplicación.
- REST: campos language y translation_of (crear la versión en euskera enlazada y heredar
  los datos comunes); endpoint bilagun/v1/needs.
- README: herramientas del equipo y tareas pendientes fuera del código.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-25 10:44:44 +02:00
parent d29a66d18b
commit a39f9f170c
8 changed files with 1614 additions and 0 deletions

View File

@@ -149,6 +149,18 @@ function ga_animal_api_update( array $profile, $post_id ) {
return true;
}
function ga_lines_of( $text ) {
return array_values( array_filter( array_map( 'trim', explode( "\n", (string) $text ) ) ) );
}
/** Default needs list (same as the theme registry), used when nothing has been saved yet. */
function ga_default_needs( $lang ) {
if ( function_exists( 'ga_customizer_registry' ) ) {
return ga_customizer_registry()['material']['fields']['list'][ $lang ] ?? '';
}
return '';
}
/** JSON schema of the profile (used by REST validation and the OpenAPI docs). */
function ga_animal_api_schema() {
$string_or_null = array( 'type' => array( 'string', 'null' ) );
@@ -181,6 +193,79 @@ function ga_animal_api_schema() {
}
add_action( 'rest_api_init', function () {
// Language of the animal and link to its translation (write), e.g. {"language":"eu","translation_of":123}.
register_rest_field( 'animal', 'language', array(
'get_callback' => fn( $post ) => function_exists( 'pll_get_post_language' ) ? pll_get_post_language( $post['id'] ) : 'es',
'update_callback' => function ( $value, $post ) {
if ( function_exists( 'pll_set_post_language' ) ) {
pll_set_post_language( $post->ID, $value );
}
return true;
},
'schema' => array( 'type' => 'string', 'enum' => array( 'es', 'eu' ), 'description' => 'Language of this translation.', 'context' => array( 'view', 'edit' ) ),
) );
register_rest_field( 'animal', 'translation_of', array(
'get_callback' => function ( $post ) {
$translations = function_exists( 'pll_get_post_translations' ) ? pll_get_post_translations( $post['id'] ) : array();
unset( $translations[ pll_get_post_language( $post['id'] ) ?: '' ] );
return $translations ? (int) reset( $translations ) : null;
},
'update_callback' => function ( $value, $post ) {
if ( ! $value || ! function_exists( 'pll_save_post_translations' ) || 'animal' !== get_post_type( $value ) ) {
return true;
}
if ( ! current_user_can( 'edit_post', $value ) ) {
return new WP_Error( 'rest_forbidden', 'Cannot edit the linked animal.', array( 'status' => 403 ) );
}
$translations = pll_get_post_translations( $value );
$translations[ pll_get_post_language( $post->ID ) ] = $post->ID;
pll_save_post_translations( $translations );
// The new translation inherits the shared data (status, dates, photos…).
foreach ( ga_fields() as $key => $field ) {
if ( ! empty( $field['shared'] ) && '' === ga_get( $post->ID, $key ) && '' !== ga_get( $value, $key ) ) {
update_post_meta( $post->ID, "_ga_$key", ga_get( $value, $key ) );
}
}
if ( ! has_post_thumbnail( $post->ID ) && has_post_thumbnail( $value ) ) {
set_post_thumbnail( $post->ID, get_post_thumbnail_id( $value ) );
}
return true;
},
'schema' => array( 'type' => array( 'integer', 'null' ), 'description' => 'ID of the same animal in the other language.', 'context' => array( 'view', 'edit' ) ),
) );
// Material needs list (Personalizar → Ayuda material), per language.
register_rest_route( 'bilagun/v1', '/needs', array(
array(
'methods' => 'GET',
'permission_callback' => '__return_true',
'callback' => fn() => array(
'es' => ga_lines_of( get_theme_mod( 'ga_material_list', ga_default_needs( 'es' ) ) ),
'eu' => ga_lines_of( get_theme_mod( 'ga_material_list_eu', ga_default_needs( 'eu' ) ) ),
),
),
array(
'methods' => 'PUT',
'permission_callback' => fn() => current_user_can( 'edit_others_posts' ),
'args' => array(
'es' => array( 'type' => 'array', 'items' => array( 'type' => 'string' ) ),
'eu' => array( 'type' => 'array', 'items' => array( 'type' => 'string' ) ),
),
'callback' => function ( WP_REST_Request $req ) {
foreach ( array( 'es' => 'ga_material_list', 'eu' => 'ga_material_list_eu' ) as $lang => $mod ) {
if ( null !== $req->get_param( $lang ) ) {
$items = array_filter( array_map( 'sanitize_text_field', (array) $req->get_param( $lang ) ) );
set_theme_mod( $mod, implode( "\n", $items ) );
}
}
return array(
'es' => ga_lines_of( get_theme_mod( 'ga_material_list', '' ) ),
'eu' => ga_lines_of( get_theme_mod( 'ga_material_list_eu', '' ) ),
);
},
),
) );
register_rest_field( 'animal', 'profile', array(
'get_callback' => fn( $post ) => ga_animal_api_data( $post['id'] ),
'update_callback' => fn( $value, $post ) => ga_animal_api_update( (array) $value, $post->ID ),