guniweb-sap-mcp 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +40 -5
  2. package/dist/index.js +122 -51
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -6,7 +6,7 @@ Connect n8n workflows to SAP S/4HANA and ECC via OData V2, V4, IDocs, and RFC/BA
6
6
 
7
7
  ```mermaid
8
8
  graph LR
9
- n8n["n8n workflow<br/>AI-assisted, human-governed"] -->|MCP Protocol| MCP["guniweb-sap-mcp<br/>22 Tools &bull; 7 Auth Types"]
9
+ n8n["n8n workflow<br/>AI-assisted, human-governed"] -->|MCP Protocol| MCP["guniweb-sap-mcp<br/>22 Tools &bull; 8 Auth Types"]
10
10
  MCP -->|"OData V2/V4<br/>IDoc XML<br/>RFC/BAPI"| SAP["SAP S/4HANA & ECC"]
11
11
  MCP -->|"OAuth2 / JWT"| BTP["SAP BTP"]
12
12
  BTP --> SAP
@@ -41,12 +41,13 @@ graph LR
41
41
  - **IDoc / RFC auto-disable** -- tools hidden when the corresponding configuration is missing
42
42
  - **MCP Annotations** -- readOnlyHint, destructiveHint, idempotentHint on all 22 tools
43
43
 
44
- ### Authentication (7 Types)
44
+ ### Authentication (8 Types)
45
45
 
46
46
  ```mermaid
47
47
  graph TB
48
48
  subgraph Technical["Technical User Auth"]
49
49
  basic["basic<br/><i>Direct S/4HANA</i>"]
50
+ userbasic["user-basic<br/><i>Personal SAP login per request</i>"]
50
51
  oauth2["oauth2<br/><i>BTP Client Credentials</i>"]
51
52
  apikey["apikey<br/><i>Sandbox / Testing</i>"]
52
53
  end
@@ -73,6 +74,18 @@ graph TB
73
74
 
74
75
  ## Quick Start
75
76
 
77
+ ### Try it without an SAP system: `--demo`
78
+
79
+ No SAP system at hand? Start the server against a built-in mock S/4HANA and try every tool from n8n or any MCP client:
80
+
81
+ ```bash
82
+ npx guniweb-sap-mcp --demo # stdio — n8n MCP Client in command mode
83
+ npx guniweb-sap-mcp --demo --transport http --port 8808 # HTTP — n8n MCP Client with URL http://localhost:8808/mcp
84
+ npx guniweb-sap-mcp --demo --allow-write # also try sap_create / sap_update / sap_delete / sap_batch
85
+ ```
86
+
87
+ The demo starts an in-memory OData V2 gateway on localhost (three services with real SAP field names — `API_BUSINESS_PARTNER`, `API_SALES_ORDER_SRV`, `API_PRODUCT_SRV`; ~20 business partners, 15 sales orders with items, 10 products) and points the server at it. It behaves like a Gateway where it matters: catalog discovery, `$metadata`, `$filter`/`$expand`/paging, CSRF tokens, ETags, deep insert, function imports, `$batch` with changesets, SAP-style error payloads. Sample data only — nothing is real, nothing leaves your machine, and changes are gone when the process ends. Also `SAP_MCP_DEMO=true` (e.g. in Docker). A workflow you build against the demo runs against a real system after you replace `--demo` with your `SAP_*` configuration.
88
+
76
89
  ### 1. Install
77
90
 
78
91
  ```bash
@@ -192,7 +205,8 @@ guniweb-sap-mcp --transport http --port 8808 --destinations /config/destinations
192
205
  }
193
206
  ```
194
207
 
195
- - **One entry = one connection.** Every entry accepts exactly the fields the `SAP_*` variables accept (`authType` and its credentials, `sapClient`, `baseUrl`) — all seven authentication types work per destination.
208
+ - **One entry = one connection.** Every entry accepts exactly the fields the `SAP_*` variables accept (`authType` and its credentials, `sapClient`, `baseUrl`) — all authentication types work per destination.
209
+ - **Personal SAP login per request** — `"authType": "user-basic"`: the destination knows system and client only; SAP user and password come with each request (`X-SAP-Username`/`X-SAP-Password`, in n8n the credential of the [GuniWeb SAP node](https://github.com/guniweb/n8n-nodes-guniweb-sap)). Everyone acts in SAP as themselves — authorizations, change documents and audit trail on the real user, no shared technical account. No fallback to a technical user (`401` without a login), caches per user, passwords never logged. HTTP transport only. Details in the [setup guide](https://github.com/guniweb/guniweb-sap-mcp/blob/main/docs/setup-guide.md#personal-sap-login-per-request-user-basic).
196
210
  - **Secrets stay out of the file.** Any string may contain `${VARIABLE}`; it is resolved from the environment at load time (Docker secrets, `.env`). A placeholder that cannot be resolved makes the file invalid — an empty password would otherwise surface as a misleading `401` from SAP.
197
211
  - **Hot reload.** The file is watched; a change is picked up on the next request, no restart. `kill -HUP <pid>` forces a reload. An invalid file **never** replaces the running configuration — the error is logged and the last valid one stays in force.
198
212
  - **Backwards compatible.** Without `--destinations` nothing changes: the `SAP_*` variables are the single destination `default`. With the file, `SAP_*` (if set) is added as `default` unless the file defines one — so an installation can migrate without a gap.
@@ -234,6 +248,27 @@ guniweb-sap-mcp destinations remove s4test [--force] # refuses while token
234
248
 
235
249
  - **Per-token permissions** — `"policy": { "readOnly": true, "tiers": ["core", "odata"] }`. A policy only ever *restricts*: `readOnly: true` hides the write tools for that token even when the server runs with `--allow-write` (`readOnly: false` cannot open a read-only server), and `tiers` is a ceiling — tiers outside it are invisible for that token and `sap_enable_tools` cannot switch them on. `tools/list` is filtered per request, so an agent on a read-only token never sees `sap_create` in the first place.
236
250
 
251
+ **Admin API — the same operations over HTTP (self-service).** With `--admin-token <secret>` (or `SAP_MCP_ADMIN_TOKEN`, at least 16 characters, e.g. `openssl rand -base64 32`) the server exposes the CLI's operations under `/admin` — for an n8n workflow that provisions a new customer's access, a script, or a person with `curl`, without a shell on the host:
252
+
253
+ ```bash
254
+ guniweb-sap-mcp --transport http --port 8808 --destinations /config/destinations.json --admin-token "$ADMIN"
255
+
256
+ A="Authorization: Bearer $ADMIN"; J="Content-Type: application/json"; U=http://localhost:8808/admin
257
+ curl -H "$A" $U/destinations # list — never secrets
258
+ curl -H "$A" -H "$J" -X PUT $U/destinations/s4prod \ # create (201) or replace (200)
259
+ -d '{"baseUrl":"https://s4prod.example.com","authType":"basic","username":"MCP_USER","password":"${S4PROD_PASSWORD}","sapClient":"100"}'
260
+ curl -H "$A" -H "$J" -X POST $U/tokens \ # → { "token": "gsm_s4prod_…", … } — shown ONCE
261
+ -d '{"destination":"s4prod","label":"n8n prod","readOnly":true,"tiers":["core","odata"]}'
262
+ curl -H "$A" $U/tokens # label, destination, hash prefix, policy, status
263
+ curl -H "$A" -X DELETE "$U/tokens/n8n%20prod" # revoke by label or hash prefix
264
+ curl -H "$A" -X DELETE "$U/destinations/s4test?force=true" # remove; without force it refuses while tokens exist
265
+ ```
266
+
267
+ - **Separate keys, separate doors.** The admin secret opens *only* `/admin`; the n8n tokens and `--api-key` open *only* `/mcp`. A leaked n8n token cannot mint further access, an admin secret cannot read SAP data. Wrong secrets are counted per client address (`429` after 10 failures per minute); every change is logged with action, target and client address — never the token, never the secret. Without `--admin-token`, `/admin/*` answers `404`.
268
+ - **Effective immediately.** Every write validates like the CLI, writes atomically and reloads the running configuration before answering (`"reloaded": true`). If the server cannot load what was written — typically a `${VAR}` that only your shell knows, written with `?allowMissingEnv=true` — the answer says `"reloaded": false` with the errors, and the last valid configuration stays in force.
269
+ - **Bootstrap without any SAP configuration.** With `--admin-token` the server starts even if `destinations.json` does not exist yet and no `SAP_*` is set: `/mcp` answers `503` until the first destination (and a token, or a destination named `default`) has been created through the API. `docker run … -e SAP_MCP_ADMIN_TOKEN=… -e SAP_MCP_DESTINATIONS=/config/destinations.json` is a complete first start.
270
+ - Put the API behind the same reverse proxy/TLS as `/mcp`; the admin secret travels as a Bearer header like any other credential.
271
+
237
272
  ### 4. Use with n8n
238
273
 
239
274
  1. Add an **AI Agent** node with an **MCP Client Tool** sub-node
@@ -356,7 +391,7 @@ graph TB
356
391
  Discovery["Service Discovery<br/>16 Domain Categories"]
357
392
  NLQuery["NL-to-OData<br/>Filter Builder"]
358
393
  HTTP["SapHttpClient<br/>CSRF &bull; Redirect &bull; Auth"]
359
- Auth["Auth Layer<br/>7 Auth Types"]
394
+ Auth["Auth Layer<br/>8 Auth Types"]
360
395
  Cache["Per-User Token Cache<br/>LRU &bull; SHA-256"]
361
396
 
362
397
  Transport --> UserCtx
@@ -475,7 +510,7 @@ Additionally validated against:
475
510
 
476
511
  ## Documentation
477
512
 
478
- - [Setup Guide](https://github.com/guniweb/guniweb-sap-mcp/blob/main/docs/setup-guide.md) -- Installation, all 7 auth types, CLI flags, named destinations and tokens, Docker deployment
513
+ - [Setup Guide](https://github.com/guniweb/guniweb-sap-mcp/blob/main/docs/setup-guide.md) -- Installation, all 8 auth types, CLI flags, named destinations and tokens, Docker deployment
479
514
  - [Architecture](https://github.com/guniweb/guniweb-sap-mcp/blob/main/docs/architecture.md) -- Technical architecture, request flows, design decisions
480
515
  - [API Reference](https://github.com/guniweb/guniweb-sap-mcp/blob/main/docs/api-reference.md) -- All 22 tools, resources, and prompts with parameters
481
516
  - [Examples](https://github.com/guniweb/guniweb-sap-mcp/blob/main/docs/examples.md) -- SAP workflow examples with step-by-step instructions