using ModelContextProtocol.Server; using System.ComponentModel; using System.Text.Json; [McpServerToolType] public class PepTools { private static readonly HttpClient _httpClient = new() { BaseAddress = new Uri("http://localhost:8085") }; private static readonly JsonSerializerOptions _jsonOptions = new() { PropertyNameCaseInsensitive = true, WriteIndented = true }; [McpServerTool, Description("Get a list of nesting programs for a specific year. Returns program name, status, customer, material info, and dates.")] public static async Task GetNests( [Description("The year to get nests for (e.g., 2024, 2025)")] int year, [Description("Filter by customer name")] string? customer = null, [Description("Filter by status: ToBeCut, Cut, Cancelled, OnHold, or All")] string? status = null, [Description("Maximum number of results to return")] int? limit = null) { try { var queryParams = new List(); if (!string.IsNullOrWhiteSpace(customer)) queryParams.Add($"customer={Uri.EscapeDataString(customer)}"); if (!string.IsNullOrWhiteSpace(status)) queryParams.Add($"status={Uri.EscapeDataString(status)}"); if (limit.HasValue) queryParams.Add($"limit={limit.Value}"); var query = queryParams.Count > 0 ? "?" + string.Join("&", queryParams) : ""; var response = await _httpClient.GetAsync($"/nests/{year}{query}"); if (!response.IsSuccessStatusCode) return $"Error: {response.StatusCode} - {await response.Content.ReadAsStringAsync()}"; var json = await response.Content.ReadAsStringAsync(); return json; } catch (Exception ex) { return $"Error calling PEP API: {ex.Message}"; } } [McpServerTool, Description("Get detailed information about a specific nesting program including parts, plates, and material details.")] public static async Task GetNestDetails( [Description("The name of the nest program (e.g., 'N12345')")] string nestName, [Description("Optional year to narrow down the search")] int? year = null) { try { var url = year.HasValue ? $"/nests/{year.Value}/{Uri.EscapeDataString(nestName)}" : $"/nests/{Uri.EscapeDataString(nestName)}"; var response = await _httpClient.GetAsync(url); if (!response.IsSuccessStatusCode) return $"Error: {response.StatusCode} - {await response.Content.ReadAsStringAsync()}"; var json = await response.Content.ReadAsStringAsync(); return json; } catch (Exception ex) { return $"Error calling PEP API: {ex.Message}"; } } [McpServerTool, Description("Get the plates used in a specific nesting program with material, size, thickness, and quantity information.")] public static async Task GetNestPlates( [Description("The name of the nest program (e.g., 'N12345')")] string nestName, [Description("Optional year to narrow down the search")] int? year = null) { try { var url = year.HasValue ? $"/nests/{year.Value}/{Uri.EscapeDataString(nestName)}/plates" : $"/nests/{Uri.EscapeDataString(nestName)}/plates"; var response = await _httpClient.GetAsync(url); if (!response.IsSuccessStatusCode) return $"Error: {response.StatusCode} - {await response.Content.ReadAsStringAsync()}"; var json = await response.Content.ReadAsStringAsync(); return json; } catch (Exception ex) { return $"Error calling PEP API: {ex.Message}"; } } [McpServerTool, Description("Search for parts across nesting programs by part name. Returns which programs contain the part and quantities.")] public static async Task SearchParts( [Description("The part name or partial name to search for")] string searchTerm, [Description("Optional year to limit the search")] int? year = null, [Description("Optional customer name to filter results")] string? customer = null, [Description("Maximum number of results to return (default 100)")] int limit = 100) { try { var queryParams = new List { $"search={Uri.EscapeDataString(searchTerm)}", $"limit={limit}" }; if (year.HasValue) queryParams.Add($"year={year.Value}"); if (!string.IsNullOrWhiteSpace(customer)) queryParams.Add($"customer={Uri.EscapeDataString(customer)}"); var query = "?" + string.Join("&", queryParams); var response = await _httpClient.GetAsync($"/nests/parts/search{query}"); if (!response.IsSuccessStatusCode) return $"Error: {response.StatusCode} - {await response.Content.ReadAsStringAsync()}"; var json = await response.Content.ReadAsStringAsync(); return json; } catch (Exception ex) { return $"Error calling PEP API: {ex.Message}"; } } [McpServerTool, Description("Get material usage analytics showing consumption by material type, grade, and thickness over a date range.")] public static async Task GetMaterialUsage( [Description("Start date for the analysis period (yyyy-MM-dd)")] string? startDate = null, [Description("End date for the analysis period (yyyy-MM-dd)")] string? endDate = null, [Description("Group results by 'month' for monthly breakdown, or omit for totals")] string? groupBy = null, [Description("Only include programs that have been cut (default true)")] bool cutOnly = true) { try { var queryParams = new List(); if (!string.IsNullOrWhiteSpace(startDate)) queryParams.Add($"startDate={Uri.EscapeDataString(startDate)}"); if (!string.IsNullOrWhiteSpace(endDate)) queryParams.Add($"endDate={Uri.EscapeDataString(endDate)}"); if (!string.IsNullOrWhiteSpace(groupBy)) queryParams.Add($"groupBy={Uri.EscapeDataString(groupBy)}"); queryParams.Add($"cutOnly={cutOnly.ToString().ToLower()}"); var query = "?" + string.Join("&", queryParams); var response = await _httpClient.GetAsync($"/analytics/material-usage{query}"); if (!response.IsSuccessStatusCode) return $"Error: {response.StatusCode} - {await response.Content.ReadAsStringAsync()}"; var json = await response.Content.ReadAsStringAsync(); return json; } catch (Exception ex) { return $"Error calling PEP API: {ex.Message}"; } } [McpServerTool, Description("Get the most commonly used plate sizes with count, total weight, and area statistics.")] public static async Task GetPlateSizes( [Description("Filter by material number")] int? materialNumber = null, [Description("Filter by material grade")] string? grade = null, [Description("Start date for the analysis period (yyyy-MM-dd)")] string? startDate = null, [Description("End date for the analysis period (yyyy-MM-dd)")] string? endDate = null, [Description("Only include programs that have been cut (default true)")] bool cutOnly = true) { try { var queryParams = new List(); if (materialNumber.HasValue) queryParams.Add($"materialNumber={materialNumber.Value}"); if (!string.IsNullOrWhiteSpace(grade)) queryParams.Add($"grade={Uri.EscapeDataString(grade)}"); if (!string.IsNullOrWhiteSpace(startDate)) queryParams.Add($"startDate={Uri.EscapeDataString(startDate)}"); if (!string.IsNullOrWhiteSpace(endDate)) queryParams.Add($"endDate={Uri.EscapeDataString(endDate)}"); queryParams.Add($"cutOnly={cutOnly.ToString().ToLower()}"); var query = "?" + string.Join("&", queryParams); var response = await _httpClient.GetAsync($"/analytics/plate-sizes{query}"); if (!response.IsSuccessStatusCode) return $"Error: {response.StatusCode} - {await response.Content.ReadAsStringAsync()}"; var json = await response.Content.ReadAsStringAsync(); return json; } catch (Exception ex) { return $"Error calling PEP API: {ex.Message}"; } } [McpServerTool, Description("Get material consumption breakdown by thickness showing nest count, plate count, and weight distribution.")] public static async Task GetThicknessBreakdown( [Description("Filter by year")] int? year = null, [Description("Start date for the analysis period (yyyy-MM-dd)")] string? startDate = null, [Description("End date for the analysis period (yyyy-MM-dd)")] string? endDate = null, [Description("Only include programs that have been cut (default true)")] bool cutOnly = true) { try { var queryParams = new List(); if (year.HasValue) queryParams.Add($"year={year.Value}"); if (!string.IsNullOrWhiteSpace(startDate)) queryParams.Add($"startDate={Uri.EscapeDataString(startDate)}"); if (!string.IsNullOrWhiteSpace(endDate)) queryParams.Add($"endDate={Uri.EscapeDataString(endDate)}"); queryParams.Add($"cutOnly={cutOnly.ToString().ToLower()}"); var query = "?" + string.Join("&", queryParams); var response = await _httpClient.GetAsync($"/analytics/thickness-breakdown{query}"); if (!response.IsSuccessStatusCode) return $"Error: {response.StatusCode} - {await response.Content.ReadAsStringAsync()}"; var json = await response.Content.ReadAsStringAsync(); return json; } catch (Exception ex) { return $"Error calling PEP API: {ex.Message}"; } } [McpServerTool, Description("Get material usage breakdown by customer showing which customers use the most material.")] public static async Task GetCustomerUsage( [Description("Filter by specific customer ID or name")] string? customerId = null, [Description("Number of months to analyze (default 12)")] int months = 12, [Description("Only include programs that have been cut (default true)")] bool cutOnly = true) { try { var queryParams = new List { $"months={months}", $"cutOnly={cutOnly.ToString().ToLower()}" }; if (!string.IsNullOrWhiteSpace(customerId)) queryParams.Add($"customerId={Uri.EscapeDataString(customerId)}"); var query = "?" + string.Join("&", queryParams); var response = await _httpClient.GetAsync($"/analytics/customer-usage{query}"); if (!response.IsSuccessStatusCode) return $"Error: {response.StatusCode} - {await response.Content.ReadAsStringAsync()}"; var json = await response.Content.ReadAsStringAsync(); return json; } catch (Exception ex) { return $"Error calling PEP API: {ex.Message}"; } } [McpServerTool, Description("Get stock recommendations based on historical material usage patterns.")] public static async Task GetStockRecommendations( [Description("Number of months of history to analyze (default 6)")] int months = 6, [Description("Multiplier for suggested stock levels (default 1.5)")] double stockMultiplier = 1.5, [Description("Filter by customer ID or name")] string? customerId = null, [Description("Only include programs that have been cut (default true)")] bool cutOnly = true) { try { var queryParams = new List { $"months={months}", $"stockMultiplier={stockMultiplier}", $"cutOnly={cutOnly.ToString().ToLower()}" }; if (!string.IsNullOrWhiteSpace(customerId)) queryParams.Add($"customerId={Uri.EscapeDataString(customerId)}"); var query = "?" + string.Join("&", queryParams); var response = await _httpClient.GetAsync($"/analytics/stock-recommendations{query}"); if (!response.IsSuccessStatusCode) return $"Error: {response.StatusCode} - {await response.Content.ReadAsStringAsync()}"; var json = await response.Content.ReadAsStringAsync(); return json; } catch (Exception ex) { return $"Error calling PEP API: {ex.Message}"; } } [McpServerTool, Description("Get the top materials by usage volume.")] public static async Task GetTopMaterials( [Description("Number of top materials to return (default 10)")] int count = 10, [Description("Number of months to analyze (default 12)")] int months = 12, [Description("Only include programs that have been cut (default true)")] bool cutOnly = true) { try { var queryParams = new List { $"count={count}", $"months={months}", $"cutOnly={cutOnly.ToString().ToLower()}" }; var query = "?" + string.Join("&", queryParams); var response = await _httpClient.GetAsync($"/analytics/top-materials{query}"); if (!response.IsSuccessStatusCode) return $"Error: {response.StatusCode} - {await response.Content.ReadAsStringAsync()}"; var json = await response.Content.ReadAsStringAsync(); return json; } catch (Exception ex) { return $"Error calling PEP API: {ex.Message}"; } } [McpServerTool, Description("Get materials that need to be ordered for programs with 'To Be Cut' status. Shows plate sizes and quantities needed.")] public static async Task GetMaterialsToOrder( [Description("Filter by customer ID or name")] string? customerId = null, [Description("Filter by year (defaults to current year)")] int? year = null, [Description("Filter by programmer name")] string? programmedBy = null) { try { var queryParams = new List(); if (!string.IsNullOrWhiteSpace(customerId)) queryParams.Add($"customerId={Uri.EscapeDataString(customerId)}"); if (year.HasValue) queryParams.Add($"year={year.Value}"); if (!string.IsNullOrWhiteSpace(programmedBy)) queryParams.Add($"programmedBy={Uri.EscapeDataString(programmedBy)}"); var query = queryParams.Count > 0 ? "?" + string.Join("&", queryParams) : ""; var response = await _httpClient.GetAsync($"/analytics/materials-to-order{query}"); if (!response.IsSuccessStatusCode) return $"Error: {response.StatusCode} - {await response.Content.ReadAsStringAsync()}"; var json = await response.Content.ReadAsStringAsync(); return json; } catch (Exception ex) { return $"Error calling PEP API: {ex.Message}"; } } [McpServerTool, Description("Get a list of all available materials in the system with grade, thickness, and density information.")] public static async Task GetMaterials() { try { var response = await _httpClient.GetAsync("/materials"); if (!response.IsSuccessStatusCode) return $"Error: {response.StatusCode} - {await response.Content.ReadAsStringAsync()}"; var json = await response.Content.ReadAsStringAsync(); return json; } catch (Exception ex) { return $"Error calling PEP API: {ex.Message}"; } } [McpServerTool, Description("Get details for a specific material by material number.")] public static async Task GetMaterial( [Description("The material number to look up")] int materialNumber) { try { var response = await _httpClient.GetAsync($"/materials/{materialNumber}"); if (!response.IsSuccessStatusCode) return $"Error: {response.StatusCode} - {await response.Content.ReadAsStringAsync()}"; var json = await response.Content.ReadAsStringAsync(); return json; } catch (Exception ex) { return $"Error calling PEP API: {ex.Message}"; } } [McpServerTool, Description("Get a list of drawings with comprehensive filtering options for all fields including dimensions, dates, and boolean flags.")] public static async Task GetDrawings( [Description("Filter by partial name (case-insensitive)")] string? name = null, [Description("Filter by partial customer name (case-insensitive)")] string? customer = null, [Description("Filter by partial customer ID (case-insensitive)")] string? custId = null, [Description("Filter by partial description (case-insensitive)")] string? description = null, [Description("Filter by partial revision (case-insensitive)")] string? revision = null, [Description("Filter by partial material (case-insensitive)")] string? material = null, [Description("Filter by partial material grade (case-insensitive)")] string? materialGrade = null, [Description("Filter by partial programmer name (case-insensitive)")] string? programmer = null, [Description("Filter by partial created by (case-insensitive)")] string? createdBy = null, [Description("Filter by exact status match")] string? status = null, [Description("Filter by exact type match")] string? type = null, [Description("Filter by exact application match")] string? application = null, [Description("Filter by creation date after (yyyy-MM-dd)")] string? createdAfter = null, [Description("Filter by creation date before (yyyy-MM-dd)")] string? createdBefore = null, [Description("Filter by modified date after (yyyy-MM-dd)")] string? modifiedAfter = null, [Description("Filter by modified date before (yyyy-MM-dd)")] string? modifiedBefore = null, [Description("Filter by minimum width")] double? minWidth = null, [Description("Filter by maximum width")] double? maxWidth = null, [Description("Filter by minimum length")] double? minLength = null, [Description("Filter by maximum length")] double? maxLength = null, [Description("Filter by minimum area")] double? minArea = null, [Description("Filter by maximum area")] double? maxArea = null, [Description("Filter by has bevel")] bool? hasBevel = null, [Description("Filter by has lead-in")] bool? hasLeadIn = null, [Description("Filter by has tab")] bool? hasTab = null, [Description("Number of results to skip (default 0)")] int offset = 0, [Description("Maximum number of results to return (default 100)")] int limit = 100) { try { var queryParams = new List(); if (!string.IsNullOrWhiteSpace(name)) queryParams.Add($"name={Uri.EscapeDataString(name)}"); if (!string.IsNullOrWhiteSpace(customer)) queryParams.Add($"customer={Uri.EscapeDataString(customer)}"); if (!string.IsNullOrWhiteSpace(custId)) queryParams.Add($"custId={Uri.EscapeDataString(custId)}"); if (!string.IsNullOrWhiteSpace(description)) queryParams.Add($"description={Uri.EscapeDataString(description)}"); if (!string.IsNullOrWhiteSpace(revision)) queryParams.Add($"revision={Uri.EscapeDataString(revision)}"); if (!string.IsNullOrWhiteSpace(material)) queryParams.Add($"material={Uri.EscapeDataString(material)}"); if (!string.IsNullOrWhiteSpace(materialGrade)) queryParams.Add($"materialGrade={Uri.EscapeDataString(materialGrade)}"); if (!string.IsNullOrWhiteSpace(programmer)) queryParams.Add($"programmer={Uri.EscapeDataString(programmer)}"); if (!string.IsNullOrWhiteSpace(createdBy)) queryParams.Add($"createdBy={Uri.EscapeDataString(createdBy)}"); if (!string.IsNullOrWhiteSpace(status)) queryParams.Add($"status={Uri.EscapeDataString(status)}"); if (!string.IsNullOrWhiteSpace(type)) queryParams.Add($"type={Uri.EscapeDataString(type)}"); if (!string.IsNullOrWhiteSpace(application)) queryParams.Add($"application={Uri.EscapeDataString(application)}"); if (!string.IsNullOrWhiteSpace(createdAfter)) queryParams.Add($"createdAfter={Uri.EscapeDataString(createdAfter)}"); if (!string.IsNullOrWhiteSpace(createdBefore)) queryParams.Add($"createdBefore={Uri.EscapeDataString(createdBefore)}"); if (!string.IsNullOrWhiteSpace(modifiedAfter)) queryParams.Add($"modifiedAfter={Uri.EscapeDataString(modifiedAfter)}"); if (!string.IsNullOrWhiteSpace(modifiedBefore)) queryParams.Add($"modifiedBefore={Uri.EscapeDataString(modifiedBefore)}"); if (minWidth.HasValue) queryParams.Add($"minWidth={minWidth.Value}"); if (maxWidth.HasValue) queryParams.Add($"maxWidth={maxWidth.Value}"); if (minLength.HasValue) queryParams.Add($"minLength={minLength.Value}"); if (maxLength.HasValue) queryParams.Add($"maxLength={maxLength.Value}"); if (minArea.HasValue) queryParams.Add($"minArea={minArea.Value}"); if (maxArea.HasValue) queryParams.Add($"maxArea={maxArea.Value}"); if (hasBevel.HasValue) queryParams.Add($"hasBevel={hasBevel.Value.ToString().ToLower()}"); if (hasLeadIn.HasValue) queryParams.Add($"hasLeadIn={hasLeadIn.Value.ToString().ToLower()}"); if (hasTab.HasValue) queryParams.Add($"hasTab={hasTab.Value.ToString().ToLower()}"); queryParams.Add($"offset={offset}"); queryParams.Add($"limit={limit}"); var query = "?" + string.Join("&", queryParams); var response = await _httpClient.GetAsync($"/drawings{query}"); if (!response.IsSuccessStatusCode) return $"Error: {response.StatusCode} - {await response.Content.ReadAsStringAsync()}"; var json = await response.Content.ReadAsStringAsync(); return json; } catch (Exception ex) { return $"Error calling PEP API: {ex.Message}"; } } [McpServerTool, Description("Get detailed information about a specific drawing by its ID.")] public static async Task GetDrawing( [Description("The drawing ID to look up")] int id) { try { var response = await _httpClient.GetAsync($"/drawings/{id}"); if (!response.IsSuccessStatusCode) return $"Error: {response.StatusCode} - {await response.Content.ReadAsStringAsync()}"; var json = await response.Content.ReadAsStringAsync(); return json; } catch (Exception ex) { return $"Error calling PEP API: {ex.Message}"; } } [McpServerTool, Description("Get detailed information about a specific drawing by its exact name.")] public static async Task GetDrawingByName( [Description("The exact drawing name to look up")] string name) { try { var response = await _httpClient.GetAsync($"/drawings/name/{Uri.EscapeDataString(name)}"); if (!response.IsSuccessStatusCode) return $"Error: {response.StatusCode} - {await response.Content.ReadAsStringAsync()}"; var json = await response.Content.ReadAsStringAsync(); return json; } catch (Exception ex) { return $"Error calling PEP API: {ex.Message}"; } } [McpServerTool, Description("Search for drawings by partial name match. Returns matching drawings with basic information.")] public static async Task SearchDrawings( [Description("The partial drawing name to search for")] string searchTerm, [Description("Maximum number of results to return (default 100)")] int limit = 100) { try { var queryParams = new List { $"search={Uri.EscapeDataString(searchTerm)}", $"limit={limit}" }; var query = "?" + string.Join("&", queryParams); var response = await _httpClient.GetAsync($"/drawings/search{query}"); if (!response.IsSuccessStatusCode) return $"Error: {response.StatusCode} - {await response.Content.ReadAsStringAsync()}"; var json = await response.Content.ReadAsStringAsync(); return json; } catch (Exception ex) { return $"Error calling PEP API: {ex.Message}"; } } }