MCP serverMCP servertypeExposes tools to a coding agent over MCP.See every one →What this type is →independently maintainedindependently maintainedoriginPublished by one person or a small company outside SAP. Says nothing about quality; the readings say that.See every one →

bw-modeling-mcp

Reads, creates and modifies BW/4HANA modelling objects through the same internal APIs Eclipse BWMT and the BW/4HANA Cockpit use — and can run centrally on BTP with each caller reaching BW as themselves.

Four APIs rather than one, because BW is four things: the BW Modeling REST API at /sap/bw/modeling/ for objects, queries and live data; the ADT API for the ABAP and AMDP routines BW generates; the BW/4HANA manage API for the request monitor and runtime; and the Push API for data loads. Beside stdio it runs as an HTTP service on Cloud Foundry behind XSUAA, reaching on-premise, private-cloud and BW Bridge systems through a BTP destination — with principal propagation, so a caller reaches BW as themselves and BW applies their own authorisations rather than a shared technical user's. Two role collections, BW MCP Reader and BW MCP Developer, decide which tools a user is even offered.

Facts

Latestv1.8.1ReleasedLast pushLicenceMIT

Where it sits

Lives in
Provides
Agent access to your codeABAPBTPAgent access to your codecapabilityLetting a coding agent read the project or the system you actually have, reach its model, its objects and its checks, and act on it. In file-based environments that works out of the box; complex systems need dedicated access capabilities. Whatever the environment, the agent also needs project- and framework-specific context, so it understands the thing in front of it rather than guessing from what it remembers of its training data. The transport is incidental: an MCP server, a language server or a CLI answer the same question, and what differs between them is how much of the real thing the agent can see. Distinct from [MCP servers](capability:mcp-servers), which is a protocol adapter on your own running service, and from [agent guidance](capability:agent-guidance), which is knowledge handed over as files rather than read from the thing itself.Tool governanceABAPBTPTool governancecapabilityControlling what an agent may call, with which authorisations, and leaving an audit trail of what it did. The authorisation question that MCP exposure raises but does not answer.Analytic modellingABAPBTPAnalytic modellingcapabilityDefining what an analytical client actually reads — the multi-dimensional layer between stored rows and a chart: measures, dimensions, hierarchies, filters and parameters, and the associations somebody navigates them by. Distinct from [analytical queries](capability:analytical-queries), which is a service answering the request a chart sends, and from [loading a warehouse](capability:warehouse-loading), which fills the tables underneath it.Loading a warehouseABAPBTPLoading a warehousecapabilityGetting data into an analytical store and keeping it current — connections to source systems, replication and the other integration tasks, import jobs and their lifecycle, and the monitoring that tells you a load failed before a report does. Distinct from [replication](capability:replication), which is an application reading data that lives elsewhere as if it were local rather than a warehouse being filled.
Serves
Runs
Cloud Foundrylocal
Requires
SAP BW/4HANASAP BW/4HANAoutside the atlasEvery tool call reaches a live BW system through its modelling, ADT, manage and Push APIs; there is nothing to run against without one, on-premise, private cloud or BW Bridge.

Who is behind it

dnic-devdnic-devPersonMaintains this
In the atlas: maintains 1, contributes to 1
@dnic-dev
maintainerDavidNicolayNLPersonContributes to this
In the atlas: contributes to 1
@DavidNicolayNL
Marian ZeisPersonContributes to this
In the atlas: maintains 35, contributes to 86, wrote 25
@marianfoo
2 contributors

Before choosing this

Conditions

PracticalFunctional boundary

Its README says it plainly and so does this entry: this is not a simulation, every tool call reaches a live BW system, and write operations produce real changes. BW modelling objects are the definition of a warehouse — the blast radius of a wrong one is every report built on it.

PolicySAP API Policy 4.2026a§ 1.1, 1.2, 2.2.2from Agent access to your codeAn MCP server calls ADT or another SAP API of a running system for an agent.
  • § 1.1 Published APIs. Published APIs are made available for the documented purposes identified by SAP, including integration, extensions and data exchange.
  • § 1.2 Non-Published APIs. Customer and third-party applications must not access non-published APIs except where documentation permits it or SAP otherwise authorizes it.
  • § 2.2.2 AI call sequences and data extraction. Outside SAP-endorsed architectures, data services or expressly identified pathways, the policy prohibits specified agentic API use and systematic or large-scale extraction.

An agent that plans and executes ADT calls is the case section 2.2.2 names. Check that the endpoints are published for the intended use, and that the design runs within a pathway SAP identifies for it.

SAP API Policy 4.2026a →

What it declares

Every artifact

Type
MCP server
Origin
Independently maintained
Runtime
Node.js
Runs on
Cloud Foundrylocal

As an MCP server

Runs as
central serverlocal process
Transport
stdioHTTP
Authentication
user and passwordOAuth / OIDCprincipal propagationBTP destinationbrowser SSO
Tools
99
Read-only
nostates it is not
Guardrails
4 quoted from README show all
  • Two role collections decide what a user is offered: BW MCP Reader and BW MCP Developer.

  • A new tool stays unavailable to read-only callers until it is explicitly classified as a

  • XSUAA logs every login; with principal propagation the BW session log shows the real user

  • write operations produce real changes.

For agent access to ABAP code

Read
sourcesearchwhere-usedDDICCDStable data
Change
writecreateactivatetransportsgit
Check
syntaxATCABAP Unit
Run
run codedumps
property statedstated nonot said

Connections5

bw-modeling-mcp connectionsbw-modeling-mcpPROVIDES→ provides4provides stated Puts BW/4HANA's own modelling surface in front of an agent — objects, queries, the generated ABAP and AMDP routines, the request monitor and data loads — rather than a generic database or a read-only view of it. bw-modeling-mcp — README: https://github.com/dnic-dev/bw-modeling-mcpAgent access to your codeprovides authored Reads, creates and modifies BW/4HANA's own modelling objects and queries through the APIs Eclipse BWMT uses, so the warehouse's model is editable from an agent rather than only from its own tooling. bw-modeling-mcp — README: https://github.com/dnic-dev/bw-modeling-mcpAnalytic modellingprovides authored Reaches the loading side of BW as well as the modelling side — the Push API for data loads and the manage API for the request monitor and runtime. bw-modeling-mcp — README: https://github.com/dnic-dev/bw-modeling-mcpLoading a warehouseprovides stated Answers who the agent is rather than only what it may call: principal propagation so BW applies the caller's own authorisations, and two role collections deciding which tools each user is offered at all. bw-modeling-mcp — README: https://github.com/dnic-dev/bw-modeling-mcpTool governanceNEEDS→ needs1needs stated Every tool call reaches a live BW system through its modelling, ADT, manage and Push APIs; there is nothing to run against without one, on-premise, private cloud or BW Bridge. bw-modeling-mcp — README: https://github.com/dnic-dev/bw-modeling-mcpSAP BW/4HANA

Install

npm add bw-modeling-mcp

Derived from the package name. No recipe recorded; the README has the configuration.

Where bw-modeling-mcp runsbw-modeling-mcp — README, checked 2026-09-11: “Besides stdio, the server can run as an HTTP service on SAP BTP Cloud Foundry with XSUAA”stdio or HTTPdocumentedrequiresYour MCP client, actorYour MCP clientSAP BW/4HANA, SAP solutionSAP BW/4HANASAP solutionbw-modeling-mcp, modulebw-modeling-mcp
SAPnot SAP's, or the project's ownstated in the documentdocumented only

README

quoted from dnic-dev/bw-modeling-mcp under MIT, read
dnic-dev/bw-modeling-mcp/README.mdOn GitHub

bw-modeling-mcp

A Model Context Protocol (MCP) server that enables AI assistants like Claude to work directly inside SAP BW/4HANA or SAP BW 7.5 on HANA systems — reading, creating and modifying BW modeling objects via the same internal SAP APIs that Eclipse BWMT and the BW/4HANA Cockpit use: the BW Modeling REST API (/sap/bw/modeling/) for objects, queries and live data, the ADT API (/sap/bc/adt/) for the ABAP and AMDP routines BW generates, the BW/4HANA manage API for the request monitor and runtime, and the Push API (/sap/bw4/) for data loads.

This is not a simulation. Every tool call connects to a live BW system — write operations produce real changes.


☁️ Running on SAP BTP Cloud Foundry

Central MCP server for AI-assisted SAP BW modeling: MCP-capable AI clients connect via OAuth to bw-modeling-mcp, whose analyst, reader and developer roles reach on-premise, private cloud and BW Bridge systems via principal propagation

Besides stdio, the server can run as an HTTP service on SAP BTP Cloud Foundry with XSUAA OAuth in front and a BTP destination behind — either a shared technical user (BasicAuthentication) or principal propagation, where each caller reaches BW as themselves and BW applies their own authorizations.

Three role collections decide what a user is offered: BW MCP Reader (everything that only reads), BW MCP Analyst (a small reporting client — run queries and understand what they return) and BW MCP Developer (everything, including changes). stdio is unchanged — npm start behaves exactly as before. Setup: docs/CENTRAL-HOSTING-SETUP.md (step-by-step) and docs/CLOUD-FOUNDRY.md (reference).

What central hosting changes
Scenariostdio onlyHosted on BTP
One analyst, one BW systemruns on the analyst's machineserver-side, the analyst logs in with their own identity
Several analysts, one servernot possible, no central authall log in via BTP, each caller's identity reaches BW
Tool permissionsnone — whoever runs it can call every toolgranted per role, independent of BW authorizations: a BW developer can be read-only in the MCP, or the querying tools can be withheld from someone who may otherwise view data
BW authorizationsenforced through the user's own credentialsunchanged, still fully enforced — with principal propagation each caller acts as themselves, never as a shared identity
Audit traillimitedXSUAA logs every login; with principal propagation the BW session log shows the real user

A new tool stays unavailable to read-only callers until it is explicitly classified as a read, so the surface never widens by accident; write implies read, never the reverse. The three role collections are a starting point and can be split further in xs-security.json. Principal propagation additionally needs a certificate rule and ICM trust on the BW side.


System Compatibility

SystemSupport
SAP BW/4HANA (all versions)✅ Full support
SAP BW Bridge (SAP BTP ABAP stack)✅ Via cookie authentication (BW_COOKIE_FILE)
SAP BW on HANA (7.5)✅ Modelling reads after a small ABAP enhancement, and modelling writes for every object type except the InfoObject — aDSOs, InfoAreas, InfoSources, CompositeProviders, queries and their reusable components, aggregation levels. The tool surface adjusts to what the system publishes, and the objects without a REST resource — including planning, chain runs and the APD — are read from their metadata tables. bw_system_profile states per write tool what was verified there. See BW 7.5 Support

On SAP BW 7.5 the REST framework looks up the Accept header case-sensitively while the kernel delivers header names in lower case, so almost every call fails with HTTP 406. A ~20-line post-exit enhancement (no modification) resolves this and makes all REST endpoints that exist on 7.5 reachable. Objects for which BW 7.5 ships no REST resource at all — transformations, DTPs, process chains and their runs, classic DSOs, InfoCubes, the planning objects and the Analysis Process Designer — are readable through bw_read_metadata_tables, which goes to their metadata tables instead, but they cannot be written; Eclipse opens the embedded SAP GUI for those as well. Details, ABAP code and setup steps: bw75/BW75-SUPPORT.md.


A two-part blog series about this project (both available in German and English):

  1. Agentic AI meets SAP BW — the full story behind this project: why I built it, what's inside, what happens when Claude walks through a complete BW data lineage on its own. https://www.nextlytics.com/blog/agentic-ai-meets-sap-bw

  2. Agentic AI in practice: MCP server for SAP BW/4HANA — how the server is operated company-wide on SAP BTP Cloud Foundry with role-based access and per-user identity, plus two real customer projects. https://www.nextlytics.com/blog/agentic-ai-in-practice-mcp-server-for-sap-bw/4hana


🆕 What's New — v1.8.0 · Agentic Planning Modelling

An agent can now build a BW planning model end to end — aggregation level, planning functions, filters and the planning sequence that ties them together — and start the process chain that runs it.

BW/4HANA

🧮 Agentic Planning Modelling

  • New bw_create_planning_function / bw_update_planning_function — every standard function type, each one created and read back: copy, delete, repost, distribution by key and by reference data, set values, revaluation, formula (FOX), characteristic relationships, currency and unit translation, forecasting
  • Characteristic usage, conditions, parameters (constant or variable), key figure and data selections in the same call; what the function type may not change is refused before saving
  • New bw_create_planning_filter / bw_update_planning_filter and bw_create_planning_sequence / bw_update_planning_sequence; bw_delete removes both
  • Deeper reads: bw_get_planning_filter, characteristic relationships and data slices in bw_get_planning_properties, the function type with all its parameters and the block characteristics in bw_get_planning_function

💱 Currency translation and unit conversion types

  • New bw_get_conversion_type / bw_create_conversion_type — currency translation and unit conversion types with every option of the BW Modeling Tools: source and target fixed, from the record or from a variable; exchange rate type, rate and time reference; unit factors from T006, an InfoObject or SI units; bw_delete removes them
  • Not a planning-only object: a translation type created here serves queries, transformation rules and planning functions alike — until now none could be created outside the modeling tools

▶️ Process chains start on request

  • New bw_start_process_chain — starts a chain now, in the background; with wait_seconds it waits for the result, and the log ID leads straight into bw_get_process_chain_run_detail

🌳 Hierarchies

  • New bw_get_hierarchy, bw_create_hierarchy, bw_update_hierarchy, bw_delete_hierarchy — text and value nodes; replacing the tree keeps the hierarchy ID, so queries that use it keep working

🔁 Transformations on a transport

  • bw_create_transformation takes a transport request and can activate in the same call (#32)
On every release (BW/4HANA and BW 7.5)

📈 Query statistics without SAP GUI

  • New bw_query_statistics — executions, users, runtime percentiles and the slowest runs broken down by layer, read from the BW statistics; bw_query_data with with_statistics measures its own call (#44)

🔤 Formula and text variables

  • bw_create_variable creates formula variables (dimension, default value, unit or currency) and text variables (replacement path or exit); text variables in descriptions are bound so they resolve at runtime instead of showing &NAME& (#47)

🔍 Query data that says what happened

  • bw_query_data filters hierarchy nodes, keeps interval bounds, says whether more rows follow and lists what BW did not apply (#45); bw_get_query shows display hierarchies

🛠️ Fixes

  • A short dump no longer leaves the server unable to lock; amount key figures activate; creates work behind a BTP destination and release their lock when they fail; master system read from the system (#34); rate limit per client on Cloud Foundry (#35); dependency advisories cleared (#40)

Thanks to @MWKAnalytics and @oliverluette for the analyses behind #44, #45 and #47.


Earlier releases — the "What's New" notes for v1.7.0 and older are archived in WHATS_NEW.md; the full structured history is in CHANGELOG.md.


What it can do

An overview by area. Every tool in detail — parameters, behaviour, and the sequences it belongs in — is in the Tools Reference (123 tools).

What the server can read, create and run in SAP BW, by object area, and how much of it works on classic SAP BW 7.5 on HANA


Combining with an ADT MCP Server

bw-modeling-mcp works best alongside an ADT MCP server such as vibing-steampunk or ARC-1. The two do not overlap as much as it may look.

This server owns the BW object and, with it, the body of the ABAP that BW generated for that object: the class behind a transformation routine, the program behind a DTP filter routine. Write those through bw_set_transformation_routine, bw_set_transformation_expert_routine and bw_set_dtp_filter_routine rather than through ADT — they do not only replace the source, they save the transformation master back afterwards, which is what re-registers the code in the transportable metadata. A class-only edit survives until the next regeneration or transport and is then gone.

The ADT MCP server covers ABAP as a subject in its own right: your own reports, classes, function modules and DDIC tables, repository search and navigation, arbitrary table reads, debugging, ATC, unit tests, dumps and transports. Together they cover the full cycle from BW object to ABAP logic.


Requirements

  • SAP BW/4HANA system with the internal SAP APIs enabled (SAP BW 7.5 works for modeling reads and most modelling writes once the enhancement in bw75/BW75-SUPPORT.md is in place)
  • Node.js 18 or later
  • An MCP-compatible AI client (Claude Desktop, Claude Code, etc.)

Installation

Two ways to run. Locally as a stdio server (one user, one machine — the steps below), or centrally hosted on SAP BTP Cloud Foundry behind XSUAA OAuth for a whole team → see docs/CENTRAL-HOSTING-SETUP.md. The installation and configuration below cover local stdio use; upgrading an existing local setup is non-breaking.

# Option 1: Install via npm (recommended)
npm install -g bw-modeling-mcp

# Option 2: Clone and build
git clone https://github.com/dnic-dev/bw-modeling-mcp.git
cd bw-modeling-mcp
npm install
npm run build

Configuration

For local (stdio) use, the server is configured via environment variables. For central BTP hosting, connection and credentials come from the BTP destination and service bindings instead — see docs/CENTRAL-HOSTING-SETUP.md.

VariableDescriptionRequired
BW_URLBW system URL (e.g. https://myhost:50001)yes
BW_USERSAP user nameyes (or BW_COOKIE_FILE)
BW_PASSWORDSAP passwordyes (or BW_COOKIE_FILE)
BW_CLIENTSAP client (e.g. 001)yes
BW_LANGUAGELanguage for object texts (e.g. EN, DE). Default: DEno
BW_COOKIE_FILEPath to a browser-exported cookie file for SAML-/OAuth-fronted systems (e.g. BW Bridge). Netscape or name=value format. When set, BW_USER / BW_PASSWORD are optional.no
BW_MCP_SERVER_NAMEServer name advertised in the MCP initialize handshake. Default: bw-modeling-mcp. Give each instance a unique name when running several against different BW systems.no
BW_MCP_SYSTEM_LABELFree-text label of the connected BW system (e.g. AP4 (BW production, read-only)), put at the top of the MCP server instructions. Lets a model tell look-alike instances apart even in clients that show an opaque connector id instead of the server name.no
BW_PLATFORMauto (default), classic or bw4. The server detects whether it is talking to BW/4HANA or a classic release and offers only the tools that release can answer. classic forces that verdict when detection cannot run, bw4 switches the filter off. See bw75/BW75-SUPPORT.md.no

Cookie authentication (BW Bridge / SAP BTP): For BW systems that sit behind a SAML or OAuth login (such as BW Bridge on the SAP BTP ABAP stack), Basic Auth is not available. Export the authenticated session cookies from your browser into a file and point BW_COOKIE_FILE at it. The login/session approach is analogous to vibing-steampunk and ARC-1. When the session expires, refresh the cookie file and restart the server.

Claude Desktop

Add to claude_desktop_config.json:

{
  "mcpServers": {
    "bw-modeling-mcp": {
      "command": "node",
      "args": ["/path/to/bw-modeling-mcp/dist/stdio.js"],
      "env": {
        "BW_URL": "https://your-bw-host:50001",
        "BW_USER": "YOUR_USER",
        "BW_PASSWORD": "YOUR_PASSWORD",
        "BW_CLIENT": "001",
        "BW_LANGUAGE": "EN"
      }
    }
  }
}
Claude Code (VS Code extension)

Add .mcp.json to your project root:

{
  "mcpServers": {
    "bw-modeling-mcp": {
      "command": "node",
      "args": ["/path/to/bw-modeling-mcp/dist/stdio.js"],
      "env": {
        "BW_URL": "https://your-bw-host:50001",
        "BW_USER": "YOUR_USER",
        "BW_PASSWORD": "YOUR_PASSWORD",
        "BW_CLIENT": "001",
        "BW_LANGUAGE": "EN"
      }
    }
  }
}

How it works

The server talks to four SAP APIs, and only one of them needs a protocol worth describing.

The BW Modeling REST API (/sap/bw/modeling/) is a full-document API: there is no way to change one attribute of an object. Every write therefore runs the same six steps.

  1. Lock — acquires an exclusive lock and returns a lockHandle
  2. Read — fetches the current complete XML of the object
  3. Modify — applies the change to that XML
  4. PUT — sends the whole document back, never a fragment
  5. Activate — promotes the inactive version to the active one
  6. Unlock — releases the lock

Two consequences are worth knowing. Saving and activating are separate: a document the server accepts can still fail to activate, so a successful write proves less than it appears to. And because the whole document travels, a read that came from a stale session buffer will silently write old values back — the tools read fresh for exactly this reason.

The other three need none of it. The ADT API (/sap/bc/adt/) is used narrowly and always for an object BW generated itself: the body of a transformation routine (its generated class), the body of a DTP filter routine (its generated program), the DataPreview service behind bw_read_metadata_tables, plus transport checks and activation runs. Everything else ABAP — writing your own reports, classes or DDIC tables, searching the repository, reading arbitrary tables, debugging — is the job of an ABAP ADT MCP server alongside this one, see Combining with an ADT MCP Server. The BW/4HANA manage API (/sap/bc/http/sap/bw4/) answers the request monitor, the remodeling monitor and runtime operations. The Push API (/sap/bw4/v1/push/) takes a JSON record array straight into a write-interface aDSO.

Session cookies and CSRF tokens are handled for all four.

See ARCHITECTURE.md for the full technical architecture and the complete endpoint reference.


Roadmap

  • Focused mode — an optional, reduced tool surface: a handful of broad tools in place of the full set, for clients and models that work better with fewer tools to choose from.

Contributing

Issues and feature requests are welcome — please use the Issue templates.

If you have access to a BW/4HANA system and want to help expand coverage, I am happy to hear from you. The best way to contribute is to try it out and report what works, what doesn't, and what's missing.


License

MIT

MIT, read

History

Latest

v1.8.1

Fixes 1

Notes
Fixed
  • Copilot Studio could no longer sign in after 30 days — a client that registers itself with the hosted server received a client_id valid for 30 days. Copilot Studio keeps its client_id and does not register again when it is refused, so once the 30 days had passed every sign-in ended with HTTP 400 at /authorize; neither restarting the server nor recreating the service bindings helped, only recreating the connection in Copilot Studio. Registrations no longer expire by age. Revoking them all at once still works by rotating the signing secret. A connection already locked out has to be recreated once; after that it keeps working.
  1. v1.8.0

    Features 16, Fixes 13, Other 1

    Notes
    Added
    • Planning functions can be changed and created — bw_update_planning_function sets the characteristic usage (FIELDS_TO_BE_CHANGED, FIELD_FOR_CONDITIONS, NONE), adds, replaces and removes conditions, sets parameters — a constant or a variable, the FOX code of a FORMULA function, the key figure selection, a key figure parameter and the data selections of a copy, repost or distribution — row by row in a table parameter, adding rows as needed, and changes the description. bw_create_planning_function creates a function of any type on an aggregation level and applies the same settings in the same call. Until now a planning function could only be read, so a wrong usage had to be corrected by hand in the modeling tools — typically the one fix an exit function needs after its code has already been changed. The function is saved straight to the active version. The answer says whether anything was saved, because the backend does not treat errors alike: an invalid value rejects the save, while a FOX syntax error is reported as an error and the function is stored with it. Verified on BW/4HANA by creating and filling one function per standard type, read back through the REST resource and the metadata tables: copy and copy without zeros, delete, repost, distribution by key (several target rows with factors) and by reference data, set values, revaluation, formula and formula with zero values, create characteristic relationships, the characteristic-relationship and DataStore variants of delete and repost, currency and unit translation with their source and target key figures and translation types, and forecasting with its time characteristic, horizons and strategy; conditions and parameter values from variables as well. A function name longer than the 20 characters BW allows is refused before anything is written. A characteristic the function type may not change on the level — a forecast changes only its time characteristic — is refused before saving, with the ones that are changeable; on a CompositeProvider whose time characteristic carries the provider prefix, BW offers none for a forecast. On a classic release the planning resource does not exist and both tools are hidden.
    • Formula variables — bw_create_variable creates them with variable_type: "Formula": a dimension, an optional default value, and the unit or currency the dimension needs; bw_update_variable changes the default value, unit and currency; bw_get_variable reports all of them. A formula variable is an operand in query formulas (bw_update_query_key_figures) and in CKFs (bw_create_ckf, bw_update_ckf); bw_get_ckf and bw_get_query name it there. Three things BW accepts without complaint are handled: a default value is read in the logon user's decimal notation (with a comma notation "2.5" is stored as 25), so the tool reads that notation and converts; a quantity without a unit or an amount without a currency saves as consistent but no query using it can be generated, so the tool requires them; and the variable carries the default hint the query generator expects. Verified on BW/4HANA (create, update, CKF and query formula, query execution) and on a classic release (create). Thanks to @oliverluette for the analysis in #47.
    • Text variables — bw_create_variable creates them with variable_type: "Text": by replacement path from a characteristic or from a characteristic variable on it, showing its key, external key or text, a bound of a range and part of the value, or filled by a customer exit or user entry. Written as &NAME& into the description of a query, a structure member, or a reusable CKF, RKF or structure, the variable is replaced at runtime. BW does that only when the description also lists the variable and the query carries a copy of it; with the name alone the query saves as consistent and shows &NAME& literally. Every tool that saves a query or a reusable component now binds the text variables its descriptions name. A text variable by replacement path is never made ready for input, which BW also saves as consistent while no query using it can be generated. bw_get_variable reports the replacement settings, bw_update_variable refuses what a text variable cannot have, and bw_get_query names a text variable used in a description once. Verified on BW/4HANA: the member "Gross Sales &NAME&" runs as "Gross Sales Juices", a CKF described "Sales &NAME&" as "Sales PC2" in a query that uses it. Thanks to @oliverluette for the analysis in #47.
    • Query statistics — bw_query_statistics reads the BW statistics (RSDDSTAT) of any frontend without SAP GUI: the executions of a query or provider in a time window with their count, the last one and its user, the executions per user, min, median, p90 and max runtime (over the latest 5,000 when there are more), and the breakdown of the slowest ones by layer and event — OLAP, cache, authorizations, database, input help, planning, MDX and OLAP BAPI, remote calls and front end — with records read and transferred and the effective "Operations in SAP HANA" setting. bw_query_data with with_statistics: true measures its own call and finds the BW step behind it, so a slow call can be split into BW runtime and transfer. The tables are read through ADT DataPreview; statistics must be switched on for the object. A direct provider call is found whether its front end recorded it under ""or"" or "!1". Verified on BW/4HANA, for a query with more than 36,000 executions a week and a direct provider call, and on a classic release. Thanks to @MWKAnalytics for the analysis in #44.
    • Planning filters, characteristic relationships, data slices and function types — bw_get_planning_filter reads the filter a planning sequence step works on: the restriction per characteristic and the variables it uses. bw_get_planning_properties adds the characteristic relationships of the provider — type, exit class, whether they derive or only check, source and target — and its data slices with type, active flag, exit class and selection, read from the metadata tables because the REST resource reports neither the selection nor the active flag. Where the backend fails to build the planning properties, which happens for a provider with an exit data slice, the tool reads them from the tables instead. bw_get_planning_function ends with its function type: the class, and every parameter the type takes with InfoObject, default and whether variables are allowed. Verified read-only on BW/4HANA, on standard and custom function types and on providers with attribute and exit relationships.
    • Planning filters and planning sequences can be created, changed and deleted — bw_create_planning_filter creates a filter on an aggregation level with fixed values, ranges, exclusions and characteristic variables, bw_update_planning_filter changes its restrictions and its description; on a level over a CompositeProvider the bare characteristic name is accepted for the prefixed field. bw_create_planning_sequence creates a sequence with its steps in execution order — planning functions with their filters, and input templates — and bw_update_planning_sequence replaces the steps or changes the description. bw_delete deletes both (plsq, filter); a filter still used by a sequence is refused with the sequence named. The protocol follows traces of the modeling tools. Verified on BW/4HANA: filters on a CompositeProvider level and on an aDSO level, a sequence created with three steps, reordered and changed repeatedly in one session, and every object deleted. A sequence cannot be executed: the modeling tools open the embedded SAP GUI for that, and there is no REST resource behind it.
    • Currency translation and unit conversion types — bw_get_conversion_type reads one, bw_create_conversion_type creates one with every option of the modeling tools: source and target fixed, from the record or chosen when converting, from an InfoObject or a variable; for a currency the exchange rate type fixed or from a variable, the exchange rate from the rate tables or an InfoObject, buying, middle or selling rate, inverse, transformations only, and the time reference — current date, fixed date, date variable, query key date or a time characteristic with reference point, shift and period handling; for a unit the factors from T006, an InfoObject or SI units, singly or in either order, and the flags for an initial source unit and a missing factor. Until now neither could be created outside the modeling tools, so a currency or unit translation planning function had nothing to refer to on a system without them. Verified on BW/4HANA: every option created, read back and checked as consistent by BW; the protocol follows a trace of the modeling tools. A time shift from a variable and a quantity attribute of an InfoObject are written as BW reads them but were not tried on a system. bw_delete with object type ctrt or uomt deletes one the way the modeling tools do — lock, delete and unlock on the object path — and releases its lock also when the delete is refused; BW deletes a conversion type even when a planning function still refers to it.
    • Process chains can be started — bw_start_process_chain starts a chain once, now, as the BW/4HANA Cockpit does with "Execute": asynchronously, in the background under the technical user of the chain, independent of its start condition and without changing its scheduling. The answer carries the log ID of the run, its status and the messages of the steps reached so far, and names bw_get_process_chain_run_detail with that log ID for the step details. With wait_seconds the tool waits up to ten minutes for the run to end, checking every five seconds. The synchronous start the Cockpit also offers is deliberately not available: it runs the chain inside the HTTP request, where an abort message of a step cannot be shown, so the request fails and the run is left reading "active". The protocol follows a trace of the Cockpit. Verified on BW/4HANA: runs started with and without waiting and read back through the run detail, and an unknown chain refused by BW with its own message. A classic release has no such API, and the tool is hidden there.
    • Hierarchies — bw_get_hierarchy lists the hierarchies of a characteristic and reads a tree with node types, names and texts; bw_create_hierarchy creates one from a nested tree of text and value nodes and activates it; bw_update_hierarchy changes its description and replaces its tree, keeping its id so queries that use it keep working; bw_delete_hierarchy removes it or a pending revised version. Until now a hierarchy could only be maintained in the maintenance app. Verified on BW/4HANA, also with every step in a session of its own, which is where a lock would show; a classic release has no such API, and the tools are hidden there.
    • bw_create_transformation takes a transport request and can activate — transport records the new transformation on the given request, which a transportable package needs; activate: true activates it in the same call with the create's own lock, and success then reflects the activation. Verified on BW/4HANA in $TMP and in a transportable package, where the object landed on the named request. Suggested in #32.
    Improved
    • bw_get_dtp answers with the summary alone — every answer carried the complete XML of the DTP after the summary, some ten times its length, and showed escaped characters such as -> as ->, also in selection values and routine code. The XML now comes only with format: "raw".
    • The planning read tools answer with the summary alone — bw_get_aggregation_level, bw_get_planning_function, bw_get_planning_sequence and bw_get_planning_properties appended the complete XML to every answer, for an aggregation level some 40,000 characters. It now comes only with format: "raw". The documentation of a planning function shows its line breaks instead of 
, and a sequence step says whether it runs a planning function or only opens its filter for input.
    • bw_query_data says whether more rows follow — BICS reports no total row count, so a page that happened to end where the result ended looked complete, and so did one that did not. The text answer now reads one row beyond the page and says More rows: yes — continue with from_row=… or More rows: no. An empty result reports the requested row range instead of a fixed one.
    • The state description of bw_query_data says what a state does to the layout — it asked for every InfoObject of the query, FREE ones included. Characteristics staying on FREE need not be listed; rows and columns, however, show only what the state lists there, and a state without the key figure structure returns no data. Based on #44.
    • bw_get_query shows the display hierarchy of each characteristic — its name or variable, whether it is active or only assigned, version, key date, and the display options that differ from the default. Until now whether a characteristic in the layout had a hierarchy was not visible in the query definition the tool returned. Names such as 0001 and key dates such as 00000000 are read literally instead of as numbers.
    • bw_get_planning_function names the block characteristics — every characteristic that is not to be changed, with the reminder that the function runs once per combination of their values. Records that differ in one of them never reach the function together, and the plain usage list made that easy to miss: an exit change meant to merge such records has no effect until the usage is changed. Table parameters also show the key figure of each row, so the values next to them can be told apart.
    Fixed
    • A short dump left the server unable to lock anything — after a request ended in a runtime error, the next lock ran without a session context and was gone before the request that used it ("lock handle could not be created", HTTP 423), until the server was restarted. The session cookie and the CSRF token of the ended session are now dropped together with its context id, so the next request logs on afresh.
    • bw_create_infoobject created amount key figures that could not be activated — a key figure with object_specific_data_type: "CURR" was written with the key figure type AMT, which BW accepts on create and refuses at activation ("invalid type AMT"). It is now AMO, as BW expects.
    • A runtime error in BW was reported as an inactive service — when a request failed on the server with an HTML error page such as "Application Server Error", the answer said the service was not reachable (ICF service inactive, or the logon rejected). It now says that BW failed while processing the request and that ST22 shows the short dump. bw_get_planning_properties meets this for providers with an exit data slice and names bw_read_metadata_tables with PLCR or PLDS as the route that reads them.
    • bw_query_data filtered hierarchy nodes as plain members — a filter value with nodeId: 1 returned "No data available" or "Invalid filter … Filter changed", because the reporting endpoint selects a node only through its node type, which was not sent. The new nodeType sends it: 0HIER_NODE for a text node, the characteristic (the default) for a value node; the node goes in lowInt, as bw_get_hierarchy shows it. An interval on an internal key also lost its upper bound and arrived as a single value; the upper bound now goes in the same format as the lower one. Thanks to @MWKAnalytics for the analysis in #45.
    • bw_query_data did not say what BW left out — BW applies some state requests differently without an error: an excluded hierarchy node comes back included, a hierarchy given in the state is ignored in favour of the one in the query definition, and a filter value can disappear from the effective selection. A Not applied section in the text answer now lists each of these, as well as a characteristic BW placed on another axis. Based on #45.
    • An ampersand in a CKF or RKF description was escaped twice — bw_update_query_key_figures with add_ckf or add_rkf copied the component's description into the new member still escaped and escaped it again, so "R&D" became "R&D". bw_query_data also showed &, <, > and quotes in member and value texts as XML entities.
    • A transformation could not be created behind a BTP destination — the create tools wrote the user from BW_USER as the object's responsible, and behind a destination, with principal propagation or a technical user, that variable is unset. A transformation with an empty responsible is refused with "model deserialization failed"; other object types accepted it, which is why only some creates failed. The user is now read from the system itself when BW_USER is not set, for every create tool.
    • A failed create left its lock behind — when the create of a transformation, an InfoObject or an InfoArea failed after the lock was taken, the lock stayed until the session timed out, and every further attempt on the same object was refused as locked. It is now released whenever the create fails.
    • A dropped connection failed a whole read — a read that the server had already closed the connection for failed with ECONNRESET, and tools that walk a tree with many requests in a row, such as bw_list_datasources, failed as a whole on one such request. A read is now retried once on a dead connection; writes are not.
    • The preview of a DataSource without data failed — bw_preview_datasource answered "Unexpected end of JSON input" when the system returned no rows, as for a file DataSource whose file is not on the application server. It now reports no data.
    • The master system of new query components came from the URL host — bw_create_rkf, bw_create_ckf, bw_create_structure and bw_create_variable derived it from the host name in BW_URL, which says nothing about the system behind a destination or a proxy. They now read the system's logical system name, as the other create tools do. Reported in #34.
    • Behind Cloud Foundry all clients shared one rate limit — the OAuth endpoints limit requests per client address, and on SAP BTP that address was the load balancer in front of the app, so one busy client could lock every other user out of the login for an hour. The server now trusts the two proxy hops in front of the app on Cloud Foundry and none elsewhere; BW_TRUST_PROXY_HOPS overrides that. Reported in #35.
    • Dependency advisories — the lockfile is refreshed within the declared ranges, so npm audit no longer reports the advisories a refresh can clear: among them IP spoofing through the proxy trust in proxy-addr, which the trusted proxy hops on Cloud Foundry rely on, and the OAuth client advisory of the MCP SDK (now 1.32). What remains comes in through the SAP Cloud SDK, or needs a major version of an XML parser, and has no fix within the declared ranges. Reported in #40, based on #41.
    Notes
    • Released as Agentic Planning Modelling: with planning functions, filters, sequences and conversion types an agent can build a planning model end to end, and with bw_start_process_chain start the chain that runs it.
  2. v1.7.1

    Fixes 1

    Notes
    Fixed
    • A variable used as the key date of a query looked unused — bw_get_query did not read the key date, so a variable set there was listed among the variables of the query but referenced nowhere else in the answer, and a reader concluded it could be deleted. The settings now carry the key date — the system date, a fixed date, or a variable with its name and description — and every variable lists where the query uses it: the filter, a default value, the key date, a structure member, a formula, a calculated or restricted key figure, an exception, a condition, a cell, or a text. The references are collected from the whole document, so a use in a part of the query the reader does not interpret is still found and named; a variable referenced nowhere is marked as such. Verified on BW/4HANA with a key date variable set in the modeling tools.
  3. v1.7.0

    Features 26, Fixes 21, Other 6

    Notes
    Added
    • Field groups and name usage on CompositeProviders — bw_update_composite_provider has two new actions. update_fields moves existing fields into a field group and/or switches how an InfoObject-bound field is exposed to queries: name_usage "direct" (the InfoObject is used directly by name) or "unique_name" (the field answers to <prefix>-<FIELD>). update_group creates a group, changes its label or renames it together with its fields. add_field, add_input and update_mapping take name_usage and dimension for the fields they create, per call or per mapping; a group other than the two defaults has to be declared first, and an unknown one is refused with the list of declared groups. Verified on BW/4HANA and on a classic release.

    • Cell definitions in queries — bw_update_query_cells adds, changes and removes the cells of a query with two structures: reference cells that make the value at an intersection addressable, formula cells at an intersection, and help cells outside the grid, as a formula (a unit-free denominator, say) or as a selection with its own key figure and restrictions. Members and cells are addressed by description; a formula operand naming an intersection without a cell gets its reference cell in the same save. Until now a new row in a query with a computed column needed the Query Designer for every cell of that column. Decimals and scaling on a cell are entered in the query's priority lists as the modeling tools do, which is what lets them win over the two members' settings. Verified on BW/4HANA and on a classic release.

    • InfoPackages are readable — bw_read_metadata_tables takes object_type="ISIP". No release publishes a REST resource for the InfoPackage and BW/4HANA dropped the object type, so the metadata tables are the only route. The answer carries the DataSource and source system, the file settings with the separator and escape characters decoded from their hex form (the raw value alongside), the selections — fixed ranges as well as dynamic time selections, OLAP variables and ABAP routines, with the routine code — deletion settings, the process chains that run it, and the load history with status, update mode, user and records per request. A file read from the local workstation is flagged as unschedulable, since such an InfoPackage cannot run in the background or in a process chain. A DataSource name instead of an InfoPackage id lists the InfoPackages of that DataSource.

    • Exception aggregation on reusable CKFs — bw_create_ckf and bw_update_ckf take exception_aggregation: a type, one to five reference characteristics and optionally exclude; bw_update_ckf resets it with false and leaves the formula alone. This is what a weighted average needs — a helper CKF "unit value × quantity" summed over the finest characteristic, divided by the quantity. Without it BW multiplies values already summed up, which gives a plausible and wrong number. Setting it on the query member that shows the CKF is no substitute: another CKF referencing the helper computes with the helper's own definition.

    • Scaling and local calculation on structure members — properties on bw_update_query_key_figures and bw_update_structure takes scaling (a power of ten, 0 to 9) and calculation: result_as ("calculate results as"), single_values_as ("calculate single values as"), apply_to_result and cumulation, each reset with false. This is what completes the member pattern of a planning query, where a price is neither summed up in the result nor shown without its own precision. The literals are those of the query model — fourteen for results, thirteen for single values, "mininum" spelled as the model spells it — and every one was written and read back on a classic release and on BW/4HANA. A partial calculation keeps the fields the member already has.

    • Analysis processes in the where-used list — on a classic release, bw_xref on an aDSO, classic DSO, InfoCube, MultiProvider or InfoObject lists the analysis processes that write to it (upstream) or read from it (downstream). The where-used index of the backend leaves them out, so a DSO filled by an analysis process looked like a source without origin. They are read from the analysis process's own dependency table through ADT DataPreview; where the calling user has no ADT access, the list is returned without them and says so in one line instead of failing. BW/4HANA has no analysis processes and is not queried.

    • Aggregation levels in the where-used list — bw_xref on a provider lists the aggregation levels built on it as upstream, since planned values are written into the provider through them, and bw_xref with object_type=ALVL lists the InfoProvider underneath (downstream) and the planning functions on the level (upstream). The where-used index misses the level entirely on a classic release, lists it without a direction on BW/4HANA, and never names the provider below a level, so the planning input of a provider was invisible to a lineage followed hop by hop. Read from the aggregation level and planning function definitions through ADT DataPreview; without ADT access the answer says what was not checked. Verified on BW/4HANA and on a classic release.

    • The filter of a DTP on a classic release — an optional helper endpoint (two ABAP classes and one ICF service, in bw75/) unpacks the filter definition a classic release stores as a serialised object: selections, the filter routine with its code, the semantic group and the package sizes. bw_read_metadata_tables with object_type="DTPA" reports them where the endpoint is installed and names them as unreadable where it is not, and bw_system_profile says whether it was found. BW/4HANA reads the DTP over REST and does not use it. Installation and rollback are described in bw75/README.md.

    • Analysis processes by name pattern — bw_read_metadata_tables with object_type="ANPR" and a pattern such as "Z*" lists the matching analysis processes with description, version and last change. The BW search index does not contain them on any release.

    • Currency and unit conversion in transformation rules — bw_update_transformation takes conversion on a direct rule: type "currency" with a currency translation type or "unit" with a unit conversion type, given as name, and optionally the source field that holds the source currency or unit. The name is checked against the conversion types defined on the system and an unknown one is refused with the list of available types; a conversion type without a fixed source is refused unless the source currency or unit can be found, since BW would otherwise be left with an empty input and the rule would not activate. The transformation's currency/unit handling is switched on. The rule wiring was verified on BW/4HANA; the conversion itself could not be run end to end, because the test system defines no conversion types.

    • Aggregation type of transformation rules — bw_update_transformation takes aggregation (SUM, MOV, MIN, MAX, NOP) for a rule into a target key figure. It decides whether several source records that end up in one target record are summed or overwritten; a key figure left on MOV in an aggregating transformation keeps one record per group, without an error or a log entry. Given alone it changes only the aggregation, given with a rule change both are saved together; a characteristic, an unknown field and a field whose aggregation the target fixes are refused. bw_get_transformation, and bw_read_metadata_tables with object_type="TRFN" on a classic release, show the aggregation type per rule. Verified on BW/4HANA, including activation.

    Improved
    • bw_get_composite_provider shows name usage and field groups — every InfoObject-bound field carries its name_usage, the declared groups are listed with label and field count, and the field summary counts ungrouped fields and fields a query can only find under their unique name. Whether a CompositeProvider is ready for a query can be checked without the modeling tools.

    • bw_create_adso reports the type it created — the result names the aDSO type the server kept (adso_type) and warns when it differs from the requested one.

    • bw_update_query_* and CKF/RKF/structure updates say how to repair an inconsistent save — the error names the follow-up call that undoes the change (remove_member with the member_id from bw_get_query).

    • bw_xref says which way a data flow points — every transformation and DTP hit carries its source and target, type and name, and its direction relative to the object asked about: upstream (feeds it) or downstream (fed from it), with a count of each in the summary line. Until now the direction had to be read out of the description, which arrived HTML-escaped. DTP titles name no types; they are taken from a transformation with the same ends. A master data flow names the part of the InfoObject it loads ([TEXT], [ATTR], [HIER]), and an analysis process listed under a query is marked as reading from it. Tracing a flow back to its DataSource is now a matter of following the upstream hits, on BW/4HANA and on classic releases alike.

    • The field rules of an analysis process are readable — bw_read_metadata_tables with object_type="ANPR" lists, per edge, which source field goes to which target field and which target fields are set to a constant, constants first. That is typically where a target version or plan category is set, and it was not visible until now.

    • Classic providers list their fields with meaning — bw_read_metadata_tables with object_type ODSO, CUBE or MPRO gives each InfoObject its kind (characteristic, time characteristic, key figure, unit, technical), data type with length and a text in the logon language, read for the whole list at once. InfoCubes and MultiProviders list characteristics and key figures apart; until now the key figures of a cube were recognisable only by standing in no dimension. A MultiProvider names, per InfoObject, the part providers it is identified in — which is what separates, say, an actuals part from a plan part.

    • Global declarations of transformation routines — bw_read_metadata_tables with object_type="TRFN" shows the global part of the routines (both sections) ahead of the routines themselves, or (none). A routine that buffers a lookup across data packages declares the buffer there, and its table kind and key decide how the lookup behaves. On BW/4HANA, bw_get_transformation points to the class definition that holds them.

    • Structure members name what they show — bw_get_query prints, per selection member, the key figure or calculated/restricted key figure behind it and its characteristic restrictions (Selection: …). The query's component UID is in the header, and the provider comes with its type on classic releases too (MultiProvider, InfoCube, classic DSO) instead of "Unknown", with the TLOGO alongside.

    • The fields an analysis process reads from a query — bw_read_metadata_tables with object_type="ANPR" resolves the generic key figure fields of a query source (KYF_0001 …) to the structure element and the key figure or calculated key figure behind it, by element UID rather than by position.

    • An empty DataSource setting is stated — bw_get_datasource shows a DataSource without a delta process as none (full loads only), and one without an active adapter says so and, for a classic file DataSource, points to its InfoPackages, which hold the file settings there.

    • MultiProvider parts carry their type — bw_read_metadata_tables with object_type="MPRO" names each part provider with its type (CUBE, ODSO, ADSO, …), ready to be passed on. Reading a provider under the wrong type, a DSO as an InfoCube say, now names the type it does exist as.

    • The metadata reader is described compactly — the description of bw_read_metadata_tables is a short list of object types and what each returns, with the details moved to the object_type parameter; clients that cut long descriptions off had lost the analysis process and InfoPackage entries. The server instructions on a classic release list analysis processes and InfoPackages, ahead of the other routes.

    • Cell definitions are readable — bw_get_query lists each grid cell with the descriptions of the two members it sits on, help cells with their formula or selection, and cell formulas with their operands as those labels. Until now it gave the member ids of each grid cell, rendered cell formulas with bare cell ids, and read formula help cells as if they were selections — so matching a cell to a row meant looking the ids up in the metadata tables.

    • The local calculation of a member is visible — bw_get_query reports calculation per member in the field names the write tools take, in the text form as resultAs=…, singleValuesAs=…, applyToResult=… and cumulated, and the scaling as scaling=N. Until now a member suppressing its result looked no different from any other.

    • Exception aggregation is visible — bw_get_ckf reports it (null for standard aggregation) in the shape the write tools take back unchanged, and bw_get_query reports it per member, in the text form as exc.agg=SUM(CHARACTERISTIC). A member that shows a CKF reports the CKF's own setting beside its own, as does the calculated key figure section. Until now neither reader said anything about it, so a weighted average could not be checked without reading the metadata tables.

    Fixed
    • A writing tool ignored parameters it did not know and reported success — a misspelt or invented parameter, an aggregation passed to bw_update_transformation say, was dropped and the call still answered success: true, so a change that never happened looked done. Every writing tool now refuses a parameter it does not declare, changes nothing and names the parameters it takes.

    • bw_update_transformation dropped the transport request on a direct rule — the default path (rule_type="direct") saved the transformation without the transport it was given, while every other rule type passed it on.

    • A CompositeProvider built through the tools could not be used in a query on a classic release — its InfoObject-based fields were created with the system-wide unique name instead of direct usage of the InfoObject, so a query did not find them ("characteristic … is not (or no longer) in the InfoProvider", "InfoObject … is not in the InfoProvider"). Fields named after their InfoObject now use it directly on every release, and they are sorted into the groups CHARACTERISTICS and KEYFIGURES instead of staying ungrouped. An existing CompositeProvider is repaired with update_fields.

    • Auto-mapping mapped technical fields of the source — the currency/unit dimension of an aDSO was mapped like an ordinary field, and activation of the CompositeProvider then failed in the generated column view ("attribute … is missing in node …"). Technical fields are left out, as the modeling tools leave them out; an explicit mapping still passes.

    • A query saved with errors could not be repaired — every follow-up change of such a query failed on a classic release with HTTP 500 ("PARSE_MODEL cannot process XML element messages"), so the only way out was deleting it. Follow-up calls now save normally; the same applies to CKFs, RKFs and structures.

    • bw_get_adso named four of six aDSO types wrongly — a data mart aDSO was shown as "staging, inbound queue only", an inbound-queue-only one as standard, and the two other staging types were swapped. The type now matches the adso_type preset that creates it, and the preset key is shown with it. On a classic release a compressing staging aDSO and a standard aDSO without change log carry the same settings; both are named then. Creating an aDSO with a preset was correct all along.

    • A refused delete left the object locked — when BW refused bw_delete, for example because a query still uses the object, the lock was not released and blocked every later change until the session timed out.

    • Reusable CKFs could not be deleted on a classic release — bw_delete with object_type="ckf" failed with HTTP 415, because the delete named only the version discovery advertises and the resource serves a lower one. The delete now negotiates the version the way the lock does, for every object type. A successful delete of a CKF or RKF is no longer reported as failed when the unlock afterwards finds the object gone.

    • An activation could be reported as failed although it succeeded — on BW/4HANA the token request after activating a CompositeProvider could be answered "HTTP 400" when the server had already ended the session context, and bw_activate reported the error although the object was active. The client now continues in a fresh context.

    • A search with an unsupported type filter failed, and took the next searches with it — the search service answers a type filter it does not support with a short dump (HTTP 500), reported as "service not reachable for this user", which pointed at authorization. Which filters are unsupported depends on the release: analysis processes and InfoPackages on every release, process chains on a classic one, the classic providers on BW/4HANA. bw_search now runs such a search unfiltered and narrows the result itself, says that it did, and names the route for types the index leaves out altogether; object_type="ANPR" is answered directly with the route to analysis processes. The dump also ended the server-side session context, and the requests that followed in that context failed as well — a hang ending in HTTP 500 on a classic release, "Session Timed Out" on BW/4HANA — so one unsupported filter made the next, valid search fail too. After an HTTP 500 the client now drops the dead context and continues in a fresh one under the same logon.

    • Requests of an InfoObject — bw_list_requests and bw_delete_request asked for the storages of an aDSO also when the target was an InfoObject, and so found none of its requests. They now use the InfoObject's own storages — attributes, texts and hierarchies — and all_requests rolls back each of them separately. A failed master data request is removed this way. A master data request that loaded successfully is refused by BW on every route; the tool says so and leaves the other storages to run, instead of stopping with a bare HTTP 400.

    • The first metadata read on a fresh connection could fail — on a classic release, table reads sent at once as the first calls of a session each open their own session, and all but one fail with HTTP 500. On a hosted instance every tool call starts on a fresh connection, so every read of a classic DSO, InfoCube or MultiProvider failed this way. The first statement on a connection now runs alone and the others follow once the session exists. As a fallback a read is retried once after HTTP 500, without the session context of the failed request, and after "Session timed out", which is what reusing that context is answered with; a malformed statement is answered with HTTP 400 and is not retried.

    • On SAP BTP, the login asked for the read scope only — the server named read in its authentication challenge, and an MCP client requests that scope in preference to the ones the server advertises. Every token therefore carried read alone: a caller with the developer role was offered no write tool, and a caller with only the analyst role was refused. The challenge no longer names a scope, so the client requests all three and receives those its role collections grant; a token holding none of them is refused.

    • The XSUAA service could not be created or updated from xs-security.json — the redirect URIs listed the IDE schemes vscode:// and cursor://, which XSUAA rejects, so cf create-service and cf update-service failed outright. Both entries are removed.

    • A rule on a currency or unit key figure could not be activated — bw_update_transformation wrote direct, formula and constant rules without a conversion type. On a key figure with a fixed or referenced currency or unit, in a transformation that allows currency and unit conversion, BW reads a missing conversion type as "conversion requested" and inserts a conversion step with no source unit; the call reported success and activation then failed with "no source unit assigned" and "conversion type missing or invalid". Such rules are now written with "no conversion", the setting the modeling tools use for these rules, and rewriting a rule broken this way repairs it. Verified on BW/4HANA for fixed currencies and fixed units.

    • bw_update_structure did not move members — set_member_properties accepted position and parent, as the schema documented, and ignored both: the structure was saved, the call reported success and listed only "properties changed", and the member stayed where it was. A member now moves among its siblings with position and under another member with parent (an empty string moves it to the top level); properties is optional when only moving. The display order of a structure is the document order of its member elements — every member carries flatPosition="0" — so a move rewrites that order, and renames the element when it changes level, since a nested member written as a top-level one is dropped on save. applied_operations reports each move with its old and new place, and the stored structure is read back afterwards: a member not in the place it was sent to fails the call. Verified on BW/4HANA and on a classic release.

    • An unknown member property was dropped without a word — properties on the query and structure tools ignored any key it did not know, and the save reported success; a caller asking for a setting the tool did not support was told it had been applied. An unknown key is now refused with the list of known ones.

    • A save that changed nothing still reached the transport — a reusable component whose document came out of the update unchanged was written anyway, which records the whole component with every element it holds in the transport request. Such a call is now not saved at all; bw_update_structure answers changed: false.

    • Exception aggregation on a selection member was accepted and discarded — bw_update_query_key_figures wrote it on add_key_figure, add_ckf and add_rkf and on set_member_properties for such a member, the backend reported the save as consistent and stored nothing, on a classic release and on BW/4HANA alike. These calls are now refused with a pointer to the CKF or a formula member, where the setting holds.

    • An unknown aggregation type is refused — the backend stores a literal it does not know as standard aggregation and reports success. The type is now checked against the sixteen literals of the aggregation domain, each of which was written and read back on both platforms.

    • bw_get_query called an active query inactive — the status came from a server field that has been seen reporting "inactive" for a query whose A version exists with full content. It now reflects the version that was actually read.

    Notes
    • After updating a BTP deployment, update the XSUAA instance as well (cf update-service … -c xs-security.json). A scope the server requests that the instance does not define makes the login fail with invalid_scope.
    • On a classic release, /sap/bc/adt has to be exposed in the Cloud Connector alongside the BW paths: the metadata-table reads go through the ADT DataPreview service.
    • Everything specific to classic releases now lives in bw75/: the setup guide moved from docs/BW75-SUPPORT.md to bw75/BW75-SUPPORT.md, and the installation steps — the required Accept header enhancement and the optional DTP filter helper with its ABAP sources — are in bw75/README.md.
    • "Use single values for the result" is stored inverted, as the flag the metadata tables call the no-subtotal indicator: apply_to_result: false — what the modeling tools write for a member with its own calculation — sets it. The lock indicator of an input-ready member remains unsettable on purpose; the backend derives it from the input readiness, and it matched in every member checked.
    • The backend stores the single value calculation "moving average of values not zero" but reports it as the plain moving average when the query is read, so the two cannot be told apart from the document.
    • Two parts of an InfoPackage stay out of reach through table access, and the reader says so rather than leaving them out: the update mode of the definition, which no relational column holds (the mode each run used is in the load history), and the adapter parameters, a string column the DataPreview service cuts at 255 characters.
  4. v1.6.0

    Features 2, Fixes 5, Other 1

    Notes
    Fixed
    • Writing BW objects works on a classic BW 7.5 system — the platform filter added in 1.5.0 hides the tools whose REST resource a classic release does not publish, and everything it left visible counted as working there by omission. That held for the reads, which were verified. It did not hold for the writes: they had never been run against a classic system, and the first run found that essentially every one of them failed. Three causes, each of which produced a message pointing somewhere else.

      The media type was resolved against the wrong key. The discovery document is read at startup and overrides the hardcoded defaults, downgrades included — but it keys on the collection name, and that name is not stable across releases: a classic system publishes InfoObjects as infoobject where BW/4HANA publishes them as iobj. The discovered iobj-v1_8_0 was therefore filed under a key nothing looked up, the hardcoded v2_2_0 stayed, and the lock was rejected with HTTP 415 naming a content type mismatch. The reads were unaffected because the read path sent a list of versions rather than one — an InfoObject-specific workaround that existed for exactly this reason, alongside a second, InfoArea-specific one. Both are gone: the collection spelling is mapped onto the key the server addresses, and one shared function derives the version range wherever a list is wanted.

      The lock ran under a session type the backend refuses. A classic backend validates a lock handle against the ADT session that took it, and keeps that session alive only when the lock asks for one. Without it — or with stateful_enqueue, which BW/4HANA accepts — the lock returned a handle and the very next request was refused with ExceptionResourceInvalidLockHandle, "lock handle for object … could not be created". The message names the enqueue, the cause is the session. On a classic release every lock now runs as plain stateful. On BW/4HANA nothing changes: the caller's request is passed through untouched, and so is the behaviour where no platform verdict exists yet.

      A request between lock and write dropped the session. The reusable query components call /sap/bc/adt/cts/transportchecks after locking. A request that declares no session type ends a stateful one, which invalidated the handle the following write was about to use — the same 423, one step later. The transport check now carries the session type of the lock it sits inside.

      The InfoObject write had to leave the lock's session entirely. This one produced no error at all: after the media type fix the lock succeeded, the backend accepted both POST and PUT, answered "object changed successfully" — and applied nothing. A key figure body produced a characteristic, CHAR(5) with ALPHA; a PUT that changed only the description did not arrive either. An ADT communication trace of Eclipse BWMT creating the same InfoObject on 7.5 showed why: BWMT uses its stateful enqueue session for the lock and the unlock and for nothing else, and sends every read, write and activation on a session of its own. Over JCo that separation is free and therefore invisible; over HTTP it has to be asked for. The InfoObject write now runs in its own session — deliberately not every type, because an InfoSource PUT applies from the lock session and, sent from elsewhere, leaves the activation checking the state from before it. The create also carries the full object there: BW/4HANA ignores the create body and takes its values from the PUT that follows, while a classic release reads it and rejects a stub outright.

      Verified object by object on SAP_BASIS 750, each write read back afterwards: InfoAreas, InfoObjects (characteristic and key figure, with their length, texts and InfoArea as given), aDSOs including field, key and settings changes, InfoSources, CompositeProviders, queries with all five update tools, variables, restricted and calculated key figures, reusable structures, aggregation levels, move, unlock and delete. bw_set_query_roles and bw_create_transport_task were expected to be untestable there and were not — the system has the roles and a modifiable request.

      The DataSource resource pinned its version too. bw_set_datasource_fields and bw_change_datasource_delta sent rsds-v1_1_0 in the PUT rather than the version discovery resolved, and a backend serving v1_0_0 rejected both with HTTP 415 — the same defect as the InfoObject media type, in a second hardcoded place. Found by exercising them against a DataSource nothing uses, which bw_xref identifies.

    • bw_list_remote_entities answered HTTP 404 on a classic release — the value help it reads is published there, at a different address and with different parameter names: is/values/hanaentity with pattern and maxrows, where BW/4HANA has rsdsint/values/hanaentity with searchPattern and resultSize. The answer is shaped differently too — a column catalog and rows of bare values, positional, rather than one named element plus attributes per row — so the tool reads both, taking the column list rather than a fixed position to decide which cell is the entity name. bw_preview_datasource also goes to rsdsint and still answers 404 there; its classic address is not known.

    • bw_change_datasource_delta could set a delta process but never remove one — the empty string the tool documents for "remove the delta process" was validated against the list of admissible values, which by definition never contains it, so every attempt came back "not admissible". A delta could be set and not taken off again. Not release-specific: equally broken on BW/4HANA.

    • HTTP 403 CSRF token validation failed, seemingly at random — two requests issued in parallel on one client both found no token and both fetched one; the second fetch starts a new session and invalidates the token the first is about to send. The failure then surfaced at the next write, which had nothing to do with either read. It looked transient because it depended on which fetch answered first, and it liked to appear on the first write of a session because platform detection issues exactly such a pair before the first tool call. A token fetch is now shared by every caller that asks while it runs.

    • bw_get_infoarea returns fields instead of raw XML on a classic system — the resource answers in XML there where BW/4HANA answers in JSON, and the tool handed the whole document back under raw for the caller to parse. Both forms are now read into the same four fields.

    Added
    • A variable can be read back and corrected — bw_get_variable and bw_update_variable close the last gap among the reusable query building blocks, which until now could only be created. The reason is verification, not symmetry: the modelling API accepts an enum literal it does not know, stores its default and reports the object as consistent, and bw_create_variable passes five such literals. Without a way to read the object back, a processing type that quietly fell back to the default was invisible — reading the variable through its query does not help, because a query resolves the technical name of a variable reference and never its definition.

      bw_update_variable changes description, ready-for-input, entry requirement, selection type and processing type while the UID stays the same, so references from queries, CKFs and structures survive. The correction path before was delete and recreate, which BW refuses once anything references the variable — and deleting the query does not take its reusable components with it, so a variable created wrong stayed in the system out of reach. The reference characteristic and the variable type are rejected instead of sent: the backend accepts such a write, answers "consistent" and keeps the old value, which is exactly the silent coercion these two tools exist to expose.

      The Accept negotiation for the variable resource existed twice, in the read path and in the create path; it is now one definition, in the same shape as the query and RKF resources. Verified on both platforms — BW/4HANA and SAP_BASIS 750 — by creating a variable, reading every enum back, changing it, and confirming that a query using it still carries the same reference afterwards.

    • bw_system_profile states what each write tool does on a classic system — the tool listed what the platform hides and was silent about the rest, which reads as "everything still offered works". It now prints a verdict per write tool: verified, blocked with what to do instead, or still to test with the missing precondition. Generated from one table in the source, and a test fails if a write tool is added without one, so the tool surface and the statement about it cannot drift apart.

    Notes
    • The count of tools hidden on a classic release is 45, not the 44 named when the filter shipped; bw_delete_request joined them in the same version. bw75/BW75-SUPPORT.md carries the corrected number and a new section on the writes.
  5. v1.5.0

    Features 20, Fixes 5

    Notes
    Added
    • Reusable calculated key figures can be created and changed — bw_create_ckf takes the formula as the operator/operand tree bw_get_ckf hands back, so a definition read from one system can be written to another unchanged. bw_update_ckf takes either a whole new formula or targeted operations, which is what makes the common case — add another summand to a sum — possible without knowing or rebuilding the expression that is already there.

    • Reusable key figure structures can be created and changed — bw_create_structure and bw_update_structure build the object that reporting queries embed as an axis, with members referencing a reusable CKF or RKF or a basic key figure. A change at the structure reaches every query that embeds it, which is the point of the object and the reason the tools sit at the structure rather than at one query that happens to use it.

    • A restricted key figure can be corrected in place — bw_update_rkf changes description, base key figure and restrictions while the UID stays the same, so references from CKFs, structures and queries survive. The only correction before was delete and recreate, which BW refuses once the RKF is referenced anywhere — the state a wrong restriction tends to be discovered in.

    • Load requests can be deleted — bw_delete_request removes requests from an InfoProvider together with the data they brought in and their entry in request management. BW demands this before several follow-up steps, above all switching a DTP from delta to full extraction, which it refuses while delta requests remain in the target; all_requests=true clears a provider for exactly that case. The two kinds of request are handled differently, as BW handles them: a load request in the inbound queue is deleted, an activation request is rolled back — and a rollback also undoes every later activation and the load request beneath it, which the response spells out rather than leaving to be discovered. Not to be confused with selective deletion by condition, which removes rows and leaves the request standing. Asynchronous: a successful call starts the deletion, bw_list_requests confirms it.

    • An analyst role, for asking questions of the data — the HTTP transport had two scopes, and a business user who should only run a query was handed the whole modelling surface: a hundred tools to pick from, most of them about transformations, DTPs and process chains. The new analyst scope carries reporting and nothing else: run a query or a provider directly, read the characteristic values to filter by, find what there is to ask (bw_search, the roles assigned to the caller) and understand what is being asked (query, calculated and restricted key figures, structures, InfoObjects, the providers behind them), plus the load history for "is today's data in yet". Fourteen tools — the size is the point, because a client carrying all 105 reaches for the wrong one far more often than one carrying fourteen, and a test enforces the count so that growth is a decision rather than an accident.

      Purely additive: read is unchanged and still admits everything it did, including bw_query_data. The analyst set is a strict subset of it, and the role exists to give a business user a smaller client, not to take anything from a reader. bw_preview_datasource is left out although it returns rows — it samples source-system data while modelling an extraction, an ETL task rather than reporting — as is bw_list_contents, which walks the repository tree by object type folder and is the modeller's way in. Both stay available under read. The BTP role collections follow: BW MCP Analyst next to BW MCP Reader and BW MCP Developer; a caller may hold several. stdio is unaffected, as it has no scopes at all.

    • Characteristics can be modelled for values with lower case letters or umlauts — bw_create_infoobject takes lower_case, and bw_update_infoobject can change the flag afterwards. Without it, a characteristic rejects any value outside the permitted character set (RSKC), and it does so at the worst possible moment: the load reports success — a push answers HTTP 204 and the record is physically in the inbound table — and only the request activation fails. The flag is the single object-local switch that turns that check off, and it was not reachable through either tool, which left a whole class of characteristics (identifiers, language keys, names) unmodellable end to end. Changing it on a characteristic already used by a provider that holds data is accepted, so a request that failed activation on such a value can simply be activated again.

    • bw_get_request reads the log of each process step — a process step that runs under its own TSN, a request activation above all, keeps its messages in its own process log, and only the request-level log was read. A failed activation therefore appeared as a red step with the reason nowhere in the output: the message list held the (successful) messages of the load itself, and diagnosis ended at "finished with errors". The per-step logs are read alongside it now, so the actual message — the value, the characteristic and the message number — is in the same answer. On a request with more steps than the per-call cap, the steps that did not finish green are read.

    • The tool surface follows the platform — one instance of this server fronts one BW system, and that system is either BW/4HANA or a classic release. Until now the model saw the full BW/4HANA surface either way: against a BW 7.5 system it called bw_list_requests, bw_get_transformation and bw_list_process_chain_runs first, because their descriptions promise they work, collected an ICF HTTP 404 from each — delivered as a complete HTML logon error page — and only then found the route that does work. A hint in the prompt does not compete with a tool list, so the tool list is what changed. The platform is detected once per process from the system's own bw.b4hanamode flag and its discovery document, and tools/list then offers only what that system can answer: a tool addressing a /sap/bw/modeling collection is offered exactly where the collection is published, so the system decides and not a hardcoded release list. On a 7.5 system 44 tools drop out; on BW/4HANA the list is unchanged. A tool called anyway — a client holding a stale list — is answered with the reason instead of a request to BW. BW_PLATFORM=auto|classic|bw4 overrides the verdict: classic forces it where detection cannot run, bw4 switches the filter off as the escape hatch. Detection never fails closed — an unidentifiable system gets the full surface and a warning in the log.

    • Every hidden tool names the route that works — a filtered tool list answers half the question: bw_get_transformation is simply gone, and nothing says that the same rules and routine sources are in the metadata tables. The substitute call is therefore spelled out in two places from one table — in the message a client with a stale tool list gets back, and in the instructions the server sends at handshake time, narrowed to what this particular system actually hides. Deliberately not a redirect inside the tools themselves: the REST API and bw_read_metadata_tables run against different backends — the latter is direct table access through ADT DataPreview — and separate tools keep that boundary where it can be governed, so an installation that does not grant ADT simply withholds that one tool. A tool that quietly switched backends would dissolve the boundary, and it would fail silently where a hidden tool fails visibly.

    • bw_read_metadata_tables reads analysis processes (object_type="ANPR") — the APD is the one object type here that closes no gap of a particular release: no release ever published a REST resource for it and BW/4HANA dropped it altogether, so the metadata table is the only route on any platform, as with data slices. The whole definition is a single XML document in RSANT_PROCESS.XML — a column typed STRG, which the DataPreview service does return, worth stating because it drops other long-text types and it would be easy to conclude the definition is out of reach. It comes back as the process it describes: nodes in execution order with the object each source reads and each target writes, the ranges of a filter, the formulas of a formula node and the ABAP of a routine node. The order is computed from the <MAPPINGS> block and never from the document, which lists nodes in no particular order — a process whose target element comes first would otherwise read as writing before it reads — and a node fed by two branches is listed after both, with its predecessors named. A node type the catalogue does not know is still shown with its category, taken from the tag prefix, and its attributes; dropping it would report a process that reads from nowhere. Verified against every analysis process on a reference system, the shipped SAP content included: 20 node types across 15 processes, the largest with 45 nodes.

    • bw_read_metadata_tables reads process chain runs (object_type="RSPCLOG") — the chain definition already had a route, its execution history had none: on BW/4HANA the monitoring comes from the RV_C_PCM* OData services, which a classic release does not register at all, so bw_list_process_chain_runs, bw_get_process_chain_run_detail and bw_list_process_chain_last_status are hidden there and "why did the chain fail last night" had no answer short of the SAP GUI. One object type covers all three questions, told apart by what the name turns out to be rather than by its shape — a 25-character value is looked up as a log id first, because RSPC_CHAIN allows names that long too. A chain name returns the run history newest first, ordered in the statement so that the row cap takes the most recent runs and not an arbitrary slice, plus the steps of the newest run, which is almost always the question being asked. A log id returns that run's steps with status, start, duration and the process variant behind each, and names the steps that did not end green. A pattern such as Z* returns the last status of every matching chain, read one chain at a time so the answer is complete rather than capped. Status codes come back as the raw letter with its colour and meaning, the way the load history does it, and a step whose recorded status differs from its actual one — what a manual repair leaves behind — says so instead of picking one. The message log of a failed step is an application log and stays out of reach through table access; the output says that rather than appearing to have looked.

    • bw_read_metadata_tables reads the planning objects (object_type="PLSE", "PLSQ", "PLCR", "PLDS") — a classic release publishes alvl but none of the three planning resources, so bw_get_planning_function, bw_get_planning_sequence and bw_get_planning_properties are hidden there and the BW-IP mechanics of a system — the part a migration assessment is actually about — had no route at all: FOX formulas, characteristic relationships and data slices were reachable only through the SAP GUI. They now come from the metadata tables. A planning function returns its function type with the exit class behind it, the aggregation level and its InfoArea, the characteristic usage, the conditions, and the full parameter tree — assembled from the type's parameter declaration, so the values nest and are labelled the way the function type defines them instead of arriving as a flat list of names. Variable references are resolved to their technical names rather than printed as a 25-character UID that reads like a characteristic value, and a FOX formula comes back as source code instead of one table row per line. A sequence returns its steps in execution order with aggregation level, function and filter. PLCR returns the planning properties of an InfoProvider — key date, max combinations, save strategy — together with the characteristic relationships and their validity ranges. Data slices (PLDS) have no REST resource on any release, so that type is not a classic-only substitute but the only route to them on either platform. PLCR and PLDS are keyed by the InfoProvider, not by the aggregation level. No new tool: four object types on the existing one, and the three hidden tools name their substitute like every other. Verified against a grown customer estate as well as the reference system: a FOX formula is not stored one row per source line but as a character stream cut into fixed-width chunks whose line breaks sit inside the values, so it is reassembled before it is shown — printed per chunk it came back with words cut in half. Conditions are grouped by rule, because a function with two rules restricted to different value types reads as one function with four mutually exclusive conditions otherwise, and the chunk numbers of a formula are left out rather than repeated once per chunk.

    • bw_read_metadata_tables reads aDSOs (object_type="ADSO") — the load history of an aDSO had no route at all on a classic release: the tool refused the type, and bw_list_requests needs the BW/4HANA manage API that release does not have. It returns the history only, because the adso REST resource exists everywhere and bw_get_adso already reads structure and settings. The output names its source: RSSTATMANPART is the classic request store and is empty by design on BW/4HANA, where requests live in RSPMREQUEST — so an empty section there is not a statement about the provider.

    Improved
    • bw_get_process_chain_run_detail lost the whole answer when the message log failed — steps and messages were fetched with Promise.all, so a failure in either took both with it, and the gateway in front of these services answers with HTTP 500 every so often under load (observed on a customer system, where the same URL succeeded three times in a row moments later and the failure moved between the run list and the run detail). The two reads are sequential now, the message log is best effort — a run whose log cannot be read still returns its steps, and says why the log is missing — and every OData read in the monitoring tools is retried once on a server error. The message list is also no longer printed in full: one run of a real chain carries hundreds of messages (824 on the run this was written against), nearly all of them progress notes, so errors and warnings are shown and the rest is reported as a count — identical messages collapsed to one line with an occurrence count, because the same warning repeats once per affected object, and without the long text, which on these messages is the SAPscript help document converted to HTML and restates the message in a few hundred characters of escaped markup. The run that prompted this went from 824 messages with their long texts to two lines.
    • A duration longer than an hour is reported in hours — the load history and the chain runs share one formatter, and it stopped at minutes: a chain that waited overnight for a cancelled step to be repeated came back as 1107m 29s. Same duration, but nobody converts that in their head, and it is the first number a reader looks at.
    • bw_get_planning_function returned an incomplete definition, and said nothing about it — five defects in the PLSE reader, each of which produced a plausible answer rather than an error, found by reading the same objects through bw_read_metadata_tables and comparing. A parameter written as a self-closing tag (a key figure selection, typically) was taken for an opening one, so the search for its closing tag ran into the next parameter's and swallowed everything in between — on a copy or distribution function that is the whole from/to table, which is the entire content of the function. The restrictions of a data selection live in a fieldSelectionSet rather than in a selectionRange and were not read at all, so those tables came back empty even where they survived. exclude="true" was dropped, which turns "everything but the unassigned value" into "exactly the unassigned value" — the reader stated the opposite of the model. A function type that supports several rules writes one <condition> block per rule, and only the first was read, so half a function was presented as the whole one; rules are now reported separately, and named where there is more than one. And XML entities in values were passed through undecoded, so FOX source containing ==> came back as ==&gt;, which is not the code that runs. Regression-tested against an offline copy of a real response, and the two routes now agree fact for fact — including the FOX source character for character — on every function, sequence and provider they were compared on.
    • An ICF error page never reaches the chat — every /sap/bw4/… path answers with the full HTML "Logon Error Message" page on a classic system, thousands of bytes that told the model nothing. An HTML error body is now replaced by the sentence it means, naming the path and pointing at bw_read_metadata_tables; ADT exception documents are passed through unchanged, because callers parse them. Every HTTP failure in the client goes through one place now instead of twenty.
    • bw_system_profile no longer reports a resource as working because the probe failed — its reporting check answered "implemented" for every error except the one it recognised, so an unrelated HTTP 403 (a CSRF token rotated by the DataPreview probe just above it) had a classic system report query reporting as OK — in the same output that listed bw_query_data as unavailable. The published collection decides first now, which is the same signal the tool filter uses, so the two cannot contradict each other; the probe runs only where the resource is published, and an error that never reached the handler is reported as UNCLEAR rather than as a verdict. The check also keyed on the wrong collection (query, the query definition resource, which classic BW publishes as well) instead of reporting.
    • bw_system_profile reuses the detected profile instead of measuring again, so it cannot contradict tools/list, and it lists every tool the platform hides together with the reason. Its endpoint groups accept both spellings of the InfoObject collection — classic advertises infoobject, BW/4HANA iobj, and the resource is iobj on both — which had it reporting a working group as unavailable on either platform.
    • Metadata table reads survive a rotated CSRF token — the token is fetched from the BW modeling service and spent on the ADT DataPreview service, and the backend rotates it out from under the client: reading modeling metadata first was enough to have the next read refused with HTTP 403. These statements have no side effects, so one retry with a fresh token is safe, and the two statements of a load history read no longer run in parallel against the same token. A dropped connection is retried once for the same reason: a read that needs several statements keeps a connection busy long enough for the server to recycle it, and the resulting ECONNRESET reached the caller as a stack trace rather than as an answer.
    Fixed
    • An MCP client that asks for no scopes no longer breaks the hosted instance — the OAuth metadata did not advertise scopes_supported, so a client requested none. XSUAA grants them anyway, and Copilot's older auth code then falls back to the JWT scope claim — an array on XSUAA, a string everywhere else — and fails with scope.split is not a function. Fixed upstream in vscode#325344, but the Eclipse Language Server lags behind, so the server advertises the scopes itself. That also narrows the verifier's accepted scopes from the framework default set to the three this server defines.
    • Setting a unit or currency source no longer destroys a neighbouring key figure mapping — bw_update_transformation with unit_source_field removed the first rule targeting the unit/currency field so it could fold it into a combined rule. In a transformation that already carries combined amount+currency rules, that first rule belongs to another amount, so every call silently dropped one neighbouring mapping (observed on a BW/4HANA system: two rules lost per transformation, with the call reporting success). Only a rule whose sole target is the unit/currency field is folded now.
    • A formula over more than one source field is modellable — bw_update_transformation with rule_type="formula" wired up exactly one source, and additional_source_fields was accepted and ignored. A formula rule is only parseable once every operand is registered as a source of the rule, so the typical derived flag — IF ( A = … AND B = … AND C > … , 'X' , '' ) — was written without complaint and then failed activation with "syntax error in formula", a message that points at the text while the text is fine. The rule is now built with one <source>/<input> pair per operand, and the operands are read out of the expression itself and resolved against the source segment (a custom characteristic appears as /BIC/NAME there, a standard one without its leading zero), so a multi-operand formula works without listing its fields. A field named by the caller that the source segment does not have is refused before anything is written, instead of being stored as a rule that cannot be activated.
    • The formula text of an existing formula rule can be changed — the same call on a rule that was already StepFormula reported success, echoed the new text back, and stored the old one: rule content was only ever written while converting a rule from another step type, and a rule that needed no conversion was left alone. Changing a formula is the most common case there is, and the answer was indistinguishable from a real change. The rule is rebuilt now whatever step type it has. The two conversions that key on the source step type — to a field routine and to a constant — no longer report a conversion they did not perform: a step type they cannot convert is named in the response instead, and a constant rule that is already a constant has its value replaced rather than silently kept.
    • A formula that comes back from BW can be handed straight back in — bw_get_transformation printed the formula exactly as stored, XML entities and all, and feeding that text into an update escaped it a second time (&gt; became &amp;gt;). The formula is shown decoded now, and an update accepts either spelling and escapes it exactly once.
  6. v1.4.1

    Features 1, Fixes 5, Other 1

    Notes
    Fixed
    • Adding a routine left the generated class inactive — bw_set_transformation_routine wrote the source of the generated _M class and then activated it while still holding the ADT lock, which the backend rejects with HTTP 403 "… is already being edited". The transformation itself had already been written at that point, so the call failed with a routine rule in place whose class had never been activated, and no obvious way back. The lock is now released before activation, the order the expert-routine tool already used (#21).
    • Generated AMDP end routines did not activate on field-based providers — the column list of the generated GLOBAL_END skeleton applied the InfoObject naming rule to every target element, so a plain field became "/BIC/FIELD_NAME" and activation failed with invalid column name. Only elements that are actually InfoObject-backed get that mapping now (#21).
    • bw_set_transformation_routine_fields reported a field list it had not stored — asked for a subset of the target fields, the backend answers HTTP 200 and keeps the full list. The tool reported the count it had computed locally, so a silent no-op looked like success. It now reads the transformation back and lets the stored state decide the result, naming the stored and the requested count when they differ (#21).
    • rawPost / rawPut / rawDelete bypassed the Cloud Connector proxy — they built their own axios instance and so lost both the proxy transport and the Proxy-Authorization (plus optional location id) header the constructor's interceptor adds. Behind a BTP connectivity proxy every such call tried to resolve the virtual destination host itself and failed with ENOTFOUND, which took ADT DataPreview and all transformation writes with it. They now go through the shared client (#24).
    • bw_push_data and bw_get_push_schema ignored the BTP destination — the push tools issued their requests outside the shared client and therefore without destination resolution or principal propagation. Both now use the same client as every other tool (#25).
    Improved
    • The server identifies itself in the handshake — BW_MCP_SERVER_NAME names the instance in serverInfo, and with BW_MCP_SYSTEM_LABEL the connected system becomes the first line of the instructions the server now sends, so several instances stay distinguishable in clients that show an opaque connector id. serverInfo.version follows package.json instead of a hardcoded copy that had drifted to 0.1.0. Both variables are optional and the default handshake is unchanged (#26).
    Notes
    • The Overflow when converting 256 reported in #21 could not be reproduced. The message is a 1-byte overflow of RSTRAN_STEPID: the id of a <step> is stored in an INT1, so 255 is written and 256 is rejected. Neither the number of target fields nor the size of the transformation drives that value — a transformation with 301 target fields in its end routine round-trips without complaint, and the tools only ever write step ids 1 and 2. The routine tools are unchanged in this area since v1.1.0, so this release is not expected to change that error on its own.
  7. v1.4.0

    Features 10, Fixes 5, Other 2

    Notes
    Added
    • SAP BW 7.5 on HANA support — out of the box almost every call against a BW 7.5 system failed with HTTP 406: the 7.5 REST framework looks the Accept header up case-sensitively while the kernel delivers header names in lower case, so content negotiation falls back to resource version 1.0.0 and rejects everything above it. A small ABAP post-exit fixes it — an enhancement, not a modification — and with it in place every REST endpoint that exists on 7.5 becomes reachable. Root cause, ABAP code, setup steps and the list of what 7.5 still does not ship a REST resource for: bw75/BW75-SUPPORT.md.
    • bw_system_profile — reports what the connected system is (BW/4HANA vs classic BW, from the system's own b4hanamode flag), which REST endpoint groups it publishes and therefore which tool groups work on it, and whether three preconditions hold: Accept-header handling, access to the ADT DataPreview service, and whether the BICS reporting resource is implemented — discovery publishes it on classic BW without the handler behind it, so the endpoint list alone overstates what the system can do. Called before the work starts, it is what lets a client pick the route that works on the connected release rather than inferring it from failed calls.
    • bw_read_metadata_tables — read-only fallback that reads an object straight from its metadata tables for the types a system publishes no REST resource for: TRFN (including start, end, expert and field routine source), DTPA, the classic providers ODSO, CUBE and MPRO, which no release exposes over REST, and RSPC. A process chain comes back with its steps, their variant parameters and the dependencies between them, in the order the chain runs — which RSPC states nowhere: the dependency is implicit in the events a step waits for and raises, and the rows come back unordered. The object name is checked against the width of the key column it is looked up by, so a name that cannot belong to any object is answered as such instead of terminating the DataPreview service with a truncation dump. InfoCubes and DataStore objects also report their load history — request, status, update mode, start time, who ran it, how long it took, records transferred and added, and the source it came from. On a classic BW system that is the only route to the load status at all, because the BW/4HANA manage API behind bw_list_requests does not exist there.
    • Process chains can be edited in place — bw_add_process_chain_edge, bw_remove_process_chain_edge and bw_remove_process_chain_step change one dependency or one step instead of replacing the whole model, which is what a small correction to a large chain needs. Removing a step takes its edges and its inline process variant with it and bridges the gap, so the strand stays connected. Steps are addressed by name — a DTP, an aDSO held by an activation step, the program of an ABAP step, a collector type, or #<index> — and an ambiguous name is rejected with the candidates listed rather than resolved by guesswork.
    • Remodeling monitor — bw_list_remodeling_requests, bw_get_remodeling_request and bw_run_remodeling monitor, diagnose and run the requests that a remodeling rule produces: the five processing steps (CHECK, SAVE, CONVERT, ACTIVATE, CLEANUP) with their individual status and the application log per step, plus execute, restart, reset and reset-step. Running a rule restructures the InfoProvider and converts the data it already holds, so this is a write with data impact, not a reload. Rules are not created here — BW creates one itself when an aDSO holding data is activated after a change that cannot be applied in place. A "running" status is never taken from the monitor service alone: it is buffered and keeps reporting Running after a run has ended, so it is cross-checked against the runtime tables and the batch job, and a corrected status names the source it came from.
    • bw_update_query_characteristic — the per-characteristic display and access properties of a query's rows, columns and free-characteristics areas, which no tool could reach before: display of result rows, display as key/text (with short, medium or long text), access type for result values, sorting, cumulation, display level, and the hierarchy assignment together with its display options (expand-to-level, position of child nodes, values of postable nodes, suppression of single-child nodes, hierarchy sorting). One entry per characteristic, or "*" to apply the same set to every characteristic in the layout; every property also takes "default" to drop the explicit value again. All changes go out in one save, like the other query update tools.
    Improved
    • aDSO fields can be put in a field group — a field added through bw_update_adso used to land in the catch-all group, and no tool could move it out again, so getting a key figure into the key figure group meant editing the aDSO XML by hand. add_field and add_pure_field now take a dimension, which is the point of it: the field is created in the right group straight away and the second activation — with the reactivation of every aggregation level, transformation and DTP hanging off the aDSO — does not happen at all. update_field_properties takes the same parameter to move an existing field. Group names are per aDSO, not a fixed vocabulary (KEY/DATA/__KEYFIGURES on one object, IOBJ/__KEY/__NON_KEY on the next), so the name is validated against the groups the aDSO declares and an unknown one is refused with the valid names listed rather than quietly falling back to the catch-all. The attribute value itself is never built from caller input — it ends in a SECTION SIGN and is copied from the document. Nothing else about the field changes, and no remodelling is triggered. The group each field sits in was already visible in the DIM column of bw_get_adso.

    • DTP filters accept the full range vocabulary — a filter field takes sign I/E with Equal, Between, ContainsPattern and the comparison operators, one or two bounds per line, instead of equality only. Each selection is validated against the operators the field itself publishes, so a field that offers fewer is not silently over-promised, and an unmatched field name is an error rather than a PUT that succeeds and changes nothing. Values and a filter routine coexist on the same field, as the server itself serializes them.

    • A block can be inserted into a chain in series — before / after on bw_append_process_chain_dtp and bw_add_process_chain_program reroute the target step's incoming (or outgoing) edges through the new block, so it really runs ahead of or behind that step. Without them the block is appended behind the strand end and runs in parallel to the target's existing successors, which is rarely what was meant.

    • The CSRF token fetch survives a dead keep-alive socket — a socket the server tore down while the previous write was committing surfaced as ECONNRESET on the next token fetch and aborted the whole flow, which showed up as a sequence of process-chain writes succeeding and then dying. The fetch is a side-effect-free GET, so it is retried once on a transport error; a real HTTP error is still passed straight through.

    Fixed
    • Process-chain timestamps are parsed again (#15) — these OData services return the day fields as /Date(<ms>)/ but every timestamp field as /Date(<ms>+0000)/, and the offset suffix was not part of the pattern. Every start and end time in bw_list_process_chain_runs, bw_get_process_chain_run_detail and bw_list_process_chain_last_status therefore reached the output as the raw /Date(…)/ string. The suffix is now tolerated with any digit count; the leading number is UTC epoch milliseconds either way, so results do not shift. Reported by @MarcusSchoelzel.
    • bw_get_request no longer needs a process log (#16) — an activation request (storage AT) has no process log, so the log endpoint answered 404 and the whole read failed, discarding the header, DTP information and process steps that were available. Every section is optional now; the read only fails when all four do, and a missing log is reported in place of its section. Reported by @MarcusSchoelzel.
    • A failed model serialization is reported instead of dumped (#17) — when the backend cannot serialize a transformation's model, CL_RSO_RES_TRFN turns that into HTTP 500 and the raw status plus XML body reached the caller. It is now reported as what it is, with a pointer to bw_read_metadata_tables, which reads the transformation from its tables and does not go through the serializer. Detection covers both shipped texts of the underlying message (RS_RES_MODEL 001) because it is language-dependent — the German wording shares no words with the English one. Reported by @MarcusSchoelzel.
    • NODESNOTCONNECTED is no longer reported as an InfoArea (#18) — the backend substitutes that placeholder whenever an object's InfoArea is empty or names an area that does not exist, so the modeling tree has a node to hang the object under, and object reads passed it on as if it were a real assignment. It is mapped to "no InfoArea" in every reader that surfaces one: aDSO, CompositeProvider and its components, InfoSource, Open Hub, InfoArea (parent), the four planning readers, plus process chain and query. Reported by @MarcusSchoelzel.
    • Namespaced object names are addressed correctly (#19) — a name such as /NAMESPACE/OBJECT_NAME went into the URL with its slashes intact, producing a double slash after the type segment, so every read and write answered HTTP 404. Names now travel through the escaping the backend applies itself (CL_RSEM_MODEL_OBJECT=>ESCAPE_OBJECT_NAME: every / becomes $, every : becomes !) wherever the name is part of the URL path — aDSO, InfoObject, transformation, DTP, CompositeProvider, InfoSource, DataSource, Open Hub, InfoArea, planning objects, query, variable, structure, process chain and process variant, plus the shared lock / update / delete / activate paths. A name without those characters produces a byte-identical URL, so nothing that worked before changes. Reported by @MarcusSchoelzel.
    Notes
    • The objectName query parameters of the where-used and dataflow services keep the plain name on purpose: those handlers only percent-decode, so the escaped form resolves to nothing and returns an empty result. Both already worked for namespaced objects and are unchanged.
    • Verified against namespaced InfoObjects, aDSOs, CompositeProviders, InfoSources, queries and InfoAreas. The DataSource and push endpoints follow the same rule but no namespaced object of either kind was available to test against.
  8. v1.3.0

    Features 5, Fixes 4, Other 1

    Notes
    Added
    • CompositeProvider authoring — bw_create_composite_provider creates a Union or Join node with its source providers attached, or copies an existing CompositeProvider (the template is named in the URL, not in the body, unlike aDSO). bw_update_composite_provider grew from two actions to eight: add_input, remove_input, update_mapping, update_join, remove_join and update_settings next to the existing add_field / remove_field. Field mappings resolve against each source's own metadata, so field-based and InfoObject-based providers both work; join conditions are set per input pair, which is how BW models an N-way join. Verified against a BW/4HANA system up to an activated CompositeProvider. Based on the traced payloads contributed in #20 by @JosephManu12, ported onto the current code base and extended.
    • Aggregation levels — bw_create_aggregation_level and bw_update_aggregation_level, the first objects from the planning side the server can create, optionally with a subset of the provider's fields.
    • bw_create_variable — BW variables for query authoring.
    Improved
    • Locks are released by the session that holds them (#13) — a BW enqueue belongs to the ABAP session that took it, so ?action=unlock from any other session answered HTTP 200 and released nothing, leaving objects locked until the ADT session timed out or SM12 was used. The client now tracks which session holds a lock, routes the release through it, and hands back the handle it already holds when the same caller locks again.
    • bw_unlock accepts hcpr and alvl — previously there was no way to release those locks through the MCP.
    Fixed
    • adtcore:masterSystem no longer guessed from the URL host (#13) — the host says nothing about the system behind a destination or a proxy, and with BW_URL unset the fallback produced LOCALHOST, which the backend rejects. It is read from the system's own logical system name now, once per process, with the old derivation as fallback. Affects transformation, DTP, query and DataSource creates.
    • CompositeProvider read served a stale model buffer — it went through the calling client, so a read right after a write returned that session's pinned state and lost attributes the write had set. It uses a fresh session now, like the aDSO and transformation tools.
    • XML entity handling in labels — labels were interpolated unescaped, so an & failed the request with HTTP 500, and entities were not decoded when reading. Decoding is shared in the client now and also applies to bw_list_contents and the aggregation level read.
    • Key figure detection in CompositeProviders — derived from the element body instead of a __KEYFIGURES dimension, which a plain Union node does not have.
    Notes
    • On the four remaining findings reported in #13: the transformation create buffer does not apply here, because lock and create already run on separate sessions; the stateless lock path is unreachable code; and the DTP stateful_enqueue behaviour could not be reproduced — on this landscape the enqueue survives the request, which points at the server's session configuration rather than the connector.
  9. v1.2.1

    Fixes 2, Other 2

    Notes
    Fixed
    • Environment proxies are no longer disabled (#22) — the HTTP client passed proxy: false whenever no explicit Cloud Connector hop was configured. In axios that does not mean "no proxy", it means "ignore proxy settings", which also switches off the HTTP_PROXY / http_proxy environment variables. Deployments whose only route to the BW host is a local or corporate proxy lost that route and failed with ENOTFOUND. An explicit Cloud Connector hop still takes precedence.
    • Media type discovery now reaches the wire (#23) — two independent defects made every aDSO call fail with HTTP 415 on systems advertising a lower adso resource version than the compiled-in fallback:
      • loadMediaTypes() kept the fallback whenever it outranked the advertised version. Discovery states what the connected backend accepts, so it is now authoritative; where one document maps several collections to the same key, the highest version still wins.
      • Accept headers for aDSO, transformation and value-help requests were bound to module-level constants, evaluated at import time — before discovery had ever run. They are resolved per call now, so a discovered media type actually reaches the request.
    Notes
    • Because environment proxies apply again, a globally set HTTP_PROXY now also covers BW hosts that were previously contacted directly. Use NO_PROXY to exclude them. This restores the behaviour of versions before v1.2.0.
    • Regression tests cover both fixes against a local fake backend and a local proxy, so neither needs a BW system to reproduce: npm test.
  10. v1.2.0

    Features 7, Fixes 1, Other 4

    Notes
    Added
    • BTP Cloud Foundry Deployment — the MCP server now runs centrally on SAP BTP Cloud Foundry as an HTTP server (npm run start:http) instead of only locally via stdio. Enables shared hosting, concurrent users, and enterprise authentication
    • XSUAA OAuth Authentication — BTP integration with SAP XSUAA service for identity management and role-based access control. Built on the same @arc-mcp/xsuaa-auth module as ARC-1 for consistency across NextLytics MCP ecosystem. Stateless Dynamic Client Registration (DCR) + callback proxy pattern ensures secure, session-independent auth. Supports both BasicAuthentication (Stage 1: shared technical user) and Principal Propagation (Stage 2: per-user identity)
    • Role-Based Access Control (RBAC) — two role collections ship with xs-security.json:
      • BW MCP Reader — read-only access to the metadata and query tools (via read scope)
      • BW MCP Developer — full access including create/update/delete/activate (via write scope). Write scope implicitly grants read, following the principle of least surprise
    • Scope Enforcement — new src/scopes.ts enforces which tools require which scopes; read-only tools are explicitly listed, all mutations default to write, ensuring new tools are safe by default (unavailable to read-only users until explicitly whitelisted)
    • Cloud Connector Integration — BTP destinations route on-premise BW traffic via Cloud Connector; supports HTTP proxy type for transparent connectivity without exposing internal networks
    Improved
    • Security by Default — the scopes system defaults new write tools to write scope rather than accidentally permitting them to read-only users. Classification comes from actual HTTP verbs (POST/PUT/DELETE usage, not tool name)
    • xs-security.json Structure — three-layer authorization model (scopes → role-templates → role-collections) allows future granularity without code changes; documentation added for extending roles (query, monitor, metadata, data_push, admin scopes as examples)
    Fixed
    • stdio entrypoint compatibility — dist/index.js again starts the stdio server when executed directly (node dist/index.js), via a run-as-main guard. The Cloud Foundry refactor had moved the bootstrap into dist/stdio.js, silently breaking existing local MCP client configurations that launch dist/index.js: the process started but exited within seconds without completing the MCP handshake. Both dist/index.js and the canonical dist/stdio.js bin now work; the guard does not fire when the module is imported, so the HTTP entrypoint never double-starts
    Notes
    • Shared technical user (Stage 1) — BasicAuthentication via the BTP destination, tested and verified end-to-end
    • Principal propagation (Stage 2) — per-user identity via Cloud Connector certificate propagation plus ABAP-side CERTRULE and ICM reverse-proxy trust, tested and verified end-to-end; setup documented in docs/CENTRAL-HOSTING-SETUP.md (Stage 2) and docs/CLOUD-FOUNDRY.md §3
    • stdio Mode Unchanged — local stdio invocation (npm run start) continues to work without authentication, unchanged by this release. The HTTP server is additive; upgrading an existing local install is non-breaking
    • npm Package — bw-modeling-mcp is now published to npm as both stdio (default bin entrypoint) and HTTP (via npm run start:http)
  11. v1.1.0

    Features 3, Fixes 2

    Notes
    Added
    • bw_create_rkf — create one reusable Restricted Key Figure (RKF, TLOGO ELEM) on an InfoProvider from a base key figure plus one or more characteristic restrictions (built for mass creation, one RKF per call); each restriction value is validated against the InfoProvider and mapped to its internal key, and the RKF is written consistent (no separate activation step). Media-type negotiation follows the working query path and the observed backend behaviour: the CREA lock on the shared comp/enq endpoint uses the query media type (that endpoint negotiates the same type for every ELEM component, Query and RKF alike), and the writes on the dedicated /rkf/<name>/a resource send Accept as a version range (the resource negotiates a lower version than the discovery-advertised collection — verified live: resource speaks rkf-v1_9_0 while discovery advertises rkf-v1_10_0), so a single discovery-derived value is never pinned. Verified live on BW/4HANA
    • bw_add_process_chain_program — add an "Execute ABAP Program" step (RSPC process type ABAP) to an existing Process Chain, optionally with a named SE38 selection variant. In-place edit: the program call is stored as an inline process variant inside the chain model (no separate variant object). Positioning via before / after / predecessor (default: strand end closest to the trigger); idempotent (an existing ABAP step for the same program/variant is skipped), with ETag concurrency and transport handling
    Improved
    • bw_create_process_chain / bw_update_process_chain — new ADSOREM step type ("Delete Requests from DataStore Object" / DSO request cleanup) with an inline variant; one entry per aDSO carrying its cleanup action and request selection (all requests / keep last N / older than N days / package size)
    Fixed
    • bw_set_dtp_filter_routine — the routine's inactive version is now syntax-checked before activation. Broken routine code (e.g. i_r_request->get_dtp( ), which does not exist on the request interface) is reported with the ABAP error messages and the DTP is left unchanged, instead of being silently reported as "activated". The generated program's EU (ADT) enqueue lock is now released on the error path too (no orphaned SM12 lock), and a genuine activation failure is surfaced instead of returning success
    • Process chain transport check — a validateobject HTTP 404 caused by a stale stateful MCP session is now handled softly (the write proceeds without a transport header) instead of aborting. The previous hard abort wrongly blocked follow-up writes to local ($TMP) chains, which need no transport at all; a genuinely transportable object is still refused by the PUT with HTTP 403, at which point a transport request must be supplied
  12. v1.0.0

    Features 7, Fixes 6

    Notes

    The largest feature drop so far, and the release that takes the server to 1.0: a broad wave of write tools turns what was already a solid read/write toolkit into full BW/4HANA modeling coverage — query authoring, extended process chain authoring, transport-request integration, and a hardened session model. No breaking API changes.

    Added
    • Query authoring — the query object graduates from read-only to fully writable:
      • bw_create_query — create a new, consistent query (TLOGO ELEM) on an InfoProvider in package $TMP; with the new copy_from parameter the query is created as a full copy of an existing query (layout, filter, variables, key figures), deriving the InfoProvider from the source when none is given
      • bw_update_query_layout — rows, columns, structures, and free characteristics
      • bw_update_query_filter — query filter and restrictions
      • bw_update_query_key_figures — basic key figures, RKF/CKF references, and local formula members (recursive operator/operand tree), with exception aggregation, display properties, and member removal
      • bw_update_query_settings — query properties
      • all four update tools accept an optional transport request number for queries on a transportable package
      • query deletion via bw_delete
    • Process chain authoring, extended:
      • bw_append_process_chain_dtp — append one DTP load step (optionally with its own DSO activation step) to an existing chain
      • bw_swap_process_chain_dtp — swap one DTP load variant for another in an existing chain
      • bw_add_process_chain_error_links — add on-error (negative) links by mirroring the existing success links
      • bw_create_decision_variant — create a DECISION process variant for use as a branch/decision step
    • Transport lifecycle:
      • bw_create_transport_task — add a task (sub-request) for a user to an existing workbench transport request
      • bw_list_changeable_transports — list transport requests and their tasks via the BW transport state (cto/check)
    • DataSource authoring:
      • bw_change_datasource_delta — change the delta process of a DataSource (full read-modify-write of deltaProperties)
      • bw_set_datasource_fields — set the transfer flag of DataSource fields and/or the segment language_field
    • bw_set_transformation_expert_routine — write Start/End/Expert routine code into the transformation master so it survives activation and transport
    Improved
    • bw_create_dtp — target_object_subtype (ATTR / TEXT / HIER) selects the InfoObject sub-object role for InfoObject targets, mapped to the correct DTP type code (IOBJA / IOBJT / IOBJH); previously only attribute targets were reachable
    • bw_update_query_key_figures — add_formula documents the full BW analytic-engine operator catalog (basic, percentage, data, mathematical, trigonometric, and boolean operators plus ternary IF) and validates each operator's operand count before saving, so a malformed formula fails with a clear message instead of leaving the query saved in an inconsistent state; operator codes are now case-insensitive. LEAF (which BW encodes as a dedicated nullary token, not a prefix operator) is rejected client-side rather than producing an HTTP 500
    Fixed
    • bw_set_transformation_runtime — runtime switches no longer report false runtime_not_persisted errors and no longer get silently reverted. Root cause was the server-side ADT session model buffer: a session that had previously read (or locked) the transformation keeps serving its stale model even with forceCacheUpdate=true, so (a) the post-activation verify read the OLD active version through the shared long-lived client and reported a persisted switch as failed, and (b) a later read-modify-write through the same session could resurrect the stale HANARuntime value and re-persist it. The switch attempt (lock → GET /m?forceCacheUpdate=true → PUT → activate → unlock) and every active-version read (initial already_set decision and verify) now each run in a fresh session, which always returns the database state. Verified live with an abap→hana round-trip and independent virgin-session confirmation; the failure mode is value-independent (sapHANAExecutionPossible COULD and MUST_NOT alike, matching a manual-GUI trace of the COULD case)
    • Transformation write tools hardened against the same stale-session hazard: bw_get_transformation, bw_update_transformation, bw_set_transformation_routine, bw_set_transformation_expert_routine, bw_set_transformation_routine_fields, bw_delete_transformation_routine, and the post-create persistence check now read the transformation model through a fresh session with forceCacheUpdate=true (shared helper). Their previous pre-lock reads through the long-lived client could return a pinned stale model, and PUTting a model built on such a read silently resurrects old attribute values — the plausible mechanism behind observed runtime reversions. Lock ownership and the returned lock_handle contract are unchanged; verified live that the shared read path returns the database state through a deliberately dirtied session
    • The same fresh-session read hardening applied across the other object types (shared freshRead helper in the BW client): all five aDSO update tools and bw_get_adso (their model reads ran before the lock, the hazardous pattern), the bw_get_infosource / bw_get_infoobject / bw_get_dtp / DTP-details readers (diagnostic reads must reflect the database, not a pinned session buffer — this also removes the known stale inactive-shadow behavior of bw_get_dtp), the InfoObject lookups inside aDSO field addition, and bw_update_infosource's post-lock read now passes forceCacheUpdate=true. Update tools that already lock before reading (InfoObject, InfoSource, DTP) were left on the locking session, since the lock refreshes the session's model buffer (verified live)
    • bw_set_transformation_routine — EXPERT routines on HANA-runtime transformations no longer generate a plain ABAP class instead of an AMDP class. The initial step is now sent bare (no classNameM, no methodNameM, no per-field target elementRefs, no sourceSegment on the group) so the server derives the class itself and generates a proper AMDP class (interfaces IF_AMDP_MARKER_HDB, method BY DATABASE PROCEDURE FOR HDB LANGUAGE SQLSCRIPT); the server-generated class source is left untouched (the END-oriented SELECT skeleton no longer applies, since the EXPERT IN type follows source columns and OUT follows target columns). Verified against a native Eclipse BWMT trace
    • bw_set_transformation_routine — creating a global routine on a transformation that has no existing rule group no longer throws. When no <group id="1"> is present the new group is appended as the last child of <trfn:transformation> instead of requiring an existing group to insert before
    • bw_get_request / bw_list_requests — the message log (the primary diagnostic source) no longer dies on a 404 from the storage-dependent header/DTP-info/process endpoints when the storage code is wrong; each section is now isolated via Promise.allSettled and reported independently

  13. v0.9.2

    Features 2

    Notes
    Added
    • bw_change_package — reassigns an existing BW object to a different package (Development Class) and records the change on a transport request via the CTO write endpoint (/sap/bw/modeling/cto/write); a single write with no activation, so the object is left inactive and must be re-activated with bw_activate using the same transport; for object_type RSDS the source system is mandatory (compound key) and the applied package is verified by re-reading the DataSource, guarding against the orphan-TADIR case where writeResult="S" is returned but the real object's package stays unchanged; verified for TRFN and RSDS
    Improved
    • bw_create_transformation — new source_object_subtype / target_object_subtype parameters (TEXT / ATTR / HIER) to select the InfoObject facet when a source or target is an InfoObject (IOBJ): text table, attributes / master data, or hierarchy; passed through to both the transient GET (sourceobjectsubtype / targetobjectsubtype) and the transformation XML (subType)

  14. v0.9.1

    Fixes 1

    Notes
    Fixed
    • bw_search / bw_get_process_chain — corrected the TLOGO codes in the tool and object_type descriptions: InfoSource is TRCS (not ISFS) and Process Chain is RSPC (not PRCH); the wrong codes were passed straight to the search endpoint and caused an HTTP 500. Verified against the RSTLOGO domain (DD07T) and live bw_search calls

  15. v0.9.0

    Features 17, Other 2

    Notes
    Added
    • bw_get_aggregation_level — reads an Aggregation Level (ALVL): the planning-enabled view on top of an InfoProvider, with the complete element list — characteristics including type, length, conversion routine, base InfoObject, compounding, and dimension group; key figures including aggregation behavior, semantics (AMO/QUA/NUM), and unit/currency reference (unit characteristic, fixed unit, or fixed currency)
    • bw_get_planning_function — reads a Planning Function (PLSE): function type, aggregation level, documentation, characteristic usage roles, conditions, and the full parameter tree with nested structure and values; for FORMULA functions the FOX code surfaces as the value of the FLINE parameter
    • bw_get_planning_sequence — reads a Planning Sequence (PLSQ): ordered step list with type code, aggregation level, planning function, and filter name per step
    • bw_get_planning_properties — reads the Planning Properties (PLCR) of a plan-enabled InfoProvider (real-time aDSO or CompositeProvider): key-date mode, maximum characteristic combinations, and save strategy (planning sequence and delta-read flag); data slices not yet included
    • bw_create_process_chain — creates a Process Chain (RSPC) via the BW/4HANA Cockpit REST API; builds the chain model from a step and edge list, creates a trigger-only skeleton, then updates it with the full model in one operation; optionally activates after creation; supported step types: DTP_LOAD, ADSOACT, CHAIN (local sub-chain start, verified), and collectors AND / OR / XOR; inline-configured process types (ABAP programs, OS commands, attribute change runs, etc.) are not yet supported
    • bw_update_process_chain — replaces the step model (nodes and edges) of an existing Process Chain; preserves the existing trigger node and scheduling configuration; optionally overrides description and InfoArea
    • bw_activate_process_chain — activates an existing Process Chain; returns the top-level activation message, severity, and full log
    • bw_list_process_chain_runs — lists execution runs of one or all process chains from the monitoring log; filterable by chain name, start date range, and status; ordered by start time descending; default limit 20
    • bw_get_process_chain_run_detail — reads step-level and message-level detail of a single chain run, including error messages; chain_id and log_id come from bw_list_process_chain_runs or bw_list_process_chain_last_status
    • bw_list_process_chain_last_status — last execution status and scheduling state for every chain in the system; one row per chain; includes log ID of the most recent run
    • bw_get_open_hub — reads an Open Hub Destination (DEST): destination type, source object, DB table, InfoArea, package, status, the complete output field list with type/length, InfoObject binding, conversion routine, compounding, and key flag; file properties for FILE-type destinations
    • bw_list_remote_entities — lists the remote entities (HANA views / virtual tables) a source system exposes as a DataSource basis; read-only discovery matching the Eclipse DataSource proposal page; the returned technical_name is exactly what binds into bw_create_datasource
    • bw_create_datasource — creates a DataSource (RSDS) on top of a remote entity from the server's field proposal, leaving it inactive; the server derives the full segment and field structure from the remote entity; local objects only ($TMP in v1); activation is a separate step via bw_activate (object_type rsds)
    • bw_set_transformation_routine_fields — edits the list of target fields a global END routine writes ("Felder setzen" in SAP GUI); accepts an explicit field list (fields) or an exclusion list (exclude_fields); requires an existing END routine; does not activate; returns lock_handle for bw_activate
    Improved
    • bw_activate — now supports hcpr (CompositeProvider) as an activatable object type
    • bw_create_dtp — new IOBJ target type for InfoObject attributes; the BW XML type attribute is correctly set to IOBJA (InfoObject Attribute DTP target role)
    • bw_update_transformation — supports field-based direct mapping for targets without an underlying InfoObject; previously always attempted an InfoObject GET, which fails for plain aDSO/InfoSource field targets
    Notes
    • bw_get_planning_properties reads generalSettings only; data slices (PLDS) are not yet included
    • Process chain authoring uses the BW/4HANA Cockpit REST API (/sap/bc/http/sap/bw4/v1/modeling/processchains) — the same API consumed internally by the BW/4HANA Cockpit

  16. v0.8.0

    Features 11, Fixes 5, Other 2

    Notes
    Added
    • bw_run_dtp — starts (executes) a DTP load via POST /sap/bw/modeling/dtpa/executerun; returns the new run request id from the Location header (an RSPM TSN usable directly with bw_get_request); runs in a fresh session to avoid stale-buffer and concurrency issues
    • bw_list_requests — lists load requests for a target InfoProvider via the BW/4HANA /sap/bc/http/sap/bw4/v1/manage/requests API; shows status, last process status/action, record count, timestamp, user, and TSN
    • bw_get_request — full status analysis of one load request in a single call: header, DTP information (start/finish/duration), process step chain, and message log; format="raw" returns the parsed JSON of all four payloads
    • bw_activate_request — activates loaded data (DSO request activation): moves a finished load from the inbound table into the active data table + change log via POST .../manage/requests/{tsn}/{storage}/activate; runtime activation distinct from bw_activate; asynchronous
    • Cookie-based authentication for SAML- or OAuth-fronted BW systems (e.g. BW Bridge on the SAP BTP ABAP stack): set BW_COOKIE_FILE to a browser-exported cookie file (Netscape or name=value format); BW_USER / BW_PASSWORD become optional; login and session handling analogous to vibing-steampunk
    • bw_create_adso — new template_type (ADSO default / RSDS) and source_system parameters: propose aDSO fields from a DataSource, not only from another aDSO
    • bw_create_dtp — new source_system parameter: use a DataSource as the DTP source (source_type="RSDS")
    • bw_update_dtp — new extraction_mode parameter (full / delta) to switch an existing DTP between Full (extractionMode="F", deltaSettingStatus="0") and Delta (extractionMode="D", deltaSettingStatus="2"); switching modes has BW delta-init implications (a later delta load may require re-initialization)
    • bw_activate — new object type rsds (with source_system) to activate a DataSource
    Improved
    • bw_get_request / bw_list_requests — surface the last process status and last action alongside the request status, so a finished green load is no longer reported as "in process"
    • Media-type handling is now fully discovery-driven: the discovery parser reads every <app:accept> per collection (previously only the first, so workspaces listing a +json variant first fell back to hardcoded media types) and selects the highest-versioned +xml type; the query read path leads with the discovered media type
    Fixed
    • Query reads negotiate the backend content-type version correctly instead of failing with HTTP 415 when the backend returns a version outside the previously hardcoded Accept range (#11)
    • DTP activation no longer fails with a false "transformation inactive" error — the pre-activation priming GET and the activation POST now share one fresh session
    • Adding fields to staging / inbound aDSOs (which have no key elements) no longer produces an invalid element position that was rejected on activation
    • Date (DATS) constants in transformation rules are written in the external date format so they survive activation
    • Transformation rule editing selects the field's own rule (not the global start/end routine rule) on transformations that have a start/end routine
    Notes
    • The runtime tools (bw_run_dtp, bw_list_requests, bw_get_request, bw_activate_request) use the BW/4HANA /sap/bc/http/sap/bw4/v1/manage API — the same API the BW/4HANA Cockpit uses — rather than the /sap/bw/modeling tool API
    • bw_activate_request only applies to aDSOs that have an activation step (not inbound-only staging aDSOs)

  17. v0.7.0

    Features 3, Other 2

    Notes
    Added
    • bw_get_process_chain — reads a Process Chain (RSPC) definition via the BW/4HANA-specific endpoint (/sap/bw/modeling/rspc/{name}/m, Accept: application/vnd.sap.bw4.modeling.processchain-v1_0_0+json); returns header metadata (description, InfoArea, status, version), scheduling attributes (job priority, owner, server, streaming mode), monitoring settings (auto-monitored, error notification, keep-alive, auto-reset), all steps (nodes) with process type, variant, description, last execution status, DECISION branch labels with socket resolution, OR join annotations, and sub-chain references; edges with full conditional flow semantics (positive/negative/neutral, DECISION branch names resolved from socket descriptions); inline variant section; by default (include_variant_details=true) automatically fetches and embeds variant configuration for each step via internal calls to /sap/bw4/v1/modeling/processtypes/{type}/variants/{name}/m — deterministic, not prompt-driven; types with no variant schema (DTP_LOAD, CHAIN, OR, AND, EXOR, DTP_ADSO) are skipped; set include_variant_details=false for structural overview without variant detail; format="raw" returns full parsed JSON; use bw_search with object_type=RSPC to find chain names
    • bw_get_process_variant — reads the detail configuration of a single Process Chain step variant from /sap/bw4/v1/modeling/processtypes/{type}/variants/{name}/m; generic across all 93 BW/4HANA process types; oDetail returned as indented JSON regardless of type — covers ABAP (program + selection variant), ADSOACT (aDSO + NOCONDENSE), ADSOREM (cleanup: days/requests), PLSWITCHL/PLSWITCHP (target aDSO), TRIGGER (full scheduling payload), DECISION (branch formula expressions), and any unknown type; format="raw" returns full parsed JSON; process_type and variant_name come from bw_get_process_chain output
    • bw_preview_datasource — fetches a live data preview from a DataSource (RSDS) via the internal rsdsint/dataprev endpoint (POST /sap/bw/modeling/rsdsint/dataprev/{source_system}/{datasource}?records={n}&external=true); field names resolved automatically from a prior GET on the DataSource structure; renders a padded plain-text table with proper column alignment; records parameter configurable (default 20); handles field/column count mismatch with fallback to COL_N headers and warning
    Notes
    • Process chain support uses the BW/4HANA-specific /sap/bw4/ API namespace — the same API consumed internally by the BW/4HANA Cockpit (Fiori); Accept: */* is used to negotiate the correct media type automatically
    • bw_get_process_chain with recursive sub-chain expansion: call the tool again on any CHAIN-type step's variant name to drill into the sub-chain

  18. v0.6.0

    Features 5

    Notes
    Added
    • bw_get_roles (Read only) — reads the complete BW role hierarchy as shown in the Eclipse BWMT "Publish to Role" dialog; returns ROLE and FOLDER nodes with technical names, descriptions, and nodeids; optional role_filter parameter limits output to roles whose name starts with the given prefix (e.g. "BW:"); endpoint: GET /sap/bw/modeling/comp/roles?level=10&requestchk=true&readleaves=false
    • bw_get_role_queries (Read only) — lists all BW Queries published in the role hierarchy, grouped by role and folder; only SAP_BW_QUERY objects are returned — PFCG menu entries of other types (e.g. AFO workbooks added as transactions) are not included; uses readleaves=true on the same endpoint to retrieve <leaf> elements
    • bw_get_query_roles (Read only) — returns all roles and folders where a specific BW Query is currently published; uses the ancof (ancestor-of) parameter: GET /sap/bw/modeling/comp/roles?type=SAP_BW_QUERY&ancof=<QUERYNAME>
    • bw_set_query_roles — publishes or removes a BW Query from a role or folder; supports action="add" and action="remove", target_type="role" or target_type="folder"; for role-level add operations the full role subtree (folders + nodeids) is fetched from bw_get_roles and sent as state="unchanged" children in the PUT body; uses PUT /sap/bw/modeling/comp/roles?type=SAP_BW_QUERY&ancof=<QUERYNAME>
    • BwClient.rawPut() — new HTTP PUT helper on the shared BW client; sends a raw request body with caller-controlled headers using a fresh axios instance and the current session cookie; used by bw_set_query_roles

  19. v0.5.0

    Features 4

    Notes
    Added
    • bw_query_data (Read only) — executes a BEx Query or previews data from an InfoProvider (aDSO, CompositeProvider) via the BICS reporting endpoint (/sap/bw/modeling/comp/reporting); parameters: comp_id, is_provider (adds ! prefix for direct provider access), state (axis layout — ROWS/COLUMNS/FREE — plus per-characteristic filters supporting EQ/BT/GT/LT/GE/LE operators, include/exclude, external key, internal GUID key with presentationMode="INT", and hierarchy-node filters via nodeId=1), variables (fills query variables; name and id must be copied verbatim from the GET response as they are session-specific and may contain trailing spaces), from_row/to_row (pagination), drill_operations (expand or collapse hierarchy and structure nodes by 1-based tuple index: drill_state=3 expands, drill_state=2 collapses), format (text default — formatted table with hierarchy indentation; raw — XML); all reporting calls use X-sap-adt-sessiontype: stateless; CSRF retry: on HTTP 403 the cached token is cleared and the request is retried once automatically
    • bw_get_filter_values (Read only) — looks up valid characteristic values before setting filters or variables; returns both CHAVL_EXT (use for state filters, presentationMode="EXT") and CHAVL_INT (use for variable inputs); supports wildcard search (* for all, prefix match e.g. 2022*); parameters: characteristic_name, search_string, info_provider (optional, scopes values to a specific provider), max_rows (default 201)
    Improved
    • bw_get_query — added format parameter: text (new default) renders a compact human-readable summary covering settings, variables, filter, layout (rows/columns/free characteristics), CKFs, RKFs, exceptions, and cell definitions; raw returns the full parsed JSON (previous behaviour)
    • BwClient — added rawGet() helper (shared session GET with caller-controlled headers, used by all reporting calls); CSRF token TTL of 4 minutes so that ensureCsrf() proactively re-fetches the token before SAP's ~5-minute session idle timeout expires (prevents "CSRF token has expired" failures in environments with slow tool-call approval); clearCsrfToken() public method exposed for use by retry logic

  20. v0.4.0

    Features 10

    Notes
    Added
    • bw_get_dataflow (Read only) — reads the complete structural data flow of any BW object (ADSO, RSDS, HCPR, TRFN, DTPA, IOBJ, TRCS, LSYS) using the same transient dataflow graph that Eclipse BWMT renders; supports direction (upwards / downwards / both), configurable depth levels, and format "text" | "raw"; text output uses tree rendering for ≤ 30 nodes and flat table for larger graphs
    • bw_list_source_systems — lists all logical source systems (LSYS) registered in BW, optionally filtered by type (ODP_BW, ODP_SAP, ODP_CDS, ODP, FILE); returns name, description, type, status, and children_path
    • bw_list_datasources — recursively traverses the full APCO hierarchy under a source system and lists all DataSources with name, description, status, and APCO path; format: text (default table) or raw (XML feed bodies)
    • bw_get_source_system — reads full metadata of a single LSYS including type, description, connection details (ODP context/destination, HANA remote source/schema/SDI adapter)
    • bw_get_datasource — reads complete DataSource structure: all fields with type, length, precision/scale, transfer flag, key flag, position, selection options, conversion exit, unit/currency reference, and active adapter config; format: text (default) or raw (XML)
    Improved
    • bw_xref — new optional source_system parameter; required when object_type=RSDS; correct space-padded 40-character objectName (datasource padded to 30 + source system) is built automatically; explicit error thrown if omitted for RSDS
    • bw_get_transformation — raw boolean replaced by format: "text" | "raw" parameter; format="raw" returns clean XML without wrapper header lines
    • bw_get_datasource, bw_list_datasources, bw_get_transformation — unified format: "text" | "raw" parameter pattern across all three tools
    • bw_xref tool description — documents that object_type=DTPA returns the process chain(s) a DTP belongs to, preferred over bw_get_dtp when only the process chain is needed
    • bw_get_dtp tool description — documents that bw_xref with object_type=DTPA is the faster alternative when only process chain membership is needed

  21. v0.3.0

    Features 5

    Notes
    Added
    • bw_get_composite_provider (Read only) — reads a CompositeProvider (HCPR) structure: view node type (Union/Join), source providers with input mapping counts, all fields with dimension classification, join conditions, and temporal join details (extended from v0.2.0: field-level detail and join conditions fully parsed)
    • bw_get_ckf (Read only) — reads a global Calculated Key Figure with recursively resolved human-readable formula and full dependency graph of all referenced CKF/RKF sub-components
    • bw_get_rkf (Read only) — reads a global Restricted Key Figure: base measure resolved by name, all characteristic restriction groups with field and value details, and metadata
    • bw_get_structure (Read only) — reads a global Structure: all members with Formula/Selection breakdown, referenced components, characteristic filters, optional child members, and metadata
    • bw_list_contents (Read only) — navigates the full BW repository tree (InfoArea → type folder → object → sub-folder), mirroring the Eclipse BWMT Project Explorer; each entry includes children_path for seamless drill-down

  22. v0.2.0

    Features 1

    Notes
    Added
    • bw_get_query — new read-only tool for BW Queries
      • Reads active version (/A) with automatic fallback to inactive (/M)
      • Parses all subComponents: Variables, Calculated Key Figures (CKFs), Restricted Key Figures (RKFs)
      • CKF formulas recursively resolved to human-readable strings: InfoObject names, cross-references between CKFs/RKFs, variable references, IF / NOERR / NODIM operators
      • RKF selection conditions fully parsed: key figure restrictions, characteristic restrictions, component references
      • Full layout parsing: columns, rows, free characteristics — both simple Dimensions and CustomDimensions (reusable structures)
      • CustomDimension members fully parsed including nested childMembers — inline RKFs with selection conditions and inline formulas with local member name resolution
      • Filter area: fixed values, variable references, mixed selections (variable + fixed value on same InfoObject)
      • Exceptions with alert levels, thresholds, cell coordinates, and evaluation flags
      • Grid cells and help cells fully parsed (cross-table layout queries)
      • Query-level settings: zero suppression, planning mode, result position, RFC/OData/easyQuery flags, sign presentation

  23. v0.1.0

    Features 9

    Notes
    Added
    • Initial public release as pre-release (v0.1.0)
    • aDSO: create, update (fields, settings, keys, field properties), delete — including write-interface (pushMode)
    • InfoObject: create CHA + KYF, update attributes (DIS/NAV), delete
    • InfoArea: create, move objects
    • InfoSource (TRCS): create with/without template, update fields, delete
    • Transformation: create (all source/target types), update (direct mapping, formula, field routines ABAP+AMDP, start/end routines), activate
    • DTP: create, update (description + value filter), set filter routine
    • Push API: bw_push_data, bw_get_push_schema
    • General: search (bw_search), activate (bw_activate), where-used/xref (bw_xref), release locks (bw_unlock), delete (bw_delete)
20downloads per week, npm
Weekly downloads for bw-modeling-mcp26 complete calendar weeks from 2026-04-27 to 2026-10-25; the latest total is 20.27 Apr – 3 May: 04 May – 10 May: 10711 May – 17 May: 3618 May – 24 May: 16325 May – 31 May: 261 Jun – 7 Jun: 398 Jun – 14 Jun: 24615 Jun – 21 Jun: 3422 Jun – 28 Jun: 3229 Jun – 5 Jul: 2106 Jul – 12 Jul: 5013 Jul – 19 Jul: 19320 Jul – 26 Jul: 24227 Jul – 2 Aug: 2053 Aug – 9 Aug: 38510 Aug – 16 Aug: 6917 Aug – 23 Aug: 25424 Aug – 30 Aug: 5831 Aug – 6 Sept: 2027 Sept – 13 Sept: 5414 Sept – 20 Sept: 28521 Sept – 27 Sept: 27228 Sept – 4 Oct: 2885 Oct – 11 Oct: 5212 Oct – 18 Oct: 8019 Oct – 25 Oct: 20

Others providing49

Agent access to ABAP code
  1. @abap2ui5/mcp-serverMCP server for the abap2UI5 check, deploy and run loop
  2. arc-1-bopf-enharc-1 extension for reading and maintaining BOPF enhancements
  3. HammerNative macOS code editor for ABAP and CDS
  4. arc-1-lspEdition of ARC-1 that runs ADT calls through adt-ls
  5. arc-1MCP server over the ADT REST API for ABAP objects
  6. arc1-abap-bridgeVS Code bridge between arc-1 and ADT for VS Code
  7. arc1-adt-abap-mcp-extEclipse add-on enabling the ADT MCP server with read-only tools
  8. ABAP Accelerator MCP ServerAWS's ABAP MCP server for Amazon Q Developer and Kiro
  9. Modernization Agents for SAP on AWSAWS agents for ABAP, PI/PO and BW modernisation work
  10. @mcp-abap-adt/coreABAP ADT MCP server published as an npm scope and plugin
  11. chandrashekhar-mahajan/abap-mcp-serverABAP MCP server with ADT tools and five authentication strategies
  12. dassian-adtFork of mcp-abap-abap-adt-api with 25 tools and session recovery
  13. erpl-adtC++ binary speaking the ADT REST API as a CLI and MCP server
  14. EPOD ADT MCP ServerMCP server for ABAP on the ADT session, in Eclipse or standalone
  15. ZARC1_EXTENSION_FORMSarc-1 extension for SAPscript and Smart Forms objects
  16. fgalastri/MCP_ABAPValidates an ABAP class before it is deployed by converting the global class to a local one and syntax-checking that — plus CDS DDL and table metadata retrieval.
  17. fr0ster/mcp-abap-adtABAP MCP server with full CRUD on on-premise and ABAP Cloud
  18. abapsmithABAP MCP server over ADT with a local journal that can undo writes
  19. sap-ai-dev-toolkitMCP proxy pinning an agent's ABAP tools to one SAP system
  20. aibap.mcpGo MCP server that writes, activates and runs ABAP classes via ADT
  21. BellaEclipse plugin bringing Claude into ABAP Development Tools
  22. Maherd18/sap-mcp-serverPolicy-checked MCP server for ABAP development over ADT
  23. sapdumpmcpMCP server reading ABAP short dumps over ADT
  24. ABAP remote filesystemVS Code extension editing ABAP systems over ADT as a filesystem
  25. mcp-abap-abap-adt-apiWrite-oriented MCP server over abap-adt-api
  26. mcp-abap-adtRead-oriented MCP server fetching ABAP programs, classes, tables
  27. SAP MCP Connection ManagerWindows desktop manager handing SAP connections to assistants over MCP
  28. ABAPilotMCP connector for ECC and on-premise S/4HANA with a licensed SAP side
  29. vibing-steampunkADT-to-MCP bridge in Go reaching past what ADT exposes
  30. ADT MCP ServerLocal HTTP MCP server started from ABAP Development Tools for VS Code
  31. ABAP Development Tools for VS CodeSAP's ABAP tools in VS Code for editing, debugging, ATC and transports
  32. SAP Joule for Developers, ABAP AI capabilitiesSAP's AI for ABAP development in ADT for Eclipse and VS Code
  33. ABAPDocMCPMCP server turning a transport request into a WRICEF specification
  34. claude-sap-kitClaude Code plugin adding Custom_* tools to arc-1
  35. Skybuffer AI MCP serverABAP add-on serving business data and transactions as MCP tools
  36. abap-adt-cliCLI that pulls and pushes ABAP packages as local files over ADT
  37. workskong/mcp-abap-adtLight MCP adapter for ABAP over ADT, documented in Korean
  38. KlystronMCP server in ABAP inside the application server, with agent skills
  39. pyrfc-mcpMCP server calling RFC-enabled function modules behind an allowlist
  40. ABAP-AI-CODE-TOOLSabapGit package adding create, modify and delete tools to ABAP AI Code
  41. ABAP AI CodeAgentic coding assistant in ABAP that runs as an SAP GUI report
  42. VERTEXADT extension for Eclipse and VS Code showing three SAP GUI tools
Tool governance
  1. arc-mcp-hubHub on SAP BTP putting several arc-1 instances behind one login
  2. SuperClaude for SAPClaude Code plugin with an ABAP MCP server and write guards
  3. Maherd18/sap-mcp-serverPolicy-checked MCP server for ABAP development over ADT
  4. SAP API Policy Evidence SkillSkill assessing API usage against the SAP API Policy
  5. pyrfc-mcpMCP server calling RFC-enabled function modules behind an allowlist
Analytic modelling
  1. MBT Logical Object ListerMetadata viewer for SAP BW, SAP BPC and SAP BW/4HANA object models
Loading a warehouse
  1. ERPLDuckDB extension reading SAP ERP tables, CDS views and BW queries
  2. pawelwiejkut/bw_remove_charactersABAP class stripping invalid characters in BW end routines
  3. pawelwiejkut/bw_trfn_pocketknifeFramework for BW transformation start routines from a table

Details

Sources4
sap-ai-mcp-serversdiscovered herechecked
npm registry, bw-modeling-mcpversion, releases, installschecked
github.com/git+https://github.com/dnic-dev/bw-modeling-mcp.gittree, pushes, contributors, CIchecked
README.mdquoted, and what the entry declareschecked

Sources are graph entities: the same page may back several entries, and an unreachable source flags every row that rests on it.

Compatibility and provenance
runtimesnode
deploymentCloud Foundry, local
latestv1.8.1
first release
releases and cadence16, ~10d median
discovered viasap-ai-mcp-servers
first seen in the atlas
ecosystem and domainbtp, ai-and-agents
Contributors2

From the repository’s contributor list, .

History in the atlas5
  1. Triaged out of data/crawled/mcp-candidates.json, the sap-ai-mcp-servers sweep. Filed on the BTP door: Datasphere, Analytics Cloud, Business Data Cloud, BW/4HANA and HANA Cloud are BTP services, and the atlas has no data-and-analytics door — an open question recorded in docs/NEIGHBOURS.md rather than settled by a placement. Written from the repository's own README.
  2. capability:analytic-modelling was named the same day. Until then this artifact answered only "is an MCP server", which said what it is and not what it is for.
  3. capability:warehouse-loading was named the same day. Until then this artifact answered only "is an MCP server", which said what it is and not what it is for.
  4. BW/4HANA entered as an external rather than a service — it is installed software with releases, not something you subscribe to and bind — which finally gave this edge something to point at.
  5. First seen.
For your agent
Ref
artifact:dnic-dev-bw-modeling-mcp
JSON
/artifacts/dnic-dev-bw-modeling-mcp.json
MCP
Connecting an agent