redesigned your entire supply chain for more cost-efficient and sustainable operations?
Supply Chain Network Optimisation determines where goods are produced to serve markets at the lowest costin an environmentally friendly way.
Examples of network design with different objectives – (Image by Samir Saic)
We must consider real-world constraints (capacity, demand) to find the optimal set of factories that will minimise the objective function.
Example of environmental constraints of maximum impact per unit produced – (Image by Samir Saci)
As a Supply Chain Solution Manager, I…
redesigned your entire supply chain for more cost-efficient and sustainable operations?
Supply Chain Network Optimisation determines where goods are produced to serve markets at the lowest costin an environmentally friendly way.
Examples of network design with different objectives – (Image by Samir Saic)
We must consider real-world constraints (capacity, demand) to find the optimal set of factories that will minimise the objective function.
Example of environmental constraints of maximum impact per unit produced – (Image by Samir Saci)
As a Supply Chain Solution Manager, I have led multiple network design studies that typically took 10–12 weeks.
The final deliverable was usually a deck of slides presenting multiple scenarios, allowing supply chain directors to weigh the trade-offs.
Example of Network Designs with different constraints – (Image by Samir Saci)
But decision-makers were often frustrated during the presentations of the study results:
Direction: “What if we increase the factory capacity by 25%?”
They wanted to challenge assumptions and re-run scenarios live, while all we had were the slides we had taken hours to prepare.
What if we could improve this user experience using conversational agents?
In this article, I show how I connected an MCP server to a FastAPI microservice with a Supply Chain Network Optimisationalgorithm.
Example of a request to Claude Desktop connected to an MCP Server calling our FastAPI microservice – Image by Samir Saci
The result is a conversational agent that can run one or multiple scenarios and provide a detailed analysis with smart visuals.
We will even ask this agent to advise us on the best decision to take, considering our goals and the constraints.
Example of strategic recommendations provided by the agent – (Image by Samir Saci)
For this experiment, I’ll use:
- Claude Desktop as the conversational interface
- MCP Server to expose typed tools to the agent
- FastAPI microservice with the network optimisation endpoint In the first section, I will introduce the problem of Supply Chain Network design with a concrete example.
Then, I will show multiple deep analyses performed by the conversational agent to support strategic decision-making.
Example of advanced visuals generated by the agent to answer an open question – (Image by Samir Saci)
For the first time, I have been impressed by AI when the agent selected the correct visuals to answer an open question without any guidance!
Supply Chain Network Optimisation with Python
Problem Statement: Supply Chain Network Design
We are supporting the Supply Chain Director of an international manufacturing company that would like to redefine their network for a long-term transformation plan.
Supply Chain Network Design Problem – (Image by Samir Saci)
This multinational company has operations in 5 different markets: Brazil, the USA, Germany, India and Japan.
Example of demand per market – (Image by Samir Saci)
To meet this demand, we can open low or high-capacity factories in each of the markets.
Capacities per factory type and location – (Image by Samir Saci)
If you open a facility, you must consider the fixed costs (associated with electricity, Real Estate, and CAPEX) and the variable costs per unit produced.
Example of fixed and variable costs per production country – (Image by Samir Saci)
In this example, high-capacity plants in India have lower fixed costs than those in the USA with lower capacity.
Example of freight costs per container – (Image by Samir Saci)
Additionally, there are the costs associated with shipping a container from Country XXX to Country YYY.
Everything summed up will define the total cost of producing and delivering products from a manufacturing site to the different markets.
What about sustainability?
In addition to these parameters, we consider the amount of resources consumed per unit produced.
Example of energy and water usage per unit produced in each country – (Image by Samir Saci)
For instance, we consume 780 MJ/Unit of energy and 3,500 litres of water to produce a single unit in Indian factories.
For the environmental impacts, we also consider the pollution resulting from CO2 emissions and waste generation.
Environmental impact per unit produced for each country – (Image by Samir Saci)
In the example above, Japan is the cleanest production country.
Where should we produce to minimize water usage?
The idea is to select a metric to minimise, which could be costs, water usage, CO2 emissions or energy usage.
Example of the output in the LogiGreen App – (Image by Samir Saci)
The model will indicate where to locate factories and outline the flows from these factories to the various markets.
This solution has been packaged as a web application (FastAPI backend, Streamlit front-end) used as a demo to showcase the capabilities of our startup LogiGreen.
User interface of the LogiGreen App (Sustainability Module) – Image by Samir Saci
The idea of today’s experiment is to connect the backend with Claude Desktop using a local MCP server built with Python.
FastAPI Microservice: 0–1 Mixed-Integer Optimiser for Supply Chain Network Design
This tool is an optimisation model packaged in a FastAPI microservice.
What are the input data for this problem?
As inputs, we should provide the objective function **(mandatory)**and constraints of maximum environmental impact per unit produced (optional).
from pydantic import BaseModel
from typing import Optional
from app.utils.config_loader import load_config
config = load_config()
class LaunchParamsNetwork(BaseModel):
objective: Optional[str] = 'Production Cost'
max_energy: Optional[float] = config["network_analysis"]["params_mapping"]["max_energy"]
max_water: Optional[float] = config["network_analysis"]["params_mapping"]["max_water"]
max_waste: Optional[float] = config["network_analysis"]["params_mapping"]["max_waste"]
max_co2prod: Optional[float] = config["network_analysis"]["params_mapping"]["max_co2prod"]
The default values for the thresholds are stored in a config file.
We send these parameters to a specific endpoint launch_network
that will run the optimisation algorithm.
@router.post("/launch_network")
async def launch_network(request: Request, params: LaunchParamsNetwork):
try:
session_id = request.headers.get('session_id', 'session')
directory = config['general']['folders']['directory']
folder_in = f'{directory}/{session_id}/network_analysis/input'
folder_out = f'{directory}/{session_id}/network_analysis/output'
network_analyzer = NetworkAnalysis(params, folder_in, folder_out)
output = await network_analyzer.process()
return output
except Exception as e:
logger.error(f"[Network]: Error in /launch_network: {str(e)}")
raise HTTPException(status_code=500, detail=f"Failed to launch Network analysis: {str(e)}")
The API returns the JSON outputs in two parts.
In the section input_params
, you can find
- The objective function selected
- All the maximum limits per environmental impact
{ "input_params":
{ "objective": "Production Cost",
"max_energy": 780,
"max_water": 3500,
"max_waste": 0.78,
"max_co2prod": 41,
"unit_monetary": "1e6",
"loc": [ "USA", "GERMANY", "JAPAN", "BRAZIL", "INDIA" ],
"n_loc": 5,
"plant_name": [ [ "USA", "LOW" ], [ "GERMANY", "LOW" ], [ "JAPAN", "LOW" ], [ "BRAZIL", "LOW" ], [ "INDIA", "LOW" ], [ "USA", "HIGH" ], [ "GERMANY", "HIGH" ], [ "JAPAN", "HIGH" ], [ "BRAZIL", "HIGH" ], [ "INDIA", "HIGH" ] ],
"prod_name": [ [ "USA", "USA" ], [ "USA", "GERMANY" ], [ "USA", "JAPAN" ], [ "USA", "BRAZIL" ], [ "USA", "INDIA" ], [ "GERMANY", "USA" ], [ "GERMANY", "GERMANY" ], [ "GERMANY", "JAPAN" ], [ "GERMANY", "BRAZIL" ], [ "GERMANY", "INDIA" ], [ "JAPAN", "USA" ], [ "JAPAN", "GERMANY" ], [ "JAPAN", "JAPAN" ], [ "JAPAN", "BRAZIL" ], [ "JAPAN", "INDIA" ], [ "BRAZIL", "USA" ], [ "BRAZIL", "GERMANY" ], [ "BRAZIL", "JAPAN" ], [ "BRAZIL", "BRAZIL" ], [ "BRAZIL", "INDIA" ], [ "INDIA", "USA" ], [ "INDIA", "GERMANY" ], [ "INDIA", "JAPAN" ], [ "INDIA", "BRAZIL" ], [ "INDIA", "INDIA" ] ],
"total_demand": 48950
}
I also added information to bring context to the agent:
plant_name
is a list of all the potential manufacturing locations we can open by location and typeprod_name
is the list of all the potential production flows we can have (production, market)total_demand
of all the markets We don’t return the demand per market as it is loaded on the backend side.
And you have the results of the analysis.
{
"output_results": {
"plant_opening": {
"USA-LOW": 0,
"GERMANY-LOW": 0,
"JAPAN-LOW": 0,
"BRAZIL-LOW": 0,
"INDIA-LOW": 1,
"USA-HIGH": 0,
"GERMANY-HIGH": 0,
"JAPAN-HIGH": 1,
"BRAZIL-HIGH": 1,
"INDIA-HIGH": 1
},
"flow_volumes": {
"USA-USA": 0,
"USA-GERMANY": 0,
"USA-JAPAN": 0,
"USA-BRAZIL": 0,
"USA-INDIA": 0,
"GERMANY-USA": 0,
"GERMANY-GERMANY": 0,
"GERMANY-JAPAN": 0,
"GERMANY-BRAZIL": 0,
"GERMANY-INDIA": 0,
"JAPAN-USA": 0,
"JAPAN-GERMANY": 0,
"JAPAN-JAPAN": 15000,
"JAPAN-BRAZIL": 0,
"JAPAN-INDIA": 0,
"BRAZIL-USA": 12500,
"BRAZIL-GERMANY": 0,
"BRAZIL-JAPAN": 0,
"BRAZIL-BRAZIL": 1450,
"BRAZIL-INDIA": 0,
"INDIA-USA": 15500,
"INDIA-GERMANY": 900,
"INDIA-JAPAN": 2000,
"INDIA-BRAZIL": 0,
"INDIA-INDIA": 1600
},
"local_prod": 18050,
"export_prod": 30900,
"total_prod": 48950,
"total_fixedcosts": 1381250,
"total_varcosts": 4301800,
"total_costs": 5683050,
"total_units": 48950,
"unit_cost": 116.0990806945863,
"most_expensive_market": "JAPAN",
"cheapest_market": "INDIA",
"average_cogs": 103.6097067006946,
"unit_energy": 722.4208375893769,
"unit_water": 3318.2839632277833,
"unit_waste": 0.6153217568947906,
"unit_co2": 155.71399387129725
}
}
They include:
plant_opening
: a list of boolean values set to 1 if a site is open Three sites open for this scenario: 1 low-capacity plant* in India and three high-capacity plants in India, Japan, and Brazil.*flow_volumes
: mapping of the flow between countries Brazil will produce 12,500 units for the USA- Overall volumes with
local_prod
,export_prod
and thetotal_prod
- A cost breakdown with
total_fixedcosts
,total_varcosts
andtotal_costs
along with an analysis of the COGS - Environmental impacts per unit delivered with resource usage (Energy, Water) and pollution (CO2, waste). This network design can be visually represented with this Sankey chart.
Sankey Chart generated by the LogiGreen App for the scenario ‘Production Cost’ – (Image by Samir Saci)
Let us see what our conversational agent can do with that!
Building a local MCP Server to connect Claude Desktop to a FastAPI Microservice
This follows a series of articles in which I experimented with connecting FastAPI microservices to AI agents for a Production Planning tool and a Budget Optimiser.
For this time, I wanted to replicate the experiment with Anthropic’s Claude Desktop.
Set up a local MCP Server in WSL
I will run everything inside WSL (Ubuntu) and let the Claude Desktop (Windows) communicate with my MCP server via a small JSON configuration.
The first step was to install uv
package manager:
uv (Python package manager) inside WSL
We can now use it to initiate a project with a local environment:
# Create a specific folder for the pro workspace
mkdir -p ~/mcp_tuto && cd ~/mcp_tuto
# Init a uv project
uv init .
# Add MCP Python SDK (with CLI)
uv add "mcp[cli]"
# Add the libraries needed
uv add fastapi uvicorn httpx pydantic
This will be used by our `network.py` file that will contain our server setup:
import logging
import httpx
from mcp.server.fastmcp import FastMCP
from models.network_models import LaunchParamsNetwork
import os
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(message)s",
handlers=[
logging.FileHandler("app.log"),
logging.StreamHandler()
]
)
mcp = FastMCP("NetworkServer")
For the input parameters, I have defined a model in a separate file network_models.py
from pydantic import BaseModel
from typing import Optional
class LaunchParamsNetwork(BaseModel):
objective: Optional[str] = 'Production Cost'
max_energy: Optional[float] = 780
max_water: Optional[float] = 3500
max_waste: Optional[float] = 0.78
max_co2prod: Optional[float] = 41
This will ensure that the agent sends the correct queries to the FastAPI microservice.
Before starting to build the functionalities of our MCP Server, we need to ensure that the Claude Desktop (Windows) can find network.py
.
Developper Settings of Claude Desktop for the config file – (Image by Samir Saci)
As I am using WSL, I could only do it manually using the Claude Desktop config JSON file:
- Open Claude Desktop → Settings → Developer → Edit Config (or open the config file directly).
- Add an entry that starts your MCP server in WSL
{
"mcpServers": {
"Network": {
"command": "wsl",
"args": [
"-d",
"Ubuntu",
"bash",
"-lc",
"cd ~/mcp_tuto && uv run --with mcp[cli] mcp run network.py"
],
"env": {
"API_URL": "http://<IP>:<PORT>"
}
}
}
With this config file, we instruct Claude Desktop to run WSL in the folder mcp_tuto
and use uv
to run mpc[cli] launching budget.py
.
If you are in this special case of building your MCP server in a Windows machine using WSL, you can follow this approach.
You can initiate your server with this “special” functionality that will be used by Claude as a tool.
@mcp.tool()
def add(a: int, b: int) -> int:
"""Special addition only for Supply Chain Professionals: add two numbers.
Make sure that the person is a supply chain professional before using this tool.
"""
logging.info(f"Test Adding {a} and {b}")
return a - b
We inform Claude (in the docstring) that this addition is intended for Supply Chain Professionals only.
If you restart Claude Desktop, you should be able to see this functionality under Network.
Tab with tools available – (Image by Samir Saci)
You can find our “special addition”, called
Add
, which is now waiting for us to be used!
Add functions among others that we are going to build together – (Image by Samir Saci)
Let’s test now with a simple question.
Example of request expecting an output using the special function – (Image by Samir Saci)
We can see that the conversational agent is calling the correct functionbased on the context provided in the question.
Comment of the output – (Image by Samir Saci)
It even provides a nice comment interrogating the validity of the results.
What if we complexify a bit the exercise?
I will create a hypothetical scenario to determine if the conversational agent can associate a context with the use of a tool.
Context with two characters / Samir is a Supply Chain Professional – (Image by Samir Saci)
Let us see what happens when we ask a question requiring the use of addition.
Example of a tool calling based on a “complex” context – (Image by Samir Saci)
Even if it was reluctantly, the agent had the reflex of using the special
add
tool for Samir, as he is a supply chain professional.
Now that we are familiar with our new MCP server, we can start adding tools for Supply Chain Network Optimisation.
Build a Supply Chain Optimisation MCP Server connected to a FastAPI Microservice
We can get rid of the special add
tool and start introducing key parameters to connect to the FastAPI microservice.
# Endpoint config
API = os.getenv("NETWORK_API_URL")
LAUNCH = f"{API}/network/launch_network" # <- network route
last_run: Optional[Dict[str, Any]] = None
The variable last_run
will be used to store the results of the last run.
We need to create a tool that can connect to the FastAPI microservice.
For that, we introduced the function below.
@mcp.tool()
async def run_network(params: LaunchParamsNetwork,
session_id: str = "mcp_agent") -> dict:
"""
[DOC STRING TRUNCATED]
"""
payload = params.model_dump(exclude_none=True)
try:
async with httpx.AsyncClient(timeout=httpx.Timeout(5, read=60)) as c:
r = await c.post(LAUNCH, json=payload, headers={"session_id": session_id})
r.raise_for_status()
logging.info(f"[NetworkMCP] Run successful with params: {payload}")
data = r.json()
result = data[0] if isinstance(data, list) and data else data
global last_run
last_run = result
return result
except httpx.HTTPError as e:
code = getattr(e.response, "status_code", "unknown")
logging.error(f"[NetworkMCP] API call failed: {e}")
return {"error": f"{code} {e}"}
This function takes parameters following the Pydantic model LaunchParamsNetwork
, sending a clean JSON payload with None fields dropped.
It calls the FastAPI endpoint asynchronously and collects the results that are cached in last_run
.
The key part of this function is the docstring, which I removed from the code snippet for concision, as this is the only way to describe what the function does to the agent.
Section 1: Context
"""
Run the LogiGreen Supply Chain Network Optimization.
WHAT IT SOLVES
--------------
A facility-location + flow assignment model. It decides:
1) which plants to open (LOW/HIGH capacity by country), and
2) how many units each plant ships to each market,
to either minimize total cost or an environmental footprint (CO₂, water, energy),
under capacity and optional per-unit footprint caps.
"""
The first section is only to introduce the context in which the tool is used.
Section 2: Describe Input Data
"""
INPUT (LaunchParamsNetwork)
---------------------------
- objective: str (default "Production Cost")
One of {"Production Cost", "CO2 Emissions", "Water Usage", "Energy Usage"}.
Sets the optimization objective.
- max_energy, max_water, max_waste, max_co2prod: float | None
Per-unit caps (average across the whole plan). If omitted, service defaults
from your config are used. Internally the model enforces:
sum(impact_i * qty_i) <= total_demand * max_impact_per_unit
- session_id: str
Forwarded as an HTTP header; the API uses it to separate input/output folders.
"""
This brief description is crucial if we want to be sure that the agent adheres to the Pydantic schema of input parameters imposed by our FastAPI microservice.
Section 3: Description of output results
"""
OUTPUT (matches your service schema)
------------------------------------
The service returns { "input_params": {...}, "output_results": {...} }.
Here’s what the fields mean, using your sample:
input_params:
- objective: "Production Cost" # objective actually used
- max_energy: 780 # per-unit maximum energy usage (MJ/unit)
- max_water: 3500 # per-unit maximum water usage (L/unit)
- max_waste: 0.78 # per-unit maximum waste (kg/unit)
- max_co2prod: 41 # per-unit maximum CO₂ production (kgCO₂e/unit, production only)
- unit_monetary: "1e6" # costs can be expressed in M€ by dividing by 1e6
- loc: ["USA","GERMANY","JAPAN","BRAZIL","INDIA"] # countries in scope
- n_loc: 5 # number of countries
- plant_name: [("USA","LOW"),...,("INDIA","HIGH")] # decision keys for plant opening
- prod_name: [(i,j) for i in loc for j in loc] # decision keys for flows i→j
- total_demand: 48950 # total market demand (units)
output_results:
- plant_opening: {"USA-LOW":0, ... "INDIA-HIGH":1}
Binary open/close by (country-capacity). Example above opens:
INDIA-LOW, JAPAN-HIGH, BRAZIL-HIGH, INDIA-HIGH.
- flow_volumes: {"INDIA-USA":15500, "BRAZIL-USA":12500, "JAPAN-JAPAN":15000, ...}
Optimal shipment plan (units) from production country to market.
- local_prod, export_prod, total_prod: 18050, 30900, 48950
Local vs. export volume with total = demand feasibility check.
- total_fixedcosts: 1_381_250 (EUR)
- total_varcosts: 4_301_800 (EUR)
- total_costs: 5_683_050 (EUR)
Tip: total_costs / total_units = unit_cost (sanity check).
- total_units: 48950
- unit_cost: 116.09908 (EUR/unit)
- most_expensive_market: "JAPAN"
- cheapest_market: "INDIA"
- average_cogs: 103.6097 (EUR/unit across markets)
- unit_energy: 722.4208 (MJ/unit)
- unit_water: 3318.284 (L/unit)
- unit_waste: 0.6153 (kg/unit)
- unit_co2: 35.5485 (kgCO₂e/unit)
"""
This part describes to the agent the outputs it will receive.
I did not want to only count on “self-explicit” naming of variables in the JSON.
I wanted ot make sure that it can understand the data it has on hand to provide summaries following the guidelines listed below.
"""
HOW TO READ THIS RUN (based on the sample JSON)
-----------------------------------------------
- Objective = cost: the model opens 4 plants (INDIA-LOW, JAPAN-HIGH, BRAZIL-HIGH, INDIA-HIGH),
heavily exporting from INDIA and BRAZIL to the USA, while JAPAN supplies itself.
- Unit economics: unit_cost ≈ €116.10; total_costs ≈ €5.683M (divide by 1e6 for M€).
- Market economics: “JAPAN” is the most expensive market; “INDIA” the cheapest.
- Localization ratio: local_prod / total_prod = 18,050 / 48,950 ≈ 36.87% local, 63.13% export.
- Footprint per unit: e.g., unit_co2 ≈ 35.55 kgCO₂e/unit. To approximate total CO₂:
unit_co2 * total_units ≈ 35.55 * 48,950 ≈ 1,740,100 kgCO₂e (≈ 1,740 tCO₂e).
QUICK SANITY CHECKS
-------------------
- Demand balance: sum_i flow(i→j) == demand(j) for each market j.
- Capacity: sum_j flow(i→j) ≤ sum_s CAP(i,s) * open(i,s) for each i.
- Unit-cost check: total_costs / total_units == unit_cost.
- If infeasible: your per-unit caps (max_water/energy/waste/CO₂) may be too tight.
TYPICAL USES
------------
- Baseline vs. sustainability: run once with objective="Production Cost", then with
objective="CO2 Emissions" (or Water/Energy) using the same caps to quantify the
trade-off (Δcost, Δunit_CO₂, change in plant openings/flows).
- Narrative for execs: report top flows (e.g., INDIA→USA=15.5k, BRAZIL→USA=12.5k),
open sites, unit cost, and per-unit footprints. Convert costs to M€ with unit_monetary.
EXAMPLES
--------
# Min cost baseline
run_network(LaunchParamsNetwork(objective="Production Cost"))
# Minimize CO₂ with a water cap
run_network(LaunchParamsNetwork(objective="CO2 Emissions", max_water=3500))
# Minimize Water with an energy cap
run_network(LaunchParamsNetwork(objective="Water Usage", max_energy=780))
"""
I share a list of potential scenarios and explanations of the type of analysis I expect using an actual example.
This is far from being concise, but my objective here is to ensure that the agent is equipped to use the tool at its highest potential.
Experiment with the tool: from simple to complex instructions
To test the workflow, I ask the agent to run the simulation with default parameters.
Sample of analysis provided by the conversation agent – (Image by Samir Saci)
As expected, the agent calls the FastAPI microservice, collects the results, and concisely summarises them.
This is cool, but I already had that with my Production Planning Optimisation Agent built with LangGraph and FastAPI.
Example of output analysis of the Production Planning Optimisation Agent – (Image by Samir Saci)
I wanted to explore MCP Servers with Claude Desktop for a more advanced usage.
Supply Chain Director: “I want to have a comparative study of multiple scenario.”
If we come back to the original plan, the idea was to equip our decision-makers (customers who pay us) with a conversational agent that would assist them in their decision-making process.
Let us try a more advanced question:
Here we provide more open questions that reflect the needs of our customers – (Image by Samir Saci)
We explicitly request a comparative study while allowing
Claude Sonnet 4
to be creative in terms of visual rendering.
Claude Agent sharing its plan – (Image by Samir Saci)
To be honest, I was impressed by the dashboard that was generated by Claude, which you can access via this link.
At the top, you can find an executive summary listing what can be considered the most important indicators of this problem.
Executive Summary generated by Claude – (Image by Samir Saci)
The model understood, without being explicitly asked in the prompt, that these four indicators were key to the decision-making process resulting from this study.
At this stage, in my opinion, we already get the added value of incorporating an LLM into the loop.
The following outputs are more conventional and could have been generated with deterministic code.
Financial and Environmental Metrics Summary Table – (Image by Samir Saci)
However, I admit that the creativity of Claude outperformed my own web application with this smart visual showing the plant openings per scenario.
Plant open per scenario – (Image by Samir Saci)
While I was starting to worry about getting replaced by AI, I had a look at the strategic analysis generated by the agent.
Example of trade-off analysis – (Image by Samir Saci)
The approach of comparing each scenario vs a baseline of cost optimisation has never been explicitly requested.
The agent took the initiative to bring up this angle when presenting results.
This appeared to demonstrate the ability to select the appropriate indicators to convey a message effectively using data.
Can we ask open questions?
Let me explore that in the next section.
A Conversation Agent capable of decision-making?
To further explore the capabilities of our new tool and test its potential, I will pose open-ended questions.
Question 1: Trade-off between cost and sustainability
Question 1 – (Image by Samir Saci)
This is the type of question I got when I was in charge of network studies.
Executive Summary – Image by Samir Saci
This appeared to be a recommendation to adopt the Water-optimised strategy to find the perfect balance.
Visuals – (Image by Samir Saci)
It used compelling visuals to support its idea.
I really like the cost vs. environmental impact scatter plot!
Implementation Plan – (Image by Samir Saci)
Unlike some strategy consulting firms, it did not forget the implementation part.
For more details, you can access the complete dashboard at this link.
Let’s try another tricky question.
Question 2: Best CO2 Emissions Performance
What is the best performance for indicator XXX under budget limits – (Image by Samir Saci)
This is a challenging question that required seven runs to answer.
7 runs to answer the question – (Image by Samir Saci)
This was enough to provide the question with the correct solution.
Optimal Solution – (Image by Samir Saci)
What I appreciate the most is the quality of the visuals used to support its reasoning.
Example of visual used – (Image by Samir Saci)
In the visual above, we can see the different scenarios simulated by the tool.
Although we could question the wrong orientation of the (x-axis), the visual remains self-explicit.
Strategic Recommendation – (Image by Samir Saci)
Where I feel beaten by the LLM is when we look at the quanlity and concision of the strategic recommendations.
Considering that these recommendations serve as the primary point of contact with decision-makers, who often lack the time to delve into details, this remains a strong argument in favour of using this agent.
Conclusion
This experiment is a success!
There is no doubt about the added value of MCP Servers compared to the simple AI workflows introduced in the previous articles.
When you have an optimisation module with multiple scenarios (depending on objective functions and constraints), you can leverage MCP servers to enable agents to make decisions based on data.
I would apply this solution to algorithms like
- Production Planning Optimisation Module Case study: Simulate scenarios with different holding and setup costs to understand their impacts.
- Distribution Planning Optimisation Module Case study: Simulate multiple distribution setups with additional warehouses or different capacities for order processing.
- Procurement Optimisation Module Case study: Test the impact of Minimum Order Quantity (MOQ) or ordering costs on the optimal procurement strategy. These are opportunities to equip your entire supply chain with conversation agents (connected to optimisation tools) that can support decision-making.
Can we go beyond operational topics?
The reasoning capacity that Claude showcased in this experiment also inspired me to explore business topics.
A solution presented in one of my YouTube tutorials could be a good candidate for our next MCP integration.
Value chain of the example used in the video – (Image by Samir Saci)
The goal was to support a friend who runs a business in the food and beverage industry.
They sell renewable cups produced in China to coffee shops and bars in Paris.
Value Chain of this business – (Image by Samir Saci)
I wanted to use Python to simulate its entire value chain to identify optimisation levers to maximise its profitability.
Business Planning with Python — Inventory and Cash Flow Management (Image by Samir Saci)
This algorithm, also packaged in a FastAPI microservice, can become your next data-driven business strategy consultant.
Simulation of scenarios to find the optimal setup to maximise profitability – (Image by Samir Saci)
Part of the job involves simulating multiple scenarios to determine the optimal trade-off between several metrics.
I clearly see a conversational agent powered by an MCP server doing the job perfectly.
For more information, have a look at the video linked below
I will share this new experiment in a future article.
Stay tuned!
Looking for inspiration?
You arrived at the end of this article, and you’re ready to set up your own MCP server?
As I shared the initial steps to set up the server with the example of the add
function, you can now implement any functionality.
You don’t need to use a FastAPI microservice.
The tools can be directly created in the same environment where the MCP server is hosted (here locally).
If you are looking for inspiration, I’ve shared dozens of analytics products (solving actual operational problems with source code) in the article linked here.
About Me
Let’s connect on Linkedin and Twitter. I am a Supply Chain Engineer who uses data analytics to improve logistics operations and reduce costs.
For consulting or advice on analytics and sustainable supply chain transformation, feel free to contact me via Logigreen Consulting.
If you are interested in Data Analytics and Supply Chain, look at my website.