{
  "markdown": "# sap-adt-mcp\n\n> **MCP server giving Claude (and any MCP-compatible client) live access to SAP systems via ADT.**\n>\n> Read source, search the repository, run syntax checks, run unit tests, run\n> ATC, diff the same object across landscapes, edit and activate ABAP — all\n> from a chat window or an autonomous agent. No add-on installation on the SAP\n> stack required.\n\n[![npm version](https://img.shields.io/npm/v/sap-adt-mcp.svg)](https://www.npmjs.com/package/sap-adt-mcp)\n[![CI](https://github.com/yzonur/sap-adt-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/yzonur/sap-adt-mcp/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n[![Node.js](https://img.shields.io/node/v/sap-adt-mcp.svg)](https://nodejs.org)\n\n---\n\n## Why\n\nSAP development is full of repetitive read-the-source / check-the-callers /\ndiff-the-system work. AI assistants are great at exactly that kind of task —\nbut only if they can reach the system. ADT (ABAP Development Tools) is the\nHTTP API that Eclipse uses; it ships with every modern NetWeaver and S/4\nsystem. This server speaks ADT on behalf of the agent so the agent can do real\nwork against your real systems, with the same auth and scoping you'd give a\ndeveloper in Eclipse.\n\n## What's in the box\n\n**27 high-level tools** wrapped around the most common ADT endpoints, plus a\ngeneric escape hatch for anything else, **plus 5 user-invokable Clean Core\nprompts** that turn the tool surface into outcome-shaped slash commands\n(see [Clean Core prompts](#clean-core-prompts) below).\n\n| Category | Tools |\n| --- | --- |\n| Connection | `adt_list_systems`, `adt_ping` |\n| Source CRUD | `adt_get_source`, `adt_set_source` |\n| Quality | `adt_syntax_check`, `adt_pretty_print`, `adt_run_unit_tests`, `adt_run_atc`, `adt_run_atc_package`, `adt_run_atc_transport` |\n| Lifecycle | `adt_create_object`, `adt_delete_object`, `adt_activate`, `adt_lock`, `adt_unlock`, `adt_list_inactive_objects` |\n| Versions | `adt_list_versions`, `adt_compare_versions` |\n| Discovery | `adt_browse_package`, `adt_list_packages`, `adt_search_objects`, `adt_grep_source`, `adt_where_used` |\n| CDS | `adt_cds_data_preview`, `adt_cds_dependencies`, `adt_list_released_apis` |\n| Cross-system | `adt_compare_source`, `adt_transport_diff` |\n| Transports | `adt_list_transports`, `adt_get_transport`, `adt_create_transport`, `adt_release_transport` |\n| Runtime errors | `adt_list_dumps`, `adt_get_dump` |\n| Debugger | `adt_debug_set_breakpoint`, `adt_debug_delete_breakpoint`, `adt_debug_listen`, `adt_debug_stack`, `adt_debug_variables`, `adt_debug_step`, `adt_debug_goto_stack`, `adt_debug_set_variable`, `adt_debug_set_watchpoint`, `adt_debug_delete_watchpoint`, `adt_debug_stop` |\n| Data | `adt_read_table` |\n| Generation | `adt_rap_scaffold` |\n| Experimental¹ | `adt_get_note`, `adt_check_note_status`, `adt_implement_note`, `adt_list_locks`, `adt_schedule_job`, `adt_read_spool` |\n| Escape hatch | `adt_request` |\n\n¹ Experimental tools target ADT endpoints (SNOTE, SM12 enqueues, SM36/SP01)\nthat classic NetWeaver does not expose; on such systems they return\n`available:false` with a fall-back hint rather than failing. They work where the\nbacking service exists (typically S/4HANA).\n\n**Multi-system aware.** One config, many SAP systems (DEV / QAS / PRD or\nlandscape-wide); switch with the `system` argument or compare across two with\n`adt_compare_source` / `adt_transport_diff`.\n\n**Safe by default.** A `readOnly` flag (global or per-system) blocks every\nwrite method. Read-only POST queries (search, where-used, package tree)\nremain allowed so agents can still discover.\n\n**Robust.** Per-request timeout. CSRF token negotiation with auto-retry on\n403. Self-signed cert opt-out. Optional debug tracing to stderr.\n\n**Structured errors.** ADT's `<exc:exception>` envelopes are parsed into\n`{ type, message, namespace }` so failed calls don't dump XML into the agent's\ncontext window.\n\n## Install\n\n> Previously published as `claude-for-abap` — that package still works but is\n> deprecated; new installs should use `sap-adt-mcp`.\n\n```bash\n# global\nnpm install -g sap-adt-mcp\n\n# or run without installing\nnpx sap-adt-mcp\n```\n\nRequires Node.js **22.19+** (undici v8, used as the HTTP client, requires\nthis minimum).\n\n## Configure\n\nCreate your config:\n\n```bash\nmkdir -p ~/.sap-adt-mcp\ncp config.example.json ~/.sap-adt-mcp/config.json\n$EDITOR ~/.sap-adt-mcp/config.json\n```\n\nThe server searches in this order:\n\n1. `$SAP_ADT_MCP_CONFIG` (absolute path)\n2. `~/.sap-adt-mcp/config.json`\n3. `./config.json` (cwd at server start)\n\n### Sample config\n\n```json\n{\n  \"defaultSystem\": \"DEV\",\n  \"readOnly\": false,\n  \"systems\": {\n    \"DEV\": {\n      \"host\": \"https://sap-dev.example.com:44300\",\n      \"client\": \"100\",\n      \"language\": \"EN\",\n      \"user\": \"DEVELOPER\",\n      \"password\": \"env:SAP_DEV_PASSWORD\",\n      \"rejectUnauthorized\": false\n    },\n    \"QAS\": {\n      \"host\": \"https://sap-qas.example.com:44300\",\n      \"client\": \"200\",\n      \"user\": \"DEVELOPER\",\n      \"password\": \"env:SAP_QAS_PASSWORD\"\n    },\n    \"PRD\": {\n      \"host\": \"https://sap-prd.example.com:44300\",\n      \"client\": \"300\",\n      \"user\": \"READONLY\",\n      \"password\": \"env:SAP_PRD_PASSWORD\",\n      \"readOnly\": true\n    }\n  }\n}\n```\n\n### Per-system options\n\n| Field | Meaning |\n| --- | --- |\n| `host` | Base URL including scheme + ICM HTTPS port (e.g. `https://...:44300`). |\n| `client` | SAP client (sets `sap-client` query param). |\n| `language` | Optional logon language (sets `sap-language`). |\n| `user` | RFC user. |\n| `password` | Either a literal string or `env:VAR_NAME` to read from environment. |\n| `rejectUnauthorized` | Set `false` to skip TLS validation for self-signed certs. Default `true`. |\n| `readOnly` | Block POST / PUT / DELETE / PATCH for this system (read-only POST queries still work). |\n| `timeoutMs` | Override default 30 s request timeout. |\n\n### Read-only mode\n\n`readOnly: true` (top-level or per-system) refuses any unsafe HTTP method.\nWhitelisted read-only POST endpoints (`nodestructure`, `search`,\n`usagereferences`, `parsers`, `checkruns`) remain available so agents can\nstill discover and analyze without being able to modify.\n\nRecommended: set `readOnly: true` for QAS and PRD profiles. Keep DEV writable.\n\n### Self-signed certificates\n\nMany internal SAP systems use self-signed certs. `\"rejectUnauthorized\": false`\ndisables TLS validation for that profile only. Don't set this on PRD.\n\n### Audit log\n\nEvery **write** the server performs against SAP (POST/PUT/DELETE/PATCH — locks,\nsource updates, activations, transport operations) is appended to a local JSONL\nfile, including which MCP tool triggered it and, for blocked attempts in\nread-only mode, the violation itself. Reads and read-only queries are not\nlogged. Nothing leaves your machine — this is your local answer to \"what exactly\ndid the AI change?\".\n\nDefault location: `~/.sap-adt-mcp/audit.log`. One JSON object per line:\n\n```json\n{\"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\"}\n```\n\nConfigure or disable:\n\n```json\n{ \"audit\": { \"enabled\": false, \"path\": \"/var/log/sap-adt-mcp/audit.log\" } }\n```\n\n…or set `SAP_ADT_MCP_AUDIT=0` (also accepts `false`/`no`/`off`).\n\n### Automatic error reporting\n\nThe server sends small, **redacted** reports to the maintainer so defects get\nfound and fixed. This is **on by default** and the server prints a notice saying\nso on startup. There are three channels:\n\n1. **Crash** — a tool handler throws an unexpected error.\n2. **ADT error** — a tool returns a non-2xx ADT response that the classifier\n   flags as a likely tool bug (406/415 content negotiation, malformed requests,\n   server dispatcher blow-ups). User/business-side responses (401/403/404, lock\n   and enqueue conflicts, data-preview SQL errors) are **not** reported.\n3. **Agent-reported** — the calling agent files a defect the other two channels\n   can't see (wrong data in a successful response, an ignored parameter, a\n   missing capability) via the **`adt_report_issue`** tool.\n\nWhat is sent: the sap-adt-mcp version, Node version, OS, the tool name, and the\nerror/finding with a fingerprint for de-duplication, plus an **anonymous install\nid** (random bytes, cached at `~/.sap-adt-mcp/install-id`) that lets repeat\nreports from the same install be grouped for triage — it identifies neither you\nnor your system. Before anything leaves your machine it is scrubbed of\n**hostnames, users, passwords, tokens, IPs, and emails**; tool arguments and\nfree-text fields are redacted the same way. Reports\ngo to a relay the maintainer owns, which files/de-dups a GitHub issue — the\nrelay holds the GitHub credentials, never this package.\n\nTurn it all off:\n\n```json\n{ \"reporting\": { \"enabled\": false } }\n```\n\n…or set `SAP_ADT_MCP_REPORT=0` (also accepts `false`/`no`/`off`). Finer control:\n\n| Key | Default | Effect |\n| --- | --- | --- |\n| `reporting.enabled` | `true` | Master switch for all three channels. |\n| `reporting.adtErrors` | `true` | Channel 2 (auto-report flagged ADT errors). |\n| `reporting.allowManual` | `true` | Channel 3 (the `adt_report_issue` tool). |\n| `reporting.includeArgs` | `true` | Include redacted tool args / repro args. Note: object names can appear here. |\n| `reporting.endpoint` | relay URL | Point at your own relay (see [`worker/`](worker/)). |\n\n### Local control panel\n\nA small HTML button panel for the **read-only** tools — search, grep, get_source,\nread_table, ATC, where-used, packages, transports, dumps, inactive objects — so\nyou can poke at SAP from a browser without going through an agent.\n\nThe trick: the panel is served **from inside the MCP process itself**, reusing\nthe same tool handlers. So it is reachable **only while a session keeps the MCP\nconnected** — close the session (or disconnect the MCP) and the process exits,\ntaking the panel down with it. There is no standalone server to leave running.\n\n**Open it from a session (easiest).** Just ask the agent to open it — that calls\nthe **`adt_open_panel`** tool, which starts the panel on demand and opens the URL\nin your browser. **`adt_close_panel`** stops it. In Claude Code the bundled\n**`/panel`** command does the same (`/panel`, `/panel url`, `/panel close`).\nNothing listens until you ask — the socket opens only on that call.\n\n**Or auto-start at boot.** Set it in config or env and it comes up with the\nserver:\n\n```json\n{ \"panel\": { \"enabled\": true, \"port\": 0 } }\n```\n\n…or `SAP_ADT_MCP_PANEL=1` (`SAP_ADT_MCP_PANEL_PORT` pins a port; `port: 0` / unset\npicks a random free one). On boot the server prints the URL, e.g.:\n\n```\n[sap-adt-mcp] panel: ready (read-only) → http://127.0.0.1:39555/?t=<token>\n```\n\nSafety: bound to `127.0.0.1` only, gated by a per-boot random **token** in that\nURL, and limited to a curated **read-only** allowlist — no write tool (set_source,\nactivate, delete, lock, transport release) is reachable from a button, regardless\nof config. Each tool's form is rendered from its live input schema, and the\nsystem selector at the top targets any configured system.\n\n## Connect a client\n\n### Claude Code (CLI)\n\n```bash\nclaude mcp add sap-adt -- npx sap-adt-mcp\n```\n\nPass secrets through the registration:\n\n```bash\nclaude mcp add sap-adt \\\n  --env SAP_DEV_PASSWORD=... \\\n  --env SAP_PRD_PASSWORD=... \\\n  -- npx sap-adt-mcp\n```\n\n### Claude Desktop\n\nEdit `claude_desktop_config.json` (Settings → Developer → Edit Config):\n\n```json\n{\n  \"mcpServers\": {\n    \"sap-adt\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"sap-adt-mcp\"],\n      \"env\": {\n        \"SAP_DEV_PASSWORD\": \"...\"\n      }\n    }\n  }\n}\n```\n\nQuit and restart Claude Desktop fully (system tray → Quit) for the change to\napply.\n\n### Validate before connecting\n\n```bash\nnpx sap-adt-mcp --validate-config\n```\n\nLoads the config and pings every system; exits non-zero if any are unreachable\nor rejecting credentials. Run this first when troubleshooting.\n\n## Tools\n\n### Object-source CRUD\n\n| Tool | Purpose | Notes |\n| --- | --- | --- |\n| `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`. For large objects, pass `outputFile` to write the source straight to disk (response omits inline `source`). |\n| `adt_set_source` | Replace source. Orchestrates lock → PUT → unlock. | Supply the new source inline via `source`, or via `sourceFile` (a local path the MCP reads itself) for large objects that exceed the per-call I/O cap. Optional `transport` assigns the change to a TR (`corrNr`); optional `lockHandle` reuses an externally-acquired lock. Refused under `readOnly: true`. |\n| `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`. |\n| `adt_delete_object` | Delete an object. | Acquires lock and DELETEs. Refused under `readOnly: true`. |\n| `adt_activate` | Activate one or more objects. | Pass `objects: [{ name, type, group? }]`. |\n| `adt_pretty_print` | Run the SAP-side ABAP formatter. | Stateless — pass source, get formatted source back. |\n| `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. |\n\n### Quality\n\n| Tool | Purpose | Notes |\n| --- | --- | --- |\n| `adt_syntax_check` | ADT syntax check on an object. | Returns `<chkrun:reports>` XML; the agent reads severity + line numbers. |\n| `adt_run_unit_tests` | ABAP Unit run. | Pass test container objects (typically classes). |\n| `adt_run_atc` | ABAP Test Cockpit run. | API surface varies across NW releases — see Caveats. |\n\n### Discovery\n\n| Tool | Purpose | Notes |\n| --- | --- | --- |\n| `adt_browse_package` | One level of package contents. | |\n| `adt_list_packages` | Recursive walk from a root. | Has `prefix` (only descend into matching subpackages) and `maxPackages` safety cap (default 200). |\n| `adt_search_objects` | Quick-search by name pattern. | `*` wildcard. Returns parsed `{ name, type, description, packageName, uri }` records. |\n| `adt_where_used` | Where-used list. | Same parsed record shape. Capped at `maxResults` (default 200); the response carries the backend's own `numberOfResults` plus `truncated` when the list was cut. |\n\n### Cross-system\n\n| Tool | Purpose | Notes |\n| --- | --- | --- |\n| `adt_compare_source` | Diff one object between two systems. | Returns unified diff + `{ added, removed }` stats. |\n| `adt_transport_diff` | Diff every object in a TR between two systems. | Caps at `maxObjects` (default 50). |\n\n### Runtime errors\n\n| Tool | Purpose | Notes |\n| --- | --- | --- |\n| `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. |\n| `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. |\n\n### Debugger\n\nExternal ABAP debugger over ADT's `/sap/bc/adt/debugger/*` API. Set a breakpoint,\nwait for a session to hit it, then inspect the stack/variables, step through, and\n(guarded) change values. Requires debug authorization on the backend; minimum\nNW 7.31 SP04 + Kernel 7.21 (fine on S/4).\n\n| Tool | Purpose | Notes |\n| --- | --- | --- |\n| `adt_debug_set_breakpoint` | Set an external breakpoint. | Give `uri`, or `object`+`type`(+`include`)+`line`. Optional `condition`. Returns the breakpoint `id`. |\n| `adt_debug_delete_breakpoint` | Remove a breakpoint by `id`. | |\n| `adt_debug_listen` | Bounded wait for a debuggee to hit a breakpoint. | Returns `{ caught: true, debuggee, … }` and auto-attaches, or `{ caught: false }` on timeout (default 30 s, capped 55 s) — just call again. One listener per process. |\n| `adt_debug_stack` | Call stack of the attached debuggee. | |\n| `adt_debug_variables` | Read variable values. | `names: ['sy-subrc','lv_total']`; omit for the scope roots. |\n| `adt_debug_step` | Advance execution. **WRITE.** | `kind`: `into` / `over` / `return` / `continue` / `runToLine` / `jumpToLine` (need `uri`) / `terminate`. A `continue` that lets the run finish answers `status: \"debuggeeEnded\"` — that is success. |\n| `adt_debug_goto_stack` | Move the active stack frame. **WRITE.** | `stackUri` (from the stack) or 0-based `position`. |\n| `adt_debug_set_variable` | Set a variable's value. **WRITE.** | `name` + `value`. Changes live session state. |\n| `adt_debug_set_watchpoint` / `adt_debug_delete_watchpoint` | Break when a variable changes. **WRITE.** | Best-effort — the watchpoint contract has no reference impl; check `raw` on a live run. |\n| `adt_debug_stop` | End the session: resume the debuggee, then delete the listener + all breakpoints it set. | Call when done so nothing dangles and no request stays suspended. `release: false` skips the resume. |\n\nTypical flow:\n\n```text\n1.  adt_debug_set_breakpoint { object: \"ZREPORT\", type: \"program\", line: 42 }   → id\n2.  adt_debug_listen {}          → ARM IT FIRST, then trigger the run\n3.  (trigger: a Fiori/OData call, an ADT classrun, an RFC, a job)\n4.  listen returns caught:true → already attached (or caught:false → call again)\n5.  adt_debug_stack {}           → where execution paused\n6.  adt_debug_variables { names: [\"sy-subrc\", \"lv_total\"] }\n7.  adt_debug_step { kind: \"over\" }   → advance; or set_variable / continue\n8.  adt_debug_stop {}            → resume the debuggee + cleanup\n```\n\n**What an external breakpoint traps:** HTTP/ICF (Fiori, OData, ADT), RFC and\nbackground sessions — **not your own SAP GUI dialog session**. Running the report\nfrom SE38 with F8 traps nothing and opens no debugger, which reads like a broken\nfeature but isn't. To debug dialog-only code, run it inside an ICF session with a\nthrowaway `IF_OO_ADT_CLASSRUN` wrapper and trigger\n`POST /sap/bc/adt/oo/classrun/<class>`:\n\n```abap\nMETHOD if_oo_adt_classrun~main.\n  DATA lt_list TYPE TABLE OF abaplist.\n  \" There is no screen in an ICF session, so list output must go to memory.\n  SUBMIT zoy_dbg_target EXPORTING LIST TO MEMORY AND RETURN.\nENDMETHOD.\n```\n\n`SUBMIT` opens a fresh program context, so you debug the target report on its own.\n\n**Timing:** arm `adt_debug_listen` *before* triggering the run — for a fast\nrequest (Fiori/OData) arming it after clicking is already too late. A trapped\ndebuggee then waits only ~15–20 s for the attach before resuming on its own, so\n`listen` attaches immediately with no round trip in between. A `caught: false`\ncan therefore mean three things: the run hasn't happened yet, the listener was\narmed too late, or that line never executes in this flow — keep a known-good\ncontrol breakpoint to tell a wrong breakpoint apart from a setup problem.\n\n**Read-only mode:** inspection (breakpoints, listen, stack, variables) is allowed;\nthe **WRITE** tools above (step, goto-stack, set-variable, watchpoints) are refused\nunder `readOnly: true`. Debugging **another user's** session (`requestUser`) is off\nby default; enable it per system with `\"debug\": { \"allowRequestUser\": true }`\n(needs backend debug authorization).\n\n### Data\n\n| Tool | Purpose | Notes |\n| --- | --- | --- |\n| `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. |\n\n### Transports\n\n| Tool | Purpose | Notes |\n| --- | --- | --- |\n| `adt_list_transports` | List TRs by user / status. | Default user = config user; default status = `modifiable`. |\n| `adt_get_transport` | TR header + objects. | |\n| `adt_create_transport` | Create a new TR. | Refused under `readOnly: true`. Endpoint shape varies — see Caveats. |\n| `adt_release_transport` | Release a TR. | Refused under `readOnly: true`. |\n\n### Control panel\n\n| Tool | Purpose | Notes |\n| --- | --- | --- |\n| `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). |\n| `adt_close_panel` | Stop the panel. | It also stops on its own when the session ends. |\n\n### Escape hatch\n\n`adt_request` — direct ADT REST call. Use this when a niche endpoint isn't\ncovered by a high-level tool. Handles auth / CSRF / cookies / sap-client\nautomatically.\n\n### Object types\n\nFriendly aliases (any of either column work):\n\n| Alias | TADIR code |\n| --- | --- |\n| program / report | PROG |\n| include | INCL |\n| class | CLAS |\n| interface | INTF |\n| function / fm | FUGR/FF (requires `group`) |\n| functiongroup | FUGR |\n| table / structure | TABL |\n| dataelement | DTEL |\n| domain | DOMA |\n| cds / ddls | DDLS |\n| accesscontrol / dcls | DCLS |\n| metadataext / ddlx | DDLX |\n| behaviordef / bdef | BDEF |\n| messageclass / msag | MSAG |\n\n## Skills (packaged workflows)\n\nThe raw tools are building blocks; [`skills/`](skills/) ships ready-made\nworkflows for Claude Code that orchestrate them. Each SKILL.md lists its own\nprerequisites (minimum NetWeaver release, required authorizations, read-only\ncompatibility).\n\n| Skill | What it does | Read-only OK? | Min. system |\n| --- | --- | --- | --- |\n| [`transport-release-gate`](skills/transport-release-gate/SKILL.md) | Pre-release quality gate over a TR: inactive objects, locks, syntax, ATC, unit tests → go/no-go report. Release stays a human decision. | Mostly (unit tests + release need write) | NW 7.50+, ATC configured |\n| [`dump-triage`](skills/dump-triage/SKILL.md) | ST22 triage: group dumps into families, deep-read top offenders, root cause + fix per family. | Yes — safe on PRD | NW 7.50+ (dumps feed) |\n| [`legacy-code-doc`](skills/legacy-code-doc/SKILL.md) | Reverse-document legacy Z code: structure, DB touchpoints, callers, risks, S/4 migration notes. | Yes — safe on PRD | NW 7.4x+ (data samples 7.55+) |\n| [`abap-clean-core`](skills/abap-clean-core/SKILL.md) | SAP Clean Core framework knowledge: levels, decision framework, governance. | Yes (knowledge-only) | none |\n\nTo use one, copy its folder into your project's `.claude/skills/` (or\n`~/.claude/skills/` for all projects):\n\n```bash\ncp -r node_modules/sap-adt-mcp/skills/dump-triage .claude/skills/\n# or from a clone: cp -r sap-adt-mcp/skills/dump-triage .claude/skills/\n```\n\nClaude Code picks them up automatically; they trigger when the conversation\nmatches (e.g. \"is E4DK900123 safe to release?\" → transport-release-gate).\n\n## Clean Core: prompts + reference\n\nThe server ships an opt-in **Clean Core** layer for SAP S/4HANA work.\nThere are two pieces, and they are deliberately separate:\n\n- **Five MCP prompts** (`src/prompts.js`) — the operational surface. The\n  user invokes them as slash commands. Each one pairs a slice of the\n  Clean Core framework with the relevant `adt_*` tools so the model can\n  act on a real system, not just lecture about levels.\n- **Long-form reference** ([`skills/abap-clean-core/`](skills/abap-clean-core/))\n  — the framework's full text: Stay Clean / Get Clean playbook, A/B/C/D\n  level deep-dive, Cloudification Repository state semantics, ABAP Cloud\n  allowed/forbidden lists, the SAP Application Extension Methodology\n  (3 phases), governance practices, KPI calculations, ATC exemption\n  process. Read once, link to it from PRs, hand to a new team member.\n  The prompts above quote what they need; the reference is everything\n  else.\n\n### Design choices\n\n- **Opt-in, not auto-firing.** Clean Core is an S/4HANA discipline. ECC\n  developers should not have it imposed on them. Nothing fires unless the\n  user types the slash command.\n- **ECC applicability check baked into every prompt.** The first thing\n  each prompt body asks the model to do is verify the target system is\n  S/4HANA. On ECC, it backs off and offers help in classic-ABAP idioms\n  with no level labels.\n- **Tone is descriptive, not judgmental.** \"I know it's Level D, just\n  ship it\" is honored. The agent ships, marks the level, sketches the\n  Level A refactor for later, and moves on.\n\n### The prompts\n\nIn Claude Code (assuming you registered the server as `sap-adt`), the\nexact commands are:\n\n| Command | Arguments | What it does |\n| --- | --- | --- |\n| `/mcp__sap-adt__clean_core_grade` | `object` (req), `type` (req), `system` | Grade one object A/B/C/D. Pulls source + ATC, classifies, cites reasons, sketches the Level A refactor if Level C/D. |\n| `/mcp__sap-adt__clean_core_review` | `package` (req), `system`, `maxObjects` (default 50) | Walk a package and compute Clean Core Share %, Tech Debt Score, top Level D offenders. |\n| `/mcp__sap-adt__clean_core_refactor` | `object`, `type`, `system` (all optional) | Enter refactor mode. Loads BAPI-wrapper / MARA→released-CDS / modification→BAdI patterns. With `object` it pre-seeds; without, waits for direction. |\n| `/mcp__sap-adt__clean_core_create` | `requirement`, `package`, `system` (all optional) | Enter creation mode at Level A by default — ABAP Cloud syntax, released CDS views, RAP, business object interfaces. Drives the `create_object → set_source → syntax_check → activate` pipeline. |\n| `/mcp__sap-adt__clean_core_design` | `use_case` (optional) | Architecture mode — fit-to-standard, 3-phase methodology, on-stack vs side-by-side, hybrid. No code writes. Produces a target-solution memo. |\n\nThe slash-command name structure is determined by the MCP client: the\n`mcp__<server-alias>__` prefix is added automatically based on the alias\nyou used when registering the server. If you registered the server with\na different alias (e.g. `claude mcp add cc -- npx sap-adt-mcp`), the\nprefix changes accordingly (`/mcp__cc__clean_core_grade`).\n\nIn Claude Desktop, prompts appear in the slash-command picker — same\nnaming.\n\n### Argument flow examples\n\nAtomic prompts (`grade`, `review`) take all their arguments inline and\nreturn a structured analysis:\n\n```\nYou: /mcp__sap-adt__clean_core_grade object:ZCL_PRICING type:class system:DEV\nAgent: → adt_get_source { ... }\n       → adt_run_atc { ... }\n       Verdict: Level C. Two SELECTs from MARA without using the released\n       I_Product view; one CALL FUNCTION to internal FM RV_PRICE_PRINT.\n       Refactor sketch: replace SELECT with `from I_Product`; encapsulate\n       the RV_PRICE_PRINT call in a Z-class so the dependency is localised.\n```\n\nMode-loading prompts (`refactor`, `create`, `design`) optionally take a\nseed; without one, they wait for the user's natural-language follow-up:\n\n```\nYou: /mcp__sap-adt__clean_core_create\nAgent: I'm in Clean Core CREATE mode (Level A by default). What do you\n       want to build, on which package and system?\nYou:   A small Fiori list-report of overdue invoices, package ZFIN_REPORTS,\n       system DEV.\nAgent: Plan: a CDS view projecting I_OperationalAcctgDocItemCube for items\n       with NetDueDate < today; a behavior definition; a service binding\n       exposing it to Fiori Elements list-report. Three objects. Confirm?\n```\n\nOr with a seed argument so the request is one-shot:\n\n```\nYou: /mcp__sap-adt__clean_core_create requirement:\"Fiori list-report of\n     overdue invoices\" package:ZFIN_REPORTS system:DEV\n```\n\n### Read the long-form reference\n\n[`skills/abap-clean-core/`](skills/abap-clean-core/) is the canonical\nsource for everything the prompts quote and more. If you're setting up\nClean Core governance for a real program — KPI baselines, ATC exemption\ndiscipline, maturity assessment, on-stack vs side-by-side trade-offs at\nthe architecture level — that's where the depth lives.\n\nThe directory is structured as one entry point (`SKILL.md`) plus four\ndeep-dive files in `references/`:\n\n- `references/levels-detailed.md` — Cloudification Repository state\n  values, released local vs released remote APIs, reclassification\n  dynamics, per-anti-pattern remediation\n- `references/decision-framework.md` — fit-to-standard, the SAP\n  Application Extension Methodology in detail, on-stack vs side-by-side\n  triggers, hybrid patterns, worked scenarios\n- `references/governance.md` — Stay Clean / Get Clean playbook, the four\n  KPIs and how to compute them, ATC exemption process, maturity\n  assessment, multi-year roadmap\n- `references/abap-cloud-rules.md` — full allowed/forbidden lists, RAP /\n  CDS / business object interfaces / Custom Fields, prebuilt services,\n  classic-to-cloud migration patterns\n\nYou can install the reference as an actual auto-loading Claude skill by\ncopying or symlinking `skills/abap-clean-core/` into your\n`~/.claude/skills/` — but that's an explicit choice. The default\nbehavior of this repo is opt-in, prompt-only.\n\n## Examples\n\nSee [`examples/`](examples) for end-to-end agent workflows: project discovery,\nclass audit, cross-system release verification, where-used-driven refactor,\ntest triage.\n\nQuick taste:\n\n```\nYou: \"Compare class ZCL_PRICING between DEV and PRD on the live systems.\"\nAgent: → adt_compare_source { systemA: \"DEV\", systemB: \"PRD\",\n                              object: \"ZCL_PRICING\", type: \"class\" }\n       Returns: { identical: false, stats: { added: 14, removed: 9 },\n                  diff: \"...\" }\n       Then narrates the meaningful changes.\n```\n\n## Architecture\n\n```\nMCP client (Claude Desktop / Claude Code / custom)\n         │  stdio (JSON-RPC)\n         ▼\n  ┌──────────────────────────────────┐\n  │  src/server.js                   │  CLI + MCP dispatch (thin)\n  │   ├─ src/tools/*.js              │  one module per category:\n  │   │                              │    connection, source, quality,\n  │   │                              │    lifecycle, discovery,\n  │   │                              │    cross-system, transports,\n  │   │                              │    runtime, data, request\n  │   ├─ src/object-uris.js          │  type alias → ADT URI map\n  │   ├─ src/node-structure.js       │  package tree XML parser\n  │   ├─ src/object-references.js    │  <objectReference> parser\n  │   ├─ src/dump-feed.js            │  runtime-dumps Atom parser\n  │   ├─ src/data-preview.js         │  Data Preview XML parser + SELECT guard\n  │   ├─ src/diff.js                 │  unified diff (LCS)\n  │   ├─ src/adt-error.js            │  <exc:exception> parser\n  │   ├─ src/lock.js                 │  ADT lock acquire / release\n  │   └─ src/adt-client.js           │  HTTP client: auth / CSRF / cookies / timeout\n  └──────────────────────────────────┘\n         │  HTTPS\n         ▼\n   SAP system (ADT REST: /sap/bc/adt/...)\n```\n\nTwo runtime dependencies: `@modelcontextprotocol/sdk` (the MCP wire protocol)\nand `undici` (HTTP with custom TLS dispatcher). Everything else is stdlib.\n\n## Multi-step editing pattern\n\nFor most edits, `adt_set_source` is enough — it acquires the lock, writes,\nand releases. For workflows that touch the same object multiple times within\na single turn (e.g. apply N method-level patches, then activate), use the\nsticky-lock pattern:\n\n```text\n1.  adt_lock { object: \"ZCL_X\", type: \"class\" }              → returns lockHandle\n2.  adt_set_source { object: \"ZCL_X\", type: \"class\",         (repeat as needed)\n                     source: \"...\", lockHandle: \"<handle>\" }\n3.  adt_activate { objects: [{ name: \"ZCL_X\", type: \"class\" }] }\n4.  adt_unlock { object: \"ZCL_X\", type: \"class\",\n                 lockHandle: \"<handle>\" }\n```\n\nThe `lockHandle` parameter on `adt_set_source` skips internal lock/unlock\nwhen present.\n\n### Large objects (thousands of lines)\n\nA multi-thousand-line class or program can exceed the per-call I/O cap, so its\nsource cannot be passed inline. Keep the content on disk instead — it never\nenters the agent context:\n\n```text\n1.  adt_get_source { object: \"ZCL_BIG\", type: \"class\",\n                     outputFile: \"/tmp/zcl_big.abap\" }   → writes the file, no inline source\n2.  (edit /tmp/zcl_big.abap locally)\n3.  adt_set_source { object: \"ZCL_BIG\", type: \"class\",\n                     sourceFile: \"/tmp/zcl_big.abap\",\n                     transport: \"E4DK900123\" }           → MCP reads the file and PUTs it\n```\n\nNote: assigning to a transport works headless, but TR *creation*\n(`adt_create_transport`) routes through a GUI dialog on some systems and can\nfail with a 500 — pass an existing TR id instead.\n\n## Caveats\n\n- **NetWeaver release variation.** A few ADT endpoints (especially around\n  transport requests, ATC, and object-create XML shapes) have small shape\n  differences across NW 7.5x, S/4 on-prem, and Steampunk. If a high-level\n  tool fails with HTTP 4xx, the tool description notes which endpoint it\n  hits — fall back to `adt_request` with the right path / content type for\n  your release.\n- **ABAP Cloud (BTP / Steampunk).** Only on-prem ADT 7.5x+ has been actively\n  exercised. Steampunk uses a stricter object-type allowlist (only released\n  / public APIs) and some collection endpoints differ. Steampunk users:\n  please open an issue with the endpoints that differ; PRs welcome.\n- **Object-create XML shapes.** The body templates target modern on-prem\n  releases. Some older systems require additional attributes\n  (`adtcore:masterLanguage`, etc.) — open an issue if your system rejects\n  the create payload, and include the response body.\n- **DDIC primitives** (tables, data elements, domains) are not creatable via\n  `adt_create_object` — these need a richer DDIC-specific payload that we\n  haven't generalised. Use `adt_request` for now.\n\n## Troubleshooting\n\n**\"failed\" in Claude Desktop's MCP server list.** Run `sap-adt-mcp\n--validate-config` from a terminal. If that prints OK, the server is fine and\nthe issue is in your `claude_desktop_config.json` (wrong path or env).\n\n**403 with `x-csrf-token: required`.** Should self-heal — the client refetches\nthe token and retries. If it persists, you likely have an SSO / front-end\nauth in front of ADT that breaks Basic auth; check your ICM and SAP web\ndispatcher rules.\n\n**Read-only mode refuses an obviously-read endpoint.** It's probably a POST\nendpoint not in the whitelist. Open a PR or issue with the path; we'll add\nit. Or temporarily flip the system to `readOnly: false`.\n\n**`SAP_ADT_MCP_DEBUG=1`** traces every request and response (status, latency,\nURL, request headers minus `Authorization`) to stderr. The MCP client shows\nstderr in its server log, so check there.\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) — bug reports, new tool coverage,\nNetWeaver compatibility notes, docs, examples all welcome.\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n",
  "bytes": 35278,
  "sha": "5b59075375a3622fd785c6388cf2608e53b87cfbdb911dca3af45c73383d7405",
  "repo_slug": "yzonur/sap-adt-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_yzonur_sap_adt_mcp_85637efb/readme"
}