mcpengine/servers/lightspeed/src/tools/inventory-tools.ts
Jake Shore d3382ec35a Update 6 MCP servers — fieldedge, lightspeed, squarespace, toast, touchbistro, servicetitan — 2026-02-12
=== UPDATES ===
- fieldedge: Added apps, tools, main server entry, full rebuild
- lightspeed: Added complete src/ directory with tools + apps
- squarespace: Full rebuild — new apps, clients, tools, types modules
- toast: Full rebuild — api-client, apps, tools, types
- touchbistro: Full rebuild — api-client, tools, types, gitignore
- servicetitan: Added 4 React UI apps (call-tracking, lead-source-analytics, performance-metrics, schedule-calendar)

All servers restructured from single-file to modular architecture.
2026-02-12 17:58:15 -05:00

337 lines
11 KiB
TypeScript

/**
* Lightspeed Inventory Tools
*/
import { z } from 'zod';
import type { LightspeedClient } from '../client.js';
import type { InventoryCount, InventoryTransfer, Supplier, PurchaseOrder } from '../types/index.js';
export function createInventoryTools(client: LightspeedClient) {
return {
lightspeed_list_inventory: {
description: 'List inventory counts for all products at a shop',
inputSchema: z.object({
shopId: z.string().describe('Shop ID'),
limit: z.number().optional().describe('Max items to return (default 100)'),
}),
handler: async (args: { shopId: string; limit?: number }) => {
try {
const inventory = await client.getAll<InventoryCount>(
`/Shop/${args.shopId}/ItemShop`,
'ItemShop',
args.limit || 100
);
return {
content: [
{
type: 'text' as const,
text: JSON.stringify({
success: true,
count: inventory.length,
inventory: inventory.map(i => ({
itemID: i.itemID,
qoh: i.qoh,
reorderPoint: i.reorderPoint,
backorder: i.backorder,
})),
}, null, 2),
},
],
};
} catch (error) {
return {
content: [{ type: 'text' as const, text: `Error: ${(error as Error).message}` }],
isError: true,
};
}
},
},
lightspeed_get_item_inventory: {
description: 'Get inventory count for a specific product at a shop',
inputSchema: z.object({
itemId: z.string().describe('Product item ID'),
shopId: z.string().describe('Shop ID'),
}),
handler: async (args: { itemId: string; shopId: string }) => {
try {
const inventory = await client.get<{ ItemShop: InventoryCount }>(
`/Item/${args.itemId}/ItemShop/${args.shopId}`
);
return {
content: [
{
type: 'text' as const,
text: JSON.stringify({ success: true, inventory: inventory.ItemShop }, null, 2),
},
],
};
} catch (error) {
return {
content: [{ type: 'text' as const, text: `Error: ${(error as Error).message}` }],
isError: true,
};
}
},
},
lightspeed_update_inventory_count: {
description: 'Update inventory quantity for a product at a shop',
inputSchema: z.object({
itemId: z.string().describe('Product item ID'),
shopId: z.string().describe('Shop ID'),
quantity: z.number().describe('New quantity on hand'),
reorderPoint: z.number().optional().describe('Reorder point threshold'),
reorderLevel: z.number().optional().describe('Reorder quantity'),
}),
handler: async (args: any) => {
try {
const updateData: any = {
qoh: args.quantity.toString(),
};
if (args.reorderPoint !== undefined) {
updateData.reorderPoint = args.reorderPoint.toString();
}
if (args.reorderLevel !== undefined) {
updateData.reorderLevel = args.reorderLevel.toString();
}
const result = await client.put<{ ItemShop: InventoryCount }>(
`/Item/${args.itemId}/ItemShop`,
args.shopId,
{ ItemShop: updateData }
);
return {
content: [
{
type: 'text' as const,
text: JSON.stringify({ success: true, inventory: result.ItemShop }, null, 2),
},
],
};
} catch (error) {
return {
content: [{ type: 'text' as const, text: `Error: ${(error as Error).message}` }],
isError: true,
};
}
},
},
lightspeed_transfer_stock: {
description: 'Transfer inventory between shops',
inputSchema: z.object({
fromShopId: z.string().describe('Source shop ID'),
toShopId: z.string().describe('Destination shop ID'),
items: z.array(z.object({
itemId: z.string().describe('Product item ID'),
quantity: z.number().describe('Quantity to transfer'),
})).describe('Items to transfer'),
}),
handler: async (args: any) => {
try {
const transferData = {
fromShopID: args.fromShopId,
toShopID: args.toShopId,
TransferItems: {
TransferItem: args.items.map((item: any) => ({
itemID: item.itemId,
quantity: item.quantity.toString(),
})),
},
};
const result = await client.post<{ Transfer: InventoryTransfer }>(
'/Transfer',
{ Transfer: transferData }
);
return {
content: [
{
type: 'text' as const,
text: JSON.stringify({ success: true, transfer: result.Transfer }, null, 2),
},
],
};
} catch (error) {
return {
content: [{ type: 'text' as const, text: `Error: ${(error as Error).message}` }],
isError: true,
};
}
},
},
lightspeed_list_inventory_adjustments: {
description: 'List inventory adjustments (stock changes)',
inputSchema: z.object({
itemId: z.string().optional().describe('Filter by product item ID'),
shopId: z.string().optional().describe('Filter by shop ID'),
limit: z.number().optional().describe('Max adjustments to return (default 100)'),
}),
handler: async (args: any) => {
try {
const params: any = {};
if (args.itemId) params.itemID = args.itemId;
if (args.shopId) params.shopID = args.shopId;
// Note: Lightspeed tracks adjustments through SaleLine with special types
const adjustments = await client.get<{ SaleLine: any[] }>('/SaleLine', params);
const results = Array.isArray(adjustments.SaleLine)
? adjustments.SaleLine
: adjustments.SaleLine ? [adjustments.SaleLine] : [];
return {
content: [
{
type: 'text' as const,
text: JSON.stringify({
success: true,
count: results.length,
adjustments: results.slice(0, args.limit || 100),
}, null, 2),
},
],
};
} catch (error) {
return {
content: [{ type: 'text' as const, text: `Error: ${(error as Error).message}` }],
isError: true,
};
}
},
},
lightspeed_list_suppliers: {
description: 'List all suppliers/vendors',
inputSchema: z.object({
limit: z.number().optional().describe('Max suppliers to return (default 100)'),
archived: z.boolean().optional().describe('Include archived suppliers'),
}),
handler: async (args: { limit?: number; archived?: boolean }) => {
try {
const params: any = {};
if (args.archived !== undefined) {
params.archived = args.archived;
}
const suppliers = await client.getAll<Supplier>('/Vendor', 'Vendor', args.limit || 100);
return {
content: [
{
type: 'text' as const,
text: JSON.stringify({
success: true,
count: suppliers.length,
suppliers: suppliers.map(s => ({
vendorID: s.vendorID,
name: s.name,
accountNumber: s.accountNumber,
archived: s.archived,
})),
}, null, 2),
},
],
};
} catch (error) {
return {
content: [{ type: 'text' as const, text: `Error: ${(error as Error).message}` }],
isError: true,
};
}
},
},
lightspeed_create_purchase_order: {
description: 'Create a purchase order for restocking inventory',
inputSchema: z.object({
vendorId: z.string().describe('Supplier/vendor ID'),
shopId: z.string().describe('Shop ID'),
items: z.array(z.object({
itemId: z.string().describe('Product item ID'),
quantity: z.number().describe('Quantity to order'),
unitCost: z.string().describe('Unit cost'),
})).describe('Items to order'),
}),
handler: async (args: any) => {
try {
const poData = {
vendorID: args.vendorId,
shopID: args.shopId,
PurchaseOrderLines: {
PurchaseOrderLine: args.items.map((item: any) => ({
itemID: item.itemId,
quantity: item.quantity.toString(),
unitCost: item.unitCost,
})),
},
};
const result = await client.post<{ PurchaseOrder: PurchaseOrder }>(
'/PurchaseOrder',
{ PurchaseOrder: poData }
);
return {
content: [
{
type: 'text' as const,
text: JSON.stringify({ success: true, purchaseOrder: result.PurchaseOrder }, null, 2),
},
],
};
} catch (error) {
return {
content: [{ type: 'text' as const, text: `Error: ${(error as Error).message}` }],
isError: true,
};
}
},
},
lightspeed_list_purchase_orders: {
description: 'List purchase orders',
inputSchema: z.object({
vendorId: z.string().optional().describe('Filter by vendor ID'),
status: z.string().optional().describe('Filter by status (e.g., open, complete)'),
limit: z.number().optional().describe('Max POs to return (default 100)'),
}),
handler: async (args: any) => {
try {
const params: any = {};
if (args.vendorId) params.vendorID = args.vendorId;
if (args.status) params.status = args.status;
const pos = await client.getAll<PurchaseOrder>(
'/PurchaseOrder',
'PurchaseOrder',
args.limit || 100
);
return {
content: [
{
type: 'text' as const,
text: JSON.stringify({
success: true,
count: pos.length,
purchaseOrders: pos.map(po => ({
purchaseOrderID: po.purchaseOrderID,
vendorID: po.vendorID,
orderNumber: po.orderNumber,
status: po.status,
createTime: po.createTime,
})),
}, null, 2),
},
],
};
} catch (error) {
return {
content: [{ type: 'text' as const, text: `Error: ${(error as Error).message}` }],
isError: true,
};
}
},
},
};
}