|
@@ -19,7 +19,9 @@ import {
|
|
|
fetchOrders,
|
|
fetchOrders,
|
|
|
fetchCustomers,
|
|
fetchCustomers,
|
|
|
ShopRenterOrder,
|
|
ShopRenterOrder,
|
|
|
- ShopRenterCustomer
|
|
|
|
|
|
|
+ ShopRenterCustomer,
|
|
|
|
|
+ ShopRenterOrderFilters,
|
|
|
|
|
+ ShopRenterCustomerFilters
|
|
|
} from '../_shared/shoprenter-client.ts';
|
|
} from '../_shared/shoprenter-client.ts';
|
|
|
import {
|
|
import {
|
|
|
McpTool,
|
|
McpTool,
|
|
@@ -27,6 +29,8 @@ import {
|
|
|
LlmOrder
|
|
LlmOrder
|
|
|
} from '../_shared/mcp-types.ts';
|
|
} from '../_shared/mcp-types.ts';
|
|
|
import {
|
|
import {
|
|
|
|
|
+ createMcpErrorResponse,
|
|
|
|
|
+ createMcpSuccessResponse,
|
|
|
validateParams
|
|
validateParams
|
|
|
} from '../_shared/mcp-helpers.ts';
|
|
} from '../_shared/mcp-helpers.ts';
|
|
|
import {
|
|
import {
|
|
@@ -51,13 +55,13 @@ const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!;
|
|
|
const supabase = createClient(supabaseUrl, supabaseServiceKey);
|
|
const supabase = createClient(supabaseUrl, supabaseServiceKey);
|
|
|
|
|
|
|
|
const SERVER_NAME = 'mcp-shoprenter';
|
|
const SERVER_NAME = 'mcp-shoprenter';
|
|
|
-const SERVER_VERSION = '1.0.0';
|
|
|
|
|
|
|
+const SERVER_VERSION = '2.0.0';
|
|
|
|
|
|
|
|
// MCP Tool Definitions
|
|
// MCP Tool Definitions
|
|
|
const TOOLS: McpTool[] = [
|
|
const TOOLS: McpTool[] = [
|
|
|
{
|
|
{
|
|
|
name: 'shoprenter_list_orders',
|
|
name: 'shoprenter_list_orders',
|
|
|
- description: 'List orders from a ShopRenter store. Returns order details including customer info, items, status, and totals.',
|
|
|
|
|
|
|
+ description: 'List orders from a ShopRenter store with filtering. At least one filter is REQUIRED (created_from, created_to, updated_from, updated_to, customer_email, or customer_name). Returns order details including customer info, items, status, and totals. Limited to 20 results maximum.',
|
|
|
inputSchema: {
|
|
inputSchema: {
|
|
|
type: 'object',
|
|
type: 'object',
|
|
|
properties: {
|
|
properties: {
|
|
@@ -65,43 +69,45 @@ const TOOLS: McpTool[] = [
|
|
|
type: 'string',
|
|
type: 'string',
|
|
|
description: 'The UUID of the ShopRenter store from the stores table'
|
|
description: 'The UUID of the ShopRenter store from the stores table'
|
|
|
},
|
|
},
|
|
|
- page: {
|
|
|
|
|
- type: 'number',
|
|
|
|
|
- description: 'Page number for pagination (default: 1)'
|
|
|
|
|
|
|
+ created_from: {
|
|
|
|
|
+ type: 'string',
|
|
|
|
|
+ description: 'Filter orders created after this ISO 8601 datetime (e.g., 2025-01-01T00:00:00Z)'
|
|
|
},
|
|
},
|
|
|
- limit: {
|
|
|
|
|
- type: 'number',
|
|
|
|
|
- description: 'Number of orders per page (default: 25, max: 100)'
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- required: ['shop_id']
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- name: 'shoprenter_list_customers',
|
|
|
|
|
- description: 'List customers from a ShopRenter store. Returns customer details including contact info and order history.',
|
|
|
|
|
- inputSchema: {
|
|
|
|
|
- type: 'object',
|
|
|
|
|
- properties: {
|
|
|
|
|
- shop_id: {
|
|
|
|
|
|
|
+ created_to: {
|
|
|
type: 'string',
|
|
type: 'string',
|
|
|
- description: 'The UUID of the ShopRenter store from the stores table'
|
|
|
|
|
|
|
+ description: 'Filter orders created before this ISO 8601 datetime'
|
|
|
},
|
|
},
|
|
|
- page: {
|
|
|
|
|
- type: 'number',
|
|
|
|
|
- description: 'Page number for pagination (default: 1)'
|
|
|
|
|
|
|
+ updated_from: {
|
|
|
|
|
+ type: 'string',
|
|
|
|
|
+ description: 'Filter orders updated after this ISO 8601 datetime'
|
|
|
|
|
+ },
|
|
|
|
|
+ updated_to: {
|
|
|
|
|
+ type: 'string',
|
|
|
|
|
+ description: 'Filter orders updated before this ISO 8601 datetime'
|
|
|
|
|
+ },
|
|
|
|
|
+ customer_email: {
|
|
|
|
|
+ type: 'string',
|
|
|
|
|
+ description: 'Filter by customer email address'
|
|
|
|
|
+ },
|
|
|
|
|
+ customer_name: {
|
|
|
|
|
+ type: 'string',
|
|
|
|
|
+ description: 'Filter by customer name (searches billing name)'
|
|
|
|
|
+ },
|
|
|
|
|
+ status: {
|
|
|
|
|
+ type: 'string',
|
|
|
|
|
+ description: 'Filter by order status: any, pending, processing, on-hold, completed, cancelled, refunded, failed'
|
|
|
},
|
|
},
|
|
|
limit: {
|
|
limit: {
|
|
|
type: 'number',
|
|
type: 'number',
|
|
|
- description: 'Number of customers per page (default: 25, max: 100)'
|
|
|
|
|
|
|
+ description: 'Number of orders to return (default: 5, max: 20)'
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
required: ['shop_id']
|
|
required: ['shop_id']
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
- name: 'shoprenter_get_customer_orders',
|
|
|
|
|
- description: 'Get all orders for a specific customer by customer email. Returns detailed order history.',
|
|
|
|
|
|
|
+ name: 'shoprenter_get_customer',
|
|
|
|
|
+ description: 'Get a specific customer from a ShopRenter store by email address. Returns a single customer with contact info, order count, and total spent. Email parameter is REQUIRED.',
|
|
|
inputSchema: {
|
|
inputSchema: {
|
|
|
type: 'object',
|
|
type: 'object',
|
|
|
properties: {
|
|
properties: {
|
|
@@ -109,50 +115,38 @@ const TOOLS: McpTool[] = [
|
|
|
type: 'string',
|
|
type: 'string',
|
|
|
description: 'The UUID of the ShopRenter store from the stores table'
|
|
description: 'The UUID of the ShopRenter store from the stores table'
|
|
|
},
|
|
},
|
|
|
- customer_email: {
|
|
|
|
|
|
|
+ email: {
|
|
|
type: 'string',
|
|
type: 'string',
|
|
|
- description: 'The customer email address'
|
|
|
|
|
|
|
+ description: 'The customer email address (required)'
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
- required: ['shop_id', 'customer_email']
|
|
|
|
|
|
|
+ required: ['shop_id', 'email']
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
];
|
|
];
|
|
|
|
|
|
|
|
-/**
|
|
|
|
|
- * Extract phone number from various ShopRenter structures
|
|
|
|
|
- */
|
|
|
|
|
-function extractPhone(data: any): string | undefined {
|
|
|
|
|
- if (data?.phone) return data.phone;
|
|
|
|
|
- if (data?.billing_address?.phone) return data.billing_address.phone;
|
|
|
|
|
- if (data?.shipping_address?.phone) return data.shipping_address.phone;
|
|
|
|
|
- return undefined;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
/**
|
|
/**
|
|
|
* Convert ShopRenter customer to LLM-friendly format
|
|
* Convert ShopRenter customer to LLM-friendly format
|
|
|
*/
|
|
*/
|
|
|
-function formatCustomerForLlm(customer: ShopRenterCustomer): LlmCustomer {
|
|
|
|
|
|
|
+function formatCustomerForLlm(customer: any): LlmCustomer {
|
|
|
return {
|
|
return {
|
|
|
- id: customer.id,
|
|
|
|
|
- name: `${customer.firstname} ${customer.lastname}`.trim(),
|
|
|
|
|
|
|
+ id: customer.id || customer.customer_id,
|
|
|
|
|
+ name: `${customer.firstname || customer.first_name || ''} ${customer.lastname || customer.last_name || ''}`.trim(),
|
|
|
email: customer.email,
|
|
email: customer.email,
|
|
|
- phone: customer.phone || undefined
|
|
|
|
|
|
|
+ phone: customer.phone || undefined,
|
|
|
|
|
+ ordersCount: customer.orders_count || undefined,
|
|
|
|
|
+ totalSpent: customer.total_spent || undefined
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* Convert ShopRenter order to LLM-friendly format
|
|
* Convert ShopRenter order to LLM-friendly format
|
|
|
*/
|
|
*/
|
|
|
-function formatOrderForLlm(order: ShopRenterOrder): LlmOrder {
|
|
|
|
|
- // Extract customer name and email
|
|
|
|
|
|
|
+function formatOrderForLlm(order: any): LlmOrder {
|
|
|
const customerName = order.customer_name ||
|
|
const customerName = order.customer_name ||
|
|
|
(order.customer ? `${order.customer.firstname || ''} ${order.customer.lastname || ''}`.trim() : 'Unknown');
|
|
(order.customer ? `${order.customer.firstname || ''} ${order.customer.lastname || ''}`.trim() : 'Unknown');
|
|
|
const customerEmail = order.customer_email || order.customer?.email || '';
|
|
const customerEmail = order.customer_email || order.customer?.email || '';
|
|
|
- const customerPhone = order.customer_phone ||
|
|
|
|
|
- order.customer?.phone ||
|
|
|
|
|
- extractPhone(order.billing_address) ||
|
|
|
|
|
- extractPhone(order.shipping_address);
|
|
|
|
|
|
|
+ const customerPhone = order.customer_phone || order.customer?.phone || undefined;
|
|
|
|
|
|
|
|
return {
|
|
return {
|
|
|
id: order.id,
|
|
id: order.id,
|
|
@@ -163,7 +157,7 @@ function formatOrderForLlm(order: ShopRenterOrder): LlmOrder {
|
|
|
phone: customerPhone
|
|
phone: customerPhone
|
|
|
},
|
|
},
|
|
|
status: order.status,
|
|
status: order.status,
|
|
|
- total: order.total,
|
|
|
|
|
|
|
+ total: order.total || '0',
|
|
|
currency: order.currency || 'HUF',
|
|
currency: order.currency || 'HUF',
|
|
|
items: (order.items || order.line_items || []).map((item: any) => ({
|
|
items: (order.items || order.line_items || []).map((item: any) => ({
|
|
|
name: item.name || item.product_name || 'Unknown',
|
|
name: item.name || item.product_name || 'Unknown',
|
|
@@ -175,31 +169,27 @@ function formatOrderForLlm(order: ShopRenterOrder): LlmOrder {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * Fetch all orders with pagination
|
|
|
|
|
|
|
+ * Fetch all orders with pagination (for filtering by customer name)
|
|
|
*/
|
|
*/
|
|
|
async function fetchAllOrdersPages(
|
|
async function fetchAllOrdersPages(
|
|
|
shop_id: string,
|
|
shop_id: string,
|
|
|
- maxPages: number = 10
|
|
|
|
|
|
|
+ maxPages: number = 10,
|
|
|
|
|
+ filters?: ShopRenterOrderFilters
|
|
|
): Promise<any[]> {
|
|
): Promise<any[]> {
|
|
|
const allOrders: any[] = [];
|
|
const allOrders: any[] = [];
|
|
|
let page = 1;
|
|
let page = 1;
|
|
|
let hasMore = true;
|
|
let hasMore = true;
|
|
|
|
|
|
|
|
while (hasMore && page <= maxPages) {
|
|
while (hasMore && page <= maxPages) {
|
|
|
- try {
|
|
|
|
|
- const response = await fetchOrders(shop_id, page, 100);
|
|
|
|
|
- const orders = Array.isArray(response) ? response : (response.data || response.orders || []);
|
|
|
|
|
-
|
|
|
|
|
- if (orders.length === 0) {
|
|
|
|
|
- hasMore = false;
|
|
|
|
|
- } else {
|
|
|
|
|
- allOrders.push(...orders);
|
|
|
|
|
- hasMore = orders.length === 100; // If we got max results, there might be more
|
|
|
|
|
- page++;
|
|
|
|
|
- }
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error(`[MCP ShopRenter] Error fetching page ${page}:`, error);
|
|
|
|
|
|
|
+ const response = await fetchOrders(shop_id, page, 100, filters);
|
|
|
|
|
+ const orders = Array.isArray(response) ? response : (response.data || response.orders || []);
|
|
|
|
|
+
|
|
|
|
|
+ if (orders.length === 0) {
|
|
|
hasMore = false;
|
|
hasMore = false;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ allOrders.push(...orders);
|
|
|
|
|
+ hasMore = orders.length === 100;
|
|
|
|
|
+ page++;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -210,7 +200,34 @@ async function fetchAllOrdersPages(
|
|
|
* Handle shoprenter_list_orders tool
|
|
* Handle shoprenter_list_orders tool
|
|
|
*/
|
|
*/
|
|
|
async function handleListOrders(args: Record<string, any>): Promise<ToolCallResult> {
|
|
async function handleListOrders(args: Record<string, any>): Promise<ToolCallResult> {
|
|
|
- const { shop_id, page = 1, limit = 25 } = args;
|
|
|
|
|
|
|
+ const {
|
|
|
|
|
+ shop_id,
|
|
|
|
|
+ created_from,
|
|
|
|
|
+ created_to,
|
|
|
|
|
+ updated_from,
|
|
|
|
|
+ updated_to,
|
|
|
|
|
+ customer_email,
|
|
|
|
|
+ customer_name,
|
|
|
|
|
+ status,
|
|
|
|
|
+ limit = 5
|
|
|
|
|
+ } = args;
|
|
|
|
|
+
|
|
|
|
|
+ // Validate at least one filter is provided
|
|
|
|
|
+ const hasFilter = created_from || created_to || updated_from || updated_to || customer_email || customer_name;
|
|
|
|
|
+ if (!hasFilter) {
|
|
|
|
|
+ return {
|
|
|
|
|
+ content: [{
|
|
|
|
|
+ type: 'text',
|
|
|
|
|
+ text: JSON.stringify({
|
|
|
|
|
+ error: 'At least one filter is required: created_from, created_to, updated_from, updated_to, customer_email, or customer_name'
|
|
|
|
|
+ })
|
|
|
|
|
+ }],
|
|
|
|
|
+ isError: true
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Enforce limit constraints
|
|
|
|
|
+ const actualLimit = Math.min(Math.max(1, limit), 20);
|
|
|
|
|
|
|
|
// Validate shop exists and is ShopRenter
|
|
// Validate shop exists and is ShopRenter
|
|
|
const { data: store, error: storeError } = await supabase
|
|
const { data: store, error: storeError } = await supabase
|
|
@@ -237,20 +254,51 @@ async function handleListOrders(args: Record<string, any>): Promise<ToolCallResu
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
|
|
+ // Build filters
|
|
|
|
|
+ const filters: ShopRenterOrderFilters = {};
|
|
|
|
|
+ if (status) filters.status = status;
|
|
|
|
|
+ if (customer_email) filters.customer_email = customer_email;
|
|
|
|
|
+ if (created_from) filters.created_from = created_from;
|
|
|
|
|
+ if (created_to) filters.created_to = created_to;
|
|
|
|
|
+ if (updated_from) filters.updated_from = updated_from;
|
|
|
|
|
+ if (updated_to) filters.updated_to = updated_to;
|
|
|
|
|
+
|
|
|
// Fetch orders from ShopRenter API
|
|
// Fetch orders from ShopRenter API
|
|
|
- const response = await fetchOrders(shop_id, page, Math.min(limit, 100));
|
|
|
|
|
- const orders = Array.isArray(response) ? response : (response.data || response.orders || []);
|
|
|
|
|
|
|
+ let orders: any[];
|
|
|
|
|
+
|
|
|
|
|
+ if (customer_name) {
|
|
|
|
|
+ // If filtering by customer name, we need to fetch and filter locally
|
|
|
|
|
+ // because ShopRenter doesn't support name filtering directly
|
|
|
|
|
+ filters.customer_name = customer_name;
|
|
|
|
|
+ const response = await fetchOrders(shop_id, 1, actualLimit, filters);
|
|
|
|
|
+ orders = Array.isArray(response) ? response : (response.data || response.orders || []);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // Direct API fetch with filters
|
|
|
|
|
+ const response = await fetchOrders(shop_id, 1, actualLimit, filters);
|
|
|
|
|
+ orders = Array.isArray(response) ? response : (response.data || response.orders || []);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Apply limit
|
|
|
|
|
+ const limitedOrders = orders.slice(0, actualLimit);
|
|
|
|
|
|
|
|
// Format for LLM
|
|
// Format for LLM
|
|
|
- const formattedOrders = orders.map(formatOrderForLlm);
|
|
|
|
|
|
|
+ const formattedOrders = limitedOrders.map(formatOrderForLlm);
|
|
|
|
|
|
|
|
return {
|
|
return {
|
|
|
content: [{
|
|
content: [{
|
|
|
type: 'text',
|
|
type: 'text',
|
|
|
text: JSON.stringify({
|
|
text: JSON.stringify({
|
|
|
count: formattedOrders.length,
|
|
count: formattedOrders.length,
|
|
|
- page,
|
|
|
|
|
- limit,
|
|
|
|
|
|
|
+ limit: actualLimit,
|
|
|
|
|
+ filters_applied: {
|
|
|
|
|
+ created_from,
|
|
|
|
|
+ created_to,
|
|
|
|
|
+ updated_from,
|
|
|
|
|
+ updated_to,
|
|
|
|
|
+ customer_email,
|
|
|
|
|
+ customer_name,
|
|
|
|
|
+ status
|
|
|
|
|
+ },
|
|
|
orders: formattedOrders
|
|
orders: formattedOrders
|
|
|
})
|
|
})
|
|
|
}]
|
|
}]
|
|
@@ -270,73 +318,23 @@ async function handleListOrders(args: Record<string, any>): Promise<ToolCallResu
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * Handle shoprenter_list_customers tool
|
|
|
|
|
|
|
+ * Handle shoprenter_get_customer tool (replaces list_customers)
|
|
|
*/
|
|
*/
|
|
|
-async function handleListCustomers(args: Record<string, any>): Promise<ToolCallResult> {
|
|
|
|
|
- const { shop_id, page = 1, limit = 25 } = args;
|
|
|
|
|
-
|
|
|
|
|
- // Validate shop exists and is ShopRenter
|
|
|
|
|
- const { data: store, error: storeError } = await supabase
|
|
|
|
|
- .from('stores')
|
|
|
|
|
- .select('id, platform_name, data_access_permissions')
|
|
|
|
|
- .eq('id', shop_id)
|
|
|
|
|
- .eq('platform_name', 'shoprenter')
|
|
|
|
|
- .single();
|
|
|
|
|
-
|
|
|
|
|
- if (storeError || !store) {
|
|
|
|
|
- return {
|
|
|
|
|
- content: [{ type: 'text', text: JSON.stringify({ error: 'ShopRenter store not found' }) }],
|
|
|
|
|
- isError: true
|
|
|
|
|
- };
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Check permissions
|
|
|
|
|
- const permissions = store.data_access_permissions as any;
|
|
|
|
|
- if (permissions && !permissions.allow_customer_access) {
|
|
|
|
|
- return {
|
|
|
|
|
- content: [{ type: 'text', text: JSON.stringify({ error: 'Customer access not allowed for this store' }) }],
|
|
|
|
|
- isError: true
|
|
|
|
|
- };
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- try {
|
|
|
|
|
- // Fetch customers from ShopRenter API
|
|
|
|
|
- const response = await fetchCustomers(shop_id, page, Math.min(limit, 100));
|
|
|
|
|
- const customers = Array.isArray(response) ? response : (response.data || response.customers || []);
|
|
|
|
|
-
|
|
|
|
|
- // Format for LLM
|
|
|
|
|
- const formattedCustomers = customers.map(formatCustomerForLlm);
|
|
|
|
|
|
|
+async function handleGetCustomer(args: Record<string, any>): Promise<ToolCallResult> {
|
|
|
|
|
+ const { shop_id, email } = args;
|
|
|
|
|
|
|
|
|
|
+ // Validate email is provided
|
|
|
|
|
+ if (!email) {
|
|
|
return {
|
|
return {
|
|
|
content: [{
|
|
content: [{
|
|
|
type: 'text',
|
|
type: 'text',
|
|
|
text: JSON.stringify({
|
|
text: JSON.stringify({
|
|
|
- count: formattedCustomers.length,
|
|
|
|
|
- page,
|
|
|
|
|
- limit,
|
|
|
|
|
- customers: formattedCustomers
|
|
|
|
|
- })
|
|
|
|
|
- }]
|
|
|
|
|
- };
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error('[MCP ShopRenter] Error fetching customers:', error);
|
|
|
|
|
- return {
|
|
|
|
|
- content: [{
|
|
|
|
|
- type: 'text',
|
|
|
|
|
- text: JSON.stringify({
|
|
|
|
|
- error: `Failed to fetch customers: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
|
|
|
|
|
+ error: 'Email parameter is required'
|
|
|
})
|
|
})
|
|
|
}],
|
|
}],
|
|
|
isError: true
|
|
isError: true
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Handle shoprenter_get_customer_orders tool
|
|
|
|
|
- */
|
|
|
|
|
-async function handleGetCustomerOrders(args: Record<string, any>): Promise<ToolCallResult> {
|
|
|
|
|
- const { shop_id, customer_email } = args;
|
|
|
|
|
|
|
|
|
|
// Validate shop exists and is ShopRenter
|
|
// Validate shop exists and is ShopRenter
|
|
|
const { data: store, error: storeError } = await supabase
|
|
const { data: store, error: storeError } = await supabase
|
|
@@ -355,41 +353,48 @@ async function handleGetCustomerOrders(args: Record<string, any>): Promise<ToolC
|
|
|
|
|
|
|
|
// Check permissions
|
|
// Check permissions
|
|
|
const permissions = store.data_access_permissions as any;
|
|
const permissions = store.data_access_permissions as any;
|
|
|
- if (permissions && (!permissions.allow_order_access || !permissions.allow_customer_access)) {
|
|
|
|
|
|
|
+ if (permissions && !permissions.allow_customer_access) {
|
|
|
return {
|
|
return {
|
|
|
- content: [{ type: 'text', text: JSON.stringify({ error: 'Order and customer access required' }) }],
|
|
|
|
|
|
|
+ content: [{ type: 'text', text: JSON.stringify({ error: 'Customer access not allowed for this store' }) }],
|
|
|
isError: true
|
|
isError: true
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
- // Fetch orders and filter by customer email
|
|
|
|
|
- const allOrders = await fetchAllOrdersPages(shop_id, 10);
|
|
|
|
|
- const customerOrders = allOrders.filter(order => {
|
|
|
|
|
- const orderEmail = order.customer_email || order.customer?.email || '';
|
|
|
|
|
- return orderEmail.toLowerCase() === customer_email.toLowerCase();
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ // Fetch customer by email from ShopRenter API
|
|
|
|
|
+ const response = await fetchCustomers(shop_id, 1, 1, { email });
|
|
|
|
|
+ const customers = Array.isArray(response) ? response : (response.data || response.customers || []);
|
|
|
|
|
|
|
|
- // Format for LLM
|
|
|
|
|
- const formattedOrders = customerOrders.map(formatOrderForLlm);
|
|
|
|
|
|
|
+ if (customers.length === 0) {
|
|
|
|
|
+ return {
|
|
|
|
|
+ content: [{
|
|
|
|
|
+ type: 'text',
|
|
|
|
|
+ text: JSON.stringify({
|
|
|
|
|
+ error: `No customer found with email: ${email}`
|
|
|
|
|
+ })
|
|
|
|
|
+ }],
|
|
|
|
|
+ isError: true
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Return only the first customer
|
|
|
|
|
+ const customer = formatCustomerForLlm(customers[0]);
|
|
|
|
|
|
|
|
return {
|
|
return {
|
|
|
content: [{
|
|
content: [{
|
|
|
type: 'text',
|
|
type: 'text',
|
|
|
text: JSON.stringify({
|
|
text: JSON.stringify({
|
|
|
- customerEmail: customer_email,
|
|
|
|
|
- count: formattedOrders.length,
|
|
|
|
|
- orders: formattedOrders
|
|
|
|
|
|
|
+ customer
|
|
|
})
|
|
})
|
|
|
}]
|
|
}]
|
|
|
};
|
|
};
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
- console.error('[MCP ShopRenter] Error fetching customer orders:', error);
|
|
|
|
|
|
|
+ console.error('[MCP ShopRenter] Error fetching customer:', error);
|
|
|
return {
|
|
return {
|
|
|
content: [{
|
|
content: [{
|
|
|
type: 'text',
|
|
type: 'text',
|
|
|
text: JSON.stringify({
|
|
text: JSON.stringify({
|
|
|
- error: `Failed to fetch customer orders: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
|
|
|
|
|
+ error: `Failed to fetch customer: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
|
})
|
|
})
|
|
|
}],
|
|
}],
|
|
|
isError: true
|
|
isError: true
|
|
@@ -422,11 +427,8 @@ async function handleToolCall(params: ToolCallParams): Promise<ToolCallResult> {
|
|
|
case 'shoprenter_list_orders':
|
|
case 'shoprenter_list_orders':
|
|
|
return await handleListOrders(args);
|
|
return await handleListOrders(args);
|
|
|
|
|
|
|
|
- case 'shoprenter_list_customers':
|
|
|
|
|
- return await handleListCustomers(args);
|
|
|
|
|
-
|
|
|
|
|
- case 'shoprenter_get_customer_orders':
|
|
|
|
|
- const customerValidation = validateParams(args, ['shop_id', 'customer_email']);
|
|
|
|
|
|
|
+ case 'shoprenter_get_customer':
|
|
|
|
|
+ const customerValidation = validateParams(args, ['shop_id', 'email']);
|
|
|
if (!customerValidation.valid) {
|
|
if (!customerValidation.valid) {
|
|
|
return {
|
|
return {
|
|
|
content: [{
|
|
content: [{
|
|
@@ -438,7 +440,30 @@ async function handleToolCall(params: ToolCallParams): Promise<ToolCallResult> {
|
|
|
isError: true
|
|
isError: true
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
- return await handleGetCustomerOrders(args);
|
|
|
|
|
|
|
+ return await handleGetCustomer(args);
|
|
|
|
|
+
|
|
|
|
|
+ // Legacy tool names for backward compatibility
|
|
|
|
|
+ case 'shoprenter_list_customers':
|
|
|
|
|
+ return {
|
|
|
|
|
+ content: [{
|
|
|
|
|
+ type: 'text',
|
|
|
|
|
+ text: JSON.stringify({
|
|
|
|
|
+ error: 'This tool has been removed. Use shoprenter_get_customer with email parameter instead.'
|
|
|
|
|
+ })
|
|
|
|
|
+ }],
|
|
|
|
|
+ isError: true
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ case 'shoprenter_get_customer_orders':
|
|
|
|
|
+ return {
|
|
|
|
|
+ content: [{
|
|
|
|
|
+ type: 'text',
|
|
|
|
|
+ text: JSON.stringify({
|
|
|
|
|
+ error: 'This tool has been removed. Use shoprenter_list_orders with customer_email filter instead.'
|
|
|
|
|
+ })
|
|
|
|
|
+ }],
|
|
|
|
|
+ isError: true
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
default:
|
|
default:
|
|
|
return {
|
|
return {
|