@gavdi/cap-mcp 1.5.1 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -29,7 +29,7 @@ By integrating MCP with your CAP applications, you unlock:
29
29
 
30
30
  - **Node.js**: Version 18 or higher
31
31
  - **SAP CAP**: Version 9 or higher
32
- - **Express**: Version 4 or higher
32
+ - **Express**: Version 5 or higher
33
33
  - **TypeScript**: Optional but recommended
34
34
 
35
35
  ### Step 1: Install the Plugin
package/lib/auth/utils.js CHANGED
@@ -304,111 +304,112 @@ function registerOAuthEndpoints(expressApp, credentials, kind) {
304
304
  // OAuth Dynamic Client Registration discovery endpoint (GET)
305
305
  expressApp.get("/oauth/register", async (req, res) => {
306
306
  const baseUrl = (0, host_resolver_1.buildPublicBaseUrl)(req);
307
+ // XSUAA does not support DCR so we will respond with the pre-configured client_id
307
308
  // IAS does not support DCR so we will respond with the pre-configured client_id
308
- if (kind === "ias") {
309
- const enhancedResponse = {
310
- client_id: credentials.clientid, // Add our CAP app's client ID
311
- redirect_uris: req.body.redirect_uris || [
312
- `${baseUrl}/oauth/callback`,
313
- ],
314
- };
315
- logger_1.LOGGER.debug("Provided static client_id during DCR registration process");
316
- res.json(enhancedResponse);
317
- return;
318
- }
309
+ // if (kind === "ias") {
310
+ const enhancedResponse = {
311
+ client_id: credentials.clientid, // Add our CAP app's client ID
312
+ redirect_uris: req.body.redirect_uris || [`${baseUrl}/oauth/callback`],
313
+ };
314
+ logger_1.LOGGER.debug("Provided static client_id during DCR registration process");
315
+ res.json(enhancedResponse);
316
+ return;
317
+ // }
319
318
  // Keep original implementation for XSUAA
320
- try {
321
- // Simple proxy for discovery - no CSRF needed
322
- const response = await fetch(`${credentials.url}/oauth/register`, {
323
- method: "GET",
324
- headers: {
325
- Authorization: `Basic ${Buffer.from(`${credentials.clientid}:${credentials.clientsecret}`).toString("base64")}`,
326
- Accept: "application/json",
327
- },
328
- });
329
- const xsuaaData = await response.json();
330
- // Add missing required fields that MCP client expects
331
- const enhancedResponse = {
332
- ...xsuaaData, // Keep all XSUAA fields
333
- client_id: credentials.clientid, // Add our CAP app's client ID
334
- redirect_uris: [`${baseUrl}/oauth/callback`], // Add our callback URL for discovery
335
- };
336
- res.status(response.status).json(enhancedResponse);
337
- }
338
- catch (error) {
339
- logger_1.LOGGER.error("OAuth registration discovery error:", error);
340
- res.status(500).json({
341
- error: "server_error",
342
- error_description: error instanceof Error ? error.message : "Unknown error",
343
- });
344
- }
319
+ // try {
320
+ // // Simple proxy for discovery - no CSRF needed
321
+ // const response = await fetch(`${credentials.url}/oauth/register`, {
322
+ // method: "GET",
323
+ // headers: {
324
+ // Authorization: `Basic ${Buffer.from(`${credentials.clientid}:${credentials.clientsecret}`).toString("base64")}`,
325
+ // Accept: "application/json",
326
+ // },
327
+ // });
328
+ // const xsuaaData = await response.json();
329
+ // // Add missing required fields that MCP client expects
330
+ // const enhancedResponse = {
331
+ // ...xsuaaData, // Keep all XSUAA fields
332
+ // client_id: credentials.clientid, // Add our CAP app's client ID
333
+ // redirect_uris: [`${baseUrl}/oauth/callback`], // Add our callback URL for discovery
334
+ // };
335
+ // res.status(response.status).json(enhancedResponse);
336
+ // } catch (error) {
337
+ // LOGGER.error("OAuth registration discovery error:", error);
338
+ // res.status(500).json({
339
+ // error: "server_error",
340
+ // error_description:
341
+ // error instanceof Error ? error.message : "Unknown error",
342
+ // });
343
+ // }
345
344
  });
346
345
  // OAuth Dynamic Client Registration endpoint (POST) with CSRF handling
347
346
  expressApp.post("/oauth/register", async (req, res) => {
348
347
  const baseUrl = (0, host_resolver_1.buildPublicBaseUrl)(req);
348
+ // XSUAA does not support DCR so we will respond with the pre-configured client_id
349
349
  // IAS does not support DCR so we will respond with the pre-configured client_id
350
- if (kind === "ias") {
351
- const enhancedResponse = {
352
- client_id: credentials.clientid, // Add our CAP app's client ID
353
- redirect_uris: req.body.redirect_uris || [
354
- `${baseUrl}/oauth/callback`,
355
- ],
356
- };
357
- logger_1.LOGGER.debug("Provided static client_id during DCR registration process");
358
- res.json(enhancedResponse);
359
- return;
360
- }
350
+ // if (kind === "ias") {
351
+ const enhancedResponse = {
352
+ client_id: credentials.clientid, // Add our CAP app's client ID
353
+ redirect_uris: req.body.redirect_uris || [`${baseUrl}/oauth/callback`],
354
+ };
355
+ logger_1.LOGGER.debug("Provided static client_id during DCR registration process");
356
+ res.json(enhancedResponse);
357
+ return;
358
+ // }
361
359
  // Keep original implementation for XSUAA
362
- try {
363
- // Step 1: Fetch CSRF token from XSUAA
364
- const csrfResponse = await fetch(`${credentials.url}/oauth/register`, {
365
- method: "GET",
366
- headers: {
367
- "X-CSRF-Token": "Fetch",
368
- Authorization: `Basic ${Buffer.from(`${credentials.clientid}:${credentials.clientsecret}`).toString("base64")}`,
369
- Accept: "application/json",
370
- },
371
- });
372
- if (!csrfResponse.ok) {
373
- throw new Error(`CSRF fetch failed: ${csrfResponse.status}`);
374
- }
375
- // Step 2: Extract CSRF token and session cookie
376
- const setCookieHeader = csrfResponse.headers.get("set-cookie") || "";
377
- const csrfToken = extractCsrfFromCookie(setCookieHeader);
378
- if (!csrfToken) {
379
- throw new Error("Could not extract CSRF token from XSUAA response");
380
- }
381
- // Step 3: Make actual registration POST with CSRF token
382
- const registrationResponse = await fetch(`${credentials.url}/oauth/register`, {
383
- method: "POST",
384
- headers: {
385
- "Content-Type": "application/json",
386
- "X-CSRF-Token": csrfToken,
387
- Cookie: setCookieHeader,
388
- Authorization: `Basic ${Buffer.from(`${credentials.clientid}:${credentials.clientsecret}`).toString("base64")}`,
389
- Accept: "application/json",
390
- },
391
- body: JSON.stringify(req.body),
392
- });
393
- const xsuaaData = await registrationResponse.json();
394
- // Add missing required fields that MCP client expects
395
- const enhancedResponse = {
396
- ...xsuaaData, // Keep all XSUAA fields
397
- client_id: credentials.clientid, // Add our CAP app's client ID
398
- redirect_uris: req.body.redirect_uris || [
399
- `${baseUrl}/oauth/callback`,
400
- ],
401
- };
402
- logger_1.LOGGER.debug("[AUTH] Register POST response", enhancedResponse);
403
- res.status(registrationResponse.status).json(enhancedResponse);
404
- }
405
- catch (error) {
406
- logger_1.LOGGER.error("OAuth registration error:", error);
407
- res.status(500).json({
408
- error: "server_error",
409
- error_description: error instanceof Error ? error.message : "Unknown error",
410
- });
411
- }
360
+ // try {
361
+ // Step 1: Fetch CSRF token from XSUAA
362
+ // const csrfResponse = await fetch(`${credentials.url}/oauth/register`, {
363
+ // method: "GET",
364
+ // headers: {
365
+ // "X-CSRF-Token": "Fetch",
366
+ // Authorization: `Basic ${Buffer.from(`${credentials.clientid}:${credentials.clientsecret}`).toString("base64")}`,
367
+ // Accept: "application/json",
368
+ // },
369
+ // });
370
+ // if (!csrfResponse.ok) {
371
+ // throw new Error(`CSRF fetch failed: ${csrfResponse.status}`);
372
+ // }
373
+ // Step 2: Extract CSRF token and session cookie
374
+ // const setCookieHeader = csrfResponse.headers.get("set-cookie") || "";
375
+ // const csrfToken = extractCsrfFromCookie(setCookieHeader);
376
+ // if (!csrfToken) {
377
+ // throw new Error("Could not extract CSRF token from XSUAA response");
378
+ // }
379
+ // Step 3: Make actual registration POST with CSRF token
380
+ // const registrationResponse = await fetch(
381
+ // `${credentials.url}/oauth/register`,
382
+ // {
383
+ // method: "POST",
384
+ // headers: {
385
+ // "Content-Type": "application/json",
386
+ // "X-CSRF-Token": csrfToken,
387
+ // Cookie: setCookieHeader,
388
+ // Authorization: `Basic ${Buffer.from(`${credentials.clientid}:${credentials.clientsecret}`).toString("base64")}`,
389
+ // Accept: "application/json",
390
+ // },
391
+ // body: JSON.stringify(req.body),
392
+ // },
393
+ // );
394
+ // const xsuaaData = await registrationResponse.json();
395
+ // Add missing required fields that MCP client expects
396
+ // const enhancedResponse = {
397
+ // ...xsuaaData, // Keep all XSUAA fields
398
+ // client_id: credentials.clientid, // Add our CAP app's client ID
399
+ // redirect_uris: req.body.redirect_uris || [
400
+ // `${baseUrl}/oauth/callback`,
401
+ // ],
402
+ // };
403
+ // LOGGER.debug("[AUTH] Register POST response", enhancedResponse);
404
+ // res.status(registrationResponse.status).json(enhancedResponse);
405
+ // } catch (error) {
406
+ // LOGGER.error("OAuth registration error:", error);
407
+ // res.status(500).json({
408
+ // error: "server_error",
409
+ // error_description:
410
+ // error instanceof Error ? error.message : "Unknown error",
411
+ // });
412
+ // }
412
413
  });
413
414
  logger_1.LOGGER.debug("OAuth endpoints registered for XSUAA integration");
414
415
  }
package/lib/mcp/utils.js CHANGED
@@ -195,14 +195,14 @@ function buildDeepInsertZodType(targetEntityName) {
195
195
  async function handleMcpSessionRequest(req, res, sessions) {
196
196
  const sessionIdHeader = req.headers[constants_1.MCP_SESSION_HEADER];
197
197
  if (!sessionIdHeader || !sessions.has(sessionIdHeader)) {
198
- res.status(400).send("Invalid or missing session ID");
198
+ res.status(404).json({
199
+ jsonrpc: "2.0",
200
+ error: { code: -32001, message: "Session not found" },
201
+ id: null,
202
+ });
199
203
  return;
200
204
  }
201
205
  const session = sessions.get(sessionIdHeader);
202
- if (!session) {
203
- res.status(400).send("Invalid session");
204
- return;
205
- }
206
206
  await session.transport.handleRequest(req, res);
207
207
  }
208
208
  /**
package/lib/mcp.js CHANGED
@@ -104,13 +104,13 @@ class McpPlugin {
104
104
  const sessionIdHeader = req.headers[constants_1.MCP_SESSION_HEADER];
105
105
  const sessions = this.sessionManager.getSessions();
106
106
  if (!sessionIdHeader || !sessions.has(sessionIdHeader)) {
107
- return res.status(400).json({
107
+ return res.status(404).json({
108
108
  jsonrpc: "2.0",
109
109
  error: {
110
- code: -32000,
111
- message: "Bad Request: No valid sessions ID provided",
112
- id: null,
110
+ code: -32001,
111
+ message: "Session not found",
113
112
  },
113
+ id: null,
114
114
  });
115
115
  }
116
116
  const session = sessions.get(sessionIdHeader);
@@ -139,13 +139,13 @@ class McpPlugin {
139
139
  : this.sessionManager.getSession(sessionIdHeader);
140
140
  if (!session) {
141
141
  logger_1.LOGGER.error("Invalid session ID", sessionIdHeader);
142
- res.status(400).json({
142
+ res.status(404).json({
143
143
  jsonrpc: "2.0",
144
144
  error: {
145
- code: -32000,
146
- message: "Bad Request: No valid sessions ID provided",
147
- id: null,
145
+ code: -32001,
146
+ message: "Session not found",
148
147
  },
148
+ id: null,
149
149
  });
150
150
  return;
151
151
  }
@@ -163,8 +163,8 @@ class McpPlugin {
163
163
  error: {
164
164
  code: -32603,
165
165
  message: "Internal Error: Transport failed",
166
- id: null,
167
166
  },
167
+ id: null,
168
168
  });
169
169
  return;
170
170
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gavdi/cap-mcp",
3
- "version": "1.5.1",
3
+ "version": "1.6.0",
4
4
  "description": "MCP Plugin for CAP",
5
5
  "keywords": [
6
6
  "MCP",
@@ -44,7 +44,7 @@
44
44
  },
45
45
  "peerDependencies": {
46
46
  "@sap/cds": ">=9",
47
- "express": "^4"
47
+ "express": "^5"
48
48
  },
49
49
  "dependencies": {
50
50
  "@modelcontextprotocol/sdk": "^1.23.0",
@@ -60,7 +60,7 @@
60
60
  "@types/cors": "^2.8.19",
61
61
  "@types/express": "^5.0.3",
62
62
  "@types/jest": "^30.0.0",
63
- "@types/node": "^24.0.3",
63
+ "@types/node": "^25.3.0",
64
64
  "@types/sinon": "^17.0.4",
65
65
  "@types/supertest": "^6.0.2",
66
66
  "docsify-cli": "^4.4.4",