BEAR
Un editor di massa in stile foglio di calcolo per i prodotti WooCommerce, con un server MCP che permette agli agenti AI di modificare il negozio in sicurezza.
- Prima versione
- Installazioni attive
- 30.000+
- Valutazione
- Leggi le recensioni
Chi sono
BEAR mostra il catalogo WooCommerce come un'unica tabella modificabile e cambia migliaia di prodotti in una volta: prezzi, giacenze, SKU, tassonomie, attributi, variazioni e meta personalizzati. Ogni modifica passa da un unico percorso di scrittura dei campi che usa gli oggetti prodotto di WooCommerce per le proprietà del prodotto, viene registrata con il valore precedente e può essere annullata dalla scheda Cronologia.
La parte difficile non è la tabella. È rendere corretta una scrittura di massa su un negozio reale: valori memorizzati in cinque posti diversi, prodotti variabili i cui prezzi e le cui giacenze stanno sulle variazioni, effetti collaterali di WooCommerce come lo stato di magazzino derivato dalla quantità in stock, e hosting condiviso che interrompe una richiesta PHP dopo 30 secondi.
Dal 2026 il plugin esegue anche un server MCP. Un agente AI può trovare i prodotti, vedere in anteprima una modifica, applicarla a porzioni e annullarla, attraverso gli stessi motori di filtro, modifica di massa e cronologia usati dalla schermata, protetto da chiavi, da un token di sessione confermato (facoltativo per la chiave valida per tutto il negozio, sempre attivo per le chiavi personali) e da una mappa di permessi per settore.
La versione gratuita e quella premium sono un'unica base di codice attivata da un solo flag; la versione gratuita limita la dimensione della pagina, i campi meta, la profondità della cronologia e la frequenza di scrittura MCP.
BEAR · editor di massa
cosa è stato risolto
Griglia prodotti e modello dei campi
shipped · 2018Cosa offreIl personale può vedere e modificare qualsiasi dettaglio di prodotti e variazioni in una schermata simile a un foglio di calcolo, anche su cataloghi troppo grandi per essere aperti in una volta.
- Problema
- Una sola schermata deve mostrare e modificare qualsiasi valore di un catalogo di qualsiasi dimensione: campi del post, proprietà WooCommerce, tassonomie, attributi, download e meta arbitrari, per i prodotti e per le loro variazioni.
- Soluzione
Un registro dichiarativo dei campi governa ogni colonna e ogni scrittura, e la tabella è paginata sul server, una pagina di WP_Query per richiesta AJAX.
- Stack
I cataloghi grandi non possono essere caricati tutti insieme nel browserPaginazione lato server: ogni pagina della tabella è una WP_Query con offset, ordinamento e filtri
woobe_get_products() riceve start, length e order da DataTables e li passa a WOOBE_PRODUCTS::gets(), che costruisce la WP_Query. L'ordinamento per una colonna meta passa a meta_value_num per i tipi number, timestamp e unix, altrimenti a meta_value.
Nel modello delle impostazioni la dimensione della pagina è limitata a 100 righe, con il commento che pagine più grandi causavano HTTP 500 sui server deboli; la versione gratuita è fissa a 10. Quando l'opzione delle variazioni è attiva, le variazioni di ogni prodotto variabile vengono interrogate e inserite come righe subito dopo il loro genitore.
Ogni tipo di colonna è memorizzato in modo diverso in WooCommerceUna voce di registro per colonna: tipo di archiviazione, tipo di valore, editor, sanitizzatore, permessi
woobe_get_fields() descrive ogni colonna: field_type (field, meta, prop, taxonomy, attribute, downloads, gallery, upsells e altri), tipo di valore, vista di modifica, sanitizzatore, tipi di prodotto a cui non si applica e visibilità per i shop manager. Gli attributi globali e tutte le tassonomie dei prodotti vengono aggiunti a runtime da wc_get_attribute_taxonomies() e get_object_taxonomies().
update_page_field() smista in base a field_type: update_post_meta per i meta, i setter di WC_Product più save() per le proprietà, wp_set_post_terms per le tassonomie, elenchi WC_Product_Attribute ricostruiti per gli attributi. Le celle della griglia, i ripristini dalla cronologia e la maggior parte delle scritture di massa finiscono in questa funzione. Le modifiche di massa a tassonomie e attributi scrivono direttamente i termini e attivano lo stesso hook della cronologia prima di scrivere.
Le chiavi meta personalizzate vanno distinte da quelle interne di WooCommerceUna sottoclasse di WC_Product_Data_Store_CPT espone il suo elenco protetto di chiavi interne
woobe_meta_get_keys() legge tutte le chiavi meta di un prodotto campione e sottrae le chiavi interne di WooCommerce. Quell'elenco è una proprietà protetta del data store dei prodotti, quindi WOOBE_PDS_CPT estende il data store solo per restituirla, aggiungendo _button_text e _product_url.
Le chiavi meta vengono ripulite dagli spazi ma non convertite in minuscolo né sanitizzate, perché le chiavi reali scritte da altri plugin, come _woocs_sale_price_USD, distinguono maiuscole e minuscole.
Un campo meta tipizzato deve funzionare in colonne, filtri e modifica di massaUna definizione di meta viene mappata tramite hook nel registro, nel pannello dei filtri e nel pannello della modifica di massa
woobe_extend_fields() trasforma ogni definizione di meta salvata in una colonna; il tipo number riceve il sanitizzatore floatval e l'ordinamento numerico. La stessa definizione alimenta woobe_filter_numbers (intervallo da/a), woobe_filter_text (LIKE, =, !=, NOT LIKE, vuoto, non vuoto), i filtri a interruttore e woobe_bulk_number (imposta, aumenta o diminuisci per valore o per percentuale).
Una colonna a interruttore può memorizzare valori diversi da 1/0 (per esempio yes/no) tramite una regola di override per chiave letta da over_switcher_swicher_to_val(). La versione gratuita mantiene i primi 2 campi meta.
Un meta con array serializzato non ha una cella piatta da modificareL'editor in popup invia chiavi e valori come array paralleli, ricostruiti in array PHP annidati
process_jsoned_meta_data() ricostruisce un valore meta dalle coppie keys/values per gli array e da keys2/values2 per gli oggetti, decodificando i valori JSON annidati, e ripiega sul valore grezzo per stringhe e numeri semplici. La cronologia memorizza tali valori come JSON con JSON_HEX_QUOT e JSON_HEX_TAG, in modo che sopravvivano al passaggio di andata e ritorno.
Lo stato dell'editor per utente deve sopravvivere allo svuotamento dell'object cacheUna riga di opzione senza autoload per utente, con un TTL per chiave, al posto dei transient
I filtri e le configurazioni di modifica di massa vengono mantenuti tra le richieste da WOOBE_STORAGE. Con Redis o Memcached un transient vive solo nell'object cache e va perso a ogni svuotamento, quindi l'archiviazione predefinita è una riga di opzione woobe_memory_<user id>: ogni chiave ha la propria scadenza, le chiavi scadute vengono eliminate alla scrittura, l'autoload è disattivato e la riga viene cancellata insieme all'utente. Le modalità sessione, transient e cookie restano disponibili tramite il filtro woobe_storage_type.
Filtri
shipped · 2018Cosa offreIl personale può scegliere esattamente i prodotti che servono, anche per prezzo o giacenza della variazione, e la stessa selezione viene usata per la modifica e per l'esportazione.
- Problema
- I filtri su prezzi, giacenze, dimensioni, testo e tassonomie devono selezionare esattamente i prodotti giusti, compresi i prodotti variabili i cui valori stanno sulle variazioni, e lo stesso insieme deve essere riutilizzato da paginazione, modifica di massa ed esportazione.
- Soluzione
Il filtro viene salvato sul server sotto una chiave e compilato a ogni query in clausole tax_query, meta_query e posts_where tramite un unico hook di WP_Query.
- Stack
Le richieste di paginazione, modifica di massa ed esportazione devono vedere lo stesso insieme filtratoFiltro salvato con una chiave casuale a ogni esecuzione; un hook di WP_Query lo ricostruisce a ogni richiesta
woobe_filter_products() memorizza il modulo del filtro analizzato in woobe_filter_<key>, dove la chiave è una stringa casuale generata a ogni filtraggio, così la modifica di massa per filtro funziona in più schede del browser contemporaneamente. Ogni chiamata a WOOBE_PRODUCTS::gets() passa i propri argomenti da woobe_apply_query_filter_data(), che legge il filtro salvato e restituisce tax_query, meta_query, post__in e post_status, e collega gli hook posts_where per il resto.
La paginazione della griglia, il conteggio della modifica di massa, il conteggio dell'esportazione e gli strumenti per le variazioni chiamano tutti gets(), quindi non possono discordare su quali prodotti un filtro indichi.
Prezzo e giacenza stanno sulle variazioni, ma la griglia elenca i genitoriQuery per intervallo su prodotti e variazioni, poi le variazioni trovate vengono ricondotte ai loro genitori
regular_price_where(), sale_price_where(), stock_quantity_where() e dimensions_where() interrogano postmeta sia per product sia per product_variation, poi leggono post_parent delle variazioni corrispondenti e limitano la query principale ai genitori più le variazioni con ID IN (...).
Quando nulla corrisponde, la clausola diventa ID = -1, così una corrispondenza vuota non restituisce alcun prodotto invece dell'intero catalogo, il che conta a un clic da una modifica di massa.
I numeri memorizzati come stringhe meta vengono confrontati male in MySQLCAST a DECIMAL con scala esplicita; stringhe vuote escluse da ogni intervallo
Gli intervalli numerici personalizzati usano meta_query di tipo DECIMAL(30,20): un DECIMAL semplice ha scala 0, quindi una ricerca da 100 a 150 troverebbe anche 99.xx. L'SQL dei prezzi esegue il cast a DECIMAL(30,10).
Un peso vuoto è memorizzato come '' e MySQL legge '' come 0, quindi gli intervalli di prezzo e dimensioni richiedono meta_value <> '' e una casella vuota non vale mai 0. L'estremo mancante di un intervallo è aperto (sostituito da -999999999 o 999999999), e da uguale a a significa esattamente quel valore.
Gli utenti vogliono AND, OR e NOT in un unico campo di ricerca testualePiccola sintassi di ricerca (^ OR, ^~ AND, ^! NOT) compilata in clausole MySQL REGEXP
posts_txt_where() divide il testo su ^; un prefisso ~ unisce la parte con AND, un prefisso ! la trasforma in NOT REGEXP dentro un gruppo AND separato. I comportamenti exact, begin, end, not ed empty corrispondono a = e a REGEXP ancorati; le parentesi quadre e graffe delle regex vengono sottoposte a escape. Si applica a titolo, contenuto, riassunto e slug.
Il filtro SKU accetta un elenco separato da virgole e costruisce condizioni LIKE preparate con esc_like, OR per le modalità di corrispondenza e AND per 'not'.
Per corrispondere a tutti i termini di un insieme serve una clausola tax_query per termineAND diviso in una clausola per termine; gli operatori sconosciuti ripiegano su IN
Per l'operatore AND ogni termine diventa una propria clausola tax_query con relation AND, perché una singola clausola con operatore AND può non restituire alcun prodotto. Gli operatori sono limitati a una lista consentita: IN, NOT IN, AND, EXISTS e NOT EXISTS; WordPress scarta una clausola con operatore sconosciuto e corrisponderebbe a ogni prodotto.
La visibilità nel catalogo viene tradotta in combinazioni dei termini product_visibility exclude-from-catalog ed exclude-from-search, e un filtro sullo stato di magazzino limita anche product_type ai tipi che hanno uno stato di magazzino.
Motore di modifica di massa
shipped · 2018Cosa offreLe modifiche a prezzi, giacenze e variazioni su migliaia di prodotti si concludono in modo affidabile su hosting ordinari e seguono le regole di prezzo e di magazzino del negozio.
- Problema
- Una modifica di massa può toccare migliaia di prodotti su hosting con limiti di tempo PHP brevi, e ogni scrittura deve rispettare le regole di WooCommerce per prezzi, giacenze e variazioni.
- Soluzione
Gli ID di destinazione vengono risolti una sola volta, poi scritti in piccoli lotti AJAX attraverso lo stesso percorso di scrittura del singolo campo usato dalla griglia, che porta con sé le regole di WooCommerce.
- Stack
Una modifica di massa di migliaia di prodotti raggiunge i limiti di tempo di PHPID recuperati una volta sola, poi inviati in lotti di N per richiesta AJAX; N è un'impostazione
woobe_bulk_products_count() memorizza la configurazione della modifica di massa sotto un bulk_key casuale di 16 caratteri e restituisce ogni ID filtrato (fields=ids, senza paginazione). __woobe_bulk_products() in bulk.js divide quell'elenco in porzioni e invia un lotto dopo l'altro, aggiorna la barra di avanzamento e chiama woobe_bulk_finish alla fine; l'utente può interrompere la richiesta in corso.
La dimensione del lotto è number_posts_processed, da 10 a 100 per richiesta, 30 per impostazione predefinita, così un server debole può scambiare velocità con stabilità.
I prodotti modificati escono dal filtro mentre l'esecuzione è in corsoElenco di ID congelato: ogni lotto porta gli ID dell'istantanea, non una query rieseguita
Paginare la query filtrata tra un lotto e l'altro farebbe saltare dei prodotti: la modifica di un prezzo sposta un prodotto fuori da un filtro sul prezzo e tutti gli offset successivi scorrono. L'elenco di ID preso all'inizio è conservato dal client e ogni richiesta di lotto invia esplicitamente i propri ID, senza la chiave del filtro.
Al termine dell'esecuzione, le righe selezionate che non compaiono più nella tabella ridisegnata vengono rimosse dalla selezione, così l'azione successiva non riguarda prodotti che l'utente non vede più.
I comportamenti numerici devono combinarsi con formule e arrotondamento dei prezziPipeline per prodotto: comportamento, formula con un altro meta, intervallo casuale, arrotondamento
_process_number_data() applica imposta, aumenta o diminuisci per valore o percentuale, e regole tra campi come prezzo in offerta = prezzo di listino meno una percentuale. Poi una formula facoltativa con un altro campo meta (+, -, *, / con protezione dalla divisione per zero), poi un importo casuale facoltativo in un intervallo con decimali impostati.
update_page_field() arrotonda nei decimali di prezzo del negozio: a unità intere, per eccesso a 0.05, a 0.1, oppure a una terminazione fissa in centesimi come .99. I campi di testo supportano accoda, anteponi, sostituisci (con o senza distinzione tra maiuscole e minuscole) e segnaposto come {TITLE}, {SKU}, {meta:key} e {attribute:pa_size}.
Il prezzo in offerta deve restare sotto il prezzo di listino e le date devono avere effettoOfferta >= listino saltata, offerta <= 0 la rimuove; _price ricalcolato quando cambiano le date
In un'esecuzione di massa, un prezzo in offerta calcolato che raggiunge il prezzo di listino viene saltato e un valore pari o inferiore a zero elimina l'offerta. Una singola scrittura di un prezzo in offerta uguale o superiore al listino viene ridotta al listino meno la più piccola unità di prezzo.
Quando vengono scritte le date dell'offerta, _price viene impostato subito sul prezzo valido ora (offerta non iniziata, in corso, terminata) e i transient del prodotto vengono cancellati, invece di attendere il job pianificato delle offerte di WooCommerce.
Le scritture di giacenza e prezzo non devono danneggiare i prodotti variabili né le giacenze condiviseGenitori variabili e variazioni con giacenza del genitore lasciati intatti; giacenza conservata tra un'attivazione e l'altra
WooCommerce legge i prezzi di un prodotto variabile dalle sue variazioni, quindi un prezzo scritto sul genitore viene saltato e una scrittura di cancellazione rimuove qualsiasi prezzo residuo del genitore. Una giacenza pari o inferiore a 0 di norma attiva la gestione del magazzino; ciò non avviene su un genitore variabile che non tiene traccia della giacenza, dove metterebbe esaurita ogni variazione priva di giacenza propria, né su una variazione che vende dalla giacenza del genitore, che verrebbe così scollegata.
WooCommerce azzera quantità, ordini arretrati e soglia di scorta bassa quando la gestione del magazzino viene disattivata. keep_stock_on_switch() li salva in un proprio meta e li ripristina quando la gestione viene riattivata.
Le variazioni vanno create e modificate per combinazione di attributiProdotto cartesiano dei termini scelti, senza duplicati; modifiche abbinate per combinazione con carattere jolly
generate_combinations() costruisce il prodotto cartesiano dei termini di attributo selezionati. Applicandolo, gli attributi vengono collegati come attributi di variazione, si saltano le combinazioni che un prodotto ha già, si copiano i prezzi del genitore, a ogni nuova variazione si assegna lo SKU parent-SKU-<id> per evitare duplicati e le variazioni vengono riordinate tramite il data store.
Modificare, eliminare ed esportare un sottoinsieme di variazioni confronta gli attributi di ciascuna variazione con le combinazioni scelte: chiavi e valori ordinati, voci '-1' trattate come qualsiasi valore, ed entrambi i lati decodificati dall'URL perché gli slug degli attributi non latini sono memorizzati codificati.
Cronologia e ripristino
nel prodottoCosa offreUna modifica sbagliata, a mano, di massa o da un assistente AI, può essere annullata invece di essere corretta prodotto per prodotto.
- Problema
- Ogni modifica, a mano, di massa o da un agente AI, deve poter essere annullata, compresi gli effetti collaterali che WooCommerce applica da sé, e il ripristino di una grande esecuzione incontra gli stessi limiti di tempo dell'esecuzione.
- Soluzione
I valori precedenti vengono registrati per campo in due tabelle personalizzate da un hook sul percorso di scrittura unico, e ripristinati a porzioni attraverso lo stesso percorso.
- Stack
Registrare i valori precedenti per qualsiasi tipo di campo senza codice specifico per funzioneUn hook prima di ogni scrittura di campo; valore precedente memorizzato per campo in una tabella personalizzata
update_page_field() attiva woobe_before_update_page_field e WOOBE_HISTORY::write() legge il valore corrente e lo codifica per tipo: ID dei termini, attributi, galleria e relazioni come JSON, download come nome/file/hash, stato di magazzino come 1/0, date delle offerte come date ISO con il proprio offset di fuso orario, così un ripristino riporta l'esatta pianificazione.
Le righe contengono field_key, product_id, prev_val, mod_date, bulk_key, user_id e via_mcp, indicizzate per prodotto, bulk key e utente. Una seconda tabella conserva una testata per ogni esecuzione di massa: stato (completed o terminated), inizio, fine, numero di prodotti e chiavi dei campi.
WooCommerce cambia lo stato di magazzino come effetto collaterale delle modifiche alla giacenzaEffetti collaterali scritti come righe aggiuntive della stessa modifica e ripristinati insieme, in ordine
Attivare la gestione del magazzino, o una giacenza di 0 su un prodotto non tracciato, fa sì che WooCommerce ricavi lo stato di magazzino; disattivare la gestione elimina anche la quantità. update_page_field() registra stock_status, e stock_quantity quando serve, come righe aggiuntive subito dopo il campo modificato.
stock_status_pair() e stock_row_at() trovano queste righe tramite ID adiacenti (+1, +2) e richiedono lo stesso prodotto, autore, canale (a mano o tramite agente) e bulk key, e un tempo di scrittura ravvicinato. revert() ripristina poi la quantità prima dello stato, perché WooCommerce ricava lo stato dalla quantità finché la giacenza è gestita. Una giacenza precedente vuota significa 'non tracciata', quindi untrack_stock() disattiva la gestione invece di scrivere 0.
Anche il ripristino di un'esecuzione di massa di migliaia di righe va in timeoutRipristino a porzioni per bulk_key; ogni riga ripristinata viene eliminata; testata rimossa alla fine
Nella scheda Cronologia, woobe_history_revert_bulk_portion() ripristina 10 righe per richiesta AJAX con una barra di avanzamento, e la riga di testata viene eliminata quando il contatore raggiunge il totale. Ogni ripristino elimina la propria riga, quindi la porzione successiva prende semplicemente ciò che resta.
Lo strumento di rollback MCP è un percorso separato: revert_bulk_portion() ripristina fino a 200 righe per chiamata e riporta le righe effettivamente rimosse, perché le due righe di una modifica di giacenza vengono ripristinate insieme e una porzione può prendere una riga in più del suo limite.
revert() rimuove prima tutti i listener di woobe_before_update_page_field, così le scritture del ripristino stesso non vengono registrate come nuova cronologia.
Più utenti e agenti AI condividono un'unica cronologiaUna sola regola scope(): gli amministratori vedono tutte le righe, gli altri le proprie; applicata sul server
Ogni riga porta user_id e via_mcp. La chiave MCP valida per tutto il negozio scrive come utente -777, che non può mai collidere con un utente reale né con 0 (non connesso). scope() restituisce un'unica condizione SQL usata da ogni elenco, ripristino, eliminazione e svuotamento, così nessuna query può discostarsi dalla regola.
I gestori controllano solo_in_scope() o bulk_in_scope() e rispondono 403 a una richiesta costruita a mano per una riga che l'utente non può vedere. I nomi degli autori per l'elenco vengono letti con un'unica query.
Le installazioni esistenti avevano bisogno di una nuova colonna senza rompere le vecchie righeOpzione di schema versionata, ALTER protetto da un controllo della colonna, riempimento retroattivo delle righe degli agenti
CREATE TABLE IF NOT EXISTS non modifica mai una tabella già esistente. maybe_upgrade_schema() viene eseguita una volta per versione dello schema (ora la 2), controlla SHOW COLUMNS prima di ogni ALTER perché una richiesta concorrente non fallisca, riprova al caricamento successivo se ALTER fallisce, e segna le vecchie righe dell'utente -777 come scritte tramite un agente.
La versione gratuita conserva una cronologia breve senza toccare gli altri utentiPotatura per autore allo shutdown: ultime 2 modifiche singole e 2 di massa, righe di giacenza conservate
Nella versione gratuita prune_author() conserva le due modifiche singole più recenti e le due esecuzioni di massa più recenti di un autore. Le righe che appartengono a una modifica di giacenza conservata restano con essa tramite stock_change_rows(). La potatura viene eseguita allo shutdown dopo una scrittura, e aprire la scheda non elimina mai le righe di un altro utente.
Esportazione
nel prodottoCosa offreGli elenchi di prodotti filtrati vengono esportati in file che il negozio può reimportare, costruiti a parti sui server deboli e mai lasciati aperti sul web.
- Problema
- L'esportazione di un catalogo filtrato con variazioni deve procedere a parti sui server deboli, produrre file che WooCommerce può reimportare e non lasciare dati di prodotto aperti sul web.
- Soluzione
L'esportazione procede come una sequenza di richieste AJAX che aggiungono dati a un solo file il cui nome è derivato per ogni esportazione, conservato in una cartella chiusa e servito tramite un gestore autorizzato.
- Stack
Una grande esportazione non può essere costruita in una sola richiestaPrima l'intestazione, poi le righe aggiunte per lotto AJAX allo stesso file
woobe_export_products_count() cancella i file precedenti, scrive l'intestazione CSV o lo scheletro XML e restituisce gli ID filtrati. woobe_export_products() aggiunge un lotto per richiesta con fopen(a+) e fputcsv, perché WP_Filesystem non può scrivere in streaming riga per riga; per l'XML carica il documento WXR e aggiunge i nodi item.
Le variazioni vengono aggiunte dopo il loro genitore, facoltativamente senza quelle esaurite, e possono essere limitate o escluse tramite combinazioni di attributi.
Il CSV deve poter essere caricato nell'importatore di prodotti di WooCommerceAttributi su quattro colonne ciascuno, download come coppie nome/URL, qualsiasi valore appiattito
get_csv_titles() scrive 'Attribute N value(s)', 'name', 'visible' e 'global' per ogni attributo e 'Download N name' e 'URL' per ogni slot di file, ossia le colonne lette dall'importatore di WooCommerce; le colonne dei meta si chiamano 'Meta: key'.
flatten_export_value() riduce array e oggetti (termini, attributi, oggetti dati di WC) a scalari uniti con '|', così un tipo di colonna che il plugin non ha mai visto, anche di terze parti, non scrive la parola 'Array'.
I file di esportazione contengono prezzi e URL di file e non devono essere pubbliciCartella chiusa da .htaccess, nome del file con coda HMAC, download tramite gestore controllato
Il suffisso del nome del file deve corrispondere a un timestamp al minuto e riceve una coda di 12 caratteri da hash_hmac('sha256', postfix, wp_salt('auth')): identica per ogni richiesta di una stessa esportazione, così tutti i lotti finiscono in un solo file, ma non indovinabile dall'esterno. La coda conta su nginx, che ignora la regola deny di .htaccess.
woobe_export_download() controlla nonce e capability, accetta solo i nomi che l'estensione genera, svuota tutti i buffer di output e trasmette in streaming con readfile() senza Content-Length. I file più vecchi di 8 ore vengono eliminati, con un controllo al massimo una volta all'ora.
Un agente AI ha bisogno di un link di download senza esporre la propria chiaveRoute REST protetta da un token monouso per un singolo file di 32 caratteri esadecimali che scade dopo 15 minuti
Un clic nel browser non può inviare un'intestazione Authorization, e la chiave MCP viene tenuta fuori dagli URL. Lo strumento di esportazione memorizza il nome del file sotto un token casuale in un transient con durata di 900 secondi, e /woobe/v1/mcp/export/<token> serve solo quel file.
Server MCP per agenti AI
shipped · 2026Cosa offreUn assistente AI può modificare il negozio per conto del titolare, ma solo su una selezione confermata, con un'anteprima prima e ogni modifica reversibile.
- Problema
- Un agente AI lavora attraverso un'API di rete senza schermo, può interpretare male un filtro o un conteggio e può emettere una scrittura sull'intero catalogo; deve comunque essere sicuro, reversibile e rientrare nei limiti di tempo di PHP.
- Soluzione
Gli strumenti riutilizzano i motori di filtro, modifica di massa e cronologia dell'editor; le scritture accettano solo selezioni congelate confermate dal loro conteggio, possono essere verificate prima con uno strumento di anteprima in prova (dry-run) e procedono a porzioni riprendibili.
- Stack
Un filtro interpretato male non deve estendere una scrittura all'intero catalogoLe scritture accettano solo un selection_id congelato più confirm_count uguale alla sua dimensione
woobe_find_products esegue il filtro una volta e memorizza gli ID in un'opzione senza autoload (fino a 200,000 ID, validi 24 ore). La risposta contiene selection_id, count, un campione e whole_catalogue, misurato rispetto a una query non filtrata, così un filtro che non restringe nulla viene segnalato come tale.
woobe_apply_bulk non accetta alcun filtro. Rifiuta l'operazione a meno che confirm_count sia uguale alla dimensione della selezione, così un agente che non ha letto il numero non può indovinarlo. I prodotti creati dopo la ricerca non fanno parte della selezione.
Il filtro di un agente va rifiutato, non ignorato, quando il motore non può applicarloUn validatore rigoroso rifiuta ogni chiave o forma che il motore non applicherebbe, e dice perché
Il motore dei filtri legge i campi del modulo dell'editor e ignora qualsiasi altra chiave. Un agente può inviare una chiave scritta male o una data come {from, to}, e una condizione ignorata allargherebbe la selezione. WOOBE_MCP_FILTER controlla ogni chiave rispetto all'elenco dei campi che il motore legge, rifiuta indicando la chiave e la forma accettata, e consegna il resto nella forma inviata dalla schermata.
Il motore collega hook posts_where che restano per il resto della richiesta, che sulla schermata equivale a un caricamento di pagina ma può essere composta da più ricerche in un solo batch JSON-RPC. release_filter_hooks() li rimuove dopo ogni query MCP, così una ricerca non eredita mai le condizioni di un'altra. I tipi degli argomenti vengono controllati rispetto all'inputSchema di ciascuno strumento, quindi la stringa 'maybe' non è una conferma.
L'anteprima deve mostrare ciò che la scrittura produrrà davveroLa prova (dry run) rispecchia l'aritmetica della modifica di massa e concatena in ordine le operazioni di un'esecuzione
simulate() copia letteralmente _process_number_data(), e run_chain() passa il risultato di ogni operazione alla successiva, così un prezzo in offerta impostato insieme a un nuovo prezzo di listino viene verificato rispetto a quello nuovo. L'aritmetica viene applicata solo ai campi numerici su cui il motore calcola; qualsiasi altro campo è mostrato come semplice sostituzione.
L'anteprima restituisce tre conteggi (selezione, interessati dopo l'espansione dei genitori alle variazioni, scrivibili) e avvisi: percentuale di un campo vuoto saltata, genitori variabili senza un prezzo proprio, giacenza non tracciata.
Una chiamata REST ha lo stesso limite di tempo di un lotto AJAXPorzioni di 25 tramite do_bulk, 20 s per chiamata, riprese con bulk_key e next_offset
Il ciclo di applicazione divide la coda in porzioni di 25 e chiama WOOBE_BULK::do_bulk(), il motore usato dalla schermata, finché non sono trascorsi 20 secondi. Restituisce next_offset e bulk_key; una continuazione scrive nella stessa testata della cronologia, così un solo rollback annulla l'intero lavoro. Le cache vengono svuotate una volta dopo le scritture, non a ogni porzione.
Nella versione gratuita la porzione si riduce alla quota di scrittura residua: un token bucket di 100 prodotti che si ricarica in modo continuo in 30 minuti, con token frazionari e un tetto rigido, così il tempo di inattività non può pagare una grande esecuzione.
Le variazioni non portano nessuno dei termini o dei meta su cui il filtro cercaFigli aggiunti dopo la query: tutti, oppure solo quelli che corrispondono a ogni attributo indicato
include_variations 'all' aggiunge ogni figlio dei genitori trovati; 'matching' mantiene i figli i cui valori di attributo corrispondono a tutti i termini pa_ del filtro ('giallo e piccolo' è una sola variazione, non l'unione), con un valore vuoto che vale 'qualsiasi'. Categorie e tag non possono restringere i figli.
Le chiavi degli attributi sono costruite con sanitize_title(), come le costruisce WooCommerce, perché i nomi di attributo non latini sono memorizzati con codifica URL e sanitize_key() li eliminerebbe. In modalità matching i genitori variabili vengono rimossi dalla selezione, così un successivo variations_only non li espande di nuovo a tutte le variazioni.
Dentro una chiamata REST il plugin e il negozio si comportano diversamente dall'area di amministrazioneAvvio proprio su rest_api_init, filtri di prezzo delle valute rimossi, termini dei genitori compilati
WOOBE::init() viene eseguita su init, prima che REST_REQUEST sia definita, quindi in una chiamata REST non vede né l'admin né REST e non farebbe nulla. bootstrap.php si aggancia da sé a rest_api_init e avvia il plugin; is_should_init() consente REST solo per le route del plugin, non per tutto il traffico REST del sito.
I selettori di valuta filtrano i getter dei prezzi, quindi l'agente leggeva prezzi convertiti; strip_price_filters() rimuove quei filtri solo per la richiesta MCP. Un prodotto inserito in una sottocategoria viene inserito anche nelle categorie antenate tramite un listener di set_object_terms, dato che in una chat nessuno spunta le caselle dei genitori.
Controllo degli accessi e sicurezza
shipped · 2026Cosa offreSolo le persone giuste possono modificare i prodotti; con il secondo fattore facoltativo attivato, una chiave di accesso AI trapelata da sola non apre il negozio, e l'accesso può essere limitato per area.
- Problema
- Un editor di massa ha accesso in scrittura a ogni prodotto. Ogni gestore AJAX, la cronologia e l'endpoint MCP devono verificare chi agisce, e una chiave MCP conservata nelle impostazioni di un assistente può trapelare.
- Soluzione
Un'unica guardia per tutti i gestori AJAX con controlli di proprietà per prodotto; per MCP, chiavi solo nelle intestazioni confrontate in tempo costante, un token di sessione confermato (facoltativo per la chiave valida per tutto il negozio, sempre attivo per le chiavi personali), chiavi personali e una mappa di permessi per settore.
- Stack
Decine di gestori AJAX avevano ciascuno i propri controlli parzialiGuardia unica: nonce, capability, poi edit_post per prodotto, tramite il genitore per le variazioni
Dopo una segnalazione di WPScan l'intero insieme dei gestori AJAX è stato sottoposto ad audit, non solo quelli segnalati, e ogni gestore è stato spostato su WOOBE_HELPER::check_ajax_access(). Questa controlla il nonce, poi la capability, poi current_user_can('edit_post') per ogni ID di prodotto.
Le variazioni vengono controllate rispetto al prodotto genitore: WooCommerce mappa edit_post su una variazione a un semplice edit_products, che ignora la proprietà, e le variazioni hanno post_author 0. get_request_array() rifiuta uno scalare dove è atteso un array invece di convertirlo. I ruoli non amministratori sono inoltre limitati per campo.
Una chiave API statica trapela da URL, log e tempi di rispostaChiave solo nelle intestazioni, hash_equals, ogni richiesta controllata, nessuna route quando MCP è spento
Con MCP spento, register_routes() ritorna prima di registrare qualsiasi cosa, quindi l'endpoint non esiste. permission() richiede la chiave a ogni richiesta, compreso l'handshake MCP, così un chiamante non autenticato non apprende nulla sull'endpoint. La chiave viene letta da Authorization: Bearer o X-WOOBE-KEY, con il ripiego Apache REDIRECT_HTTP_AUTHORIZATION, e confrontata con hash_equals.
I rifiuti usano 403, non 401: in MCP un 401 avvia la scoperta OAuth e il client segnala il server come guasto. I link di download e di upload usano propri token monouso perché un clic nel browser non può inviare un'intestazione.
Una chiave trapelata da sola non dovrebbe dare accesso al negozioSecondo fattore: un token di sessione confermato in WordPress, memorizzato come hash, scadenza per inattività di 1 h
L'assistente chiede un token, composto da quattro gruppi di otto lettere e cifre generati con random_int, e questo viene confermato da una persona connessa a WordPress: un amministratore per la chiave valida per tutto il negozio, il proprietario della chiave nelle proprie impostazioni per una chiave personale. Per la chiave valida per tutto il negozio questo secondo fattore è un'impostazione; per le chiavi personali è sempre attivo. Viene memorizzato solo il suo hash sha256 con gli orari di conferma e di ultimo utilizzo. check_connection() confronta con hash_equals e chiude la connessione dopo 3600 secondi senza chiamate; l'orario dell'ultimo utilizzo viene scritto al massimo una volta al minuto.
Le connessioni sono per proprietario: un token confermato per una chiave personale viene rifiutato con qualsiasi altra chiave.
Le chiavi per utente non devono rivelare quali utenti hanno accessoChiave memorizzata sempre letta e confrontata, con un valore fittizio di pari lunghezza quando non esiste
Una chiave personale è woobe_<40 hex>-<user id>. personal_key_owner() legge e confronta sempre la chiave memorizzata con hash_equals, usando un valore fittizio della stessa lunghezza quando l'utente non ne ha una, e interroga l'elenco delle autorizzazioni e la capability solo dopo una corrispondenza completa, così il tempo di risposta non dice a un estraneo chi ha accesso. Gli ID con zeri iniziali prendono il percorso del valore fittizio.
La richiesta viene poi eseguita come quell'utente tramite wp_set_current_user(), così il suo ruolo e la visibilità dei campi si applicano esattamente come sulla schermata, e la connessione a due fattori è sempre attiva per le chiavi personali.
I titolari hanno bisogno di accesso in sola lettura o nessun accesso per area del negozioMappa dei settori full/read/none per utente, ristretta dalle capability di WP; strumenti chiusi nascosti
Ogni strumento dichiara un settore e annotations.readOnlyHint. Il titolare restituisce una mappa dal filtro woobe_mcp_permissions; '*' imposta il valore predefinito, quindi funziona come lista consentita. Le voci non valide vengono registrate nel log e ignorate, e un '*' non valido ripiega su full, come nessuna callback. Per una chiave personale ogni settore viene ulteriormente ristretto dalla capability che WooCommerce richiede per quella schermata (edit_products, edit_shop_orders, view_woocommerce_reports e altre).
Gli strumenti che l'identità non può chiamare non vengono affatto elencati. Una scrittura che finisce in un altro settore, come un rollback che riscrive i prodotti, chiede a require_access() quel settore prima di scrivere. Il filtro woobe_mcp_tool_allowed può solo restringere ulteriormente. Nessuna chiave MCP, valida per tutto il negozio o personale, può scrivere post_author o ID; questi si modificano in wp-admin da una persona.
Numeri
wordpress.org- Installazioni attive 30.000+ contatore di wordpress.org.
- Recensioni 227 Media 4.7 su 5.
- Anni di supporto 8 Dal 7 maggio 2018, ultimo aggiornamento 13 ottobre 2026.