'object', 'properties' => $props]; if ($required) $schema['required'] = $required; return ['name' => $name, 'description' => $desc, 'scope' => $scope, 'handler' => 'AskTamMCP\\Extensions\\Batch20::' . $method, 'inputSchema' => $schema]; }; $registry->register($def('wc_list_product_categories', 'List WooCommerce product categories (id, name, slug, parent, count).', ['parent' => ['type' => 'integer'], 'search' => ['type' => 'string'], 'per_page' => ['type' => 'integer', 'default' => 100]], [], 'read', 'listCategories')); $registry->register($def('wc_create_product_category', 'Create a WooCommerce product category.', ['name' => ['type' => 'string'], 'slug' => ['type' => 'string'], 'parent' => ['type' => 'integer'], 'description' => ['type' => 'string']], ['name'], 'write', 'createCategory')); $registry->register($def('wc_list_payment_gateways', 'List WooCommerce payment gateways and whether each is enabled.', [], [], 'read', 'paymentGateways')); $registry->register($def('wc_list_shipping_zones', 'List WooCommerce shipping zones with their regions and methods.', [], [], 'read', 'shippingZones')); $registry->register($def('wc_list_tax_rates', 'List WooCommerce tax rates (country, state, rate, name, class).', ['class' => ['type' => 'string'], 'limit' => ['type' => 'integer', 'default' => 100]], [], 'read', 'taxRates')); $registry->register($def('wc_get_settings', 'Read a group of WooCommerce settings. group = general | products | tax | account | email. Returns the relevant woocommerce_* options.', ['group' => ['type' => 'string', 'enum' => ['general', 'products', 'tax', 'account', 'email']]], ['group'], 'read', 'getSettings')); $registry->register($def('wc_add_order_note', 'Add a note to a WooCommerce order. customer_note=true emails it to the customer.', ['order_id' => ['type' => 'integer'], 'note' => ['type' => 'string'], 'customer_note' => ['type' => 'boolean']], ['order_id', 'note'], 'write', 'addOrderNote')); $registry->register($def('wc_create_refund', 'Create a refund against a WooCommerce order. amount optional (full refund if omitted). reason optional.', ['order_id' => ['type' => 'integer'], 'amount' => ['type' => 'number'], 'reason' => ['type' => 'string'], 'restock' => ['type' => 'boolean']], ['order_id'], 'write', 'createRefund')); $registry->register($def('wc_sales_report', 'Sales report over a date range: order count, gross total, and item count. Dates are YYYY-MM-DD (default last 30 days).', ['date_from' => ['type' => 'string'], 'date_to' => ['type' => 'string'], 'statuses' => ['type' => 'array']], [], 'read', 'salesReport')); $registry->register($def('wc_list_product_reviews', 'List WooCommerce product reviews (rating, author, excerpt, product). Optionally filter by product_id.', ['product_id' => ['type' => 'integer'], 'per_page' => ['type' => 'integer', 'default' => 50]], [], 'read', 'listReviews')); } public static function listCategories(array $args): array { $terms = get_terms(['taxonomy' => 'product_cat', 'hide_empty' => false, 'parent' => isset($args['parent']) ? (int) $args['parent'] : '', 'search' => $args['search'] ?? '', 'number' => (int) ($args['per_page'] ?? 100)]); $out = []; foreach ((array) $terms as $t) { if (!is_object($t)) continue; $out[] = ['id' => $t->term_id, 'name' => $t->name, 'slug' => $t->slug, 'parent' => $t->parent, 'count' => $t->count]; } return ['count' => count($out), 'categories' => $out]; } public static function createCategory(array $args): array { $name = trim((string) ($args['name'] ?? '')); if ($name === '') throw new \RuntimeException('name required.'); $res = wp_insert_term($name, 'product_cat', [ 'slug' => $args['slug'] ?? '', 'parent' => (int) ($args['parent'] ?? 0), 'description' => $args['description'] ?? '', ]); if (is_wp_error($res)) throw new \RuntimeException('Create failed: ' . $res->get_error_message()); return ['id' => $res['term_id'], 'name' => $name]; } public static function paymentGateways(array $args): array { $gw = WC()->payment_gateways() ? WC()->payment_gateways()->payment_gateways() : []; $out = []; foreach ((array) $gw as $id => $g) { $out[] = ['id' => $id, 'title' => $g->get_title(), 'enabled' => ($g->enabled === 'yes'), 'method_title' => $g->get_method_title()]; } return ['count' => count($out), 'gateways' => $out]; } public static function shippingZones(array $args): array { if (!class_exists('WC_Shipping_Zones')) throw new \RuntimeException('Shipping zones API unavailable.'); $zones = \WC_Shipping_Zones::get_zones(); $out = []; foreach ((array) $zones as $z) { $methods = []; foreach ((array) ($z['shipping_methods'] ?? []) as $m) { $methods[] = ['id' => $m->id, 'title' => $m->get_title(), 'enabled' => ($m->is_enabled())]; } $out[] = ['id' => $z['id'] ?? $z['zone_id'] ?? null, 'name' => $z['zone_name'] ?? '', 'regions' => $z['formatted_zone_location'] ?? '', 'methods' => $methods]; } return ['count' => count($out), 'zones' => $out]; } public static function taxRates(array $args): array { global $wpdb; $t = $wpdb->prefix . 'woocommerce_tax_rates'; $limit = max(1, (int) ($args['limit'] ?? 100)); $where = ''; if (!empty($args['class'])) $where = $wpdb->prepare('WHERE tax_rate_class = %s', (string) $args['class']); $rows = $wpdb->get_results("SELECT tax_rate_country, tax_rate_state, tax_rate, tax_rate_name, tax_rate_class FROM {$t} {$where} ORDER BY tax_rate_order LIMIT {$limit}", ARRAY_A); return ['count' => count((array) $rows), 'rates' => array_map(fn($r) => [ 'country' => $r['tax_rate_country'], 'state' => $r['tax_rate_state'], 'rate' => $r['tax_rate'], 'name' => $r['tax_rate_name'], 'class' => $r['tax_rate_class'] ?: 'standard', ], (array) $rows)]; } public static function getSettings(array $args): array { $group = (string) ($args['group'] ?? 'general'); $keys = [ 'general' => ['woocommerce_store_address', 'woocommerce_store_city', 'woocommerce_default_country', 'woocommerce_currency', 'woocommerce_currency_pos', 'woocommerce_price_decimal_sep', 'woocommerce_price_num_decimals'], 'products' => ['woocommerce_weight_unit', 'woocommerce_dimension_unit', 'woocommerce_manage_stock', 'woocommerce_notify_low_stock_amount', 'woocommerce_hide_out_of_stock_items', 'woocommerce_cart_redirect_after_add'], 'tax' => ['woocommerce_calc_taxes', 'woocommerce_prices_include_tax', 'woocommerce_tax_based_on', 'woocommerce_tax_display_shop', 'woocommerce_tax_display_cart'], 'account' => ['woocommerce_enable_guest_checkout', 'woocommerce_enable_checkout_login_reminder', 'woocommerce_enable_myaccount_registration'], 'email' => ['woocommerce_email_from_name', 'woocommerce_email_from_address', 'woocommerce_email_header_image'], ]; if (!isset($keys[$group])) throw new \RuntimeException('Unknown group.'); $out = []; foreach ($keys[$group] as $k) $out[$k] = get_option($k); return ['group' => $group, 'settings' => $out]; } public static function addOrderNote(array $args): array { $id = (int) ($args['order_id'] ?? 0); $order = wc_get_order($id); if (!$order) throw new \RuntimeException('Order not found: ' . $id); $note = (string) ($args['note'] ?? ''); if ($note === '') throw new \RuntimeException('note required.'); $noteId = $order->add_order_note($note, !empty($args['customer_note']) ? 1 : 0, true); return ['order_id' => $id, 'note_id' => $noteId, 'customer_note' => !empty($args['customer_note'])]; } public static function createRefund(array $args): array { $id = (int) ($args['order_id'] ?? 0); $order = wc_get_order($id); if (!$order) throw new \RuntimeException('Order not found: ' . $id); $amount = isset($args['amount']) ? (float) $args['amount'] : (float) $order->get_total(); $refund = wc_create_refund([ 'order_id' => $id, 'amount' => $amount, 'reason' => (string) ($args['reason'] ?? ''), 'restock_items' => !empty($args['restock']), ]); if (is_wp_error($refund)) throw new \RuntimeException('Refund failed: ' . $refund->get_error_message()); return ['order_id' => $id, 'refund_id' => $refund->get_id(), 'amount' => $amount]; } public static function salesReport(array $args): array { $to = !empty($args['date_to']) ? (string) $args['date_to'] : date('Y-m-d'); $from = !empty($args['date_from']) ? (string) $args['date_from'] : date('Y-m-d', strtotime('-30 days')); $statuses = !empty($args['statuses']) && is_array($args['statuses']) ? $args['statuses'] : ['wc-completed', 'wc-processing']; $statuses = array_map(fn($s) => strpos($s, 'wc-') === 0 ? $s : 'wc-' . $s, $statuses); $orders = wc_get_orders([ 'limit' => -1, 'status' => $statuses, 'date_created' => $from . '...' . $to, 'return' => 'objects', ]); $gross = 0.0; $items = 0; $n = 0; foreach ((array) $orders as $o) { $gross += (float) $o->get_total(); $items += (int) $o->get_item_count(); $n++; } return ['date_from' => $from, 'date_to' => $to, 'statuses' => $statuses, 'orders' => $n, 'gross_total' => round($gross, 2), 'currency' => get_woocommerce_currency(), 'items_sold' => $items]; } public static function listReviews(array $args): array { $cargs = ['post_type' => 'product', 'status' => 'approve', 'number' => (int) ($args['per_page'] ?? 50)]; if (!empty($args['product_id'])) $cargs['post_id'] = (int) $args['product_id']; $comments = get_comments($cargs); $out = []; foreach ((array) $comments as $c) { $out[] = [ 'id' => (int) $c->comment_ID, 'product_id' => (int) $c->comment_post_ID, 'product' => get_the_title((int) $c->comment_post_ID), 'author' => $c->comment_author, 'rating' => (int) get_comment_meta($c->comment_ID, 'rating', true), 'excerpt' => mb_substr(wp_strip_all_tags($c->comment_content), 0, 200), 'date' => $c->comment_date, ]; } return ['count' => count($out), 'reviews' => $out]; } }