Content
# MCP-RFC bridge for SAP ABAP (RFC-only / SAProuter / ECC R3)
An MCP server that lets an MCP client (e.g. Claude Code) work with SAP ABAP
objects **over RFC** instead of ADT/HTTP. Because it speaks RFC + SAProuter, it
reaches systems that expose no HTTP/ADT endpoint — including classic ECC / R/3.
> Inspired by the SAP Community blog "Using Claude for SAP ABAP development on
> RFC-only / SAProuter systems (even ECC R/3)". This is an independent,
> prototype implementation — not an official SAP or Anthropic product.
## Architecture
```
Claude Code --stdio/MCP--> server.py (FastMCP)
|
sap/tools.py (wraps standard SAP FMs)
|
sap/connection.py (pyrfc Connection, reconnect)
|
sap/config.py (.env -> conn params, SAProuter)
|
pyrfc --RFC/SAProuter--> SAP system
```
## Tools exposed
| Tool | Purpose | Needs write? |
|-----------------------------|--------------------------------------|:------------:|
| `sap_ping` | Test connection, system info | no |
| `sap_read_table` | Read a table (RFC_READ_TABLE) | no |
| `sap_read_program` | Read ABAP report source | no |
| `sap_read_function_module` | Read FM source + interface | no |
| `sap_read_class` | Read class/interface source | no |
| `sap_read_method` | Read ONE method of a class | no |
| `sap_class_api` | Public API only (+ dependency APIs) | no |
| `sap_dead_code` | Unused-method scan for a package | no |
| `sap_search_objects` | Search TADIR for repository objects | no |
| `sap_ddic_info` | Describe table/structure fields | no |
| `sap_list_dumps` | Recent ST22 runtime errors | no |
| `sap_list_transports` | List transport requests (E070) | no |
| `sap_read_transport` | One transport + its object list | no |
| `sap_where_used` | Where-used via WBCROSSGT index | no |
| `sap_read_screen` | Read Dynpro definition (via Z-FM) | no |
| `sap_read_gui_status` | Read GUI status/CUA (via Z-FM) | no |
| `sap_syntax_check` | Syntax-check source, no save (Z-FM) | no |
| `sap_read_cds` | Read CDS/DDL source (Z-FM) | no |
| `sap_textpool_read` | Read program text elements (Z-FM) | no |
| `sap_run_unit_tests` | Run ABAP Unit for a class (Z-FM) | no |
| `sap_run_atc` | Run ATC check (Z-FM stub) | no |
| `sap_adt_dispatch` | Raw ZMCP_ADT_DISPATCH call | read: no / write: **yes** |
| `sap_activate` | Activate inactive objects (Z-FM) | **yes** |
| `sap_textpool_write` | Write program text elements (Z-FM) | **yes** |
| `sap_write_program` | Create/update program (INACTIVE) | **yes** |
| `sap_run_rfc` | Call an arbitrary RFC-enabled FM | **yes** |
### Custom dispatcher `ZMCP_ADT_DISPATCH`
The Z-FM tools above route through a custom RFC-enabled FM **`ZMCP_ADT_DISPATCH`**
(function group `ZMCP_ADT_UTILS`) installed on the SAP system — the escape hatch
for features no standard RFC FM exposes. It runs *inside* SAP, so it can call
non-remote workbench APIs (`SYNTAX-CHECK`, `RS_WORKING_OBJECTS_ACTIVATE`,
`READ/INSERT TEXTPOOL`, `DDDDLSRC`, ABAP Unit, ATC) and return JSON.
Install the two FMs from [`abap/`](abap/) on any SAP system you want to drive
(see [`abap/README.md`](abap/README.md) for the interfaces and step-by-step
setup). Action → tool map:
| Action | Tool | Underlying ABAP |
|--------|------|-----------------|
| `SYNTAX_CHECK` | `sap_syntax_check` | `SYNTAX-CHECK FOR` |
| `READ_DDLS` | `sap_read_cds` | `SELECT … FROM DDDDLSRC` |
| `RUN_UNIT_TESTS` | `sap_run_unit_tests` | `CL_AUCV_TEST_RUNNER_STANDARD` |
| `ATC_CHECK` | `sap_run_atc` | `CL_SATC_API_FACTORY` (stub — fill in) |
| `ACTIVATE` | `sap_activate` | `RS_WORKING_OBJECTS_ACTIVATE` |
| `DYNPRO_*` / `CUA_*` | `sap_read_screen` / `sap_read_gui_status` | `RPY_DYNPRO_*` / `RS_CUA_INTERNAL_*` |
`RUN_UNIT_TESTS` and `ATC_CHECK` use release-sensitive class APIs — verify/adjust
the ABAP in SE24 for your system (`ATC_CHECK` ships as an explicit stub). Every
write action goes through the same `SAP_ALLOW_WRITE` guard.
**Text elements** use a separate dedicated FM, **`ZMCP_ADT_TEXTPOOL`** (same
function group), driven by `sap_textpool_read` / `sap_textpool_write`. Flag it
Remote-Enabled in SE37 to use it. `sap_textpool_write` defaults to
`WRITE_INACTIVE` (staged; published on program activation), matching the
"nothing goes live until activate" model; pass `active=true` to write directly.
Write tools are disabled unless `SAP_ALLOW_WRITE=true`.
**Sensitive-table policy:** `sap_read_table` refuses tables holding
credentials/PII (USR02, PA0*, SECSTORE*, …). Override the pattern list via
`SAP_TABLE_BLOCKLIST` (comma-separated, `*` wildcards) in `.env`.
**Package whitelist for writes:** set `SAP_ALLOWED_PACKAGES` (e.g.
`$TMP,ZPK_SANDBOX*`) to restrict `sap_write_program` to specific packages.
Unset = no package restriction (only `SAP_ALLOW_WRITE` applies).
**FM deny list:** `sap_run_rfc` refuses high-risk function modules (OS command
execution, arbitrary ABAP, mass delete, raw SQL, user admin, file transfer,
kernel admin) even with `SAP_ALLOW_WRITE=true`. Override the pattern list via
`SAP_FM_DENYLIST` (comma-separated, `*` wildcards).
## Project layout
```
MCP_SAP_PRIVATE/
bootstrap.bat copy to a new machine: clones repo + runs setup
setup.ps1 / setup.bat installer + MCP registration
README.md
mcp/ the MCP server (everything it needs to run)
server.py MCP server entrypoint (FastMCP, stdio)
test_connection.py standalone connectivity check
requirements.txt
.env / .env.example base/shared settings (real .env is git-ignored)
profiles/ one <name>.env per SAP system (multi-system)
ds4.env.example per-system profile template
sap/ Python package (the bridge)
config.py .env -> pyrfc params, SAProuter, SDK discovery
connection.py pyrfc wrapper, reconnect, DLL registration
tools.py FM wrappers behind each MCP tool
vendor/ third-party / licensed binaries
nwrfcsdk/ SAP NW RFC SDK (bundled, auto-detected)
pyrfc-3.3.1-*.whl prebuilt pyrfc wheel (offline install)
abap/ ABAP to install on the SAP system (reusable)
README.md install guide + FM interfaces
zmcp_adt_dispatch.abap ZMCP_ADT_DISPATCH (screens, syntax, activate, …)
zmcp_adt_textpool.abap ZMCP_ADT_TEXTPOOL (text elements)
```
## Deploy via git (clone-and-go)
This repo is meant for a **private** git remote: the SAP NW RFC SDK
(`mcp/vendor/nwrfcsdk`) and the pyrfc wheel are committed, so a fresh clone runs
without any SAP-portal download. Secrets are **not** committed — `.env` and
`mcp/profiles/*.env` are git-ignored; each machine fills them in locally.
### First push (once, from this folder)
```powershell
git init
git add .
git commit -m "SAP MCP-RFC bridge"
git remote add origin https://github.com/ChinhHN-DEV/MCP_SAP_PRIVATE.git
git push -u origin main
```
> The commit includes the ~60 MB SDK. Keep the remote **private** — the SDK is
> licensed and must not be public.
### On a new machine (one command)
Edit the `REPO=` line in **`bootstrap.bat`**, copy just that one file to the new
machine, and run it (or `bootstrap.bat <git-url>`). It clones the repo and runs
`setup.ps1`. Afterwards, create your connection config:
```powershell
# single system:
copy mcp\.env.example mcp\.env # then edit
# or multi-system:
copy mcp\profiles\ds4.env.example mcp\profiles\ds4.env # then edit, re-run setup.ps1
```
## Quick install on a new machine
Copy the whole folder over, then from inside it run **one** of:
```powershell
# Windows (PowerShell)
powershell -ExecutionPolicy Bypass -File .\setup.ps1
```
…or just **double-click `setup.bat`**.
`setup.ps1` finds Python, installs `python-dotenv` + `mcp`, installs the
matching `pyrfc` wheel (from `vendor/`, or downloads the right one), verifies
the bundled SDK, creates `.env` from the template, and registers the `sap-rfc`
MCP server with Claude Code using this folder's current absolute path — so it
works no matter where the folder was moved to. Afterwards just edit `.env` and
run `python mcp\test_connection.py`. The manual steps below are the same thing by
hand.
## Multiple SAP systems
Run one MCP server per system, each selected by a **profile** file. Claude Code
then sees separate, namespaced tool sets (`mcp__sap-ds4__…`, `mcp__sap-qa4__…`)
and picks the right system by server name.
1. Put per-system logon in `mcp/profiles/<name>.env` (copy `mcp/profiles/ds4.env.example`):
```
profiles/ds4.env # DEV, SAP_ALLOW_WRITE=true
profiles/qa4.env # QA, read-only
profiles/prd.env # PROD, read-only
```
Shared policy (`SAP_TABLE_BLOCKLIST`, `SAP_FM_DENYLIST`, …) stays in the base
`.env`; each profile **overrides** the base and holds that system's
credentials + `SAP_ALLOW_WRITE`.
2. Run `setup.ps1` — it registers one server `sap-<name>` per profile file
(falls back to a single `sap-rfc` from `.env` when no profiles exist).
3. Test a specific system:
```powershell
python mcp\test_connection.py --profile qa4
```
Profile selection precedence: `--env-file <path>` > `--profile <name>` >
`SAP_PROFILE` env var > base `.env`. `sap_ping` reports which `profile` answered.
Keep DEV writable and QA/PROD read-only (`SAP_ALLOW_WRITE=false`) so the deny
guards differ per system automatically.
## Setup (manual)
### 1. SAP NW RFC SDK (prerequisite for pyrfc)
`pyrfc` wraps the **SAP NetWeaver RFC SDK** (licensed; from the SAP Support
Portal). This repo bundles the **Windows** SDK at `mcp/vendor/nwrfcsdk`, auto-detected
and registered at runtime (via `os.add_dll_directory`). No PATH edits needed. To
use a different SDK location, set `SAPNWRFC_HOME` — it takes precedence over the
bundled copy.
#### OS runtime prerequisite for the SDK (SAP Note 2573790)
The NW RFC SDK DLL needs the **Microsoft Visual C++ 2015-2022 Redistributable
x64** (`vcruntime140.dll`) present — without it, `import pyrfc` fails with a
cryptic load error. `setup.ps1` warns and links the installer if it is missing.
Patch level of an installed SDK: `findstr Patch sapnwrfc.dll`.
### 2. Install Python dependencies
`pyrfc` has no PyPI wheel for recent Python, so a matching prebuilt wheel is
vendored in `mcp/vendor/`:
```powershell
python -m pip install python-dotenv mcp
python -m pip install mcp/vendor/pyrfc-3.3.1-cp312-cp312-win_amd64.whl
```
The wheel is `cp312 / win_amd64` (Python 3.12, Windows 64-bit) from the
`SAP-archive/PyRFC` GitHub releases. For a different Python version, fetch the
matching wheel from there.
### 3. Configure the connection
```powershell
Copy-Item mcp\.env.example mcp\.env
# then edit .env
```
SAProuter example — either put the full route in `SAP_ASHOST`:
```
SAP_ASHOST=/H/saprouter.example.com/S/3299/H/sapappserver.internal
SAP_SYSNR=00
```
…or keep a plain host and set the router prefix separately:
```
SAP_ASHOST=sapappserver.internal
SAP_SYSNR=00
SAP_SAPROUTER=/H/saprouter.example.com/S/3299
```
### 4. Test connectivity (no MCP needed)
```powershell
python mcp\test_connection.py
```
### 5. Register the server with Claude Code
```powershell
claude mcp add sap-rfc -- python "c:\Users\Administrator\Desktop\MCP_SAP_PRIVATE\mcp\server.py"
```
Or add to your MCP client config:
```json
{
"mcpServers": {
"sap-rfc": {
"command": "python",
"args": ["c:\\Users\\Administrator\\Desktop\\MCP_SAP_PRIVATE\\mcp\\server.py"]
}
}
}
```
## Safety
* **Read-only by default.** Keep `SAP_ALLOW_WRITE=false` unless you intend to
modify objects, and only enable writes against a **DEV** system.
* Writes are saved **INACTIVE** — nothing goes live until you activate it in SAP.
* Use an RFC user with the **minimum** authorizations required.
* `.env` holds credentials — it is git-ignored; never commit it.
## Verified on
Read tools (`sap_ping`, `sap_read_table`, `sap_read_program`,
`sap_read_function_module`, `sap_read_class`, `sap_search_objects`) verified
end-to-end against an **S/4HANA system (SAP_BASIS 758)** reached through a
SAProuter. Class source uses the remote-enabled `SIW_RFC_READ_CLIF_SOURCE`.
## Caveats / limits over pure RFC
* Only **remote-enabled** function modules are callable over RFC. Many workbench
FMs (e.g. the `SEO_*` class APIs, the ABAP `SYNTAX-CHECK`) are **not** RFC-
enabled, so some ADT-style features simply aren't reachable this way.
* **CDS/DDL source read is not available** over pure RFC on the tested system —
there is no remote-enabled FM that returns DDL source text. Use ADT for that.
* **Object activation and syntax check** are likewise not wired up: no clean
remote-enabled FM was found on the tested release. Writes therefore stay
**INACTIVE**; activate in SAP GUI/ADT.
* `RFC_READ_TABLE` cannot select rows wider than 512 bytes or long/deep fields;
long WHERE clauses are auto-wrapped to the 72-char OPTIONS line limit.
* FM availability/interfaces can still vary by release — validate on your target.
```
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available