Install
$ agentstack add mcp-yzonur-sap-adt-mcp ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
sap-adt-mcp
> MCP server giving Claude (and any MCP-compatible client) live access to SAP systems via ADT. > > Read source, search the repository, run syntax checks, run unit tests, run > ATC, diff the same object across landscapes, edit and activate ABAP — all > from a chat window or an autonomous agent. No add-on installation on the SAP > stack required.
[](https://www.npmjs.com/package/sap-adt-mcp) [](https://github.com/yzonur/sap-adt-mcp/actions/workflows/ci.yml) [](LICENSE) [](https://nodejs.org)
Why
SAP development is full of repetitive read-the-source / check-the-callers / diff-the-system work. AI assistants are great at exactly that kind of task — but only if they can reach the system. ADT (ABAP Development Tools) is the HTTP API that Eclipse uses; it ships with every modern NetWeaver and S/4 system. This server speaks ADT on behalf of the agent so the agent can do real work against your real systems, with the same auth and scoping you'd give a developer in Eclipse.
What's in the box
27 high-level tools wrapped around the most common ADT endpoints, plus a generic escape hatch for anything else, plus 5 user-invokable Clean Core prompts that turn the tool surface into outcome-shaped slash commands (see [Clean Core prompts](#clean-core-prompts) below).
| Category | Tools | | --- | --- | | Connection | adt_list_systems, adt_ping | | Source CRUD | adt_get_source, adt_set_source | | Quality | adt_syntax_check, adt_pretty_print, adt_run_unit_tests, adt_run_atc, adt_run_atc_package, adt_run_atc_transport | | Lifecycle | adt_create_object, adt_delete_object, adt_activate, adt_lock, adt_unlock, adt_list_inactive_objects | | Versions | adt_list_versions, adt_compare_versions | | Discovery | adt_browse_package, adt_list_packages, adt_search_objects, adt_grep_source, adt_where_used | | CDS | adt_cds_data_preview, adt_cds_dependencies, adt_list_released_apis | | Cross-system | adt_compare_source, adt_transport_diff | | Transports | adt_list_transports, adt_get_transport, adt_create_transport, adt_release_transport | | Runtime errors | adt_list_dumps, adt_get_dump | | Data | adt_read_table | | Generation | adt_rap_scaffold | | Experimental¹ | adt_get_note, adt_check_note_status, adt_implement_note, adt_list_locks, adt_schedule_job, adt_read_spool | | Escape hatch | adt_request |
¹ Experimental tools target ADT endpoints (SNOTE, SM12 enqueues, SM36/SP01) that classic NetWeaver does not expose; on such systems they return available:false with a fall-back hint rather than failing. They work where the backing service exists (typically S/4HANA).
Multi-system aware. One config, many SAP systems (DEV / QAS / PRD or landscape-wide); switch with the system argument or compare across two with adt_compare_source / adt_transport_diff.
Safe by default. A readOnly flag (global or per-system) blocks every write method. Read-only POST queries (search, where-used, package tree) remain allowed so agents can still discover.
Robust. Per-request timeout. CSRF token negotiation with auto-retry on
- Self-signed cert opt-out. Optional debug tracing to stderr.
Structured errors. ADT's ` envelopes are parsed into { type, message, namespace }` so failed calls don't dump XML into the agent's context window.
Install
> Previously published as claude-for-abap — that package still works but is > deprecated; new installs should use sap-adt-mcp.
# global
npm install -g sap-adt-mcp
# or run without installing
npx sap-adt-mcp
Requires Node.js 22.19+ (undici v8, used as the HTTP client, requires this minimum).
Configure
Create your config:
mkdir -p ~/.sap-adt-mcp
cp config.example.json ~/.sap-adt-mcp/config.json
$EDITOR ~/.sap-adt-mcp/config.json
The server searches in this order:
$SAP_ADT_MCP_CONFIG(absolute path)~/.sap-adt-mcp/config.json./config.json(cwd at server start)
Sample config
{
"defaultSystem": "DEV",
"readOnly": false,
"systems": {
"DEV": {
"host": "https://sap-dev.example.com:44300",
"client": "100",
"language": "EN",
"user": "DEVELOPER",
"password": "env:SAP_DEV_PASSWORD",
"rejectUnauthorized": false
},
"QAS": {
"host": "https://sap-qas.example.com:44300",
"client": "200",
"user": "DEVELOPER",
"password": "env:SAP_QAS_PASSWORD"
},
"PRD": {
"host": "https://sap-prd.example.com:44300",
"client": "300",
"user": "READONLY",
"password": "env:SAP_PRD_PASSWORD",
"readOnly": true
}
}
}
Per-system options
| Field | Meaning | | --- | --- | | host | Base URL including scheme + ICM HTTPS port (e.g. https://...:44300). | | client | SAP client (sets sap-client query param). | | language | Optional logon language (sets sap-language). | | user | RFC user. | | password | Either a literal string or env:VAR_NAME to read from environment. | | rejectUnauthorized | Set false to skip TLS validation for self-signed certs. Default true. | | readOnly | Block POST / PUT / DELETE / PATCH for this system (read-only POST queries still work). | | timeoutMs | Override default 30 s request timeout. |
Read-only mode
readOnly: true (top-level or per-system) refuses any unsafe HTTP method. Whitelisted read-only POST endpoints (nodestructure, search, usagereferences, parsers, checkruns) remain available so agents can still discover and analyze without being able to modify.
Recommended: set readOnly: true for QAS and PRD profiles. Keep DEV writable.
Self-signed certificates
Many internal SAP systems use self-signed certs. "rejectUnauthorized": false disables TLS validation for that profile only. Don't set this on PRD.
Audit log
Every write the server performs against SAP (POST/PUT/DELETE/PATCH — locks, source updates, activations, transport operations) is appended to a local JSONL file, including which MCP tool triggered it and, for blocked attempts in read-only mode, the violation itself. Reads and read-only queries are not logged. Nothing leaves your machine — this is your local answer to "what exactly did the AI change?".
Default location: ~/.sap-adt-mcp/audit.log. One JSON object per line:
{"ts":"2026-06-11T12:00:00.000Z","tool":"adt_set_source","host":"https://...","sapUser":"DEVELOPER","method":"PUT","path":"/sap/bc/adt/programs/programs/ztest/source/main","status":200,"ok":true,"transport":"E4DK900123"}
Configure or disable:
{ "audit": { "enabled": false, "path": "/var/log/sap-adt-mcp/audit.log" } }
…or set SAP_ADT_MCP_AUDIT=0 (also accepts false/no/off).
Automatic error reporting
The server sends small, redacted reports to the maintainer so defects get found and fixed. This is on by default and the server prints a notice saying so on startup. There are three channels:
- Crash — a tool handler throws an unexpected error.
- ADT error — a tool returns a non-2xx ADT response that the classifier
flags as a likely tool bug (406/415 content negotiation, malformed requests, server dispatcher blow-ups). User/business-side responses (401/403/404, lock and enqueue conflicts, data-preview SQL errors) are not reported.
- Agent-reported — the calling agent files a defect the other two channels
can't see (wrong data in a successful response, an ignored parameter, a missing capability) via the adt_report_issue tool.
What is sent: the sap-adt-mcp version, Node version, OS, the tool name, and the error/finding with a fingerprint for de-duplication. Before anything leaves your machine it is scrubbed of hostnames, users, passwords, tokens, IPs, and emails; tool arguments and free-text fields are redacted the same way. Reports go to a relay the maintainer owns, which files/de-dups a GitHub issue — the relay holds the GitHub credentials, never this package.
Turn it all off:
{ "reporting": { "enabled": false } }
…or set SAP_ADT_MCP_REPORT=0 (also accepts false/no/off). Finer control:
| Key | Default | Effect | | --- | --- | --- | | reporting.enabled | true | Master switch for all three channels. | | reporting.adtErrors | true | Channel 2 (auto-report flagged ADT errors). | | reporting.allowManual | true | Channel 3 (the adt_report_issue tool). | | reporting.includeArgs | true | Include redacted tool args / repro args. Note: object names can appear here. | | reporting.endpoint | relay URL | Point at your own relay (see [worker/](worker/)). |
Local control panel
A small HTML button panel for the read-only tools — search, grep, getsource, readtable, ATC, where-used, packages, transports, dumps, inactive objects — so you can poke at SAP from a browser without going through an agent.
The trick: the panel is served from inside the MCP process itself, reusing the same tool handlers. So it is reachable only while a session keeps the MCP connected — close the session (or disconnect the MCP) and the process exits, taking the panel down with it. There is no standalone server to leave running.
Open it from a session (easiest). Just ask the agent to open it — that calls the adt_open_panel tool, which starts the panel on demand and opens the URL in your browser. adt_close_panel stops it. In Claude Code the bundled /panel command does the same (/panel, /panel url, /panel close). Nothing listens until you ask — the socket opens only on that call.
Or auto-start at boot. Set it in config or env and it comes up with the server:
{ "panel": { "enabled": true, "port": 0 } }
…or SAP_ADT_MCP_PANEL=1 (SAP_ADT_MCP_PANEL_PORT pins a port; port: 0 / unset picks a random free one). On boot the server prints the URL, e.g.:
[sap-adt-mcp] panel: ready (read-only) → http://127.0.0.1:39555/?t=
Safety: bound to 127.0.0.1 only, gated by a per-boot random token in that URL, and limited to a curated read-only allowlist — no write tool (set_source, activate, delete, lock, transport release) is reachable from a button, regardless of config. Each tool's form is rendered from its live input schema, and the system selector at the top targets any configured system.
Connect a client
Claude Code (CLI)
claude mcp add sap-adt -- npx sap-adt-mcp
Pass secrets through the registration:
claude mcp add sap-adt \
--env SAP_DEV_PASSWORD=... \
--env SAP_PRD_PASSWORD=... \
-- npx sap-adt-mcp
Claude Desktop
Edit claude_desktop_config.json (Settings → Developer → Edit Config):
{
"mcpServers": {
"sap-adt": {
"command": "npx",
"args": ["-y", "sap-adt-mcp"],
"env": {
"SAP_DEV_PASSWORD": "..."
}
}
}
}
Quit and restart Claude Desktop fully (system tray → Quit) for the change to apply.
Validate before connecting
npx sap-adt-mcp --validate-config
Loads the config and pings every system; exits non-zero if any are unreachable or rejecting credentials. Run this first when troubleshooting.
Tools
Object-source CRUD
| Tool | Purpose | Notes | | --- | --- | --- | | adt_get_source | Fetch ABAP source by object name + type. | Returns plain text. For classes, pick the include via include: main (default), definitions, implementations, macros, testclasses. Function modules require group. | | adt_set_source | Replace source. Orchestrates lock → PUT → unlock. | Optional transport parameter assigns the change to a TR (corrNr). Optional lockHandle to reuse an externally-acquired lock. Refused under readOnly: true. | | adt_create_object | Create a new ABAP object in a package. | Supported types: program, class, interface, include, functiongroup, function, cds, accesscontrol, metadataext, behaviordef, messageclass. After creation, set the source body with adt_set_source and activate. Refused under readOnly: true. | | adt_delete_object | Delete an object. | Acquires lock and DELETEs. Refused under readOnly: true. | | adt_activate | Activate one or more objects. | Pass objects: [{ name, type, group? }]. | | adt_pretty_print | Run the SAP-side ABAP formatter. | Stateless — pass source, get formatted source back. | | adt_lock / adt_unlock | Acquire / release a lock for multi-step edits. | For one-shot edits, prefer adt_set_source (manages the lock for you). Use these when you need to keep an object locked across multiple writes within a single agent turn. |
Quality
| Tool | Purpose | Notes | | --- | --- | --- | | adt_syntax_check | ADT syntax check on an object. | Returns ` XML; the agent reads severity + line numbers. | | adtrununittests | ABAP Unit run. | Pass test container objects (typically classes). | | adtrun_atc` | ABAP Test Cockpit run. | API surface varies across NW releases — see Caveats. |
Discovery
| Tool | Purpose | Notes | | --- | --- | --- | | adt_browse_package | One level of package contents. | | | adt_list_packages | Recursive walk from a root. | Has prefix (only descend into matching subpackages) and maxPackages safety cap (default 200). | | adt_search_objects | Quick-search by name pattern. | * wildcard. Returns parsed { name, type, description, packageName, uri } records. | | adt_where_used | Where-used list. | Same parsed record shape. |
Cross-system
| Tool | Purpose | Notes | | --- | --- | --- | | adt_compare_source | Diff one object between two systems. | Returns unified diff + { added, removed } stats. | | adt_transport_diff | Diff every object in a TR between two systems. | Caps at maxObjects (default 50). |
Runtime errors
| Tool | Purpose | Notes | | --- | --- | --- | | adt_list_dumps | List ST22 short dumps. | Optional filters: user, host, from/to (YYYYMMDD), maxResults (default 20). Atom feed is parsed into structured entries with runtimeError, program, user, updated, and release-specific rba:*/dump:* fields surfaced as a map. Trims client-side because some releases ignore the server-side cap. | | adt_get_dump | Fetch a single dump by id. | Two-step fetch: metadata XML (runtime error, program, links) followed by the formatted dump text from the dump:link relation="contents" sub-resource. Returns a chapters map (shortText, whatHappened, errorAnalysis, howToCorrect, whereTerminated, sourceCodeExtract, …). Pass chapters: [...] to limit, full: true to include the raw text. |
Data
| Tool | Purpose | Notes | | --- | --- | --- | | adt_read_table | Run an OpenSQL SELECT via the ADT Data Preview API. | SE16-style table reads. SELECT-only — INSERT/UPDATE/DELETE rejected client-side; the SAP endpoint enforces server-side too. maxRows capped at 5000 (default 100). Requires NetWeaver 7.55+ / S/4HANA. |
Transports
| Tool | Purpose | Notes | | --- | --- | --- | | adt_list_transports | List TRs by user / status. | Default user = config user; default status = modifiable. | | adt_get_transport | TR header + objects. | | | adt_create_transport | Create a new TR. | Refused under readOnly: true. Endpoint shape varies — see Caveats. | | adt_release_transport | Release a TR. | Refused under readOnly: true. |
Control panel
| Tool | Purpose | Notes | | --- | --- | --- | | adt_open_panel | Start the local read-only HTML control panel and return its URL. | Opens the URL in the browser by default (open: false to just return it). Reachable only while this session keeps the MCP connected. See [Local control panel](#local-control-panel). | | adt_close_panel | Stop the panel. | It also stops on its own when the session ends. |
Escape hatch
adt_request — direct ADT REST call. Use this when a niche endpoint isn't covered by a high-level tool. Handles auth / CSRF / cookies / sap-cl
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: yzonur
- Source: yzonur/sap-adt-mcp
- License: MIT
- Homepage: https://github.com/yzonur/claude-for-abap
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.