@mcp-abap-adt/connection 1.10.1 → 2.0.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/CHANGELOG.md +758 -0
- package/README.md +42 -11
- package/dist/__tests__/helpers/session.d.ts +15 -0
- package/dist/__tests__/helpers/session.d.ts.map +1 -0
- package/dist/__tests__/helpers/session.js +19 -0
- package/dist/auth/ntlm.d.ts +15 -0
- package/dist/auth/ntlm.d.ts.map +1 -1
- package/dist/auth/ntlm.js +38 -0
- package/dist/connection/AbstractAbapConnection.d.ts +163 -11
- package/dist/connection/AbstractAbapConnection.d.ts.map +1 -1
- package/dist/connection/AbstractAbapConnection.js +351 -14
- package/dist/connection/BaseAbapConnection.d.ts +5 -1
- package/dist/connection/BaseAbapConnection.d.ts.map +1 -1
- package/dist/connection/BaseAbapConnection.js +11 -1
- package/dist/connection/CertificateAbapConnection.d.ts +5 -1
- package/dist/connection/CertificateAbapConnection.d.ts.map +1 -1
- package/dist/connection/CertificateAbapConnection.js +11 -1
- package/dist/connection/JwtAbapConnection.d.ts +3 -2
- package/dist/connection/JwtAbapConnection.d.ts.map +1 -1
- package/dist/connection/JwtAbapConnection.js +19 -8
- package/dist/connection/KerberosAbapConnection.d.ts +5 -1
- package/dist/connection/KerberosAbapConnection.d.ts.map +1 -1
- package/dist/connection/KerberosAbapConnection.js +54 -3
- package/dist/connection/SamlAbapConnection.d.ts +5 -1
- package/dist/connection/SamlAbapConnection.d.ts.map +1 -1
- package/dist/connection/SamlAbapConnection.js +11 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/session/SessionLifecycle.d.ts +131 -0
- package/dist/session/SessionLifecycle.d.ts.map +1 -0
- package/dist/session/SessionLifecycle.js +301 -0
- package/docs/INDEX.md +106 -0
- package/docs/INSTALLATION.md +304 -0
- package/docs/JWT_AUTH_TOOLS.md +142 -0
- package/docs/MIGRATION-2.0.md +125 -0
- package/docs/SCOPE.md +44 -0
- package/docs/STATEFUL_SESSION_GUIDE.md +121 -0
- package/docs/USAGE.md +749 -0
- package/examples/README.md +112 -0
- package/examples/basic-connection.js +55 -0
- package/examples/jwt-with-token-refresh.js +87 -0
- package/examples/saml-connection.js +52 -0
- package/examples/websocket-transport.js +87 -0
- package/package.json +11 -4
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Connection Examples
|
|
2
|
+
|
|
3
|
+
This directory contains example code demonstrating how to use the `@mcp-abap-adt/connection` package.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Install dependencies
|
|
9
|
+
cd packages/connection
|
|
10
|
+
npm install
|
|
11
|
+
|
|
12
|
+
# Build the package
|
|
13
|
+
npm run build
|
|
14
|
+
|
|
15
|
+
# Set up environment variables
|
|
16
|
+
cp .env.example .env
|
|
17
|
+
# Edit .env with your SAP credentials
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Available Examples
|
|
21
|
+
|
|
22
|
+
### basic-connection.js
|
|
23
|
+
|
|
24
|
+
Simple example showing how to connect to SAP and make an ADT request.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
node examples/basic-connection.js
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**What it demonstrates:**
|
|
31
|
+
- Creating connection with factory function
|
|
32
|
+
- Connecting to SAP system
|
|
33
|
+
- Making GET request to ADT endpoint
|
|
34
|
+
- Basic error handling
|
|
35
|
+
|
|
36
|
+
### jwt-with-token-refresh.js
|
|
37
|
+
|
|
38
|
+
Shows how to use `ITokenRefresher` for automatic token refresh.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
node examples/jwt-with-token-refresh.js
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**What it demonstrates:**
|
|
45
|
+
- Creating connection with token refresher injection
|
|
46
|
+
- Automatic token refresh on 401/403 errors
|
|
47
|
+
- Retry logic with refreshed token
|
|
48
|
+
|
|
49
|
+
### saml-connection.js
|
|
50
|
+
|
|
51
|
+
Shows how to use cookie-based SAML connection.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
node examples/saml-connection.js
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**What it demonstrates:**
|
|
58
|
+
- Creating SAML connection via factory (`authType: "saml"`)
|
|
59
|
+
- Using `sessionCookies` from environment
|
|
60
|
+
- Fetching CSRF token and making ADT request with cookie auth
|
|
61
|
+
|
|
62
|
+
### websocket-transport.js
|
|
63
|
+
|
|
64
|
+
Shows how to use `GenericWebSocketTransport` with injected WS factory.
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
node examples/websocket-transport.js
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**What it demonstrates:**
|
|
71
|
+
- Creating `GenericWebSocketTransport`
|
|
72
|
+
- Registering `onOpen`, `onMessage`, `onError`, `onClose` handlers
|
|
73
|
+
- Sending `IWebSocketMessageEnvelope` payload
|
|
74
|
+
- Connecting/disconnecting transport lifecycle
|
|
75
|
+
|
|
76
|
+
## Configuration
|
|
77
|
+
|
|
78
|
+
### Using .env file
|
|
79
|
+
|
|
80
|
+
Create `.env` in project root:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# Basic Auth
|
|
84
|
+
SAP_URL=https://your-sap-system.com
|
|
85
|
+
SAP_AUTH_TYPE=basic
|
|
86
|
+
SAP_USERNAME=your_username
|
|
87
|
+
SAP_PASSWORD=your_password
|
|
88
|
+
SAP_CLIENT=100
|
|
89
|
+
|
|
90
|
+
# JWT Auth (Cloud/BTP)
|
|
91
|
+
SAP_AUTH_TYPE=jwt
|
|
92
|
+
SAP_JWT_TOKEN=eyJhbGciOiJSUzI1NiIs...
|
|
93
|
+
# Note: Token refresh is handled by @mcp-abap-adt/auth-broker package
|
|
94
|
+
|
|
95
|
+
# SAML Auth (Cookie-based)
|
|
96
|
+
SAP_AUTH_TYPE=saml
|
|
97
|
+
SAP_SESSION_COOKIES=sap-usercontext=sap-client=100; sap-contextid=...
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Using Environment Variables
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
export SAP_URL=https://your-sap-system.com
|
|
104
|
+
export SAP_USERNAME=your_username
|
|
105
|
+
export SAP_PASSWORD=your_password
|
|
106
|
+
node examples/basic-connection.js
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## See Also
|
|
110
|
+
|
|
111
|
+
- [Connection README](../README.md) - Main package documentation
|
|
112
|
+
- [Session Lifecycle](../docs/USAGE.md#session-lifecycle) - connect/disconnect, lock windows, a lost session
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Basic Connection Example
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates simple connection to SAP system and making ADT request
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { createAbapConnection } = require('@mcp-abap-adt/connection');
|
|
8
|
+
|
|
9
|
+
async function main() {
|
|
10
|
+
// Configuration from environment or hardcoded
|
|
11
|
+
const config = {
|
|
12
|
+
url: process.env.SAP_URL || 'https://your-sap-server.com',
|
|
13
|
+
authType: process.env.SAP_AUTH_TYPE || 'basic',
|
|
14
|
+
username: process.env.SAP_USERNAME,
|
|
15
|
+
password: process.env.SAP_PASSWORD,
|
|
16
|
+
client: process.env.SAP_CLIENT || '100',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
console.log('Creating connection to', config.url);
|
|
20
|
+
|
|
21
|
+
// Create connection (factory auto-detects cloud vs on-premise)
|
|
22
|
+
const connection = createAbapConnection(config, console);
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
// Connect and get CSRF token
|
|
26
|
+
console.log('Connecting to SAP system...');
|
|
27
|
+
await connection.connect();
|
|
28
|
+
console.log('✓ Connected successfully');
|
|
29
|
+
|
|
30
|
+
// Make a simple ADT request
|
|
31
|
+
console.log('\nFetching repository structure...');
|
|
32
|
+
const response = await connection.makeAdtRequest({
|
|
33
|
+
method: 'GET',
|
|
34
|
+
url: '/sap/bc/adt/repository/nodestructure',
|
|
35
|
+
params: {
|
|
36
|
+
parent_name: 'DEVC/K',
|
|
37
|
+
parent_type: 'DEVC/K',
|
|
38
|
+
withShortDescriptions: 'true',
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
console.log('✓ Request successful');
|
|
43
|
+
console.log('Response status:', response.status);
|
|
44
|
+
console.log('Data length:', response.data.length);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error('✗ Error:', error.message);
|
|
47
|
+
if (error.response) {
|
|
48
|
+
console.error(' Status:', error.response.status);
|
|
49
|
+
console.error(' Data:', error.response.data);
|
|
50
|
+
}
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
main();
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example: JWT Connection with Automatic Token Refresh
|
|
3
|
+
*
|
|
4
|
+
* This example demonstrates how to create a JwtAbapConnection with
|
|
5
|
+
* automatic token refresh using ITokenRefresher from auth-broker.
|
|
6
|
+
*
|
|
7
|
+
* When 401/403 errors occur, the connection automatically:
|
|
8
|
+
* 1. Calls tokenRefresher.refreshToken() to get a new token
|
|
9
|
+
* 2. Updates internal token state
|
|
10
|
+
* 3. Retries the failed request with the new token
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { JwtAbapConnection } = require('@mcp-abap-adt/connection');
|
|
14
|
+
// const { AuthBroker } = require('@mcp-abap-adt/auth-broker');
|
|
15
|
+
|
|
16
|
+
// Simple logger
|
|
17
|
+
const logger = {
|
|
18
|
+
info: (msg, meta) => console.log('[INFO]', msg, meta || ''),
|
|
19
|
+
error: (msg, meta) => console.error('[ERROR]', msg, meta || ''),
|
|
20
|
+
warn: (msg, meta) => console.warn('[WARN]', msg, meta || ''),
|
|
21
|
+
debug: (msg, meta) => console.debug('[DEBUG]', msg, meta || ''),
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
async function main() {
|
|
25
|
+
// Option 1: Using AuthBroker (recommended for production)
|
|
26
|
+
// const broker = new AuthBroker({
|
|
27
|
+
// sessionStore: mySessionStore,
|
|
28
|
+
// serviceKeyStore: myServiceKeyStore,
|
|
29
|
+
// tokenProvider: myTokenProvider,
|
|
30
|
+
// });
|
|
31
|
+
// const tokenRefresher = broker.createTokenRefresher('TRIAL');
|
|
32
|
+
// const initialToken = await tokenRefresher.getToken();
|
|
33
|
+
|
|
34
|
+
// Option 2: Manual ITokenRefresher implementation (for testing/custom scenarios)
|
|
35
|
+
const tokenRefresher = {
|
|
36
|
+
getToken: async () => {
|
|
37
|
+
console.log('getToken called - returning cached or refreshed token');
|
|
38
|
+
return process.env.SAP_JWT_TOKEN || 'your-jwt-token';
|
|
39
|
+
},
|
|
40
|
+
refreshToken: async () => {
|
|
41
|
+
console.log('refreshToken called - forcing token refresh');
|
|
42
|
+
// In real implementation: call OAuth2 token endpoint
|
|
43
|
+
// Save new token to session store
|
|
44
|
+
// Return new token
|
|
45
|
+
return 'newly-refreshed-jwt-token';
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Get initial token
|
|
50
|
+
const initialToken = await tokenRefresher.getToken();
|
|
51
|
+
|
|
52
|
+
// JWT configuration
|
|
53
|
+
const config = {
|
|
54
|
+
url: process.env.SAP_URL || 'https://your-instance.abap.cloud.sap',
|
|
55
|
+
authType: 'jwt',
|
|
56
|
+
jwtToken: initialToken,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Create connection with token refresher
|
|
60
|
+
// 4th parameter is the ITokenRefresher
|
|
61
|
+
const connection = new JwtAbapConnection(
|
|
62
|
+
config,
|
|
63
|
+
logger,
|
|
64
|
+
undefined,
|
|
65
|
+
tokenRefresher,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
await connection.connect();
|
|
70
|
+
|
|
71
|
+
// This request will automatically refresh token if 401/403 occurs. Note
|
|
72
|
+
// that a refresh replaces the SAP session: with a lock window open the
|
|
73
|
+
// request would fail with ADT_SESSION_REPLACED rather than continue on a
|
|
74
|
+
// session your lock is not in.
|
|
75
|
+
const response = await connection.makeAdtRequest({
|
|
76
|
+
method: 'GET',
|
|
77
|
+
url: '/sap/bc/adt/discovery',
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
console.log('Request succeeded:', response.status);
|
|
81
|
+
console.log('Discovery data available');
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error('Request failed:', error.message);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SAML Connection Example
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates using cookie-based SAML authentication.
|
|
5
|
+
* Session cookies are expected from prior login flow.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { createAbapConnection } = require('@mcp-abap-adt/connection');
|
|
9
|
+
|
|
10
|
+
async function main() {
|
|
11
|
+
const config = {
|
|
12
|
+
url: process.env.SAP_URL || 'https://your-sap-server.com',
|
|
13
|
+
authType: 'saml',
|
|
14
|
+
sessionCookies: process.env.SAP_SESSION_COOKIES,
|
|
15
|
+
client: process.env.SAP_CLIENT || '100',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
if (!config.sessionCookies) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
'SAP_SESSION_COOKIES is required for SAML example (full Cookie header value)',
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
console.log('Creating SAML connection to', config.url);
|
|
25
|
+
const connection = createAbapConnection(config, console);
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
console.log('Connecting using session cookies...');
|
|
29
|
+
await connection.connect();
|
|
30
|
+
console.log('✓ Connected successfully');
|
|
31
|
+
|
|
32
|
+
const response = await connection.makeAdtRequest({
|
|
33
|
+
method: 'GET',
|
|
34
|
+
url: '/sap/bc/adt/discovery',
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
console.log('✓ Request successful');
|
|
38
|
+
console.log('Response status:', response.status);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error('✗ Error:', error.message || String(error));
|
|
41
|
+
if (error.response) {
|
|
42
|
+
console.error(' Status:', error.response.status);
|
|
43
|
+
console.error(' Data:', error.response.data);
|
|
44
|
+
}
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
main().catch((error) => {
|
|
50
|
+
console.error('Fatal:', error.message || String(error));
|
|
51
|
+
process.exit(1);
|
|
52
|
+
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic WebSocket Transport Example
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates how to plug a concrete WebSocket implementation
|
|
5
|
+
* into GenericWebSocketTransport through the factory interface.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { GenericWebSocketTransport } = require('@mcp-abap-adt/connection');
|
|
9
|
+
|
|
10
|
+
class MockWebSocket {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.readyState = 0;
|
|
13
|
+
this.onopen = null;
|
|
14
|
+
this.onmessage = null;
|
|
15
|
+
this.onerror = null;
|
|
16
|
+
this.onclose = null;
|
|
17
|
+
|
|
18
|
+
setTimeout(() => {
|
|
19
|
+
this.readyState = 1;
|
|
20
|
+
if (this.onopen) this.onopen({});
|
|
21
|
+
}, 10);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
send(payload) {
|
|
25
|
+
// Echo payload back as "event" for demo purposes.
|
|
26
|
+
if (this.onmessage) {
|
|
27
|
+
this.onmessage({ data: payload });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
close(code, reason) {
|
|
32
|
+
this.readyState = 3;
|
|
33
|
+
if (this.onclose) {
|
|
34
|
+
this.onclose({
|
|
35
|
+
code: code || 1000,
|
|
36
|
+
reason: reason || 'normal',
|
|
37
|
+
wasClean: true,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const mockFactory = {
|
|
44
|
+
create(_url, _protocols, _options) {
|
|
45
|
+
return new MockWebSocket();
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
async function main() {
|
|
50
|
+
const transport = new GenericWebSocketTransport(mockFactory);
|
|
51
|
+
|
|
52
|
+
transport.onOpen(() => {
|
|
53
|
+
console.log('✓ WS open');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
transport.onMessage((message) => {
|
|
57
|
+
console.log('message:', message);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
transport.onError((error) => {
|
|
61
|
+
console.error('error:', error.message);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
transport.onClose((info) => {
|
|
65
|
+
console.log('closed:', info);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
await transport.connect('wss://example.invalid/realtime', {
|
|
69
|
+
connectTimeoutMs: 5000,
|
|
70
|
+
heartbeatIntervalMs: 30000,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
await transport.send({
|
|
74
|
+
kind: 'request',
|
|
75
|
+
correlationId: 'demo-1',
|
|
76
|
+
operation: 'debugger.listen',
|
|
77
|
+
payload: { timeoutSeconds: 30 },
|
|
78
|
+
timestamp: Date.now(),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
await transport.disconnect(1000, 'demo finished');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
main().catch((error) => {
|
|
85
|
+
console.error('Fatal:', error.message || String(error));
|
|
86
|
+
process.exit(1);
|
|
87
|
+
});
|
package/package.json
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-abap-adt/connection",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "ABAP connection layer for MCP ABAP ADT server",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"files": [
|
|
8
8
|
"dist",
|
|
9
9
|
"bin",
|
|
10
|
+
"docs",
|
|
11
|
+
"examples",
|
|
10
12
|
"README.md",
|
|
13
|
+
"CHANGELOG.md",
|
|
11
14
|
"LICENSE"
|
|
12
15
|
],
|
|
13
16
|
"bin": {
|
|
@@ -39,14 +42,15 @@
|
|
|
39
42
|
"format": "npx biome format --write src",
|
|
40
43
|
"build": "npm run --silent clean && npx biome check src --diagnostic-level=error && npx tsc -p tsconfig.json",
|
|
41
44
|
"build:fast": "npx tsc -p tsconfig.json",
|
|
42
|
-
"test": "jest",
|
|
43
|
-
"prepublishOnly": "npm run build"
|
|
45
|
+
"test": "npm run --silent check:docs && jest",
|
|
46
|
+
"prepublishOnly": "npm run build && npm run check:docs",
|
|
47
|
+
"check:docs": "node scripts/check-docs.mjs"
|
|
44
48
|
},
|
|
45
49
|
"engines": {
|
|
46
50
|
"node": ">=18.0.0"
|
|
47
51
|
},
|
|
48
52
|
"dependencies": {
|
|
49
|
-
"@mcp-abap-adt/interfaces": "^
|
|
53
|
+
"@mcp-abap-adt/interfaces": "^11.5.0",
|
|
50
54
|
"axios": "^1.16.0",
|
|
51
55
|
"commander": "^14.0.3",
|
|
52
56
|
"express": "^5.1.0",
|
|
@@ -65,5 +69,8 @@
|
|
|
65
69
|
"jest-util": "^30.2.0",
|
|
66
70
|
"ts-jest": "^29.2.5",
|
|
67
71
|
"typescript": "^5.9.2"
|
|
72
|
+
},
|
|
73
|
+
"overrides": {
|
|
74
|
+
"brace-expansion": "^5.0.8"
|
|
68
75
|
}
|
|
69
76
|
}
|