README
ΒΆ
aibap.mcp
A community-built MCP (Model Context Protocol) server that lets AI assistants like Claude read, write, and manage ABAP source code on SAP systems β directly from the editor.
Community project. This project is not affiliated with, endorsed by, or supported by SAP SE. SAP and ABAP are trademarks of SAP SE.
Obey SAP API Guidelines
When using this MCP server, make sure to obey the SAP API Policy (see also the respective FAQ).
[!WARNING] You must not use this MCP server for purposes outside the intended scope of the ADT API as a development tooling framework. Specifically, this MCP server is not intended for programmatic reading of application tables or export of business data, SQL execution against SAP backend systems, business data integration or runtime orchestration, agentic AI workflows operating on business data, or substitution for business APIs.
[!TIP] Your agent can run the ABAP it just wrote β
run_class! π This server does not stop at writing and activating code, it closes the loop:create_objectβset_source_from_fileβactivate_objectβrun_class, which invokes ADT's classrun (Run as ABAP Application) on any global, active class implementingIF_OO_ADT_CLASSRUNand hands the console output back to the agent. So the agent can check what its code actually printed instead of asserting that it works β the runtime-only defects a syntax check never sees. Nothing to install on the SAP side.run_classisn't limited to classes that already exist for their own sake, either β wrap any ABAP logic (a report'sSUBMIT, a function module call, an ad-hoc expression) in a throwaway classrun class to run it, so it's the tool to reach for whenever you need to execute ABAP that has no dedicated tool of its own. See Available tools.
How it works
The server connects to your SAP system via the SAP ADT (ABAP Development Tools) REST API β the same HTTP API that ABAP Development Tools for Eclipse uses under the hood. For the vast majority of operations, that's all you need: no SAP GUI, no RFC, no additional middleware.
The SAP-touching code lives in adtler, a standalone Go client library for the ADT REST API. aibap.mcp is the thin MCP layer that exposes adtler's operations as MCP tools.
graph TD
A["Claude / AI assistant"] -->|"MCP (stdio)"| B["aibap.mcp<br/>(tools/ wrappers)"]
B -->|"Go API"| C["adtler<br/>(SAP ADT client library)"]
C -->|"HTTP + Basic Auth or OAuth2 + CSRF"| D["SAP System<br/>/sap/bc/adt/..."]
B -.->|"BlackMagicClient interface<br/>(BYO Go hook, compile-time β<br/>no default impl shipped)"| E["your BlackMagicClient impl<br/>(you write this,<br/>baked into a custom build)"]
E -.->|"SAP GUI / RFC / whatever you can conjure"| D
style E fill:#fff,stroke:#888,stroke-dasharray: 5 5
The BlackMagic fallback (BYO)
A handful of operations aren't exposed by the ADT REST API at all β customizing table writes (SM30/SM34), transport release on ECC (SE09), and similar SAP-GUI-only workflows. For these, aibap.mcp defines a BlackMagicClient Go interface, but ships no implementation. The hook says what the tool needs done; it does not say how to do it.
If you command the forbidden knowledge (or the raw power) to make SAP GUI, SAP Web GUI, RFC, or whatever else you've conjured bend to your will, you can write a Go implementation of BlackMagicClient and compile it into a custom build β the hook is a package-level var blackMagic tools.BlackMagicClient in main.go, set from a build-tagged init() in a file you maintain in your own fork. There is no runtime flag or config entry for this; the choice is baked into the binary at compile time. Your build then calls through your implementation transparently for the fallback-requiring tools. The interface is deliberately shape-agnostic: any Go struct that satisfies it works. No public implementation is shipped β producing one, and maintaining a custom build, is the implementer's own problem (and, arguably, part of the craft).
Without such a build, the fallback-requiring tools return an error at runtime on the stock binary; everything else keeps working. If building your own binary isn't your path, a GUI-driven peer MCP (for example sapgui.mcp, which your agent calls directly β separate from this server, not plugged into its BlackMagicClient interface) can cover the same SAP-GUI-only workflows from outside.
Available tools (72)
Tools are organized into groups. By default, all groups except debug are enabled. Tools that accept an object_uri parameter also accept an array of URIs for batch operations with parallel execution.
Source code β source (8 tools)
| Tool | Description |
|---|---|
get_source |
Read ABAP source code of any object (supports batch) |
get_class_definition |
Read only the class definition (no implementations) β saves ~95% tokens on large classes |
get_include_source |
Read class include source (testclasses, definitions, implementations, macros) |
set_source_from_file |
Write ABAP source from a local file (auto-locks) |
set_include_source |
Write class include source (requires lock on the class) |
patch_source |
Apply line-based or search/replace edits to source code (auto-locks) |
pretty_print |
Format ABAP source code using SAP Pretty Printer |
create_test_include |
Bootstrap the test-classes include (CCAU) for a class that has never had one |
Code intelligence β code-intelligence (4 tools)
| Tool | Description |
|---|---|
get_completions |
Get code completion proposals at a cursor position |
navigate_to_definition |
Jump to the definition of a symbol at a source position |
get_abap_doc |
Look up ABAP keyword documentation |
verify_source |
Syntax-check source code without saving to SAP |
Objects and packages β objects (10 tools)
| Tool | Description |
|---|---|
search_objects |
Search for objects by name pattern and type (supports wildcards) |
browse_package |
List all objects in a package (flat, one level) |
get_object_info |
Get object metadata: type, package, description (supports batch) |
object_exists |
Check if an ABAP object exists (true/false + metadata, supports batch) |
where_used |
Find all objects that reference a given object (supports batch) |
get_object_dependencies |
Find all objects that a given object references β forward direction counterpart to where_used (queries D010TAB, DD03L/DD04L/DD01L/DD40L, SEOMETAREL; also resolves Fiori catalogs and their app entries via UIAC/UIAD) |
get_table_fields |
Get DDIC table/structure field definitions (DD03L) |
create_object |
Create a new ABAP object (PROG, CLAS, INTF, FUGR, MSAG, DDLS, TABL, DTEL, DOMA) |
delete_object |
Delete an ABAP object (uses optimistic locking) |
rename |
Rename a symbol and update all references automatically |
Version history β version (3 tools)
| Tool | Description |
|---|---|
get_version_history |
List version history of an object β like git log |
get_version_source |
Read source of a specific version β like git show |
diff_active_inactive |
Compare active vs inactive source β like git diff |
Locking and activation β locking (7 tools)
| Tool | Description |
|---|---|
lock_object |
Lock an object for editing (returns lock handle) |
unlock_object |
Release a lock on an object |
force_unlock |
Force-release stuck edit locks by terminating the active system's SAP session (releases all its ENQUEUEs) |
list_locks |
List the locks this MCP session currently holds (client-side ledger; not a system-wide SM12 view) |
activate_object |
Activate a single ABAP object |
activate_objects |
Activate multiple objects at once |
get_inactive_objects |
List all inactive objects for the current user |
Testing and quality β testing (4 tools)
| Tool | Description |
|---|---|
syntax_check |
Run syntax check on an object (supports batch) |
run_unit_tests |
Run ABAP Unit tests on an object (supports batch) |
run_atc_check |
Run ATC (ABAP Test Cockpit) static analysis |
get_atc_customizing |
Get ATC check variant configuration |
Messages and texts β messages (4 tools)
| Tool | Description |
|---|---|
get_message_class |
Read all messages of a message class (SE91) |
search_messages |
Search messages across all message classes |
set_messages |
Write messages to a message class |
get_text_elements |
Read text symbols and selection texts of a program |
set_text_elements |
Write text symbols and/or selection texts (S/4 only) |
Runtime errors (ST22) β shortdumps (2 tools)
| Tool | Description |
|---|---|
list_short_dumps |
List recent short dumps (headers only, filterable by date/user) |
get_short_dump_details |
Get full dump details with error analysis and call stack |
Transport management β transport (8 tools)
| Tool | Description |
|---|---|
get_transport_requests |
List open or released transport requests |
get_transport_objects |
List all objects recorded in a transport (deduplicated) |
create_transport |
Create a new transport request |
create_transport_task |
Create a task under an existing transport request |
release_transport |
Release a transport request or task |
add_to_transport |
Assign an object to a transport request |
remove_from_transport |
Remove an object entry from a transport task |
delete_transport |
Delete a transport request or task |
rollback_transport |
Restore all source objects to their pre-transport version |
Enhancements / BAdIs β enhancements (3 tools)
| Tool | Description |
|---|---|
get_badi_definition |
Read a BAdI enhancement spot β definitions, interfaces, filters |
get_badi_implementation |
Read a BAdI enhancement implementation β implementing classes and flags |
set_badi_implementation |
Update an existing enhancement implementation (creation requires SE19) |
Debugging β debug (10 tools, off by default)
| Tool | Description |
|---|---|
debug_start |
Set a breakpoint and wait for it to be hit |
debug_stop |
Stop the debug listener and clean up breakpoints |
debug_attach |
Attach to an active debuggee session |
debug_step |
Step into / over / out / continue, or terminate / detach the debuggee |
debug_get_variable |
Read a variable value in the current scope |
debug_get_stack |
Get the current call stack |
debug_get_sessions |
List active debuggee sessions |
debug_set_breakpoint |
Set an additional breakpoint |
debug_set_watchpoint |
Break when a variable value changes |
debug_remove_breakpoint |
Remove a breakpoint (not yet implemented) |
Export β export (4 tools)
| Tool | Description |
|---|---|
export_package |
Export an ABAP package as abapGit ZIP or folder (requires companion) |
export_packages |
Bulk export with wildcard patterns and include/exclude filters |
export_customizing |
Export all customizing tables to SQLite + JSON (read-only, ~16K tables with customer_only) |
update_customizing |
Write entries to a customizing table (SM30/SM34) β requires BlackMagic fallback (SAP GUI automation) |
System β system (3 tools)
| Tool | Description |
|---|---|
select_system |
Switch the active SAP system (multi-system config) |
run_query |
Execute a SELECT query on SAP database tables (read-only) |
run_class |
Execute an ABAP class implementing IF_OO_ADT_CLASSRUN and return its console output β general-purpose ABAP execution: wrap any logic (e.g. a report's SUBMIT) in a throwaway classrun class to run it |
Requirements
- SAP NetWeaver 7.40+ with ADT services active (transaction SICF:
/sap/bc/adt) - A user with developer authorizations (
S_ADT_RES,S_DEVELOP) β or OAuth2 SSO (see below) - Go 1.27+ (to build from source)
Getting started
1. Download the binary
Each release on the releases page ships two flavours of the same binary. Pick one:
Default build β silent
No telemetry. Logs go only to your own stderr. Equivalent to building from source.
| Platform | File |
|---|---|
| Windows | aibap.mcp-*-windows-amd64.zip |
| macOS (Intel) | aibap.mcp-*-darwin-amd64.tar.gz |
| macOS (Apple Silicon) | aibap.mcp-*-darwin-arm64.tar.gz |
| Linux | aibap.mcp-*-linux-amd64.tar.gz |
Build with remote logging to Hochfrequenz
Same binary plus a compiled-in Papertrail destination that streams structured log events (tool name, SAP system key, duration, SAP object names, SAP error messages β no credentials, no source, no row data) to Hochfrequenz's log collector over TLS syslog. Helps us prioritise fixes and spot regressions for users who choose to opt in. Details and the opt-out env var are in Logging below.
| Platform | File |
|---|---|
| Windows | aibap.mcp-with-remote-logging-*-windows-amd64.zip |
| macOS (Intel) | aibap.mcp-with-remote-logging-*-darwin-amd64.tar.gz |
| macOS (Apple Silicon) | aibap.mcp-with-remote-logging-*-darwin-arm64.tar.gz |
| Linux | aibap.mcp-with-remote-logging-*-linux-amd64.tar.gz |
Run aibap.mcp --version after extracting to confirm which flavour you have β the output ends with remote-logging=on or remote-logging=off.
Extract the archive. You'll get a single executable (.exe on Windows).
2. Create systems.json
Create ~/.config/sap-mcp/systems.json (shared with sapgui.mcp β configure once, use everywhere):
{
"default_system": "dev",
"systems": {
"dev": {
"host": "https://your-sap-system:8000",
"user": "YOUR_USER",
"password": "YOUR_PASSWORD",
"client": "100",
"tls_skip_verify": false
}
}
}
[!TIP] Keep the password out of the file. A system's string fields β
connection_name,host,client,user,password,language,oauth2_client_idβ and the top-leveldefault_systemmay reference an environment variable with${env:VAR}:"user": "${env:SAP_DEV_USER}", "password": "${env:SAP_DEV_PASSWORD}"The file then carries only structure β which systems exist, their hosts and clients β so it can be committed and shared, while credentials come from your environment or CI secret store. This is especially useful for the Docker setup below, where the config is mounted but the secrets can be passed with
-e. A referenced variable that is unset or empty is a startup error naming the variable, never a silently empty credential.Placeholders are not resolved in the
toolslist or in system names β those are read by this server rather than by the shared config layer. Text that only looks like a placeholder, such as${SAP_PASSWORD}without theenv:prefix, is used verbatim.See sap-mcp-config for the exact rules, including which text is and is not treated as a placeholder.
3. Connect to Claude
See Usage with Claude below for copy-paste configuration snippets.
Alternative: Docker
docker pull ghcr.io/hochfrequenz/aibap.mcp:latest
docker run -i -v ./config.json:/config.json -e SAP_CONFIG_FILE=/config.json ghcr.io/hochfrequenz/aibap.mcp
Alternative: Build from source
Requires Go 1.27+. Either clone and build:
git clone https://github.com/Hochfrequenz/aibap.mcp.git
cd aibap.mcp
go build -o aibap.mcp .
Or install directly with go install:
go install github.com/Hochfrequenz/aibap.mcp@latest
Source builds always produce the silent variant (no remote logging baked in); the -with-remote-logging flavour is only produced by the release pipeline.
Configuration
Copy the example config and fill in your SAP system details:
cp config.json.example config.json
{
"default_system": "dev",
"systems": {
"dev": {
"host": "https://your-dev-system:8000",
"user": "YOUR_USER",
"password": "YOUR_PASSWORD",
"client": "100",
"tls_skip_verify": false
},
"prod": {
"host": "https://your-prod-system:8000",
"user": "YOUR_USER",
"password": "YOUR_PASSWORD"
}
}
}
Tool groups
By default, all tool groups except debug are enabled. You can customize which groups are loaded:
In systems.json (top-level):
{
"default_system": "dev",
"tools": ["source", "objects", "transport", "debug"],
"systems": { ... }
}
Via CLI flag (overrides config):
aibap.mcp --tools=source,objects,transport,debug
Special values:
--tools=allβ enable all tool groups- No config and no flag β default set (everything except
debug)
Available groups: source, code-intelligence, objects, version, locking, testing, messages, shortdumps, transport, enhancements, debug, export, system.
Consent for the irreversible tools
This server never asks you to confirm anything itself. Approving a call is the MCP
client's job, and the client decides what to ask. The problem that creates: in Claude Code
the first call to a destructive tool offers "Yes, and don't ask again", and accepting it
writes an allow rule that auto-approves every later call to that tool β on every system,
with every argument, for as long as the rule stays in the settings file. The rule cannot be
narrowed to one system or package: Claude Code skips any mcp__ rule containing
parentheses, and these tools take no system argument to match on.
Six tools do something this server cannot undo:
- delete_object β the object is gone. SAP has no undo, and the source is not under version control on the server side.
- delete_transport β the request cannot be recreated with its original number.
- release_transport β a released request cannot be un-released; the change is on its way to the next system.
- rollback_transport β restores an earlier state by overwriting the current one, so a wrong call destroys work the same way a delete does.
- run_class β executes arbitrary ABAP under the configured user. What it does is not part of the call.
- update_customizing β an entry with
"op": "delete"removes a customizing row. Customizing tables have no version database, so the row cannot be reconstructed. Note that on the stock binary this tool always fails with "configure a BlackMagic fallback", and understrictthe client asks for approval before the handler runs β so you answer a prompt for a call that cannot succeed. That is the price of marking it for the builds where it does work.
The --consent flag decides how the client's permission system treats those six:
aibap.mcp --consent=strict # default
aibap.mcp --consent=prompt
strict(default) marks the six with_meta["anthropic/requiresUserInteraction"]. Per the Claude Code MCP documentation the permission prompt then appears on every call β including inacceptEdits,autoandbypassPermissionsmodes β and an allow rule that already matches the tool does not skip it. IndontAskmode, and for anallowanswer from a--permission-prompt-toolin headless mode, the call is denied instead.promptmarks nothing. All tools stay on the client's normal permission flow, so a prompt can be answered once with "Yes, and don't ask again". Intended for callers who bring their own safety net β a working copy under abapGit, a throwaway sandbox system β and would rather answer fewer dialogs, and for unattended callers thatstrictwould otherwise deny.
Before choosing strict, three things are worth knowing:
- It cannot be scoped. A delete in a sandbox package costs the same prompt as one in
production, and no allow rule reverses the marking.
--consent=promptat launch is the way back. - Client version matters. The key requires Claude Code v2.1.199 or later; earlier versions ignore it entirely. Between v2.1.199 and v2.1.245 the prompt still displayed a "Yes, and don't ask again" option whose allow rule was then ignored β the prompt kept reappearing, but the option was misleading. It is no longer offered from v2.1.246. Other MCP clients may treat the key differently or not at all, and there the two modes are indistinguishable.
- Unattended is not uniformly broken. A headless run behind
--permission-prompt-toolis denied, but an Agent SDK host is not: itscanUseToolcallback still receives these calls and may approve them, because such a host is expected to put the question to a person. On surfaces that normally offer one-tap approval β Remote Control, and Agent SDK applications β Claude Code withholds the one-tap action for a marked tool and shows the full permission prompt instead, so the answer comes from the terminal dialog.
The tools left unmarked are unmarked on the merits:
run_queryruns SELECT statements only and changes nothing. Itspurposeparameter is a scope check under the SAP API Policy, not an approval step, and a missing or unrecognised value is rejected locally in both modes.renamerewrites source, and a second rename puts the old name back. The version database usually holds the previous state too β though not for an object that has never been versioned, such as one in$TMPor one whose transport is still open.remove_from_transportchanges an object's link to a transport rather than the object, andadd_to_transportrestores the link. This is the weakest of the exclusions: the tool's own description warns that a stalepositioncan remove the wrong entry, and restoring it means knowing which one vanished.force_unlockterminates this server's own SAP session to drop the locks it holds. It cannot reach another user's or another session's enqueues, so the worst case is losing unsaved work this same process was holding.
OAuth2 / SSO
For systems with SAML SSO, omit user and password to use OAuth2:
{
"systems": {
"prod": {
"host": "https://your-prod-system:8000",
"oauth2_client_id": "aibap.mcp"
}
}
}
Then authenticate via browser before starting the server:
aibap.mcp login prod
This opens your browser for SAML authentication. After login, tokens are cached in ~/.config/aibap.mcp/tokens.json and refreshed automatically.
SAP prerequisites: Register OAuth2 client aibap.mcp in transaction SOAUTH2 with grant type "Authorization Code" and redirect URI pattern http://localhost:*. SAML IdP trust must be configured in transaction SAML2.
Alternatively, configure via environment variables:
| Variable | Description |
|---|---|
SAP_CONFIG_FILE |
Path to systems.json (default: ~/.config/sap-mcp/systems.json) |
Usage with Claude
Claude Desktop
Add to your claude_desktop_config.json:
{
"mcpServers": {
"abap": {
"command": "/path/to/aibap.mcp",
"args": [],
"env": {
"SAP_CONFIG_FILE": "/path/to/config.json"
}
}
}
}
To reduce token footprint, load only the tool groups you need:
{
"mcpServers": {
"abap": {
"command": "/path/to/aibap.mcp",
"args": ["--tools=source,objects,testing,transport"],
"env": {
"SAP_CONFIG_FILE": "/path/to/config.json"
}
}
}
}
Claude Code (CLI)
Add to your Claude Code MCP settings or run directly:
SAP_CONFIG_FILE=/path/to/config.json aibap.mcp
Example workflow
Once connected, Claude can:
You: Show me the source of class ZCL_MY_SERVICE
Claude: [calls get_source] Here's the source...
You: Fix the bug in method GET_DATA and activate the class
Claude: [calls lock_object, patch_source, activate_object, unlock_object] Done. Activation succeeded.
You: Run the unit tests for this class
Claude: [calls run_unit_tests] 5 tests passed, 0 failed.
Logging
Logs go to stderr by default (text format). Configure via environment variables:
| Variable | Default | Description |
|---|---|---|
LOG_FORMAT |
text |
text or json |
LOG_LEVEL |
info |
debug, info, warn, error |
PAPERTRAIL_HOST |
β | Papertrail syslog host (e.g. logs5.papertrailapp.com) |
PAPERTRAIL_PORT |
β | Papertrail syslog port (e.g. 12345) |
Every log line carries version, commit, and remote_logging=on|off as default attributes so bug reports unambiguously identify which build emitted them.
Which builds ship with remote logging
| Install path | Remote logging by default |
|---|---|
aibap.mcp-* release archive (default flavour) |
off |
aibap.mcp-with-remote-logging-* release archive |
on β to logs5.papertrailapp.com:35329 |
Docker image (ghcr.io/hochfrequenz/aibap.mcp) |
off |
Source build (go build, make build, go install github.com/Hochfrequenz/aibap.mcp@latest) |
off |
The -with-remote-logging archive is the only path where telemetry is on by default. Picking it over the default archive is the consent step: users who download it have chosen to stream structured log events (tool name, SAP system key, duration, SAP object names such as ZCL_CUSTOMER_INVOICE, SAP error messages) to Hochfrequenz's Papertrail collector, where they help us prioritise fixes. No credentials, source code, or table row data are transmitted. Confirm which flavour you have with:
aibap.mcp --version
# aibap.mcp v0.2.1 (commit abc1234, remote-logging=on)
Disabling remote logging in the -with-remote-logging build
Set PAPERTRAIL_HOST= (explicit empty) before launching the server:
# Linux / macOS / Git Bash
PAPERTRAIL_HOST= ./aibap.mcp-with-remote-logging
# Windows PowerShell
$env:PAPERTRAIL_HOST=""
.\aibap.mcp-with-remote-logging.exe
Setting either PAPERTRAIL_HOST or PAPERTRAIL_PORT (even to empty) is treated as an explicit override and disables the baked-in defaults. To point at a different Papertrail account, set both.
Enabling your own Papertrail destination (any build)
Every build honours PAPERTRAIL_HOST + PAPERTRAIL_PORT at runtime. To ship your own logs to your own Papertrail account from any build flavour:
- Create a Papertrail account and set up a Log Destination (Settings > Log Destinations).
- Note the host and port (e.g.
logs5.papertrailapp.com:12345). - Set the environment variables before starting the server:
export PAPERTRAIL_HOST=logs5.papertrailapp.com
export PAPERTRAIL_PORT=12345
SAP_CONFIG_FILE=config.json ./aibap.mcp
Logs are sent over TLS. Both stderr and Papertrail receive every log event.
Architecture
The MCP server is a thin layer over the adtler Go library, which provides the SAP ADT HTTP client and OAuth2 token management. aibap.mcp depends on adtler the same way any other Go consumer would, and the library can be used standalone in CLIs, CI pipelines, and other integrations.
aibap.mcp
βββ tools/ β MCP tool handlers (thin wrappers around adtler)
βββ config/ β Multi-system JSON config loading
βββ cmd/ β CLI (login subcommand)
βββ logging/ β slog + Papertrail setup
βββ main.go β MCP server entry point (stdio transport)
Development
Unit tests
go test ./...
Integration tests
MCP-layer integration tests (this repo)
MCP-layer integration tests live in tools/integration_test.go behind //go:build integration. They are never run by default go test ./....
Prerequisites:
- VPN connectivity to the target SAP system(s)
~/.config/sap-mcp/systems.jsonconfigured (see Setup above)Z_ADT_MCP_TESTpackage installed on each target system β see Hochfrequenz/Z_ADT_MCP_TEST
Run:
go test -tags integration -v -count=1 ./tools/...
Target specific systems:
MCP_INTEGRATION_SYSTEMS=hfq go test -tags integration -v -count=1 ./tools/...
The default target set is hfq,s4u.
Coverage visibility: TestMain prints a grep-friendly summary at the top, e.g. integration targets: hfq=OK s4u=UNREACHABLE. Always check this β subtests skip loudly when a system or fixture is unreachable rather than failing, so it is possible to get a green go test without actually covering everything.
ADT client integration tests (adtler)
The ADT HTTP client integration tests live in adtler and cover the ADT HTTP client, XML marshalling, customizing export, and OAuth2. To run them against a real SAP system, clone that repo and follow its README.
Contributing
Issues and pull requests welcome. This is a community project β if your SAP system exposes additional ADT endpoints you'd like to see supported, open an issue.
License
MIT
Documentation
ΒΆ
There is no documentation for this package.