abap-config-mcp
Connect to configured SAP ABAP systems through an MCP server and perform ABAP development, quality, transport, data-analysis, debugging, and customizing/IMG tasks via ADT and an in-system customizing engine.
Discover and search ABAP objects, read source, grep source lines, batch-read objects, and inspect metadata/ADT services.
Create, write, lock/unlock, delete, and activate ABAP objects, including batch activation and syntax checks.
Run ATC analysis, ABAP unit tests, console classes, and create test includes.
Manage transports: list all users' requests, inspect/create/release/delete/change owner, find transport for an object, and move objects/tasks with dry-run checks.
Query SAP data: search tables by description, describe table structures, run read-only SELECT queries, and read table contents.
Analyze code and runtime: where-used, version history, ST22 dumps, and inactive objects.
Maintain text elements for programs, classes, and function groups.
Browse and create packages.
Debug ABAP: attach/listen, set/delete breakpoints, step, inspect/change variables, and view call stack.
Search IMG/SPRO activities and describe/read/diff/plan/apply customizing changes through SM30-style runtime with governed transport recording.
Create customizing rows, copy/delete org units EC01-style, perform retail article listing, poll async writes, run engine self-tests, deploy/clean up/ping the customizing engine, and choose transportable vs local engine deployment.
Run HANA and ABAP memory reports, force re-login to heal degraded sessions, and use tier-based tool exposure for read-only through full development surfaces.
Connects directly to SAP systems via the ADT REST API to provide ABAP development and customizing/IMG capabilities. Enables searching, reading, writing, activating, debugging, and testing ABAP objects; querying table data; managing transports; and driving SM30/SM34 maintenance-view customizing with foreign-key checks, change documents, and transport recording.
abap-config-mcp
A standalone Model Context Protocol (MCP) server for SAP ABAP development and customizing. Connects directly to your SAP system via the ADT REST API and exposes 60 tools to any MCP-compatible AI client — no VS Code, no Copilot subscription required.
It does two things that, together, let an AI assistant work in an SAP system the way a developer/consultant does:
ABAP development — search, read, write, activate, transport, debug, run ATC & unit tests, browse packages, query and compare table data — a full ABAP development toolset exposed as standalone MCP tools.
Customizing / IMG (the headline addition) — an in-system customizing engine the server deploys into your SAP box drives the standard SM30/SM34 maintenance-view runtime, so configuration changes get foreign-key checks, change documents and governed transport recording exactly as a manual SPRO change would. Search the IMG, read/diff/copy config, copy whole organizational units. See docs/customizing-engine.md.
Works with Claude Desktop, Claude Code (the terminal/prompt), Cursor, Windsurf, Microsoft 365 Copilot, IBM Bob, and any other MCP-compatible AI client.
Acknowledgements
The idea of driving SAP ADT from outside the SAP GUI was inspired by Marcello Urbani's excellent marcellourbani/vscode_abap_remote_fs — go give it a star. This project is an independent, from-scratch MCP server (its own development tools and its own in-system customizing engine); it does not contain that project's code. It does build on Marcello's separately published, MIT-licensed abap-adt-api library for the low-level ADT protocol calls. See NOTICE for full attribution.
⚠️ Before you connect this to anything, read Security & authorization. In short: protect the server with a strong shared secret, and remember the server can do nothing beyond the rights of the one SAP user it logs in as. It is built for DEV → UAT; on Production, only read-only investigation with a read-only user is intended.
What it enables
Ask your AI assistant things like:
"Find program ZCAR_MM_ORDERS and add a method to filter by plant, then activate it on transport CARX000123"
"Run ATC checks on class ZCL_INVOICE_PROCESSOR and fix any priority-1 findings"
"Show me all open transports across all developers in the system — who owns what and what objects are in each one?"
"Find tables related to article listing — we're on IS-Retail so I don't know the technical names"
"Debug the next execution of report ZCAR_BATCH_JOB — set a breakpoint on line 142 and inspect variable LT_ITEMS when it hits"
"Copy the POS profile customizing from profile 0001 to ZRET and record it on a Customizing transport the SM30-standard way"
The AI handles the full workflow autonomously: search → read → write → check quality → activate → test. For customizing/IMG it drives the in-system maintenance-view runtime (SM30/SM34) so FK checks, change documents and transport recording all happen exactly as a manual SPRO change would — see docs/customizing-engine.md.
Related MCP server: MCP-ADT
Feature overview
Category | Tools | What the AI can do |
Discovery | 4 tools | Find objects by name/wildcard, inspect metadata, list systems, explore ADT services |
Source reading | 4 tools | Read full source, grep within source, batch-read multiple objects, syntax-check without saving |
Write | 5 tools | Lock objects, write source, unlock/discard, create new objects, delete objects |
Activate | 2 tools | Activate single objects, batch-activate all inactive objects |
Quality | 4 tools | ATC checks with findings and locations, unit test execution, run a console class ( |
Transports | 4 tools | List transports across all users with their objects (read from E070/E071), create/release/delete/change owner, find which transport an object needs, move objects or a whole task to another request with dependency and release-order checks. Every write picks its transport by one rule — see Transport selection |
Data | 4 tools | Search tables by description keyword, describe table structure, run SELECT queries, read table contents |
Analysis | 4 tools | Where-used cross-reference, version history, runtime dumps (ST22), inactive objects list |
Text elements | 2 tools | Read/write selection screen texts, text symbols, and list headings |
Packages | 2 tools | Browse package contents grouped by type, create new packages |
Debugging | 7 tools | Attach debugger, set/delete breakpoints, step through code, inspect/change variables, call stack |
Customizing & IMG | 14 tools | Search IMG/SPRO, describe/read/diff/plan customizing, copy or delete config the SM30-standard way with governed transport recording, create new rows from explicit values, headless org-unit copy, retail article listing, bootstrap/cleanup the in-system engine — see docs/customizing-engine.md |
Operations | 4 tools | Force re-login to heal a degraded ADT session, HANA and ABAP memory reports, install the in-system ABAP as local or transportable ( |
Quick start
1. Install
git clone https://github.com/netweaver1970/abap-config-mcp
cd abap-config-mcp
npm install
npm run build2. Configure connections
There are two separate pieces of configuration — don't mix them up:
What | Where | Who sets it |
SAP systems the server may log into (host, user, password, client) — plus the server's port and shared secret |
| Once, on the machine running the server |
Registering this server with your AI client (the MCP endpoint URL + the shared secret) | Your AI client's config — a different file per client (Claude Desktop vs. Claude Code vs. Cursor…). See Connecting AI clients | Each user / each client |
This step is the first one. Create connections.json in the project directory:
{
"connections": [
{
"id": "DEV",
"url": "http://your-sap-host:8001",
"username": "DEVELOPER",
"password": "your-password",
"client": "100",
"language": "EN"
}
],
"server": {
"port": 4847,
"apiKey": "replace-with-a-random-secret"
}
}A call that names no connectionId goes to the first connection listed. To pick another, add "defaultConnection": "<id>" at the top level (or set ABAP_MCP_DEFAULT_CONNECTION); connected_systems shows which one it is.
connections.jsonis gitignored and never committed — it holds SAP passwords and the shared secret. Seeconfig/connections.example.jsonfor reference.
The server.apiKey is the shared secret every AI client must present to use the server. Generate a strong one now and paste the same value into both this file and each client config later:
openssl rand -hex 32 # e.g. 9f2c… → put this in server.apiKeyWhy it matters and what the SAP user can/can't do is covered in Security & authorization — please read it before connecting a client.
Connection options:
Field | Required | Description |
| yes | Unique name for this system (e.g. |
| yes | SAP ICM base URL. System nr |
| yes | SAP logon credentials |
| no | Logon client (e.g. |
| no | Logon language (default |
| no | Set |
| no | PEM CA certificate string to trust |
| no | HTTP request timeout in milliseconds |
3. Verify the connection
npm run test-connection # tests the first configured connection
npm run test-connection -- CAR # tests a specific connection by idThe test logs in, fetches the ADT discovery endpoint, and runs a sample object search. It prints specific troubleshooting hints (port, client, credentials, ICF node) if anything fails.
4. Start the server
npm startYou should see the startup banner (your version numbers may be newer):
╔══════════════════════════════════════════════════════════════════════╗
║ ABAP Config MCP Server v1.9.0 ║
╠══════════════════════════════════════════════════════════════════════╣
║ Server version : 1.9.0 ║
║ Cust. engine ver. : 0.9.17 (expected on SAP after deploy) ║
╠══════════════════════════════════════════════════════════════════════╣
║ MCP endpoint : http://localhost:4847/mcp ║
║ Health check : http://localhost:4847/health ║
╠══════════════════════════════════════════════════════════════════════╣
║ Debug mode : ABAP_MCP_DEBUG=1 npm start ║
║ Session idle : ABAP_SESSION_IDLE_MS=360000 npm start (default 8m)║
╚══════════════════════════════════════════════════════════════════════╝Leave this terminal running. Check it any time with curl http://localhost:4847/health →
{"status":"ok",...}. To stop it, press Ctrl-C. (To run it permanently in the
background instead, see 4b below.)
4b. (Optional) Run it permanently in the background
npm start (step 4) is the manual way — it runs in the foreground and stops when
you close the terminal or log out. For day-to-day use you usually want the server to
start automatically at login and restart itself if it crashes. Pick your OS:
Install the bundled LaunchAgent (deploy/com.abap-config-mcp.server.plist):
# 1. Check the node path in the plist (which node, e.g. /opt/homebrew/bin/node).
# The plist expects the repo at ~/github/abap-config-mcp; edit it if yours is elsewhere.
# 2. Install it with YOUR_USER replaced by your home folder (launchd does not expand ~),
# then start it:
sed "s|/Users/YOUR_USER|$HOME|g" deploy/com.abap-config-mcp.server.plist \
> ~/Library/LaunchAgents/com.abap-config-mcp.server.plist
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.abap-config-mcp.server.plist
launchctl enable gui/$(id -u)/com.abap-config-mcp.serverRunAtLoad starts it at login; KeepAlive restarts it if it ever exits. Logs go to
~/Library/Logs/abap-config-mcp.log. The agent sets WorkingDirectory to the repo
root because connections.json and the engine ABAP (src/abap/*.abap) resolve
relative to the working directory.
launchctl print gui/$(id -u)/com.abap-config-mcp.server | grep -E 'state|pid' # status
launchctl kickstart -k gui/$(id -u)/com.abap-config-mcp.server # restart (after a rebuild)
launchctl bootout gui/$(id -u)/com.abap-config-mcp.server # stop & unloadSimple — Task Scheduler (auto-start at logon):
Open Task Scheduler → Create Task…
General: name
ABAP Config MCP Server; tick Run only when user is logged on.Triggers → New: At log on.
Actions → New:
Program/script:
C:\Program Files\nodejs\node.exe(runwhere nodeto confirm)Add arguments:
dist\index.jsStart in:
C:\path\to\abap-config-mcp← must be the repo root, soconnections.jsonandsrc\abapresolve.
Save. Right-click the task → Run to start it now.
Robust — install as a Windows Service with NSSM (auto-restarts on crash, runs without login):
nssm install ABAP-CONFIG-MCP "C:\Program Files\nodejs\node.exe" "dist\index.js"
nssm set ABAP-CONFIG-MCP AppDirectory "C:\path\to\abap-config-mcp"
nssm set ABAP-CONFIG-MCP AppStdout "C:\path\to\abap-config-mcp\logs\server.log"
nssm set ABAP-CONFIG-MCP AppStderr "C:\path\to\abap-config-mcp\logs\server.log"
nssm start ABAP-CONFIG-MCPEither way, verify with curl http://localhost:4847/health.
pm2 works the same on macOS, Windows and Linux:
npm install -g pm2
pm2 start dist/index.js --name abap-config-mcp --cwd /path/to/abap-config-mcp
pm2 save && pm2 startup # follow the printed command to enable boot-startAfter a
npm run build, restart whichever service you use (launchctl kickstart -k …, Task Scheduler Run,nssm restart ABAP-CONFIG-MCP, orpm2 restart abap-mcp) so it serves the newdist/. ABAP-only edits tosrc/abap/*.abapneed no restart — the engine source is read from disk per call.
4c. (Optional) Run it in Docker
The image runs the stdio server, for clients and registries that start MCP servers in a container:
docker build -t abap-config-mcp .
docker run -i --rm -e SAP_URL=https://your-sap-system:44301 -e SAP_USER=DEVELOPER \
-e SAP_PASSWORD=... -e SAP_CLIENT=100 abap-config-mcpFor several systems, mount a connections.json and set ABAP_MCP_CONFIG=/config/connections.json. With no system configured the server still starts and lists its tools, but every tool call fails until one is set.
5. Connect your AI client
Pick the section below for your client. All clients use the same running server — you can connect multiple clients simultaneously.
Security & authorization
Please read this before exposing the server to any client. There are two independent layers of protection — the shared secret (who may talk to the server) and the SAP user's authorizations (what the server is allowed to do once it's in SAP).
1. The shared secret (server.apiKey)
The server is a plain HTTP service. Anyone who can reach its port and knows the secret can drive every tool. The secret is the only thing standing between the network and your SAP system, so treat it like a password.
It lives in
connections.jsonunderserver.apiKey, and the exact same value must be set in every AI client config (asAuthorization: Bearer <secret>, orx-api-keyfor clients that use that header — see each client below).Every request without a matching secret is rejected with HTTP 401. The comparison is constant-time. If you leave
apiKeyempty the server runs unauthenticated and logs a warning — only ever acceptable onlocalhostfor a quick local test.Use a long, random secret — not a dictionary word, not
changeme, not your username. Generate one withopenssl rand -hex 32(or[Convert]::ToBase64String((1..32|%{Get-Random -Max 256}))in PowerShell). Rotate it if it ever leaks; rotating means updating the server file and every client.Bind to
localhost(the default) and reach it over your VPN, or put it behind a TLS-terminating reverse proxy / API gateway if a remote cloud client (e.g. M365 Copilot) must reach it. Never put a plain-HTTP server with a weak secret on a routable address.
2. The server can do nothing beyond the SAP user's rights
This is the most important guardrail. The server holds no special powers of its
own — every action runs through ADT / RFC as the SAP user in that connection's
username, inside that user's client. SAP's own authorization checks apply to
everything:
writing/activating code needs
S_DEVELOP; table maintenance needsS_TABU_DIS(the customizing engine explicitly enforces it on the target table); transports needS_TRANSPRT; debugging needsS_DEVELOPdebug activities, and so on.If the user lacks an authorization, the tool fails with SAP's authorization error — the server cannot escalate around it.
So scope the service user with least privilege for what you actually want to do, and rely on SAP roles — not the tool — as the real control. A read-only user makes the whole server effectively read-only no matter which tools a client calls.
3. Where it belongs in the landscape: DEV → UAT, read-only on Production
This tooling is intended for development and UAT systems, where writing code and customizing is the point. It is not meant to perform development or customizing on Production:
A Production client is normally closed for changes anyway (SCC4 /
SE06system change option), so write/customizing tools would be refused by SAP regardless.What is legitimate on Production — and needs no write access at all — is read-only investigation: reading code,
where-used, dump analysis (ST22), and table-content comparisons between systems. Point a read-only, limited user at the Production connection for those use cases; the write tools simply won't have the rights to do anything, which is exactly what you want.
Rule of thumb: give the connection the least SAP authorization that still lets you do the job, and let SAP enforce it. On Production, that authorization is read-only.
4. Risk tiers — cap the tool surface with ABAP_MCP_MAX_TIER
SAP authorizations are the real control, but the server can also hide whole classes of tool so a deployment exposes only what corporate security accepts. All 60 tools are classified into three risk tiers (full registry: docs/TIERS.md):
Tier | What it does | Examples |
0 | read-only / diagnostics |
|
1 | config / data writes (SM30 runtime, governed transports) |
|
2 | repository / code writes, execution, debug |
|
Set ABAP_MCP_MAX_TIER = 0 (read-only Production server), 1 (also allow
governed customizing), or 2/unset (full Dev surface). Tools above the ceiling are
not registered at all, and any unclassified tool defaults to Tier 2 — a new tool
is never silently exposed on a restricted server.
Transport selection
Every tool that records onto a transport — create_abap_object, write_abap_object_source,
delete_abap_object, set_text_elements, create_test_include, create_package,
customizing_create, customizing_apply — decides by the same rule, in this order:
A transport you pass is checked (exists, still modifiable, right kind: Workbench for repository objects, Customizing for customizing) and used. It becomes the transport of that piece of work. If SAP already has the object locked in a different request, nothing is written and the tool names the request SAP requires.
An object SAP already has locked into a request records there — SAP allows nothing else. That lock belongs to the one object: it does not become the transport for the rest of the work.
createTransport: truecreates a new request. Nothing else ever creates one.The transport this piece of work already uses, if still open, is used again, and the result says so. Pass another
transportto change it.Existing requests are preferred: exactly one open request of the right kind is used; with several, the tool lists them and asks which; with none, it asks, offering creation. The list holds your own requests and those where you own a task, created in the logon client.
A choice is remembered only after the write has gone through, so a refused write never moves a
piece of work to another request. createTransport: true is ignored — and the result says so — when
rule 1 or 2 applies. If SAP's own transport check fails, nothing is written and the tool says why;
it never leaves SAP to pick a request on its own.
A piece of work is workItem (any short name — HPM, a ticket number). Without one it is the MCP
session, so two conversations never share a transport by accident. The choices are kept per SAP
connection in ~/.abap-mcp/transport-memory.json (override with ABAP_MCP_TRANSPORT_MEMORY), so
they survive server restarts; manage_transport_requests list shows which work item each request
belongs to. Local packages ($TMP), delivery class A tables and clients that do not record changes
need no transport.
5. In-system ABAP: transported, not pushed (engine_deploy)
The server installs a small amount of ABAP into the box (the customizing/diagnostic engine). For Production these objects should arrive via normal CTS transport, not the Tier-2 bootstrap push. They're split into two units so the low-risk part can ship and be approved on its own:
Unit | In-system object | Tier | Prod path |
|
| 0 | ✅ ship via CTS — standalone box-health value |
|
| 1 | with scrutiny |
The engine_deploy tool makes transportability a per-connection choice: run it per
unit with transportable:true package:"ZMCP_DIAG" transport:"…" to create it in a
Workbench Z package on a transport (CTS to Prod), or transportable:false for $TMP
(Dev-only); re-run any time to flip a unit. The unit's SICF node is registered
automatically as part of the deploy. See docs/TIERS.md for the
full deployment + capability-awareness story.
Connecting AI clients
Reminder (from step 2): SAP systems are configured once in
connections.jsonon the server; what differs per client is where you paste the endpoint URL + shared secret. The Claude Code "prompt" (terminal) and the Claude Desktop app use different config files — pick your client below.
Claude Desktop
The desktop app (the Claude GUI). It speaks MCP over stdio, so it uses the
mcp-remote bridge to reach our HTTP server. Edit the Claude Desktop config file
(create it if it doesn't exist) — this is not the same file Claude Code uses:
macOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:
%APPDATA%\Claude\claude_desktop_config.json(Microsoft Store install:%LOCALAPPDATA%\Packages\Claude_…\LocalCache\Roaming\Claude\claude_desktop_config.json)
The easiest way to find the right file is Settings → Developer → Edit Config in the app. On Windows that opens File Explorer with the file selected — right-click it → Open with → Notepad.
macOS
{
"mcpServers": {
"abap": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"http://127.0.0.1:4847/mcp",
"--header",
"Authorization: Bearer replace-with-a-random-secret"
]
}
}
}Windows
{
"mcpServers": {
"abap": {
"command": "cmd",
"args": [
"/c", "npx", "-y", "mcp-remote@latest",
"http://127.0.0.1:4847/mcp",
"--transport", "http-only",
"--header", "Authorization:${AUTH_HEADER}"
],
"env": {
"AUTH_HEADER": "Bearer replace-with-a-random-secret"
}
}
}
}Why the Windows version differs:
cmd /c npx— Claude Desktop can't launchnpxdirectly on Windows (spawn npx ENOENT).Token in
env, no space afterAuthorization:— Windows splits arguments that contain spaces, so"Authorization: Bearer …"arrives mangled and the server answers 401.127.0.0.1, notlocalhost(both platforms) — the server listens on IPv4127.0.0.1only, and recent Node resolveslocalhostto IPv6::1first →ECONNREFUSED.
The secret must match the one the server was started with. Start the server before Claude Desktop, then fully quit Claude (tray / menu-bar icon → Quit — closing the window isn't enough) and reopen it. The ABAP tools will appear in the tools list.
Troubleshooting: test the bridge on its own from a terminal — if this lists the ABAP tools, the server and secret are fine and any remaining problem is in the Claude config:
npx -y -p mcp-remote@latest mcp-remote-client http://127.0.0.1:4847/mcp --header "Authorization:Bearer replace-with-a-random-secret"Claude Desktop's own log is mcp-server-abap.log in %APPDATA%\Claude\logs\ (Windows)
or ~/Library/Logs/Claude/ (macOS).
Claude Code (the terminal / "prompt")
The command-line Claude. It connects to HTTP MCP servers directly (no bridge). The easiest way is the CLI — run this from any terminal:
claude mcp add --transport http abap http://localhost:4847/mcp \
--header "Authorization: Bearer replace-with-a-random-secret"Or edit the config by hand — per-project .mcp.json (in the project root, shareable
with your team) or your user-level Claude Code config:
{
"mcpServers": {
"abap": {
"url": "http://localhost:4847/mcp",
"headers": {
"Authorization": "Bearer replace-with-a-random-secret"
}
}
}
}Check it connected with claude mcp list (or /mcp inside a session).
Same server, different doorway: the desktop app and the terminal keep their MCP registration in separate files, but both point at the one running server and use the same shared secret.
Cursor / Windsurf
Both support MCP natively. Add to your MCP settings (Cursor: Settings → MCP, Windsurf: Settings → AI → MCP Servers):
{
"mcpServers": {
"abap": {
"url": "http://localhost:4847/mcp",
"headers": {
"Authorization": "Bearer replace-with-a-random-secret"
}
}
}
}Microsoft 365 Copilot (via Copilot Studio)
M365 Copilot connects to MCP servers through Copilot Studio, which then surfaces the tools in Copilot Chat, Teams, Word, Excel, and other Microsoft 365 apps.
Network requirement: The MCP server must be reachable from Microsoft's cloud. For internal SAP systems, expose the server through your corporate API gateway (e.g. Azure API Management) or a reverse proxy in your DMZ. The server needs no code changes — just a reachable HTTPS URL.
Steps:
Start the ABAP Config MCP server and expose it at an HTTPS URL your organisation's cloud can reach, for example:
https://abap-mcp.yourcompany.com/mcpGo to Copilot Studio → open or create an agent
In the agent editor: Tools → Add Tool → New Tool → MCP
Fill in the connection details:
Field
Value
Name
ABAP Config MCP ServerURL
https://abap-mcp.yourcompany.com/mcpAuthentication
API Key
API Key
your
server.apiKeyvalue fromconnections.jsonCopilot Studio auto-discovers all 60 tools via the MCP handshake — no manual tool registration needed
Publish the agent and deploy it to Microsoft 365
Authentication options:
Option | When to use |
API Key | Simplest — good for internal/pilot use |
OAuth 2.0 (DCR) | Production — use with Azure API Management in front of the server |
Once published, users can invoke ABAP tools from Copilot Chat in Teams or the Microsoft 365 web app using natural language exactly like any other Copilot client.
IBM Bob
IBM Bob is IBM's AI coding IDE (1.0 released March 2026). It has native MCP support using the same config format as Claude Code.
Per-project config — create .bob/mcp.json in your project root (commit this file to share the setup with your team):
{
"mcpServers": {
"abap": {
"url": "http://sapcar:4847/mcp",
"headers": {
"x-api-key": "replace-with-a-random-secret"
}
}
}
}Global config (per developer, not checked in) — create ~/.bob/mcp.json with the same structure, or configure via Bob → Settings → MCP in the IDE.
If Bob and the SAP system are on the same network (e.g. both inside your corporate VPN), use the plain
http://URL directly — no need to expose the server externally.
Once configured, Bob's chat and agent modes can call all 60 tools directly: search objects, read/write source, manage transports, browse packages, query tables, and more.
Testing
The project has two independent test suites.
Unit tests — no SAP required
380 tests across the tool handlers. Each handler is tested in isolation with a mocked ADTClient — no SAP system, no network, runs in under 3 seconds. This is the default npm test.
npm test # run once
npm run test:watch # watch mode during developmentCategory | Tests | What is verified |
Discovery | 14 | Search results, empty results, metadata fields, class includes, service list |
Source | 11 | Source read, null source, grep matches/misses, batch errors, syntax errors |
Write | 13 | Lock→write sequence, transport passthrough, rollback on failure, create/delete |
Activate | 10 | Single activate, batch from inactive list, preaudit flag, failure formatting |
Quality | 12 | ATC findings format, clean result, unit test pass/fail/alert details, test include lifecycle |
Transports | 21 | List, details with objects, create, release success/failure, delete, change_owner, all-users scan, error tolerance |
Data | 12 | DML blocking, SELECT validation, column formatting, row access, table name validation |
Analysis | 9 | Where-used snippets, fallback to refs, version list, dump format, inactive list |
Text elements | 15 | Read selections/symbols/headings, all-categories fetch, error tolerance, write with lock, transport, rollback |
Packages | 11 | Browse contents grouped by type, empty package, sub-package expansion, tolerates failures, create with all options |
Table discovery | 15 | Search TABL+VIEW types, result count, package shown, not-found, describe fields, key markers, invalid name |
Debug | 19 | Session status/detach/listen/conflict, breakpoint URI format, step types, variable display |
Session recovery | 10 | Auto-relogin retry on degraded session (HTTP 400/401/CSRF), |
Total | 172 |
The customizing / IMG engine tools (
customizing_*,org_copy,img_search,hana_memory_report) are exercised by the integration suite (below), not the unit suite — they need a live SAP system with the engine deployed, so they can't run against a pure mock.
Integration tests — requires a live SAP connection
24 read-only tests that run against a real SAP system. They verify the full end-to-end path: login → ADT call → response parsing → formatted output. Nothing is written or activated — safe to run against any system including productive.
SAP_TEST_CONNECTION=CAR npm run test:integrationReplace CAR with the id of any connection in your connections.json.
Category | Tests | What is verified |
Discovery | 7 | Connected systems list, Z* search, type filter, impossible pattern, ADT services, object metadata |
Source | 5 | Read real source, grep match/no-match, batch read, syntax check |
Data | 6 | SELECT from TDEVC, DML rejection, maxRows limit, table read, WHERE clause, invalid table name |
Analysis | 4 | Where-used references, version history, ST22 dump list, inactive objects |
Transports | 3 | List open transports, list by username, transport info for object |
Design notes:
Tests that depend on a Z* object existing log a skip message instead of failing if none are found — the suite works on a near-empty system.
Tests run serially with a 30-second timeout each to avoid saturating the ADT session.
npm testnever runs integration tests — they are completely separate and only triggered bynpm run test:integration.
All 60 tools — detailed reference
Discovery
connected_systems
Lists all SAP systems configured in connections.json.
Useful as a first step to confirm which systems are available and what their IDs are.
→ Returns: id, url, client, language for each configured connectionsearch_abap_objects
Search for ABAP development objects by name or wildcard pattern, with optional type filtering.
Parameters:
query Pattern to search (wildcards * and + supported, e.g. "ZCAR_MM*")
connection System id (optional, uses first if omitted)
maxResults Max objects to return (default 100)
objectType Filter by type: "PROG", "CLAS", "FUGR", "TABL", "INTF", etc.
→ Returns: name, type, package, description for each matchget_abap_object_info
Get detailed metadata for a specific ABAP object: package, responsible developer, include structure, and the ADT URL needed for subsequent read/write operations.
Parameters:
objectName ABAP object name (e.g. "ZCL_MY_CLASS")
objectType Object type (e.g. "CLAS/OB")
connection System id
→ Returns: name, type, package, responsible, ADT URL, class includes (for classes)adt_discovery
Discover which ADT services and features are available on the connected SAP system. Useful for checking compatibility before using advanced features.
→ Returns: list of service collections with their titles and endpointsSource reading
get_abap_object_lines
Read the full source code of any ABAP object — programs, classes, function modules, includes, interfaces, etc.
Parameters:
objectUrl ADT URL of the object (from get_abap_object_info or search)
connection System id
→ Returns: full source as text with line numberssearch_abap_object_lines
Grep-style search within an object's source. Finds all lines matching a pattern, returned with their line numbers.
Parameters:
objectUrl ADT URL of the object
pattern Text or pattern to search for
connection System id
→ Returns: matching lines with line numbersget_abap_batch_lines
Read source code of multiple ABAP objects in a single call — efficient when analysing several related programs or classes at once.
Parameters:
objects Array of { objectUrl, connection } entries
→ Returns: source for each object, keyed by URLsyntax_check
Check ABAP source code syntax against the server without saving or activating. Useful for validating a proposed change before writing it.
Parameters:
objectUrl ADT URL of the object
mainUrl URL of the main object (for includes)
source Source code to check
connection System id
→ Returns: list of syntax errors/warnings with line, offset, severity, and messageWrite operations
write_abap_object_source
Write new source code to an ABAP object. Automatically locks the object before writing. The object remains locked after the write — call abap_activate to compile and unlock, or unlock_abap_object to discard.
Parameters:
objectUrl ADT URL of the object
source New source code (full replacement)
sourcePath Instead of source: absolute path of a local file under your home
directory (UTF-8, max 5 MB), sent as it is — for large sources that
should not pass through the conversation. Exactly one of the two.
transport Transport request number (required for transportable packages)
connection System id
→ Returns: confirmation; object stays locked awaiting activationlock_abap_object
Explicitly lock an ABAP object for editing. Returns a lock handle required for write and activate operations. Use when you need the handle for a series of operations (e.g. creating a test include).
Parameters:
objectUrl ADT URL of the object
connection System id
→ Returns: lock handle (LOCK_HANDLE string)unlock_abap_object
Unlock a locked object and discard any unsaved changes. Use this to roll back a write without activating.
Parameters:
objectUrl ADT URL of the object
lockHandle Lock handle from lock_abap_object
connection System idcreate_abap_object
Create a new ABAP development object — program, class, interface, function group, table, data element, domain, and more.
Parameters:
objectType Type id (e.g. "PROG", "CLAS/OB", "FUGR", "TABL")
objectName Name for the new object
parentName Package name
description Short description
parentPath ADT path of the parent package
responsible Developer responsible (optional, defaults to current user)
transport Transport number (required for transportable packages)
connection System id
→ Returns: ADT URL of the newly created objectdelete_abap_object
Delete an ABAP development object. The object is locked, then deleted. Requires a transport for transportable objects.
Parameters:
objectUrl ADT URL of the object
transport Transport number
connection System idActivation
abap_activate
Activate a single ABAP object — compiles it and makes it executable. Equivalent to pressing F8/Activate in SE80. The server automatically resolves includes so you only need to name the top-level object.
Parameters:
objectName Name of the object (e.g. "ZCL_MY_CLASS")
objectUrl ADT URL of the object
preaudit Run pre-activation ATC checks before activating (default false)
connection System id
→ Returns: success flag, any activation messages with line referencesabap_activate_multiple
Activate several ABAP objects in one batch. Automatically queries the system for all currently inactive objects and activates the ones you specify — or all of them if no list is given.
Parameters:
objectUrls Array of ADT URLs to activate (omit to activate all inactive objects)
preaudit Run pre-activation ATC checks (default false)
connection System id
→ Returns: success flag, messages per object, list of still-inactive objectsQuality
run_atc_analysis
Run ABAP Test Cockpit (ATC) checks on an object and return all findings with priority, check name, message text, and source location. Covers code style, performance, security, and correctness rules.
Parameters:
objectUrl ADT URL of the object
connection System id
variant ATC check variant (optional, uses system default)
maxResults Maximum number of findings (default 100)
→ Returns: findings sorted by priority, each with:
priority (1=error, 2=warning, 3=info), checkId, checkTitle,
messageTitle, source line numberrun_unit_tests
Execute ABAP unit tests for an object and return full results: which tests passed, which failed, and the failure details with stack traces.
Parameters:
objectUrl ADT URL of the object
connection System id
→ Returns: per-class, per-method results with pass/fail, alert kind,
severity, title, and detail lines for failuresabap_run_class
Run a class that implements IF_OO_ADT_CLASSRUN (ADT's "Run as ABAP Application (Console)") and return what it wrote to out. Each run gets a stateless SAP session of its own, so it always runs the current active version. The conversation's stateful session, which keeps running a class pool it has already loaded, is not used, and a slow run cannot leave that session stale. A run still going after waitSeconds keeps running in SAP and the answer carries a runId; call again with that runId to wait for it and get the output (the server waits for SAP up to 30 min, ABAP_RUN_CLASS_TIMEOUT_MS).
Parameters:
className Class to run (starts a run)
runId Instead of className: collect a run that was still going
waitSeconds How long this call waits (default 25, max 40)
connectionId System id
→ Returns: the class's console output, or a runId while it is still runningcreate_test_include
Create a local test class include for an ABAP class (the CCAU include). The class must be locked before calling this. Returns the include URL for subsequent source writes.
Parameters:
classUrl ADT URL of the class
lockHandle Lock handle from lock_abap_object
transport Transport number
connection System id
→ Returns: ADT URL of the new test includeTransports
list_all_transports
List open (or all) transport requests across all users in the SAP system. Shows each transport's owner, description, status, and every object it contains. Ideal for a pre-upgrade audit of what is in flight.
Parameters:
status "modifiable" (default — open/unreleased) | "released" | "all"
connectionId System id
→ Returns: transports grouped by user, each showing:
transport number, status, description, owner,
and all contained objects (PGMID / type / name)manage_transport_requests
Full lifecycle management for individual transport requests.
Parameters:
action "list" — open transports for one user
"details" — full readable view: tasks, objects, owner, status
"create" — new workbench transport
"release" — export transport to import queue
"delete" — permanently delete transport
"change_owner" — reassign transport to another user
transportNumber Transport number for details/release/delete/change_owner (e.g. DEVK123456)
username Username for list action (defaults to current user)
newOwner New owner username for change_owner action
objectUrl Object ADT URL (create action)
description Description for new transport (create action)
packageName Package name for new transport (create action)
connectionId System id
Actions in detail:
list → Open workbench transports for one user
details → Owner, status, description, total object count,
all direct objects + per-task objects
create → Creates transport, returns new transport number
release → Releases transport, returns per-check status and messages
delete → Permanently removes transport (cannot be undone)
change_owner → Reassigns ownership; confirms new owner in responseget_transport_for_object
Find out which transport request is needed to modify a specific ABAP object.
Parameters:
objectUrl ADT URL of the object
packageName Package name of the object (optional)
connectionId System id
→ Returns: PGMID, object type/name, dev class, operation type, transport texttransport_move
Move object entries from one request or task to another, or reassign a whole task to another request — with the checks a careful Basis colleague makes first. Dry run by default.
What moves: name entries as
"R3TR CLAS ZCL_X","CLAS ZCL_X","ZCL_X"or a wildcard"ZCL_FOO*". An object's parts (methods, class sections, includes, a function group's modules) move with it unlessincludeParts: false. With a task asfromand no objects, the whole task moves (TR_REASSIGN_TASK). Entries go into your own task in the target request when you have one there.Stops before SAP is called when a request is released, the kinds differ (Workbench vs Customizing), the clients differ, or a named object is not on the source. Warns when the target goes to another system, or when an object would be split over two requests. Entries the target already holds are left out.
Dependencies: from the where-used index (
WBCROSSGT,CROSS), what the moved objects use and what uses them, among objects on other open requests — and the release order that follows ("release ZTAB's request before or together with the target"; "release the target before the source, ZREPORT stays there and uses ZCL_X"). It follows ABAP code references, not DDIC structure, and only as far as the where-used index is current — on some systems a fresh activation is indexed later.SAP checks it too: the dry run calls SAP's own test move (
TR_MOVE_OBJECTStest mode) through the in-system engine (v0.9.35+, deployed automatically).commit: truemoves, then verifies inE071that every entry is on the target and gone from the source.
transport_move from: "DEVK900123" to: "DEVK900456" objects: ["ZCL_ORDER_API"]
→ plan, stops/warnings, dependencies, release order, SAP test result
transport_move … commit: true
→ moved, verified in E071Data
search_database_tables
Search for SAP database tables and views by description keyword — no need to know the technical name. Works for all installed tables: standard SAP, industry solutions (IS-Retail WLK*, IS-Automotive, IS-Mill, IS-Oil & Gas, etc.), and custom Z/Y tables. Searches the live system so it finds whatever is actually installed.
Parameters:
keyword Description to search for (e.g. "article listing", "vehicle order",
"warehouse transfer", "purchase order header")
maxResults Maximum results per object type (default 50)
connectionId System id
→ Returns: table/view name, type (TABLE or VIEW), package, description
with a hint to use describe_database_table and read_table_contents nextExample prompts:
"Find tables related to article listing" → discovers
WLK1,WLK2,WLKA, etc."What tables store transport requests?" → discovers
E070,E071,E07T"Show me warehouse transfer order tables" → discovers
LTAP,LTBK, etc.
describe_database_table
Show the complete field list for a SAP table or view: field name, type, length, description, and which fields form the key. Fetches metadata only (0 rows) so it's instant.
Parameters:
tableName Table or view name (e.g. MARA, WLK1, EKKO, Z_MY_TABLE)
connectionId System id
→ Returns: all fields with type, length, description
key fields marked with 🔑
total field count and key field count
example read_table_contents query at the bottomexecute_data_query
Run a SELECT SQL query against SAP ABAP dictionary tables or CDS views. Read-only — INSERT, UPDATE, DELETE, DROP, ALTER, and other DML/DDL statements are blocked.
Parameters:
sql SELECT statement (e.g. "SELECT * FROM MARA WHERE MATNR LIKE 'Z%'")
maxRows Row limit (default 100)
connectionId System id
→ Returns: column headers + rows as a formatted tableread_table_contents
Read the contents of an ABAP dictionary table or view by name, with an optional WHERE clause.
Parameters:
tableName Table or view name (e.g. "MARA", "EKKO", "WLK1")
maxRows Row limit (default 100)
whereClause Optional WHERE clause without the WHERE keyword (e.g. "MATNR LIKE 'Z%'")
connectionId System id
→ Returns: column headers + rows as a formatted tableNote — WHERE clause requires ADT Data Preview service
The
whereClauseparameter uses the/sap/bc/adt/datapreviewADT service (SAP_BASIS ≥ 7.40 SP08). If your system returns an error when using a WHERE clause, the service is either not installed or not active.To activate: transaction
SICF→ navigate todefault_host / sap / bc / adt / datapreview→ right-click → Activate Service.If the node does not exist (older SAP_BASIS), use
execute_data_querywith a full ABAP Open SQL statement instead:execute_data_query sql: "SELECT * FROM MARA WHERE MATNR LIKE 'Z%' UP TO 10 ROWS"
execute_data_queryuses a different ADT endpoint and works on all supported SAP versions.
Typical table query workflow:
1. search_database_tables — find the right table by description
2. describe_database_table — see field names, types, and key fields
3. read_table_contents — read rows (no filter needed for small tables)
— or —
execute_data_query — "SELECT * FROM <table> WHERE <condition> UP TO 10 ROWS"
use this when WHERE clause filtering is neededAnalysis
where_used
Find all places across the SAP system where an object or symbol is referenced — cross-reference analysis. Returns source locations with file, line number, and a code snippet.
Parameters:
objectUrl ADT URL of the object to look up
connection System id
→ Returns: list of usage locations, each with: object name, type, source URL,
line number, and surrounding code snippetversion_history
List all previous versions of an ABAP object with timestamps and the developer who made each change. Useful for auditing changes or understanding how a program evolved.
Parameters:
objectUrl ADT URL of the object
connection System id
→ Returns: list of versions with: version id, date/time, author, version titleanalyze_dump
List ABAP runtime short dumps (ST22) from the SAP system. Shows recent errors with their type, program, and timestamp — useful for diagnosing production issues without opening the SAP GUI.
Parameters:
connection System id
maxResults Maximum number of dumps (optional)
→ Returns: dumps with: id, type, title, timestamp, author (if available)check_inactive_objects
List all currently inactive (not yet activated) ABAP objects in the system. Useful before a batch activation to see exactly what is pending.
Parameters:
connection System id
→ Returns: list of inactive objects with: name, type, ADT URL, owner,
and whether they are pending deletionText elements
What are text elements? Every ABAP program has three pools of translatable texts stored separately from the source code: selection texts (labels shown next to
PARAMETERSandSELECT-OPTIONSon the selection screen), text symbols (referenced asTEXT-001etc. in source), and list headings (short/medium/long titles shown in report output). These must be maintained separately from the source and are often forgotten when creating new programs.
get_text_elements
Read text elements for any ABAP program, class, or function group. Retrieves all three categories at once if no category is specified.
Parameters:
objectType ADT object type: "PROG/P", "CLAS/OC", "FUGR/F"
objectName Object name (e.g. "ZCAR_MM_ORDERS")
category "selections" | "symbols" | "headings" (optional — omit for all three)
connectionId System id
→ Returns: for each element: id, label text, max length (if set), DDIC reference (if set)
Categories:
selections Labels for PARAMETERS and SELECT-OPTIONS on the selection screen
symbols Text symbols referenced in source as TEXT-001, TEXT-002, etc.
headings Report headings: S (short), M (medium), L (long), H (column header)set_text_elements
Write text elements for an ABAP object. Locks the object automatically and leaves it locked — call abap_activate afterwards to activate and unlock. Only the elements listed are updated; existing elements not in the list are left untouched.
Parameters:
objectType ADT object type: "PROG/P", "CLAS/OC", "FUGR/F"
objectName Object name (e.g. "ZCAR_MM_ORDERS")
objectUrl ADT object URL — used for locking (from get_abap_object_info)
category "selections" | "symbols" | "headings"
elements Array of { id, text } pairs:
selections → id is the parameter name: "P_PLANT", "S_WERKS"
symbols → id is the 3-digit number: "001", "002"
headings → id is "S" (short), "M" (medium), "L" (long), "H" (header)
transport Transport number (required for transportable packages)
connectionId System id
→ Returns: confirmation with element list and lock handle; activate to finaliseTypical text element workflow:
1. get_abap_object_info — get the object URL
2. get_text_elements — read existing texts to see current ids and gaps
3. set_text_elements — write the new/updated texts (auto-locks)
4. abap_activate — activate and unlockPackages
browse_package
List all development objects inside an ABAP package, grouped by object type. Sub-packages are marked with ▶ and can be expanded automatically.
Parameters:
packageName Package (development class) to browse, e.g. "ZCAR_MM" or "$TMP"
includeSubPackages Also fetch and display the contents of any sub-packages (default: false)
connectionId System id
→ Returns: objects grouped by type (PROG/P, CLAS/OC, FUGR/F, DEVC/K, etc.)
each with name, description, and ▶ marker if it contains childrencreate_package
Create a new ABAP development package (development class). Packages are the containers that group related objects and control transport behaviour.
Parameters:
name Package name (e.g. ZCAR_MM_REPORTS)
description Short description
parentPackage Parent package this lives under (e.g. ZCAR_MM)
packageType "development" (default — contains objects)
"structure" (grouping only, no objects directly)
"main" (top-level package)
swComponent Software component (e.g. HOME, LOCAL). Leave empty for local packages.
transportLayer Transport layer (e.g. Z, SAP) — controls the transport route
transport Transport number — required when the parent package is transportable
connectionId System id
→ Returns: confirmation with all settings; hints to use create_abap_object nextDebugging
How ABAP debugging works with AI: Set a breakpoint, then trigger execution in SAP (run the report, call the BAPI, send the HTTP request). The AI calls
abap_debug_sessionwith actionlisten, which waits for the breakpoint to be hit, then gives you full control for stepping and inspection — all from the chat window.
abap_debug_session
Attach to an ABAP debugging session and wait for a breakpoint to be hit, check session status, or detach.
Parameters:
action "listen" — block until a breakpoint is hit
"status" — check current debugger state
"detach" — end the debugging session
connection System id
terminalId Debug terminal id (generated if not provided)
ideId IDE session id (generated if not provided)
→ listen: returns call stack and variable snapshot when breakpoint is hit
→ status: returns current debugger stateabap_debug_set_breakpoint
Set a breakpoint at a specific line in an ABAP source file.
Parameters:
sourceUrl ADT URL of the source file
line Line number to break at
connection System id
terminalId Debug terminal id (must match the listen session)
ideId IDE session id
→ Returns: breakpoint id — store this to delete the breakpoint laterabap_debug_delete_breakpoint
Delete a breakpoint by its id (returned from abap_debug_set_breakpoint).
Parameters:
breakpointId Breakpoint id from abap_debug_set_breakpoint
connection System id
terminalId Debug terminal id
ideId IDE session idabap_debug_step
Step through ABAP code in an active debug session.
Parameters:
stepType "stepInto" — step into the next called method/function
"stepOver" — execute current line, skip into calls
"stepReturn" — run until current method returns to caller
"stepContinue" — continue to next breakpoint
"terminateDebuggee" — stop the debugged program entirely
connection System id
terminalId Debug terminal id
ideId IDE session id
→ Returns: updated call stack and current source position after the stepabap_debug_variable
Inspect the current values of variables in an active debug session. Can drill into complex types (structures, internal tables, object instances).
Parameters:
parents Array of variable names or paths to inspect
connection System id
terminalId Debug terminal id
ideId IDE session id
→ Returns: for each variable: name, declared type, actual type, current valueabap_debug_stack
Get the current call stack in an active debug session — all active stack frames with program name, include, and line number.
Parameters:
connection System id
terminalId Debug terminal id
ideId IDE session id
→ Returns: ordered call stack frames with program, include, line, event typeabap_debug_set_variable
Change the value of a variable in an active debug session. Useful for injecting test values or bypassing a condition without modifying source.
Parameters:
variableName Name of the variable to change
value New value (as string)
connection System id
terminalId Debug terminal id
ideId IDE session idCustomizing & IMG (in-system engine)
These tools drive an in-system customizing engine (class
ZCL_MCP_CUST_ENGINEbehind a SICF node) so config changes run through the SM30/SM34 maintenance-view runtime — foreign-key checks, change documents and transport recording all happen exactly as a manual SPRO change would. Deploy/refresh it withcustomizing_engine_bootstrap. Full design, delivery-class rules and the governed-transport flow: docs/customizing-engine.md.Governed transport selection: a recorded write uses an explicit
transportas-is; with none given it returns an interactive prompt listing your open requests (passshowAllTransports: truefor everyone's) plus the create options, rather than silently minting one. A supplied request you have no task in gets a task created for you automatically.
img_search
Search the IMG / SPRO activity tree by keyword. Returns matching activities with their breadcrumb path and the customizing object/table behind each — the way in when you don't know the technical name.
Parameters:
keyword Keyword(s) to match in IMG activity titles
namespace / inScopeOnly / language / maxRows Optional scoping
connectionId System id
→ Returns: activities with breadcrumb path + linked customizing object/tablecustomizing_read
Read the rows of a customizing table/view for a given key (e.g. all config for POS profile 0001), resolving the maintenance view the standard way.
Parameters:
table, keyField, key Customizing object + key to read
connectionId System id
→ Returns: the matching customizing rowscustomizing_describe
Describe a customizing object before changing it: its maintenance object, delivery class, key fields, text table and transport object — so you know how a change will be recorded.
Parameters:
table Customizing table/view name
connectionId System id
→ Returns: maintenance object, delivery class, keys, text table, transport objectcustomizing_diff
Compare the customizing rows for two keys and report what differs — the basis for a "copy the gaps" plan.
Parameters:
table, keyField, sourceKey, targetKey What to compare
connectionId System id
→ Returns: rows present in source but missing/different in targetcustomizing_plan_change
Dry-run a customizing copy/delete: returns the exact rows that would be written or removed, with nothing applied.
Parameters:
table, keyField, sourceKey, targetKey Planned change
connectionId System id
→ Returns: planned rows (no changes made)customizing_apply
Copy or delete customizing rows through the SM30 maintenance-view runtime (VIEW_MAINTENANCE_SINGLE_ENTRY). Dry-run by default; commit: true writes. Class C/G/E config records onto a Customizing request (governed transport selection); class-A application tables write direct with no transport.
Parameters:
table, keyField, sourceKey, targetKey Rows to copy (or targetKey to delete)
action "copy" (default) | "delete"
values Per-row field overrides after the key swap
commit true to write (default false = dry run)
transport / createTransport / showAllTransports / recordTransport
connectionId System id
→ Returns: rows planned/written, the transport recorded onto, messagesorg_copy
Headless copy of an organizational unit and its EC01-style dependent customizing, through SAP's standard entity copier (the "org units in the dark" engine behind EC01/EC02/EC04…) — e.g. clone a plant or sales org with its dependent tables. The commit runs in a background job; the copier records onto a Customizing request it creates itself.
Parameters:
orgUnit, sourceKey, targetKey Org-key domain and units (e.g. WERKS 1000 → 1010)
action "copy" (default) | "delete"
commit, createTransport commit: true copies; createTransport: true acknowledges the copier's own request
retestScope true (default for a copy) | "customizing" | "application" | false (only the copier's set)
connectionId System id
→ Returns: the copier's dependent tables, the retest scope (below), transport, run id / resultRetest scope. Copying the org unit is rarely what breaks; the entries keyed on it that sit outside the copier's set are. The new plant looks complete in SPRO, then the first goods movement or pricing run fails. So the dry run lists, with live row counts for the source and the target:
Copied — the copier's dependent tables (from the copier itself).
Not copied — check these — every customizing table (delivery class C, G or E; client-dependent, transparent) with a key field on the org-key domain (DD03L
DOMNAME=WERKS,BUKRS,VKORG…) that the copier leaves alone. Those where the source has rows and the target has none come first: that is the retest list. Then tables where both have rows, and a count of tables with nothing for the source.Not customizing — application and master-data tables (class A) keyed on the domain where the source has rows, summarised (count and the largest few): created by business processes (material extension, postings…), not copied by design.
The counts run as UNION ALL statements of 40 tables, six at a time, customizing first, then the copier's own tables, then application data; a statement that fails is split until the table that breaks it is found and reported as unreadable. The probe stops starting new counts 30 s into the call (below the server's 45 s request watchdog) and says how many tables it did not count. Measured on a demo S/4HANA system:
Org unit | Keyed tables | Copied by the copier | Not copied (C/G/E) | Time |
Plant ( | ~1,800 | 385 | 363 | ~26 s, all counted |
Sales org ( | ~620 | 120 | 144 | ~7 s, all counted |
Company code ( | ~6,600 | 1,587 | 1,573 | 30 s: customizing complete, application data partly |
A company code has too many keyed tables for one call. The first run always completes the customizing part (the retest list); re-run with retestScope: "application" to count the application tables on their own.
Diff after the copy. Run the same dry run again — same source and target — once the copy is committed: the copier does not object to an existing target on a dry run, so the three sections then compare the source with the new unit table by table (copier tables still empty on the target, non-copied tables to fill by hand). A commit also appends the scope when the copy finished with enough time left; otherwise it says to run the dry run. For row-level differences in one table, use customizing_diff.
customizing_create
Create new customizing rows from explicit field/value maps, with full (also composite) keys and distinct values per row, when there is no source row to copy. For example storage locations in T001L (key WERKS + LGORT), each with its own description. Rows go through the SM30 view runtime, so foreign-key checks, events, change documents and transport recording run the standard way. Dry run by default.
Parameters:
table Table name; its generated maintenance view is found automatically
rows [{ FIELD: value }, …], each including all key fields
commit true to write (default: dry run returning the planned rows)
onlyMissing Skip rows whose key exists (default true: idempotent)
transport | createTransport | workItem Transport selection, see Transport selection
recordTransport false for a direct, untransported sandbox write
connectionId System id
→ Returns: the planned or written rows, and the transport they were recorded onretail_listing
List articles into retail assortments through SAP's standard listing engine (EXECUTE_LISTING_ART_ASSORT_RFC). Listing extends each article to the assortment's sites (plant and valuation segments) and writes the WLK1 listing conditions; a general assortment propagates to all its assigned sites. Dry run by default.
Parameters:
items [{ product, assortment, dateFrom?, dateTo? }, …]; dates default to today / 9999-12-31
commit true to execute (default: dry run returning the prepared items)
connectionId System id
→ Returns: the prepared or executed listing items with SAP's messagescustomizing_status
Poll a background write job started by customizing_apply / org_copy and return its result (rows written, E071K entries, messages) when it finishes.
Parameters:
runId The run id returned by a pending write
connectionId System id
→ Returns: job status, rows written, transport entries, messagescustomizing_selftest
Run the engine's built-in self-test (read / plan / transport-resolution paths) against the connected system without changing config.
Parameters:
connectionId System id
→ Returns: per-check pass/fail with detailscustomizing_engine_bootstrap
Deploy or update the in-system engine class + SICF node from the version-controlled ABAP source. Run after pulling a new engine version.
Parameters:
connectionId System id
→ Returns: actions taken (class created/updated, node registered), engine versioncustomizing_engine_cleanup
Remove the engine's in-system artifacts (class, SICF node) — the uninstall counterpart of bootstrap.
Parameters:
connectionId System id
→ Returns: artifacts removedcustomizing_engine_ping
Health-check the deployed engine: returns its version and the connected client's customizing settings (SCC4 category, recording mode).
Parameters:
connectionId System id
→ Returns: engine version, client customizing category + recording statusOperations
force_relogin
Drop and re-establish the ADT session for a connection. Heals a degraded session (every ADT call returns HTTP 400/401/CSRF) without restarting the server. The old session's locks are unlocked and its SAP context is ended before the new login. Every other re-login (idle refresh, failed keep-alive) and a server shutdown do the same, so no lock is left behind in an orphaned context.
Parameters:
connectionId System id to re-login
→ Returns: confirmation the session was re-establishedhana_memory_report
Report HANA memory usage (used / peak / allocation limit, top consumers) via the engine's ADBC access to the SYS.M_* views — a quick "is the box healthy" check.
Parameters:
connectionId System id
→ Returns: used/peak/limit GB and top memory consumersabap_memory_report
Report the ABAP application server's memory and box-health profile parameters (PHYS_MEMSIZE, extended memory, heap and roll areas, buffer size, work-process counts). The companion to hana_memory_report: on a single-host appliance, read both and check that the HANA allocation limit plus ABAP working memory plus the OS fit in physical RAM.
Parameters:
connectionId System id
→ Returns: the profile parameters with their current valuesengine_deploy
Install the server's in-system ABAP and choose per unit whether it is transportable (a Workbench Z package on a transport, released to production through CTS like any development) or local in $TMP (development only). Run without a unit to see the setup prompt or the current status; re-run to switch a unit. See In-system ABAP: transported, not pushed.
Parameters:
unit diag (tier 0 read-only diagnostics) | cust (tier 1 customizing engine); omit for status
transportable true = Z package on a transport; false = $TMP
package Workbench Z package (for a transportable deploy)
transport Workbench request to record the objects on
connectionId System id
→ Returns: what was deployed where, and the SICF node to register after a transportable deployTypical workflows
Write / activate
1. search_abap_objects — find the object by name or pattern
2. get_abap_object_info — confirm its structure, get the ADT URL
3. get_abap_object_lines — read the current source
4. get_transport_for_object — find or create the right transport
5. write_abap_object_source — write the new source (auto-locks the object)
6. run_atc_analysis — check for quality issues before activating
7. abap_activate — compile and activate
8. run_unit_tests — verify correctnessQuery a table when you don't know the technical name
1. search_database_tables — find tables by description ("article listing",
"vehicle order", "warehouse stock", etc.)
Works for IS-Retail, IS-Automotive, custom Z/Y — anything installed
2. describe_database_table — see all fields, types, key fields for the found table
3. read_table_contents — read rows (good for small tables or full scans)
— or —
execute_data_query — "SELECT FIELD1, FIELD2 FROM <table>
WHERE <condition> UP TO 20 ROWS"
Use this when you need WHERE filtering and
/sap/bc/adt/datapreview is not active on your systemPre-upgrade transport audit
1. list_all_transports — see every open transport across all developers,
with all contained objects
2. manage_transport_requests — action: details <number>
get full breakdown of tasks and objects for any transport
3. manage_transport_requests — action: change_owner (if reassignment needed)
4. manage_transport_requests — action: release (export transport before upgrade)
— or —
manage_transport_requests — action: delete (remove unwanted transports)Debug a running program
1. search_abap_objects — find the program to debug
2. get_abap_object_lines — read source to identify the right line
3. abap_debug_set_breakpoint — set breakpoint at target line
4. abap_debug_session listen — wait for the breakpoint to be hit
(trigger execution in SAP: run the report, call the BAPI, etc.)
5. abap_debug_stack — see where you are in the call chain
6. abap_debug_variable — inspect variables of interest
7. abap_debug_step — step through the code as needed
8. abap_debug_delete_breakpoint — clean up when doneATC quality pass
1. search_abap_objects — find the object or package
2. run_atc_analysis — get all findings with priority and location
3. get_abap_object_lines — read source for each finding
4. write_abap_object_source — fix the issues
5. abap_activate — activate the fixes
6. run_atc_analysis — confirm findings are resolvedEnvironment variables
Variable | Description |
| Path to config JSON file (overrides default search paths) |
SAP system requirements
SAP NetWeaver 7.40+ or SAP S/4HANA
ADT (ABAP Development Tools) REST service enabled — activate in SICF:
/sap/bc/adtUser with development authorizations:
S_DEVELOP,S_TCODEforSE80For debugging:
S_ADMI_FCDwithPADMactivity or equivalent debug authorization
Troubleshooting
read_table_contents WHERE clause returns an error
The WHERE clause filter uses the ADT Data Preview service which is a separate SICF node from the core ADT service.
Symptom: read_table_contents with a whereClause returns HTTP 400 or
"No suitable resource found".
Fix — activate the service in SICF:
Open transaction
SICFNavigate to
default_host / sap / bc / adt / datapreviewRight-click → Activate Service
If the node does not exist: your SAP_BASIS release does not include the
Data Preview endpoint (requires ≥ 7.40 SP08). Use execute_data_query with
full ABAP Open SQL instead:
SELECT * FROM MARA WHERE MATNR LIKE 'Z%' UP TO 10 ROWSexecute_data_query uses the ADT SQL console endpoint which is available on
all supported SAP versions.
Server becomes unresponsive / SAP session times out
The server proactively re-establishes the SAP session after 8 minutes of
inactivity (before SAP ICM's default 10-minute timeout). If you see
authentication errors after a long pause, the session was not refreshed in
time. Set ABAP_SESSION_IDLE_MS to a lower value (e.g. 360000 for 6 min)
to refresh earlier.
"User is currently editing" on activate
This was a known bug — fixed in the current version. The lock acquired by
write_abap_object_source is now automatically released before activation.
Upgrade to the latest version and the conflict will no longer occur.
Lock handle errors after server restart
ADT lock handles are tied to the HTTP session. Restarting the MCP server
creates a new session, making all previously held handles invalid. Simply
call abap_activate or write_abap_object_source again — the server will
re-acquire a fresh lock automatically.
Extending the server — ADT locking rules (read before adding write tools)
When adding any new ADT write operation, the single most important rule is:
Lock the exact ADT resource you PUT/POST to — not its parent object.
ADT enqueues are per resource, and some objects expose sub-resources that
have their own enqueue, separate from the object's source lock. Locking the
parent object does not cover them. SAP then rejects the write with
Resource <X> is not locked even though you hold a valid handle — and a naive
"stale handle" retry that re-locks the parent will collide with your own
still-held lock and surface as User <U> is currently editing with an empty
SM12 (the conflict is self-inflicted, not a real orphan).
This bit us with text elements: the program source lives in REPS/TRDIR,
but text elements (selection texts, text symbols, headings) live in the REPT
text pool — a different enqueue. The fix was to lock the text-elements URL
(/sap/bc/adt/textelements/programs/<name>), the same resource
setTextElements writes to, rather than the program object URL.
Checklist for a new write tool:
Identify the exact URL the write targets (
PUT/POSTpath).Acquire the lock on that resource's lockable object, then thread the real
LOCK_HANDLEfrom the LOCK response into the write — never reuse a constant or cached handle across objects/sessions.Make the lock lifecycle self-contained: acquire → write → always release on every exit path (success and error), in a
try/finally-style guard. For changes that go to the inactive buffer (source, text elements), you do not need to keep the lock held for activation.On a genuine stale/invalid-handle error, unlock the old handle before re-acquiring — otherwise the re-lock conflicts with your own lock.
Never deny a write from cached lock state. If unsure, attempt the real ADT LOCK and let SAP arbitrate.
Pass
objectType + objectNameand derive resource URLs from them where the library expects a name (e.g.createTestIncludetakes a class name, not a URL — passing a URL gets it encoded into the path and 404s).
Known enqueue boundaries to watch for (each may need its own lock):
Object source (parent) | Separate sub-resource / enqueue |
Program source ( | Text elements / text pool ( |
Program source | Program documentation / long texts |
DDIC object | Documentation, sub-components |
Any object | GUI status/title pools, dynpros |
Source writes (/source/main) and class test includes share the parent
object's enqueue, so locking the object URL is correct for those.
License
MIT
Available Tools
60 toolsabap_activateActivate ABAP ObjectAIdempotent
Activate an ABAP object — compiles it and makes it executable (equivalent to F8/Activate in the ABAP workbench). If the object is currently locked by a prior write_abap_object_source or set_text_elements call, the lock is released automatically before activation. The object must have been written/saved first.
Inputs: url (required), preaudit, connectionId.
abap_activate_multipleActivate Multiple ABAP ObjectsAIdempotent
Activate several inactive ABAP objects in one call. Only URLs that match the inactive worklist (check_inactive_objects) are activated; others are skipped, and if none match nothing happens. Releases any lock this server holds on them first. Reports success or failure with messages, and warns when SAP says success but an object is still inactive. Use abap_activate for a single object.
Inputs: urls (required), preaudit, connectionId.
abap_debug_delete_breakpointDelete ABAP BreakpointAIdempotent
Remove one external breakpoint by the id abap_debug_set_breakpoint returned. Use it when a breakpoint should no longer stop the program; it does not end a running debug session (use abap_debug_session with detach, or abap_debug_step with terminateDebuggee, for that). Removes the breakpoint registration in SAP only; source is untouched.
Inputs: breakpointId (required), username, connectionId.
abap_debug_sessionABAP Debug SessionA
Start, check or end the external ABAP debugger listener for a user. 'listen' registers a listener and then blocks until a program of that user hits a breakpoint (set one first with abap_debug_set_breakpoint) or the listener times out, and returns the debuggee details; then use abap_debug_step, abap_debug_variable and abap_debug_stack. 'status' shows the listener this server holds for the connection; 'detach' removes it. Reports a conflict if another debugger is already attached for that user. Does not change any object.
Inputs: action (required), username, connectionId.
abap_debug_set_breakpointSet ABAP BreakpointA
Set an external breakpoint on one source line for a user. Returns the breakpoint id (or an error entry), which abap_debug_delete_breakpoint takes. Set breakpoints before abap_debug_session with action listen, then run the program as that user to trigger it. Registers the breakpoint in SAP for that user; it does not change the object's source.
Inputs: line (required), sourceUrl (required), username, connectionId.
abap_debug_set_variableABAP Debug Set VariableADestructiveIdempotent
Overwrite a variable in the stopped debuggee and return the value SAP reports back. Affects only the running program's memory for this debug session, not the database or source, but whatever the program does afterwards uses the new value. Needs an active session stopped at a breakpoint; check the current value first with abap_debug_variable.
Inputs: value (required), variableName (required), connectionId.
abap_debug_stackABAP Debug StackARead-onlyIdempotent
Return the call stack of the stopped debuggee as JSON (the stack entries ADT reports). Use it after a breakpoint hit to see where you are before stepping or reading variables with abap_debug_variable. Read-only; needs an active session stopped at a breakpoint (abap_debug_session with action listen).
Inputs: connectionId.
abap_debug_stepABAP Debug StepADestructive
Execute one debugger step in the active session and return the new position as JSON. stepInto, stepOver and stepReturn move one step; stepContinue runs on to the next breakpoint or to the end; terminateDebuggee ends the debugged program. Needs a debuggee stopped at a breakpoint, which abap_debug_session with action listen delivers. Changes the running program's state, so it cannot be undone.
Inputs: stepType (required), connectionId.
abap_debug_variableABAP Debug VariableARead-onlyIdempotent
Read the current values of named variables in the stopped debuggee, one line each as NAME (type) = value. Use abap_debug_stack first to see which frame you are in, and abap_debug_set_variable to change a value. Read-only; needs an active session stopped at a breakpoint (abap_debug_session with action listen).
Inputs: variables (required), connectionId.
abap_memory_reportABAP App-Server Memory ReportARead-onlyIdempotent
Report the ABAP application server's key kernel memory / box-health profile parameters (PHYS_MEMSIZE, em/initial_size_MB extended memory, abap/heap_area_*, ztta/roll_*, abap/buffersize, work-process counts) via C_SAPGPARAM — basis-only, so it works on any ABAP box. Companion to hana_memory_report: on a single-host AS+DB appliance, read BOTH and check that HANA global_allocation_limit + ABAP working memory + OS ≤ physical RAM (over-commit shows up as swap). Needs engine v0.9.16+.
Inputs: icfPath, connectionId.
abap_run_classRun ABAP Class (Console)ADestructive
Run a class that implements IF_OO_ADT_CLASSRUN (ADT's 'Run as ABAP Application (Console)') and return what its MAIN method wrote to OUT. Runs the class's current active version, in a session of its own. The class decides what happens — it can post documents and commit — so read it before running it. Never retried automatically after a lost session. A run that takes longer than waitSeconds keeps running in SAP and answers with a runId: call again with runId to wait for it and get the output.
Inputs: runId, className, waitSeconds, connectionId.
adt_discoveryADT DiscoveryARead-onlyIdempotent
List the ADT services the SAP system exposes, each with its collection titles or hrefs, from the ADT discovery document. Use it to check whether a feature (for example a data preview or ATC endpoint) exists on this release before relying on a tool that needs it, or when a tool fails with 404. Read-only; takes no object input and returns a plain list, which can be long.
Inputs: connectionId.
analyze_dumpAnalyze Runtime DumpARead-onlyIdempotent
List ABAP runtime dumps (the short dumps shown in ST22) from the SAP system: category, short text, dump id and user. Lists them only; it does not return the dump text or stack. Use it to find a failing run after abap_run_class or run_unit_tests, then open the dump in ST22. Read-only; the optional query narrows the feed.
Inputs: query, connectionId.
browse_packageBrowse PackageARead-onlyIdempotent
List the objects directly inside an ABAP package, grouped by object type, with name and description (at most maxObjects per package). Sub-packages appear as entries of type DEVC/K; includeSubPackages also lists the contents of those one level down. Use it to explore a package; use search_abap_objects to find an object by name across packages. Read-only.
Inputs: packageName (required), maxObjects, connectionId, includeSubPackages.
check_inactive_objectsCheck Inactive ObjectsARead-onlyIdempotent
List ABAP objects with an inactive version (saved but not activated): type, name and ADT URL, for the connected user's inactive worklist. Use it after write_abap_object_source to find what still needs activating, then pass the URLs to abap_activate or abap_activate_multiple. Read-only; takes no object input.
Inputs: connectionId.
connected_systemsConnected SystemsARead-onlyIdempotent
List the SAP systems this server is configured for: connection id, URL, user and client, with the default connection marked. Call this first when more than one system is configured, and pass the id as connectionId to any other tool. SOAP RFC connections are flagged as read-only (tables and sources only, no ADT). Only reads local configuration; does not contact SAP or log in.
Inputs: no named parameters.
create_abap_objectCreate ABAP ObjectA
Create a new ABAP development object (program, class, function group, include, table, etc.) in a package. A local package ($TMP) needs no transport. Creating a function group include (FUGR/I) also adds its INCLUDE line to the group's main program — do not add it again. Transport: a transport you pass is checked and used; an object already locked into a request uses it; createTransport: true creates a new one; otherwise the transport already used for this piece of work (workItem, else this session) is reused; otherwise exactly one open request is used, several are listed for you to choose, none offers creation.
Inputs: name (required), objectType (required), description (required), packageName (required), +5 more.
create_packageCreate PackageA
Create a new ABAP package (DEVC) under an existing parent package and return its settings. Writes to the repository and is recorded on a Workbench request; the transport is chosen as described for write_abap_object_source, and a local package ($TMP) needs none. Add objects afterwards with create_abap_object. Needs development authorization (S_DEVELOP) and a software component and transport layer that exist in the system.
Inputs: name (required), description (required), parentPackage (required), workItem, +6 more.
create_test_includeCreate Test IncludeA
Create the local test include of an existing class so local test classes can be added to it; it adds no test code itself. Write the tests afterwards with write_abap_object_source, activate, then run them with run_unit_tests. The tool locks the class, creates the include and unlocks it again, and records the change on a Workbench request (not needed in a local package). Transport: a transport you pass is checked and used; an object already locked into a request uses it; createTransport: true creates a new one; otherwise the transport already used for this piece of work (workItem, else this session) is reused; otherwise exactly one open request is used, several are listed for you to choose, none offers creation.
Inputs: classUrl (required), workItem, transport, connectionId, +1 more.
customizing_applyApply Customizing ChangeADestructive
Copy or delete customizing rows through the in-system engine, which drives the SM30 maintenance-view runtime (VIEW_MAINTENANCE_SINGLE_ENTRY) so FK checks, events, change docs and transport recording all run the standard way. DRY RUN by default — returns the exact rows affected. Set commit: true to apply. Use it for one table or view; use org_copy to copy a whole org unit, customizing_create for new rows with no source row, and customizing_plan_change for a quick preview without the engine.
action: "copy" (default) — duplicate rows from sourceKey to targetKey. action: "delete" — remove the entry whose keyField = targetKey (sourceKey not needed), recording the deletion onto the transport like an SM30 delete.
Delivery class behaviour: C / G / E — customizing + control tables: transport-based write. Provide a transport when committing. A — application data (e.g. /POSDW/STORE): direct MODIFY + COMMIT, no transport recording (copy only). Do NOT pass a transport for class-A tables.
The engine enforces S_TABU_DIS authorization on the target table.
Inputs: table (required), keyField (required), targetKey (required), action, +13 more.
customizing_createCreate Customizing RowsADestructiveIdempotent
Create new customizing rows from explicit field/value maps — full keys (including COMPOSITE keys) and distinct per-row values, with NO source row to copy from. This is what customizing_apply (a single-key copy) can't express: e.g. storage locations T001L (key WERKS+LGORT) each with their own LGOBE description.
Each entry in rows is one row as a { FIELD: value } object and must include all key fields. Rows are written through the SM30 view runtime (VIEW_MAINTENANCE_SINGLE_ENTRY, INS→UPD fallback) so FK checks, events, change docs and transport recording run the standard way; INITIAL/blank non-key fields are allowed.
DRY RUN by default — returns the planned rows. Set commit: true to write. onlyMissing (default true) skips rows whose key already exists — idempotent create. recordTransport: false → direct write, no transport (sandbox/test data); omit transport then. Delivery class C/G/E records onto a Customizing request (provide transport or createTransport); class A writes directly. Enforces S_TABU_DIS on the table.
Inputs: rows (required), table (required), commit, icfPath, +10 more.
customizing_describeDescribe Customizing ObjectARead-onlyIdempotent
Describe a SAP customizing view or table: underlying base table, all fields with key markers, delivery class (transport behaviour), authorization group (S_TABU_DIS), maintenance program, and foreign-key references on key fields (org-unit candidates). Use after img_search to understand the structure before reading or changing config.
Inputs: objectName (required), language, connectionId.
customizing_diffCompare Customizing Between Two Org UnitsARead-onlyIdempotent
Compare customizing configuration for a table/view between two org-unit key values (e.g. plant 1000 vs plant 2000, or company code DE01 vs AT01). Shows rows present in source but missing from target, and vice versa. Use customizing_plan_change to generate the insert plan for the missing rows.
Inputs: keyField (required), sourceKey (required), targetKey (required), objectName (required), +1 more.
customizing_engine_bootstrapDeploy/Update Customizing EngineAIdempotent
Install or update the Customizing Engine in the SAP system: creates or overwrites the class ZCL_MCP_CUST_ENGINE and the batch writer report ZMCP_CUST_WRITE, writes their source and activates both. Run it once to install and again when the engine version changes; the first install then needs a one-time SICF service registration (handler ZCL_MCP_CUST_ENGINE), which the result spells out. Use it to update the engine in place (default package $TMP); for a first setup, or to make the engine transportable, use engine_deploy instead, which calls this and also registers SICF. The customizing tools with autoDeploy update the engine themselves, so this is rarely needed. Verify with customizing_engine_ping; remove with customizing_engine_cleanup. Writes repository objects and needs development authorization (S_DEVELOP).
Inputs: transport, packageName, connectionId.
customizing_engine_cleanupRemove Customizing EngineADestructiveIdempotent
Delete the Customizing Engine class ZCL_MCP_CUST_ENGINE from the SAP system. Without confirm: true it only shows what would happen and changes nothing. The report ZMCP_CUST_WRITE and the SICF service node are not removed; delete the node manually in SICF. Customizing tools that need the engine stop working until customizing_engine_bootstrap or engine_deploy installs it again. Use it only to remove the engine from a system; to update it, run customizing_engine_bootstrap. Deletion is irreversible and uses the same delete path as delete_abap_object.
Inputs: confirm, connectionId.
customizing_engine_pingPing Customizing EngineARead-onlyIdempotent
Call the Customizing Engine's SICF endpoint with a ping and report the deployed version against the version this server expects, plus the release, client and which engine features the system offers. Use it to confirm customizing_engine_bootstrap worked and the SICF service is active; a version mismatch means run bootstrap again. Read-only: changes nothing in SAP. On failure, retry with rawDump to see the raw HTTP response.
Inputs: icfPath, rawDump, connectionId.
customizing_plan_changePlan Customizing Change (Dry Run)ARead-onlyIdempotent
Preview, from ADT reads alone, the rows that copying customizing from one org-unit key to another would insert (e.g. plant 1000 to a new plant 2000), with the maintenance view, delivery class and transport object. Nothing is written and the in-system engine is not needed, so it works before the engine is installed. Use it for a quick look at one table; for the plan of the write you will actually run, use customizing_apply without commit (its dry run goes through the engine and the SM30 runtime), and org_copy for a whole org unit across all its tables.
Inputs: keyField (required), sourceKey (required), targetKey (required), objectName (required), +2 more.
customizing_readRead Customizing DataARead-onlyIdempotent
Read the current rows of a customizing table or maintenance view (a view is resolved to its base table) as an aligned text table, optionally filtered to rows where one key field equals one value. The filter applies only when both keyField and keyValue are given. Use customizing_diff to compare two org units, customizing_describe for the field list, and read_table_contents for non-customizing tables. Read-only; needs table read authorization (S_TABU_DIS); returns at most maxRows rows.
Inputs: objectName (required), maxRows, keyField, keyValue, +1 more.
customizing_selftestSelf-Test Customizing EngineAIdempotent
Non-destructively validate the engine's risky internals — dynamic table typing, sample read, flat TABKEY construction, and (if a transport is given) TR_OBJECTS_INSERT in SIMULATION mode — without writing data or recording any transport. Auto-deploys the engine class if needed. Run this after the first bootstrap to confirm the write path will work before any real commit.
Inputs: table, icfPath, transport, autoDeploy, +1 more.
customizing_statusPoll Async Customizing WriteARead-onlyIdempotent
Check the result of an asynchronous customizing write (customizing_apply with commit: true) by its run_id. The write runs in a background job for headless transport recording; if it doesn't finish within apply's poll budget, apply returns a run_id — use this tool to poll until it reports ok/error.
Inputs: runId (required), icfPath, connectionId.
delete_abap_objectDelete ABAP ObjectADestructive
Delete an ABAP repository object from the SAP system and record the deletion on a Workbench request (not needed for a local $ package). Irreversible. The tool locks the object, deletes it and unlocks it. Check where_used first, since other objects may still reference it. Transport: a transport you pass is checked and used; an object already locked into a request uses it; createTransport: true creates a new one; otherwise the transport already used for this piece of work (workItem, else this session) is reused; otherwise exactly one open request is used, several are listed for you to choose, none offers creation.
Inputs: url (required), workItem, transport, connectionId, +1 more.
describe_database_tableDescribe Database TableARead-onlyIdempotent
Show all field names, types, lengths, and descriptions for a SAP database table or view. Key fields are marked with 🔑. Use this after search_database_tables to understand the structure before querying, so you know which fields to filter or select.
Inputs: tableName (required), connectionId.
engine_deployDeploy In-System Engine (choose transportability)AIdempotent
Install/manage the MCP's in-system ABAP, choosing per unit whether it is TRANSPORTABLE (created in a Workbench Z package on a transport, to be released to Production via CTS independent of the MCP) or deployed to $TMP (local, Dev-only). Run with NO unit to see the first-run setup prompt (or current status). Run per unit to deploy/flip: engine_deploy unit:"diag" transportable:true package:"ZMCP_DIAG" transport:"DEVK9…" engine_deploy unit:"cust" transportable:false Re-run any time to flip a unit (e.g. promote a higher-tier unit to transportable). Units: diag (Tier 0 read-only diagnostics) · cust (Tier 1 customizing engine). After a transportable deploy, register the unit's SICF node once (reported in the output). Which engine tool: engine_deploy for the first setup of a system and to choose or change where a unit lives (transportable or $TMP); customizing_engine_bootstrap only to install or update the customizing engine in place (the customizing tools also do that themselves via autoDeploy); customizing_engine_cleanup to remove it; customizing_engine_ping to check what is installed.
Inputs: unit, package, transport, connectionId, +1 more.
execute_data_queryExecute Data QueryARead-onlyIdempotent
Run one freestyle SELECT (ABAP SQL, as in the ADT data preview) against dictionary tables or CDS views and return the rows as an aligned text table or JSON. Statements containing INSERT, UPDATE, DELETE, DROP, ALTER, CREATE, EXEC, EXECUTE, TRUNCATE or MERGE are rejected, and the query must start with SELECT. Use read_table_contents for a plain table read and describe_database_table to learn the field names. Needs data preview and table read authorization (S_TABU_DIS); returns at most maxRows rows.
Inputs: sql (required), format, maxRows, connectionId.
force_reloginForce Re-loginADestructiveIdempotent
Drop this conversation's SAP ADT session and log in again. Use when ADT calls keep failing with HTTP 400 (degraded stateful session) and the automatic retry has not recovered it. Releases this conversation's locks only — every conversation has its own SAP session.
Inputs: connectionId.
get_abap_batch_linesGet ABAP Batch SourceARead-onlyIdempotent
Return the source of several ABAP objects in one call, each under a header line with its URL. One failing URL is reported as an error entry and does not stop the others. Use get_abap_object_lines for a single object. Read-only; the combined text can be large, so batch only what you need.
Inputs: urls (required), connectionId.
get_abap_object_infoGet ABAP Object InfoARead-onlyIdempotent
Read an ABAP object's metadata from its ADT URL: name, type, description, responsible user, language, version and URL, plus its includes for objects that have them (e.g. classes). Does not return source code; use get_abap_object_lines for that and get_transport_for_object for transport and lock state. Read-only. Find the URL with search_abap_objects.
Inputs: url (required), connectionId.
get_abap_object_linesGet ABAP Object SourceARead-onlyIdempotent
Return the full ABAP source text of one object (program, class, function module, include, ...). Give the object URL and /source/main is appended automatically if you omit it. Use search_abap_object_lines to find a pattern instead of reading everything, get_abap_batch_lines for several objects at once, and get_abap_object_info for metadata only. Read-only; returns the stored version as one string, with no line numbers.
Inputs: url (required), connectionId.
get_text_elementsGet Text ElementsARead-onlyIdempotent
Read text elements for an ABAP program, function group or class: selection texts (PARAMETERS / SELECT-OPTIONS labels, flagged when they are dictionary references), text symbols (TEXT-001 etc., with their maximum length), and list headings (LISTHEADER, COLUMNHEADER_1..4). Omit category to retrieve all three.
Inputs: objectName (required), objectType (required), category, connectionId.
get_transport_for_objectGet Transport Info for ObjectARead-onlyIdempotent
Report how a change to an ABAP object is transported: its package, whether recording is needed (a local $ package needs none), the request it is already locked into, and the open requests the transport layer allows. Use it before write_abap_object_source or delete_abap_object to pick a transport; list_all_transports shows open requests generally. Read-only; takes no lock and creates no request.
Inputs: url (required), packageName, connectionId.
hana_memory_reportHANA Memory ReportARead-onlyIdempotent
Diagnose HANA memory pressure on the tenant this ABAP system runs on, via the Customizing Engine's SICF endpoint (ADBC over SYS.M_* monitoring views — read-only). Reports host RAM/swap, per-service usage, top heap allocators, the largest loaded column tables, row store, SQL plan cache, and the configured global_allocation_limit. Use when the system is slow/swapping or before resizing. Needs engine v0.9.8+ (run customizing_engine_bootstrap to update).
Inputs: icfPath, connectionId.
img_searchSearch IMG / SPRO ActivitiesARead-onlyIdempotent
Search the SAP Implementation Guide (IMG / SPRO) for customizing activities by keyword, using the IMG index (CUS_IMGACT activity titles) joined to the maintenance object. Returns activity IDs, SPRO titles, maintenance objects, and their pattern (V=view→VDAT, S=table→TABU, C=cluster→CDAT, T=transaction, D=doc). Pass namespace to scope (e.g. '/POSDW/' for POS DTA) — recommended, and required for free-text matching on this system since its SQL endpoint blocks LIKE scans.
Inputs: keyword (required), language, namespace, maxResults, +2 more.
list_all_transportsList All TransportsARead-onlyIdempotent
List transport requests of all users (or one owner), grouped by owner and newest first: number, kind, status, date, description, the piece of work it is remembered for, and optionally its objects including its tasks' objects. Reads the CTS tables E070, E07T and E071 with SQL through ADT, so it needs table read authorization (S_TABU_DIS). Use manage_transport_requests with list for one user's open requests and details for a single request. Read-only; at most 500 requests, objects for the newest 100.
Inputs: owner, status, withObjects, connectionId.
lock_abap_objectLock ABAP ObjectA
Take an exclusive edit lock (enqueue) on an ABAP object and return the lock handle; the lock belongs to this conversation's SAP session and blocks other users from changing the object. Rarely needed: write_abap_object_source locks and unlocks by itself, and abap_activate releases a held lock. Use it only to keep an object locked across several calls, and release it with unlock_abap_object. Fails if another user or session already holds the lock.
Inputs: url (required), connectionId.
manage_transport_requestsManage Transport RequestsADestructive
Work with one transport request at a time through action: list (a user's open requests), details (header, tasks and objects of a request), create, release, delete, change_owner. release exports the request and cannot be undone; delete fails while SAP still holds object locks on it; create and change_owner change the CTS. Use list_all_transports to see other users' requests, get_transport_for_object for the request an object needs, and transport_move to move objects between requests. Needs CTS authorization (S_TRANSPRT).
Inputs: action (required), newOwner, username, objectUrl, +4 more.
org_copyCopy Organizational Unit (EC01-style)ADestructive
Copy or delete a WHOLE organizational unit including all its dependent customizing, via SAP's standard entity copier (ECOP_ORG_UNITS_IN_THE_DARK) — the exact engine behind EC01/EC02/EC04…, run headlessly through the in-system Customizing Engine.
orgUnit values (org-key DOMAIN): BUKRS company code (EC01), WERKS plant (EC02), CACCD controlling area (EC03), VKORG sales org (EC04), VTWEG distribution channel (EC05), SPART division (EC06), VSTEL shipping point (EC07), LGNUM warehouse no. (EC09), EKORG purchasing org (EC13), LGORT storage location (EC14), MTART material type (EC15).
DRY RUN by default: lists the copier's dependent tables, then the RETEST SCOPE — every customizing table (delivery class C/G/E) keyed on the org domain that the copier does NOT copy, with live source/target row counts (source has rows, target none = retest first), and a summary of application/master-data tables (class A) keyed on it. Run the dry run again after the copy, same source and target, to diff the new unit against the source. commit: true performs the copy and commits.
TRANSPORT: the entity copier records onto a Customizing request it MINTS ITSELF and reports back — it cannot record into a request you supply (a passed transport is ignored). On commit set createTransport: true to acknowledge that; the copier names the request. The copier never overwrites: it fails if the target unit already exists. After the copy, patch unit-specific values (name, currency, country, …) with customizing_apply using values + onlyMissing: false. Needs engine v0.9.9+.
Inputs: orgUnit (required), sourceKey (required), action, commit, +6 more.
read_table_contentsRead Table ContentsARead-onlyIdempotent
Read the rows of one ABAP dictionary table or view by name, optionally filtered with a WHERE clause, up to maxRows. Use it for a quick look at one table; use execute_data_query for joins, selected columns, aggregates or ORDER BY, describe_database_table for the field list, and customizing_read for a customizing table filtered on one key field. Read-only. The optional whereClause filter requires the ADT Data Preview service (SICF node /sap/bc/adt/datapreview, SAP_BASIS ≥ 7.40 SP08) to be active. If whereClause returns an error, use execute_data_query with a full SELECT ... WHERE ... UP TO n ROWS statement instead — that uses a different ADT endpoint and works on all supported SAP versions.
Inputs: tableName (required), maxRows, whereClause, connectionId.
retail_listingList Articles into Assortments (Retail)AIdempotent
List one or more articles into retail assortments via SAP's standard listing engine (EXECUTE_LISTING_ART_ASSORT_RFC, determine_data=X). Listing EXTENDS each article to the assortment's assigned sites (creating the plant/valuation segments) and writes the WLK1 listing conditions, so the article becomes available at those sites. Listing into a GENERAL assortment propagates to all its assigned sites (incl. their local assortments).
DRY RUN by default — returns the prepared listing items. Set commit: true to execute. dateFrom/dateTo default to today / 9999-12-31. Material numbers are ALPHA-padded automatically.
Inputs: items (required), commit, icfPath, autoDeploy, +1 more.
run_atc_analysisRun ATC AnalysisARead-onlyIdempotent
Run an ABAP Test Cockpit (ATC) check on one object and return each finding as priority, check title and id, message, and object with line number, or a pass message when there are none. Use syntax_check for a quick compile check of unsaved source and run_unit_tests to execute tests. Starts an ATC run on the SAP system for the object; changes no object. Needs ATC authorization and a variant that exists in the system.
Inputs: url (required), variant, maxResults, connectionId.
run_unit_testsRun ABAP Unit TestsA
Run the ABAP Unit test classes of an object and report each test method as passed or failed with its alert details, plus a total. A class with no method results is reported as unknown, not passed. By default runs all risk levels and durations; narrow with riskLevels and durations. Executes the test code on the SAP system, which can change data if the tests do; use abap_run_class to run a console class instead and run_atc_analysis for static checks.
Inputs: url (required), durations, riskLevels, connectionId.
search_abap_object_linesSearch ABAP Object Source LinesARead-onlyIdempotent
Find lines in one object's source that match a case-insensitive regular expression and return them as numbered lines (line number, then text). Use it to locate a statement before editing or setting a breakpoint; use where_used for references across objects and get_abap_object_lines to read the whole source. Read-only; searches a single object per call and reads the full source first.
Inputs: url (required), pattern (required), connectionId.
search_abap_objectsSearch ABAP ObjectsARead-onlyIdempotent
Search ABAP repository objects by name or wildcard pattern and return type, name, description and ADT URL for each hit. Use it to find the URL that get_abap_object_lines, write_abap_object_source or where_used need; use browse_package to list a package instead, search_abap_object_lines to search inside source, and search_database_tables for tables by keyword. Read-only; at most maxResults hits (default 50).
Inputs: query (required), maxResults, objectType, connectionId.
search_database_tablesSearch Database TablesARead-onlyIdempotent
Find database tables and views by keyword when you do not know the technical name, via the ADT object search for types TABL and VIEW. Returns kind, name, package and description per hit, sorted by name, for standard, industry and Z/Y objects alike. Follow with describe_database_table for the fields, then read_table_contents or execute_data_query for the rows; use search_abap_objects for other object types. Read-only; maxResults applies to tables and views separately.
Inputs: keyword (required), maxResults, connectionId.
set_text_elementsSet Text ElementsADestructiveIdempotent
Write text elements for an ABAP program, function group or class, in the format SAP requires. • selections — selection-screen texts for PARAMETERS / SELECT-OPTIONS. Set dictionaryReference: true to take the text from the data element (the SE38 'Dictionary ref.' box); text may then be empty and SAP fills it. Own texts max 30 characters. • symbols — TEXT-xxx, ids of 3 characters. Every symbol is written with a maximum length (maxLength, default the text's length); SAP rejects symbols without one. • headings — LISTHEADER (max 71) and COLUMNHEADER_1..COLUMNHEADER_4 (max 255). Writes MERGE by default: listed ids are added or updated, everything else is kept (the underlying ADT write replaces the whole category, so the tool reads first). Use remove to delete ids, or replace: true to write exactly the list. Returns what SAP stored, then activates the text elements (activating the program alone does not; pass activate: false to leave them inactive).
Inputs: category (required), elements (required), objectName (required), objectType (required), +8 more.
syntax_checkABAP Syntax CheckARead-onlyIdempotent
Run the SAP syntax check on source text you supply, in the context of an existing object, without saving or locking anything. Returns each message as line:column, severity and text, or a no-errors confirmation. Use it before write_abap_object_source; use run_atc_analysis for quality checks on the saved object. Read-only; the object must already exist.
Inputs: url (required), source (required), mainUrl, connectionId.
transport_moveMove Transport Objects or TaskA
Move object entries from one transport request/task to another, or reassign a whole task to another request — with the checks a careful Basis colleague makes first. DRY RUN by default: shows the plan, runs SAP's own test move (TR_MOVE_OBJECTS test mode), and reports what the moved objects use and are used by on other open requests, with the release order that follows. commit: true moves and verifies in E071.
objects given → those entries move ("R3TR CLAS ZCL_X", "CLAS ZCL_X", "ZCL_X", "ZCL_FOO*"); an object's parts (methods, sections, includes, function modules) move with it unless includeParts: false. no objects, from = a task → the whole task moves to the target request (TR_REASSIGN_TASK).
Stops before SAP is called when a request is released, the kinds differ (Workbench/Customizing), or the clients differ; warns when the target goes to another system or an object would be split.
Inputs: to (required), from (required), commit, objects, +3 more.
unlock_abap_objectUnlock ABAP ObjectADestructiveIdempotent
Release the edit lock this server holds on an ABAP object, taken by lock_abap_object or kept by write_abap_object_source with keepLock. Saved (inactive) source is not removed. Use it to free an object you will not activate; abap_activate releases a held lock itself. If this server holds no lock and no handle is given, nothing is released and the call explains why; force_relogin drops the whole session's locks as a last resort.
Inputs: url (required), lockHandle, connectionId.
version_historyVersion HistoryARead-onlyIdempotent
List the stored revisions of an ABAP object, one line per revision: version id, date, author and version title. Lists metadata only, not the source of each version. Use it to see who changed an object and when; use get_transport_for_object for its current transport. Read-only. Returns a no-history message for objects without revisions.
Inputs: url (required), connectionId.
where_usedWhere UsedARead-onlyIdempotent
List where an ABAP object, or a symbol inside it, is used (the where-used list). Returns each usage as a location with a source snippet when SAP provides snippets, otherwise as name, type and URL. For symbol-level search pass line and column of the symbol in the object's source. Use search_abap_object_lines to search text inside one object instead. Read-only.
Inputs: url (required), line, column, connectionId.
write_abap_object_sourceWrite ABAP Object SourceADestructiveIdempotent
Write/update the source code of an ABAP object: locks, writes, reads back to verify, and releases the lock (keepLock: true to keep it). Give the code as source, or — for large sources — sourcePath, an absolute path to a local file under your home directory (UTF-8, at most 5 MB) that is sent as it is; exactly one of the two. The new source is the inactive version until abap_activate. Transport: a transport you pass is checked and used; an object already locked into a request uses it; createTransport: true creates a new one; otherwise the transport already used for this piece of work (workItem, else this session) is reused; otherwise exactly one open request is used, several are listed for you to choose, none offers creation.
Inputs: url (required), source, keepLock, workItem, +5 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 60 tools
Descriptions are unusually detailed and explicitly cross-reference neighbouring tools, separating overlapping areas such as read_table_contents vs execute_data_query vs customizing_read and customizing_engine_bootstrap vs engine_deploy. With 60 tools there is still some risk of confusion among the customizing, transport and object-lifecycle clusters, but the boundaries are well documented.
Most names are snake_case with recognizable verbs or noun phrases, but prefixes are inconsistent: get_abap_object_info and abap_activate coexist with where_used, version_history and syntax_check, while customizing_engine_bootstrap sits beside engine_deploy. The naming remains readable, but the mixed conventions make the surface less predictable than it could be.
At 60 tools the surface is far beyond a well-scoped MCP server, even for a broad ABAP development and customizing platform. The tools cluster into coherent domains, but the sheer count imposes a heavy selection and context burden on an agent.
Coverage is comprehensive: ABAP object search/read/write/create/activate/delete, locking, text elements, packages, transports, where-used, version history, syntax/ATC/unit testing, debugging, dumps, table queries, customizing operations, org-copy and engine deployment. Obvious lifecycle and diagnostic paths are represented with no major dead ends.
Maintenance
Related MCP Connectors
Read-only AI coding tools for change verification, release readiness, capacity, and guidance.
30+ deterministic dev and delivery tools: linting, metrics, diagrams, model advice, AI-text checks.
List, read, edit, and deploy your GenMB AI-generated apps from any MCP client.
327 dev tools via REST API and MCP. Generate Dockerfiles, schemas, K8s, APIs, and more.
Related MCP Servers
- FlicenseNot gradedqualityCmaintenanceEnables SAP ABAP development workflows including ADT login, object search, read, create, update, activate, delete, and OData service publishing.1-
- AlicenseCqualityDmaintenanceFacilitates interaction with SAP ABAP systems via ADT APIs, enabling object management, transport handling, and code analysis.100207 npmMIT
- AlicenseBqualityBmaintenanceEnables full ABAP development workflows against SAP systems via the ADT API, including reading and editing objects, running code, managing transports, executing tests, and performing code analysis.1771MIT
- AlicenseBqualityBmaintenanceLets Claude and other MCP hosts do real ABAP development on SAP systems through ADT: search objects, read and write source, transports, activation, ABAP Unit, ATC, short dumps, debugger and SQL queries. 173 tools, multi-system (S/4HANA Cloud and on-prem), with server-side guard rails and an audit log.2323 npm15MIT