npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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.

Readme

sap-wm-mcp

npm version npm downloads License: MIT Node.js

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

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_CANCEL

Unlike 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-mcp as 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.json is 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-mcp

Or 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-mcp

Step 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-choices before 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.js

Then 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 LPTYP set in LAGP (e.g. type 001) require a destStorageUnit (LENUM) when used as the TO destination. Storage types with LPTYP blank (e.g. type 003) 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: 100

Expect 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, MATNRGESME (total stock), EINME (in-transfer qty), BDATU (last movement date) | | LTAK | Transfer Order Header | LGNUM, TANUMBWLVS (movement type), BDATU (creation date). No STATUS field — derive from LTAP | | LTAP | Transfer Order Items | LGNUM, TANUM, TAPOSNSOLM (planned qty), NISTM (confirmed qty), VLTYP/VLPLA (source), NLTYP/NLPLA (destination) | | LTBK | Transfer Requirement Header | LGNUM, TBNUMSTATU (' '=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, LGORTLABST (unrestricted stock) |

Common naming traps: LGPLA is a field name (bin number), not a table — the bin master table is LAGP. The stock quantity field in LQUA is GESME, not LGMNG.


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.js

Project 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.json

Adding a new tool

  1. Create tools/myTool.js — always import { esc } from '../lib/sanitize.js' and use esc() on every string OData filter param
  2. Always use data.value ?? [] (never bare data.value)
  3. Always return truncated: rows.length === top on paginated responses
  4. Import and register in index.js using server.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