sap-ai-dev-toolkit 0.9.9918 → 0.9.9920

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.
Files changed (59) hide show
  1. package/.github/agents/sap-integration-architect.agent.md +32 -18
  2. package/.github/agents/sap-integration-developer.agent.md +28 -12
  3. package/.github/skills/integration-suite-delivery/SKILL.md +94 -37
  4. package/.github/skills/sap-integration-architect/SKILL.md +49 -25
  5. package/README.md +72 -15
  6. package/dist/checksums.txt +5 -5
  7. package/dist/vsp-darwin-arm64 +0 -0
  8. package/dist/vsp-darwin-x64 +0 -0
  9. package/dist/vsp-linux-arm64 +0 -0
  10. package/dist/vsp-linux-x64 +0 -0
  11. package/dist/vsp-win32-x64.exe +0 -0
  12. package/inventory.md +97 -57
  13. package/package.json +1 -1
  14. package/scripts/check-live-integration.mjs +151 -0
  15. package/scripts/install-local.mjs +11 -4
  16. package/scripts/run-live-integration.mjs +48 -56
  17. package/src/apim-bundle.mjs +416 -13
  18. package/src/apim-cleanup-tools.mjs +145 -0
  19. package/src/apim-content.mjs +157 -0
  20. package/src/apim-delete-tools.mjs +139 -0
  21. package/src/apim-documentation.mjs +162 -0
  22. package/src/apim-inspection.mjs +141 -0
  23. package/src/apim-monitoring.mjs +140 -0
  24. package/src/apim-policy-steps.mjs +31 -0
  25. package/src/apim-runtime.mjs +262 -0
  26. package/src/apim-tools.mjs +202 -109
  27. package/src/cf-integration.mjs +46 -7
  28. package/src/cpi-async-monitoring.mjs +177 -0
  29. package/src/cpi-content.mjs +92 -0
  30. package/src/cpi-monitoring.mjs +225 -0
  31. package/src/cpi-operational.mjs +129 -0
  32. package/src/cpi-release.mjs +273 -0
  33. package/src/cpi-security.mjs +48 -0
  34. package/src/cpi-tools.mjs +184 -319
  35. package/src/devhub-retirement.mjs +345 -0
  36. package/src/devhub-tools.mjs +177 -0
  37. package/src/edge-monitoring.mjs +101 -0
  38. package/src/iflow-bundle.mjs +295 -7
  39. package/src/integration-alerts.mjs +248 -0
  40. package/src/integration-analysis.mjs +488 -0
  41. package/src/integration-capabilities.mjs +181 -0
  42. package/src/integration-collections.mjs +203 -0
  43. package/src/integration-common.mjs +140 -35
  44. package/src/integration-config.mjs +20 -14
  45. package/src/integration-credentials.mjs +16 -10
  46. package/src/integration-http.mjs +255 -67
  47. package/src/integration-messaging.mjs +118 -0
  48. package/src/integration-optional-config.mjs +122 -0
  49. package/src/integration-optional-tools.mjs +153 -0
  50. package/src/integration-release-store.mjs +410 -0
  51. package/src/integration-server.mjs +34 -16
  52. package/src/integration-state.mjs +31 -0
  53. package/src/integration-tools.mjs +44 -8
  54. package/src/integration-transport.mjs +184 -0
  55. package/src/integration-validation.mjs +26 -0
  56. package/src/integration-verify.mjs +143 -62
  57. package/src/open-connectors-tools.mjs +143 -0
  58. package/src/setup.mjs +66 -44
  59. package/tools.md +193 -26
package/src/setup.mjs CHANGED
@@ -12,6 +12,7 @@ import { parseIntegrationServiceKey, resolveIntegrationConfig, resolveIntegratio
12
12
  import { readStoredIntegrationKeys, storeIntegrationKey } from './integration-credentials.mjs';
13
13
  import { INTEGRATION_CF_SERVICES, createIntegrationInstance, findIntegrationInstances, integrationServiceKey } from './cf-integration.mjs';
14
14
  import { createIntegrationClients, verificationLines, verifyIntegrationClients } from './integration-verify.mjs';
15
+ import { OPTIONAL_COMPONENT_KEYS, OPTIONAL_INPUTS, parseOptionalConfig, resolveOptionalConfig } from './integration-optional-config.mjs';
15
16
  import { discoverCertificateDnsNames } from './tls-adt-proxy.mjs';
16
17
  import { findRemoteControllableSsoBrowsers } from './sso-browsers.mjs';
17
18
 
@@ -285,16 +286,24 @@ async function confirmPrompt(message, { input = stdin, output = stdout, defaultY
285
286
  return !/^n(?:o)?$/i.test(trimmed);
286
287
  }
287
288
 
288
- // Credential status for the Integration Suite companion, without exposing any value: which sides
289
- // (CPI, APIM) resolve from this environment or the stored-key file, and from which source.
289
+ // Report configured component identities and source evidence without credential values.
290
290
  export function integrationSuiteCredentialStatus(env = process.env) {
291
291
  try {
292
292
  const config = resolveIntegrationConfig(env, { stored: readStoredIntegrationKeys(env) });
293
- const side = value => (value ? `configured from ${value.source}${value.bindingName ? ` (${value.bindingName})` : ''}, host ${new URL(value.url).host}` : 'not configured');
294
- return { found: true, config, lines: [`CPI: ${side(config.cpi)}`, `APIM: ${side(config.apim)}`] };
293
+ const keys = ['cpi', 'apim', 'devhub', ...OPTIONAL_COMPONENT_KEYS];
294
+ const side = (key, value) => {
295
+ if (!value) return 'not configured';
296
+ const host = value.url ? new URL(value.url).host : key === 'aem' ? 'api.solace.cloud' : 'unknown';
297
+ return `configured from ${value.source}${value.bindingName ? ` (${value.bindingName})` : ''}, host ${host}`;
298
+ };
299
+ return { found: keys.some(key => Boolean(config[key])), config, lines: [
300
+ `Environment label: ${config.environment ?? 'default'} (tenant authority remains bound host identity)`,
301
+ ...keys.map(key => `${key.toUpperCase()}: ${side(key, config[key])}`),
302
+ ...(config.optional_config_errors ?? []).map(error => `${error.component.toUpperCase()}: invalid optional configuration (${error.source}); adapter disabled`)
303
+ ] };
295
304
  } catch (error) {
296
305
  const message = String(error?.message || '');
297
- if (/^Configure CPI/.test(message)) return { found: false, config: null, lines: ['No CPI or APIM credentials were found in this environment.'] };
306
+ if (/^Configure CPI/.test(message)) return { found: false, config: null, lines: ['No Integration Suite component credentials were found in this environment.'] };
298
307
  return { found: false, config: null, lines: [`Credentials found but not usable: ${message}`] };
299
308
  }
300
309
  }
@@ -315,28 +324,32 @@ function pastedServiceKey(side, value, env) {
315
324
  throw new Error('Paste the service key JSON (starting with {) or the path to a readable .json file.');
316
325
  }
317
326
  }
318
- return parseIntegrationServiceKey(side, text);
327
+ return OPTIONAL_COMPONENT_KEYS.includes(side.toLowerCase())
328
+ ? parseOptionalConfig(side.toLowerCase(), text, { source: 'setup', allowRawCtms: true })
329
+ : parseIntegrationServiceKey(side, text);
319
330
  }
320
331
 
321
332
  async function printIntegrationVerification(config, { env, output, verifyIntegration }) {
322
- const stop = startProgress('Signing in to Integration Suite and checking each tool area', { output, env, label: 'Integration Suite' });
333
+ const stop = startProgress('Checking configured core and optional Integration Suite access', { output, env, label: 'Integration Suite' });
323
334
  let report;
324
- try {
325
- report = await verifyIntegration(config, { env });
326
- } finally {
327
- stop();
335
+ try { report = await verifyIntegration(config, { env }); }
336
+ finally { stop(); }
337
+ const host = side => config[side]?.url ? new URL(config[side].url).host : side === 'aem' && config.aem ? 'api.solace.cloud' : '';
338
+ for (const label of ['CPI', 'APIM', 'DEVHUB']) {
339
+ for (const line of verificationLines(label, host(label.toLowerCase()), report[label.toLowerCase()])) print(output, ` ${line}`);
328
340
  }
329
- const host = side => (config[side] ? new URL(config[side].url).host : '');
330
- for (const line of [...verificationLines('CPI', host('cpi'), report.cpi), ...verificationLines('APIM', host('apim'), report.apim)]) print(output, ` ${line}`);
331
- const missing = [report.cpi, report.apim].flatMap(side => side?.checks || []).filter(check => check.result === 'forbidden').length;
332
- if (report.ok) {
333
- 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'));
334
- } else {
335
- const signedIn = [report.cpi, report.apim].some(side => side?.signed_in);
336
- print(output, formatStatus(signedIn
337
- ? 'The key signs in but lacks the roles for the core area; use an instance with the needed roles (the Cloud Foundry option can create one) or replace the key.'
338
- : 'Integration Suite sign-in failed; fix or replace the service key before using the tools.', 'warning', output, 'Integration Suite'));
341
+ for (const side of OPTIONAL_COMPONENT_KEYS) {
342
+ const access = report[side];
343
+ if (access?.configured) print(output, ` ${side.toUpperCase()} ${host(side)}: ${access.result ?? access.checks?.[0]?.result ?? 'untested'} (optional access; activation/entitlement unknown)`);
339
344
  }
345
+ const core = [report.cpi, report.apim, report.devhub].filter(side => side?.configured);
346
+ const denied = core.flatMap(side => side.checks ?? []).filter(check => check.result === 'forbidden').length;
347
+ const message = !core.length ? 'No core clients configured; optional checks are reported separately.'
348
+ : report.ok ? `Configured core access verified${denied ? `; ${denied} capability checks were access-denied (cause not established)` : ''}.`
349
+ : 'Configured core reads did not pass. Inspect the individual access evidence; a role or entitlement diagnosis is not established.';
350
+ print(output, formatStatus(message, report.ok && !denied ? 'success' : 'warning', output, 'Integration Suite'));
351
+ const optional = OPTIONAL_COMPONENT_KEYS.filter(side => report[side]?.configured);
352
+ if (optional.length) print(output, ` Optional capability completeness: ${report.optional_completeness?.complete === true ? 'complete' : 'unverified/partial'}; core access is a separate result.`);
340
353
  return report;
341
354
  }
342
355
 
@@ -347,7 +360,7 @@ const defaultIntegrationCloudFoundry = Object.freeze({
347
360
  createInstance: createIntegrationInstance
348
361
  });
349
362
 
350
- const SIDE_LABELS = Object.freeze({ CPI: 'Cloud Integration (CPI)', APIM: 'API Management' });
363
+ const SIDE_LABELS = Object.freeze({ CPI: 'Cloud Integration (CPI)', APIM: 'API Management', DEVHUB: 'Developer Hub' });
351
364
 
352
365
  function cfFailure(error) {
353
366
  const detail = String(error?.stderr || error?.message || '').replace(/\s+/g, ' ').trim();
@@ -359,12 +372,14 @@ async function acceptIntegrationKey(side, config, context) {
359
372
  const { env, input, output, verifyIntegration } = context;
360
373
  const choice = side.toLowerCase();
361
374
  const report = await printIntegrationVerification({ [choice]: config }, { env, output, verifyIntegration });
362
- if (!report[choice].signed_in && !await confirmPrompt('Save this key anyway? (y/n, Enter=no)', { input, output, defaultYes: false })) return { saved: false, report };
375
+ if (!report[choice]?.signed_in && !await confirmPrompt('Save these existing credentials despite unverified access? (y/n, Enter=no)', { input, output, defaultYes: false })) return { saved: false, report };
363
376
  const path = await storeIntegrationKey(side, config, env);
364
377
  print(output, formatStatus(`Saved the ${side} key to ${path} (readable only by you; not written to mcp.json).`, 'success', output, 'Integration Suite'));
365
378
  let fromEnvironment = null;
366
379
  try {
367
- fromEnvironment = resolveIntegrationSideFromEnvironment(env, side);
380
+ fromEnvironment = OPTIONAL_COMPONENT_KEYS.includes(choice)
381
+ ? resolveOptionalConfig(env, {})[choice]
382
+ : resolveIntegrationSideFromEnvironment(env, side);
368
383
  } catch {
369
384
  // A broken environment value is reported by the status lines.
370
385
  }
@@ -374,7 +389,7 @@ async function acceptIntegrationKey(side, config, context) {
374
389
  }
375
390
 
376
391
  function lacksRoles(side) {
377
- return Boolean(side?.signed_in) && (!side.ok || side.checks.some(check => check.result === 'forbidden'));
392
+ return Boolean(side?.signed_in) && (side.checks ?? []).some(check => check.result === 'forbidden');
378
393
  }
379
394
 
380
395
  async function cloudFoundryStep(message, operation, { env, output }) {
@@ -418,13 +433,13 @@ async function useCloudFoundrySpace(target, context) {
418
433
  print(output, formatStatus(`Could not list service instances: ${cfFailure(error)}`, 'warning', output, 'Cloud Foundry'));
419
434
  return;
420
435
  }
421
- for (const side of ['CPI', 'APIM']) {
436
+ for (const side of ['CPI', 'APIM', 'DEVHUB']) {
422
437
  const service = INTEGRATION_CF_SERVICES[side];
423
438
  const candidates = instances[side] || [];
424
439
  print(output, '');
425
440
  if (!candidates.length) {
426
441
  print(output, formatStatus(`No ${service.label} instance with plan ${service.plan} (${service.offering}) in this space.`, 'info', output, SIDE_LABELS[side]));
427
- if (await confirmPrompt(`Create instance ${service.dedicatedInstance} (${service.offering}, plan ${service.plan}) with the roles the tools need? (y/n, Enter=no)`, { input, output, defaultYes: false })) {
442
+ if (await confirmPrompt(`Create instance ${service.dedicatedInstance} (${service.offering}, plan ${service.plan}) with ${service.parameters.role || 'the roles the tools need'}, then create or reuse its sap-ai-dev-integration service key? (y/n, Enter=no)`, { input, output, defaultYes: false })) {
428
443
  await createDedicatedInstance(side, context);
429
444
  }
430
445
  continue;
@@ -436,6 +451,7 @@ async function useCloudFoundrySpace(target, context) {
436
451
  choices: [...candidates.map(candidate => ({ name: candidate.name, value: candidate.name })), { name: 'Skip', value: '', shortcut: 's' }]
437
452
  }, { input, output });
438
453
  if (!instanceName) continue;
454
+ if (side === 'DEVHUB' && !await confirmPrompt(`Use ${instanceName} and create or reuse its sap-ai-dev-integration service key? (y/n, Enter=no)`, { input, output, defaultYes: false })) continue;
439
455
  let result;
440
456
  try {
441
457
  result = await useCloudFoundryInstance(side, instanceName, context);
@@ -454,15 +470,15 @@ async function useCloudFoundrySpace(target, context) {
454
470
  }
455
471
  }
456
472
 
457
- // Verify the detected credentials online and let the user take CPI/APIM keys from the logged-in CF space or
473
+ // Verify the detected credentials online and let the user take CPI/APIM/DEVHUB keys from the logged-in CF space or
458
474
  // paste them. A key is checked online first and saved to the user-only store (never to mcp.json); it is used
459
475
  // when the environment has no key.
460
476
  async function configureIntegrationCredentials({ env, input, output, verifyIntegration, cloudFoundry = defaultIntegrationCloudFoundry }) {
461
477
  let status = integrationSuiteCredentialStatus(env);
462
- // Latest verification per side ({ cpi, apim }); updated whenever a key is checked or saved.
478
+ // Latest verification per side ({ cpi, apim, devhub }); updated whenever a key is checked or saved.
463
479
  const health = {};
464
480
  const recordHealth = report => {
465
- for (const side of ['cpi', 'apim']) if (report?.[side]?.configured) health[side] = report[side];
481
+ for (const side of ['cpi', 'apim', 'devhub', ...OPTIONAL_COMPONENT_KEYS]) if (report?.[side]?.configured) health[side] = report[side];
466
482
  };
467
483
  if (status.found) recordHealth(await printIntegrationVerification(status.config, { env, output, verifyIntegration }));
468
484
  let target = { available: false };
@@ -475,9 +491,9 @@ async function configureIntegrationCredentials({ env, input, output, verifyInteg
475
491
  for (;;) {
476
492
  print(output, '');
477
493
  const space = target.available ? [target.orgName, target.spaceName].filter(Boolean).join(' / ') || target.spaceGuid : '';
478
- // Sides whose key signs in but lacks roles: a new key cannot fix that, a dedicated instance can.
479
- const roleless = ['CPI', 'APIM'].filter(side => lacksRoles(health[side.toLowerCase()]));
480
- const healthy = status.found && !roleless.length && Object.values(health).every(side => side.ok);
494
+ // Access denial is observed; its role/entitlement cause is not established. Optional setup never provisions.
495
+ const roleless = ['CPI', 'APIM', 'DEVHUB'].filter(side => lacksRoles(health[side.toLowerCase()]));
496
+ const healthy = status.found && !roleless.length && Object.values(health).length > 0 && Object.values(health).every(side => side.ok);
481
497
  const fixes = target.available
482
498
  ? roleless.map(side => ({
483
499
  name: `Create ${INTEGRATION_CF_SERVICES[side].dedicatedInstance} in ${space} with the roles the ${side} tools need, then use it`,
@@ -489,10 +505,16 @@ async function configureIntegrationCredentials({ env, input, output, verifyInteg
489
505
  defaultValue: healthy ? 'done' : (fixes[0]?.value ?? (target.available ? 'cf' : (status.found ? 'done' : 'cpi'))),
490
506
  choices: [
491
507
  ...fixes,
492
- { name: !status.found ? 'Continue without credentials (the server will not start until a key is configured)' : (healthy ? 'Continue' : 'Continue with the current key (tools in the ✗ areas will fail with HTTP 403)'), value: 'done', shortcut: 'c' },
508
+ { name: !status.found ? 'Continue without credentials (operational tools need configured component access)' : (healthy ? 'Continue' : 'Continue with current access (unavailable capabilities remain unverified)'), value: 'done', shortcut: 'c' },
493
509
  ...(target.available ? [{ name: `Use the logged-in CF space (${space}): find instances, reuse or create keys`, value: 'cf', shortcut: 'f' }] : []),
494
510
  { name: 'Paste a Cloud Integration (CPI) service key — Process Integration Runtime, plan api', value: 'cpi', shortcut: 'p' },
495
511
  { name: 'Paste an API Management service key — API Management, plan apiportal-apiaccess', value: 'apim', shortcut: 'a' },
512
+ { name: 'Paste a Developer Hub service key — API Management, Developer Hub, plan devportal-apiaccess (AuthGroup.API.Admin)', value: 'devhub', shortcut: 'd' },
513
+ ...OPTIONAL_COMPONENT_KEYS.map(side => ({
514
+ name: `Paste existing ${OPTIONAL_INPUTS[side].label} configuration — ${OPTIONAL_INPUTS[side].variable} (no provisioning)`,
515
+ value: `optional-${side}`,
516
+ shortcut: { ctms: 't', ans: 'n', event_mesh: 'e', aem: 'm', aem_semp: 's', open_connectors: 'o', edge: 'g' }[side]
517
+ })),
496
518
  ...(status.found ? [{ name: 'Check the configured credentials online again', value: 'verify', shortcut: 'v' }] : [])
497
519
  ]
498
520
  }, { input, output });
@@ -506,9 +528,9 @@ async function configureIntegrationCredentials({ env, input, output, verifyInteg
506
528
  } else if (choice === 'cf') {
507
529
  await useCloudFoundrySpace(target, context);
508
530
  } else {
509
- const side = choice.toUpperCase();
531
+ const side = (choice.startsWith('optional-') ? choice.slice('optional-'.length) : choice).toUpperCase();
510
532
  const pasted = await textPrompt({
511
- message: `Paste the ${side} service key JSON or the path to its .json file`,
533
+ message: `Paste existing ${side} credential JSON or the path to its .json file`,
512
534
  secret: true,
513
535
  validate: value => {
514
536
  try {
@@ -528,15 +550,15 @@ async function configureIntegrationCredentials({ env, input, output, verifyInteg
528
550
 
529
551
  async function promptIntegrationSuite({ env, alreadyConfigured, input, output, verifyIntegration = defaultVerifyIntegration, integrationCloudFoundry, ask = true }) {
530
552
  print(output, '');
531
- print(output, formatStatus('SAP Integration Suite tools (Cloud Integration + API Management).', 'step', output, 'Integration Suite'));
532
- 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.');
533
- 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.');
553
+ print(output, formatStatus('SAP Integration Suite tools (core lifecycle and separately credentialed optional services).', 'step', output, 'Integration Suite'));
554
+ print(output, ' Adds the sap-ai-integration MCP server: author/configure/release, bounded monitoring and diagnosis, guarded recovery/retirement, and evidence-backed optional/native workflows.');
555
+ print(output, ' Writes require exact approval; private previews are not approval. Optional setup accepts existing credentials only, without provisioning. Agents: SAP Integration Architect, SAP Integration Developer.');
534
556
  if (alreadyConfigured) print(output, ' Already configured in this MCP config; keeping it and refreshing its version.');
535
557
  const status = integrationSuiteCredentialStatus(env);
536
558
  for (const line of status.lines) print(output, ` ${line}`);
537
559
  if (!status.found) {
538
- print(output, ' The server needs CPI_SERVICE_KEY and/or APIM_SERVICE_KEY (or CPI_*/APIM_* variables, VCAP_SERVICES) in the MCP host environment,');
539
- 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.');
560
+ print(output, ' Configure existing core or optional component credentials in the MCP host environment,');
561
+ print(output, ' or paste typed credential JSON in the next hidden step. Credentials are verified read-only and stored user-only, never in mcp.json.');
540
562
  }
541
563
  if (ask && !alreadyConfigured) {
542
564
  print(output, '');
@@ -1062,7 +1084,7 @@ export async function runSetup({
1062
1084
  defaultValue: 'manual',
1063
1085
  choices: [
1064
1086
  { name: '✍️ Start manual entry wizard (system name, ADT URL, client, login)', value: 'manual', shortcut: 'm' },
1065
- { name: '🔌 Set up only the SAP Integration Suite tools (paste and verify CPI / API Management service keys)', value: 'integration', shortcut: 'i' },
1087
+ { name: '🔌 Set up only the SAP Integration Suite tools (paste and verify CPI / API Management / Developer Hub service keys)', value: 'integration', shortcut: 'i' },
1066
1088
  { name: 'Exit setup without changes', value: 'exit', shortcut: 'e' }
1067
1089
  ]
1068
1090
  }, { input, output });
@@ -1087,9 +1109,9 @@ export async function runSetup({
1087
1109
  sapDevelopmentServers = ['integration-suite'];
1088
1110
  } else if (includeSapDevelopmentToolsPrompt) {
1089
1111
  print(output, '');
1090
- print(output, formatStatus('Optionally add companion MCP servers for full-stack SAP development, HANA inspection, and Integration Suite (CPI/API Management).', 'step', output, 'Tools'));
1112
+ print(output, formatStatus('Optionally add companion MCP servers for full-stack SAP development, HANA inspection, and Integration Suite (CPI/API Management/Developer Hub).', 'step', output, 'Tools'));
1091
1113
  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.');
1092
- 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.');
1114
+ print(output, ' Integration Suite uses CPI/APIM/DEVHUB 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.');
1093
1115
  print(output, ' Leave empty to only configure ABAP/ADT destination servers.');
1094
1116
  print(output, '');
1095
1117
  sapDevelopmentServers = await checkboxPrompt({
package/tools.md CHANGED
@@ -113,9 +113,27 @@ Connection secrets are read from the MCP host environment via explicit `HANA_RO_
113
113
 
114
114
  ## Separate optional Integration Suite server
115
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:
116
+ The `integration-suite` companion starts `sap-ai-integration` as a separate stdio MCP server. It does not use the BAS proxy or a destination prefix. Operational tools appear only for their own configured component; optional setup accepts existing credentials and never provisions a service or retargets Cloud Foundry.
117
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.
118
+ - `integration_connection_info` — always exposes the full core/optional/native component catalog, immutable tenant bindings, actual registered actions, public-contract/source/setup evidence, and distinct core/optional verification results. Missing configuration, denied access, inaccessible contracts and unknown entitlement remain separate.
119
+
120
+ Collection tools return `{items, returned, total, complete, next_cursor, stop_reason}`. Follow `next_cursor` with the same target and filters; cursors are private-runtime-bound, expire after 15 minutes, and stop working after a server restart. Default pages are 100/max 200, except MPL (20/100) and runtime artifacts (200/500). `total` is a server count or `null`, never a sampled page length. `complete` means verified traversal end, not a transactional snapshot. `output_limit`, `source_limit`, `source_changed`, `paging_unsupported` and `deadline` retain incomplete evidence; do not interpret them as empty/healthy. Package/Partner Directory cursors require one concrete artifact/parameter type.
121
+
122
+ Capability `ok` means configured core access only. Per-capability results distinguish `ok`, `unauthenticated`, `forbidden`, `unavailable`, `untested` and `error`. A 403 proves access denial, not which role is missing; 404/501 describe the endpoint, not entitlement. Entitlement stays unknown without authoritative account evidence. Generic OData readers use implemented metadata-only route projectors; `/$metadata` discovery does not authorize new raw-data roots or function imports.
123
+
124
+ Optional host/setup JSON overrides only its corresponding stored record; malformed supplied input disables that adapter with a safe configuration finding, never falls back to unrelated credentials or breaks usable core clients. Stored records contain exactly these normalized fields:
125
+
126
+ | Host input / stored key | Normalized fields | Authentication boundary |
127
+ | --- | --- | --- |
128
+ | `CTMS_SERVICE_KEY` / `ctms` | `url, tokenUrl, clientId, clientSecret` | Normalize raw `uri`/`uaa` at setup/environment only; cTMS OAuth token suffix normalization is specific to cTMS. |
129
+ | `ANS_CONFIG` / `ans` | `url, username, password` | Technical-client Basic; configuration read and separately authorized write permissions. |
130
+ | `EVENT_MESH_CONFIG` / `event_mesh` | `url, tokenUrl, clientId, clientSecret, probeQueueName?` | Management OAuth; preserve the binding's exact token endpoint. No queue probe means untested, not non-entitlement. |
131
+ | `AEM_CONFIG` / `aem` | `apiToken` | Static bearer at fixed `https://api.solace.cloud`; cloud read permission `services:view`. |
132
+ | `AEM_SEMP_CONFIG` / `aem_semp` | `url, msgVpnName, username, password` | Separate broker-local Basic, VPN-scoped read-only management user; private session reuse. |
133
+ | `OPEN_CONNECTORS_CONFIG` / `open_connectors` | `url, userSecret, organizationSecret` | Base includes `/elements/api-v2`; private User/Organization authorization, not CPI OAuth. |
134
+ | `EDGE_CONFIG` / `edge` | `url, clientId, clientSecret` | Local service-key Basic at the API virtual-host origin, preserving `/local`; not cloud OAuth. |
135
+
136
+ Use HTTPS without URL credentials/query/fragment. Loopback HTTP is fixture-only. Hidden `--setup-integration` input and the 0600 credential store accept normalized optional records; secrets never belong in tool-call setup arguments or `mcp.json`. `INTEGRATION_ENVIRONMENT` is a non-secret immutable report label (`default` otherwise), not tenant authority or a mutable global switch. Use independently configured server instances for different environments. Common-subaccount identity and activation/entitlement stay unverified without authoritative account evidence.
119
137
 
120
138
  Cloud Integration — design time:
121
139
 
@@ -124,43 +142,57 @@ Cloud Integration — design time:
124
142
  - `cpi_list_package_artifacts` — list a package's iFlows, value mappings, script collections, and message mappings.
125
143
  - `cpi_get_iflow` — read iFlow metadata, externalized configuration, and bundle files (or one file by path).
126
144
  - `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.
145
+ - `cpi_get_value_mappings` — explicit authoring read; `agency_pairs` and each pair's `values` / `default_values` use collection envelopes (`items`, `returned`, `total`, `complete`, `next_cursor`, `stop_reason`). At most 20 pairs and 500 entries per nested collection, additionally byte-bounded. Missing counts remain null; unverified traversal stops with `source_limit`, without invented continuations.
128
146
  - `cpi_create_package` — create a package.
129
147
  - `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).
148
+ - `cpi_copy_package` — copy a Discover package into Design (new copy with suffix, or overwrite-merge keeping configuration). `accepted` / `http_status` distinguish HTTP acceptance from `copied`; an unusable accepted response returns `copied:false,outcome:"unverified"` and must not trigger an automatic repeat POST.
131
149
  - `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.
150
+ - `cpi_update_iflow` — expected-version draft replacement, merging bundled defaults < fresh configured values < explicit overrides, then reapplying surviving tenant configuration. Reports exact partial progress; `new_version` is the resulting draft-bundle version and `retained_version_created:false`.
151
+ - `cpi_update_iflow_configuration` — ordered configuration writes with updated names and safe first-failure evidence, not private values.
152
+ - `cpi_save_iflow_version` — save the approved current draft as an explicit retained version, checking current version/bundle hash and target absence before one bodyless SaveAsVersion POST. Requires `SAVE IFLOW VERSION <id> <version>`; no hidden upload or repeated POST.
153
+ - `cpi_validate_iflow` — exact selected-version validation, requiring `VALIDATE IFLOW <id> VERSION <version>`. HTTP 202 is acceptance, not business success: `Passed`, `Failed`, and unknown results remain distinct. The latest completed result replaces older evidence for 15 minutes; readiness requires matching successful content/configuration state. Save a version before validating that selected saved version.
154
+ - `integration_snapshot_content` — dry-run-first exact-target/host preview and `SNAPSHOT INTEGRATION CONTENT <SHA256>` approval; immutable original ZIP/configuration capture under the private credential directory. No source/configuration values or caller-selected filesystem paths returned.
155
+ - `integration_list_snapshots` — bounded safe inventory with integrity findings. Directories are 0700/files 0600; archives are bounded to 20 MiB compressed/member, 100 MiB actual expansion and 2,000 members; snapshot total is 100 MiB.
156
+ - `integration_restore_snapshot` — preview complete effects/current state hashes, then require every expected target state and `RESTORE INTEGRATION SNAPSHOT <SHA256>`. Rechecks all targets before any write and each before its write; CPI restores only active draft/configuration, with deployment separate. APIM import replaces draft and deploys a new revision. Stops on partial failure without automatic compensation.
157
+
158
+ Snapshot restoration does not reverse processed messages, datastores, security material, issued tokens or receiver transactions. Retained CPI rollback is exact-version deployment. APIM retained Revert → Deploy and standalone Undeploy remain native actions, not ZIP-import aliases.
134
159
  - `cpi_upsert_value_mappings` — add or change value-mapping pairs.
135
160
  - `cpi_update_script_collection` — add or replace scripts in a script collection.
136
161
 
137
162
  Cloud Integration — deployment and monitoring:
138
163
 
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.
164
+ - `cpi_deploy_iflow` / `cpi_deploy_artifact` — deploy selected `active` or retained `x.y.z`, preserving build/task and runtime evidence separately. Outcomes are `pending`, `succeeded`, `failed` or `unverified`; old `STARTED` cannot prove the new deployment, and same-version redeploy needs fresh deployment identity. `wait_seconds:0` initiates once without post-polling.
165
+ - `cpi_get_deployment_task` — continue observing an accepted task without another deploy POST, retaining the original same-runtime baseline. Unknown build states, license failure and absent freshness proof remain explicit.
141
166
  - `cpi_undeploy_artifact` — undeploy any runtime artifact and wait until it is gone.
142
167
  - `cpi_list_runtime_artifacts` — list deployed artifacts, optionally by status.
143
168
  - `cpi_get_iflow_runtime_status` — read deployed runtime status, including error text when the iFlow failed to start.
144
169
  - `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).
170
+ - `cpi_list_message_logs` — bounded `[from,to)` UTC `LogStart` listing, preserving milliseconds and processing records without `LogEnd`. Supply both timestamps or neither (a frozen last-24-hour window); maximum 31 days. Correlation/application/custom-header filters are exact. Mutable lists report best-effort consistency and detect observed count/ID drift.
171
+ - `cpi_get_message_statistics` — explicit-window tenant counts and eight status counts, unclassified remainder, source failures and completeness; never derives totals from sampled lists. Zero traffic is not a health sign-off.
172
+ - `cpi_get_message_log` — allowlisted message/error metadata; no payload attachments.
173
+ - `cpi_list_message_log_runs` — bounded run identity/state metadata.
174
+ - `cpi_get_message_log_steps` — latest or explicitly selected member run, with bounded errors and paged steps. Missing message and no runs are distinct; continue the cursor to inspect steps beyond 200.
175
+ - `cpi_trace_message` — exact nonempty propagated correlation only; without one, returns the seed rather than guessing a business identifier or APIM relationship.
176
+ - `cpi_list_jms_queues` / `cpi_get_jms_queue` — safe queue metadata only; no message consumption. Positive depth alone is informational; missing/unrecognized flags stay unknown.
177
+ - `cpi_get_jms_broker_status` — separate capacity/limit, warning/critical and inactive-queue observations with independent source failures.
178
+ - `cpi_list_data_stores` / `cpi_list_data_store_entries` — query-free local metadata pages; `integration_flow: ""` addresses global stores. Store-reported backlog/overdue totals remain distinct from fetched entries; unresolved MessageIds do not establish MPL links.
179
+ - `cpi_list_variables` — bounded metadata with exact iFlow/visibility filtering; no values.
152
180
 
153
181
  Cloud Integration — security material and tenant configuration:
154
182
 
155
183
  - `cpi_list_security_material` — list user credentials, OAuth2 client credentials, or keystore entries with secret fields redacted.
156
184
  - `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.
185
+ - `cpi_set_oauth2_client_credential` — create or update an OAuth2 client credential; the client secret is write-only. Omitted optional metadata is preserved on update; upstream failure bodies are withheld, including escaped reflected secrets.
186
+ - `cpi_upload_certificate` — locally parse a PEM X.509 certificate and report its computed SHA-256 fingerprint before the approved PUT. Tenant fingerprint/read-back verification remains distinct; an unrelated HTTP 400 is not translated into an alias collision.
187
+ - `cpi_get_certificate_health` — bounded validity-only assessment (`valid`, `expiring`, `expired`, `not_yet_valid`, `unknown`), frozen evaluation time and explicit warning interval. No subject personal data, trust-chain or connectivity claim.
188
+ - `cpi_get_operational_summary` — bounded on-demand runtime/message/JMS/store/certificate snapshot. Health is `no_issues_observed`, `degraded` or `unknown` within observed management signals only. Ordinary positive queue depth/processing is informational; zero traffic or inaccessible required sources is unknown unless an observed issue establishes degradation.
189
+ - `integration_inspect_dependencies` — explicit-target private bundle/configuration inspection, literal alias/provider/KVM/certificate existence, unresolved dynamic/shared/custom dependencies, and configuration-aware digests. Values/source stay private.
190
+ - `integration_check_release_readiness` — exact target/version/dependency/deployment-access evidence and latest state-matching iFlow validation. Missing required dependencies, version mismatch, referenced invalid certificates or latest failed validation block; missing/stale/unknown validation and unreadable/dynamic evidence require review. Unrelated expired inventory certificates do not block a release.
159
191
  - `cpi_list_number_ranges` — list number ranges.
160
192
  - `cpi_set_number_range` — create or update a number range.
161
193
  - `cpi_list_partner_parameters` — list Partner Directory string parameters and binary parameter metadata.
162
194
  - `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.
195
+ - `cpi_odata_get` — allowlisted operational metadata and service discovery only; discovered roots without safe projectors are not authorized for raw data access.
164
196
 
165
197
  API Management:
166
198
 
@@ -171,18 +203,153 @@ API Management:
171
203
  - `apim_list_api_proxies` — list API proxies.
172
204
  - `apim_get_api_proxy` — read one API proxy with endpoints, virtual hosts, and the computed runtime URL.
173
205
  - `apim_export_api_proxy` — export an API proxy bundle and return its files (policies, endpoints, scripts).
206
+ - `apim_get_api_proxy_status` — safe observed state/version/deployment metadata and sanitized runtime URL; management state does not establish runtime health.
207
+ - `apim_inspect_api_proxy_security` — private structural bundle inspection: registrations, endpoint/flow/phase attachments, condition presence, literal dependency aliases and unresolved/custom behavior. No source returned; static evidence does not prove effective authentication, rate limiting or certificate trust.
208
+ - `apim_get_analytics` — separate public AnalyticsService V4 server aggregates over explicit UTC `[from,to)` (at most 31 days): calls/errors, policy/target errors, weighted response-time means and source watermark. Derived aliases do not collide with entity fields. `source_records` counts analytics input records, not API calls; authoritative zero records distinguish empty null sums (`no_data`) from partial metrics. Analytics lag and unverified instantaneous health remain explicit.
209
+ - `apim_get_analytics_series` / `apim_get_api_error_breakdown` — bounded server-grouped pages (at most 200 rows; at most 1,000 requested time buckets). Group counts are not total API calls. No raw request URLs, developer details, p95 or p99 are fabricated.
210
+
211
+ Inspection/release preconditions use deterministic SHA-256 identities. `bundle_sha256` binds UTF-8-sorted member paths and bytes with unsigned 32-bit big-endian length prefixes, independent of ZIP timestamps/compression/order. `state_sha256` additionally binds fresh CPI configured parameter values/types; reports expose names and hashes, never those private values.
174
212
  - `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.
213
+ - `apim_get_api_documentation` — inspect registered OpenAPI metadata, security schemes and a paginated operation inventory (100 operations per page); select one `path`/`method` pair for structural parameter, content-type and response details. Operation parameters override matching path parameters; OpenAPI 2 `consumes` is inherited unless overridden.
214
+ - `apim_update_api_documentation` — replace registered English documentation with self-contained OpenAPI 2.0/3.0/3.1 JSON (maximum 1 MiB), pinned to the proxy's public HTTPS runtime URL. Requires `UPDATE API DOCUMENTATION <name>`; preserves routing, authentication and other bundle resources, redeploys, and compares decoded readback with the submitted definition.
215
+
216
+ Documentation readback permits only the observed portal metadata synchronization and semantically equivalent description whitespace/HTML entities, reported through `read_back_normalized`; paths, schemas, security, examples and runtime destinations are not ignored.
217
+
218
+ - `apim_create_api_proxy` — import (and thereby deploy) an API proxy for a provider or https URL target, optionally including up to 10 policies before its first deployment.
176
219
  - `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.
220
+ - `apim_attach_api_proxy_policies` — add/replace policy documents and append missing assignments. A supplied changed condition updates that exact supported assignment; omission preserves it. It does not move/remove/reorder existing steps.
221
+ - `apim_edit_policy_steps` — complete desired order for one named endpoint/pre-or-post/request-or-response phase, guarded by fresh `expected_bundle_sha256` and `EDIT API PROXY POLICY STEPS <name>`. Omitted conditions survive. Explicit unreferenced-policy removal inventories conditional/fault/unknown references first; ambiguity blocks import. Preserves shared scripts/resources and verifies intended assignments privately after import.
178
222
  - `apim_list_api_products` — list API products.
179
223
  - `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.
224
+ - `apim_create_api_product` — create (and by default publish) an API product over existing proxies with an optional quota. `publish: false` explicitly creates native `DRAFT` state rather than omitting its required status.
225
+ - `apim_update_api_product` — change product title, description, scope, quota, or publish state using a multipart OData batch; proxy membership remains unchanged. Embedded HTTP and OData errors are checked before reporting success.
226
+ - `apim_delete_api_product` — delete one named product, including a published product, only when no applications reference it; never removes its proxies or consumers. Requires `DELETE API PRODUCT <name>` and verifies absence.
227
+ - `apim_delete_api_proxy` — delete one named proxy after proving there are no product associations. Deployed proxies additionally require `allow_deployed: true`; confirms both definition and deployment-record absence. Requires `DELETE API PROXY <name>`.
228
+ - `apim_cleanup_api_objects` — preview or execute explicit product/proxy lists, at most 100 each and 200 total. Defaults to `dry_run: true`; execution needs `dry_run: false` and the exact `DELETE APIM OBJECTS <SHA256>` confirmation returned for those lists and `allow_deployed`. Preflights every target, removes selected products before selected proxies, rechecks dependencies, stops on the first failed/unverified deletion, and returns per-object partial progress. Never expands prefixes/wildcards or deletes applications, providers, KVMs or credentials.
182
229
  - `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.
230
+ - `apim_get_key_value_map` — read map metadata and entry names; all values are write-only, including unencrypted maps.
231
+ - `apim_create_key_value_map` — create a key value map; refuses to overwrite an existing map's credentials. Values are write-only; use `apim_set_key_value_map_entries` for an explicit update.
185
232
  - `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.
233
+ - `apim_odata_get` — allowlisted metadata projections and service discovery, never arbitrary entity/function/payload/source reads.
234
+
235
+ For one public capability API with multiple fixed SAP backends, `src/apim-bundle.mjs` exports the low-level `buildRoutedApiProxyBundle(config)` builder. It produces a complete secured ZIP for an explicitly approved Transport.svc import; it is not an additional MCP tool. Configuration supplies the public `name`/`basePath`/`serviceType`, `backendPath`, trusted `issuer`/`jwksUrl`/`scopePrefix`, explicit `environment`, and `systems` entries containing fixed `id`/`provider`/`kvm` names. `vimVersion` preserves the ingestion query parameter when required. No password, client secret, bearer token, or KVM value belongs in this configuration.
236
+
237
+ The proxy verifies RS256 signatures, issuer, token time claims, and the client-credentials grant before a JavaScript selector authorizes exactly one system scope together with its matching resource audience. Missing, unsupported, or ambiguous selection faults before routing. The mandatory selector performs the audience check: VerifyJWT's comma-separated Audience check required all configured audiences in this tenant and rejected correctly narrowed single-system tokens. Do not bypass the selector/fault pair or replace it with unverified JWT decoding.
238
+
239
+ Each named target has its own fixed provider and private KVM-backed Basic authentication steps. There is no unconditional backend fallback, request-selected URL, or cross-environment failover. Configuration rejects duplicate system authorities and backend policy-name collisions; configure one SAP client per system family. Encrypted maps are read only, without `Put` or `InitialEntries`.
240
+
241
+ Credential KVM lookups use `index="1"` so BasicAuthentication receives scalar username/password values, not the unindexed `java.util.List`. Both variables remain private; no KVM values are changed.
242
+
243
+ After JWT verification and authorized route selection, BasicAuthentication uses `AssignTo createNew="true"` to replace the incoming bearer header with the selected backend's private Basic header. `createNew="false"` leaves an existing Authorization header untouched and would forward the consumer bearer token to SAP instead.
244
+
245
+ The first encoding policy constrains backend negotiation to `gzip, deflate`; browser clients may advertise Brotli, which these SAP hops do not support. The management and OAuth clients use the same supported encoding negotiation.
246
+
247
+ With `environment: 'production'`, the builder rejects any configuration other than a single S4P target; non-production rejects S4P. Promote the same public API contract with reviewed environment-specific issuer, provider, KVM and scope configuration, rather than transporting D/T/Q provider references into production. The builder does not create providers, credentials, Cloud Connector tunnels or XSUAA authorities.
248
+
249
+ The generic create/update/policy-attachment tools still resolve the default target endpoint; they do not author this multi-target topology or attach policies independently to non-default targets. Use the routed builder for a complete reviewed bundle, and preserve all named-target attachments when making later changes. Approved native proxy deletion can permanently remove deployed proxies; standalone undeploy without deletion is not exposed. Product unpublishing is not runtime shutdown.
250
+
251
+ Live Isuite_Non-Prod validation on 2026-10-27 deployed 25 shared capability proxies and passed 100 missing-token, tampered-signature, unauthorized-client and ambiguous-scope rejection checks. All 72 metadata and 66 bounded data requests returned HTTP 503 because no matching default-location Cloud Connector tunnel was connected to subaccount `7c0ef4b7-aad3-444b-8152-4f3759b50e9f`. This is not successful SAP data-access or backend-password verification.
252
+
253
+ Deletion smoke on the same tenant created two isolated secured proxy/product pairs, then exercised individual deletion and bulk cleanup through the MCP addon. Both published products and deployed proxies were removed; definition/deployment absence was verified, and both disposable runtime URLs changed from the deliberate `418` rejection to `404`. A product-linked proxy was refused before deletion. No business proxies or shared credentials were deleted by that smoke.
254
+
255
+ After a read-only preview found the exact legacy set dependency-free, explicit user approval authorized deletion of its 75 products and 75 proxies (`S4D100_`, `S4T300_`, `S4Q300_` capability names). All 150 deletions were verified. Post-cleanup inventory contained no legacy definitions; all 75 retired runtime URLs returned `404`. The 25 shared `S4_` APIs, all other unselected proxies, three API providers and three encrypted KVMs were preserved unchanged. Shared routes still rejected unauthenticated requests with `401`; cleanup does not establish SAP backend data access or resolve the earlier Cloud Connector issue.
256
+
257
+ A subsequent user-approved cleanup removed all six remaining `ZAI` smoke API proxies from run `LVR0WX5Z`. Definition/deployment absence and `404` responses from all six retired URLs were verified. At that point, the APIM inventory contained exactly the 25 shared business proxies and no API products or smoke APIs. All six provider definitions, all four KVMs and the shared API configurations were preserved; all shared routes continued rejecting missing bearer tokens with `401`.
258
+
259
+ Validation on 2026-10-28 corrected the approved D/T/Q providers and `ISuite_QA` location, scalar KVM reads, backend Authorization replacement, and encoding negotiation without changing credential values. The locally installed `0.9.9919` server exposed 70 tools and returned Business Partner metadata and a bounded entity read with HTTP 200 in all three systems through the same public API. All 100 authentication/scope rejection scenarios and all 25 trusted Hub-origin preflights passed. The broader matrix passed 52 of 72 metadata contracts and 47 of 69 bounded entity-read contracts; these are not an all-API readiness certification.
260
+
261
+ Initial native bundle readback found 24 metadata/source-derived OpenAPI definitions with 1,432 operations and 548 schemas. A subsequent approved documentation-only correction removed six metadata-prohibited writes and 18 unconfirmed currency/unit/value-help helper writes from GLAccountValueHelp and SiteMaintain, leaving 1,408 documented operations; helper reads, business operations, schemas and security were preserved. `S4_VIMIngestionStatus` lacks an authoritative available contract. Other observed backend restrictions include missing T Gateway services, Q V4 resource-exposure denials, four Q V2 services returning empty HTTP 200 HTML bodies, unsupported Article collection GET, and two Journal Entry read timeouts. These are external prerequisites, not authorization to modify SAP or SCC.
262
+
263
+ Developer Hub lists `S4_NonProd_APIs` and all 25 proxy records; declared endpoint/resource navigation resolves all 25 registered Swagger documents. A documentation-only correction added the missing required `info.version: "1"` to the existing `S4_VIMIngestionStatus` reference, using its current APIM public version; APIM readback and the actual Hub document both verified version `1`. Its operation list remains empty, not a fabricated contract.
264
+
265
+ Product publication still returned native HTTP 400, `An error occurred while reading the response`, after that correction; APIM readback at `2026-10-28T12:24:38Z` remained `DRAFT/isPublished=false`. The user observed the same error in the UI under request ID `08140d3a-253b-4f3a-9d45-ed1fa1428dc6`. All 25 proxy deployments and deployment links were intact, and there were only 25 registered resources, not more than 100. Catalog presence is not proof of completed publication. Publisher traces require SAP support; signed-in Hub reference rendering and Try Out remain unverified. No SAP backend, connector or business-data changes were made.
266
+
267
+ Subsequent user-authorized product replacement verified zero APIM application associations and zero Hub application/subscription rows, then deleted only the APIM product. Hub retained the managed product. Both published recreation and an explicit native draft request with `status_code: "DRAFT"` were rejected because the product already exists in Developer Hub; the mounted draft-create tool separately returned a null-status HTTP 500. At `2026-10-28T13:24:54Z`, the APIM product was absent, the orphan Hub product still referenced all 25 APIs, and every APIM proxy remained `DEPLOYED/hasChanges=false`. The original metadata and membership are saved in `.tmp/product-replacement-snapshot.json`. [SAP KBA 3313270](https://userapps.support.sap.com/sap/support/knowledge/en/3313270) describes the matching orphan-publication condition; its full resolution requires authenticated SAP for Me access. Direct Hub deletion was not attempted because the metadata declares cascade semantics whose impact was not established.
268
+
269
+ The user-authorized clean replacement `S4_NonProd_API_Catalog` is the active consumer product. Native creation returned HTTP 201; APIM readback at `2026-10-28T14:55:55Z` confirmed `PUBLISHED/isPublished=true`, and both APIM and Developer Hub contained the exact original 25-API membership. All 25 actual Hub references were read and validated; the 24 full definitions matched the manifest's 1,408 operations and 548 schemas and declared the expected OAuth client-credentials configuration. VIM status remains an empty reference pending its authoritative contract. API URLs, proxy policies and credentials were unchanged. The original `S4_NonProd_APIs` Hub cache residue is not the active product and still requires SAP-owned cleanup under KBA 3313270; the supplied resolution identifies development-team residue cleanup and, if necessary, a managed cache restart. Signed-in browser rendering and Try Out remain unverified. Evidence: `.tmp/publication-success-evidence.json`.
270
+
271
+ The delivery scope is APIM exposure and Developer Hub readiness only: proxy/product configuration and publication, accurate references, OAuth usage instructions and non-mutating usage checks. ABAP changes, SAP service registration/publication, transports, SCC administration and business-write testing are out of scope. No such changes or business writes were executed.
272
+
273
+ For Developer Hub consumption, choose **ACTIVE — S/4HANA Non-Production APIs** (`S4_NonProd_API_Catalog`), open its API Reference and authorize with either OAuth2 client credentials or a privately obtained bearer token—not both at once. Select exactly one granted scope: `sap-api-access-lm3cx1ja!t20691.S4D100.Access`, `sap-api-access-lm3cx1ja!t20691.S4T300.Access`, or `sap-api-access-lm3cx1ja!t20691.S4Q300.Access`. The token selects the system; the API URL stays unchanged. A Developer Hub API key or subscription does not grant these external XSUAA scopes and does not replace the bearer token. Use `GET /$metadata` and a bounded documented entity GET for Try Out; do not execute mutation operations during readiness verification. Never put backend Basic credentials, client secrets or tokens in documentation or chat.
274
+
275
+ Developer Hub — consumer onboarding and subscriptions:
276
+
277
+ - `devhub_list_developers` — list registered application developers and role/status metadata.
278
+ - `devhub_create_developer` — submit application-developer onboarding using the admin service key; approval is separate.
279
+ - `devhub_approve_developer` — approve an onboarded application developer.
280
+ - `devhub_list_applications` — list consumer application metadata without keys or secrets.
281
+ - `devhub_get_application` — read application metadata, subscriptions, and consumer-key availability; credentials are not returned.
282
+ - `devhub_create_application` — create an application on behalf of a developer with product subscriptions and SAP-generated consumer credentials.
283
+ - `devhub_subscribe_application` — add a product subscription to a subscription-based application without bypassing governance.
284
+ - `apim_test_api` — GET-only metadata or bounded entity inspection with exactly one private authentication source: `application_id` requires DEVHUB; `oauth: { service_instance, service_key, scope }` works with APIM alone. Fresh VerifyJWT issuer pinning precedes token acquisition. No arbitrary URLs, redirects, keys, tokens or business row values returned.
285
+ - `devhub_list_subscriptions` — bounded safe application/product/rate-plan IDs and subscription/approval status; incomplete/legacy relationships are explicit.
286
+ - `devhub_unsubscribe_application` — dry-run-first exact subscription retirement, approved by `UNSUBSCRIBE APPLICATION <application_id> FROM <product>`. One documented batch member preserves actual relationship references; each embedded HTTP result and fresh `isSubscribed:false` read-back are checked.
287
+ - `devhub_delete_application` — preview owner/dependency evidence and approve `DELETE CONSUMER APPLICATION <SHA256>`. A private 15-minute preview binds owner, subscriptions, legacy product links, tenant and `allow_active_subscriptions`. Both relationship sets are revalidated before one DELETE; incomplete/ambiguous evidence blocks even with permission. Named absence is distinct from runtime-key invalidation and issued OAuth-token revocation.
288
+
289
+ Native consumer recovery remains separate: My Workspace → Subscriptions → Regenerate Key changes key and secret and invalidates the old key; coordinate private cutover. Admin Center → Manage Users → Registered Users → Revoke User removes portal access only. Product unpublish/deprecation is not credential revocation. No APIM cleanup operation cascades into consumer retirement.
290
+
291
+ Credentials come from host environment or the user-only setup store, never `mcp.json`. Every write requires exact approval before network access; a preview hash is not user approval. Deletion is limited to explicit dependency-checked APIM products/proxies and separately approved named consumer applications. CPI design-time deletion, entitlement/platform administration, external-governance callbacks, consumer-key rotation and standalone proxy undeploy remain native. Passwords, client secrets, KVM values, consumer keys and application secrets stay private.
292
+
293
+ ### Native continuous monitoring and alert rules
294
+
295
+ - `ans_list_subscriptions`, `ans_list_conditions`, `ans_list_action_metadata` — documented zero-based native paging with safe operational projections; condition values, action properties/recipients and credentials stay private. Condition/subscription rows carry canonical `configuration_digest`.
296
+ - `ans_validate_native_rules` — fully resolves conditions, actions and fallback references. Disabled/snoozed subscriptions, missing/disabled actions, unsupported predicates and broad/unknown event/node scope remain findings. Same-property OR, different-property AND and explicit mandatory conditions are not flattened into an invented match.
297
+ - `ans_set_condition` / `ans_set_subscription` — exact `SET ALERT CONDITION <name>` / `SET ALERT SUBSCRIPTION <name>` approval before requests; fresh `expected_digest` required for update and absent for create. Preserve omitted existing metadata, resolve references before writing, verify intended read-back. No action-property writer, bulk import or implicit all-events subscription.
298
+ - `integration_monitoring_readiness` — combines actual accessible management/rule evidence with native setup requirements. API reads and valid rules never prove continuous collection or notification delivery.
299
+
300
+ Cloud ALM remains administrator-run: dedicated CPI API credential → Landscape Management endpoint → Integration & Exception Monitoring collection/categories and Health Monitoring → native thresholds/notifications. `MonitoringDataRead`, `MonitoringArtifactsDeploy` and `HealthCheckMonitoringDataRead` are separate setup requirements, not grants to the developer identity. Collection and approved synthetic notification delivery must be observed; otherwise they remain `needs_admin_confirmation`.
301
+
302
+ cTMS uses the trusted internal ANS integration in each subaccount, intentionally scoped to `TmsImportStarted`, `TmsImportFinished`, `TmsTransportRequestAdded`, `TmsNodeImportJobDeactivated`, `TmsStorageQuotaUsage` and exact `tags.nodeName`/`tags.nodeId`. Do not create the obsolete `ALERT_NOTIFICATION_SERVICE` destination (scheduled removal Q4 2026). APIM anomaly-event readiness is distinct from analytics polling. CPI failures are not automatically ANS events. The stdio server adds no daemon, scheduler or telemetry backend.
303
+
304
+ Contracts: [SAP ANS models](https://github.com/SAP/alert-notification-node-client/blob/main/src/configuration-api/models.ts), [configuration routes](https://github.com/SAP/alert-notification-node-client/blob/main/src/configuration-api/configuration-client.ts), [Cloud ALM CPI monitoring](https://support.sap.com/en/alm/sap-cloud-alm/operations/expert-portal/integration-monitoring/calm-cpi.html), [trusted cTMS/ANS cutover](https://github.com/SAP-docs/btp-cloud-transport-management/blob/main/docs/80-ans-integration/migrating-to-sap-alert-notification-service-as-a-trusted-internal-service-0914d2c.md).
305
+
306
+ ### Native transport readiness
307
+
308
+ `ctms_list_nodes` reads the public `GET {uri}/v2/nodes` contract with bounded local paging. Only `id`/`name` are established by the public implementation; upload/import eligibility stays unknown. [Public node client](https://github.com/SAP/jenkins-library/blob/master/src/com/sap/piper/integration/TransportManagementService.groovy), [node identity lookup](https://github.com/SAP/jenkins-library/blob/master/vars/tmsUpload.groovy).
309
+
310
+ `integration_transport_readiness({source_package_ids?,apim_proxy_names?,target_node_name})` requires explicit source content. It traverses all four package artifact groups with bounded validated continuations and explicit-count evidence, checks exact observed versions in batches of at most 20 iFlows, inspects APIM dependencies, and verifies matching private source snapshots. Countless/capped navigation, failed groups, unchecked members, non-iFlow members requiring native review, stale validation/configuration and missing matching backups block. No first-page or first-20 readiness claim.
311
+
312
+ The result is only `blocked` or `needs_admin_confirmation`: source hosts, exact versions, source-bound backup identities and node visibility are not target readiness. An authorized owner must verify Content Agent assembly format, export node, destinations and route; name the request set; approve assembly/upload/import separately; track native request/action logs; then use an independently target-bound server for exact version/runtime verification. ZIPs and toolkit snapshots are not MTARs. Native import `Error` may mean partial application; resetting to `Repeatable` is not rollback. Detailed upload/import/history schemas remain SAP-ID-gated, so no speculative automation is exposed.
313
+
314
+ ### Narrow optional component adapters
315
+
316
+ | Authority | Tools and verified boundary |
317
+ |---|---|
318
+ | Standalone Event Mesh default plan | `event_mesh_get_queue({name})`: encode the full queue identity, including slashes, as one management path parameter. Safe configuration/property metadata only; no message consumption, backlog health or Suite-embedded parity. |
319
+ | AEM cloud | `aem_list_brokers`, `aem_get_broker({service_id})`, `aem_get_broker_operation({service_id,operation_id})`: fixed US GA Mission Control API, one-based bounded pages, documented expansions and observed state/error IDs only. No Beta endpoint, region fallback, broker credentials or inferred operation success. |
320
+ | Broker-local AEM SEMP | `aem_list_queues`: configured broker/VPN only, exact queue-monitor continuations, private scoped session reuse. Numeric/boolean metadata; unknown total remains null. HTTP 529 means no session slot, not no queues; Basic access failures require native setup, never a cloud-token fallback. |
321
+ | Open Connectors | `open_connectors_list_instances`, `open_connectors_get_instance({instance_id})`: identity and element id/key/name only; private pagination tokens, no configuration/provider data. |
322
+ | Open Connectors enabled-state action | `open_connectors_set_instance_enabled({instance_id,enabled,confirmation})`: exact `ENABLE OPEN CONNECTOR INSTANCE <id>` or `DISABLE OPEN CONNECTOR INSTANCE <id>` before requests; fresh private Element token for bodyless PUT/DELETE `/instances/enabled` only. DELETE disables, not deletes. HTTP acceptance does not prove effective state or provider health; provider OAuth/re-auth stays native. |
323
+ | Edge local APIs | `edge_get_api_metadata`, `edge_list_message_logs`, `edge_get_message_log`: local Basic authority; preserve `/local/api/eic/v1` discovery and `/local/api/v1` MPL roots. Shared CPI UTC/cursor/redacted-diagnostic contracts, no payloads. Schema/entity presence is discovery, not a component/pod/job field contract. Missing `/local` can yield 401; unsupported local routes can yield 403, not proof of non-entitlement. |
324
+
325
+ Contracts: [AEM GA OpenAPI](https://api.solace.dev/cloud/openapi/mission-control.json), [SEMP security/session management](https://help.pubsub.em.services.cloud.sap/Admin/SEMP/SEMP-Security.htm), [Open Connectors enabled-state action](https://help.openconnectors.ext.hana.ondemand.com/home/disabling-and-enabling-instances), [pagination](https://help.openconnectors.ext.hana.ondemand.com/home/standardized-pagination), [Edge local API access](https://github.com/SAP-docs/btp-integration-suite/blob/main/docs/ISuite_Edge_Integration_Cell/local-odata-api-access-fe61332.md).
326
+
327
+ ### Native lifecycle handoffs
328
+
329
+ `integration_connection_info.capabilities` always includes these boundaries, prerequisites, primary source links, concrete steps, safety warnings and post-action verification. `native_access: unverified` is not hidden automation or an entitlement decision. Require an authorized native owner and exact identities before native writes.
330
+
331
+ | Surface | Native action and required verification |
332
+ |---|---|
333
+ | Capability activation/permissions | Administrator inspects Suite capability activation, BTP entitlement and operation-specific roles. Tool access/403/404 alone does not establish their cause; never grant roles or provision services through a metadata check. |
334
+ | Shared CPI artifact creation and version history | Design → package → Edit → Artifacts → Add → correct Value Mapping/Script Collection/Message Mapping Create/Upload; save/restore the selected version. Verify identity/version and approve deployment separately. Design-workspace ZIP, mass-download ZIP and MTAR are not interchangeable. |
335
+ | CPI queue controls/retry | Monitor → Manage Stores → Message Queues → inspect exact queue, Where-Used, access type/state → Actions → Usage → Start/Stop. Approve exact-message retry separately for an active non-exclusive queue; whole-queue Retry affects all messages and can duplicate receiver effects. Refresh metadata and correlate MPL. |
336
+ | CPI cancellation/design deletion | Review exact message/artifact, backup, dependencies, configuration-only restrictions and locks; use supported native confirmation. Never unlock another user, guess a status write, promise processing stops or substitute queue deletion. Verify named design absence/observed processing state. |
337
+ | APIM native revision/standalone undeploy | Preserve current draft and product associations → API details → Revisions → select retained revision → Revert → inspect new revision → Deploy; use native Undeploy separately. Toolkit saved-bundle import creates a new imported revision, not native Revert. Verify content/runtime and separately approve consumer reads. |
338
+ | Consumer rotation/portal revoke | Owner coordinates private key/secret delivery and client cutover → My Workspace → Subscriptions → Regenerate Key. Portal removal: Admin Center → Manage Users → Registered Users → Revoke User with reason. Rotation invalidates the old key; portal revoke, unpublish and application deletion do not prove issued OAuth-token revocation. |
339
+ | cTMS promotion | Follow the native transport-readiness procedure above: Content Agent, named source/export node/route/request set, distinct assembly/upload/import approvals, action logs and independent target-bound verification. Partial import is not rollback. |
340
+ | Cloud ALM / ANS delivery | Follow native continuous-monitoring setup above. Rule validity/API reads do not prove collection or delivery; require observed collection and approved synthetic notification evidence. |
341
+ | Suite-embedded Event Mesh | Suite administrator inspects Configure/Monitor → Event Mesh activation and configured limits. Standalone default-plan credentials/APIs do not establish parity or embedded access. |
342
+ | TPM | Design → B2B Scenarios → Agreements → exact version → Activate → explicitly approve Default or Full. Default does not activate inactive security/identifier/custom-rule references; Full activates all references. Inspect activation failure/Partner Directory Data and Monitor → Interchanges. `B2bInterchangeRead` excludes payload access; operational retry/restart/cancel needs separate approval and `B2bInterchangeOperate`. Never edit generated Partner Directory data to simulate agreement management. |
343
+ | Integration Advisor | Select MIG/MAG and exact version → validate/simulate → Additional Options → Export → SAP Cloud Integration Runtime Artifacts. Inspect/import generated CPI content separately; editing it does not edit the originating guideline. Do not mix old/new namespace-dependent exported files. |
344
+ | Integration Assessment | Existing APIs/default service key → User Actions → API Reference → establish exact resource/schema. `url` is the token-server URL; `entities` and `management` are resource origins. Do not parse as CPI or guess `/applications`. Record the selected native assessment outcome. |
345
+ | Migration Assessment/tooling | Request → Data Extraction → inspect Log; Request → Scenario Evaluation → inspect dashboard → export/generate report. Conversion occurs in native tooling; inspect generated CPI identities/content separately. Assessment is not conversion or deployment; no PO-credential reuse. |
346
+ | New Integration Cell API/MCP artifacts | Design → Integrations and APIs → package → Edit → Artifacts → Add → API / MCP Server; review exact runtime/version, source and security. API-derived MCP requires deployed OAuth OData/REST and OpenAPI 3.0.0–3.0.3. Separately approve Deploy and runtime/virtual host; Monitor → Integrations and APIs → Integration Cell → Manage Integration Content / Monitor Message Processing. Classic APIM tools and MCP invocation access are not administration of these artifacts. |
347
+ | Edge component/pod/job operations | Authorized native Operations Cockpit → Component Monitor / Job Management → exact component/pod/job diagnostics. Published routes/metadata do not establish safe field schemas. No restart, job control, Kubernetes or shell bridge; correlate supported local MPL separately. |
348
+
349
+ Primary procedures for new artifacts: [API design](https://github.com/SAP-docs/btp-integration-suite/blob/main/docs/ISuite_Integrations_APIs/create-an-api-artifact-using-a-target-url-or-an-openapi-specification-914f57e.md), [API-derived MCP](https://github.com/SAP-docs/btp-integration-suite/blob/main/docs/ISuite_Integrations_APIs/create-an-mcp-server-from-an-api-artifact-9f18140.md), [native monitoring](https://github.com/SAP-docs/btp-integration-suite/blob/main/docs/ISuite_Integrations_APIs/monitor-apis-and-mcp-servers-399b6c6.md).
350
+
351
+ ### Operator verification and approval gates
352
+
353
+ The existing Integration Architect/Developer agents and skills cover discover → reuse → dependencies → approved author/configure → retained save → selected-version validation → private snapshot/readiness → separately approved deploy/native promotion → independent target verification → bounded observation/native alert readiness → diagnosis → separately approved recovery/retirement. No extra operator persona is installed.
187
354
 
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.
355
+ Use `node scripts/check-live-integration.mjs --window-minutes 60 --top 3 --json` for connected read-only verification. It permits management GETs and exact configured token POSTs only, projects diagnostic metadata and reports absent optional adapters as `live_unverified`. It never validates/saves/imports/deploys/configures, probes consumer APIs, consumes/retries messages, deletes, or asserts native collection/delivery/promotion success. Exit 0 is read/schema execution evidence, not landscape health. `npm run test:live:integration` is the distinct mutating fixture runner and needs separate explicit approval.