sap-wm-mcp
v0.3.2
Published
MCP server for SAP Classic Warehouse Management — connects AI agents to S/4HANA WM via a custom RAP OData V4 service. For systems where EWM is not active.
Maintainers
Readme
sap-wm-mcp
MCP server for SAP Classic Warehouse Management (LE-WM)
Connect AI agents — Claude, Copilot, or any MCP-compatible client — directly to your SAP S/4HANA or ECC classic WM system. Query bin status, find empty storage locations, check stock levels, detect anomalies, create and confirm transfer orders — all through natural language.
EWM has standard OData APIs. Classic WM doesn't. So I built one.
This project ships a custom RAP OData V4 service that exposes classic WM operations as a proper API — and wraps it in an MCP server so AI agents can drive it. Large portions of the SAP install base are still on classic WM. This fills the gap.
The npm package ships 23 tools covering core operations, analytics, shift management, anomaly detection, audit history, proactive replenishment, interim zone reconciliation, and cycle count management. This repository contains the 9 open-sourced tool implementations — the ABAP RAP service source and additional tool source are available separately (see ABAP Service Installation).
Contents
- How it works
- Prerequisites
- Quick Start
- Configuration
- MCP Client Setup
- Tools Reference
- ABAP Service Installation
- Custom RAP Service — Architecture
- Classic WM Tables
- Development
- Roadmap
- License
How it works
AI Agent (Claude / Copilot / any MCP client)
│ MCP protocol (stdio)
▼
sap-wm-mcp ← this package
│ OData V4 (custom RAP service ZSD_WMMCPSERVICE)
▼
SAP S/4HANA / ECC — Classic Warehouse Management
│
├── LAGP (Storage Bin Master)
├── LQUA (Quants / Stock per Bin)
├── LTAK (Transfer Order Header)
├── LTAP (Transfer Order Items)
├── LTBK (Transfer Requirement Header)
├── MARD (IM Stock per Storage Location)
└── FMs L_TO_CREATE_SINGLE · L_TO_CONFIRM · L_TO_CONFIRM_SU · L_TO_CANCELUnlike EWM, classic WM has no standard OData APIs. This package requires a custom RAP OData V4 service (ZSD_WMMCPSERVICE) installed in your SAP system. The MCP server calls that service. See ABAP Service Installation.
Prerequisites
| Requirement | Details |
|---|---|
| SAP S/4HANA or ECC | Classic Warehouse Management (LE-WM) active |
| SAP user | Basic Auth credentials with read access to WM tables + RFC execute on L_TO_CREATE_SINGLE / L_TO_CONFIRM |
| Node.js ≥ 20 | nodejs.org |
| RAP service installed | ZSD_WMMCPSERVICE deployed in your SAP system (see below) |
EWM systems: If your system has EWM (
/SCWM/package present), use sap-ewm-mcp instead — it uses standard SAP APIs and requires no custom ABAP.
Quick Start
Step 1 — Install the ABAP service (one-time per SAP system)
The MCP server calls a custom RAP OData V4 service that must exist in your SAP system. See ABAP Service Installation.
Step 2 — Configure your MCP client
Add the server to your MCP client config with your SAP credentials inline. See MCP Client Setup for Claude Desktop, Claude Code, and Cursor.
No separate install or .env file needed — credentials go directly in the config.
Step 3 — Ask a warehouse question
"Show me all empty bins in warehouse 102"
"Where is material TG0001 stored?"
"What is the utilization of warehouse 102?"
"Show me all open transfer orders"
"Create a TO to move TG0001 from bin 0000000017 to bin 1-013"Configuration
| Variable | Required | Description |
|---|---|---|
| SAP_URL | ✅ | Full URL of your SAP system — e.g. https://172.0.0.21:44300 |
| SAP_CLIENT | ✅ | SAP client number — e.g. 100 |
| SAP_USER | ✅ | SAP username |
| SAP_PASSWORD | ✅ | SAP password |
| SAP_INSECURE | optional | Set true to skip TLS certificate validation (on-premise / self-signed certs) |
MCP Client Setup
Two installation options — choose based on your use case.
| Option | When to use | |---|---| | A — npx (recommended) | Just want to use it. No cloning, no install step. Credentials go inline in the config. | | B — Clone locally | Want to modify tools, extend the service, or contribute. |
Option A — npx (recommended)
No cloning or install required. Credentials are passed as environment variables directly in your MCP client config.
Claude Desktop
Step 1 — Find your config file:
| OS | Path |
|---|---|
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
Step 2 — Add the server entry:
{
"mcpServers": {
"sap-wm-mcp": {
"command": "npx",
"args": ["sap-wm-mcp"],
"env": {
"SAP_URL": "https://your-sap-host:44300",
"SAP_CLIENT": "100",
"SAP_USER": "your-user",
"SAP_PASSWORD": "your-password",
"SAP_INSECURE": "true"
}
}
}
}Step 3 — Restart Claude Desktop.
The sap-wm-mcp tools appear automatically in the tools panel. You'll see a hammer icon — click it to confirm the WM tools are loaded.
Note: If you already have other MCP servers configured, add
sap-wm-mcpas an additional entry inside"mcpServers"— do not replace the whole file.
Claude Code
Step 1 — Create .mcp.json in your project root (or add to an existing one):
{
"mcpServers": {
"sap-wm-mcp": {
"type": "stdio",
"command": "npx",
"args": ["sap-wm-mcp"],
"env": {
"SAP_URL": "https://your-sap-host:44300",
"SAP_CLIENT": "100",
"SAP_USER": "your-user",
"SAP_PASSWORD": "your-password",
"SAP_INSECURE": "true"
}
}
}
}Step 2 — Verify the tools are loaded:
/mcp
.mcp.jsonis project-scoped. Add it to.gitignore— it contains credentials.
Windows users: If the server doesn't appear after restart, see Windows troubleshooting below.
Claude Code — Windows
On Windows, Claude Code may silently fail to launch npx or node directly (known issue with process spawning). If the server doesn't appear in /mcp, use this pattern instead.
Step 1 — Create a wrapper script (requires Git for Windows):
Save as scripts/run-sap-wm-mcp.sh in your project:
#!/bin/bash
SCRIPT_DIR="$(cd "${BASH_SOURCE[0]%/*}" && pwd)"
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
exec npx --prefix "$ROOT_DIR" sap-wm-mcpOr to load credentials from a .env file instead of inline in .mcp.json:
#!/bin/bash
SCRIPT_DIR="$(cd "${BASH_SOURCE[0]%/*}" && pwd)"
ENV_FILE="$(cd "$SCRIPT_DIR/.." && pwd)/.env"
if [ -f "$ENV_FILE" ]; then
set -a; source "$ENV_FILE"; set +a
else
echo "ERROR: .env not found" >&2; exit 1
fi
exec npx sap-wm-mcpStep 2 — Update .mcp.json to use bash.exe as the command:
{
"mcpServers": {
"sap-wm-mcp": {
"command": "C:/Program Files/Git/usr/bin/bash.exe",
"args": [
"C:/absolute/path/to/your/project/scripts/run-sap-wm-mcp.sh"
]
}
}
}Use an absolute path with forward slashes. No env block needed if credentials are in .env.
Step 3 — Skip the approval dialog (optional but recommended):
Create .claude/settings.local.json in your project root:
{
"enableAllProjectMcpServers": true
}This prevents a silently-dismissed approval prompt from blocking the server on every restart.
Step 4 — Restart Claude Code. Run /mcp to verify the tools are loaded.
Note: If the server was previously rejected in the approval dialog, run
claude mcp reset-project-choicesbefore restarting.
Cursor, Windsurf, and other MCP clients
Any MCP client that supports stdio transport works. Use the same env block approach.
{
"mcpServers": {
"sap-wm-mcp": {
"command": "npx",
"args": ["sap-wm-mcp"],
"env": {
"SAP_URL": "https://your-sap-host:44300",
"SAP_CLIENT": "100",
"SAP_USER": "your-user",
"SAP_PASSWORD": "your-password",
"SAP_INSECURE": "true"
}
}
}
}Option B — Clone locally (for developers)
git clone https://github.com/CodeOfHANA/sap-wm-mcp.git
cd sap-wm-mcp
npm install
cp .env.example .env # fill in your SAP credentials
node index.jsThen point your MCP client at the local file:
Claude Desktop / macOS / Linux:
{
"mcpServers": {
"sap-wm-mcp": {
"command": "node",
"args": ["/absolute/path/to/sap-wm-mcp/index.js"]
}
}
}Claude Code on Windows — use the bash wrapper pattern (see Windows troubleshooting) with exec node "$ROOT_DIR/index.js" in the script instead of npx.
Verify it's working
"Show me the status of bins in warehouse 102"
"How many empty bins are in storage type 003?"
"Where is material TG0001 stored?"
"What is the overall utilization of warehouse 102?"For write operations:
"Move 10 ST of TG0001 from bin 0000000017 to bin 1-013 in warehouse 102, movement type 999, plant 1010"
"Confirm transfer order 652 in warehouse 102"Example conversations
Inventory queries
"Give me a full picture of warehouse 102 — utilization, empty bins, and where the stock is"
"Which bins in storage type 003 still have capacity?"
"How much stock of TG0001 do we have, and in which bins?"Transfer order operations
"Create a transfer order to move 5 ST of TG0001 from bin 0000000017 (type 999)
to bin 1-014 (type 003), warehouse 102, movement type 999, plant 1010"
"Confirm transfer order number 0000000654 in warehouse 102"
"Confirm all transfer orders on storage unit 00000000001000000017"Analytics and shift management
"Run a shift health check for warehouse 102 — open TOs, negative stock, GR area, anomalies"
"Which bins haven't moved stock in over 90 days?"
"Are there any negative quants I need to investigate?"
"Which materials have more WM stock than IM stock?"
"Find bins due for cycle counting"
"Are there any fragmented quants that need consolidation TOs?"Audit and history
"Show me all transfer orders created in warehouse 102 this month"
"Which TOs did NOMANH create last week?"
"Show me all open TOs created more than 3 days ago — something is stuck"
"Give me a history of movement type 999 TOs for material TG0001"Tools Reference
The npm package ships 23 tools across five capability areas. The 9 tools below are open-sourced in this repository. Analytics, shift management, anomaly detection, cycle count management, and operations tools are available in the published package.
Core Operations
get_bin_status
Query storage bins in a classic WM warehouse. Returns empty/blocked status, capacity, weight, and last movement date.
| Parameter | Type | Required | Description |
|---|---|---|---|
| warehouse | string | ✅ | Warehouse number — e.g. 102 |
| storageType | string | | Filter by storage type — e.g. 001 |
| bin | string | | Filter by specific bin number — e.g. 1-013 |
| top | number | | Max records to return (default: 20) |
get_stock_for_material
Get physical WM stock for a material — which bins hold it and how much.
| Parameter | Type | Required | Description |
|---|---|---|---|
| warehouse | string | ✅ | Warehouse number |
| material | string | | Material number — e.g. TG0001 |
| storageType | string | | Filter by storage type |
| top | number | | Max records (default: 20) |
find_empty_bins
Find all empty storage bins, optionally filtered by storage type or bin type.
| Parameter | Type | Required | Description |
|---|---|---|---|
| warehouse | string | ✅ | Warehouse number |
| storageType | string | | Filter by storage type |
| binType | string | | Filter by bin type — e.g. E1, E2. Use when the destination bin must match a specific storage unit type and SAP rejects mismatched types |
| top | number | | Max records (default: 50) |
get_bin_utilization
Get bin utilization statistics — occupied vs. empty vs. blocked, grouped by storage type.
| Parameter | Type | Required | Description |
|---|---|---|---|
| warehouse | string | ✅ | Warehouse number |
| storageType | string | | Filter by storage type |
| top | number | | Max bins to analyze (default: 100) |
get_stock_by_type
List all stock grouped by storage type — useful for understanding what is in each zone.
| Parameter | Type | Required | Description |
|---|---|---|---|
| warehouse | string | ✅ | Warehouse number |
| storageType | string | | Filter by specific storage type |
| top | number | | Max records (default: 100) |
get_open_transfer_orders
List open (unconfirmed) Transfer Orders with their items, age, and bin details.
| Parameter | Type | Required | Description |
|---|---|---|---|
| warehouse | string | ✅ | Warehouse number |
| storageType | string | | Filter by source or destination storage type |
| material | string | | Filter by material |
| top | number | | Max TO headers (default: 50) |
Write Operations
create_transfer_order
Create a classic WM Transfer Order — moves stock from a source bin to a destination bin. Equivalent to transaction LT01.
Internally calls L_TO_CREATE_SINGLE via an RFC-enabled wrapper, isolated from the RAP LUW using DESTINATION 'NONE'.
| Parameter | Type | Required | Description |
|---|---|---|---|
| warehouse | string | ✅ | Warehouse number |
| movementType | string | ✅ | WM movement type — e.g. 999 (manual relocation) |
| material | string | ✅ | Material number |
| plant | string | ✅ | Plant — e.g. 1010 |
| quantity | number | ✅ | Quantity to move |
| unitOfMeasure | string | ✅ | Unit of measure — e.g. ST, KG |
| sourceType | string | | Source storage type |
| sourceBin | string | | Source bin |
| sourceStorageUnit | string | | Source storage unit (LENUM) — required for SU-managed source types |
| destType | string | ✅ | Destination storage type |
| destBin | string | ✅ | Destination bin |
| destStorageUnit | string | | Destination storage unit (LENUM) — for SU-managed destination types |
| autoConfirm | boolean | | When true, the TO is created and immediately confirmed in a single call — no separate confirm_transfer_order step needed. Maps to I_SQUIT = 'X' in L_TO_CREATE_SINGLE. Default: false. |
autoConfirm: Use for internal replenishment or relocation moves where no physical scan or operator acknowledgement is required. Do not use for inbound putaway from GR zone or outbound picks where confirmation must happen on the warehouse floor.
Note on SU-managed storage types: Storage types with
LPTYPset inLAGP(e.g. type001) require adestStorageUnit(LENUM) when used as the TO destination. Storage types withLPTYPblank (e.g. type003) do not.
confirm_transfer_order
Confirm an open Transfer Order by number — marks it as physically executed. Equivalent to transaction LT12.
| Parameter | Type | Required | Description |
|---|---|---|---|
| warehouse | string | ✅ | Warehouse number |
| transferOrderNumber | string | ✅ | Transfer order number — e.g. 0000000652 |
confirm_transfer_order_su
Confirm all open Transfer Orders on a storage unit in one call — useful for SU-managed warehouses.
| Parameter | Type | Required | Description |
|---|---|---|---|
| warehouse | string | ✅ | Warehouse number |
| storageUnit | string | ✅ | Storage unit number (LENUM) |
cancel_transfer_order
Cancel an open Transfer Order — removes it from the active TO queue. Equivalent to transaction LT15.
Internally calls L_TO_CANCEL. Returns a clear error message for each SAP exception (already confirmed, locked, partially confirmed, etc.).
| Parameter | Type | Required | Description |
|---|---|---|---|
| warehouse | string | ✅ | Warehouse number |
| transferOrderNumber | string | ✅ | Transfer order number — e.g. 0000000652 |
Note: Only open (unconfirmed) TOs can be cancelled. Confirmed TOs, partially confirmed TOs, and TOs locked by another user will return a descriptive error.
Additional Tools (npm package)
The following tools are available in the published npm package and fully functional via npx sap-wm-mcp:
| Tool | Description |
|---|---|
| get_transfer_requirements | Open TRs — the demand side before a TO is created, flagged by age |
| get_wm_im_variance | Compare WM stock (LQUA) vs IM stock (MARD) — surfaces reconciliation discrepancies |
| get_cycle_count_candidates | Bins due for cycle counting, ordered by ABC class and days since last count |
| get_stock_aging | Stock not moved in N days — surface slow movers and forgotten inventory |
| get_negative_stock_report | All negative quants with likely cause diagnosis |
| get_goods_receipt_monitor | GR staging area status and open inbound TRs — start-of-shift inbound check |
| get_quant_fragmentation | Bin+material combinations with excessive quant count — consolidation candidates |
| get_unresolved_su_negatives | Persistent negative quants in SU zones older than a configurable age |
| get_inventory_anomalies | Bins stuck in mid-inventory state — empty bins with locks, open count docs, orphaned lock codes |
| get_transfer_order_history | Full TO history — creator, executor (resolved from LTAP.QNAME), and item detail; filterable by date range, status, movement type, material, createdBy, or executedBy |
| get_replenishment_needs | Find forward-pick bins at or below a stock threshold — defaultReplenishQty param (default 50) used as fallback when no bin max qty is configured; flags bins with an open replenishment TO to avoid duplicate moves |
| get_interim_zone_anomalies | Detect positive stock stranded in interim/staging zones (types 999, 998, 902) — surfaces same-day, overnight, and multi-day strandings with likely cause per zone; use minDaysStranded to filter noise during active shifts |
| get_goods_issue_monitor | Open outbound deliveries — delivery qty vs picked qty, GI status, overdue flag, and stock still in GI zone (type 999). Tracks picking progress and stalled shipments. |
| create_cycle_count_document | Create a WM inventory document (LI01N equivalent) for a specific bin. Activates the bin immediately by default (LAGP.KZINV = 'IL'). Use after get_cycle_count_candidates to trigger a count. |
ABAP Service Installation
The MCP server calls a custom RAP OData V4 service (ZSD_WMMCPSERVICE) that must be installed in your SAP system.
The ABAP source is not included in this public repository. To obtain the ABAP package for installation in your system, open an issue or reach out via GitHub.
What gets installed
| Object | Type | Description |
|---|---|---|
| ZWM_MCP | Package | Container for all objects |
| ZR_WMSTORAGBIN | CDS View | Interface view over LAGP (bin master) |
| ZC_WMSTORAGBIN | CDS View | Projection view for WMStorageBin entity |
| ZR_WMWAREHOUSESTOCK | CDS View | Interface view over LQUA (stock per bin) |
| ZC_WMWAREHOUSESTOCK | CDS View | Projection view for WMWarehouseStock entity |
| ZR_WMTRANSFERORDER | CDS View | Interface view over LTAK (TO headers) |
| ZR_WMTRANSFERORDERITEM | CDS View | View over LTAP (TO items) |
| ZR_WMTRANSFERREQUIREMENT | CDS View | View over LTBK + LTBP (transfer requirements) |
| ZR_WMIMSTOCK | CDS View | View over MARD + MARA (IM stock for variance) |
| ZR_WMCYCLECOUNTBIN | CDS View | View over LAGP (cycle count indicators) |
| ZR_WMTRANSFERORDER | BDEF | Behavior definition — defines actions |
| ZBP_R_WMTRANSFERORDER | Class | RAP behavior implementation |
| ZR_WMGOODSISSUEDELIVERY | CDS View | View over LIKP + LIPS (outbound deliveries for GI monitor) |
| ZR_WMTRANSFERORDER | BDEF | Behavior definition — defines actions |
| ZBP_R_WMTRANSFERORDER | Class | RAP behavior implementation |
| ZWM_MFG | Function Group | Contains all RFC wrapper FMs |
| ZWM_TO_CREATE | Function Module | RFC-enabled wrapper for L_TO_CREATE_SINGLE |
| ZWM_TO_CANCEL | Function Module | RFC-enabled wrapper for L_TO_CANCEL |
| ZWM_INV_CREATE | Function Module | RFC-enabled wrapper — writes directly to LINK/LINP/LINV and locks bin in LAGP |
| ZA_WMCREATETOPARAM | Abstract Entity | Parameter type for CreateTransferOrder action |
| ZA_WMCONFIRMTOSUPARAM | Abstract Entity | Parameter type for ConfirmTransferOrderSU action |
| ZA_WMCREATEINVDOCPARAM | Abstract Entity | Parameter type for CreateInventoryDocument action |
| ZSD_WMMCPSERVICE | Service Def | OData V4 service definition (8 entity sets) |
| ZSB_WMMCPSERVICE_ODATA4_UI | Service Binding | OData V4 UI binding |
abapGit compatibility
If you are installing these objects via abapGit, be aware of a version compatibility issue with BDEF serialization. Some abapGit releases reference a SYNTAX_CONFIGURATION field in the BDEF metadata structure (CL_BLUE_SOURCE_OBJECT_DATA=>TY_OBJECT_DATA-METADATA) that was introduced in a later S/4HANA kernel/SP level. If your system is on an older kernel and you hit a short dump (ASSERTION_FAILED in zcl_abapgit_object_bdef→clear_field) when pulling this package, downgrade your abapGit standalone to a version compatible with your kernel level.
This is a known abapGit issue — not specific to this package. The ABAP objects in this repo are plain source files and are portable across all kernel versions.
Verify the service
Once installed, verify the service is reachable:
GET https://<your-host>:44300/sap/opu/odata4/iwbep/all/srvd/sap/zsd_wmmcpservice/0001/WMStorageBin?$top=3
Authorization: Basic <base64>
sap-client: 100Expect HTTP 200 with bin data.
Custom RAP Service — Architecture
Classic WM has no standard OData APIs. This project builds one using ABAP RESTful Application Programming Model (RAP).
Entity sets
| OData Entity | Source Table(s) | Operations |
|---|---|---|
| WMStorageBin | LAGP | Read, Filter |
| WMWarehouseStock | LQUA | Read, Filter |
| WMTransferOrder | LTAK | Read + Actions |
| WMTransferOrderItem | LTAP | Read |
| WMTransferRequirement | LTBK + LTBP | Read |
| WMIMStock | MARD + MARA | Read |
| WMCycleCountBin | LAGP | Read |
Actions
| Action | FM Internally | Pattern |
|---|---|---|
| CreateTransferOrder | L_TO_CREATE_SINGLE | Called via RFC wrapper ZWM_TO_CREATE with DESTINATION 'NONE' |
| ConfirmTransferOrder | L_TO_CONFIRM | Called directly — no COMMIT needed |
| ConfirmTransferOrderSU | L_TO_CONFIRM_SU | Called directly — no COMMIT needed |
| CancelTransferOrder | L_TO_CANCEL | Called via RFC wrapper ZWM_TO_CANCEL with DESTINATION 'NONE' |
| CreateInventoryDocument | Direct writes to LINK/LINP/LINV | Called via RFC wrapper ZWM_INV_CREATE with DESTINATION 'NONE' |
Why the RFC wrapper?
L_TO_CREATE_SINGLE internally calls COMMIT WORK and CALL FUNCTION ... IN UPDATE TASK. Both are illegal inside a RAP action handler — they cause a BEHAVIOR_ILLEGAL_STATEMENT runtime error.
The solution: an RFC-enabled wrapper FM (ZWM_TO_CREATE) called via DESTINATION 'NONE'. This creates a loopback RFC session. The COMMIT WORK runs in that isolated session — it does not touch the RAP handler's LUW.
L_TO_CONFIRM does not commit and can be called directly from the RAP handler.
Service URL pattern
/sap/opu/odata4/iwbep/all/srvd/sap/zsd_wmmcpservice/0001/{EntitySet}Classic WM Tables
| Table | Description | Key Fields |
|---|---|---|
| LAGP | Storage Bin Master | LGNUM, LGTYP, LGPLA — capacity, LPTYP (SU management), KZINV (inventory lock), KZLER (empty flag) |
| LQUA | Quants (Stock per Bin) | LGNUM, LGTYP, LGPLA, LQNUM, MATNR — GESME (total stock), EINME (in-transfer qty), BDATU (last movement date) |
| LTAK | Transfer Order Header | LGNUM, TANUM — BWLVS (movement type), BDATU (creation date). No STATUS field — derive from LTAP |
| LTAP | Transfer Order Items | LGNUM, TANUM, TAPOS — NSOLM (planned qty), NISTM (confirmed qty), VLTYP/VLPLA (source), NLTYP/NLPLA (destination) |
| LTBK | Transfer Requirement Header | LGNUM, TBNUM — STATU (' '=open, 'B'=partial, 'T'=TO created, 'E'=complete) |
| LTBP | Transfer Requirement Items | LGNUM, TBNUM, TBPOS — material, qty, source bin |
| MARD | IM Stock per Storage Location | MATNR, WERKS, LGORT — LABST (unrestricted stock) |
Common naming traps:
LGPLAis a field name (bin number), not a table — the bin master table isLAGP. The stock quantity field in LQUA isGESME, notLGMNG.
Development
Run locally
git clone https://github.com/CodeOfHANA/sap-wm-mcp.git
cd sap-wm-mcp
npm install
cp .env.example .env
node index.jsProject structure
sap-wm-mcp/
├── index.js ← MCP server — all tools registered
├── lib/
│ ├── s4hClient.js ← s4hGet + s4hPost (OData V4 HTTP client)
│ └── sanitize.js ← esc() — OData filter injection prevention
├── tools/
│ ├── binStatus.js ← get_bin_status
│ ├── stockByMaterial.js ← get_stock_for_material
│ ├── emptyBins.js ← find_empty_bins
│ ├── binUtilization.js ← get_bin_utilization
│ ├── stockByType.js ← get_stock_by_type
│ ├── openTransferOrders.js ← get_open_transfer_orders
│ ├── createTransferOrder.js ← create_transfer_order
│ ├── confirmTransferOrder.js ← confirm_transfer_order
│ ├── confirmTransferOrderSU.js ← confirm_transfer_order_su
│ ├── cancelTransferOrder.js ← cancel_transfer_order
│ ├── transferOrderHistory.js ← get_transfer_order_history
│ ├── replenishmentNeeds.js ← get_replenishment_needs
│ ├── interimZoneAnomalies.js ← get_interim_zone_anomalies
│ ├── goodsIssueMonitor.js ← get_goods_issue_monitor
│ └── createCycleCountDoc.js ← create_cycle_count_document
├── .env.example
└── package.jsonAdding a new tool
- Create
tools/myTool.js— alwaysimport { esc } from '../lib/sanitize.js'and useesc()on every string OData filter param - Always use
data.value ?? [](never baredata.value) - Always return
truncated: rows.length === topon paginated responses - Import and register in
index.jsusingserver.tool(name, description, schema, handler)
Companion project
sap-ewm-mcp — the same MCP interface for SAP Extended Warehouse Management. Uses only standard SAP OData APIs, no custom ABAP required.
Running both side-by-side shows the contrast directly: same tools, same questions — EWM uses plug-and-play APIs, WM required building the socket first.
Roadmap
| Phase | Status | Description | |---|---|---| | Phase 0 — RAP Service | ✅ Complete | Custom OData V4 service with 7 entity sets over classic WM tables | | Phase 1 — Local MCP | ✅ Complete | 23 tools working, security hardened, published to npm | | Phase 2 — BTP CF | 🔜 Planned | Deploy to SAP BTP Cloud Foundry with SSE transport + XSUAA + Cloud Connector | | Phase 3 — Joule Agent | 💡 Future | Native Joule Studio agent using the same RAP service |
License
MIT — see LICENSE
Built by Noman Mohamed Hanif · Senior SAP Technology Consultant @ RELACON IT Consulting GmbH, Hamburg
