sap-ai-dev-toolkit 0.8.1 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -23
- package/package.json +1 -1
- package/src/launcher.mjs +8 -2
- package/src/local-sap-gui.mjs +193 -11
- package/src/setup.mjs +38 -0
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
SAP AI Dev Toolkit connects your AI coding assistant to the SAP development tools available for each selected destination. Agents can inspect ABAP and CDS, propose changes, run checks, and return the evidence—without losing sight of which SAP system each operation targets.
|
|
34
34
|
|
|
35
35
|
<p align="center">
|
|
36
|
-
<strong>Agent chat</strong> → <strong>destination-scoped MCP</strong> → <strong>VSP + BAS
|
|
36
|
+
<strong>Agent chat</strong> → <strong>destination-scoped MCP</strong> → <strong>VSP + configured route (BAS, CF, or local SAP GUI)</strong> → <strong>SAP ADT</strong> → <strong>your SAP system</strong><br>
|
|
37
37
|
<em>results and diagnostics flow back along the same path</em>
|
|
38
38
|
</p>
|
|
39
39
|
|
|
@@ -52,7 +52,7 @@ To also add optional full-stack SAP companion MCP servers for Fiori, UI5, CAP, a
|
|
|
52
52
|
sap-ai-dev --setup --tools
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
After setup, connect a selected BAS destination or local SAP GUI system from your MCP-capable editor:
|
|
56
56
|
|
|
57
57
|
1. Open the Command Palette.
|
|
58
58
|
2. Run **MCP: List Servers**.
|
|
@@ -79,11 +79,11 @@ Use the attached destination-prefixed tools directly in chat. Do not launch `sap
|
|
|
79
79
|
|
|
80
80
|
**Included skills:** `abap-development` · `abap-testing-quality` · `cds-development` · `rap-development` · `abap-debugging` · `abap-runtime-analysis` · `rap-service-delivery` · `sap-standard-api-analysis` · `clean-core-extensibility` · `sap-sdlc-orchestration` · `sap-transport-release` · `hana-cloud-inspection` · `hana-cloud-native-development` · `hana-cloud-validation`
|
|
81
81
|
|
|
82
|
-
Agents and skills install for the harnesses you pick during global installation: a multi-select offers GitHub Copilot and Claude Code pre-checked, plus OpenAI Codex, Cursor, Gemini CLI, opencode, and Pi Coding Agent. In non-interactive installs they are installed for GitHub Copilot only. Repository-scoped installation instructions appear below.
|
|
82
|
+
Agents and skills install for the harnesses you pick during global installation: a multi-select offers GitHub Copilot and Claude Code pre-checked, plus OpenAI Codex, Cursor, Gemini CLI, opencode, and Pi Coding Agent. In non-interactive installs they are installed for GitHub Copilot only; run `sap-ai-dev --setup` afterwards to configure destinations or local SAP GUI systems. Repository-scoped installation instructions appear below.
|
|
83
83
|
|
|
84
84
|
## ✨ Your SAP development cockpit, inside your AI assistant
|
|
85
85
|
|
|
86
|
-
`sap-ai-dev-toolkit` installs `sap-ai-dev`, discovers your BAS destinations, and exposes a curated SAP development toolset through MCP. Instead of manually switching between chat, terminal commands, repository searches, ADT screens, and SAP checks, describe the outcome you want and let the appropriate bundled agent coordinate the available tools.
|
|
86
|
+
`sap-ai-dev-toolkit` installs `sap-ai-dev`, discovers your BAS destinations or local SAP GUI systems, and exposes a curated SAP development toolset through MCP. Instead of manually switching between chat, terminal commands, repository searches, ADT screens, and SAP checks, describe the outcome you want and let the appropriate bundled agent coordinate the available tools.
|
|
87
87
|
|
|
88
88
|
<p align="center"><strong>💬 Request → 🔎 Inspect → 🧠 Reason → 🧑💻 Implement → 🧪 Verify → 📋 Report</strong></p>
|
|
89
89
|
|
|
@@ -94,7 +94,7 @@ The workflow covers ABAP, CDS, RAP, repository analysis, table and query access,
|
|
|
94
94
|
| | Feature | What it gives you |
|
|
95
95
|
| --- | --- | --- |
|
|
96
96
|
| 🤖 | **AI-driven ABAP development** | Describe the outcome; a task-fit agent coordinates inspection, implementation, validation, and a clear report. |
|
|
97
|
-
| 🧭 | **Destination-aware by design** | Discover BAS destinations and connect each MCP server to one selected SAP system. |
|
|
97
|
+
| 🧭 | **Destination-aware by design** | Discover BAS destinations or local SAP GUI systems and connect each MCP server to one selected SAP system. |
|
|
98
98
|
| 🔎 | **Understand before editing** | Trace source, callers, definitions, package contents, dependencies, and CDS impact in one workflow. |
|
|
99
99
|
| 🧩 | **CDS + RAP development** | Explore models and dependencies, then build RAP business objects and services with system context. |
|
|
100
100
|
| 🗃️ | **Ground decisions in SAP data** | Inspect DDIC structures, read table contents, and run controlled ABAP SQL queries. |
|
|
@@ -242,11 +242,17 @@ Once the destination server is enabled in your chat's tools picker, ask for outc
|
|
|
242
242
|
|
|
243
243
|
## 📦 Installation
|
|
244
244
|
|
|
245
|
-
Install globally from SAP Business Application Studio or from a local workstation with SAP GUI installed:
|
|
245
|
+
Install globally from SAP Business Application Studio or from a local workstation with SAP GUI installed, then run setup explicitly for your environment:
|
|
246
246
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
247
|
+
| Environment / device | What to run | What setup does |
|
|
248
|
+
| --- | --- | --- |
|
|
249
|
+
| **SAP Business Application Studio dev space** | `npm install --global sap-ai-dev-toolkit`<br>`sap-ai-dev --setup` | Discovers BAS destinations, optionally imports Cloud Foundry Destination service records, and writes VS Code/BAS MCP entries. |
|
|
250
|
+
| **Windows workstation with SAP GUI** | `npm install --global sap-ai-dev-toolkit`<br>`sap-ai-dev --setup` | Discovers SAP GUI landscape entries, asks for SAP client(s) and ADT URL, then writes local VS Code MCP entries. Windows SSO can be selected if the ADT endpoint supports Negotiate/SPNEGO. |
|
|
251
|
+
| **macOS workstation with SAP GUI** | `npm install --global sap-ai-dev-toolkit`<br>`sap-ai-dev --setup` | Discovers SAP GUI landscape entries, asks for SAP client(s) and ADT URL, then writes local VS Code MCP entries with secure login prompts. |
|
|
252
|
+
| **Any local interactive terminal, already installed** | `sap-ai-dev --setup` | Re-runs the wizard to add, update, or remove generated MCP entries. Plain `sap-ai-dev` with no arguments also starts local setup when no BAS environment is detected. |
|
|
253
|
+
| **npm install hook did not show a wizard** | `sap-ai-dev --setup` | This is expected on some npm/Windows combinations because lifecycle scripts may run without an interactive console. The explicit setup command is the reliable path. |
|
|
254
|
+
| **Force npm to show install-time script output** | `npm install --global --foreground-scripts sap-ai-dev-toolkit` | Optional troubleshooting mode if you specifically want to see postinstall output during installation. Still run `sap-ai-dev --setup` afterwards if no destinations were configured. |
|
|
255
|
+
| **No global install / temporary setup** | `npx --yes --ignore-scripts --package=sap-ai-dev-toolkit sap-ai-dev --setup --npx` | Runs the guided setup through npm and writes generated entries that launch the pinned package through `npx`. |
|
|
250
256
|
|
|
251
257
|
If you previously installed the old package, remove it first because the legacy `bas-vsp-mcp` command is no longer shipped:
|
|
252
258
|
|
|
@@ -272,7 +278,7 @@ The installer handles VSP provisioning automatically:
|
|
|
272
278
|
|
|
273
279
|
Setup detects where it is running. With `H2O_URL` set, it treats the environment as SAP Business Application Studio and opens a checkbox picker for BAS destinations, with no destinations selected by default. Use **Space** to choose destinations and **Enter** to confirm. Press **a** to toggle all destinations (select all if any are unchecked; otherwise clear the selection). Confirming with none selected removes this add-on's managed MCP entries. When the `cf` CLI 8.18 or newer is authenticated to a targeted space, setup first offers an optional import from that space's Destination service; type **y** then **Enter** to include it, or press **Enter** to skip. Accepted CF and BAS destinations appear together in the picker.
|
|
274
280
|
|
|
275
|
-
Without `H2O_URL`, interactive setup looks for local SAP GUI system configuration on macOS and Windows (for example SAP GUI landscape XML and `saplogon.ini`). SAP GUI landscape files describe SAP GUI/DIAG connectivity, not the ADT HTTP(S) endpoint or a complete ABAP client catalog, so setup asks for the SAP client or comma-separated clients to configure for each selected system (for example `100,200`), proposes an ADT URL from the SAP GUI host and instance when it can, and asks you to confirm or edit that value. Multiple entered clients create separate isolated MCP entries, one per system/client pair. When the ADT URL uses an IP address, setup automatically inspects the SAP HTTPS certificate, extracts DNS subjectAltName entries, and offers to enable secure TLS self-healing with a simple yes/no confirmation; you do not need to type certificate names. The generated MCP entry uses VS Code input prompts for the SAP user and password at server start; raw SAP credentials are not stored in `mcp.json`. On Windows, setup can instead write an experimental `SAP_AUTH_MODE=windows-sso` entry without username/password prompts; this requires the ADT endpoint to support HTTP Negotiate/SPNEGO. Runtime uses the bundled Windows Negotiate helper by default, or a trusted custom helper configured with `SAP_AI_DEV_TOOLKIT_NEGOTIATE_HELPER`. npm may run its install hook without an interactive terminal, even when the shell is interactive; in that case, destination selection is skipped without changing MCP config.
|
|
281
|
+
Without `H2O_URL`, interactive setup looks for local SAP GUI system configuration on macOS and Windows (for example SAP GUI landscape XML and `saplogon.ini`). SAP GUI landscape files describe SAP GUI/DIAG connectivity, not the ADT HTTP(S) endpoint or a complete ABAP client catalog, so setup asks for the SAP client or comma-separated clients to configure for each selected system (for example `100,200`), proposes an ADT URL from the SAP GUI host and instance when it can, and asks you to confirm or edit that value. Multiple entered clients create separate isolated MCP entries, one per system/client pair. When the ADT URL uses an IP address, setup automatically inspects the SAP HTTPS certificate, extracts DNS subjectAltName entries, and offers to enable secure TLS self-healing with a simple yes/no confirmation; you do not need to type certificate names. The generated MCP entry uses VS Code input prompts for the SAP user and password at server start; raw SAP credentials are not stored in `mcp.json`. On Windows, setup can instead write an experimental `SAP_AUTH_MODE=windows-sso` entry without username/password prompts; this requires the ADT endpoint to support HTTP Negotiate/SPNEGO. Runtime uses the bundled Windows Negotiate helper by default, or a trusted custom helper configured with `SAP_AI_DEV_TOOLKIT_NEGOTIATE_HELPER`. npm may run its install hook without an interactive terminal, even when the shell is interactive; in that case, destination selection is skipped without changing MCP config, and you should run `sap-ai-dev --setup` afterwards.
|
|
276
282
|
|
|
277
283
|
### 🖥️ Local machine SAP GUI support
|
|
278
284
|
|
|
@@ -302,9 +308,9 @@ sap-ai-dev --setup
|
|
|
302
308
|
5. Choose password or Windows SSO when the prompt is available.
|
|
303
309
|
6. Start each generated MCP server from VS Code's **MCP: List Servers** command.
|
|
304
310
|
|
|
305
|
-
The same postinstall offers the bundled agents and skills for several AI coding harnesses in a checkbox picker. **GitHub Copilot** and **Claude Code** start pre-checked, so pressing **Enter** alone installs both; **Space** selects or deselects a harness, **a** toggles all seven, and confirming with none selected skips the step without changing files. When
|
|
311
|
+
The same postinstall offers the bundled agents and skills for several AI coding harnesses in a checkbox picker. **GitHub Copilot** and **Claude Code** start pre-checked, so pressing **Enter** alone installs both; **Space** selects or deselects a harness, **a** toggles all seven, and confirming with none selected skips the step without changing files. When MCP setup succeeds, the generated managed MCP servers are also mirrored into selected harnesses that have a stable user-level JSON MCP configuration (Claude Code, Cursor, Gemini CLI, and Pi Coding Agent). GitHub Copilot continues to use the VS Code/BAS MCP configuration written during setup. With no interactive terminal, the assets are installed for GitHub Copilot only; set `SAP_AI_DEV_TOOLKIT_HARNESSES` to a comma-separated list of harness ids (`github-copilot`, `claude-code`, `codex`, `cursor`, `gemini-cli`, `opencode`, `pi-coding-agent`) to choose non-interactively.
|
|
306
312
|
|
|
307
|
-
Some current npm versions also require install hooks to be approved. If npm reports that `sap-ai-dev-toolkit`'s `postinstall` was blocked, allow it during a fresh install with `npm install --global --allow-scripts=sap-ai-dev-toolkit sap-ai-dev-toolkit`, or run `sap-ai-dev --setup` from an interactive
|
|
313
|
+
Some current npm versions also require install hooks to be approved. If npm reports that `sap-ai-dev-toolkit`'s `postinstall` was blocked, allow it during a fresh install with `npm install --global --allow-scripts=sap-ai-dev-toolkit sap-ai-dev-toolkit`, or run `sap-ai-dev --setup` from an interactive terminal after installation.
|
|
308
314
|
|
|
309
315
|
The setup report uses icons and terminal colors; set `NO_COLOR=1` to disable ANSI colors. The table is a weather report, not a bouncer: green **PASS** means the ADT probe responded, red **FAIL** means it failed, and yellow **SKIPPED** means it was skipped. Probe failures do not block MCP registration or startup for destinations you select.
|
|
310
316
|
|
|
@@ -373,13 +379,13 @@ The package includes five user-invocable custom agents (**SAP Solution Architect
|
|
|
373
379
|
| 🧱 | `hana-cloud-native-development` | Create CAP CDS or required native HDI source artifacts while avoiding duplicate models. |
|
|
374
380
|
| ✅ | `hana-cloud-validation` | Build locally, review deployment risks, and hand off deployment to the user. |
|
|
375
381
|
|
|
376
|
-
### 📥 Install for your
|
|
382
|
+
### 📥 Install agents and skills for your user
|
|
377
383
|
|
|
378
|
-
|
|
384
|
+
During npm postinstall, after the destination or local SAP GUI setup step, the installer prints a 🤖 notice and a checkbox picker asking which AI coding harnesses should receive the bundled agents and skills. **Space** selects or deselects, **a** toggles all, **Enter** confirms. **GitHub Copilot** and **Claude Code** are pre-checked, so pressing **Enter** without changes installs both; confirming with none selected skips the step and leaves your files unchanged.
|
|
379
385
|
|
|
380
386
|
| Harness | Skills land in | Agents land in | MCP auto-wiring |
|
|
381
387
|
| --- | --- | --- | --- |
|
|
382
|
-
| GitHub Copilot | `$HOME/.copilot/skills/` | `$HOME/.copilot/agents/*.agent.md` | Uses the
|
|
388
|
+
| GitHub Copilot | `$HOME/.copilot/skills/` | `$HOME/.copilot/agents/*.agent.md` | Uses the VS Code/BAS `mcp.json` written by setup |
|
|
383
389
|
| Claude Code | `$HOME/.claude/skills/` | `$HOME/.claude/agents/<id>.md` (frontmatter reduced to `name` and `description`) | `$HOME/.claude.json` |
|
|
384
390
|
| OpenAI Codex | `$HOME/.agents/skills/` | — | Not changed automatically |
|
|
385
391
|
| Cursor | `$HOME/.cursor/skills/` | — | `$HOME/.cursor/mcp.json` |
|
|
@@ -391,13 +397,13 @@ All 14 skills use the portable `SKILL.md` format and are installed verbatim. The
|
|
|
391
397
|
|
|
392
398
|
MCP auto-wiring preserves unrelated servers in each harness config and replaces only entries previously managed by `sap-ai-dev-toolkit`. Generated entries carry `SAP_AI_DEV_TOOLKIT_MANAGED=true` in their server environment so future reinstalls can reconcile them without touching user-owned MCP servers.
|
|
393
399
|
|
|
394
|
-
These user-level customizations are available across workspaces opened by the same
|
|
400
|
+
These user-level customizations are available across workspaces opened by the same OS user (or the same BAS user in a dev space). The harness may need a window reload to discover new files. A non-interactive install skips the picker and installs for GitHub Copilot only; override with `SAP_AI_DEV_TOOLKIT_HARNESSES` (comma-separated harness ids). `npm install --ignore-scripts` skips the postinstall wizard entirely.
|
|
395
401
|
|
|
396
402
|
On reinstall or package upgrade, unchanged add-on-managed files are updated per harness. Existing customizations and files edited since the previous install are preserved; postinstall reports paths that need manual review instead of overwriting them.
|
|
397
403
|
|
|
398
404
|
### 🗂️ Add the agents and skills to a repository
|
|
399
405
|
|
|
400
|
-
To commit repository-scoped customizations, copy the packaged files from the
|
|
406
|
+
To commit repository-scoped customizations, copy the packaged files from the installed package without overwriting existing files:
|
|
401
407
|
|
|
402
408
|
```sh
|
|
403
409
|
ADDON_ROOT="$(npm root -g)/sap-ai-dev-toolkit"
|
|
@@ -417,7 +423,7 @@ Review skipped or conflicting files and merge changes manually. `LintABAP` analy
|
|
|
417
423
|
If the `cf` CLI is version 8.18 or newer and authenticated to a targeted space, setup offers an opt-in import from that space's SAP Destination service. It does not create service keys unless you accept. The imported records are limited to the current space and are merged with BAS destinations for selection. OnPremise destinations require choosing a Connectivity service instance; runtime traffic uses that service's proxy, and PrincipalPropagation uses the current CF user's token. Internet destinations use the configured HTTP(S) proxy environment. Setup stores only service-instance/key references in `mcp.json`, not the service-key credentials. At runtime the generated entry verifies the active CF space and resolves those references. Setup removes an add-on-created key only when no remaining CF entry references it and the CLI is targeted to the key's recorded space; keys are left untouched when the space cannot be verified.
|
|
418
424
|
Cloud Foundry HTTP destination probes use forward-form HTTP requests, not CONNECT tunnels; SAP Connectivity requires HTTP from the application to its proxy. OnPremise requests are canceled when the client disconnects or the runtime route closes.
|
|
419
425
|
|
|
420
|
-
Interactive npm install and `sap-ai-dev --setup` both use a checkbox picker with no destinations selected by default. Use **Space** to choose destinations, **Enter** to confirm, and **a** to toggle all (select all if any are unchecked; otherwise clear the selection). Confirming with none checked removes this package's generated MCP entries. Non-interactive installs skip destination selection without changing MCP config. Run the interactive setup command above; add `--npx` when the package is not installed globally.
|
|
426
|
+
Interactive npm install and `sap-ai-dev --setup` both use a checkbox picker with no destinations selected by default. Use **Space** to choose destinations, **Enter** to confirm, and **a** to toggle all (select all if any are unchecked; otherwise clear the selection). Confirming with none checked removes this package's generated MCP entries. Non-interactive installs skip destination or local SAP GUI system selection without changing MCP config. Run the interactive setup command above; add `--npx` when the package is not installed globally.
|
|
421
427
|
|
|
422
428
|
Setup tidies its own footprint: it reconciles MCP entries managed by this package and removes legacy `sapAiDev_*` / `basVspMcp_*` entries from earlier releases. Existing unrelated MCP servers and top-level configuration such as `inputs` stay untouched.
|
|
423
429
|
|
|
@@ -522,9 +528,9 @@ The file must be strict JSON with an object-valued `servers` property. Existing
|
|
|
522
528
|
|
|
523
529
|
With `H2O_URL` set, the normal command starts the MCP proxy. Each generated entry supplies one `SAP_AI_DEV_TOOLKIT_DESTINATION`, so each server stays in its own lane and exposes only its selected SAP system.
|
|
524
530
|
|
|
525
|
-
The proxy exposes a curated subset of VSP tools, plus local `LintABAP`, destination-scoped workflow tools, and the convenience `GetApplicationLog` mapping when the VSP SAP router is available. Hidden VSP tools remain available only behind local workflow chaining where required; they are not advertised or directly callable through the proxy. Direct VSP invocation remains
|
|
531
|
+
The proxy exposes a curated subset of VSP tools, plus local `LintABAP`, destination-scoped workflow tools, and the convenience `GetApplicationLog` mapping when the VSP SAP router is available. Hidden VSP tools remain available only behind local workflow chaining where required; they are not advertised or directly callable through the proxy. Direct VSP invocation remains available outside BAS when you pass an explicit VSP argument.
|
|
526
532
|
|
|
527
|
-
Without `H2O_URL`,
|
|
533
|
+
Without `H2O_URL`, generated local SAP GUI entries start a destination-scoped local MCP server. In an interactive local terminal with no arguments, `sap-ai-dev` starts local setup. Other non-setup arguments are passed directly to the installed VSP binary—no BAS proxy detour.
|
|
528
534
|
|
|
529
535
|
### 🔄 How an MCP tool call reaches SAP
|
|
530
536
|
|
|
@@ -678,7 +684,7 @@ The proxy starts VSP with `--enable-transports` and omits `--transport-read-only
|
|
|
678
684
|
- `messages: true` adds BALDAT details and T100 message text; otherwise, the tool returns log headers only.
|
|
679
685
|
- The proxy maps this convenience tool to the single upstream `SAP(action="analyze", type="application_log")` operation. The general-purpose `SAP` router itself is hidden from direct calls.
|
|
680
686
|
|
|
681
|
-
The proxy intentionally does not expose the full child VSP process. Object deletion, broad trace surfaces, arbitrary SAP router/RFC calls, debugger helper/delete-breakpoint routes, and transport release/delete tools are hidden from direct MCP calls. The curated debugger tools (`SetBreakpoint`, `GetBreakpoints`, `DebuggerAttach`, `DebuggerGetStack`, `DebuggerGetVariables`, `DebuggerStep`, and `DebuggerDetach`) remain available when VSP and the target system expose them. Direct VSP invocation without `H2O_URL` retains the VSP binary's own tool surface.
|
|
687
|
+
The proxy intentionally does not expose the full child VSP process. Object deletion, broad trace surfaces, arbitrary SAP router/RFC calls, debugger helper/delete-breakpoint routes, and transport release/delete tools are hidden from direct MCP calls. The curated debugger tools (`SetBreakpoint`, `GetBreakpoints`, `DebuggerAttach`, `DebuggerGetStack`, `DebuggerGetVariables`, `DebuggerStep`, and `DebuggerDetach`) remain available when VSP and the target system expose them. Direct VSP invocation without `H2O_URL` retains the VSP binary's own tool surface; in an interactive local terminal, use an explicit VSP argument because plain `sap-ai-dev` starts local setup.
|
|
682
688
|
|
|
683
689
|
### 🚀 Ready to put the tools to work from chat?
|
|
684
690
|
|
|
@@ -732,9 +738,9 @@ The MCP client discovers the schemas; callers do not need to memorize every argu
|
|
|
732
738
|
| What to inspect | How |
|
|
733
739
|
| --- | --- |
|
|
734
740
|
| Add-on command options | `sap-ai-dev --help` |
|
|
735
|
-
| BAS destinations and probe status | `sap-ai-dev --list-destinations --json` |
|
|
741
|
+
| BAS destinations and probe status | `sap-ai-dev --list-destinations --json` (BAS only) |
|
|
736
742
|
| Destination availability only | `sap-ai-dev --check` |
|
|
737
|
-
| Generated server names and destination mapping | **MCP: Open User Configuration**; look for lowercase slug entries (for example `demo-abap`) whose `SAP_AI_DEV_TOOLKIT_DESTINATION` names the BAS destination. |
|
|
743
|
+
| Generated server names and destination mapping | **MCP: Open User Configuration**; look for lowercase slug entries (for example `demo-abap`) whose `SAP_AI_DEV_TOOLKIT_DESTINATION` names the BAS destination or local SAP GUI system. |
|
|
738
744
|
| Running server | **MCP: List Servers**; select the server and choose **Start Server**. |
|
|
739
745
|
| Tools and exact schemas | Expand that server in the Chat tools picker. The MCP host requests `tools/list`, whose entries include `name`, `description`, and `inputSchema`; the agent should call those tools through chat, not reproduce the protocol in a terminal. |
|
|
740
746
|
| Runtime and tool-call logs | Select the MCP server in the Output view. Startup, call lifecycle, and child stderr logs are written to stderr; child MCP log notifications are forwarded to the client. Tool arguments and result contents are not logged. |
|
package/package.json
CHANGED
package/src/launcher.mjs
CHANGED
|
@@ -18,7 +18,8 @@ const root = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
|
18
18
|
const pkg = JSON.parse(await readFile(join(root, 'package.json'), 'utf8'));
|
|
19
19
|
const runtimeEnv = withBrandedEnvironment(process.env);
|
|
20
20
|
|
|
21
|
-
function
|
|
21
|
+
function args() { return process.argv.slice(2); }
|
|
22
|
+
function hasFlag(name) { return args().includes(name); }
|
|
22
23
|
function json(value) { process.stdout.write(`${JSON.stringify(value)}\n`); }
|
|
23
24
|
// Timestamped stderr lines so MCP server output shows when each event
|
|
24
25
|
// happened, not just that it did.
|
|
@@ -28,7 +29,7 @@ function usage() {
|
|
|
28
29
|
return [
|
|
29
30
|
'Usage: sap-ai-dev [options]',
|
|
30
31
|
'',
|
|
31
|
-
'With H2O_URL set, starts the SAP AI Dev Toolkit stdio server for selected BAS destinations; with local SAP GUI setup, starts the selected local ADT system.',
|
|
32
|
+
'With H2O_URL set, starts the SAP AI Dev Toolkit stdio server for selected BAS destinations; with local SAP GUI setup, starts the selected local ADT system. With no BAS environment and no options in an interactive terminal, starts local setup.',
|
|
32
33
|
'Options:',
|
|
33
34
|
' --setup Configure generated BAS MCP servers',
|
|
34
35
|
' --tools Also offer full-stack SAP companion MCP servers (use with --setup)',
|
|
@@ -294,6 +295,11 @@ async function main() {
|
|
|
294
295
|
}
|
|
295
296
|
|
|
296
297
|
if (!runtimeEnv.H2O_URL) {
|
|
298
|
+
if (args().length === 0 && process.stdin.isTTY && process.stdout.isTTY) {
|
|
299
|
+
console.error('sap-ai-dev: no BAS environment detected; starting local interactive setup. Use --help for other commands.');
|
|
300
|
+
await runSetup();
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
297
303
|
const binary = await binaryOrError();
|
|
298
304
|
const child = spawn(binary, process.argv.slice(2), { env: runtimeEnv, stdio: 'inherit' });
|
|
299
305
|
process.once('SIGINT', () => child.kill('SIGINT'));
|
package/src/local-sap-gui.mjs
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { platform, homedir } from 'node:os';
|
|
2
|
-
import {
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
import { execFile } from 'node:child_process';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { basename, dirname, extname, join } from 'node:path';
|
|
3
6
|
import { readdir, readFile, stat } from 'node:fs/promises';
|
|
4
7
|
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
9
|
+
|
|
5
10
|
function text(value) { return value == null ? '' : String(value).trim(); }
|
|
6
11
|
function decodeXml(value) {
|
|
7
12
|
return text(value)
|
|
@@ -18,25 +23,88 @@ async function exists(path) {
|
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
function unique(values) {
|
|
21
|
-
|
|
26
|
+
const seen = new Set();
|
|
27
|
+
return values.map(pathFromEnv).filter(Boolean).filter(value => {
|
|
28
|
+
const key = String(value).toLowerCase();
|
|
29
|
+
if (seen.has(key)) return false;
|
|
30
|
+
seen.add(key);
|
|
31
|
+
return true;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function pathFromEnv(value) {
|
|
36
|
+
const candidate = text(value);
|
|
37
|
+
if (!candidate) return '';
|
|
38
|
+
if (/^file:\/\//i.test(candidate)) {
|
|
39
|
+
try { return fileURLToPath(candidate); } catch { return candidate.replace(/^file:\/\/\/?/i, ''); }
|
|
40
|
+
}
|
|
41
|
+
return candidate;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const WINDOWS_LANDSCAPE_FILES = [
|
|
45
|
+
'SAPUILandscape.xml',
|
|
46
|
+
'SAPUILandscapeGlobal.xml',
|
|
47
|
+
'SAPGUILandscape.xml',
|
|
48
|
+
'SAPGUILandscapeGlobal.xml',
|
|
49
|
+
'SAPLogonTree.xml',
|
|
50
|
+
'SAPLogonTreeGlobal.xml',
|
|
51
|
+
'saplogon.ini'
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
const WINDOWS_EXPLICIT_PATH_ENV = [
|
|
55
|
+
'SAP_AI_DEV_SAP_GUI_LANDSCAPE',
|
|
56
|
+
'SAP_AI_DEV_SAP_GUI_CONFIG',
|
|
57
|
+
'SAPUILANDSCAPE',
|
|
58
|
+
'SAPGUI_LANDSCAPE',
|
|
59
|
+
'SAPLOGON_LSXML_FILE',
|
|
60
|
+
'SAPLOGON_INI_FILE'
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
function addWindowsSapGuiFiles(candidates, directory) {
|
|
64
|
+
if (!directory) return;
|
|
65
|
+
for (const name of WINDOWS_LANDSCAPE_FILES) candidates.push(join(directory, name));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function addSapGuiJavaFiles(candidates, directory) {
|
|
69
|
+
if (!directory) return;
|
|
70
|
+
candidates.push(join(directory, 'connections'));
|
|
71
|
+
addWindowsSapGuiFiles(candidates, directory);
|
|
22
72
|
}
|
|
23
73
|
|
|
24
74
|
export function sapGuiLandscapeCandidates(env = process.env) {
|
|
25
75
|
const home = env.HOME || env.USERPROFILE || homedir();
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
|
|
76
|
+
const userProfile = env.USERPROFILE || home;
|
|
77
|
+
const appData = env.APPDATA || (userProfile ? join(userProfile, 'AppData', 'Roaming') : '');
|
|
78
|
+
const localAppData = env.LOCALAPPDATA || (userProfile ? join(userProfile, 'AppData', 'Local') : '');
|
|
79
|
+
const programData = env.PROGRAMDATA || (platform() === 'win32' ? 'C:\\ProgramData' : '');
|
|
80
|
+
const publicProfile = env.PUBLIC || (platform() === 'win32' ? 'C:\\Users\\Public' : '');
|
|
81
|
+
const windir = env.WINDIR || env.SystemRoot || (platform() === 'win32' ? 'C:\\Windows' : '');
|
|
82
|
+
const candidates = WINDOWS_EXPLICIT_PATH_ENV.map(name => env[name]);
|
|
83
|
+
if (platform() === 'win32' || appData || localAppData || programData) {
|
|
84
|
+
for (const base of [appData, localAppData, programData, publicProfile]) {
|
|
85
|
+
if (!base) continue;
|
|
86
|
+
addWindowsSapGuiFiles(candidates, join(base, 'SAP', 'Common'));
|
|
87
|
+
addWindowsSapGuiFiles(candidates, join(base, 'SAP'));
|
|
88
|
+
}
|
|
29
89
|
candidates.push(
|
|
30
|
-
join(
|
|
31
|
-
join(
|
|
32
|
-
join(
|
|
90
|
+
join(windir || '', 'saplogon.ini'),
|
|
91
|
+
join(windir || '', 'SAPLogon.ini'),
|
|
92
|
+
join(userProfile || '', 'saplogon.ini'),
|
|
93
|
+
join(userProfile || '', 'SAPLogon.ini')
|
|
33
94
|
);
|
|
95
|
+
addSapGuiJavaFiles(candidates, join(userProfile || '', '.SAPGUI'));
|
|
96
|
+
addSapGuiJavaFiles(candidates, join(appData || '', 'SAPGUI'));
|
|
97
|
+
addSapGuiJavaFiles(candidates, join(localAppData || '', 'SAPGUI'));
|
|
98
|
+
if (env.ProgramFiles) addWindowsSapGuiFiles(candidates, join(env.ProgramFiles, 'SAP', 'FrontEnd', 'SAPgui'));
|
|
99
|
+
if (env['ProgramFiles(x86)']) addWindowsSapGuiFiles(candidates, join(env['ProgramFiles(x86)'], 'SAP', 'FrontEnd', 'SAPgui'));
|
|
34
100
|
}
|
|
35
101
|
candidates.push(
|
|
36
102
|
join(home, 'Library', 'Preferences', 'SAP', 'SAPGUILandscape.xml'),
|
|
37
103
|
join(home, 'Library', 'Preferences', 'SAP', 'SAPUILandscape.xml'),
|
|
38
104
|
join(home, 'Library', 'Preferences', 'SAP', 'connections'),
|
|
39
|
-
join(home, '.SAPGUI', 'SAPGUILandscape.xml')
|
|
105
|
+
join(home, '.SAPGUI', 'SAPGUILandscape.xml'),
|
|
106
|
+
join(home, '.SAPGUI', 'SAPUILandscape.xml'),
|
|
107
|
+
join(home, '.SAPGUI', 'connections')
|
|
40
108
|
);
|
|
41
109
|
return unique(candidates);
|
|
42
110
|
}
|
|
@@ -162,6 +230,113 @@ async function readGuiFile(path) {
|
|
|
162
230
|
return bytes.toString('utf8').replace(/^\uFEFF/, '');
|
|
163
231
|
}
|
|
164
232
|
|
|
233
|
+
function sapGuiSearchRoots(env = process.env) {
|
|
234
|
+
const home = env.HOME || env.USERPROFILE || homedir();
|
|
235
|
+
const userProfile = env.USERPROFILE || home;
|
|
236
|
+
return unique([
|
|
237
|
+
env.APPDATA && join(env.APPDATA, 'SAP'),
|
|
238
|
+
env.APPDATA && join(env.APPDATA, 'SAPGUI'),
|
|
239
|
+
userProfile && join(userProfile, 'AppData', 'Roaming', 'SAP'),
|
|
240
|
+
userProfile && join(userProfile, 'AppData', 'Roaming', 'SAPGUI'),
|
|
241
|
+
env.LOCALAPPDATA && join(env.LOCALAPPDATA, 'SAP'),
|
|
242
|
+
env.LOCALAPPDATA && join(env.LOCALAPPDATA, 'SAPGUI'),
|
|
243
|
+
userProfile && join(userProfile, 'AppData', 'Local', 'SAP'),
|
|
244
|
+
userProfile && join(userProfile, 'AppData', 'Local', 'SAPGUI'),
|
|
245
|
+
env.PROGRAMDATA && join(env.PROGRAMDATA, 'SAP'),
|
|
246
|
+
env.PUBLIC && join(env.PUBLIC, 'SAP'),
|
|
247
|
+
userProfile && join(userProfile, '.SAPGUI'),
|
|
248
|
+
home && join(home, 'Library', 'Preferences', 'SAP'),
|
|
249
|
+
home && join(home, '.SAPGUI')
|
|
250
|
+
]);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async function findSapGuiFilesUnder(root, { maxDepth = 4, maxEntries = 500 } = {}) {
|
|
254
|
+
const wanted = new Set(WINDOWS_LANDSCAPE_FILES.map(value => value.toLowerCase()).concat(['connections']));
|
|
255
|
+
const found = [];
|
|
256
|
+
let visited = 0;
|
|
257
|
+
async function walk(directory, depth) {
|
|
258
|
+
if (depth < 0 || visited >= maxEntries) return;
|
|
259
|
+
let entries;
|
|
260
|
+
try { entries = await readdir(directory, { withFileTypes: true }); } catch { return; }
|
|
261
|
+
for (const entry of entries) {
|
|
262
|
+
if (++visited > maxEntries) return;
|
|
263
|
+
const child = join(directory, entry.name);
|
|
264
|
+
if (entry.isFile() && wanted.has(basename(entry.name).toLowerCase())) found.push(child);
|
|
265
|
+
else if (entry.isDirectory()) {
|
|
266
|
+
if (basename(entry.name).toLowerCase() === 'connections') found.push(child);
|
|
267
|
+
await walk(child, depth - 1);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
await walk(root, maxDepth);
|
|
272
|
+
return found;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const SAP_GUI_REGISTRY_ROOTS = [
|
|
276
|
+
'HKCU\\Software\\SAP\\SAPLogon',
|
|
277
|
+
'HKCU\\Software\\SAP\\SAPLogon\\Options',
|
|
278
|
+
'HKCU\\Software\\WOW6432Node\\SAP\\SAPLogon',
|
|
279
|
+
'HKCU\\Software\\WOW6432Node\\SAP\\SAPLogon\\Options',
|
|
280
|
+
'HKLM\\Software\\SAP\\SAPLogon',
|
|
281
|
+
'HKLM\\Software\\SAP\\SAPLogon\\Options',
|
|
282
|
+
'HKLM\\Software\\WOW6432Node\\SAP\\SAPLogon',
|
|
283
|
+
'HKLM\\Software\\WOW6432Node\\SAP\\SAPLogon\\Options'
|
|
284
|
+
];
|
|
285
|
+
|
|
286
|
+
function registryPathCandidates(value) {
|
|
287
|
+
const raw = pathFromEnv(value);
|
|
288
|
+
if (!raw) return [];
|
|
289
|
+
const lower = raw.toLowerCase();
|
|
290
|
+
if (/\.(?:xml|ini)$/i.test(lower)) return [raw];
|
|
291
|
+
if (/\\|\//.test(raw) && !extname(raw)) {
|
|
292
|
+
const result = [];
|
|
293
|
+
addWindowsSapGuiFiles(result, raw);
|
|
294
|
+
result.push(join(raw, 'connections'));
|
|
295
|
+
return result;
|
|
296
|
+
}
|
|
297
|
+
if (/\.(?:xml|ini)(?:\s|$)/i.test(lower)) return [raw.split(/\s+/)[0]];
|
|
298
|
+
return [];
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async function windowsRegistrySapGuiCandidates(env = process.env) {
|
|
302
|
+
if (platform() !== 'win32') return [];
|
|
303
|
+
const candidates = [];
|
|
304
|
+
for (const root of SAP_GUI_REGISTRY_ROOTS) {
|
|
305
|
+
try {
|
|
306
|
+
const { stdout } = await execFileAsync('reg.exe', ['query', root, '/s'], { windowsHide: true, timeout: 2000, maxBuffer: 256 * 1024 });
|
|
307
|
+
for (const line of stdout.split(/\r?\n/)) {
|
|
308
|
+
const match = line.match(/^\s*\S+\s+REG_(?:SZ|EXPAND_SZ)\s+(.+?)\s*$/i);
|
|
309
|
+
if (!match) continue;
|
|
310
|
+
candidates.push(...registryPathCandidates(match[1].replace(/%([^%]+)%/g, (_, name) => env[name] || env[name.toUpperCase()] || `%${name}%`)));
|
|
311
|
+
}
|
|
312
|
+
} catch {
|
|
313
|
+
// Registry access can be blocked by policy or absent on non-standard installs.
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return unique(candidates);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
async function selfHealingSapGuiCandidates(env, candidates) {
|
|
320
|
+
const expanded = [...candidates, ...await windowsRegistrySapGuiCandidates(env)];
|
|
321
|
+
for (const root of sapGuiSearchRoots(env)) {
|
|
322
|
+
expanded.push(...await findSapGuiFilesUnder(root));
|
|
323
|
+
}
|
|
324
|
+
return unique(expanded);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function broadSapGuiSearchRoots(env = process.env) {
|
|
328
|
+
const home = env.HOME || env.USERPROFILE || homedir();
|
|
329
|
+
return unique([home, env.USERPROFILE, env.APPDATA, env.LOCALAPPDATA, env.PROGRAMDATA, env.PUBLIC]);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
async function broadSelfHealingSapGuiCandidates(env) {
|
|
333
|
+
const expanded = [];
|
|
334
|
+
for (const root of broadSapGuiSearchRoots(env)) {
|
|
335
|
+
expanded.push(...await findSapGuiFilesUnder(root, { maxDepth: 6, maxEntries: 5000 }));
|
|
336
|
+
}
|
|
337
|
+
return unique(expanded);
|
|
338
|
+
}
|
|
339
|
+
|
|
165
340
|
async function parseConnectionsFile(path) {
|
|
166
341
|
const content = await readGuiFile(path);
|
|
167
342
|
if (content.includes('<')) return parseLandscapeXml(content, path);
|
|
@@ -172,9 +347,9 @@ async function parseConnectionsFile(path) {
|
|
|
172
347
|
}).filter(Boolean);
|
|
173
348
|
}
|
|
174
349
|
|
|
175
|
-
|
|
350
|
+
async function parseSapGuiCandidatePaths(candidatePaths) {
|
|
176
351
|
const systems = [];
|
|
177
|
-
for (const path of unique(
|
|
352
|
+
for (const path of unique(candidatePaths)) {
|
|
178
353
|
if (!path) continue;
|
|
179
354
|
try {
|
|
180
355
|
if (!await exists(path)) continue;
|
|
@@ -197,6 +372,13 @@ export async function discoverSapGuiSystems({ env = process.env, paths = sapGuiL
|
|
|
197
372
|
// Ignore unreadable or unfamiliar SAP GUI files; discovery is best-effort.
|
|
198
373
|
}
|
|
199
374
|
}
|
|
375
|
+
return systems;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export async function discoverSapGuiSystems({ env = process.env, paths } = {}) {
|
|
379
|
+
const candidatePaths = paths ? unique(paths) : await selfHealingSapGuiCandidates(env, sapGuiLandscapeCandidates(env));
|
|
380
|
+
let systems = await parseSapGuiCandidatePaths(candidatePaths);
|
|
381
|
+
if (!systems.length && !paths) systems = await parseSapGuiCandidatePaths(await broadSelfHealingSapGuiCandidates(env));
|
|
200
382
|
const seen = new Set();
|
|
201
383
|
return systems.filter(system => {
|
|
202
384
|
const key = `${system.name}\0${system.host}\0${system.systemId}\0${system.instance}\0${system.client}`.toLowerCase();
|
package/src/setup.mjs
CHANGED
|
@@ -15,6 +15,41 @@ function print(output, message) {
|
|
|
15
15
|
output.write(`${message}\n`);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
function startSapGuiLandscapeAnimation(output, env = process.env) {
|
|
19
|
+
if (!output?.isTTY || !Number.isFinite(output?.columns)) return () => {};
|
|
20
|
+
if (env.TERM === 'dumb' || env.CI || env.SAP_AI_DEV_TOOLKIT_DISABLE_SCAN_ANIMATION === 'true') return () => {};
|
|
21
|
+
const frames = [
|
|
22
|
+
'=^.^= ~(o:>',
|
|
23
|
+
' =^.^= ~(o:>',
|
|
24
|
+
' =^.^= ~(o:>',
|
|
25
|
+
' =^.^= ~(o:>',
|
|
26
|
+
' =^.^= ~(o:>',
|
|
27
|
+
' =^.^= caught it!'
|
|
28
|
+
];
|
|
29
|
+
let index = 0;
|
|
30
|
+
let active = false;
|
|
31
|
+
const render = () => {
|
|
32
|
+
active = true;
|
|
33
|
+
const columns = Math.max(20, output.columns || 80);
|
|
34
|
+
const frame = frames[index % frames.length];
|
|
35
|
+
index += 1;
|
|
36
|
+
const line = `Scanning SAP GUI landscapes ${frame}`.slice(0, Math.max(1, columns - 1));
|
|
37
|
+
output.write(`\r\u001b[2K${line}`);
|
|
38
|
+
};
|
|
39
|
+
const delay = setTimeout(() => {
|
|
40
|
+
render();
|
|
41
|
+
interval = setInterval(render, 160);
|
|
42
|
+
if (typeof interval.unref === 'function') interval.unref();
|
|
43
|
+
}, 150);
|
|
44
|
+
if (typeof delay.unref === 'function') delay.unref();
|
|
45
|
+
let interval;
|
|
46
|
+
return () => {
|
|
47
|
+
clearTimeout(delay);
|
|
48
|
+
if (interval) clearInterval(interval);
|
|
49
|
+
if (active) output.write('\r\u001b[2K');
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
18
53
|
function windowsSsoSetupAvailable(env = process.env) {
|
|
19
54
|
return platform() === 'win32' || env.SAP_AI_DEV_TOOLKIT_ENABLE_WINDOWS_SSO_SETUP === 'true';
|
|
20
55
|
}
|
|
@@ -214,10 +249,13 @@ export async function runSetup({
|
|
|
214
249
|
destinations = basDestinations.map(safeBasDestination);
|
|
215
250
|
} else {
|
|
216
251
|
let localSystems;
|
|
252
|
+
const stopScanAnimation = startSapGuiLandscapeAnimation(output, env);
|
|
217
253
|
try {
|
|
218
254
|
localSystems = await discoverLocalSapGui({ env });
|
|
219
255
|
} catch (error) {
|
|
220
256
|
throw new Error(`SAP GUI discovery failed: ${error.message}`);
|
|
257
|
+
} finally {
|
|
258
|
+
stopScanAnimation();
|
|
221
259
|
}
|
|
222
260
|
destinations = localSystems.map(safeLocalSapGuiDestination);
|
|
223
261
|
}
|