sap-ai-dev-toolkit 0.9.9912 → 0.9.9914
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/.github/agents/abap-developer.agent.md +1 -1
- package/.github/agents/sap-integration-architect.agent.md +38 -0
- package/.github/agents/sap-integration-developer.agent.md +31 -0
- package/.github/agents/sap-solution-architect.agent.md +1 -0
- package/.github/skills/abap-development/SKILL.md +1 -1
- package/.github/skills/cds-development/SKILL.md +1 -1
- package/.github/skills/integration-suite-delivery/SKILL.md +74 -0
- package/.github/skills/sap-integration-architect/SKILL.md +76 -0
- package/NOTICE +10 -0
- package/README.md +36 -8
- package/dist/checksums.txt +5 -5
- package/dist/vsp-darwin-arm64 +0 -0
- package/dist/vsp-darwin-x64 +0 -0
- package/dist/vsp-linux-arm64 +0 -0
- package/dist/vsp-linux-x64 +0 -0
- package/dist/vsp-win32-x64.exe +0 -0
- package/inventory.md +66 -2
- package/package.json +5 -2
- package/patches/vsp-bas-proxy-auth.patch +88 -7
- package/scripts/run-live-integration.mjs +1079 -0
- package/src/apim-bundle.mjs +381 -0
- package/src/apim-tools.mjs +641 -0
- package/src/cpi-tools.mjs +1042 -0
- package/src/engineering-tools.mjs +105 -0
- package/src/iflow-bundle.mjs +282 -0
- package/src/integration-common.mjs +232 -0
- package/src/integration-config.mjs +167 -0
- package/src/integration-credentials.mjs +65 -0
- package/src/integration-http.mjs +266 -0
- package/src/integration-server.mjs +125 -0
- package/src/integration-templates/https-request-reply-http.iflw +391 -0
- package/src/integration-tools.mjs +27 -0
- package/src/integration-verify.mjs +118 -0
- package/src/launcher.mjs +14 -2
- package/src/mcp-config.mjs +18 -3
- package/src/setup.mjs +189 -11
- package/tools.md +81 -3
package/src/setup.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
1
2
|
import { createInterface } from 'node:readline/promises';
|
|
2
3
|
import { stdin, stdout } from 'node:process';
|
|
3
4
|
import { platform } from 'node:os';
|
|
@@ -7,6 +8,9 @@ import { discoverDestinations, remediation } from './bas-discovery.mjs';
|
|
|
7
8
|
import { adtBaseUrl, adtPort, defaultAdtUrl, discoverSapGuiSystems, hasExplicitPort, hasPathPrefix, messageServerAppServers } from './local-sap-gui.mjs';
|
|
8
9
|
import { discoverCloudFoundryDestinations, getCloudFoundryTarget, deleteManagedCloudFoundryServiceKeys, findOrphanedCloudFoundryServiceKeys } from './cf-destination.mjs';
|
|
9
10
|
import { SAP_DEVELOPMENT_MCP_SERVERS, collectCloudFoundryKeyReferencesFromAllEntries, collectManagedCloudFoundryKeyReferences, installMcpConfig, readMcpConfig, resolveMcpConfigPath } from './mcp-config.mjs';
|
|
11
|
+
import { parseIntegrationServiceKey, resolveIntegrationConfig, resolveIntegrationSideFromEnvironment } from './integration-config.mjs';
|
|
12
|
+
import { readStoredIntegrationKeys, storeIntegrationKey } from './integration-credentials.mjs';
|
|
13
|
+
import { createIntegrationClients, verificationLines, verifyIntegrationClients } from './integration-verify.mjs';
|
|
10
14
|
import { discoverCertificateDnsNames } from './tls-adt-proxy.mjs';
|
|
11
15
|
import { findRemoteControllableSsoBrowsers } from './sso-browsers.mjs';
|
|
12
16
|
|
|
@@ -268,14 +272,167 @@ function warnSsoIpHost(destination, label, output) {
|
|
|
268
272
|
print(output, formatStatus(`${label} uses an IP address; SSO needs the DNS host name for Kerberos and the browser certificate check. Enter the host name if the sign-in fails.`, 'warning', output, 'SSO'));
|
|
269
273
|
}
|
|
270
274
|
|
|
271
|
-
async function confirmPrompt(message, { input = stdin, output = stdout } = {}) {
|
|
275
|
+
async function confirmPrompt(message, { input = stdin, output = stdout, defaultYes = true } = {}) {
|
|
272
276
|
const answer = await textPrompt({
|
|
273
277
|
message,
|
|
274
|
-
placeholder: 'y',
|
|
278
|
+
placeholder: defaultYes ? 'y' : 'n',
|
|
275
279
|
required: false,
|
|
276
280
|
validate: value => /^(?:|y|yes|n|no)$/i.test(String(value || '').trim()) || 'Enter y or n.'
|
|
277
281
|
}, { input, output });
|
|
278
|
-
|
|
282
|
+
const trimmed = String(answer || '').trim();
|
|
283
|
+
if (!trimmed) return defaultYes;
|
|
284
|
+
return !/^n(?:o)?$/i.test(trimmed);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Credential status for the Integration Suite companion, without exposing any value: which sides
|
|
288
|
+
// (CPI, APIM) resolve from this environment or the stored-key file, and from which source.
|
|
289
|
+
export function integrationSuiteCredentialStatus(env = process.env) {
|
|
290
|
+
try {
|
|
291
|
+
const config = resolveIntegrationConfig(env, { stored: readStoredIntegrationKeys(env) });
|
|
292
|
+
const side = value => (value ? `configured from ${value.source}${value.bindingName ? ` (${value.bindingName})` : ''}, host ${new URL(value.url).host}` : 'not configured');
|
|
293
|
+
return { found: true, config, lines: [`CPI: ${side(config.cpi)}`, `APIM: ${side(config.apim)}`] };
|
|
294
|
+
} catch (error) {
|
|
295
|
+
const message = String(error?.message || '');
|
|
296
|
+
if (/^Configure CPI/.test(message)) return { found: false, config: null, lines: ['No CPI or APIM credentials were found in this environment.'] };
|
|
297
|
+
return { found: false, config: null, lines: [`Credentials found but not usable: ${message}`] };
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function defaultVerifyIntegration(config, { env }) {
|
|
302
|
+
return verifyIntegrationClients(createIntegrationClients(config, { env }));
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** Read a pasted service key: JSON text (line breaks from a multi-line paste are dropped) or a path to a .json file. */
|
|
306
|
+
function pastedServiceKey(side, value, env) {
|
|
307
|
+
const trimmed = String(value || '').trim();
|
|
308
|
+
let text = trimmed;
|
|
309
|
+
if (!trimmed.startsWith('{')) {
|
|
310
|
+
const path = trimmed.replace(/^~(?=$|[\\/])/, env.HOME || env.USERPROFILE || '~');
|
|
311
|
+
try {
|
|
312
|
+
text = readFileSync(path, 'utf8');
|
|
313
|
+
} catch {
|
|
314
|
+
throw new Error('Paste the service key JSON (starting with {) or the path to a readable .json file.');
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return parseIntegrationServiceKey(side, text);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async function printIntegrationVerification(config, { env, output, verifyIntegration }) {
|
|
321
|
+
const stop = startProgress('Signing in to Integration Suite and checking each tool area', { output, env, label: 'Integration Suite' });
|
|
322
|
+
let report;
|
|
323
|
+
try {
|
|
324
|
+
report = await verifyIntegration(config, { env });
|
|
325
|
+
} finally {
|
|
326
|
+
stop();
|
|
327
|
+
}
|
|
328
|
+
const host = side => (config[side] ? new URL(config[side].url).host : '');
|
|
329
|
+
for (const line of [...verificationLines('CPI', host('cpi'), report.cpi), ...verificationLines('APIM', host('apim'), report.apim)]) print(output, ` ${line}`);
|
|
330
|
+
const missing = [report.cpi, report.apim].flatMap(side => side?.checks || []).filter(check => check.result === 'forbidden').length;
|
|
331
|
+
if (report.ok) {
|
|
332
|
+
print(output, formatStatus(`Integration Suite credentials verified${missing ? `; ${missing} area${missing === 1 ? ' needs' : 's need'} an extra role on the service key` : ''}.`, missing ? 'warning' : 'success', output, 'Integration Suite'));
|
|
333
|
+
} else {
|
|
334
|
+
print(output, formatStatus('Integration Suite verification failed; fix or replace the service key before using the tools.', 'warning', output, 'Integration Suite'));
|
|
335
|
+
}
|
|
336
|
+
return report;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Verify the detected credentials online and let the user paste CPI/APIM service keys. A pasted key is checked
|
|
340
|
+
// online first and saved to the user-only store (never to mcp.json); it is used when the environment has no key.
|
|
341
|
+
async function configureIntegrationCredentials({ env, input, output, verifyIntegration }) {
|
|
342
|
+
let status = integrationSuiteCredentialStatus(env);
|
|
343
|
+
if (status.found) await printIntegrationVerification(status.config, { env, output, verifyIntegration });
|
|
344
|
+
for (;;) {
|
|
345
|
+
print(output, '');
|
|
346
|
+
const choice = await selectPrompt({
|
|
347
|
+
message: colorText('🔑 Integration Suite credentials', 'cyan', output),
|
|
348
|
+
defaultValue: status.found ? 'done' : 'cpi',
|
|
349
|
+
choices: [
|
|
350
|
+
{ name: status.found ? 'Continue' : 'Continue without credentials (the server will not start until a key is configured)', value: 'done', shortcut: 'c' },
|
|
351
|
+
{ name: 'Paste a Cloud Integration (CPI) service key — Process Integration Runtime, plan api', value: 'cpi', shortcut: 'p' },
|
|
352
|
+
{ name: 'Paste an API Management service key — API Management, plan apiportal-apiaccess', value: 'apim', shortcut: 'a' },
|
|
353
|
+
...(status.found ? [{ name: 'Check the configured credentials online again', value: 'verify', shortcut: 'v' }] : [])
|
|
354
|
+
]
|
|
355
|
+
}, { input, output });
|
|
356
|
+
if (choice === 'done') return status;
|
|
357
|
+
if (choice === 'verify') {
|
|
358
|
+
await printIntegrationVerification(status.config, { env, output, verifyIntegration });
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
const side = choice.toUpperCase();
|
|
362
|
+
const pasted = await textPrompt({
|
|
363
|
+
message: `Paste the ${side} service key JSON or the path to its .json file`,
|
|
364
|
+
secret: true,
|
|
365
|
+
validate: value => {
|
|
366
|
+
try {
|
|
367
|
+
pastedServiceKey(side, value, env);
|
|
368
|
+
return true;
|
|
369
|
+
} catch (error) {
|
|
370
|
+
return error.message;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}, { input, output });
|
|
374
|
+
const config = pastedServiceKey(side, pasted, env);
|
|
375
|
+
const report = await printIntegrationVerification({ [choice]: config }, { env, output, verifyIntegration });
|
|
376
|
+
if (!report[choice].signed_in && !await confirmPrompt('Save this key anyway? (y/n, Enter=no)', { input, output, defaultYes: false })) continue;
|
|
377
|
+
const path = await storeIntegrationKey(side, config, env);
|
|
378
|
+
print(output, formatStatus(`Saved the ${side} key to ${path} (readable only by you; not written to mcp.json).`, 'success', output, 'Integration Suite'));
|
|
379
|
+
let fromEnvironment = null;
|
|
380
|
+
try {
|
|
381
|
+
fromEnvironment = resolveIntegrationSideFromEnvironment(env, side);
|
|
382
|
+
} catch {
|
|
383
|
+
// A broken environment value is reported by the status lines below.
|
|
384
|
+
}
|
|
385
|
+
if (fromEnvironment) print(output, formatStatus(`${side} credentials from ${fromEnvironment.source} in this environment take precedence over the saved key.`, 'warning', output, 'Integration Suite'));
|
|
386
|
+
status = integrationSuiteCredentialStatus(env);
|
|
387
|
+
for (const line of status.lines) print(output, ` ${line}`);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
async function promptIntegrationSuite({ env, alreadyConfigured, input, output, verifyIntegration = defaultVerifyIntegration, ask = true }) {
|
|
392
|
+
print(output, '');
|
|
393
|
+
print(output, formatStatus('SAP Integration Suite tools (Cloud Integration + API Management).', 'step', output, 'Integration Suite'));
|
|
394
|
+
print(output, ' Adds the sap-ai-integration MCP server: iFlows, value mappings, security material and monitoring in CPI; API providers, proxies, policies, products and key value maps in APIM.');
|
|
395
|
+
print(output, ' Every write needs an exact confirmation string; there are no delete tools and secrets are write-only. Agents: SAP Integration Architect, SAP Integration Developer.');
|
|
396
|
+
if (alreadyConfigured) print(output, ' Already configured in this MCP config; keeping it and refreshing its version.');
|
|
397
|
+
const status = integrationSuiteCredentialStatus(env);
|
|
398
|
+
for (const line of status.lines) print(output, ` ${line}`);
|
|
399
|
+
if (!status.found) {
|
|
400
|
+
print(output, ' The server needs CPI_SERVICE_KEY and/or APIM_SERVICE_KEY (or CPI_*/APIM_* variables, VCAP_SERVICES) in the MCP host environment,');
|
|
401
|
+
print(output, ' or a service key pasted in the next step (verified online, then saved for your user only). Credentials are never written to mcp.json.');
|
|
402
|
+
}
|
|
403
|
+
if (ask && !alreadyConfigured) {
|
|
404
|
+
print(output, '');
|
|
405
|
+
const defaultYes = status.found;
|
|
406
|
+
if (!await confirmPrompt(`Add the SAP Integration Suite tools? (y/n, Enter=${defaultYes ? 'yes' : 'no'})`, { input, output, defaultYes })) return false;
|
|
407
|
+
}
|
|
408
|
+
await configureIntegrationCredentials({ env, input, output, verifyIntegration });
|
|
409
|
+
return true;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/** `sap-ai-dev --setup-integration`: only the Integration Suite step (paste, verify, add the server), keeping every other MCP entry. */
|
|
413
|
+
export async function runIntegrationSuiteSetup({
|
|
414
|
+
env = process.env,
|
|
415
|
+
input = stdin,
|
|
416
|
+
output = stdout,
|
|
417
|
+
install = installMcpConfig,
|
|
418
|
+
verifyIntegration = defaultVerifyIntegration
|
|
419
|
+
} = {}) {
|
|
420
|
+
if (!input.isTTY || !output.isTTY) {
|
|
421
|
+
print(output, formatStatus('Interactive setup needs a terminal. Rerun sap-ai-dev --setup-integration from a terminal.', 'warning', output, 'SAP AI Dev Toolkit'));
|
|
422
|
+
return { skipped: true, reason: 'non-tty' };
|
|
423
|
+
}
|
|
424
|
+
let alreadyConfigured = false;
|
|
425
|
+
try {
|
|
426
|
+
alreadyConfigured = Boolean((await readMcpConfig(await resolveMcpConfigPath(env)))?.servers?.['integration-suite']);
|
|
427
|
+
} catch {
|
|
428
|
+
// A missing or unreadable config is created or healed by install.
|
|
429
|
+
}
|
|
430
|
+
await promptIntegrationSuite({ env, alreadyConfigured, input, output, verifyIntegration, ask: false });
|
|
431
|
+
const result = await install([], { env, sapDevelopmentServers: ['integration-suite'], additive: true });
|
|
432
|
+
print(output, '');
|
|
433
|
+
print(output, formatStatus(`Configured the Integration Suite MCP server${result?.path ? ` in ${result.path}` : ''}.`, 'success', output, 'Integration Suite'));
|
|
434
|
+
for (const message of Array.isArray(result?.warnings) ? result.warnings : []) print(output, formatStatus(message, 'warning', output, 'Warning'));
|
|
435
|
+
return { selected: [], ...(result || {}) };
|
|
279
436
|
}
|
|
280
437
|
|
|
281
438
|
function buildLocalClientDestination(destination, client, authMode, { multipleClients = false, duplicateSystemName = false, browserPath } = {}) {
|
|
@@ -613,7 +770,8 @@ export async function runSetup({
|
|
|
613
770
|
install = installMcpConfig,
|
|
614
771
|
includeSapDevelopmentToolsPrompt = false,
|
|
615
772
|
detectAdtUrl,
|
|
616
|
-
discoverSsoBrowsers = findRemoteControllableSsoBrowsers
|
|
773
|
+
discoverSsoBrowsers = findRemoteControllableSsoBrowsers,
|
|
774
|
+
verifyIntegration = defaultVerifyIntegration
|
|
617
775
|
} = {}) {
|
|
618
776
|
const isBas = Boolean(env.H2O_URL);
|
|
619
777
|
if (!isBas && (!input.isTTY || !output.isTTY)) {
|
|
@@ -661,11 +819,13 @@ export async function runSetup({
|
|
|
661
819
|
let configPath;
|
|
662
820
|
let managedKeys = [];
|
|
663
821
|
let previousAdtUrls = new Map();
|
|
822
|
+
let integrationSuiteConfigured = false;
|
|
664
823
|
try {
|
|
665
824
|
configPath = await resolveMcpConfigPath(env);
|
|
666
825
|
const existingConfig = await readMcpConfig(configPath);
|
|
667
826
|
managedKeys = collectManagedCloudFoundryKeyReferences(existingConfig);
|
|
668
827
|
previousAdtUrls = previousLocalAdtUrls(existingConfig);
|
|
828
|
+
integrationSuiteConfigured = Boolean(existingConfig?.servers?.['integration-suite']);
|
|
669
829
|
} catch {
|
|
670
830
|
warnings.push('Existing MCP config could not be read before setup; existing CF service keys will not be reused.');
|
|
671
831
|
}
|
|
@@ -750,6 +910,7 @@ export async function runSetup({
|
|
|
750
910
|
};
|
|
751
911
|
let selected;
|
|
752
912
|
let additiveSetup = false;
|
|
913
|
+
let integrationOnly = false;
|
|
753
914
|
if (!selectable.length) {
|
|
754
915
|
// Nothing was detected — no BAS/CF destinations, or no SAP GUI entries.
|
|
755
916
|
// Offer the manual MCP entry wizard before giving up.
|
|
@@ -761,23 +922,34 @@ export async function runSetup({
|
|
|
761
922
|
defaultValue: 'manual',
|
|
762
923
|
choices: [
|
|
763
924
|
{ name: '✍️ Start manual entry wizard (system name, ADT URL, client, login)', value: 'manual', shortcut: 'm' },
|
|
925
|
+
{ name: '🔌 Set up only the SAP Integration Suite tools (paste and verify CPI / API Management service keys)', value: 'integration', shortcut: 'i' },
|
|
764
926
|
{ name: 'Exit setup without changes', value: 'exit', shortcut: 'e' }
|
|
765
927
|
]
|
|
766
928
|
}, { input, output });
|
|
767
|
-
if (choice
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
929
|
+
if (choice === 'integration') {
|
|
930
|
+
selected = [];
|
|
931
|
+
additiveSetup = true;
|
|
932
|
+
integrationOnly = true;
|
|
933
|
+
} else {
|
|
934
|
+
if (choice !== 'manual') return await exitWithoutDestinations();
|
|
935
|
+
selected = await runManualEntryWizard({ env, input, output, detectAdtUrl, selectSsoBrowser });
|
|
936
|
+
additiveSetup = true;
|
|
937
|
+
if (!selected.length) return await exitWithoutDestinations();
|
|
938
|
+
}
|
|
771
939
|
} else {
|
|
772
940
|
selected = await chooseDiscoveredDestinations(destinations, { isBas, input, output, env, detectAdtUrl, previousAdtUrls, selectSsoBrowser });
|
|
773
941
|
additiveSetup = selected.length > 0;
|
|
774
942
|
}
|
|
775
943
|
|
|
776
944
|
let sapDevelopmentServers = [];
|
|
777
|
-
if (
|
|
945
|
+
if (integrationOnly) {
|
|
946
|
+
await promptIntegrationSuite({ env, alreadyConfigured: integrationSuiteConfigured, input, output, verifyIntegration, ask: false });
|
|
947
|
+
sapDevelopmentServers = ['integration-suite'];
|
|
948
|
+
} else if (includeSapDevelopmentToolsPrompt) {
|
|
778
949
|
print(output, '');
|
|
779
|
-
print(output, formatStatus('Optionally add companion MCP servers for full-stack SAP development and
|
|
950
|
+
print(output, formatStatus('Optionally add companion MCP servers for full-stack SAP development, HANA inspection, and Integration Suite (CPI/API Management).', 'step', output, 'Tools'));
|
|
780
951
|
print(output, ' HANA Cloud inspector is read-only and uses HANA_RO_* or VCAP_SERVICES already present in the MCP host environment; credentials are not written to mcp.json.');
|
|
952
|
+
print(output, ' Integration Suite uses CPI/APIM service keys from the MCP host environment or keys you paste next (verified online, saved for your user only); credentials are not written to mcp.json.');
|
|
781
953
|
print(output, ' Leave empty to only configure ABAP/ADT destination servers.');
|
|
782
954
|
print(output, '');
|
|
783
955
|
sapDevelopmentServers = await checkboxPrompt({
|
|
@@ -785,11 +957,17 @@ export async function runSetup({
|
|
|
785
957
|
choices: SAP_DEVELOPMENT_MCP_SERVERS.map(server => ({
|
|
786
958
|
value: server.id,
|
|
787
959
|
name: `${server.name} — ${server.description}`,
|
|
788
|
-
checked:
|
|
960
|
+
checked: server.id === 'integration-suite' && integrationSuiteConfigured
|
|
789
961
|
})),
|
|
790
962
|
required: false,
|
|
791
963
|
shortcuts: { all: 'a' }
|
|
792
964
|
}, { input, output });
|
|
965
|
+
if (sapDevelopmentServers.includes('integration-suite')) {
|
|
966
|
+
await promptIntegrationSuite({ env, alreadyConfigured: integrationSuiteConfigured, input, output, verifyIntegration, ask: false });
|
|
967
|
+
}
|
|
968
|
+
} else if (await promptIntegrationSuite({ env, alreadyConfigured: integrationSuiteConfigured, input, output, verifyIntegration })) {
|
|
969
|
+
// Without --tools, Integration Suite still gets its own yes/no step instead of being hidden.
|
|
970
|
+
sapDevelopmentServers = ['integration-suite'];
|
|
793
971
|
}
|
|
794
972
|
|
|
795
973
|
try {
|
package/tools.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Curated SAP tools exposed through the BAS proxy
|
|
2
2
|
|
|
3
|
-
The proxy no longer advertises every tool provided by the SAP runtime. To keep the developer-lifecycle MCP surface manageable, each generated one-destination server exposes a curated set of upstream tools plus local workflow tools (
|
|
3
|
+
The proxy no longer advertises every tool provided by the SAP runtime. To keep the developer-lifecycle MCP surface manageable, each generated one-destination server exposes a curated set of upstream tools plus local workflow tools (up to 75 tools: 74 destination-scoped tools plus one workspace-scoped project-lint tool).
|
|
4
4
|
|
|
5
5
|
The names below are the logical tool names. Publicly they are exposed in lowercase snake_case without a prefix (`get_table_contents`, `run_query`) because each generated MCP server is scoped to one destination; only when a single server fronts multiple destinations does each name gain its destination slug (`demo-abap_get_table_contents`) to stay unambiguous. BAS/VS Code chat tool references bind only lowercase identifiers, so mixed-case names never reach the model.
|
|
6
6
|
|
|
@@ -73,11 +73,11 @@ Hidden upstream tools are not directly callable through the proxy. Local workflo
|
|
|
73
73
|
- `UnpublishServiceBinding`
|
|
74
74
|
- `WriteSource`
|
|
75
75
|
|
|
76
|
-
`CreatePackage` creates a local package or a transportable package with an explicit transport; it changes SAP state and requires explicit approval. `CreateTable` creates a transparent DDIC table, writes its generated DDL source, and activates it. `GetStructure`, `GetTypeInfo`, and `GetMessages` are read-only metadata lookups. The upstream VSP package and transport mutation checks still apply. The pinned backend does not create structures, table types, or authorization objects.
|
|
76
|
+
`CreatePackage` creates a local package or a transportable package with an explicit transport; it changes SAP state and requires explicit approval. `CreateTable` creates a transparent DDIC table, writes its generated DDL source, and activates it. Client-dependent tables already get the implicit `CLIENT` key; supplied `CLIENT` or `MANDT` fields map to that same key and are not emitted twice. `GetStructure`, `GetTypeInfo`, and `GetMessages` are read-only metadata lookups. The upstream VSP package and transport mutation checks still apply. The pinned backend does not create structures, table types, or authorization objects.
|
|
77
77
|
|
|
78
78
|
## Hidden from direct proxy calls
|
|
79
79
|
|
|
80
|
-
Broad/destructive surfaces such as object deletion, SQL trace, arbitrary SAP router calls, transport release/delete, RFC execution, debugger listen/delete-breakpoint helpers, and one-off generator/update helpers are intentionally not advertised by the BAS proxy. UI5 repository writes are hidden too: the ADT filestore only reads UI5 apps, so deploy them with SAP Fiori tools instead.
|
|
80
|
+
Broad/destructive surfaces such as generic object deletion, SQL trace, arbitrary SAP router calls, transport release/delete, RFC execution, debugger listen/delete-breakpoint helpers, and one-off generator/update helpers are intentionally not advertised by the BAS proxy. The local `DeleteEmptyDDLS` workflow is a narrow exception: it verifies the source is blank and requires exact confirmation before deleting a Z/Y DDLS shell. UI5 repository writes are hidden too: the ADT filestore only reads UI5 apps, so deploy them with SAP Fiori tools instead.
|
|
81
81
|
|
|
82
82
|
## Local and destination-scoped workflow tools
|
|
83
83
|
|
|
@@ -86,6 +86,8 @@ The proxy adds local tools to the MCP surface. Some are workspace-wide; others a
|
|
|
86
86
|
- `LintABAP` — local in-memory ABAP linting.
|
|
87
87
|
- `LintABAPProject` — scan serialized ABAP objects in the configured workspace root (SAP_AI_DEV_TOOLKIT_WORKSPACE_ROOT, defaulting to the MCP server working directory) using the project abaplint config and available local dependency folders. This is static analysis only; it does not contact SAP or activate objects.
|
|
88
88
|
- `GetApplicationLog` — bounded convenience mapping to the upstream SAP application-log route when available.
|
|
89
|
+
- `UpdateTable` — update and activate an existing Z/Y DDIC table from replacement DDL after comparing expected_source with `GetTable`; the write uses VSP's hash check under lock and requires `UPDATE TABLE <TABLE_NAME>` confirmation.
|
|
90
|
+
- `DeleteEmptyDDLS` — delete only a blank Z/Y DDLS source after an emptiness check and exact `DELETE EMPTY DDLS <OBJECT_NAME>` confirmation. It calls the hidden generic delete operation internally.
|
|
89
91
|
- `PrepareABAPChangeSet` and `ApplyABAPChangeSet` — review full-source diffs and recheck source before explicit writes.
|
|
90
92
|
- `CheckTransportReadiness` — collect a whitelisted bundle of transport, dependency, inactive-object, ABAP Unit, and ATC results.
|
|
91
93
|
- `PlanABAPCloudMigration` — batch SAP API release-state checks and prioritize recognized unreleased results.
|
|
@@ -108,3 +110,79 @@ The optional `hana-cloud-inspector` companion starts `sap-ai-hana` as a separate
|
|
|
108
110
|
- `hana_read_rows` — perform parameterized reads of selected columns, capped at 200 rows; credential-like columns are blocked and arbitrary SQL is not accepted.
|
|
109
111
|
|
|
110
112
|
Connection secrets are read from the MCP host environment via explicit `HANA_RO_*` variables or a selected `VCAP_SERVICES` binding and are not written to `mcp.json`. TLS certificate verification is mandatory. No HANA write, DDL/DML, deploy/undeploy, grants, or service-key tools are exposed; deployment remains a user-run project workflow.
|
|
113
|
+
|
|
114
|
+
## Separate optional Integration Suite server
|
|
115
|
+
|
|
116
|
+
The optional `integration-suite` companion starts `sap-ai-integration` as a separate stdio MCP server for SAP Cloud Integration (CPI) and API Management (APIM). It does not use the BAS proxy or a destination prefix. Tools for a side appear only when that side is configured:
|
|
117
|
+
|
|
118
|
+
- `integration_connection_info` — show which of CPI/APIM are configured, the credential source, and tenant hosts; with `verify: true` it signs in and reports per tool area whether the service key has the needed role. Never returns client ids or secrets.
|
|
119
|
+
|
|
120
|
+
Cloud Integration — design time:
|
|
121
|
+
|
|
122
|
+
- `cpi_list_packages` — list Cloud Integration packages.
|
|
123
|
+
- `cpi_list_iflows` — list the iFlows of one package.
|
|
124
|
+
- `cpi_list_package_artifacts` — list a package's iFlows, value mappings, script collections, and message mappings.
|
|
125
|
+
- `cpi_get_iflow` — read iFlow metadata, externalized configuration, and bundle files (or one file by path).
|
|
126
|
+
- `cpi_get_artifact` — read a value mapping, script collection, or message mapping with its bundle files.
|
|
127
|
+
- `cpi_get_value_mappings` — read value-mapping agency pairs with their value and default pairs.
|
|
128
|
+
- `cpi_create_package` — create a package.
|
|
129
|
+
- `cpi_update_package` — change package name, short text, description, or version.
|
|
130
|
+
- `cpi_copy_package` — copy a Discover package into Design (new copy with suffix, or overwrite-merge keeping configuration).
|
|
131
|
+
- `cpi_create_iflow` — create an iFlow from the vetted HTTPS → Request Reply → HTTP template or caller-supplied BPMN.
|
|
132
|
+
- `cpi_update_iflow` — replace/add bundle files and parameters with an expected-version check and patch-version bump.
|
|
133
|
+
- `cpi_update_iflow_configuration` — set externalized configuration parameters.
|
|
134
|
+
- `cpi_upsert_value_mappings` — add or change value-mapping pairs.
|
|
135
|
+
- `cpi_update_script_collection` — add or replace scripts in a script collection.
|
|
136
|
+
|
|
137
|
+
Cloud Integration — deployment and monitoring:
|
|
138
|
+
|
|
139
|
+
- `cpi_deploy_iflow` — deploy the active iFlow version and wait for build and runtime status.
|
|
140
|
+
- `cpi_deploy_artifact` — deploy a value mapping, script collection, or message mapping and wait for runtime status.
|
|
141
|
+
- `cpi_undeploy_artifact` — undeploy any runtime artifact and wait until it is gone.
|
|
142
|
+
- `cpi_list_runtime_artifacts` — list deployed artifacts, optionally by status.
|
|
143
|
+
- `cpi_get_iflow_runtime_status` — read deployed runtime status, including error text when the iFlow failed to start.
|
|
144
|
+
- `cpi_list_service_endpoints` — list deployed endpoint URLs, optionally for one iFlow.
|
|
145
|
+
- `cpi_list_message_logs` — list message processing logs by iFlow, status, time window, correlation id, application message id, or custom header.
|
|
146
|
+
- `cpi_get_message_log` — read one message log with error text and custom header properties; payload attachments are never read.
|
|
147
|
+
- `cpi_get_message_log_steps` — read the processing steps of a message's latest run with step errors.
|
|
148
|
+
- `cpi_get_jms_broker_status` — read JMS broker capacity and queues that are not healthy.
|
|
149
|
+
- `cpi_list_data_stores` — list data stores with message and overdue counts.
|
|
150
|
+
- `cpi_list_data_store_entries` — list entry metadata of one data store; payloads are never read.
|
|
151
|
+
- `cpi_list_variables` — list global and iFlow variables (metadata only).
|
|
152
|
+
|
|
153
|
+
Cloud Integration — security material and tenant configuration:
|
|
154
|
+
|
|
155
|
+
- `cpi_list_security_material` — list user credentials, OAuth2 client credentials, or keystore entries with secret fields redacted.
|
|
156
|
+
- `cpi_set_user_credential` — create or update a user credential; the password is write-only.
|
|
157
|
+
- `cpi_set_oauth2_client_credential` — create or update an OAuth2 client credential; the client secret is write-only.
|
|
158
|
+
- `cpi_upload_certificate` — add or replace a public certificate in the tenant keystore.
|
|
159
|
+
- `cpi_list_number_ranges` — list number ranges.
|
|
160
|
+
- `cpi_set_number_range` — create or update a number range.
|
|
161
|
+
- `cpi_list_partner_parameters` — list Partner Directory string parameters and binary parameter metadata.
|
|
162
|
+
- `cpi_set_partner_parameter` — create or update a Partner Directory string or binary parameter.
|
|
163
|
+
- `cpi_odata_get` — guarded read-only GET for other CPI entity sets (for example access policies or JMS queues) and the service metadata.
|
|
164
|
+
|
|
165
|
+
API Management:
|
|
166
|
+
|
|
167
|
+
- `apim_list_api_providers` — list API providers without credential fields.
|
|
168
|
+
- `apim_get_api_provider` — read one API provider with credential properties removed.
|
|
169
|
+
- `apim_create_api_provider` — create an Internet or On-Premise (Cloud Connector) API provider without credentials.
|
|
170
|
+
- `apim_update_api_provider` — change host, port, type, location, path prefix, or sap-client of a credential-free provider.
|
|
171
|
+
- `apim_list_api_proxies` — list API proxies.
|
|
172
|
+
- `apim_get_api_proxy` — read one API proxy with endpoints, virtual hosts, and the computed runtime URL.
|
|
173
|
+
- `apim_export_api_proxy` — export an API proxy bundle and return its files (policies, endpoints, scripts).
|
|
174
|
+
- `apim_list_virtual_hosts` — list virtual hosts.
|
|
175
|
+
- `apim_create_api_proxy` — import (and thereby deploy) a policy-free API proxy for a provider or https URL target.
|
|
176
|
+
- `apim_update_api_proxy` — change base path, target, title, or description; existing policies and steps are kept.
|
|
177
|
+
- `apim_attach_api_proxy_policies` — add or replace policies (API key, quota, spike arrest, CORS, headers, OAuth, IP allow list, response cache, Basic auth from a key value map, raise fault, JavaScript, custom XML) and their flow steps, then redeploy.
|
|
178
|
+
- `apim_list_api_products` — list API products.
|
|
179
|
+
- `apim_get_api_product` — read one API product with its proxies.
|
|
180
|
+
- `apim_create_api_product` — create (and by default publish) an API product over existing proxies with an optional quota.
|
|
181
|
+
- `apim_update_api_product` — change product title, description, scope, quota, or publish state.
|
|
182
|
+
- `apim_list_key_value_maps` — list key value maps.
|
|
183
|
+
- `apim_get_key_value_map` — read one key value map; encrypted values are redacted.
|
|
184
|
+
- `apim_create_key_value_map` — create a key value map; values are write-only.
|
|
185
|
+
- `apim_set_key_value_map_entries` — add or change key value map entries; values are write-only.
|
|
186
|
+
- `apim_odata_get` — guarded read-only GET for other Management.svc entity sets (for example applications, developers, certificate store references) and the service metadata.
|
|
187
|
+
|
|
188
|
+
Credentials come from CPI/APIM OAuth client service keys in the MCP host environment (`CPI_SERVICE_KEY`/`APIM_SERVICE_KEY`, explicit `CPI_*`/`APIM_*` variables, or `VCAP_SERVICES`) and are never written to `mcp.json`. Every write requires an exact confirmation string (for example `CREATE IFLOW <iflow_id>` or `UNDEPLOY ARTIFACT <artifact_id>`) and is checked before any network call. Scope is create, update, deploy, and undeploy: there are no delete tools, and deletions stay manual in the Integration Suite UI. Secrets (passwords, client secrets, key value map values) are write-only: tools accept them as input and never return them. Message payloads, attachments, and data-store payloads are never read.
|