@cap-js/ord 1.3.14 → 1.4.1

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/lib/constants.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const AUTHENTICATION_TYPE = Object.freeze({
2
2
  Open: "open",
3
3
  Basic: "basic",
4
+ CfMtls: "cf-mtls",
4
5
  });
5
6
 
6
7
  const BASIC_AUTH_HEADER_KEY = "authorization";
@@ -15,11 +16,24 @@ const BLOCKED_SERVICE_NAME = Object.freeze({
15
16
  const ORD_ACCESS_STRATEGY = Object.freeze({
16
17
  Open: "open",
17
18
  Basic: "basic-auth",
19
+ CfMtls: "sap:cmp-mtls:v1",
18
20
  });
19
21
 
20
22
  const AUTH_TYPE_ORD_ACCESS_STRATEGY_MAP = Object.freeze({
21
23
  [AUTHENTICATION_TYPE.Open]: ORD_ACCESS_STRATEGY.Open,
22
24
  [AUTHENTICATION_TYPE.Basic]: ORD_ACCESS_STRATEGY.Basic,
25
+ [AUTHENTICATION_TYPE.CfMtls]: ORD_ACCESS_STRATEGY.CfMtls,
26
+ });
27
+
28
+ // CF mTLS default header names
29
+ const CF_MTLS_HEADERS = Object.freeze({
30
+ ISSUER: "x-ssl-client-issuer-dn",
31
+ SUBJECT: "x-ssl-client-subject-dn",
32
+ ROOT_CA: "x-ssl-client-root-ca-dn",
33
+ // XFCC (X-Forwarded-Client-Cert) headers for proxy-verified mTLS
34
+ XFCC: "x-forwarded-client-cert",
35
+ CLIENT: "x-ssl-client",
36
+ CLIENT_VERIFY: "x-ssl-client-verify",
23
37
  });
24
38
 
25
39
  const CDS_ELEMENT_KIND = Object.freeze({
@@ -38,6 +52,7 @@ const COMPILER_TYPES = Object.freeze({
38
52
  asyncapi2: "asyncapi2",
39
53
  edmx: "edmx",
40
54
  csn: "csn",
55
+ mcp: "mcp",
41
56
  });
42
57
 
43
58
  const CONTENT_MERGE_KEY = "ordId";
@@ -98,6 +113,31 @@ const SHORT_DESCRIPTION_PREFIX = "Short description of ";
98
113
  const SEM_VERSION_REGEX =
99
114
  /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
100
115
 
116
+ const MCP_CUSTOM_TYPE = "sap:mcp-server-card:v0";
117
+
118
+ // CF mTLS Error Reasons
119
+ const CF_MTLS_ERROR_REASON = Object.freeze({
120
+ NO_HEADERS: "NO_HEADERS",
121
+ HEADER_MISSING: "HEADER_MISSING",
122
+ INVALID_ENCODING: "INVALID_ENCODING",
123
+ XFCC_VERIFICATION_FAILED: "XFCC_VERIFICATION_FAILED",
124
+ CERT_PAIR_MISMATCH: "CERT_PAIR_MISMATCH",
125
+ ROOT_CA_MISMATCH: "ROOT_CA_MISMATCH",
126
+ });
127
+
128
+ // HTTP Configuration Constants
129
+ const HTTP_CONFIG = Object.freeze({
130
+ METHOD_GET: "GET",
131
+ CONTENT_TYPE_JSON: "application/json",
132
+ MTLS_TIMEOUT_MS: 10000,
133
+ });
134
+
135
+ // Authentication String Constants
136
+ const AUTH_STRINGS = Object.freeze({
137
+ BASIC_PREFIX: "Basic ",
138
+ WWW_AUTHENTICATE_REALM: 'Basic realm="401"',
139
+ });
140
+
101
141
  module.exports = {
102
142
  AUTHENTICATION_TYPE,
103
143
  AUTH_TYPE_ORD_ACCESS_STRATEGY_MAP,
@@ -105,6 +145,7 @@ module.exports = {
105
145
  BUILD_DEFAULT_PATH,
106
146
  BLOCKED_SERVICE_NAME,
107
147
  CDS_ELEMENT_KIND,
148
+ CF_MTLS_HEADERS,
108
149
  COMPILER_TYPES,
109
150
  CONTENT_MERGE_KEY,
110
151
  DATA_PRODUCT_ANNOTATION,
@@ -113,6 +154,7 @@ module.exports = {
113
154
  DESCRIPTION_PREFIX,
114
155
  ENTITY_RELATIONSHIP_ANNOTATION,
115
156
  LEVEL,
157
+ MCP_CUSTOM_TYPE,
116
158
  OPEN_RESOURCE_DISCOVERY_VERSION,
117
159
  ORD_ACCESS_STRATEGY,
118
160
  ORD_DOCUMENT_FILE_NAME,
@@ -127,4 +169,7 @@ module.exports = {
127
169
  SUPPORTED_IMPLEMENTATIONSTANDARD_VERSIONS,
128
170
  SHORT_DESCRIPTION_PREFIX,
129
171
  SEM_VERSION_REGEX,
172
+ CF_MTLS_ERROR_REASON,
173
+ HTTP_CONFIG,
174
+ AUTH_STRINGS,
130
175
  };
package/lib/defaults.js CHANGED
@@ -1,6 +1,5 @@
1
1
  const { OPEN_RESOURCE_DISCOVERY_VERSION, SHORT_DESCRIPTION_PREFIX, RESOURCE_VISIBILITY } = require("./constants");
2
2
  const { hasSAPPolicyLevel } = require("./utils");
3
- const { getAuthConfig } = require("./authentication");
4
3
  const _ = require("lodash");
5
4
 
6
5
  const packageTypes = [
@@ -139,14 +138,19 @@ module.exports = {
139
138
  apiResources: [],
140
139
  eventResources: [],
141
140
  entityTypes: [],
142
- baseTemplate: {
143
- openResourceDiscoveryV1: {
144
- documents: [
145
- {
146
- url: "/ord/v1/documents/ord-document",
147
- accessStrategies: getAuthConfig().accessStrategies,
148
- },
149
- ],
150
- },
141
+ baseTemplate: (authConfig) => {
142
+ // Get access strategies from the provided authConfig
143
+ // If auth config is not available, fall back to empty array
144
+ const accessStrategies = authConfig?.accessStrategies || [];
145
+ return {
146
+ openResourceDiscoveryV1: {
147
+ documents: [
148
+ {
149
+ url: "/ord/v1/documents/ord-document",
150
+ accessStrategies,
151
+ },
152
+ ],
153
+ },
154
+ };
151
155
  },
152
156
  };
@@ -1,7 +1,7 @@
1
1
  const { CONTENT_MERGE_KEY } = require("./constants");
2
2
  const cds = require("@sap/cds");
3
3
  const fs = require("fs");
4
- const { Logger } = require("./logger");
4
+ const Logger = require("./logger");
5
5
  const path = require("path");
6
6
  const _ = require("lodash");
7
7
 
@@ -19,13 +19,44 @@ function cleanNullProperties(obj) {
19
19
  function patchGeneratedOrdResources(destinationObj, sourceObj) {
20
20
  const destObj = _.keyBy(destinationObj, CONTENT_MERGE_KEY);
21
21
  const srcObj = _.keyBy(sourceObj, CONTENT_MERGE_KEY);
22
- for (const ordId in srcObj) {
23
- if (ordId in destObj) {
24
- destObj[ordId] = _.assignWith(structuredClone(destObj[ordId]), structuredClone(srcObj[ordId]));
22
+
23
+ // Helper: extract MCP merge logic for clarity & reuse
24
+ const mergeMCPResource = (ordId, existingKey) => {
25
+ const baseKey = ordId in destObj ? ordId : existingKey;
26
+ if (baseKey) {
27
+ const base = structuredClone(destObj[baseKey]);
28
+ const override = structuredClone(srcObj[ordId]);
29
+ const merged = {
30
+ ...base, // preserve generated structural fields
31
+ ...override, // overlay custom changes
32
+ apiProtocol: "mcp", // enforce protocol
33
+ partOfPackage: base.partOfPackage || override.partOfPackage,
34
+ resourceDefinitions: base.resourceDefinitions || override.resourceDefinitions,
35
+ };
36
+ if (baseKey !== ordId) delete destObj[baseKey];
37
+ destObj[ordId] = merged;
25
38
  } else {
26
- destObj[ordId] = srcObj[ordId];
39
+ // MCP plugin not available: do not introduce brand-new MCP resource purely from custom file
40
+ // This preserves contract: MCP resource only exists when plugin is available.
41
+ // Silently skip or log (warn) to avoid test noise.
42
+ // Logger.warn could be added here if desired.
43
+ return; // skip adding
44
+ }
45
+ };
46
+
47
+ const existingMCPKey = Object.keys(destObj).find((k) => destObj[k]?.apiProtocol === "mcp");
48
+
49
+ for (const ordId in srcObj) {
50
+ if (ordId.endsWith(":apiResource:mcp-server:v1")) {
51
+ mergeMCPResource(ordId, existingMCPKey);
52
+ continue;
27
53
  }
54
+ destObj[ordId] =
55
+ ordId in destObj
56
+ ? _.assignWith(structuredClone(destObj[ordId]), structuredClone(srcObj[ordId]))
57
+ : srcObj[ordId];
28
58
  }
59
+
29
60
  return cleanNullProperties(Object.values(destObj));
30
61
  }
31
62
 
@@ -54,10 +85,11 @@ function compareAndHandleCustomORDContentWithExistingContent(ordContent, customO
54
85
  function getCustomORDContent(appConfig) {
55
86
  if (!appConfig.env?.customOrdContentFile) return;
56
87
  const pathToCustomORDContent = path.join(cds.root, appConfig.env?.customOrdContentFile);
57
- if (fs.existsSync(pathToCustomORDContent)) {
88
+ if (!fs.existsSync(pathToCustomORDContent)) {
58
89
  Logger.error("Custom ORD content file not found at", pathToCustomORDContent);
59
- return require(pathToCustomORDContent);
90
+ return;
60
91
  }
92
+ return require(pathToCustomORDContent);
61
93
  }
62
94
 
63
95
  function extendCustomORDContentIfExists(appConfig, ordContent) {
package/lib/index.js CHANGED
@@ -1,8 +1,11 @@
1
+ /**
2
+ * Public API entry point for @cap-js/ord library
3
+ * Exposes core functionality for ORD document generation and metadata retrieval
4
+ *
5
+ * @example
6
+ * const { ord, getMetadata } = require('@cap-js/ord/lib');
7
+ */
1
8
  module.exports = {
2
- defaults: require("./defaults.js"),
3
- getMetadata: require("./metaData.js"),
4
9
  ord: require("./ord.js"),
5
- Logger: require("./logger.js"),
6
- constants: require("./constants.js"),
7
- authentication: require("./authentication.js"),
10
+ getMetadata: require("./metaData.js"),
8
11
  };
package/lib/logger.js CHANGED
@@ -1,17 +1,8 @@
1
1
  const cds = require("@sap/cds");
2
2
 
3
- const _debugLevel = cds.env?.DEBUG || process.env.DEBUG;
4
-
5
- function _getLogLevel(debugLevel, logLevels) {
6
- return debugLevel ? logLevels.DEBUG : logLevels.WARN;
7
- }
8
-
9
- const level = _getLogLevel(_debugLevel, cds.log?.levels);
10
-
3
+ // Unified INFO-level logging - simple and consistent across all environments
11
4
  const Logger = cds.log("ord-plugin", {
12
- level,
5
+ level: cds.log?.levels?.INFO,
13
6
  });
14
7
 
15
- module.exports = {
16
- Logger,
17
- };
8
+ module.exports = Logger;
@@ -0,0 +1,132 @@
1
+ const Logger = require("./logger");
2
+ const cds = require("@sap/cds");
3
+ const path = require("path");
4
+
5
+ /**
6
+ * MCP Plugin Adapter
7
+ * Provides an abstraction layer for interacting with @btp-ai/mcp-plugin
8
+ * This allows for easy mocking and testing without requiring the actual plugin
9
+ */
10
+
11
+ /**
12
+ * Load package.json from project root
13
+ * @returns {Object} Package.json content
14
+ * @throws {Error} If package.json is not found
15
+ */
16
+ function _loadPackageJson() {
17
+ const packageJsonPath = path.join(cds.root, "package.json");
18
+ if (!cds.utils.exists(packageJsonPath)) {
19
+ throw new Error(`package.json not found in the project root directory`);
20
+ }
21
+ return require(packageJsonPath);
22
+ }
23
+
24
+ /**
25
+ * Check if MCP plugin is listed in package.json dependencies
26
+ * @param {Function} loadPackageJsonFn - Optional function to load package.json (for testing)
27
+ * @returns {boolean} True if plugin is in dependencies or devDependencies
28
+ */
29
+ function isMCPPluginInPackageJson(loadPackageJsonFn = _loadPackageJson) {
30
+ try {
31
+ const packageJson = loadPackageJsonFn();
32
+ const allDependencies = {
33
+ ...(packageJson.dependencies || {}),
34
+ ...(packageJson.devDependencies || {}),
35
+ };
36
+ const isInstalled = "@btp-ai/mcp-plugin" in allDependencies;
37
+ if (isInstalled) {
38
+ Logger.log("MCP plugin found in package.json dependencies");
39
+ }
40
+ return isInstalled;
41
+ } catch (error) {
42
+ Logger.error("Could not check package.json for MCP plugin:", error.message);
43
+ return false;
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Check if MCP plugin is available at runtime
49
+ * @param {Function} resolveFunction - Optional custom resolve function for testing
50
+ * @returns {boolean} True if plugin is available
51
+ */
52
+ function isMCPPluginAvailable(resolveFunction = require.resolve) {
53
+ try {
54
+ resolveFunction("@btp-ai/mcp-plugin");
55
+ Logger.log("MCP plugin is available at runtime");
56
+ return true;
57
+ } catch {
58
+ return false;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Get MCP plugin instance
64
+ * Returns null if plugin is not available
65
+ * @returns {Object|null} MCP plugin module or null
66
+ */
67
+ function getMcpPlugin() {
68
+ if (!isMCPPluginAvailable()) {
69
+ return null;
70
+ }
71
+
72
+ try {
73
+ return require("@btp-ai/mcp-plugin");
74
+ } catch (error) {
75
+ Logger.error("Failed to load MCP plugin:", error.message);
76
+ return null;
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Check if MCP plugin is ready for use (both installed and available)
82
+ * This function combines the logic of both isMCPPluginInPackageJson and isMCPPluginAvailable
83
+ * to provide a comprehensive check for MCP plugin readiness.
84
+ * Maintains the original AND logic: both conditions must be true.
85
+ *
86
+ * @param {Object} options - Options for checking
87
+ * @param {Function} options.resolveFunction - Custom resolve function for testing
88
+ * @param {Function} options.loadPackageJsonFn - Custom package.json loader for testing
89
+ * @returns {boolean} True if plugin is ready for use
90
+ */
91
+ function isMCPPluginReady(options = {}) {
92
+ const { resolveFunction, loadPackageJsonFn } = options;
93
+
94
+ // Both conditions must be satisfied: declared in package.json AND available at runtime
95
+ return isMCPPluginInPackageJson(loadPackageJsonFn) && isMCPPluginAvailable(resolveFunction);
96
+ }
97
+
98
+ /**
99
+ * Build MCP server definition
100
+ * @param {Array} services - Array of CDS services
101
+ * @returns {Promise<Object>} MCP server definition
102
+ * @throws {Error} If MCP plugin is not available
103
+ */
104
+ async function buildMcpServerDefinition(services = []) {
105
+ const plugin = getMcpPlugin();
106
+
107
+ if (!plugin) {
108
+ throw new Error("MCP plugin not available");
109
+ }
110
+
111
+ // Validate services parameter
112
+ if (!Array.isArray(services)) {
113
+ throw new Error("Services parameter must be an array");
114
+ }
115
+
116
+ try {
117
+ const { exposeMcpServerDefinitionForOrd } = require("@btp-ai/mcp-plugin/lib/utils/metadata");
118
+ return await exposeMcpServerDefinitionForOrd(services);
119
+ } catch (error) {
120
+ Logger.error("Failed to build MCP server definition:", error.message);
121
+ throw error;
122
+ }
123
+ }
124
+
125
+ module.exports = {
126
+ isMCPPluginAvailable,
127
+ isMCPPluginInPackageJson,
128
+ isMCPPluginReady,
129
+ getMcpPlugin,
130
+ buildMcpServerDefinition,
131
+ _loadPackageJson, // Export for testing
132
+ };
package/lib/metaData.js CHANGED
@@ -2,11 +2,12 @@ const cds = require("@sap/cds/lib");
2
2
  const { compile: openapi } = require("@cap-js/openapi");
3
3
  const { compile: asyncapi } = require("@cap-js/asyncapi");
4
4
  const { COMPILER_TYPES } = require("./constants");
5
- const { Logger } = require("./logger");
5
+ const Logger = require("./logger");
6
6
  const { interopCSN } = require("./interopCsn.js");
7
7
  const cdsc = require("@sap/cds-compiler/lib/main");
8
+ const { isMCPPluginReady, buildMcpServerDefinition } = require("./mcpAdapter");
8
9
 
9
- module.exports = async (url, model = null) => {
10
+ const getMetadata = async (url, model = null) => {
10
11
  const parts = url
11
12
  ?.split("/")
12
13
  .pop()
@@ -15,13 +16,14 @@ module.exports = async (url, model = null) => {
15
16
  const compilerType = parts.pop();
16
17
  const serviceName = parts.join(".");
17
18
  const csn = model || cds.services[serviceName]?.model;
19
+ const compileOptions = cds.env["ord"]?.compileOptions || {};
18
20
 
19
21
  let responseFile;
20
22
  const options = { service: serviceName, as: "str", messages: [] };
21
23
  switch (compilerType) {
22
24
  case COMPILER_TYPES.oas3:
23
25
  try {
24
- responseFile = openapi(csn, options);
26
+ responseFile = openapi(csn, { ...options, ...(compileOptions?.openapi || {}) });
25
27
  } catch (error) {
26
28
  Logger.error("OpenApi error:", error.message);
27
29
  throw error;
@@ -29,7 +31,7 @@ module.exports = async (url, model = null) => {
29
31
  break;
30
32
  case COMPILER_TYPES.asyncapi2:
31
33
  try {
32
- responseFile = asyncapi(csn, options);
34
+ responseFile = asyncapi(csn, { ...options, ...(compileOptions?.asyncapi || {}) });
33
35
  } catch (error) {
34
36
  Logger.error("AsyncApi error:", error.message);
35
37
  throw error;
@@ -47,14 +49,33 @@ module.exports = async (url, model = null) => {
47
49
  break;
48
50
  case COMPILER_TYPES.edmx:
49
51
  try {
50
- responseFile = await cds.compile(csn).to["edmx"](options);
52
+ responseFile = await cds.compile(csn).to["edmx"]({ ...options, ...(compileOptions?.edmx || {}) });
51
53
  } catch (error) {
52
54
  Logger.error("Edmx error:", error.message);
53
55
  throw error;
54
56
  }
57
+ break;
58
+ case COMPILER_TYPES.mcp:
59
+ if (!isMCPPluginReady()) {
60
+ throw new Error("MCP plugin is not available or not ready for use");
61
+ }
62
+ try {
63
+ // Get all available CDS services
64
+ const allServices = Object.values(cds.services);
65
+ // Generate metadata from runtime services using adapter
66
+ const mcpResult = await buildMcpServerDefinition(allServices);
67
+ // Extract only the MCP content, not the ORD metadata
68
+ responseFile = mcpResult.mcp;
69
+ } catch (error) {
70
+ Logger.error("MCP server definition error:", error.message);
71
+ throw error;
72
+ }
73
+ break;
55
74
  }
56
75
  return {
57
76
  contentType: `application/${compilerType === "edmx" ? "xml" : "json"}`,
58
77
  response: responseFile,
59
78
  };
60
79
  };
80
+
81
+ module.exports = getMetadata;
@@ -1,23 +1,37 @@
1
1
  const cds = require("@sap/cds");
2
- const { ord, getMetadata, defaults, authentication, Logger } = require("./index.js");
2
+ const ord = require("./ord.js");
3
+ const getMetadata = require("./metaData.js");
4
+ const defaults = require("./defaults.js");
5
+ const { createAuthConfig, createAuthMiddleware } = require("./auth/authentication.js");
6
+ const Logger = require("./logger.js");
3
7
 
4
8
  class OpenResourceDiscoveryService extends cds.ApplicationService {
5
- init() {
9
+ async init() {
10
+ // Initialize authentication configuration from .cdsrc.json or environment variables
11
+ // CF mTLS validator is lazily initialized on first mTLS request
12
+ const authConfig = createAuthConfig();
13
+ if (authConfig.error) {
14
+ throw new Error(`Authentication initialization failed: ${authConfig.error}`);
15
+ }
16
+
17
+ // Create authentication middleware
18
+ const authMiddleware = createAuthMiddleware(authConfig);
19
+
6
20
  cds.app.get(`${this.path}`, (_, res) => {
7
- return res.status(200).send(defaults.baseTemplate);
21
+ return res.status(200).send(defaults.baseTemplate(authConfig));
8
22
  });
9
23
 
10
- cds.app.get(`/ord/v1/documents/ord-document`, authentication.authenticate, async (_, res) => {
24
+ cds.app.get(`/ord/v1/documents/ord-document`, authMiddleware, async (_, res) => {
11
25
  const csn = cds.context?.model || cds.model;
12
26
  const data = ord(csn);
13
27
  return res.status(200).send(data);
14
28
  });
15
29
 
16
- cds.app.get(`/ord/v1/documents/:id`, authentication.authenticate, async (_, res) => {
30
+ cds.app.get(`/ord/v1/documents/:id`, authMiddleware, async (_, res) => {
17
31
  return res.status(404).send("404 Not Found");
18
32
  });
19
33
 
20
- cds.app.get(`/ord/v1/:ordId?/:service?`, authentication.authenticate, async (req, res) => {
34
+ cds.app.get(`/ord/v1/:ordId?/:service?`, authMiddleware, async (req, res) => {
21
35
  try {
22
36
  const { contentType, response } = await getMetadata(req.url);
23
37
  return res.status(200).contentType(contentType).send(response);
package/lib/ord.js CHANGED
@@ -11,18 +11,38 @@ const {
11
11
  createEntityTypeMappingsItemTemplate,
12
12
  createEventResourceTemplate,
13
13
  createGroupsTemplateForService,
14
+ createMCPAPIResourceTemplate,
14
15
  _propagateORDVisibility,
15
16
  } = require("./templates");
16
17
  const { extendCustomORDContentIfExists } = require("./extendOrdWithCustom");
17
18
  const { getRFC3339Date } = require("./date");
18
- const { getAuthConfig } = require("./authentication");
19
+ const { createAuthConfig } = require("./auth/authentication");
20
+ const { isMCPPluginReady } = require("./mcpAdapter");
19
21
 
20
- const { Logger } = require("./logger");
22
+ const Logger = require("./logger");
21
23
  const _ = require("lodash");
22
24
  const cds = require("@sap/cds");
23
25
  const defaults = require("./defaults");
24
26
  const path = require("path");
25
27
 
28
+ const _addMCPResourceIfAvailable = (apiResources, appConfig, packageIds, accessStrategies) => {
29
+ // Use comprehensive check for MCP plugin readiness
30
+ const shouldAddMCP = isMCPPluginReady();
31
+ if (shouldAddMCP) {
32
+ try {
33
+ const mcpResources = createMCPAPIResourceTemplate(appConfig, packageIds, accessStrategies);
34
+ // Handle both array and single object responses from MCP plugin
35
+ if (Array.isArray(mcpResources)) {
36
+ apiResources.push(...mcpResources);
37
+ } else if (mcpResources) {
38
+ apiResources.push(mcpResources);
39
+ }
40
+ } catch (error) {
41
+ Logger.warn("Failed to create MCP API resource:", error.message);
42
+ }
43
+ }
44
+ };
45
+
26
46
  const initializeAppConfig = (csn) => {
27
47
  const packageJson = _loadPackageJson();
28
48
  const packageName = packageJson.name;
@@ -230,7 +250,7 @@ const _getEntityTypes = (appConfig, packageIds) => {
230
250
  };
231
251
 
232
252
  const _getAPIResources = (csn, appConfig, packageIds, accessStrategies) => {
233
- return appConfig.apiResourceNames.flatMap((apiResourceName) =>
253
+ const apiResources = appConfig.apiResourceNames.flatMap((apiResourceName) =>
234
254
  createAPIResourceTemplate(
235
255
  apiResourceName,
236
256
  csn.definitions[apiResourceName],
@@ -239,6 +259,11 @@ const _getAPIResources = (csn, appConfig, packageIds, accessStrategies) => {
239
259
  accessStrategies,
240
260
  ),
241
261
  );
262
+
263
+ // Conditionally add MCP API resource if plugin is available
264
+ _addMCPResourceIfAvailable(apiResources, appConfig, packageIds, accessStrategies);
265
+
266
+ return apiResources;
242
267
  };
243
268
 
244
269
  const _getEventResources = (csn, appConfig, packageIds, accessStrategies) => {
@@ -322,7 +347,13 @@ function _filterUnusedPackages(ordDocument) {
322
347
  module.exports = (csn) => {
323
348
  const linkedCsn = _propagateORDVisibility(cds.linked(csn));
324
349
  const appConfig = initializeAppConfig(linkedCsn);
325
- const accessStrategies = getAuthConfig().accessStrategies;
350
+
351
+ // Create auth config and fail-closed on configuration errors
352
+ const authConfig = createAuthConfig();
353
+ if (authConfig.error) {
354
+ throw new Error(`Authentication configuration error: ${authConfig.error}`);
355
+ }
356
+ const accessStrategies = authConfig.accessStrategies;
326
357
 
327
358
  let ordDocument = createDefaultORDDocument(linkedCsn, appConfig);
328
359
  const packageIds = extractPackageIds(ordDocument);