LISA — Localization & Internationalization Service for ABAP

npm npm ghcr.io CI Product release Publish extension node license MCP

Let AI assistants read, write and compare SAP object translations through a single, secure MCP server.

LISA (Localization & Internationalization Service for ABAP) is a Model Context Protocol (MCP) server that lets AI assistants (Claude, Cursor, VS Code, …) manage the translation of SAP repository objects — data elements, domains, CDS views, message classes, class/function-group text pools, and more — without leaving the chat.

For authentication and SAP BTP connectivity it builds on the same stack as ARC-1 — in fact it depends on the published @arc-mcp/xsuaa-auth package (the XSUAA OAuth proxy + BTP principal-propagation layer extracted from ARC-1) rather than re-implementing it, on the same Express / MCP-SDK transport. On top of that, instead of the full ADT toolset, it exposes 3 focused translation tools backed by a small ABAP HTTP service that wraps SAP's XCO i18n APIs.

LISA ships in two distributions from this one repo, sharing the same wire contract and ABAP handler — pick whichever fits how you already run things:

  • Standalone MCP server — its own Cloud Foundry app, its own XSUAA login. The primary, production-supported path. Published on npm as @lisa-mcp/server — consume it from npm (npx @lisa-mcp/server locally, or deploy to BTP via the bundled MTA template, no clone) or from a clone of this repo (develop / build from source). → Part 2.
  • ARC-1 extension — if you already run ARC-1, load LISA's 3 tools in-process instead: no second app, no second URL, no second XSUAA — you reuse ARC-1's authenticated SAP client and per-user principal propagation. Published on npm as @lisa-mcp/arc1-extension. → Part 3.

Running it locally (Part 4) is also fully supported, but meant for development and testing, not production.

No MCP client at all? A BTP runtime (Joule Studio action, CAP service, SAP Build) can call the underlying ABAP API (zi18n_service) directly — no LISA Node layer — through a BTP destination that carries the auth (per-user propagation or a shared user). You get the same 3 operations over plain JSON. See BTP destination setup, the OpenAPI spec (import as a Joule Studio action), and the API-usage skill.


How it works

┌──────────────┐   MCP/HTTP    ┌────────────────────┐   HTTPS (JSON)    ┌──────────────────────────┐
│  AI assistant│ ────────────> │  LISA MCP          │ ───────────────>  │  SAP ABAP system         │
│ (Claude/IDE) │   3 tools     │(Node.js, this repo)│  /zi18n_service   │  ZCL_I18N_SERVICE        │
└──────────────┘ <──────────── └────────────────────┘ <───────────────  │  → XCO i18n APIs         │
                                                                        └──────────────────────────┘

There are two halves to a working setup:

  1. The ABAP service — a handler class (ZCL_I18N_SERVICE, three per-platform variants) that you import into your SAP system and expose as an ABAP HTTP service. It does the actual translation work using the XCO i18n APIs. → see abap/ and docs: ABAP service setup.
  2. The MCP server — this Node.js project. It authenticates the caller, propagates their identity to SAP, and translates MCP tool calls into HTTP calls to the ABAP service.

The 3 tools

Tool What it does
TranslateListLanguages List all languages installed on the SAP system.
TranslateGetTexts Read all translatable texts of an object in a given language (or its original language when none is given). Each slot comes back with its full key (level, field_name, position, attribute), its value, and a populated flag (false = empty in this language = still to translate).
TranslateSetTexts Write/update translations (requires a transport request). Each text entry may carry its own field_name/position, so all fields of one CDS view (e.g. every ui_lineitem_label) are written in a single call — locking the object only once.

Discover / list / compare all collapse into TranslateGetTexts: read with no language to see the original-language slots, keep populated === true to list only filled texts, and call it once per language and diff on (key, populated, value) to compare.

Supported object types (target_type)

This is the full catalog of object types LISA understands — XCO semantic literals, not DDIC short codes. Which of them are actually available depends on the target system: public cloud / BTP ABAP Environment and on-premise / private cloud support different types per operation, and the set can also differ by system version. LISA probes the connected backend at startup and states the concrete per-operation list in each tool's description (a target_type the system doesn't support is rejected up-front).

target_type SAP object Typical attributes
cds_entity CDS entity — merged view + DDLX everything below for both objects, each row tagged owner
data_element Data element (DTEL) short_field_label, medium_field_label, long_field_label, heading_field_label
domain Domain fixed-value texts (DOMA) fixed_value_description
data_definition CDS view (DDLS) entity/field labels endusertext_label
message_class Message class (MSAG) message_short_text
text_pool Class / function-group text symbols text symbol values
metadata_extension CDS metadata extension (DDLX) UI labels endusertext_label
application_log_object Application log object (APLO) object / sub-object texts
business_configuration_object Business configuration object (SMBC) description texts
text_table Text table (delivery class C/S DB table with one LANG key field, e.g. T005T) its non-key character columns, e.g. LANDX

Text tables (text_table): a database table whose delivery class is C or S and that has exactly one language key field (LANG, e.g. SPRAS) is a translatable text table — its non-key character columns (e.g. LANDX on T005T) are the text attributes. Beyond the usual params it takes language_key_field_name (the LANG key field, e.g. SPRAS) and master_key_fields ([{ name, value }] fixing all master keys — every key except the language field — so a single record is targeted, e.g. [{ "name": "LAND1", "value": "DE" }]). On write, each texts entry's attribute is a text column name (e.g. LANDX), not a UI label. A table with no language field (e.g. delivery class W) is rejected by XCO.

CDS views & metadata extensions: a CDS view's UI labels are frequently defined (or overridden) in a separate metadata extension (DDLX). Use cds_entity (a virtual, LISA-only target — not a backend type) to treat one named entity and its own DDLX as a single translation surface: a single TranslateGetTexts returns the view (data_definition) and its DDLX (metadata_extension) together — the DDLX labels come back without a separate metadata_extension call — and stamps every CDS row with an owner ("data_definition" or "metadata_extension"). On write, pass each row's owner back: LISA groups by it and writes each group to its physical object in one call (so the view and the DDLX are each locked/transported once); a cds_entity write with any row missing owner is rejected. Positional UI labels round-trip as the bare attribute (e.g. ui_lineitem_label) plus a separate position (1-based); the index is never renumbered. Writes are not atomic across the two objects, so the result reports per-owner outcomes. The single-object data_definition and metadata_extension targets remain available to address one object explicitly.

cds_entity is per-entity, not per-stack. It covers the named entity + its DDLX only — it does not reach the underlying/parent views of an as projection on chain (e.g. the interface view ZI_… behind a projection ZC_…, which carries its own @EndUserText.label). Translate a RAP stack with one cds_entity pass per distinct entity (one view = one call). cds_entity works with or without a DDLX; for a single view with no DDLX, data_definition direct is cleaner (it skips the empty DDLX sub-call).


Use it alongside an ADT MCP server

LISA is focused on the translation step — it deliberately does not discover objects or manage transports. For an interactive, AI-driven workflow it is designed to be used next to an ADT MCP server — SAP's own ABAP MCP Server shipped with ADT for VS Code, or ARC-1 — which provides the surrounding capabilities:

  • object discovery — find the data element / CDS view / message class to translate;
  • transport handling — locate or create the transport request that TranslateSetTexts requires;
  • inspection — read the object before translating it.

Typical division of labour: the ADT MCP finds the object and a transport → LISA reads, writes and compares its translations. On its own, LISA still works whenever the object name and transport are already known (e.g. batch or scripted translation).

This describes two separate MCP servers talking to the same AI assistant. If your "ADT MCP" is ARC-1, you can skip running LISA as a second server altogether and load its tools inside ARC-1 instead — see Part 3 — Use as an ARC-1 extension.


Prerequisites

  • An SAP system with the XCO i18n APIs available (S/4HANA 2022+ / ABAP Platform 2022+ / ABAP Cloud) and the new HTTP handler model (IF_HTTP_SERVICE_EXTENSION). XCO i18n docs per landscape: ABAP Platform (on-premise / private cloud) · S/4HANA Public Cloud · SAP BTP ABAP Environment.
  • Authorization to import a class and create an ABAP HTTP service (ADT). On-premise / private cloud also needs rights to enable it via UCON_HTTP_SERVICES; on BTP ABAP Environment / public cloud the endpoint activates automatically with the HTTP Service object — no communication scenario needed.
  • Node.js 22.x to run the MCP server.
  • For production: an SAP BTP subaccount (Cloud Foundry) with XSUAA, Destination and Connectivity services.

Part 1 — Install the ABAP service

The ABAP handler to copy into your target SAP system lives in abap/, in three platform folders — pick the one that matches your stack. Each class is fully self-contained (the JSON/parameter helpers are inlined, no external utility class to import alongside it), but spans three files — the global class plus two local-types includes that define lcl_slot_visitor (an XCO CDS-annotation visitor used for positional UI labels):

Folder Object Use when
abap/ABAP_PLATFORM_2022/ ZCL_I18N_SERVICE On-premise / private cloud on ABAP Platform 2022 (7.57) — original XCO i18n surface.
abap/ABAP_PLATFORM_2025/ ZCL_I18N_SERVICE On-premise / private cloud on ABAP Platform 2025 (newer releases) — newer XCO i18n surface (positional entity texts, Fiori launchpad targets).
abap/CLOUD/ ZCL_I18N_SERVICE SAP BTP ABAP Environment / public cloud (Steampunk) — Cloud-API-compliant variant.

With abapGit the three files of the folder you pick reassemble into the single class object automatically — drop them into a linked package and pull. With ADT / SE24, paste the three parts into their respective tabs before activating: *.clas.abap → Global Class, *.clas.locals_def.abap → Class-relevant Local Types (CCDEF), *.clas.locals_imp.abap → Local Types (CCIMP). Skipping the local-types parts leaves lcl_slot_visitor undefined and the class won't activate.

All three are named ZCL_I18N_SERVICE (separated by folder, not class name), implement IF_HTTP_SERVICE_EXTENSION, route actions from the URL path, and speak the same wire contract — they differ only in the XCO i18n API surface available on each release. Import the class from the folder for your platform, create an ABAP HTTP service whose handler class is that class, and enable it (UCON_HTTP_SERVICES on-premise / private cloud; on ABAP Environment the endpoint activates automatically with the HTTP Service object — no communication scenario). Point the MCP at its URL (default /sap/bc/http/sap/zi18n_service).

👉 Full step-by-step instructions: docs: ABAP service setup.


Part 2 — Deploy to SAP BTP (recommended)

This is the main way to run LISA. It deploys to Cloud Foundry as an MTA and uses XSUAA for authentication only — there are no scopes, role templates or role collections. XSUAA proves the caller's identity; the JWT is propagated to SAP (principal propagation via the Destination + Connectivity services), and SAP's own authorization objects decide what each user may read, write or translate. Every authenticated user gets all 3 tools.

npm install
npm run build
mbt build           # produces mta_archives/lisa_<version>.mtar (version from mta.yaml)
cf deploy mta_archives/lisa_<version>.mtar
# or in one step, with your landscape overrides applied: npm run btp:build-deploy-dev

No clone needed — deploy from npm. The server is published as @lisa-mcp/server, which ships a ready-made MTA template (templates/mta/): a 3-file deploy project whose nodejs module just npm installs the published package — same XSUAA/Destination resources, and upgrading LISA is a version bump. See docs: BTP deployment — deploy from npm.

Then set the DCR signing secret (one-off) and point your MCP client at the deployed URL:

cf set-env lisa-mcp LISA_DCR_SIGNING_SECRET "$(openssl rand -base64 48)"
cf restage lisa-mcp
{
  "mcpServers": {
    "lisa": { "url": "https://lisa-<space>.<domain>/mcp" }
  }
}

The client is redirected through XSUAA login on first use; its identity is then propagated to SAP per call.

👉 Full guide: docs: BTP deployment · docs: authentication · the mta.yaml / xs-security.json.

Why LISA_DCR_SIGNING_SECRET? Without it the OAuth dynamic-client store signs with the XSUAA clientsecret, which cf deploy rotates — invalidating all cached MCP client registrations on every deploy. It's a secret, so it lives in cf set-env, not mta.yaml.


Part 3 — Use as an ARC-1 extension

Already run ARC-1? You don't need a second CF app for LISA. packages/arc1-extension packages the same 3 tools as Custom_TranslateListLanguages / Custom_TranslateGetTexts / Custom_TranslateSetTexts, loaded in-process by ARC-1 via ARC1_PLUGINS — they reach SAP through ARC-1's own gated ctx.http.post, so there is no second auth stack and no second URL to operate.

Pick the… when
Standalone server (Part 2) You want LISA reachable as its own MCP endpoint/URL, with its own XSUAA login, independent of any ARC-1 deployment.
ARC-1 extension (this Part) You already run ARC-1 and want LISA's tools to appear inside it — reusing ARC-1's authenticated SAP client, safety ceiling, scope policy, audit, and per-user principal propagation. Two CF apps (arc1 + lisa) become one.

Install it from npm, or build from source:

npm install @lisa-mcp/arc1-extension                    # → node_modules/@lisa-mcp/arc1-extension/dist/index.js
# — or build from source —
git clone https://github.com/ClementRingot/LISA.git && cd LISA
npm ci
npm run build --workspace packages/arc1-extension   # → packages/arc1-extension/dist/index.js

npm run build bundles the plugin into a single self-contained ESM file (packages/arc1-extension/dist/index.js, ~24 KB) via esbuild — @lisa-mcp/core is inlined and only arc-1/public and zod stay external (both provided by the host ARC-1 process). That file is ready to load as-is; no manual bundling step is needed.

Point an existing ARC-1 instance at the built plugin (env vars on the ARC-1 side):

ARC1_PLUGINS=/abs/path/to/lisa/dist/index.js
SAP_ALLOW_WRITES=true
SAP_ALLOW_PLUGIN_RAW_WRITES=true   # required even for the read-only tools — see the guide below

The ABAP service (Part 1) still installs the same way either distribution you pick — only who performs the HTTP call differs.

👉 Full guide (Docker/buildpack deploy, the SAP_ALLOW_PLUGIN_RAW_WRITES rationale, troubleshooting): docs: ARC-1 extension deployment.


Part 4 — Run locally (development & testing)

For local development you connect directly to SAP (BasicAuth) — no BTP services involved. This path is for trying things out and developing the server; production should run on BTP (Part 2) or inside ARC-1 (Part 3).

Fastest — straight from npm (no clone; a .env in the current directory is picked up):

npx @lisa-mcp/server

Or from source (for developing LISA itself):

git clone https://github.com/ClementRingot/LISA.git
cd LISA
npm install
cp packages/server/.env.example .env      # then edit .env
npm run dev               # tsx packages/server/src/index.ts  (hot dev)
# or
npm run build && npm start

Minimum local .env:

SAP_URL=https://your-abap-system.example.com
SAP_USERNAME=ABAP_USER
SAP_PASSWORD=secret
SAP_CLIENT=100
SAP_I18N_SERVICE_PATH=/sap/bc/http/sap/zi18n_service
MCP_TRANSPORT=http-streamable      # or "stdio"
PORT=8080

By default the server starts an HTTP-streamable MCP endpoint on http://localhost:8080/mcp with a /health probe.

Connect an MCP client over HTTP (Claude web/desktop, Cursor, VS Code):

{
  "mcpServers": {
    "lisa": { "url": "http://localhost:8080/mcp" }
  }
}

Or over stdio (local-only, no auth) — set MCP_TRANSPORT=stdio and launch the built server directly:

{
  "mcpServers": {
    "lisa": {
      "command": "node",
      "args": ["/absolute/path/to/LISA/packages/server/dist/index.js"],
      "env": { "MCP_TRANSPORT": "stdio", "SAP_URL": "…", "SAP_USERNAME": "…", "SAP_PASSWORD": "…" }
    }
  }
}

⚠️ Local mode has no XSUAA in front of it — with no auth vars set the HTTP transport is open, and SAP calls use a single technical user instead of per-user principal propagation. Keep it on your machine; don't expose it. For anything shared or production-grade, use BTP (Part 2).

Run in Docker (self-hosted)

A production-grade image (multi-stage, non-root, tini) is published to ghcr.io on every product release — or build it yourself from the repo's Dockerfile:

docker pull ghcr.io/clementringot/lisa-mcp:latest     # or :X.Y.Z to pin
# — or: git clone … && docker build -t lisa-mcp .

docker run -p 8080:8080 \
  -e SAP_URL=https://your-abap-system.example.com \
  -e SAP_USERNAME=ABAP_USER -e SAP_PASSWORD=secret -e SAP_CLIENT=100 \
  -e SAP_API_KEYS=mykey123:developer \
  ghcr.io/clementringot/lisa-mcp:latest
# MCP endpoint: http://localhost:8080/mcp   |   health: /health

This is the self-hosted path (Kubernetes, VM, on-prem): outside BTP there is no XSUAA and no Destination service, so SAP is reached directly (SAP_URL, technical user — no per-user principal propagation) and callers authenticate via API keys or OIDC/Entra (OIDC_ISSUER + OIDC_AUDIENCE) — set at least one, or the endpoint is open. For per-user SSO + principal propagation, deploy on BTP (Part 2).


Configuration reference

Variable Purpose
SAP_I18N_SERVICE_PATH URL path of the ZCL_I18N_SERVICE HTTP service (default /sap/bc/http/sap/zi18n_service).
SAP_URL / SAP_USERNAME / SAP_PASSWORD / SAP_CLIENT Direct connection for local dev.
SAP_BTP_DESTINATION BasicAuth Destination — system-level calls / fallback (BTP).
SAP_BTP_PP_DESTINATION PrincipalPropagation Destination — per-user calls (BTP).
MCP_TRANSPORT http-streamable (default) or stdio.
PORT HTTP port (default 8080).
LOG_LEVEL / LOG_FORMAT debug|info|warn|error / text|json.
SAP_API_KEYS key:profile,… CSV API-key auth (viewer|developer|admin).
OIDC_ISSUER / OIDC_AUDIENCE OIDC/Entra ID token validation.
VCAP_SERVICES Injected by BTP; carries the XSUAA binding.
LISA_DCR_SIGNING_SECRET Stable signing secret for the OAuth DCR store (set via cf set-env).
SAP_OAUTH_DCR_TTL_SECONDS DCR registration TTL (0 = never expire).
MCP_RATE_LIMIT / OAUTH_RATE_LIMIT Per-minute rate limits (default 600 / 20).
CORS_ORIGINS Comma-separated allowed CORS origins.

The full commented template lives at packages/server/.env.example (also shipped inside the npm package).


Documentation

The docs_page/ folder holds the long-form guides:

Guide
Index Documentation home.
Quickstart Fastest path to a working setup.
ABAP service setup Import the class & publish the HTTP service.
MCP tools usage Every tool, with examples.
Text tables Translating delivery-class C/S tables (text_table, e.g. T005T).
Configuration reference All env vars in detail.
Authentication Auth model & options.
BTP deployment Cloud Foundry / MTA (standalone server).
ARC-1 extension deployment Run LISA's tools in-process inside ARC-1.
Local development Dev loop, lint, build.
Architecture How the pieces fit together.
Wire-contract evolution Growing the contract as XCO APIs diverge per platform.
Releasing Changesets, tags, npm publication, which ref to deploy.
BTP destination setup Create the destination that authenticates zi18n_service calls (per-user propagation or shared Basic-auth user), for the MCP server or a Joule Studio / CAP action.
Agent skill: using the ABAP API How to drive zi18n_service — the six actions, wire contract, pitfalls. Auth-agnostic (a destination or the MCP server carries it).
OpenAPI spec Import into Joule Studio / API tooling — the full zi18n_service contract.

Roadmap

Larger structural work lives in roadmap/. Two tracks:

Track Doc In one line
Distribute LISA as an ARC-1 extension roadmap/arc1-extension.md Shipped in v0.7.0packages/arc1-extension packages the 3 tools as in-process Custom_* tools loaded via ARC1_PLUGINS. See docs: ARC-1 extension deployment.
Share the auth layer (standalone) roadmap/shared-auth-module.md Shipped in v0.4.0 — LISA's in-tree XSUAA/BTP auth was replaced by a dependency on @arc-mcp/xsuaa-auth.

Project structure

LISA/
├── abap/                 # ⬅ ABAP handler to import (pick one folder for your platform; 3 files each)
│   ├── ABAP_PLATFORM_2022/   # ZCL_I18N_SERVICE — on-premise / private cloud (ABAP Platform 2022 / 7.57)
│   ├── ABAP_PLATFORM_2025/   # ZCL_I18N_SERVICE — on-premise / private cloud (ABAP Platform 2025+)
│   └── CLOUD/                # ZCL_I18N_SERVICE — BTP ABAP Environment / public cloud
├── docs_page/            # long-form documentation
├── roadmap/              # forward-looking design docs (planned, not implemented)
├── packages/
│   ├── core/             # @lisa-mcp/core — transport-agnostic wire contract (Zod schemas + ZCL_I18N_SERVICE wire logic)
│   │   └── src/          # wire.ts, schemas.ts, index.ts
│   ├── server/           # @lisa-mcp/server — standalone MCP server (published to npm, deployable on BTP)
│   │   ├── src/
│   │   │   ├── index.ts  # entry point
│   │   │   ├── handlers/ # tool registration (intent.ts), built on @lisa-mcp/core
│   │   │   ├── sap/      # transport.ts (HTTP to ABAP; BTP via @arc-mcp/xsuaa-auth/btp)
│   │   │   └── server/   # transport, config, logging (XSUAA OAuth via @arc-mcp/xsuaa-auth)
│   │   ├── templates/mta/    # deploy-from-npm MTA project + mta-overrides-*.mtaext.example landscape templates
│   │   └── .env.example      # local-dev config template (shipped in the npm package)
│   └── arc1-extension/   # @lisa-mcp/arc1-extension — the same 3 tools as an in-process ARC-1 plugin (published to npm)
│       └── src/          # Custom_* tool defs + index.ts (loaded via ARC1_PLUGINS)
├── mta.yaml              # BTP MTA descriptor (source-build path)
└── xs-security.json      # XSUAA config (authentication only)

Credits

LISA's authentication and BTP connectivity layer is provided by @arc-mcp/xsuaa-auth — the XSUAA/OAuth proxy + principal-propagation package extracted from ARC-1 by marianfoo; LISA's transport and overall architecture follow ARC-1's patterns. The translation service itself is built on SAP's XCO i18n generation APIs.

License

MIT © 2026 Clément Ringot.

LISA's XSUAA OAuth + BTP connectivity layer is provided by the MIT-licensed @arc-mcp/xsuaa-auth dependency (authored by the ARC-1 maintainers). A small remaining portion (the OAuth callback-proxy handler) is derived from ARC-1 and used under its MIT License — see the NOTICE file for the preserved upstream copyright and third-party attributions.