Skip to main content
Glama

MCP Inflow Ingredients

index.js9.19 kB
#!/usr/bin/env node // @ts-check /** * MCP Server for Inflow Inventory Integration * Provides tools to manage products/ingredients and inventory */ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { z } from 'zod'; import dotenv from 'dotenv'; import { InflowClient } from './src/inflow-client.js'; import { productHandlers } from './src/handlers/product-handlers.js'; import { inventoryHandlers } from './src/handlers/inventory-handlers.js'; // Load environment variables dotenv.config(); /** @typedef {import('./types').InflowConfig} InflowConfig */ /** @typedef {import('./types').MCPResponse} MCPResponse */ /** @typedef {import('./types').MCPToolResult} MCPToolResult */ // Validate environment variables if (!process.env.INFLOW_API_KEY) { console.error('Error: INFLOW_API_KEY is required in environment variables'); process.exit(1); } if (!process.env.INFLOW_COMPANY_ID) { console.error('Error: INFLOW_COMPANY_ID is required in environment variables'); process.exit(1); } /** @type {InflowConfig} */ const config = { apiKey: process.env.INFLOW_API_KEY, companyId: process.env.INFLOW_COMPANY_ID, apiUrl: process.env.INFLOW_API_URL || 'https://cloudapi.inflowinventory.com', apiVersion: process.env.INFLOW_API_VERSION || '2025-06-24' }; // Initialize Inflow client const inflowClient = new InflowClient(config); // Initialize MCP Server const server = new McpServer({ name: 'mcp-inflow-ingredients', version: '1.0.0' }); /** * Register all available tools */ // List Ingredients (Products) server.registerTool( 'list_ingredients', { description: 'List all ingredients/products in Inflow inventory with optional filters', inputSchema: { name: z.string().optional().describe('Filter by product name'), description: z.string().optional().describe('Filter by description'), isActive: z.boolean().optional().describe('Filter by active status'), barcode: z.string().optional().describe('Filter by barcode'), smart: z.string().optional().describe('Full-text search across name, description, SKU, barcode'), include: z.string().optional().describe('Related entities to include (e.g., "inventoryLines,defaultImage")'), limit: z.number().optional().describe('Maximum number of results (default: 50)') } }, async (args) => { const result = await productHandlers.listProducts(inflowClient, args); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } ] }; } ); // Get Ingredient Details server.registerTool( 'get_ingredient', { description: 'Get detailed information about a specific ingredient/product', inputSchema: { productId: z.string().describe('The product ID (UUID)'), include: z.string().optional().describe('Related entities to include (e.g., "inventoryLines,defaultImage")') } }, async (args) => { const result = await productHandlers.getProduct(inflowClient, args); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } ] }; } ); // Get Inventory Summary server.registerTool( 'get_inventory_summary', { description: 'Get inventory summary for a product including quantities on hand, available, reserved, etc.', inputSchema: { productId: z.string().describe('The product ID (UUID)') } }, async (args) => { const result = await productHandlers.getProductSummary(inflowClient, args); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } ] }; } ); // Search Ingredients server.registerTool( 'search_ingredients', { description: 'Search for ingredients using full-text search across name, description, SKU, and barcode', inputSchema: { query: z.string().describe('Search query string'), limit: z.number().optional().describe('Maximum number of results (default: 25)'), include: z.string().optional().describe('Related entities to include') } }, async (args) => { const result = await productHandlers.searchProducts(inflowClient, args); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } ] }; } ); // Create Ingredient server.registerTool( 'create_ingredient', { description: 'Create a new ingredient/product in Inflow inventory', inputSchema: { productId: z.string().describe('UUID for the new product (generate with crypto.randomUUID())'), name: z.string().describe('Product name'), sku: z.string().optional().describe('SKU code'), description: z.string().optional().describe('Product description'), isActive: z.boolean().optional().describe('Active status (default: true)'), additionalFields: z.record(z.any()).optional().describe('Any additional product fields') } }, async (args) => { const result = await productHandlers.createProduct(inflowClient, args); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } ] }; } ); // Update Ingredient server.registerTool( 'update_ingredient', { description: 'Update an existing ingredient/product in Inflow inventory', inputSchema: { productId: z.string().describe('The product ID to update'), name: z.string().optional().describe('New product name'), sku: z.string().optional().describe('New SKU code'), description: z.string().optional().describe('New description'), isActive: z.boolean().optional().describe('Active status'), additionalFields: z.record(z.any()).optional().describe('Any additional fields to update') } }, async (args) => { const result = await productHandlers.updateProduct(inflowClient, args); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } ] }; } ); // Create Stock Adjustment server.registerTool( 'create_stock_adjustment', { description: 'Create a stock adjustment to modify inventory quantities for one or more products', inputSchema: { stockAdjustmentId: z.string().describe('UUID for the adjustment (generate with crypto.randomUUID())'), locationId: z.string().describe('Location ID where adjustment occurs'), lines: z.array(z.object({ productId: z.string().describe('Product ID being adjusted'), quantity: z.number().describe('Quantity to adjust (can be negative for reductions)') })).describe('Array of products and quantities to adjust'), adjustmentReasonId: z.string().optional().describe('Reason ID for the adjustment'), notes: z.string().optional().describe('Notes about the adjustment'), adjustmentDate: z.string().optional().describe('Date of adjustment (ISO format)') } }, async (args) => { const result = await inventoryHandlers.createStockAdjustment(inflowClient, args); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } ] }; } ); // List Stock Adjustments server.registerTool( 'list_stock_adjustments', { description: 'List stock adjustments with optional filters', inputSchema: { adjustmentNumber: z.string().optional().describe('Filter by adjustment number'), include: z.string().optional().describe('Related entities to include'), limit: z.number().optional().describe('Maximum number of results (default: 50)') } }, async (args) => { const result = await inventoryHandlers.listStockAdjustments(inflowClient, args); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } ] }; } ); // Get Stock Adjustment server.registerTool( 'get_stock_adjustment', { description: 'Get details of a specific stock adjustment', inputSchema: { stockAdjustmentId: z.string().describe('The stock adjustment ID'), include: z.string().optional().describe('Related entities to include') } }, async (args) => { const result = await inventoryHandlers.getStockAdjustment(inflowClient, args); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2) } ] }; } ); /** * Start the server */ async function main() { const transport = new StdioServerTransport(); try { await server.connect(transport); console.error('Inflow Inventory MCP Server started successfully'); console.error(`API URL: ${config.apiUrl}`); console.error(`Company ID: ${config.companyId}`); console.error(`API Version: ${config.apiVersion}`); } catch (error) { console.error('Failed to start server:', error); process.exit(1); } } // Handle errors process.on('unhandledRejection', (error) => { console.error('Unhandled rejection:', error); process.exit(1); }); // Start server main();

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/intelligent-staffing-systems/mcp-inflow-ingredients'

If you have feedback or need assistance with the MCP directory API, please join our Discord server