124 lines
4.9 KiB
Markdown
124 lines
4.9 KiB
Markdown
# SAP MCP Bridge
|
||
|
||
Five SAP / UI5 / Fiori Model Context Protocol servers, packaged in a
|
||
single container and routed through an internal **nginx reverse
|
||
proxy** so the app exposes only **one external port** (Runtipi /
|
||
Traefik friendly).
|
||
|
||
## Bundled MCP servers
|
||
|
||
| Path prefix | MCP server | Use case |
|
||
| -------------- | ----------------------------------------- | ------------------------------------------ |
|
||
| `/cap/mcp` | `@iflow-mcp/cap-js-mcp-server` | SAP CAP (Cloud Application Programming) |
|
||
| `/abap/mcp` | `@iflow-mcp/mcp-abap-adt` | SAP ABAP via ADT (S/4HANA Cloud, on-prem) |
|
||
| `/odata/mcp` | `@iflow-mcp/btp-sap-odata-to-mcp-server` | SAP OData via BTP Destination service |
|
||
| `/ui5/mcp` | `@ui5/mcp-server` | SAPUI5 framework guidance + linting |
|
||
| `/fiori/mcp` | `@sap-ux/fiori-mcp-server` | SAP Fiori tools / app generation |
|
||
|
||
Each stdio MCP server is wrapped by `supergateway` and speaks
|
||
**Streamable HTTP** on its own internal port (9001–9005). An internal
|
||
nginx (port 8080 — the only EXPOSEd port) routes external requests by
|
||
URL path. Long timeouts and disabled buffering make MCP streaming
|
||
responses work correctly.
|
||
|
||
## Health endpoints
|
||
|
||
- `/healthz` — aggregate (probes the CAP gateway)
|
||
- `/cap/healthz`, `/abap/healthz`, `/odata/healthz`, `/ui5/healthz`, `/fiori/healthz`
|
||
|
||
## Using from Claude Code
|
||
|
||
Once the app is installed (and either exposed via a domain or
|
||
reachable on the LAN), wire the servers you want into your MCP
|
||
client. With the Claude Code CLI:
|
||
|
||
```bash
|
||
claude mcp add --transport http sap-cap https://sap-mcp.example.com/cap/mcp
|
||
claude mcp add --transport http sap-abap https://sap-mcp.example.com/abap/mcp
|
||
claude mcp add --transport http sap-odata https://sap-mcp.example.com/odata/mcp
|
||
claude mcp add --transport http ui5 https://sap-mcp.example.com/ui5/mcp
|
||
claude mcp add --transport http fiori https://sap-mcp.example.com/fiori/mcp
|
||
```
|
||
|
||
…or by adding the equivalent block to your project's `.mcp.json` /
|
||
`~/.claude.json`:
|
||
|
||
```json
|
||
{
|
||
"mcpServers": {
|
||
"sap-cap": { "type": "http", "url": "https://sap-mcp.example.com/cap/mcp" },
|
||
"sap-abap": { "type": "http", "url": "https://sap-mcp.example.com/abap/mcp" },
|
||
"sap-odata": { "type": "http", "url": "https://sap-mcp.example.com/odata/mcp" },
|
||
"ui5": { "type": "http", "url": "https://sap-mcp.example.com/ui5/mcp" },
|
||
"fiori": { "type": "http", "url": "https://sap-mcp.example.com/fiori/mcp" }
|
||
}
|
||
}
|
||
```
|
||
|
||
## ⚠️ Security warning — read before exposing externally
|
||
|
||
This app intentionally enables **wildcard CORS** (`Access-Control-Allow-Origin: *`)
|
||
inside the supergateway wrappers, because MCP clients may call the
|
||
endpoint from a variety of origins. Combined with `exposable: true`
|
||
and **no built-in authentication**, this means: if you expose the app
|
||
on a public URL via Traefik or a Cloudflare Tunnel, *anyone who knows
|
||
the URL* can drive your configured SAP credentials.
|
||
|
||
Recommended deployments:
|
||
|
||
- **Local LAN only**: leave `exposable: false` (default) and reach it
|
||
via `http://<runtipi-host>:<port>` from inside the LAN.
|
||
- **Exposed to the internet**: put it behind a Traefik
|
||
**ForwardAuth** middleware (e.g. Authentik / Authelia), a
|
||
Cloudflare Access policy, or basic-auth in nginx-proxy-manager —
|
||
i.e. authenticate at the proxy, not the app.
|
||
|
||
Do NOT expose this publicly without an auth layer in front of it.
|
||
|
||
## Configuration
|
||
|
||
All credential form fields are **optional**. Empty values are fine —
|
||
servers that don't have their required env vars simply won't be
|
||
usable, but the others (and the container as a whole) will still run.
|
||
|
||
- **`sap-abap-adt` needs:** `SAP_URL`, `SAP_USERNAME`, `SAP_PASSWORD`,
|
||
`SAP_CLIENT`
|
||
- **`sap-odata` needs:** `SAP_DESTINATION_NAME`,
|
||
`DESTINATION_SERVICE_URL`, `DESTINATION_CLIENT_ID`,
|
||
`DESTINATION_CLIENT_SECRET`, `DESTINATION_AUTH_URL`
|
||
- **`sap-cap`, `ui5-mcp-server`, `fiori-mcp-server`** require no
|
||
credentials — they read your **project workspace** at request time.
|
||
|
||
## Workspace mount
|
||
|
||
The container's `/workspace` is bound to:
|
||
|
||
```
|
||
/etc/runtipi/user-config/runtipi/sap-mcp-bridge/workspace
|
||
```
|
||
|
||
This lives under `user-config/`, which **survives Runtipi restarts
|
||
and `app update`**. Drop your CAP / UI5 / Fiori projects in there for
|
||
the `cap`, `ui5`, and `fiori` MCP servers to operate on.
|
||
|
||
The entrypoint chowns this directory to the in-container `mcp` user
|
||
(UID 10001) on every start, so a freshly auto-created mount works.
|
||
|
||
If you want `/workspace` to point at a different host path, create
|
||
`/etc/runtipi/user-config/runtipi/sap-mcp-bridge/docker-compose.yml`
|
||
with an overlay, e.g.:
|
||
|
||
```yaml
|
||
services:
|
||
sap-mcp-bridge:
|
||
volumes:
|
||
- /your/other/path:/workspace
|
||
```
|
||
|
||
## Image
|
||
|
||
Custom image:
|
||
`git.alexzaw.dev/alexz/sap-mcp-bridge:latest` — built from the
|
||
sources in `build/` (Dockerfile + entrypoint.sh + nginx.conf). See
|
||
`build/README.md` for the build & push procedure.
|