abap-adt-mcp-gateway
by rogerix4
README.md
# abap-adt-mcp-gateway
[](https://github.com/rogerix4/abap-adt-mcp-gateway/actions/workflows/ci.yml)
[](https://rogerix4.github.io/abap-adt-mcp-gateway/)
[](./LICENSE)
[](https://nodejs.org/)
[](https://modelcontextprotocol.io/)
**One MCP endpoint in front of every SAP system you work with.**
The SAP ABAP ADT server ([`@babamba2/abap-mcp-adt-powerup`](https://github.com/babamba2/abap-mcp-adt-powerup)) binds one SAP destination per process. That means one MCP server entry, one tool list, one login **per system** — and an assistant that keeps mixing up which system it is talking to.
This gateway keeps one child process per system but exposes the tool set **once**, with a mandatory `system` argument on every tool. Adding an eighth system does not add an eighth MCP server to your editor.
```
┌──────────────────────────────┐
MCP client ──► abap-adt-mcp-gateway │
(VS Code, │ · one tool surface │
Claude, │ · `system` routing │──────► child: powerup ──► SAP dev-100 (DEV, writable)
Cursor, …) │ · tier + read-only guard │──────► child: powerup ──► SAP qa-200 (QA, read-only)
│ · audit log │──────► child: powerup ──► SAP prd-300 (PRD, read-only)
└──────────────────────────────┘
```
## Why this exists
| Problem with one-server-per-system | What the gateway does |
|---|---|
| 8 systems = 8 × ~300 tools in the client, and the model picks the wrong one | one tool list; the target is an argument, not a separate tool namespace |
| every new system means editing `mcp.json` | one entry, one config file, no client change |
| a QA/PRD system is writable as long as someone adds `SAP_TIER=DEV` | tiers are evaluated per call, unknown tiers are **read-only**, and the name is not trusted as proof of "read-only" |
| credentials are copied into per-system env files | secrets stay in the environment or in the OS keychain |
| a tool call lands on the wrong system and nobody can tell afterwards | every call is recorded in a JSON Lines audit log (tool, system, outcome, duration — never argument values) |
## Features
- **One MCP server, many SAP systems** — stable tool count, `system` argument on every tool.
- **VS Code style configuration** — a single `adt-mcp.config.jsonc` with comments, trailing commas and a JSON Schema for autocompletion; edited while the server is running, picked up without a restart.
- **No secrets on disk** — `${env:NAME}` or `keychain:<service>/<account>`; credentials are materialised only into a private env file that is removed on shutdown.
- **Fail-closed safety** — anything that is not `DEV` is read-only; unknown tiers become `PRD`; a write that cannot be proven read-only will not guess between two writable systems.
- **Per-system serialisation** — one child process holds one SAP session, so calls to the same system are queued instead of colliding (`HTTP 400`, `Session Timed Out`, half-applied writes).
- **Audit trail** — append-only JSONL with redaction; argument *names* are logged, values never are.
- **Live reload** — editing the config or patching the child server recycles the affected child on the next call; other systems keep running.
- **Offline tool list** — `tools/list` works without a SAP login thanks to a per-system cache keyed by package, exposition and connection settings.
- **Diagnostics** — `adt-mcp-gateway doctor` checks paths, tiers, keychain entries, secret storage and leftover files.
## Requirements
- Node.js **20+**
- the ABAP ADT MCP server: `npm i -g @babamba2/abap-mcp-adt-powerup` (or a local install — it is auto-detected)
- network access to your SAP systems
## Install
```bash
git clone https://github.com/rogerix4/abap-adt-mcp-gateway.git
cd abap-adt-mcp-gateway
npm install
npm run build
```
Run it straight from the repository:
```bash
node dist/cli.js serve --config ./adt-mcp.config.jsonc
```
or install the CLI globally:
```bash
npm link # exposes `adt-mcp-gateway`
adt-mcp-gateway --help
```
## Quick start
1. **Create a configuration** (interactive; writes to the per-user config folder):
```bash
adt-mcp-gateway init
```
Or copy [`adt-mcp.config.example.jsonc`](./adt-mcp.config.example.jsonc) next to your workspace and edit it.
2. **Verify it** — this reads everything except SAP itself:
```bash
adt-mcp-gateway doctor --config ./adt-mcp.config.jsonc
```
3. **Point your MCP client at it** (`mcp.json` / `settings.json`, VS Code flavour):
```json
{
"servers": {
"sap": {
"type": "stdio",
"command": "node",
"args": ["D:\\tools\\abap-adt-mcp-gateway\\dist\\cli.js", "serve"],
"env": {
"SAP_DEV_100_PASSWORD": "${input:sapDevPassword}"
}
}
}
}
```
4. **Ask for a system.** Every tool takes `system`; `GatewayListSystems` tells you the ids:
```text
GatewayListSystems()
→ [{ "system": "dev-100", "tier": "DEV", "writable": true, "connected": true, "tools": 302 }]
ReadClass({ "system": "dev-100", "class_name": "ZCL_HELLO" })
```
## Configuration
The full reference lives in [docs/configuration.md](./docs/configuration.md); the schema is
[`schema/adt-mcp.config.schema.json`](./schema/adt-mcp.config.schema.json) (add `"$schema"` for completion and inline documentation).
Minimal file:
```jsonc
{
"defaultSystem": "dev-100",
"systems": [
{
"id": "dev-100",
"tier": "DEV",
"endpoint": { "url": "https://sap-dev.example.com:44300", "client": "100" },
"auth": { "type": "basic", "username": "DEVELOPER", "password": "${env:SAP_DEV_100_PASSWORD}" }
},
{
"id": "prd-300",
"tier": "PRD",
"endpoint": { "url": "https://sap-prd.example.com:44300", "client": "300" },
"auth": { "type": "basic", "username": "READER", "password": "keychain:adt-mcp/prd-300" }
}
]
}
```
Lookup order: `--config` → `$ADT_MCP_CONFIG` → `./adt-mcp.config.jsonc` → `./.adt-mcp/config.jsonc` → the per-user config folder.
## Gateway tools
Besides the ~300 ABAP tools, four tools belong to the gateway itself:
| Tool | Purpose |
|---|---|
| `GatewayListSystems` | systems, tiers, writability, connection state, tool count, queued calls |
| `GatewayListTools` | search the whole surface (including hidden tools) and see which systems provide each tool |
| `GatewayCallTool` | call any tool by name, even if it is hidden from `tools/list` |
| `GatewayRestartSystem` | recycle one child process after changing its settings or upgrading the child |
`toolPolicy.allow` / `toolPolicy.deny` only change what `tools/list` advertises. They are **not** an authorisation boundary — the tier guard and `GatewayCallTool` are.
## Safety model
1. **Tier first.** `tier` must be `DEV` for writes. `QA`, `QAS`, `PRD`, `PROD`, a missing tier and a typo all mean read-only.
2. **Read-only is proven, not guessed.** A tool counts as read-only only when its name starts with a read verb (`Get`, `Read`, `List`, `Check`, `Validate`, `Search`, `Describe`, `Analyze`, `Inspect`, `Show`, `Query`, `Preview`, or `Runtime` + read verb). Everything else is treated as a write on non-DEV systems. Unknown tools are blocked, not assumed harmless.
3. **No silent rerouting.** A call for a system that does not provide the tool fails; it is never redirected to another system.
4. **Ambiguity is an error.** With several writable systems and no `defaultSystem`, a call that cannot be proven read-only must pass `system` explicitly.
5. **Conflicting tool definitions disable the tool.** If two systems expose the same tool name with different schemas (typically different child versions), the tool is hidden and rejected until the versions are aligned.
6. **Everything is audited.** `audit.jsonl` records tool, system, tier, outcome, duration and queue wait; argument names only.
## Documentation
- [Getting started](./docs/getting-started.md)
- [Configuration reference](./docs/configuration.md)
- [Architecture and lifecycle](./docs/architecture.md)
- [Security model](./docs/security.md)
- [Changelog](./CHANGELOG.md)
## Development
```bash
npm run check # type-check only
npm run build # tsc → dist/
npm test # node:test suites (no SAP, no network)
```
The test suite covers the routing policy, the configuration loader, the per-system queue and redaction.
Nothing in it needs a SAP system.
Optional end-to-end check against a development system (creates, activates and deletes `ZTMP_LIVECHECK`
in `$TMP`):
```bash
ADT_LIVE_SYSTEM=dev-100 node tools/live-check.mjs --config ./adt-mcp.config.jsonc
```
## License
MIT — see [LICENSE](./LICENSE). This project builds on
[`@babamba2/abap-mcp-adt-powerup`](https://github.com/babamba2/abap-mcp-adt-powerup) (MIT); see [NOTICE](./NOTICE) for attribution.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues