Skip to main content
Glama
horsemanjackyliu

mcp4odata

README.md
# mcp4odata

An MCP (Model Context Protocol) server that exposes SAP S/4HANA OData services as tools for LLMs. Deployed on SAP Cloud Foundry (BTP).

## Overview

```
LLM / MCP Client
      │  Bearer token (XSUAA)
      ▼
mcp4odata  (CF app)
      │  SAP Cloud SDK → BTP Destination Service
      ▼
SAP S/4HANA  (CE_SALESORDER_0001, OData v4)
```

- Inbound auth: XSUAA Bearer token validation via `@sap/xssec`
- Outbound connectivity: SAP Cloud SDK `executeHttpRequest()` with BTP Destination Service
- Two MCP transports: Streamable HTTP (MCP 2025-11-25) and SSE fallback (MCP 2024-11-05)

---

## Tools

### `salesorder_get_list`

Retrieves a list of S/4HANA sales order headers.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `soldToParty` | string | No | Filter by customer number, e.g. `10100001` |
| `salesOrganization` | string | No | Filter by sales org, e.g. `1710` |
| `top` | integer | No | Max results, 1–100 (default: 20) |

Returns: array of sales order headers — order number, type, sold-to party, total net amount, currency, requested delivery date.

---

### `salesorder_create`

Creates a new S/4HANA sales order with one or more line items (deep-insert POST).

**Header fields**

| Parameter | Type | Required | Description |
|---|---|---|---|
| `salesOrderType` | string | Yes | Order type, e.g. `OR` |
| `salesOrganization` | string | Yes | Sales org, e.g. `1710` |
| `distributionChannel` | string | Yes | Distribution channel, e.g. `10` |
| `organizationDivision` | string | Yes | Division, e.g. `00` |
| `soldToParty` | string | Yes | Customer number, e.g. `10100001` |
| `transactionCurrency` | string | No | ISO currency, e.g. `USD` (defaults from customer master) |
| `purchaseOrderByCustomer` | string | No | Customer PO number |
| `requestedDeliveryDate` | string | No | Delivery date, `YYYY-MM-DD` |

**Item fields** (at least one item required)

| Parameter | Type | Required | Description |
|---|---|---|---|
| `material` | string | Yes | Product number, e.g. `MZ-FG-M500` |
| `requestedQuantity` | string | Yes | Quantity as a string, e.g. `5` |
| `requestedQuantitySAPUnit` | string | Yes | SAP unit of measure, e.g. `EA`, `KG` |
| `plant` | string | No | Delivering plant, e.g. `1710` |
| `salesOrderItemCategory` | string | No | Item category, e.g. `TAN` (derived from config if omitted) |
| `salesOrderItemText` | string | No | Line item description (defaults to material description) |

Returns: created sales order header including the generated order number.

---

## Endpoints

| Endpoint | Auth | Description |
|---|---|---|
| `POST/GET/DELETE /mcp` | Bearer (XSUAA) | Streamable HTTP transport — MCP 2025-11-25 |
| `GET /sse` | Bearer (XSUAA) | SSE transport — MCP 2024-11-05 |
| `POST /messages` | Bearer (XSUAA) | SSE message posting |
| `GET /health` | None | Health check (used by CF) |

---

## Local Development

### Prerequisites

- Node.js 20+
- SAP BTP subaccount with XSUAA and Destination service instances

### Setup

```bash
npm install
cp .env.example .env
# Fill in VCAP_SERVICES with your XSUAA and Destination credentials
```

`.env` structure (see `.env.example` for the full template):

```
VCAP_SERVICES='{ "xsuaa": [...], "destination": [...] }'
S4_DESTINATION_NAME=S4HANA_SALESORDER
PORT=3000
```

### Run

```bash
npm run dev        # tsx watch mode
npm run build      # compile TypeScript → dist/
```

### Test with MCP Inspector

Point the inspector at `http://localhost:3000/mcp` with a valid XSUAA Bearer token.

---

## BTP Destination Configuration

Create an HTTP destination in the BTP cockpit with:

| Field | Value |
|---|---|
| Name | `S4HANA_SALESORDER` (or your `S4_DESTINATION_NAME`) |
| Type | HTTP |
| URL | Your S/4HANA base URL |
| Authentication | OAuth2ClientCredentials |
| Token Service URL | XSUAA token endpoint for S/4HANA |

The SAP Cloud SDK reads the Destination service binding from `VCAP_SERVICES`, fetches the destination, and injects the Authorization header automatically.

---

## Cloud Foundry Deployment

### Prerequisites

```bash
brew install cloudfoundry/tap/cf-cli@8
cf login -a https://api.cf.<region>.hana.ondemand.com
cf target -o <org> -s <space>
```

### Create required service instances

```bash
cf create-service xsuaa application xsuaa-mcp4odata -c xsuaa-config.json
cf create-service destination lite destination-mcp4odata
```

### Deploy

```bash
npm run build
cf push
cf logs mcp4odata --recent
```

### Useful CF commands

```bash
cf env mcp4odata                          # inspect bound service credentials
cf set-env mcp4odata S4_DESTINATION_NAME  # override env variable
cf restage mcp4odata                      # restage after env changes
```

---

## Project Structure

```
src/
├── server.ts           # Express app, MCP transports, route setup
├── auth/
│   └── middleware.ts   # XsuaaTokenVerifier — inbound Bearer token validation
├── odata/
│   └── client.ts       # getSalesOrders(), createSalesOrder() via SAP Cloud SDK
└── tools/
    ├── get-sales-orders.ts    # salesorder_get_list tool
    └── create-sales-order.ts  # salesorder_create tool
manifest.yml            # CF deployment manifest
.env.example            # Local dev environment template
```

## Tech Stack

| Layer | Technology |
|---|---|
| MCP Framework | `@modelcontextprotocol/sdk` |
| OData HTTP | `@sap-cloud-sdk/http-client` |
| Destination resolution | `@sap-cloud-sdk/connectivity` |
| Inbound auth | `@sap/xssec`, `@sap/xsenv` |
| Web server | Express 5 |
| Language | TypeScript (Node.js 20+) |
| Deployment | SAP Cloud Foundry |