arc-1 0.4.4 → 0.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 +6 -5
- package/dist/adt/client.d.ts +11 -1
- package/dist/adt/client.d.ts.map +1 -1
- package/dist/adt/client.js +51 -1
- package/dist/adt/client.js.map +1 -1
- package/dist/adt/codeintel.js +1 -1
- package/dist/adt/codeintel.js.map +1 -1
- package/dist/adt/crud.d.ts.map +1 -1
- package/dist/adt/crud.js +1 -7
- package/dist/adt/crud.js.map +1 -1
- package/dist/adt/features.d.ts +29 -1
- package/dist/adt/features.d.ts.map +1 -1
- package/dist/adt/features.js +114 -2
- package/dist/adt/features.js.map +1 -1
- package/dist/adt/http.d.ts +24 -1
- package/dist/adt/http.d.ts.map +1 -1
- package/dist/adt/http.js +87 -28
- package/dist/adt/http.js.map +1 -1
- package/dist/adt/oauth.d.ts +19 -2
- package/dist/adt/oauth.d.ts.map +1 -1
- package/dist/adt/oauth.js +78 -28
- package/dist/adt/oauth.js.map +1 -1
- package/dist/adt/safety.d.ts +15 -7
- package/dist/adt/safety.d.ts.map +1 -1
- package/dist/adt/safety.js +49 -45
- package/dist/adt/safety.js.map +1 -1
- package/dist/adt/types.d.ts +33 -0
- package/dist/adt/types.d.ts.map +1 -1
- package/dist/adt/xml-parser.d.ts +10 -1
- package/dist/adt/xml-parser.d.ts.map +1 -1
- package/dist/adt/xml-parser.js +47 -0
- package/dist/adt/xml-parser.js.map +1 -1
- package/dist/aff/schemas/bdef-v1.json +62 -0
- package/dist/aff/schemas/clas-v1.json +276 -0
- package/dist/aff/schemas/ddls-v1.json +144 -0
- package/dist/aff/schemas/intf-v1.json +243 -0
- package/dist/aff/schemas/prog-v1.json +133 -0
- package/dist/aff/schemas/srvb-v1.json +115 -0
- package/dist/aff/schemas/srvd-v1.json +108 -0
- package/dist/aff/validator.d.ts +14 -0
- package/dist/aff/validator.d.ts.map +1 -0
- package/dist/aff/validator.js +83 -0
- package/dist/aff/validator.js.map +1 -0
- package/dist/handlers/hyperfocused.d.ts +1 -0
- package/dist/handlers/hyperfocused.d.ts.map +1 -1
- package/dist/handlers/hyperfocused.js +7 -6
- package/dist/handlers/hyperfocused.js.map +1 -1
- package/dist/handlers/intent.d.ts +17 -1
- package/dist/handlers/intent.d.ts.map +1 -1
- package/dist/handlers/intent.js +369 -27
- package/dist/handlers/intent.js.map +1 -1
- package/dist/handlers/schemas.d.ts +296 -0
- package/dist/handlers/schemas.d.ts.map +1 -0
- package/dist/handlers/schemas.js +250 -0
- package/dist/handlers/schemas.js.map +1 -0
- package/dist/handlers/tools.d.ts +1 -1
- package/dist/handlers/tools.d.ts.map +1 -1
- package/dist/handlers/tools.js +111 -42
- package/dist/handlers/tools.js.map +1 -1
- package/dist/handlers/zod-errors.d.ts +20 -0
- package/dist/handlers/zod-errors.d.ts.map +1 -0
- package/dist/handlers/zod-errors.js +43 -0
- package/dist/handlers/zod-errors.js.map +1 -0
- package/dist/server/config.d.ts +26 -0
- package/dist/server/config.d.ts.map +1 -1
- package/dist/server/config.js +156 -7
- package/dist/server/config.js.map +1 -1
- package/dist/server/http.d.ts +8 -0
- package/dist/server/http.d.ts.map +1 -1
- package/dist/server/http.js +134 -71
- package/dist/server/http.js.map +1 -1
- package/dist/server/server.d.ts +13 -2
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +82 -9
- package/dist/server/server.js.map +1 -1
- package/dist/server/types.d.ts +8 -1
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js +2 -2
- package/dist/server/types.js.map +1 -1
- package/dist/server/xsuaa.d.ts +11 -1
- package/dist/server/xsuaa.d.ts.map +1 -1
- package/dist/server/xsuaa.js +127 -9
- package/dist/server/xsuaa.js.map +1 -1
- package/package.json +4 -3
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format Zod validation errors into LLM-friendly messages.
|
|
3
|
+
*
|
|
4
|
+
* Produces structured error text with field paths and expected values,
|
|
5
|
+
* consistent with the existing errorResult() pattern in intent.ts.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Format a ZodError into an LLM-friendly multi-line string.
|
|
9
|
+
*
|
|
10
|
+
* Example output:
|
|
11
|
+
* Invalid arguments for SAPRead:
|
|
12
|
+
* - "type": expected one of: PROG, CLAS, INTF, ..., got "PROGG"
|
|
13
|
+
* - "maxRows": expected number, got string
|
|
14
|
+
*
|
|
15
|
+
* Hint: Check the tool schema for valid parameter types and values.
|
|
16
|
+
*/
|
|
17
|
+
// biome-ignore lint/suspicious/noExplicitAny: accepts any Zod error shape
|
|
18
|
+
export function formatZodError(error, toolName) {
|
|
19
|
+
const issues = error.issues.map((issue) => {
|
|
20
|
+
const path = issue.path.length > 0 ? `"${issue.path.join('.')}"` : 'input';
|
|
21
|
+
// Zod v4 uses 'code' to distinguish issue types
|
|
22
|
+
if (issue.code === 'invalid_value' && issue.values) {
|
|
23
|
+
return `${path}: expected one of: ${issue.values.join(', ')}`;
|
|
24
|
+
}
|
|
25
|
+
if (issue.code === 'invalid_type') {
|
|
26
|
+
if (issue.input === undefined) {
|
|
27
|
+
return `${path}: required (expected ${issue.expected ?? 'value'})`;
|
|
28
|
+
}
|
|
29
|
+
return `${path}: expected ${issue.expected ?? 'value'}, got ${typeof issue.input}`;
|
|
30
|
+
}
|
|
31
|
+
if (issue.code === 'unrecognized_keys' && issue.keys) {
|
|
32
|
+
return `Unknown parameter(s): ${issue.keys.join(', ')}`;
|
|
33
|
+
}
|
|
34
|
+
return `${path}: ${issue.message}`;
|
|
35
|
+
});
|
|
36
|
+
return [
|
|
37
|
+
`Invalid arguments for ${toolName}:`,
|
|
38
|
+
...issues.map((i) => ` - ${i}`),
|
|
39
|
+
'',
|
|
40
|
+
'Hint: Check the tool schema for valid parameter types and values.',
|
|
41
|
+
].join('\n');
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=zod-errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zod-errors.js","sourceRoot":"","sources":["../../src/handlers/zod-errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;GASG;AACH,0EAA0E;AAC1E,MAAM,UAAU,cAAc,CAAC,KAAqC,EAAE,QAAgB;IACpF,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACxC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;QAE3E,gDAAgD;QAChD,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACnD,OAAO,GAAG,IAAI,sBAAsB,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAChE,CAAC;QAED,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YAClC,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBAC9B,OAAO,GAAG,IAAI,wBAAwB,KAAK,CAAC,QAAQ,IAAI,OAAO,GAAG,CAAC;YACrE,CAAC;YACD,OAAO,GAAG,IAAI,cAAc,KAAK,CAAC,QAAQ,IAAI,OAAO,SAAS,OAAO,KAAK,CAAC,KAAK,EAAE,CAAC;QACrF,CAAC;QAED,IAAI,KAAK,CAAC,IAAI,KAAK,mBAAmB,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YACrD,OAAO,yBAAyB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1D,CAAC;QAED,OAAO,GAAG,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,yBAAyB,QAAQ,GAAG;QACpC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;QAChC,EAAE;QACF,mEAAmE;KACpE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
|
package/dist/server/config.d.ts
CHANGED
|
@@ -8,6 +8,27 @@
|
|
|
8
8
|
* for drop-in compatibility with existing deployments and documentation.
|
|
9
9
|
*/
|
|
10
10
|
import type { ServerConfig } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Parse API keys string into structured array.
|
|
13
|
+
* Format: "key1:profile1,key2:profile2"
|
|
14
|
+
* Each entry maps an API key to a named profile.
|
|
15
|
+
*/
|
|
16
|
+
export declare function parseApiKeys(raw: string): Array<{
|
|
17
|
+
key: string;
|
|
18
|
+
profile: string;
|
|
19
|
+
}>;
|
|
20
|
+
/**
|
|
21
|
+
* Maps profile names to the scopes they grant.
|
|
22
|
+
* Used when API keys are assigned to profiles — the key inherits these scopes.
|
|
23
|
+
* Kept in sync with PROFILES: each profile's safety flags determine its scopes.
|
|
24
|
+
*/
|
|
25
|
+
export declare const PROFILE_SCOPES: Record<string, string[]>;
|
|
26
|
+
/**
|
|
27
|
+
* Named profiles — convenience presets for common safety configurations.
|
|
28
|
+
* Each profile sets a combination of safety flags. Individual CLI flags
|
|
29
|
+
* applied after the profile can override any profile default.
|
|
30
|
+
*/
|
|
31
|
+
export declare const PROFILES: Record<string, Partial<ServerConfig>>;
|
|
11
32
|
/**
|
|
12
33
|
* Parse CLI arguments and environment variables into a ServerConfig.
|
|
13
34
|
*
|
|
@@ -16,4 +37,9 @@ import type { ServerConfig } from './types.js';
|
|
|
16
37
|
* Commander is used for the full CLI (cli.ts), not the server startup.
|
|
17
38
|
*/
|
|
18
39
|
export declare function parseArgs(args: string[]): ServerConfig;
|
|
40
|
+
/**
|
|
41
|
+
* Validate configuration for internally consistent auth settings.
|
|
42
|
+
* Fails fast at startup for invalid or dangerous config combinations.
|
|
43
|
+
*/
|
|
44
|
+
export declare function validateConfig(config: ServerConfig): void;
|
|
19
45
|
//# sourceMappingURL=config.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/server/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAiB,YAAY,EAAiB,MAAM,YAAY,CAAC;AAG7E;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,YAAY,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/server/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAiB,YAAY,EAAiB,MAAM,YAAY,CAAC;AAG7E;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CA8BjF;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAOnD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC,CAwC1D,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,YAAY,CAiKtD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAkBzD"}
|
package/dist/server/config.js
CHANGED
|
@@ -8,6 +8,98 @@
|
|
|
8
8
|
* for drop-in compatibility with existing deployments and documentation.
|
|
9
9
|
*/
|
|
10
10
|
import { DEFAULT_CONFIG } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Parse API keys string into structured array.
|
|
13
|
+
* Format: "key1:profile1,key2:profile2"
|
|
14
|
+
* Each entry maps an API key to a named profile.
|
|
15
|
+
*/
|
|
16
|
+
export function parseApiKeys(raw) {
|
|
17
|
+
const entries = [];
|
|
18
|
+
for (const pair of raw.split(',')) {
|
|
19
|
+
const trimmed = pair.trim();
|
|
20
|
+
if (!trimmed)
|
|
21
|
+
continue;
|
|
22
|
+
// Use LAST colon as separator — keys may contain colons (e.g. base64)
|
|
23
|
+
// but profile names never do
|
|
24
|
+
const colonIdx = trimmed.lastIndexOf(':');
|
|
25
|
+
if (colonIdx === -1) {
|
|
26
|
+
throw new Error(`Invalid API key entry '${trimmed}': expected 'key:profile' format. ` +
|
|
27
|
+
`Valid profiles: ${Object.keys(PROFILES).join(', ')}`);
|
|
28
|
+
}
|
|
29
|
+
const key = trimmed.slice(0, colonIdx);
|
|
30
|
+
const profile = trimmed.slice(colonIdx + 1);
|
|
31
|
+
if (!key) {
|
|
32
|
+
throw new Error('Invalid API key entry: key cannot be empty');
|
|
33
|
+
}
|
|
34
|
+
if (!PROFILES[profile]) {
|
|
35
|
+
throw new Error(`Invalid profile '${profile}' in API key entry. Valid profiles: ${Object.keys(PROFILES).join(', ')}`);
|
|
36
|
+
}
|
|
37
|
+
entries.push({ key, profile });
|
|
38
|
+
}
|
|
39
|
+
if (entries.length === 0) {
|
|
40
|
+
throw new Error('ARC1_API_KEYS is set but contains no valid entries. Format: "key1:profile1,key2:profile2"');
|
|
41
|
+
}
|
|
42
|
+
return entries;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Maps profile names to the scopes they grant.
|
|
46
|
+
* Used when API keys are assigned to profiles — the key inherits these scopes.
|
|
47
|
+
* Kept in sync with PROFILES: each profile's safety flags determine its scopes.
|
|
48
|
+
*/
|
|
49
|
+
export const PROFILE_SCOPES = {
|
|
50
|
+
viewer: ['read'],
|
|
51
|
+
'viewer-data': ['read', 'data'],
|
|
52
|
+
'viewer-sql': ['read', 'data', 'sql'],
|
|
53
|
+
developer: ['read', 'write'],
|
|
54
|
+
'developer-data': ['read', 'write', 'data'],
|
|
55
|
+
'developer-sql': ['read', 'write', 'data', 'sql'],
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Named profiles — convenience presets for common safety configurations.
|
|
59
|
+
* Each profile sets a combination of safety flags. Individual CLI flags
|
|
60
|
+
* applied after the profile can override any profile default.
|
|
61
|
+
*/
|
|
62
|
+
export const PROFILES = {
|
|
63
|
+
viewer: {
|
|
64
|
+
readOnly: true,
|
|
65
|
+
blockData: true,
|
|
66
|
+
blockFreeSQL: true,
|
|
67
|
+
enableTransports: false,
|
|
68
|
+
},
|
|
69
|
+
'viewer-data': {
|
|
70
|
+
readOnly: true,
|
|
71
|
+
blockData: false,
|
|
72
|
+
blockFreeSQL: true,
|
|
73
|
+
enableTransports: false,
|
|
74
|
+
},
|
|
75
|
+
'viewer-sql': {
|
|
76
|
+
readOnly: true,
|
|
77
|
+
blockData: false,
|
|
78
|
+
blockFreeSQL: false,
|
|
79
|
+
enableTransports: false,
|
|
80
|
+
},
|
|
81
|
+
developer: {
|
|
82
|
+
readOnly: false,
|
|
83
|
+
blockData: true,
|
|
84
|
+
blockFreeSQL: true,
|
|
85
|
+
enableTransports: true,
|
|
86
|
+
allowedPackages: ['$TMP'],
|
|
87
|
+
},
|
|
88
|
+
'developer-data': {
|
|
89
|
+
readOnly: false,
|
|
90
|
+
blockData: false,
|
|
91
|
+
blockFreeSQL: true,
|
|
92
|
+
enableTransports: true,
|
|
93
|
+
allowedPackages: ['$TMP'],
|
|
94
|
+
},
|
|
95
|
+
'developer-sql': {
|
|
96
|
+
readOnly: false,
|
|
97
|
+
blockData: false,
|
|
98
|
+
blockFreeSQL: false,
|
|
99
|
+
enableTransports: true,
|
|
100
|
+
allowedPackages: ['$TMP'],
|
|
101
|
+
},
|
|
102
|
+
};
|
|
11
103
|
/**
|
|
12
104
|
* Parse CLI arguments and environment variables into a ServerConfig.
|
|
13
105
|
*
|
|
@@ -60,15 +152,42 @@ export function parseArgs(args) {
|
|
|
60
152
|
const transport = resolve('transport', 'SAP_TRANSPORT', 'stdio');
|
|
61
153
|
config.transport = (transport === 'http-streamable' ? 'http-streamable' : 'stdio');
|
|
62
154
|
config.httpAddr = resolve('http-addr', 'SAP_HTTP_ADDR', '0.0.0.0:8080');
|
|
63
|
-
// ---
|
|
64
|
-
|
|
65
|
-
|
|
155
|
+
// --- Profile (apply before individual safety flags so flags can override) ---
|
|
156
|
+
const profileName = getFlag('profile') ?? process.env.ARC1_PROFILE;
|
|
157
|
+
if (profileName) {
|
|
158
|
+
const profile = PROFILES[profileName];
|
|
159
|
+
if (!profile) {
|
|
160
|
+
throw new Error(`Unknown profile '${profileName}'. Valid profiles: ${Object.keys(PROFILES).join(', ')}`);
|
|
161
|
+
}
|
|
162
|
+
Object.assign(config, profile);
|
|
163
|
+
}
|
|
164
|
+
// --- Safety (individual flags override profile defaults) ---
|
|
165
|
+
// Only override profile defaults when the flag/env is explicitly set
|
|
166
|
+
const readOnlyExplicit = getFlag('read-only') ?? process.env.SAP_READ_ONLY;
|
|
167
|
+
if (readOnlyExplicit !== undefined)
|
|
168
|
+
config.readOnly = readOnlyExplicit === 'true' || readOnlyExplicit === '1';
|
|
169
|
+
else if (!profileName)
|
|
170
|
+
config.readOnly = false;
|
|
171
|
+
const blockFreeSQLExplicit = getFlag('block-free-sql') ?? process.env.SAP_BLOCK_FREE_SQL;
|
|
172
|
+
if (blockFreeSQLExplicit !== undefined)
|
|
173
|
+
config.blockFreeSQL = blockFreeSQLExplicit === 'true' || blockFreeSQLExplicit === '1';
|
|
174
|
+
else if (!profileName)
|
|
175
|
+
config.blockFreeSQL = false;
|
|
176
|
+
const blockDataExplicit = getFlag('block-data') ?? process.env.SAP_BLOCK_DATA;
|
|
177
|
+
if (blockDataExplicit !== undefined)
|
|
178
|
+
config.blockData = blockDataExplicit === 'true' || blockDataExplicit === '1';
|
|
179
|
+
else if (!profileName)
|
|
180
|
+
config.blockData = false;
|
|
66
181
|
config.allowedOps = resolve('allowed-ops', 'SAP_ALLOWED_OPS', '');
|
|
67
182
|
config.disallowedOps = resolve('disallowed-ops', 'SAP_DISALLOWED_OPS', '');
|
|
68
|
-
const pkgs =
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
183
|
+
const pkgs = getFlag('allowed-packages') ?? process.env.SAP_ALLOWED_PACKAGES;
|
|
184
|
+
if (pkgs)
|
|
185
|
+
config.allowedPackages = pkgs.split(',').map((p) => p.trim());
|
|
186
|
+
const enableTransportsExplicit = getFlag('enable-transports') ?? process.env.SAP_ENABLE_TRANSPORTS;
|
|
187
|
+
if (enableTransportsExplicit !== undefined)
|
|
188
|
+
config.enableTransports = enableTransportsExplicit === 'true' || enableTransportsExplicit === '1';
|
|
189
|
+
else if (!profileName)
|
|
190
|
+
config.enableTransports = false;
|
|
72
191
|
// --- Features ---
|
|
73
192
|
config.featureAbapGit = resolveFeature('feature-abapgit', 'SAP_FEATURE_ABAPGIT');
|
|
74
193
|
config.featureRap = resolveFeature('feature-rap', 'SAP_FEATURE_RAP');
|
|
@@ -81,8 +200,18 @@ export function parseArgs(args) {
|
|
|
81
200
|
config.systemType = (['btp', 'onprem'].includes(systemType) ? systemType : 'auto');
|
|
82
201
|
// --- Authentication (MCP client → ARC-1) ---
|
|
83
202
|
config.apiKey = getFlag('api-key') ?? process.env.ARC1_API_KEY;
|
|
203
|
+
// Multiple API keys with per-key profiles: "key1:viewer,key2:developer"
|
|
204
|
+
const apiKeysRaw = getFlag('api-keys') ?? process.env.ARC1_API_KEYS;
|
|
205
|
+
if (apiKeysRaw) {
|
|
206
|
+
config.apiKeys = parseApiKeys(apiKeysRaw);
|
|
207
|
+
}
|
|
84
208
|
config.oidcIssuer = getFlag('oidc-issuer') ?? process.env.SAP_OIDC_ISSUER;
|
|
85
209
|
config.oidcAudience = getFlag('oidc-audience') ?? process.env.SAP_OIDC_AUDIENCE;
|
|
210
|
+
const clockTolerance = getFlag('oidc-clock-tolerance') ?? process.env.SAP_OIDC_CLOCK_TOLERANCE;
|
|
211
|
+
if (clockTolerance) {
|
|
212
|
+
const parsed = Number.parseInt(clockTolerance, 10);
|
|
213
|
+
config.oidcClockTolerance = Number.isNaN(parsed) ? undefined : parsed;
|
|
214
|
+
}
|
|
86
215
|
config.xsuaaAuth = resolveBool('xsuaa-auth', 'SAP_XSUAA_AUTH', false);
|
|
87
216
|
// --- BTP ABAP Environment (direct connection via service key) ---
|
|
88
217
|
config.btpServiceKey = getFlag('btp-service-key') ?? process.env.SAP_BTP_SERVICE_KEY;
|
|
@@ -116,6 +245,26 @@ export function parseArgs(args) {
|
|
|
116
245
|
if (config.verbose) {
|
|
117
246
|
config.logLevel = 'debug';
|
|
118
247
|
}
|
|
248
|
+
// --- Startup Validation ---
|
|
249
|
+
validateConfig(config);
|
|
119
250
|
return config;
|
|
120
251
|
}
|
|
252
|
+
/**
|
|
253
|
+
* Validate configuration for internally consistent auth settings.
|
|
254
|
+
* Fails fast at startup for invalid or dangerous config combinations.
|
|
255
|
+
*/
|
|
256
|
+
export function validateConfig(config) {
|
|
257
|
+
// OIDC: audience is required when issuer is set (RFC 9700 §2.3 audience restriction)
|
|
258
|
+
if (config.oidcIssuer && !config.oidcAudience) {
|
|
259
|
+
throw new Error('SAP_OIDC_AUDIENCE is required when SAP_OIDC_ISSUER is set — ' +
|
|
260
|
+
'audience validation prevents token confusion across services (RFC 9700 §2.3)');
|
|
261
|
+
}
|
|
262
|
+
if (config.oidcAudience && !config.oidcIssuer) {
|
|
263
|
+
throw new Error('SAP_OIDC_ISSUER is required when SAP_OIDC_AUDIENCE is set');
|
|
264
|
+
}
|
|
265
|
+
// PP: ppStrict requires ppEnabled
|
|
266
|
+
if (config.ppStrict && !config.ppEnabled) {
|
|
267
|
+
throw new Error('SAP_PP_STRICT=true requires SAP_PP_ENABLED=true — strict mode has no effect without principal propagation enabled');
|
|
268
|
+
}
|
|
269
|
+
}
|
|
121
270
|
//# sourceMappingURL=config.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/server/config.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/server/config.ts"],"names":[],"mappings":"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"}
|
package/dist/server/http.d.ts
CHANGED
|
@@ -31,4 +31,12 @@ import type { XsuaaCredentials } from './xsuaa.js';
|
|
|
31
31
|
* Start the HTTP Streamable server.
|
|
32
32
|
*/
|
|
33
33
|
export declare function startHttpServer(serverFactory: () => McpServer, config: ServerConfig, xsuaaCredentials?: XsuaaCredentials): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Extract scopes from an OIDC JWT payload.
|
|
36
|
+
*
|
|
37
|
+
* Tries `scope` (space-separated string, standard OIDC) then `scp` (array, Azure AD style).
|
|
38
|
+
* Filters to known scopes, applies implied scope expansion, and falls back to read-only
|
|
39
|
+
* when no scope claims are present (safe default for providers that don't emit scopes).
|
|
40
|
+
*/
|
|
41
|
+
export declare function extractOidcScopes(payload: Record<string, unknown>): string[];
|
|
34
42
|
//# sourceMappingURL=http.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/server/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,2CAA2C,CAAC;
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/server/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,2CAA2C,CAAC;AAQrF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAsEnD;;GAEG;AACH,wBAAsB,eAAe,CACnC,aAAa,EAAE,MAAM,SAAS,EAC9B,MAAM,EAAE,YAAY,EACpB,gBAAgB,CAAC,EAAE,gBAAgB,GAClC,OAAO,CAAC,IAAI,CAAC,CAkKf;AA2GD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAiC5E"}
|
package/dist/server/http.js
CHANGED
|
@@ -26,8 +26,31 @@
|
|
|
26
26
|
*/
|
|
27
27
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
28
28
|
import express from 'express';
|
|
29
|
+
import { expandImpliedScopes } from '../adt/safety.js';
|
|
30
|
+
import { PROFILE_SCOPES } from './config.js';
|
|
29
31
|
import { logger } from './logger.js';
|
|
30
32
|
import { VERSION } from './server.js';
|
|
33
|
+
// ─── API Key Matching Helper ─────────────────────────────────────────
|
|
34
|
+
/**
|
|
35
|
+
* Match a token against configured API keys (multi-key with profiles).
|
|
36
|
+
* Returns the matched entry's profile and scopes, or undefined if no match.
|
|
37
|
+
*/
|
|
38
|
+
function matchApiKey(token, config) {
|
|
39
|
+
// Multi-key: check apiKeys array first
|
|
40
|
+
if (config.apiKeys) {
|
|
41
|
+
for (const entry of config.apiKeys) {
|
|
42
|
+
if (token === entry.key) {
|
|
43
|
+
const scopes = PROFILE_SCOPES[entry.profile] ?? ['read'];
|
|
44
|
+
return { profile: entry.profile, scopes, clientId: `api-key:${entry.profile}` };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Single key: legacy behavior (full scopes)
|
|
49
|
+
if (config.apiKey && token === config.apiKey) {
|
|
50
|
+
return { profile: 'full', scopes: ['read', 'write', 'data', 'sql', 'admin'], clientId: 'api-key' };
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
31
54
|
// ─── JWKS / JWT types (lazy-loaded from jose) ────────────────────────
|
|
32
55
|
let joseModule = null;
|
|
33
56
|
let jwksClient = null;
|
|
@@ -166,7 +189,7 @@ export async function startHttpServer(serverFactory, config, xsuaaCredentials) {
|
|
|
166
189
|
issuerUrl: new URL(appUrl),
|
|
167
190
|
baseUrl: new URL(appUrl),
|
|
168
191
|
resourceServerUrl: new URL(`${appUrl}/mcp`),
|
|
169
|
-
scopesSupported: ['read', 'write', 'admin'],
|
|
192
|
+
scopesSupported: ['read', 'write', 'data', 'sql', 'admin'],
|
|
170
193
|
resourceName: 'ARC-1 SAP MCP Server',
|
|
171
194
|
}));
|
|
172
195
|
// Protected MCP endpoint with chained token verification
|
|
@@ -181,16 +204,18 @@ export async function startHttpServer(serverFactory, config, xsuaaCredentials) {
|
|
|
181
204
|
if (config.oidcIssuer) {
|
|
182
205
|
await initJwks(config.oidcIssuer);
|
|
183
206
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
207
|
+
if (config.apiKey || config.apiKeys || config.oidcIssuer) {
|
|
208
|
+
// Use requireBearerAuth so that authInfo is populated on the MCP request context.
|
|
209
|
+
// This enables scope enforcement, per-request safety, and principal propagation.
|
|
210
|
+
const { requireBearerAuth } = await import('@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js');
|
|
211
|
+
const verifier = createStandardVerifier(config);
|
|
212
|
+
const bearerAuth = requireBearerAuth({ verifier: { verifyAccessToken: verifier } });
|
|
213
|
+
app.all('/mcp', bearerAuth, mcpHandler);
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
// No auth configured — open access
|
|
217
|
+
app.all('/mcp', mcpHandler);
|
|
218
|
+
}
|
|
194
219
|
}
|
|
195
220
|
// ─── 404 for anything else ─────────────────────────────────
|
|
196
221
|
app.use((req, res) => {
|
|
@@ -202,8 +227,10 @@ export async function startHttpServer(serverFactory, config, xsuaaCredentials) {
|
|
|
202
227
|
let authMode = 'NONE (open)';
|
|
203
228
|
if (config.xsuaaAuth && xsuaaCredentials)
|
|
204
229
|
authMode = 'XSUAA OAuth proxy';
|
|
205
|
-
else if (config.apiKey && config.oidcIssuer)
|
|
230
|
+
else if ((config.apiKey || config.apiKeys) && config.oidcIssuer)
|
|
206
231
|
authMode = 'API key + OIDC';
|
|
232
|
+
else if (config.apiKeys)
|
|
233
|
+
authMode = `API keys (${config.apiKeys.length} keys)`;
|
|
207
234
|
else if (config.apiKey)
|
|
208
235
|
authMode = 'API key';
|
|
209
236
|
else if (config.oidcIssuer)
|
|
@@ -216,6 +243,63 @@ export async function startHttpServer(serverFactory, config, xsuaaCredentials) {
|
|
|
216
243
|
});
|
|
217
244
|
});
|
|
218
245
|
}
|
|
246
|
+
// ─── Standard Mode Verifier ─────────────────────────────────────────
|
|
247
|
+
/**
|
|
248
|
+
* Create a token verifier for standard auth mode (API key + OIDC).
|
|
249
|
+
* Returns AuthInfo so the MCP SDK populates extra.authInfo on the request,
|
|
250
|
+
* enabling scope enforcement, per-request safety, and principal propagation.
|
|
251
|
+
*/
|
|
252
|
+
function createStandardVerifier(config) {
|
|
253
|
+
return async (token) => {
|
|
254
|
+
// Lazy-import SDK error classes so bearerAuth maps them to 401/403
|
|
255
|
+
const { InvalidTokenError } = await import('@modelcontextprotocol/sdk/server/auth/errors.js');
|
|
256
|
+
// API key: match against multi-key map or single key
|
|
257
|
+
const apiKeyMatch = matchApiKey(token, config);
|
|
258
|
+
if (apiKeyMatch) {
|
|
259
|
+
// expiresAt is required by requireBearerAuth — use far-future expiry for static keys
|
|
260
|
+
const ONE_YEAR_SECS = 365 * 24 * 60 * 60;
|
|
261
|
+
return {
|
|
262
|
+
token,
|
|
263
|
+
clientId: apiKeyMatch.clientId,
|
|
264
|
+
scopes: apiKeyMatch.scopes,
|
|
265
|
+
expiresAt: Math.floor(Date.now() / 1000) + ONE_YEAR_SECS,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
// OIDC: validate JWT and extract scopes
|
|
269
|
+
if (config.oidcIssuer) {
|
|
270
|
+
try {
|
|
271
|
+
if (!joseModule || !jwksClient) {
|
|
272
|
+
await initJwks(config.oidcIssuer);
|
|
273
|
+
}
|
|
274
|
+
if (!joseModule || !jwksClient) {
|
|
275
|
+
throw new Error('OIDC not initialized — check SAP_OIDC_ISSUER configuration');
|
|
276
|
+
}
|
|
277
|
+
const { payload } = await joseModule.jwtVerify(token, jwksClient, {
|
|
278
|
+
issuer: config.oidcIssuer,
|
|
279
|
+
audience: config.oidcAudience,
|
|
280
|
+
requiredClaims: ['exp'],
|
|
281
|
+
...(config.oidcClockTolerance != null ? { clockTolerance: config.oidcClockTolerance } : {}),
|
|
282
|
+
});
|
|
283
|
+
logger.debug('Standard OIDC JWT validated', { sub: payload.sub, iss: payload.iss });
|
|
284
|
+
const scopes = extractOidcScopes(payload);
|
|
285
|
+
return {
|
|
286
|
+
token,
|
|
287
|
+
clientId: payload.azp ?? payload.sub ?? 'oidc-user',
|
|
288
|
+
scopes,
|
|
289
|
+
expiresAt: payload.exp,
|
|
290
|
+
extra: { sub: payload.sub, iss: payload.iss },
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
catch (err) {
|
|
294
|
+
// Wrap JWT validation errors as InvalidTokenError so bearerAuth returns 401
|
|
295
|
+
if (err instanceof InvalidTokenError)
|
|
296
|
+
throw err;
|
|
297
|
+
throw new InvalidTokenError(err.message ?? 'Invalid token');
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
throw new InvalidTokenError('Authentication failed: invalid token');
|
|
301
|
+
};
|
|
302
|
+
}
|
|
219
303
|
// ─── OIDC Verifier Factory ───────────────────────────────────────────
|
|
220
304
|
/**
|
|
221
305
|
* Create an Entra ID / OIDC token verifier using jose.
|
|
@@ -230,66 +314,67 @@ async function createOidcVerifier(config) {
|
|
|
230
314
|
const { payload } = await joseModule.jwtVerify(token, jwksClient, {
|
|
231
315
|
issuer: config.oidcIssuer,
|
|
232
316
|
audience: config.oidcAudience,
|
|
317
|
+
requiredClaims: ['exp'],
|
|
318
|
+
...(config.oidcClockTolerance != null ? { clockTolerance: config.oidcClockTolerance } : {}),
|
|
233
319
|
});
|
|
234
320
|
logger.debug('OIDC JWT validated', { sub: payload.sub, iss: payload.iss });
|
|
321
|
+
const scopes = extractOidcScopes(payload);
|
|
235
322
|
return {
|
|
236
323
|
token,
|
|
237
324
|
clientId: payload.azp ?? payload.sub ?? 'oidc-user',
|
|
238
|
-
scopes
|
|
325
|
+
scopes,
|
|
239
326
|
expiresAt: payload.exp,
|
|
240
327
|
extra: { sub: payload.sub, iss: payload.iss },
|
|
241
328
|
};
|
|
242
329
|
};
|
|
243
330
|
}
|
|
331
|
+
// ─── OIDC Scope Extraction ──────────────────────────────────────────
|
|
332
|
+
const KNOWN_SCOPES = ['read', 'write', 'data', 'sql', 'admin'];
|
|
244
333
|
/**
|
|
245
|
-
*
|
|
246
|
-
*
|
|
334
|
+
* Extract scopes from an OIDC JWT payload.
|
|
335
|
+
*
|
|
336
|
+
* Tries `scope` (space-separated string, standard OIDC) then `scp` (array, Azure AD style).
|
|
337
|
+
* Filters to known scopes, applies implied scope expansion, and falls back to read-only
|
|
338
|
+
* when no scope claims are present (safe default for providers that don't emit scopes).
|
|
247
339
|
*/
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
340
|
+
export function extractOidcScopes(payload) {
|
|
341
|
+
let rawScopes;
|
|
342
|
+
// Standard OIDC: space-separated string
|
|
343
|
+
if (typeof payload.scope === 'string') {
|
|
344
|
+
rawScopes = payload.scope.split(' ').filter((s) => s.length > 0);
|
|
252
345
|
}
|
|
253
|
-
|
|
254
|
-
if (
|
|
255
|
-
|
|
256
|
-
ok: false,
|
|
257
|
-
status: 401,
|
|
258
|
-
message: 'Missing or invalid Authorization header. Expected: Bearer <token>',
|
|
259
|
-
};
|
|
346
|
+
// Azure AD / Entra: `scp` as space-delimited string (delegated tokens) or array (app tokens)
|
|
347
|
+
else if (typeof payload.scp === 'string') {
|
|
348
|
+
rawScopes = payload.scp.split(' ').filter((s) => s.length > 0);
|
|
260
349
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
if (config.apiKey) {
|
|
264
|
-
if (token === config.apiKey) {
|
|
265
|
-
return { ok: true, status: 200, message: '' };
|
|
266
|
-
}
|
|
267
|
-
if (!config.oidcIssuer) {
|
|
268
|
-
return { ok: false, status: 403, message: 'Invalid API key' };
|
|
269
|
-
}
|
|
350
|
+
else if (Array.isArray(payload.scp)) {
|
|
351
|
+
rawScopes = payload.scp.filter((s) => typeof s === 'string' && s.length > 0);
|
|
270
352
|
}
|
|
271
|
-
//
|
|
272
|
-
if (
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
353
|
+
// No scope claims at all → read-only (safe default)
|
|
354
|
+
if (rawScopes === undefined) {
|
|
355
|
+
logger.warn('OIDC JWT has no scope/scp claims — granting read-only access. ' +
|
|
356
|
+
'Configure scope claims in your OIDC provider to grant write/data/sql access.');
|
|
357
|
+
return ['read'];
|
|
358
|
+
}
|
|
359
|
+
// Filter to known scopes
|
|
360
|
+
const filtered = rawScopes.filter((s) => KNOWN_SCOPES.includes(s));
|
|
361
|
+
// If scopes were present but none are known, grant minimum read access
|
|
362
|
+
if (filtered.length === 0) {
|
|
363
|
+
logger.warn('OIDC JWT has scope claims but none match known scopes — granting read-only', { rawScopes });
|
|
364
|
+
return ['read'];
|
|
282
365
|
}
|
|
283
|
-
return
|
|
366
|
+
return expandImpliedScopes(filtered);
|
|
284
367
|
}
|
|
285
368
|
/**
|
|
286
369
|
* Initialize JWKS client from OIDC discovery.
|
|
287
370
|
*/
|
|
288
371
|
async function initJwks(issuer) {
|
|
289
|
-
if (joseModule)
|
|
372
|
+
if (joseModule && jwksClient)
|
|
290
373
|
return;
|
|
291
374
|
try {
|
|
292
|
-
|
|
375
|
+
if (!joseModule) {
|
|
376
|
+
joseModule = await import('jose');
|
|
377
|
+
}
|
|
293
378
|
const jwksUri = new URL('.well-known/openid-configuration', issuer.endsWith('/') ? issuer : `${issuer}/`);
|
|
294
379
|
const discoveryResp = await fetch(jwksUri.toString());
|
|
295
380
|
const discovery = (await discoveryResp.json());
|
|
@@ -306,26 +391,4 @@ async function initJwks(issuer) {
|
|
|
306
391
|
});
|
|
307
392
|
}
|
|
308
393
|
}
|
|
309
|
-
/**
|
|
310
|
-
* Validate a JWT token against the configured OIDC issuer.
|
|
311
|
-
*/
|
|
312
|
-
async function validateJwt(token, config) {
|
|
313
|
-
if (!joseModule || !jwksClient) {
|
|
314
|
-
if (config.oidcIssuer) {
|
|
315
|
-
await initJwks(config.oidcIssuer);
|
|
316
|
-
}
|
|
317
|
-
if (!joseModule || !jwksClient) {
|
|
318
|
-
throw new Error('OIDC not initialized — check SAP_OIDC_ISSUER configuration');
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
const { payload } = await joseModule.jwtVerify(token, jwksClient, {
|
|
322
|
-
issuer: config.oidcIssuer,
|
|
323
|
-
audience: config.oidcAudience,
|
|
324
|
-
});
|
|
325
|
-
logger.debug('JWT validated', {
|
|
326
|
-
sub: payload.sub,
|
|
327
|
-
iss: payload.iss,
|
|
328
|
-
exp: payload.exp,
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
394
|
//# sourceMappingURL=http.js.map
|