diff --git a/PepApi.sln b/PepApi.sln
index 1beb7d2..66559be 100644
--- a/PepApi.sln
+++ b/PepApi.sln
@@ -1,12 +1,14 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
-VisualStudioVersion = 17.14.36518.9 d17.14
+VisualStudioVersion = 17.14.36518.9
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepApi.Core", "PepApi.Core\PepApi.Core.csproj", "{6761DEB6-14DD-4930-B1BB-BEC16A278E73}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepLib.Core", "PepLib.Core\PepLib.Core.csproj", "{3E03F837-2CD5-4681-BED9-7179DF766DF6}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepMcp", "PepMcp\PepMcp.csproj", "{28899B7B-2185-4141-B348-E227DFB355E9}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -41,6 +43,18 @@ Global
{3E03F837-2CD5-4681-BED9-7179DF766DF6}.Release|x64.Build.0 = Release|Any CPU
{3E03F837-2CD5-4681-BED9-7179DF766DF6}.Release|x86.ActiveCfg = Release|Any CPU
{3E03F837-2CD5-4681-BED9-7179DF766DF6}.Release|x86.Build.0 = Release|Any CPU
+ {28899B7B-2185-4141-B348-E227DFB355E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {28899B7B-2185-4141-B348-E227DFB355E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {28899B7B-2185-4141-B348-E227DFB355E9}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {28899B7B-2185-4141-B348-E227DFB355E9}.Debug|x64.Build.0 = Debug|Any CPU
+ {28899B7B-2185-4141-B348-E227DFB355E9}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {28899B7B-2185-4141-B348-E227DFB355E9}.Debug|x86.Build.0 = Debug|Any CPU
+ {28899B7B-2185-4141-B348-E227DFB355E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {28899B7B-2185-4141-B348-E227DFB355E9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {28899B7B-2185-4141-B348-E227DFB355E9}.Release|x64.ActiveCfg = Release|Any CPU
+ {28899B7B-2185-4141-B348-E227DFB355E9}.Release|x64.Build.0 = Release|Any CPU
+ {28899B7B-2185-4141-B348-E227DFB355E9}.Release|x86.ActiveCfg = Release|Any CPU
+ {28899B7B-2185-4141-B348-E227DFB355E9}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/PepMcp/PepMcp.csproj b/PepMcp/PepMcp.csproj
new file mode 100644
index 0000000..ca79672
--- /dev/null
+++ b/PepMcp/PepMcp.csproj
@@ -0,0 +1,15 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
diff --git a/PepMcp/PepTools.cs b/PepMcp/PepTools.cs
new file mode 100644
index 0000000..1c74bb1
--- /dev/null
+++ b/PepMcp/PepTools.cs
@@ -0,0 +1,403 @@
+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}";
+ }
+ }
+}
diff --git a/PepMcp/Program.cs b/PepMcp/Program.cs
new file mode 100644
index 0000000..a58a053
--- /dev/null
+++ b/PepMcp/Program.cs
@@ -0,0 +1,11 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+
+var builder = Host.CreateApplicationBuilder(args);
+builder.Services
+ .AddMcpServer()
+ .WithStdioServerTransport()
+ .WithTools();
+
+var app = builder.Build();
+await app.RunAsync();