@mcp-z/client 1.0.1 → 1.0.3
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/dist/cjs/auth/capability-discovery.d.cts +0 -19
- package/dist/cjs/auth/capability-discovery.d.ts +0 -19
- package/dist/cjs/auth/capability-discovery.js +123 -52
- package/dist/cjs/auth/capability-discovery.js.map +1 -1
- package/dist/cjs/auth/index.js.map +1 -1
- package/dist/cjs/auth/interactive-oauth-flow.js.map +1 -1
- package/dist/cjs/auth/oauth-callback-listener.js.map +1 -1
- package/dist/cjs/auth/pkce.js.map +1 -1
- package/dist/cjs/auth/rfc9728-discovery.d.cts +7 -0
- package/dist/cjs/auth/rfc9728-discovery.d.ts +7 -0
- package/dist/cjs/auth/rfc9728-discovery.js +208 -46
- package/dist/cjs/auth/rfc9728-discovery.js.map +1 -1
- package/dist/cjs/auth/types.js.map +1 -1
- package/dist/cjs/client-helpers.js.map +1 -1
- package/dist/cjs/config/server-loader.js.map +1 -1
- package/dist/cjs/config/validate-config.js +3 -7
- package/dist/cjs/config/validate-config.js.map +1 -1
- package/dist/cjs/connection/connect-client.js.map +1 -1
- package/dist/cjs/connection/existing-process-transport.js.map +1 -1
- package/dist/cjs/connection/types.js.map +1 -1
- package/dist/cjs/connection/wait-for-http-ready.js.map +1 -1
- package/dist/cjs/dcr/dcr-authenticator.js.map +1 -1
- package/dist/cjs/dcr/dynamic-client-registrar.js.map +1 -1
- package/dist/cjs/dcr/index.js.map +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/monkey-patches.js.map +1 -1
- package/dist/cjs/response-wrappers.js.map +1 -1
- package/dist/cjs/search/index.js.map +1 -1
- package/dist/cjs/search/search.js.map +1 -1
- package/dist/cjs/search/types.js.map +1 -1
- package/dist/cjs/spawn/spawn-server.js +4 -0
- package/dist/cjs/spawn/spawn-server.js.map +1 -1
- package/dist/cjs/spawn/spawn-servers.js.map +1 -1
- package/dist/cjs/types.js.map +1 -1
- package/dist/cjs/utils/logger.js.map +1 -1
- package/dist/cjs/utils/path-utils.js.map +1 -1
- package/dist/cjs/utils/sanitizer.js.map +1 -1
- package/dist/esm/auth/capability-discovery.d.ts +0 -19
- package/dist/esm/auth/capability-discovery.js +43 -41
- package/dist/esm/auth/capability-discovery.js.map +1 -1
- package/dist/esm/auth/index.js.map +1 -1
- package/dist/esm/auth/interactive-oauth-flow.js.map +1 -1
- package/dist/esm/auth/oauth-callback-listener.js.map +1 -1
- package/dist/esm/auth/pkce.js.map +1 -1
- package/dist/esm/auth/rfc9728-discovery.d.ts +7 -0
- package/dist/esm/auth/rfc9728-discovery.js +67 -0
- package/dist/esm/auth/rfc9728-discovery.js.map +1 -1
- package/dist/esm/auth/types.js.map +1 -1
- package/dist/esm/client-helpers.js.map +1 -1
- package/dist/esm/config/server-loader.js.map +1 -1
- package/dist/esm/config/validate-config.js +3 -7
- package/dist/esm/config/validate-config.js.map +1 -1
- package/dist/esm/connection/connect-client.js.map +1 -1
- package/dist/esm/connection/existing-process-transport.js.map +1 -1
- package/dist/esm/connection/types.js.map +1 -1
- package/dist/esm/connection/wait-for-http-ready.js.map +1 -1
- package/dist/esm/dcr/dcr-authenticator.js.map +1 -1
- package/dist/esm/dcr/dynamic-client-registrar.js.map +1 -1
- package/dist/esm/dcr/index.js.map +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/monkey-patches.js.map +1 -1
- package/dist/esm/response-wrappers.js.map +1 -1
- package/dist/esm/search/index.js.map +1 -1
- package/dist/esm/search/search.js.map +1 -1
- package/dist/esm/search/types.js.map +1 -1
- package/dist/esm/spawn/spawn-server.js +4 -0
- package/dist/esm/spawn/spawn-server.js.map +1 -1
- package/dist/esm/spawn/spawn-servers.js.map +1 -1
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/utils/logger.js.map +1 -1
- package/dist/esm/utils/path-utils.js.map +1 -1
- package/dist/esm/utils/sanitizer.js.map +1 -1
- package/package.json +1 -1
|
@@ -12,6 +12,9 @@ function _export(target, all) {
|
|
|
12
12
|
});
|
|
13
13
|
}
|
|
14
14
|
_export(exports, {
|
|
15
|
+
get discoverAuthorizationServerIssuer () {
|
|
16
|
+
return discoverAuthorizationServerIssuer;
|
|
17
|
+
},
|
|
15
18
|
get discoverAuthorizationServerMetadata () {
|
|
16
19
|
return discoverAuthorizationServerMetadata;
|
|
17
20
|
},
|
|
@@ -178,27 +181,37 @@ function _ts_generator(thisArg, body) {
|
|
|
178
181
|
}
|
|
179
182
|
function discoverProtectedResourceMetadata(resourceUrl) {
|
|
180
183
|
return _async_to_generator(function() {
|
|
181
|
-
var origin, path, rootUrl, response, metadata, rootMetadata, subPathUrl, subPathResponse, unused, unused1, subPathUrl1, response1, unused2, _error;
|
|
184
|
+
var headerMetadata, origin, path, rootUrl, response, metadata, rootMetadata, subPathUrl, subPathResponse, unused, unused1, subPathUrl1, response1, unused2, _error;
|
|
182
185
|
return _ts_generator(this, function(_state) {
|
|
183
186
|
switch(_state.label){
|
|
184
187
|
case 0:
|
|
185
188
|
_state.trys.push([
|
|
186
189
|
0,
|
|
187
|
-
|
|
190
|
+
20,
|
|
188
191
|
,
|
|
189
|
-
|
|
192
|
+
21
|
|
190
193
|
]);
|
|
194
|
+
return [
|
|
195
|
+
4,
|
|
196
|
+
discoverProtectedResourceMetadataFromHeader(resourceUrl)
|
|
197
|
+
];
|
|
198
|
+
case 1:
|
|
199
|
+
headerMetadata = _state.sent();
|
|
200
|
+
if (headerMetadata) return [
|
|
201
|
+
2,
|
|
202
|
+
headerMetadata
|
|
203
|
+
];
|
|
191
204
|
origin = getOrigin(resourceUrl);
|
|
192
205
|
path = getPath(resourceUrl);
|
|
193
206
|
// Strategy 1: Try root location (REQUIRED by RFC 9728)
|
|
194
207
|
rootUrl = "".concat(origin, "/.well-known/oauth-protected-resource");
|
|
195
|
-
_state.label =
|
|
196
|
-
case
|
|
208
|
+
_state.label = 2;
|
|
209
|
+
case 2:
|
|
197
210
|
_state.trys.push([
|
|
198
|
-
|
|
199
|
-
|
|
211
|
+
2,
|
|
212
|
+
12,
|
|
200
213
|
,
|
|
201
|
-
|
|
214
|
+
13
|
|
202
215
|
]);
|
|
203
216
|
return [
|
|
204
217
|
4,
|
|
@@ -210,17 +223,17 @@ function discoverProtectedResourceMetadata(resourceUrl) {
|
|
|
210
223
|
}
|
|
211
224
|
})
|
|
212
225
|
];
|
|
213
|
-
case
|
|
226
|
+
case 3:
|
|
214
227
|
response = _state.sent();
|
|
215
228
|
if (!response.ok) return [
|
|
216
229
|
3,
|
|
217
|
-
|
|
230
|
+
11
|
|
218
231
|
];
|
|
219
232
|
return [
|
|
220
233
|
4,
|
|
221
234
|
response.json()
|
|
222
235
|
];
|
|
223
|
-
case
|
|
236
|
+
case 4:
|
|
224
237
|
metadata = _state.sent();
|
|
225
238
|
// Check if the discovered resource matches what we're looking for
|
|
226
239
|
if (metadata.resource === resourceUrl) {
|
|
@@ -239,20 +252,20 @@ function discoverProtectedResourceMetadata(resourceUrl) {
|
|
|
239
252
|
}
|
|
240
253
|
if (!resourceUrl.startsWith(metadata.resource)) return [
|
|
241
254
|
3,
|
|
242
|
-
|
|
255
|
+
11
|
|
243
256
|
];
|
|
244
257
|
// Still try sub-path location to see if there's more specific metadata
|
|
245
258
|
// But save root metadata as fallback
|
|
246
259
|
rootMetadata = metadata;
|
|
247
260
|
// Try sub-path location for more specific metadata
|
|
248
261
|
subPathUrl = "".concat(origin, "/.well-known/oauth-protected-resource").concat(path);
|
|
249
|
-
_state.label =
|
|
250
|
-
case
|
|
262
|
+
_state.label = 5;
|
|
263
|
+
case 5:
|
|
251
264
|
_state.trys.push([
|
|
252
|
-
|
|
253
|
-
|
|
265
|
+
5,
|
|
266
|
+
9,
|
|
254
267
|
,
|
|
255
|
-
|
|
268
|
+
10
|
|
256
269
|
]);
|
|
257
270
|
return [
|
|
258
271
|
4,
|
|
@@ -264,62 +277,62 @@ function discoverProtectedResourceMetadata(resourceUrl) {
|
|
|
264
277
|
}
|
|
265
278
|
})
|
|
266
279
|
];
|
|
267
|
-
case
|
|
280
|
+
case 6:
|
|
268
281
|
subPathResponse = _state.sent();
|
|
269
282
|
if (!subPathResponse.ok) return [
|
|
270
283
|
3,
|
|
271
|
-
|
|
284
|
+
8
|
|
272
285
|
];
|
|
273
286
|
return [
|
|
274
287
|
4,
|
|
275
288
|
subPathResponse.json()
|
|
276
289
|
];
|
|
277
|
-
case
|
|
290
|
+
case 7:
|
|
278
291
|
return [
|
|
279
292
|
2,
|
|
280
293
|
_state.sent()
|
|
281
294
|
];
|
|
282
|
-
case
|
|
295
|
+
case 8:
|
|
283
296
|
return [
|
|
284
297
|
3,
|
|
285
|
-
|
|
298
|
+
10
|
|
286
299
|
];
|
|
287
|
-
case
|
|
300
|
+
case 9:
|
|
288
301
|
unused = _state.sent();
|
|
289
302
|
return [
|
|
290
303
|
3,
|
|
291
|
-
|
|
304
|
+
10
|
|
292
305
|
];
|
|
293
|
-
case
|
|
306
|
+
case 10:
|
|
294
307
|
// Return root metadata as it applies to this resource
|
|
295
308
|
return [
|
|
296
309
|
2,
|
|
297
310
|
rootMetadata
|
|
298
311
|
];
|
|
299
|
-
case
|
|
312
|
+
case 11:
|
|
300
313
|
return [
|
|
301
314
|
3,
|
|
302
|
-
|
|
315
|
+
13
|
|
303
316
|
];
|
|
304
|
-
case
|
|
317
|
+
case 12:
|
|
305
318
|
unused1 = _state.sent();
|
|
306
319
|
return [
|
|
307
320
|
3,
|
|
308
|
-
|
|
321
|
+
13
|
|
309
322
|
];
|
|
310
|
-
case
|
|
323
|
+
case 13:
|
|
311
324
|
if (!path) return [
|
|
312
325
|
3,
|
|
313
|
-
|
|
326
|
+
19
|
|
314
327
|
];
|
|
315
328
|
subPathUrl1 = "".concat(origin, "/.well-known/oauth-protected-resource").concat(path);
|
|
316
|
-
_state.label =
|
|
317
|
-
case
|
|
329
|
+
_state.label = 14;
|
|
330
|
+
case 14:
|
|
318
331
|
_state.trys.push([
|
|
319
|
-
|
|
320
|
-
|
|
332
|
+
14,
|
|
333
|
+
18,
|
|
321
334
|
,
|
|
322
|
-
|
|
335
|
+
19
|
|
323
336
|
]);
|
|
324
337
|
return [
|
|
325
338
|
4,
|
|
@@ -331,46 +344,143 @@ function discoverProtectedResourceMetadata(resourceUrl) {
|
|
|
331
344
|
}
|
|
332
345
|
})
|
|
333
346
|
];
|
|
334
|
-
case
|
|
347
|
+
case 15:
|
|
335
348
|
response1 = _state.sent();
|
|
336
349
|
if (!response1.ok) return [
|
|
337
350
|
3,
|
|
338
|
-
|
|
351
|
+
17
|
|
339
352
|
];
|
|
340
353
|
return [
|
|
341
354
|
4,
|
|
342
355
|
response1.json()
|
|
343
356
|
];
|
|
344
|
-
case
|
|
357
|
+
case 16:
|
|
345
358
|
return [
|
|
346
359
|
2,
|
|
347
360
|
_state.sent()
|
|
348
361
|
];
|
|
349
|
-
case
|
|
362
|
+
case 17:
|
|
350
363
|
return [
|
|
351
364
|
3,
|
|
352
|
-
|
|
365
|
+
19
|
|
353
366
|
];
|
|
354
|
-
case
|
|
367
|
+
case 18:
|
|
355
368
|
unused2 = _state.sent();
|
|
356
369
|
return [
|
|
357
370
|
3,
|
|
358
|
-
|
|
371
|
+
19
|
|
359
372
|
];
|
|
360
|
-
case
|
|
373
|
+
case 19:
|
|
361
374
|
// Neither location found or resource didn't match
|
|
362
375
|
return [
|
|
363
376
|
2,
|
|
364
377
|
null
|
|
365
378
|
];
|
|
366
|
-
case
|
|
379
|
+
case 20:
|
|
367
380
|
_error = _state.sent();
|
|
368
381
|
// Network error, invalid URL, or other failure
|
|
369
382
|
return [
|
|
370
383
|
2,
|
|
371
384
|
null
|
|
372
385
|
];
|
|
373
|
-
case
|
|
386
|
+
case 21:
|
|
387
|
+
return [
|
|
388
|
+
2
|
|
389
|
+
];
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
})();
|
|
393
|
+
}
|
|
394
|
+
function discoverProtectedResourceMetadataFromHeader(resourceUrl) {
|
|
395
|
+
return _async_to_generator(function() {
|
|
396
|
+
var response, header, postResponse, match, metadataUrl, metadataResponse, _error;
|
|
397
|
+
return _ts_generator(this, function(_state) {
|
|
398
|
+
switch(_state.label){
|
|
399
|
+
case 0:
|
|
400
|
+
_state.trys.push([
|
|
401
|
+
0,
|
|
402
|
+
6,
|
|
403
|
+
,
|
|
404
|
+
7
|
|
405
|
+
]);
|
|
406
|
+
return [
|
|
407
|
+
4,
|
|
408
|
+
fetch(resourceUrl, {
|
|
409
|
+
method: 'GET',
|
|
410
|
+
headers: {
|
|
411
|
+
Accept: 'application/json',
|
|
412
|
+
Connection: 'close'
|
|
413
|
+
}
|
|
414
|
+
})
|
|
415
|
+
];
|
|
416
|
+
case 1:
|
|
417
|
+
response = _state.sent();
|
|
418
|
+
header = response.headers.get('www-authenticate');
|
|
419
|
+
if (!!header) return [
|
|
420
|
+
3,
|
|
421
|
+
3
|
|
422
|
+
];
|
|
423
|
+
return [
|
|
424
|
+
4,
|
|
425
|
+
fetch(resourceUrl, {
|
|
426
|
+
method: 'POST',
|
|
427
|
+
headers: {
|
|
428
|
+
Accept: 'application/json',
|
|
429
|
+
Connection: 'close',
|
|
430
|
+
'Content-Type': 'application/json'
|
|
431
|
+
},
|
|
432
|
+
body: '{}'
|
|
433
|
+
})
|
|
434
|
+
];
|
|
435
|
+
case 2:
|
|
436
|
+
postResponse = _state.sent();
|
|
437
|
+
header = postResponse.headers.get('www-authenticate');
|
|
438
|
+
_state.label = 3;
|
|
439
|
+
case 3:
|
|
440
|
+
if (!header) return [
|
|
441
|
+
2,
|
|
442
|
+
null
|
|
443
|
+
];
|
|
444
|
+
match = header.match(/resource_metadata="([^"]+)"/i);
|
|
445
|
+
if (!match || !match[1]) return [
|
|
446
|
+
2,
|
|
447
|
+
null
|
|
448
|
+
];
|
|
449
|
+
metadataUrl = match[1];
|
|
450
|
+
return [
|
|
451
|
+
4,
|
|
452
|
+
fetch(metadataUrl, {
|
|
453
|
+
method: 'GET',
|
|
454
|
+
headers: {
|
|
455
|
+
Accept: 'application/json',
|
|
456
|
+
Connection: 'close'
|
|
457
|
+
}
|
|
458
|
+
})
|
|
459
|
+
];
|
|
460
|
+
case 4:
|
|
461
|
+
metadataResponse = _state.sent();
|
|
462
|
+
if (!metadataResponse.ok) {
|
|
463
|
+
return [
|
|
464
|
+
2,
|
|
465
|
+
null
|
|
466
|
+
];
|
|
467
|
+
}
|
|
468
|
+
return [
|
|
469
|
+
4,
|
|
470
|
+
metadataResponse.json()
|
|
471
|
+
];
|
|
472
|
+
case 5:
|
|
473
|
+
return [
|
|
474
|
+
2,
|
|
475
|
+
_state.sent()
|
|
476
|
+
];
|
|
477
|
+
case 6:
|
|
478
|
+
_error = _state.sent();
|
|
479
|
+
return [
|
|
480
|
+
2,
|
|
481
|
+
null
|
|
482
|
+
];
|
|
483
|
+
case 7:
|
|
374
484
|
return [
|
|
375
485
|
2
|
|
376
486
|
];
|
|
@@ -433,4 +543,56 @@ function discoverAuthorizationServerMetadata(authServerUrl) {
|
|
|
433
543
|
});
|
|
434
544
|
})();
|
|
435
545
|
}
|
|
546
|
+
function discoverAuthorizationServerIssuer(resourceUrl) {
|
|
547
|
+
return _async_to_generator(function() {
|
|
548
|
+
var _match_, response, header, match, _error;
|
|
549
|
+
return _ts_generator(this, function(_state) {
|
|
550
|
+
switch(_state.label){
|
|
551
|
+
case 0:
|
|
552
|
+
_state.trys.push([
|
|
553
|
+
0,
|
|
554
|
+
2,
|
|
555
|
+
,
|
|
556
|
+
3
|
|
557
|
+
]);
|
|
558
|
+
return [
|
|
559
|
+
4,
|
|
560
|
+
fetch(resourceUrl, {
|
|
561
|
+
method: 'GET',
|
|
562
|
+
headers: {
|
|
563
|
+
Accept: 'application/json',
|
|
564
|
+
Connection: 'close'
|
|
565
|
+
}
|
|
566
|
+
})
|
|
567
|
+
];
|
|
568
|
+
case 1:
|
|
569
|
+
response = _state.sent();
|
|
570
|
+
header = response.headers.get('www-authenticate');
|
|
571
|
+
if (!header) return [
|
|
572
|
+
2,
|
|
573
|
+
null
|
|
574
|
+
];
|
|
575
|
+
match = header.match(/(?:authorization_server|issuer)="([^"]+)"/i);
|
|
576
|
+
if (!match) return [
|
|
577
|
+
2,
|
|
578
|
+
null
|
|
579
|
+
];
|
|
580
|
+
return [
|
|
581
|
+
2,
|
|
582
|
+
(_match_ = match[1]) !== null && _match_ !== void 0 ? _match_ : null
|
|
583
|
+
];
|
|
584
|
+
case 2:
|
|
585
|
+
_error = _state.sent();
|
|
586
|
+
return [
|
|
587
|
+
2,
|
|
588
|
+
null
|
|
589
|
+
];
|
|
590
|
+
case 3:
|
|
591
|
+
return [
|
|
592
|
+
2
|
|
593
|
+
];
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
})();
|
|
597
|
+
}
|
|
436
598
|
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/libs/client/src/auth/rfc9728-discovery.ts"],"sourcesContent":["/**\n * RFC 9728 Protected Resource Metadata Discovery\n * Probes .well-known/oauth-protected-resource endpoint\n */\n\nimport type { AuthorizationServerMetadata, ProtectedResourceMetadata } from './types.ts';\n\n/**\n * Extract origin (protocol + host) from a URL\n * @param url - Full URL that may include a path\n * @returns Origin (e.g., \"https://example.com\") or original string if invalid URL\n *\n * @example\n * getOrigin('https://example.com/mcp') // → 'https://example.com'\n * getOrigin('http://localhost:9999/api/v1/mcp') // → 'http://localhost:9999'\n */\nfunction getOrigin(url: string): string {\n try {\n return new URL(url).origin;\n } catch {\n // Invalid URL - return as-is for graceful degradation\n return url;\n }\n}\n\n/**\n * Extract path from a URL (without origin)\n * @param url - Full URL\n * @returns Path component (e.g., \"/mcp\", \"/api/v1/mcp\") or empty string if no path\n */\nfunction getPath(url: string): string {\n try {\n const parsed = new URL(url);\n // pathname includes leading slash, e.g., \"/mcp\"\n return parsed.pathname === '/' ? '' : parsed.pathname;\n } catch {\n return '';\n }\n}\n\n/**\n * Discover OAuth 2.0 Protected Resource Metadata (RFC 9728)\n * Probes .well-known/oauth-protected-resource endpoint\n *\n * Discovery Strategy:\n * 1. Try origin root: {origin}/.well-known/oauth-protected-resource\n * 2. If 404, try sub-path: {origin}/.well-known/oauth-protected-resource{path}\n *\n * @param resourceUrl - URL of the protected resource (e.g., https://ai.todoist.net/mcp)\n * @returns ProtectedResourceMetadata if discovered, null otherwise\n *\n * @example\n * // Todoist case: MCP at ai.todoist.net/mcp, OAuth at todoist.com\n * const metadata = await discoverProtectedResourceMetadata('https://ai.todoist.net/mcp');\n * // Returns: { resource: \"https://ai.todoist.net/mcp\", authorization_servers: [\"https://todoist.com\"] }\n */\nexport async function discoverProtectedResourceMetadata(resourceUrl: string): Promise<ProtectedResourceMetadata | null> {\n try {\n const origin = getOrigin(resourceUrl);\n const path = getPath(resourceUrl);\n\n // Strategy 1: Try root location (REQUIRED by RFC 9728)\n const rootUrl = `${origin}/.well-known/oauth-protected-resource`;\n\n try {\n const response = await fetch(rootUrl, {\n method: 'GET',\n headers: { Accept: 'application/json', Connection: 'close' },\n });\n\n if (response.ok) {\n const metadata = (await response.json()) as ProtectedResourceMetadata;\n // Check if the discovered resource matches what we're looking for\n if (metadata.resource === resourceUrl) {\n return metadata;\n }\n // If there's no path component, return root metadata\n // (e.g., looking for http://example.com and found it)\n if (!path) {\n return metadata;\n }\n // If requested URL starts with metadata.resource, the root metadata applies to sub-paths\n // (e.g., looking for http://example.com/api/v1/mcp, found http://example.com)\n if (resourceUrl.startsWith(metadata.resource)) {\n // Still try sub-path location to see if there's more specific metadata\n // But save root metadata as fallback\n const rootMetadata = metadata;\n\n // Try sub-path location for more specific metadata\n const subPathUrl = `${origin}/.well-known/oauth-protected-resource${path}`;\n try {\n const subPathResponse = await fetch(subPathUrl, {\n method: 'GET',\n headers: { Accept: 'application/json', Connection: 'close' },\n });\n if (subPathResponse.ok) {\n return (await subPathResponse.json()) as ProtectedResourceMetadata;\n }\n } catch {\n // Sub-path failed, use root metadata\n }\n\n // Return root metadata as it applies to this resource\n return rootMetadata;\n }\n // Otherwise, try sub-path location before giving up\n }\n } catch {\n // Continue to sub-path location\n }\n\n // Strategy 2: Try sub-path location (MCP spec extension)\n // Only try if there's a path component\n if (path) {\n const subPathUrl = `${origin}/.well-known/oauth-protected-resource${path}`;\n\n try {\n const response = await fetch(subPathUrl, {\n method: 'GET',\n headers: { Accept: 'application/json', Connection: 'close' },\n });\n\n if (response.ok) {\n return (await response.json()) as ProtectedResourceMetadata;\n }\n } catch {\n // Fall through to return null\n }\n }\n\n // Neither location found or resource didn't match\n return null;\n } catch (_error) {\n // Network error, invalid URL, or other failure\n return null;\n }\n}\n\n/**\n * Discover OAuth 2.0 Authorization Server Metadata (RFC 8414)\n * Probes .well-known/oauth-authorization-server endpoint\n *\n * @param authServerUrl - URL of the authorization server (typically from RFC 9728 discovery)\n * @returns AuthorizationServerMetadata if discovered, null otherwise\n *\n * @example\n * const metadata = await discoverAuthorizationServerMetadata('https://todoist.com');\n * // Returns: { issuer: \"https://todoist.com\", authorization_endpoint: \"...\", ... }\n */\nexport async function discoverAuthorizationServerMetadata(authServerUrl: string): Promise<AuthorizationServerMetadata | null> {\n try {\n const origin = getOrigin(authServerUrl);\n const wellKnownUrl = `${origin}/.well-known/oauth-authorization-server`;\n\n const response = await fetch(wellKnownUrl, {\n method: 'GET',\n headers: { Accept: 'application/json', Connection: 'close' },\n });\n\n if (!response.ok) {\n return null;\n }\n\n return (await response.json()) as AuthorizationServerMetadata;\n } catch (_error) {\n return null;\n }\n}\n"],"names":["discoverAuthorizationServerMetadata","discoverProtectedResourceMetadata","getOrigin","url","URL","origin","getPath","parsed","pathname","resourceUrl","path","rootUrl","response","metadata","rootMetadata","subPathUrl","subPathResponse","_error","fetch","method","headers","Accept","Connection","ok","json","resource","startsWith","authServerUrl","wellKnownUrl"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/auth/rfc9728-discovery.ts"],"sourcesContent":["/**\n * RFC 9728 Protected Resource Metadata Discovery\n * Probes .well-known/oauth-protected-resource endpoint\n */\n\nimport type { AuthorizationServerMetadata, ProtectedResourceMetadata } from './types.ts';\n\n/**\n * Extract origin (protocol + host) from a URL\n * @param url - Full URL that may include a path\n * @returns Origin (e.g., \"https://example.com\") or original string if invalid URL\n *\n * @example\n * getOrigin('https://example.com/mcp') // → 'https://example.com'\n * getOrigin('http://localhost:9999/api/v1/mcp') // → 'http://localhost:9999'\n */\nfunction getOrigin(url: string): string {\n try {\n return new URL(url).origin;\n } catch {\n // Invalid URL - return as-is for graceful degradation\n return url;\n }\n}\n\n/**\n * Extract path from a URL (without origin)\n * @param url - Full URL\n * @returns Path component (e.g., \"/mcp\", \"/api/v1/mcp\") or empty string if no path\n */\nfunction getPath(url: string): string {\n try {\n const parsed = new URL(url);\n // pathname includes leading slash, e.g., \"/mcp\"\n return parsed.pathname === '/' ? '' : parsed.pathname;\n } catch {\n return '';\n }\n}\n\n/**\n * Discover OAuth 2.0 Protected Resource Metadata (RFC 9728)\n * Probes .well-known/oauth-protected-resource endpoint\n *\n * Discovery Strategy:\n * 1. Try origin root: {origin}/.well-known/oauth-protected-resource\n * 2. If 404, try sub-path: {origin}/.well-known/oauth-protected-resource{path}\n *\n * @param resourceUrl - URL of the protected resource (e.g., https://ai.todoist.net/mcp)\n * @returns ProtectedResourceMetadata if discovered, null otherwise\n *\n * @example\n * // Todoist case: MCP at ai.todoist.net/mcp, OAuth at todoist.com\n * const metadata = await discoverProtectedResourceMetadata('https://ai.todoist.net/mcp');\n * // Returns: { resource: \"https://ai.todoist.net/mcp\", authorization_servers: [\"https://todoist.com\"] }\n */\nexport async function discoverProtectedResourceMetadata(resourceUrl: string): Promise<ProtectedResourceMetadata | null> {\n try {\n const headerMetadata = await discoverProtectedResourceMetadataFromHeader(resourceUrl);\n if (headerMetadata) return headerMetadata;\n\n const origin = getOrigin(resourceUrl);\n const path = getPath(resourceUrl);\n\n // Strategy 1: Try root location (REQUIRED by RFC 9728)\n const rootUrl = `${origin}/.well-known/oauth-protected-resource`;\n\n try {\n const response = await fetch(rootUrl, {\n method: 'GET',\n headers: { Accept: 'application/json', Connection: 'close' },\n });\n\n if (response.ok) {\n const metadata = (await response.json()) as ProtectedResourceMetadata;\n // Check if the discovered resource matches what we're looking for\n if (metadata.resource === resourceUrl) {\n return metadata;\n }\n // If there's no path component, return root metadata\n // (e.g., looking for http://example.com and found it)\n if (!path) {\n return metadata;\n }\n // If requested URL starts with metadata.resource, the root metadata applies to sub-paths\n // (e.g., looking for http://example.com/api/v1/mcp, found http://example.com)\n if (resourceUrl.startsWith(metadata.resource)) {\n // Still try sub-path location to see if there's more specific metadata\n // But save root metadata as fallback\n const rootMetadata = metadata;\n\n // Try sub-path location for more specific metadata\n const subPathUrl = `${origin}/.well-known/oauth-protected-resource${path}`;\n try {\n const subPathResponse = await fetch(subPathUrl, {\n method: 'GET',\n headers: { Accept: 'application/json', Connection: 'close' },\n });\n if (subPathResponse.ok) {\n return (await subPathResponse.json()) as ProtectedResourceMetadata;\n }\n } catch {\n // Sub-path failed, use root metadata\n }\n\n // Return root metadata as it applies to this resource\n return rootMetadata;\n }\n // Otherwise, try sub-path location before giving up\n }\n } catch {\n // Continue to sub-path location\n }\n\n // Strategy 2: Try sub-path location (MCP spec extension)\n // Only try if there's a path component\n if (path) {\n const subPathUrl = `${origin}/.well-known/oauth-protected-resource${path}`;\n\n try {\n const response = await fetch(subPathUrl, {\n method: 'GET',\n headers: { Accept: 'application/json', Connection: 'close' },\n });\n\n if (response.ok) {\n return (await response.json()) as ProtectedResourceMetadata;\n }\n } catch {\n // Fall through to return null\n }\n }\n\n // Neither location found or resource didn't match\n return null;\n } catch (_error) {\n // Network error, invalid URL, or other failure\n return null;\n }\n}\n\nasync function discoverProtectedResourceMetadataFromHeader(resourceUrl: string): Promise<ProtectedResourceMetadata | null> {\n try {\n const response = await fetch(resourceUrl, {\n method: 'GET',\n headers: { Accept: 'application/json', Connection: 'close' },\n });\n\n let header = response.headers.get('www-authenticate');\n if (!header) {\n const postResponse = await fetch(resourceUrl, {\n method: 'POST',\n headers: { Accept: 'application/json', Connection: 'close', 'Content-Type': 'application/json' },\n body: '{}',\n });\n header = postResponse.headers.get('www-authenticate');\n }\n\n if (!header) return null;\n\n const match = header.match(/resource_metadata=\"([^\"]+)\"/i);\n if (!match || !match[1]) return null;\n\n const metadataUrl = match[1];\n const metadataResponse = await fetch(metadataUrl, {\n method: 'GET',\n headers: { Accept: 'application/json', Connection: 'close' },\n });\n\n if (!metadataResponse.ok) {\n return null;\n }\n\n return (await metadataResponse.json()) as ProtectedResourceMetadata;\n } catch (_error) {\n return null;\n }\n}\n\n/**\n * Discover OAuth 2.0 Authorization Server Metadata (RFC 8414)\n * Probes .well-known/oauth-authorization-server endpoint\n *\n * @param authServerUrl - URL of the authorization server (typically from RFC 9728 discovery)\n * @returns AuthorizationServerMetadata if discovered, null otherwise\n *\n * @example\n * const metadata = await discoverAuthorizationServerMetadata('https://todoist.com');\n * // Returns: { issuer: \"https://todoist.com\", authorization_endpoint: \"...\", ... }\n */\nexport async function discoverAuthorizationServerMetadata(authServerUrl: string): Promise<AuthorizationServerMetadata | null> {\n try {\n const origin = getOrigin(authServerUrl);\n const wellKnownUrl = `${origin}/.well-known/oauth-authorization-server`;\n\n const response = await fetch(wellKnownUrl, {\n method: 'GET',\n headers: { Accept: 'application/json', Connection: 'close' },\n });\n\n if (!response.ok) {\n return null;\n }\n\n return (await response.json()) as AuthorizationServerMetadata;\n } catch (_error) {\n return null;\n }\n}\n\n/**\n * Discover OAuth Authorization Server Issuer from resource response (RFC 9207)\n *\n * @param resourceUrl - URL of the protected resource\n * @returns Issuer URL if present in WWW-Authenticate header, null otherwise\n */\nexport async function discoverAuthorizationServerIssuer(resourceUrl: string): Promise<string | null> {\n try {\n const response = await fetch(resourceUrl, {\n method: 'GET',\n headers: { Accept: 'application/json', Connection: 'close' },\n });\n\n const header = response.headers.get('www-authenticate');\n if (!header) return null;\n\n const match = header.match(/(?:authorization_server|issuer)=\"([^\"]+)\"/i);\n if (!match) return null;\n\n return match[1] ?? null;\n } catch (_error) {\n return null;\n }\n}\n"],"names":["discoverAuthorizationServerIssuer","discoverAuthorizationServerMetadata","discoverProtectedResourceMetadata","getOrigin","url","URL","origin","getPath","parsed","pathname","resourceUrl","headerMetadata","path","rootUrl","response","metadata","rootMetadata","subPathUrl","subPathResponse","_error","discoverProtectedResourceMetadataFromHeader","fetch","method","headers","Accept","Connection","ok","json","resource","startsWith","header","postResponse","match","metadataUrl","metadataResponse","get","body","authServerUrl","wellKnownUrl"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/auth/types.ts"],"sourcesContent":["/**\n * Shared types for OAuth and DCR authentication\n */\n\n/**\n * OAuth callback result from authorization server\n */\nexport interface CallbackResult {\n /** Authorization code from OAuth server */\n code: string;\n /** State parameter for CSRF protection */\n state?: string;\n}\n\n/**\n * PKCE (Proof Key for Code Exchange) parameters (RFC 7636)\n * Used to secure OAuth 2.0 authorization code flow for public clients\n */\nexport interface PkceParams {\n /** Code verifier - cryptographically random string (43-128 characters) */\n codeVerifier: string;\n /** Code challenge - derived from code verifier using challenge method */\n codeChallenge: string;\n /** Code challenge method - S256 (SHA-256) or plain */\n codeChallengeMethod: 'S256' | 'plain';\n}\n\n/**\n * OAuth token set with access and refresh tokens\n */\nexport interface TokenSet {\n /** Access token for API requests */\n accessToken: string;\n /** Refresh token for obtaining new access tokens */\n refreshToken: string;\n /** Timestamp when access token expires (milliseconds since epoch) */\n expiresAt: number;\n /** Scopes granted for this token set */\n scopes?: string[];\n /** Client ID used for DCR registration (stored for future use) */\n clientId?: string;\n /** Client secret used for DCR registration (stored for future use) */\n clientSecret?: string;\n}\n\n/**\n * OAuth 2.0 Protected Resource Metadata (RFC 9728)\n * Response from .well-known/oauth-protected-resource endpoint\n */\nexport interface ProtectedResourceMetadata {\n /** The protected resource identifier */\n resource: string;\n /** List of authorization server URLs that can issue tokens for this resource */\n authorization_servers: string[];\n /** Optional list of scopes supported by this resource */\n scopes_supported?: string[];\n /** Optional list of bearer token methods supported (header, query, body) */\n bearer_methods_supported?: string[];\n}\n\n/**\n * OAuth 2.0 Authorization Server Metadata (RFC 8414)\n * Response from .well-known/oauth-authorization-server endpoint\n */\nexport interface AuthorizationServerMetadata {\n /** The authorization server's issuer identifier */\n issuer?: string;\n /** URL of the authorization endpoint */\n authorization_endpoint?: string;\n /** URL of the token endpoint */\n token_endpoint?: string;\n /** URL of the client registration endpoint (DCR - RFC 7591) */\n registration_endpoint?: string;\n /** URL of the token introspection endpoint */\n introspection_endpoint?: string;\n /** List of OAuth scopes supported by the authorization server */\n scopes_supported?: string[];\n /** Response types supported (code, token, etc.) */\n response_types_supported?: string[];\n /** Grant types supported (authorization_code, refresh_token, etc.) */\n grant_types_supported?: string[];\n /** Token endpoint authentication methods supported */\n token_endpoint_auth_methods_supported?: string[];\n}\n\n/**\n * OAuth server capabilities discovered from .well-known endpoint\n */\nexport interface AuthCapabilities {\n /** Whether the server supports Dynamic Client Registration (RFC 7591) */\n supportsDcr: boolean;\n /** DCR client registration endpoint */\n registrationEndpoint?: string;\n /** OAuth authorization endpoint */\n authorizationEndpoint?: string;\n /** OAuth token endpoint */\n tokenEndpoint?: string;\n /** Token introspection endpoint */\n introspectionEndpoint?: string;\n /** Supported OAuth scopes */\n scopes?: string[];\n}\n\n/**\n * Client credentials from DCR registration\n */\nexport interface ClientCredentials {\n /** OAuth client ID */\n clientId: string;\n /** OAuth client secret */\n clientSecret: string;\n /** Timestamp when client was registered */\n issuedAt?: number;\n}\n\n/**\n * Options for DCR client registration\n */\nexport interface DcrRegistrationOptions {\n /** Client name to register */\n clientName?: string;\n /** Redirect URI for OAuth callback */\n redirectUri?: string;\n}\n\n/**\n * Options for OAuth authorization flow\n */\nexport interface OAuthFlowOptions {\n /** Port for OAuth callback listener (required - use get-port to find available port) */\n port: number;\n /** Redirect URI for OAuth callback (optional - will be built from port if not provided) */\n redirectUri?: string;\n /** OAuth scopes to request */\n scopes?: string[];\n /** Resource parameter (RFC 8707) - target resource server identifier */\n resource?: string;\n /** Enable PKCE (RFC 7636) - recommended for all clients, required for public clients */\n pkce?: boolean;\n /** Headless mode (don't open browser) */\n headless?: boolean;\n /** Timeout for callback (milliseconds) */\n timeout?: number;\n /** Optional logger for debug output (defaults to singleton logger) */\n logger?: import('../utils/logger.ts').Logger;\n}\n"],"names":[],"mappings":"AAAA;;CAEC,GAED;;CAEC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/client-helpers.ts"],"sourcesContent":["import type { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport type { RequestOptions } from '@modelcontextprotocol/sdk/shared/protocol.js';\nimport type { ToolArguments } from './connection/types.ts';\nimport { type NativeCallToolResponse, type NativeGetPromptResponse, type NativeReadResourceResponse, PromptResponseWrapper, ResourceResponseWrapper, ToolResponseWrapper } from './response-wrappers.ts';\n\nexport type PromptArguments = Record<string, string>;\n\ntype NativeCallToolParams = Parameters<Client['callTool']>;\ntype NativeCallToolReturn = ReturnType<Client['callTool']>;\n/**\n * Fetch-style wrapper returned by `ManagedClient.callTool()`.\n * @public\n */\nexport type WrappedCallToolReturn = Promise<ToolResponseWrapper>;\n\ntype NativeGetPromptParams = Parameters<Client['getPrompt']>;\ntype NativeGetPromptReturn = ReturnType<Client['getPrompt']>;\n/**\n * Fetch-style wrapper returned by `ManagedClient.getPrompt()`.\n * @public\n */\nexport type WrappedGetPromptReturn = Promise<PromptResponseWrapper>;\n\ntype NativeReadResourceParams = Parameters<Client['readResource']>;\ntype NativeReadResourceReturn = ReturnType<Client['readResource']>;\n/**\n * Fetch-style wrapper returned by `ManagedClient.readResource()`.\n * @public\n */\nexport type WrappedReadResourceReturn = Promise<ResourceResponseWrapper>;\n\n/**\n * Client returned by registry.connect() with convenience overloads for\n * calling tools, reading resources, and getting prompts using simple arguments.\n */\nexport type ManagedClient = Omit<Client, 'callTool' | 'getPrompt' | 'readResource'> & {\n /** Name of the server this client is connected to. */\n readonly serverName: string;\n /** Underlying MCP SDK client for advanced scenarios. */\n readonly nativeClient: Client;\n\n callTool(toolName: string, args?: ToolArguments, requestOptions?: RequestOptions): WrappedCallToolReturn;\n callTool(invocation: NativeCallToolParams[0], sessionId?: NativeCallToolParams[1], requestOptions?: NativeCallToolParams[2]): WrappedCallToolReturn;\n callToolRaw(toolName: string, args?: ToolArguments, requestOptions?: RequestOptions): NativeCallToolReturn;\n callToolRaw(invocation: NativeCallToolParams[0], sessionId?: NativeCallToolParams[1], requestOptions?: NativeCallToolParams[2]): NativeCallToolReturn;\n\n getPrompt(name: string, args?: PromptArguments, requestOptions?: NativeGetPromptParams[1]): WrappedGetPromptReturn;\n getPrompt(invocation: NativeGetPromptParams[0], requestOptions?: NativeGetPromptParams[1]): WrappedGetPromptReturn;\n getPromptRaw(name: string, args?: PromptArguments, requestOptions?: NativeGetPromptParams[1]): NativeGetPromptReturn;\n getPromptRaw(invocation: NativeGetPromptParams[0], requestOptions?: NativeGetPromptParams[1]): NativeGetPromptReturn;\n\n readResource(uri: string, requestOptions?: NativeReadResourceParams[1]): WrappedReadResourceReturn;\n readResource(request: NativeReadResourceParams[0], requestOptions?: NativeReadResourceParams[1]): WrappedReadResourceReturn;\n readResourceRaw(uri: string, requestOptions?: NativeReadResourceParams[1]): NativeReadResourceReturn;\n readResourceRaw(request: NativeReadResourceParams[0], requestOptions?: NativeReadResourceParams[1]): NativeReadResourceReturn;\n};\n\n/**\n * Enhance an MCP SDK client with convenience overloads.\n */\nexport function decorateClient(client: Client, metadata: { serverName: string }): ManagedClient {\n const enhanced = client as unknown as ManagedClient;\n\n Object.defineProperty(enhanced, 'serverName', {\n value: metadata.serverName,\n enumerable: true,\n configurable: false,\n writable: false,\n });\n Object.defineProperty(enhanced, 'nativeClient', {\n value: client,\n enumerable: false,\n configurable: false,\n writable: false,\n });\n\n const nativeCallTool = client.callTool.bind(client);\n const wrapCallTool = (promise: NativeCallToolReturn): WrappedCallToolReturn => promise.then((payload) => new ToolResponseWrapper(payload as NativeCallToolResponse));\n\n enhanced.callTool = ((nameOrInvocation: string | NativeCallToolParams[0], argsOrSession?: ToolArguments | NativeCallToolParams[1], requestOptions?: NativeCallToolParams[2]) => {\n if (typeof nameOrInvocation === 'string') {\n return wrapCallTool(nativeCallTool({ name: nameOrInvocation, arguments: (argsOrSession as ToolArguments) ?? {} }, undefined, requestOptions));\n }\n return wrapCallTool(nativeCallTool(nameOrInvocation, argsOrSession as NativeCallToolParams[1], requestOptions));\n }) as ManagedClient['callTool'];\n\n enhanced.callToolRaw = ((nameOrInvocation: string | NativeCallToolParams[0], argsOrSession?: ToolArguments | NativeCallToolParams[1], requestOptions?: NativeCallToolParams[2]) => {\n if (typeof nameOrInvocation === 'string') {\n return nativeCallTool({ name: nameOrInvocation, arguments: (argsOrSession as ToolArguments) ?? {} }, undefined, requestOptions);\n }\n return nativeCallTool(nameOrInvocation, argsOrSession as NativeCallToolParams[1], requestOptions);\n }) as ManagedClient['callToolRaw'];\n\n const nativeGetPrompt = client.getPrompt.bind(client);\n const wrapPrompt = (promise: NativeGetPromptReturn): WrappedGetPromptReturn => promise.then((payload) => new PromptResponseWrapper(payload as NativeGetPromptResponse));\n\n enhanced.getPrompt = ((nameOrParams: string | NativeGetPromptParams[0], argsOrOptions?: PromptArguments | NativeGetPromptParams[1], requestOptions?: NativeGetPromptParams[1]) => {\n if (typeof nameOrParams === 'string') {\n return wrapPrompt(nativeGetPrompt({ name: nameOrParams, ...(argsOrOptions ? { arguments: argsOrOptions as PromptArguments } : {}) }, requestOptions));\n }\n return wrapPrompt(nativeGetPrompt(nameOrParams, argsOrOptions as NativeGetPromptParams[1]));\n }) as ManagedClient['getPrompt'];\n\n enhanced.getPromptRaw = ((nameOrParams: string | NativeGetPromptParams[0], argsOrOptions?: PromptArguments | NativeGetPromptParams[1], requestOptions?: NativeGetPromptParams[1]) => {\n if (typeof nameOrParams === 'string') {\n return nativeGetPrompt({ name: nameOrParams, ...(argsOrOptions ? { arguments: argsOrOptions as PromptArguments } : {}) }, requestOptions);\n }\n return nativeGetPrompt(nameOrParams, argsOrOptions as NativeGetPromptParams[1]);\n }) as ManagedClient['getPromptRaw'];\n\n const nativeReadResource = client.readResource.bind(client);\n const wrapResource = (promise: NativeReadResourceReturn): WrappedReadResourceReturn => promise.then((payload) => new ResourceResponseWrapper(payload as NativeReadResourceResponse));\n\n enhanced.readResource = ((uriOrRequest: string | NativeReadResourceParams[0], requestOptions?: NativeReadResourceParams[1]) => {\n if (typeof uriOrRequest === 'string') {\n return wrapResource(nativeReadResource({ uri: uriOrRequest }, requestOptions));\n }\n return wrapResource(nativeReadResource(uriOrRequest, requestOptions));\n }) as ManagedClient['readResource'];\n\n enhanced.readResourceRaw = ((uriOrRequest: string | NativeReadResourceParams[0], requestOptions?: NativeReadResourceParams[1]) => {\n if (typeof uriOrRequest === 'string') {\n return nativeReadResource({ uri: uriOrRequest }, requestOptions);\n }\n return nativeReadResource(uriOrRequest, requestOptions);\n }) as ManagedClient['readResourceRaw'];\n\n return enhanced;\n}\n"],"names":["decorateClient","client","metadata","enhanced","Object","defineProperty","value","serverName","enumerable","configurable","writable","nativeCallTool","callTool","bind","wrapCallTool","promise","then","payload","ToolResponseWrapper","nameOrInvocation","argsOrSession","requestOptions","name","arguments","undefined","callToolRaw","nativeGetPrompt","getPrompt","wrapPrompt","PromptResponseWrapper","nameOrParams","argsOrOptions","getPromptRaw","nativeReadResource","readResource","wrapResource","ResourceResponseWrapper","uriOrRequest","uri","readResourceRaw"],"mappings":";;;;+BA4DgBA;;;eAAAA;;;kCAzDgK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyDzK,SAASA,eAAeC,MAAc,EAAEC,QAAgC;IAC7E,IAAMC,WAAWF;IAEjBG,OAAOC,cAAc,CAACF,UAAU,cAAc;QAC5CG,OAAOJ,SAASK,UAAU;QAC1BC,YAAY;QACZC,cAAc;QACdC,UAAU;IACZ;IACAN,OAAOC,cAAc,CAACF,UAAU,gBAAgB;QAC9CG,OAAOL;QACPO,YAAY;QACZC,cAAc;QACdC,UAAU;IACZ;IAEA,IAAMC,iBAAiBV,OAAOW,QAAQ,CAACC,IAAI,CAACZ;IAC5C,IAAMa,eAAe,SAACC;eAAyDA,QAAQC,IAAI,CAAC,SAACC;mBAAY,IAAIC,uCAAmB,CAACD;;;IAEjId,SAASS,QAAQ,GAAI,SAACO,kBAAoDC,eAAyDC;QACjI,IAAI,OAAOF,qBAAqB,UAAU;YACxC,OAAOL,aAAaH,eAAe;gBAAEW,MAAMH;gBAAkBI,SAAS,EAAGH,0BAAAA,2BAAAA,gBAAmC,CAAC;YAAE,GAAGI,WAAWH;QAC/H;QACA,OAAOP,aAAaH,eAAeQ,kBAAkBC,eAA0CC;IACjG;IAEAlB,SAASsB,WAAW,GAAI,SAACN,kBAAoDC,eAAyDC;QACpI,IAAI,OAAOF,qBAAqB,UAAU;YACxC,OAAOR,eAAe;gBAAEW,MAAMH;gBAAkBI,SAAS,EAAGH,0BAAAA,2BAAAA,gBAAmC,CAAC;YAAE,GAAGI,WAAWH;QAClH;QACA,OAAOV,eAAeQ,kBAAkBC,eAA0CC;IACpF;IAEA,IAAMK,kBAAkBzB,OAAO0B,SAAS,CAACd,IAAI,CAACZ;IAC9C,IAAM2B,aAAa,SAACb;eAA2DA,QAAQC,IAAI,CAAC,SAACC;mBAAY,IAAIY,yCAAqB,CAACZ;;;IAEnId,SAASwB,SAAS,GAAI,SAACG,cAAiDC,eAA4DV;QAClI,IAAI,OAAOS,iBAAiB,UAAU;YACpC,OAAOF,WAAWF,gBAAgB;gBAAEJ,MAAMQ;eAAkBC,gBAAgB;gBAAER,WAAWQ;YAAiC,IAAI,CAAC,IAAMV;QACvI;QACA,OAAOO,WAAWF,gBAAgBI,cAAcC;IAClD;IAEA5B,SAAS6B,YAAY,GAAI,SAACF,cAAiDC,eAA4DV;QACrI,IAAI,OAAOS,iBAAiB,UAAU;YACpC,OAAOJ,gBAAgB;gBAAEJ,MAAMQ;eAAkBC,gBAAgB;gBAAER,WAAWQ;YAAiC,IAAI,CAAC,IAAMV;QAC5H;QACA,OAAOK,gBAAgBI,cAAcC;IACvC;IAEA,IAAME,qBAAqBhC,OAAOiC,YAAY,CAACrB,IAAI,CAACZ;IACpD,IAAMkC,eAAe,SAACpB;eAAiEA,QAAQC,IAAI,CAAC,SAACC;mBAAY,IAAImB,2CAAuB,CAACnB;;;IAE7Id,SAAS+B,YAAY,GAAI,SAACG,cAAoDhB;QAC5E,IAAI,OAAOgB,iBAAiB,UAAU;YACpC,OAAOF,aAAaF,mBAAmB;gBAAEK,KAAKD;YAAa,GAAGhB;QAChE;QACA,OAAOc,aAAaF,mBAAmBI,cAAchB;IACvD;IAEAlB,SAASoC,eAAe,GAAI,SAACF,cAAoDhB;QAC/E,IAAI,OAAOgB,iBAAiB,UAAU;YACpC,OAAOJ,mBAAmB;gBAAEK,KAAKD;YAAa,GAAGhB;QACnD;QACA,OAAOY,mBAAmBI,cAAchB;IAC1C;IAEA,OAAOlB;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/config/server-loader.ts"],"sourcesContent":["/**\n * Load server definitions from .mcp.json (data-driven)\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { fileURLToPath } from 'url';\n\n// Get package root: dist/esm/lib -> ../../../ or dist/cjs/lib -> ../../../\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\nconst packageRoot = path.resolve(__dirname, '../../..');\n\nexport interface McpServerConfig {\n command: string;\n args: string[];\n env?: Record<string, string>;\n}\n\nexport interface MCPConfiguration {\n mcpServers: Record<string, McpServerConfig>;\n}\n\n/**\n * Load available servers from resources/.mcp.json\n */\nexport function loadAvailableServers(): MCPConfiguration {\n const configPath = path.join(packageRoot, 'resources/.mcp.json');\n\n if (!fs.existsSync(configPath)) {\n throw new Error(`Server configuration not found: ${configPath}`);\n }\n\n const content = fs.readFileSync(configPath, 'utf-8');\n return JSON.parse(content) as MCPConfiguration;\n}\n\n/**\n * Get list of all available server names\n */\nexport function getAllServerNames(): string[] {\n const config = loadAvailableServers();\n return Object.keys(config.mcpServers);\n}\n\n/**\n * Parse comma-separated server list or \"all\"\n */\nexport function parseServerList(input: string): string[] {\n const allServers = getAllServerNames();\n\n if (input === 'all') {\n return allServers;\n }\n\n const requested = input.split(',').map((s) => s.trim());\n const invalid = requested.filter((s) => !allServers.includes(s));\n\n if (invalid.length > 0) {\n throw new Error(`Invalid server names: ${invalid.join(', ')}.\\nAvailable: ${allServers.join(', ')}`);\n }\n\n return requested;\n}\n\n/**\n * Get server configuration by name\n */\nexport function getServerConfig(serverName: string): McpServerConfig {\n const config = loadAvailableServers();\n const serverConfig = config.mcpServers[serverName];\n\n if (!serverConfig) {\n throw new Error(`Unknown server: ${serverName}.\\nAvailable: ${getAllServerNames().join(', ')}`);\n }\n\n return serverConfig;\n}\n"],"names":["getAllServerNames","getServerConfig","loadAvailableServers","parseServerList","__filename","fileURLToPath","__dirname","path","dirname","packageRoot","resolve","configPath","join","fs","existsSync","Error","content","readFileSync","JSON","parse","config","Object","keys","mcpServers","input","allServers","requested","split","map","s","trim","invalid","filter","includes","length","serverName","serverConfig"],"mappings":"AAAA;;CAEC;;;;;;;;;;;QAsCeA;eAAAA;;QA4BAC;eAAAA;;QA1CAC;eAAAA;;QAsBAC;eAAAA;;;0DA5CI;4DACE;mBACQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAE9B,2EAA2E;AAC3E,IAAMC,cAAaC,IAAAA,kBAAa,EAAC;AACjC,IAAMC,YAAYC,MAAKC,OAAO,CAACJ;AAC/B,IAAMK,cAAcF,MAAKG,OAAO,CAACJ,WAAW;AAerC,SAASJ;IACd,IAAMS,aAAaJ,MAAKK,IAAI,CAACH,aAAa;IAE1C,IAAI,CAACI,IAAGC,UAAU,CAACH,aAAa;QAC9B,MAAM,IAAII,MAAM,AAAC,mCAA6C,OAAXJ;IACrD;IAEA,IAAMK,UAAUH,IAAGI,YAAY,CAACN,YAAY;IAC5C,OAAOO,KAAKC,KAAK,CAACH;AACpB;AAKO,SAAShB;IACd,IAAMoB,SAASlB;IACf,OAAOmB,OAAOC,IAAI,CAACF,OAAOG,UAAU;AACtC;AAKO,SAASpB,gBAAgBqB,KAAa;IAC3C,IAAMC,aAAazB;IAEnB,IAAIwB,UAAU,OAAO;QACnB,OAAOC;IACT;IAEA,IAAMC,YAAYF,MAAMG,KAAK,CAAC,KAAKC,GAAG,CAAC,SAACC;eAAMA,EAAEC,IAAI;;IACpD,IAAMC,UAAUL,UAAUM,MAAM,CAAC,SAACH;eAAM,CAACJ,WAAWQ,QAAQ,CAACJ;;IAE7D,IAAIE,QAAQG,MAAM,GAAG,GAAG;QACtB,MAAM,IAAInB,MAAM,AAAC,yBAA2DU,OAAnCM,QAAQnB,IAAI,CAAC,OAAM,kBAAsC,OAAtBa,WAAWb,IAAI,CAAC;IAC9F;IAEA,OAAOc;AACT;AAKO,SAASzB,gBAAgBkC,UAAkB;IAChD,IAAMf,SAASlB;IACf,IAAMkC,eAAehB,OAAOG,UAAU,CAACY,WAAW;IAElD,IAAI,CAACC,cAAc;QACjB,MAAM,IAAIrB,MAAM,AAAC,mBAA6Cf,OAA3BmC,YAAW,kBAA+C,OAA/BnC,oBAAoBY,IAAI,CAAC;IACzF;IAEA,OAAOwB;AACT"}
|
|
@@ -69,10 +69,8 @@ var validatorCache = null;
|
|
|
69
69
|
/**
|
|
70
70
|
* Get servers schema (loads once from bundled file, then caches)
|
|
71
71
|
*/ function getSchema() {
|
|
72
|
-
if (schemaCache)
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
var schemaPath = _path.join(packageRoot, 'schemas/servers.schema.json');
|
|
72
|
+
if (schemaCache) return schemaCache;
|
|
73
|
+
var schemaPath = _path.join(packageRoot, './schemas/servers.schema.json');
|
|
76
74
|
if (!_fs.existsSync(schemaPath)) {
|
|
77
75
|
throw new Error("Servers schema not found at: ".concat(schemaPath));
|
|
78
76
|
}
|
|
@@ -82,9 +80,7 @@ var validatorCache = null;
|
|
|
82
80
|
/**
|
|
83
81
|
* Get compiled AJV validator (creates once, then caches)
|
|
84
82
|
*/ function getValidator() {
|
|
85
|
-
if (validatorCache)
|
|
86
|
-
return validatorCache;
|
|
87
|
-
}
|
|
83
|
+
if (validatorCache) return validatorCache;
|
|
88
84
|
var schema = getSchema();
|
|
89
85
|
var ajv = new _ajv.Ajv({
|
|
90
86
|
allErrors: true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/client/src/config/validate-config.ts"],"sourcesContent":["import { Ajv, type ErrorObject } from 'ajv';\nimport addFormats from 'ajv-formats';\nimport * as fs from 'fs';\nimport moduleRoot from 'module-root-sync';\nimport * as path from 'path';\nimport * as url from 'url';\n\n// Import ajv-formats (CommonJS module - use createRequire for ESM compatibility)\nconst __dirname = path.dirname(typeof __filename !== 'undefined' ? __filename : url.fileURLToPath(import.meta.url));\nconst packageRoot = moduleRoot(__dirname);\n\n/**\n * Validation result for servers configuration\n */\nexport interface ValidationResult {\n valid: boolean;\n errors?: string[];\n warnings?: string[];\n}\n\n// Module-level cache for schema and validator\nlet schemaCache: object | null = null;\nlet validatorCache: ReturnType<Ajv['compile']> | null = null;\n\n/**\n * Get servers schema (loads once from bundled file, then caches)\n */\nfunction getSchema(): object {\n if (schemaCache) return schemaCache;\n\n const schemaPath = path.join(packageRoot, './schemas/servers.schema.json');\n if (!fs.existsSync(schemaPath)) {\n throw new Error(`Servers schema not found at: ${schemaPath}`);\n }\n\n schemaCache = JSON.parse(fs.readFileSync(schemaPath, 'utf8')) as object;\n return schemaCache;\n}\n\n/**\n * Get compiled AJV validator (creates once, then caches)\n */\nfunction getValidator(): ReturnType<Ajv['compile']> {\n if (validatorCache) return validatorCache;\n\n const schema = getSchema();\n const ajv = new Ajv({\n allErrors: true,\n verbose: true,\n strictSchema: false, // Allow non-standard keywords like \"example\"\n });\n\n // Add format validators (uri, email, etc.)\n addFormats(ajv);\n\n validatorCache = ajv.compile(schema);\n return validatorCache;\n}\n\n/**\n * Validate servers configuration against JSON Schema\n *\n * @param servers - Servers configuration object to validate (map of server names to configs)\n * @returns ValidationResult with valid flag, errors, and warnings\n */\nexport function validateServers(servers: unknown): ValidationResult {\n try {\n const validate = getValidator();\n const valid = validate(servers);\n\n if (!valid) {\n const errors =\n validate.errors?.map((e: ErrorObject) => {\n const path = e.instancePath || '(root)';\n return `${path}: ${e.message}`;\n }) || [];\n\n return { valid: false, errors };\n }\n\n const warnings: string[] = [];\n return { valid: true, warnings };\n } catch (err) {\n return {\n valid: false,\n errors: [`Configuration validation failed: ${(err as Error).message}`],\n };\n }\n}\n"],"names":["validateServers","__dirname","path","dirname","__filename","url","fileURLToPath","packageRoot","moduleRoot","schemaCache","validatorCache","getSchema","schemaPath","join","fs","existsSync","Error","JSON","parse","readFileSync","getValidator","schema","ajv","Ajv","allErrors","verbose","strictSchema","addFormats","compile","servers","validate","valid","errors","map","e","instancePath","message","warnings","err"],"mappings":";;;;+BAiEgBA;;;eAAAA;;;mBAjEsB;iEACf;0DACH;qEACG;4DACD;2DACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAErB,iFAAiF;AACjF,IAAMC,YAAYC,MAAKC,OAAO,CAAC,OAAOC,eAAe,cAAcA,aAAaC,KAAIC,aAAa,CAAC;AAClG,IAAMC,cAAcC,IAAAA,uBAAU,EAACP;AAW/B,8CAA8C;AAC9C,IAAIQ,cAA6B;AACjC,IAAIC,iBAAoD;AAExD;;CAEC,GACD,SAASC;IACP,IAAIF,aAAa,OAAOA;IAExB,IAAMG,aAAaV,MAAKW,IAAI,CAACN,aAAa;IAC1C,IAAI,CAACO,IAAGC,UAAU,CAACH,aAAa;QAC9B,MAAM,IAAII,MAAM,AAAC,gCAA0C,OAAXJ;IAClD;IAEAH,cAAcQ,KAAKC,KAAK,CAACJ,IAAGK,YAAY,CAACP,YAAY;IACrD,OAAOH;AACT;AAEA;;CAEC,GACD,SAASW;IACP,IAAIV,gBAAgB,OAAOA;IAE3B,IAAMW,SAASV;IACf,IAAMW,MAAM,IAAIC,QAAG,CAAC;QAClBC,WAAW;QACXC,SAAS;QACTC,cAAc;IAChB;IAEA,2CAA2C;IAC3CC,IAAAA,mBAAU,EAACL;IAEXZ,iBAAiBY,IAAIM,OAAO,CAACP;IAC7B,OAAOX;AACT;AAQO,SAASV,gBAAgB6B,OAAgB;IAC9C,IAAI;QACF,IAAMC,WAAWV;QACjB,IAAMW,QAAQD,SAASD;QAEvB,IAAI,CAACE,OAAO;gBAERD;YADF,IAAME,SACJF,EAAAA,mBAAAA,SAASE,MAAM,cAAfF,uCAAAA,iBAAiBG,GAAG,CAAC,SAACC;gBACpB,IAAMhC,SAAOgC,EAAEC,YAAY,IAAI;gBAC/B,OAAO,AAAC,GAAWD,OAAThC,QAAK,MAAc,OAAVgC,EAAEE,OAAO;YAC9B,OAAM,EAAE;YAEV,OAAO;gBAAEL,OAAO;gBAAOC,QAAAA;YAAO;QAChC;QAEA,IAAMK,WAAqB,EAAE;QAC7B,OAAO;YAAEN,OAAO;YAAMM,UAAAA;QAAS;IACjC,EAAE,OAAOC,KAAK;QACZ,OAAO;YACLP,OAAO;YACPC,QAAQ;gBAAE,oCAA0D,OAAvB,AAACM,IAAcF,OAAO;aAAG;QACxE;IACF;AACF"}
|