@elliotding/ai-agent-mcp 0.1.1 → 0.1.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/api/client.d.ts +37 -8
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +68 -40
- package/dist/api/client.js.map +1 -1
- package/dist/tools/manage-subscription.d.ts +4 -0
- package/dist/tools/manage-subscription.d.ts.map +1 -1
- package/dist/tools/manage-subscription.js +20 -6
- package/dist/tools/manage-subscription.js.map +1 -1
- package/dist/tools/search-resources.d.ts +4 -0
- package/dist/tools/search-resources.d.ts.map +1 -1
- package/dist/tools/search-resources.js +7 -1
- package/dist/tools/search-resources.js.map +1 -1
- package/dist/tools/sync-resources.d.ts +4 -0
- package/dist/tools/sync-resources.d.ts.map +1 -1
- package/dist/tools/sync-resources.js +9 -2
- package/dist/tools/sync-resources.js.map +1 -1
- package/dist/tools/upload-resource.d.ts +4 -0
- package/dist/tools/upload-resource.d.ts.map +1 -1
- package/dist/tools/upload-resource.js +120 -23
- package/dist/tools/upload-resource.js.map +1 -1
- package/dist/types/tools.d.ts +17 -2
- package/dist/types/tools.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/api/client.ts +142 -71
- package/src/tools/manage-subscription.ts +25 -6
- package/src/tools/search-resources.ts +15 -5
- package/src/tools/sync-resources.ts +13 -5
- package/src/tools/upload-resource.ts +157 -31
- package/src/types/tools.ts +17 -2
package/src/api/client.ts
CHANGED
|
@@ -23,11 +23,21 @@ class APIClient {
|
|
|
23
23
|
},
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
-
// Request interceptor for authentication and logging
|
|
26
|
+
// Request interceptor for authentication and logging.
|
|
27
|
+
// Every request MUST carry a per-request Authorization header supplied by
|
|
28
|
+
// the caller via authConfig(userToken). If none is present the request is
|
|
29
|
+
// rejected immediately with a clear error so the caller knows they must
|
|
30
|
+
// configure their token in mcp.json under env.CSP_API_TOKEN.
|
|
27
31
|
this.client.interceptors.request.use(
|
|
28
32
|
(requestConfig) => {
|
|
29
|
-
if (
|
|
30
|
-
|
|
33
|
+
if (!requestConfig.headers.Authorization) {
|
|
34
|
+
return Promise.reject(
|
|
35
|
+
new Error(
|
|
36
|
+
'CSP_API_TOKEN is not configured. ' +
|
|
37
|
+
'Please add your personal CSP token to your mcp.json under ' +
|
|
38
|
+
'mcpServers["csp-ai-agent"].env.CSP_API_TOKEN and restart Cursor.'
|
|
39
|
+
)
|
|
40
|
+
);
|
|
31
41
|
}
|
|
32
42
|
|
|
33
43
|
// Enhanced request logging
|
|
@@ -115,6 +125,27 @@ class APIClient {
|
|
|
115
125
|
);
|
|
116
126
|
}
|
|
117
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Build an AxiosRequestConfig that carries a per-request user token.
|
|
130
|
+
* Pass the result as the `config` argument to get/post/put/delete or merge it
|
|
131
|
+
* into any existing request config so that the caller's token overrides the
|
|
132
|
+
* server-level fallback set in the interceptor.
|
|
133
|
+
*
|
|
134
|
+
* Usage:
|
|
135
|
+
* await apiClient.get('/some/path', apiClient.authConfig(userToken));
|
|
136
|
+
* await apiClient.post('/some/path', body, apiClient.authConfig(userToken));
|
|
137
|
+
*/
|
|
138
|
+
authConfig(token: string | undefined, extra?: AxiosRequestConfig): AxiosRequestConfig {
|
|
139
|
+
if (!token) return extra ?? {};
|
|
140
|
+
return {
|
|
141
|
+
...extra,
|
|
142
|
+
headers: {
|
|
143
|
+
...(extra?.headers ?? {}),
|
|
144
|
+
Authorization: `Bearer ${token}`,
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
118
149
|
/**
|
|
119
150
|
* Sanitize headers to hide sensitive information
|
|
120
151
|
*/
|
|
@@ -241,12 +272,19 @@ class APIClient {
|
|
|
241
272
|
|
|
242
273
|
/**
|
|
243
274
|
* Get subscription list
|
|
275
|
+
*
|
|
276
|
+
* @param params Query parameters for filtering subscriptions.
|
|
277
|
+
* @param userToken Per-request token from the caller's mcp.json configuration.
|
|
278
|
+
* When provided it overrides the server-level fallback token.
|
|
244
279
|
*/
|
|
245
|
-
async getSubscriptions(
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
280
|
+
async getSubscriptions(
|
|
281
|
+
params?: {
|
|
282
|
+
scope?: 'general' | 'team' | 'user' | 'all';
|
|
283
|
+
types?: string[];
|
|
284
|
+
detail?: boolean;
|
|
285
|
+
},
|
|
286
|
+
userToken?: string
|
|
287
|
+
): Promise<{
|
|
250
288
|
total: number;
|
|
251
289
|
subscriptions: Array<{
|
|
252
290
|
id: string;
|
|
@@ -281,27 +319,29 @@ class APIClient {
|
|
|
281
319
|
};
|
|
282
320
|
}>;
|
|
283
321
|
};
|
|
284
|
-
}>('/csp/api/resources/subscriptions', { params });
|
|
285
|
-
|
|
286
|
-
// Extract data from CSP API response format
|
|
322
|
+
}>('/csp/api/resources/subscriptions', this.authConfig(userToken, { params }));
|
|
323
|
+
|
|
287
324
|
if (!response.data) {
|
|
288
325
|
throw new Error('Invalid API response: missing data field');
|
|
289
326
|
}
|
|
290
|
-
|
|
327
|
+
|
|
291
328
|
return response.data;
|
|
292
329
|
}
|
|
293
330
|
|
|
294
331
|
/**
|
|
295
332
|
* Subscribe to resource
|
|
333
|
+
*
|
|
334
|
+
* @param userToken Per-request token from the caller's mcp.json configuration.
|
|
296
335
|
*/
|
|
297
336
|
async subscribe(
|
|
298
|
-
resourceIds: string[],
|
|
337
|
+
resourceIds: string[],
|
|
299
338
|
autoSync = true,
|
|
300
|
-
scope?: 'general' | 'team' | 'user'
|
|
339
|
+
scope?: 'general' | 'team' | 'user',
|
|
340
|
+
userToken?: string
|
|
301
341
|
): Promise<{
|
|
302
342
|
success: boolean;
|
|
303
|
-
subscriptions: Array<{
|
|
304
|
-
id: string;
|
|
343
|
+
subscriptions: Array<{
|
|
344
|
+
id: string;
|
|
305
345
|
name: string;
|
|
306
346
|
type: string;
|
|
307
347
|
subscribed_at: string;
|
|
@@ -312,44 +352,39 @@ class APIClient {
|
|
|
312
352
|
result: string;
|
|
313
353
|
data: {
|
|
314
354
|
success?: boolean;
|
|
315
|
-
subscriptions: Array<{
|
|
316
|
-
id: string;
|
|
355
|
+
subscriptions: Array<{
|
|
356
|
+
id: string;
|
|
317
357
|
name: string;
|
|
318
358
|
type: string;
|
|
319
359
|
subscribed_at: string;
|
|
320
360
|
}>;
|
|
321
361
|
};
|
|
322
|
-
}>(
|
|
323
|
-
|
|
324
|
-
auto_sync: autoSync,
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
362
|
+
}>(
|
|
363
|
+
'/csp/api/resources/subscriptions/add',
|
|
364
|
+
{ resource_ids: resourceIds, auto_sync: autoSync, scope },
|
|
365
|
+
this.authConfig(userToken)
|
|
366
|
+
);
|
|
367
|
+
|
|
328
368
|
if (!response.data) {
|
|
329
369
|
throw new Error('Invalid API response: missing data field');
|
|
330
370
|
}
|
|
331
|
-
|
|
332
|
-
return {
|
|
333
|
-
success: true,
|
|
334
|
-
subscriptions: response.data.subscriptions
|
|
335
|
-
};
|
|
371
|
+
|
|
372
|
+
return { success: true, subscriptions: response.data.subscriptions };
|
|
336
373
|
}
|
|
337
374
|
|
|
338
375
|
/**
|
|
339
376
|
* Unsubscribe from resource
|
|
377
|
+
*
|
|
378
|
+
* @param userToken Per-request token from the caller's mcp.json configuration.
|
|
340
379
|
*/
|
|
341
|
-
async unsubscribe(resourceIds: string | string[]): Promise<void> {
|
|
342
|
-
// Support batch unsubscribe
|
|
380
|
+
async unsubscribe(resourceIds: string | string[], userToken?: string): Promise<void> {
|
|
343
381
|
const ids = Array.isArray(resourceIds) ? resourceIds : [resourceIds];
|
|
344
382
|
const response = await this.delete<{
|
|
345
383
|
code: number;
|
|
346
384
|
result: string;
|
|
347
385
|
data: { removed_count: number };
|
|
348
|
-
}>('/csp/api/resources/subscriptions/remove', {
|
|
349
|
-
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
// Just validate response, no need to return anything
|
|
386
|
+
}>('/csp/api/resources/subscriptions/remove', this.authConfig(userToken, { data: { resource_ids: ids } }));
|
|
387
|
+
|
|
353
388
|
if (!response.data) {
|
|
354
389
|
throw new Error('Invalid API response: missing data field');
|
|
355
390
|
}
|
|
@@ -357,15 +392,20 @@ class APIClient {
|
|
|
357
392
|
|
|
358
393
|
/**
|
|
359
394
|
* Search resources
|
|
395
|
+
*
|
|
396
|
+
* @param userToken Per-request token from the caller's mcp.json configuration.
|
|
360
397
|
*/
|
|
361
|
-
async searchResources(
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
398
|
+
async searchResources(
|
|
399
|
+
params: {
|
|
400
|
+
keyword: string;
|
|
401
|
+
team?: string;
|
|
402
|
+
type?: string;
|
|
403
|
+
detail?: boolean;
|
|
404
|
+
page?: number;
|
|
405
|
+
page_size?: number;
|
|
406
|
+
},
|
|
407
|
+
userToken?: string
|
|
408
|
+
): Promise<{
|
|
369
409
|
total: number;
|
|
370
410
|
page?: number;
|
|
371
411
|
page_size?: number;
|
|
@@ -415,22 +455,21 @@ class APIClient {
|
|
|
415
455
|
};
|
|
416
456
|
}>;
|
|
417
457
|
};
|
|
418
|
-
}>('/csp/api/resources/search', { params });
|
|
419
|
-
|
|
420
|
-
// Extract data from CSP API response format
|
|
458
|
+
}>('/csp/api/resources/search', this.authConfig(userToken, { params }));
|
|
459
|
+
|
|
421
460
|
if (!response.data) {
|
|
422
461
|
throw new Error('Invalid API response: missing data field');
|
|
423
462
|
}
|
|
424
|
-
|
|
463
|
+
|
|
425
464
|
return {
|
|
426
465
|
total: response.data.total,
|
|
427
466
|
page: response.data.page,
|
|
428
467
|
page_size: response.data.page_size,
|
|
429
|
-
results: response.data.results.map(r => ({
|
|
468
|
+
results: response.data.results.map((r) => ({
|
|
430
469
|
...r,
|
|
431
470
|
score: r.score || 0,
|
|
432
|
-
is_subscribed: r.is_subscribed || false
|
|
433
|
-
}))
|
|
471
|
+
is_subscribed: r.is_subscribed || false,
|
|
472
|
+
})),
|
|
434
473
|
};
|
|
435
474
|
}
|
|
436
475
|
|
|
@@ -443,8 +482,13 @@ class APIClient {
|
|
|
443
482
|
* files[].path is the relative path within the resource directory.
|
|
444
483
|
* Single-file resources (command, rule) have exactly one element.
|
|
445
484
|
* Multi-file resources (skill, mcp) have all their files included.
|
|
485
|
+
*
|
|
486
|
+
* @param userToken Per-request token from the caller's mcp.json configuration.
|
|
446
487
|
*/
|
|
447
|
-
async downloadResource(
|
|
488
|
+
async downloadResource(
|
|
489
|
+
resourceId: string,
|
|
490
|
+
userToken?: string
|
|
491
|
+
): Promise<{
|
|
448
492
|
resource_id: string;
|
|
449
493
|
name: string;
|
|
450
494
|
type: string;
|
|
@@ -463,14 +507,19 @@ class APIClient {
|
|
|
463
507
|
hash: string;
|
|
464
508
|
files: Array<{ path: string; content: string }>;
|
|
465
509
|
};
|
|
466
|
-
}>(`/csp/api/resources/download/${resourceId}
|
|
510
|
+
}>(`/csp/api/resources/download/${resourceId}`, this.authConfig(userToken));
|
|
467
511
|
return response.data;
|
|
468
512
|
}
|
|
469
513
|
|
|
470
514
|
/**
|
|
471
515
|
* Get resource detail
|
|
516
|
+
*
|
|
517
|
+
* @param userToken Per-request token from the caller's mcp.json configuration.
|
|
472
518
|
*/
|
|
473
|
-
async getResourceDetail(
|
|
519
|
+
async getResourceDetail(
|
|
520
|
+
resourceId: string,
|
|
521
|
+
userToken?: string
|
|
522
|
+
): Promise<{
|
|
474
523
|
id: string;
|
|
475
524
|
name: string;
|
|
476
525
|
type: string;
|
|
@@ -489,7 +538,7 @@ class APIClient {
|
|
|
489
538
|
};
|
|
490
539
|
download_url: string;
|
|
491
540
|
}> {
|
|
492
|
-
return this.get(`/csp/api/resources/${resourceId}
|
|
541
|
+
return this.get(`/csp/api/resources/${resourceId}`, this.authConfig(userToken));
|
|
493
542
|
}
|
|
494
543
|
|
|
495
544
|
/**
|
|
@@ -501,22 +550,29 @@ class APIClient {
|
|
|
501
550
|
*
|
|
502
551
|
* The server validates path traversal, total size (< 10 MB), and name conflicts.
|
|
503
552
|
* All file types are supported — mcp packages may include .py, .js, package.json, etc.
|
|
553
|
+
*
|
|
554
|
+
* @param userToken Per-request token from the caller's mcp.json configuration.
|
|
504
555
|
*/
|
|
505
|
-
async uploadResourceFiles(
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
556
|
+
async uploadResourceFiles(
|
|
557
|
+
params: {
|
|
558
|
+
type: string;
|
|
559
|
+
name: string;
|
|
560
|
+
files: Array<{ path: string; content: string }>;
|
|
561
|
+
target_source?: string;
|
|
562
|
+
force?: boolean;
|
|
563
|
+
},
|
|
564
|
+
userToken?: string
|
|
565
|
+
): Promise<{
|
|
512
566
|
upload_id: string;
|
|
513
567
|
status: string;
|
|
514
568
|
expires_at: string;
|
|
515
569
|
preview_url?: string;
|
|
516
570
|
}> {
|
|
517
|
-
const resp = await this.post<{
|
|
518
|
-
|
|
519
|
-
|
|
571
|
+
const resp = await this.post<{
|
|
572
|
+
code: number;
|
|
573
|
+
result: string;
|
|
574
|
+
data: { upload_id: string; status: string; expires_at: string; preview_url?: string };
|
|
575
|
+
}>('/csp/api/resources/upload', params, this.authConfig(userToken));
|
|
520
576
|
return resp.data;
|
|
521
577
|
}
|
|
522
578
|
|
|
@@ -526,19 +582,34 @@ class APIClient {
|
|
|
526
582
|
* POST /csp/api/resources/finalize
|
|
527
583
|
* Body: { upload_id, commit_message }
|
|
528
584
|
* Response: { resource_id, version, url, commit_hash, download_url }
|
|
585
|
+
*
|
|
586
|
+
* @param userToken Per-request token from the caller's mcp.json configuration.
|
|
529
587
|
*/
|
|
530
|
-
async finalizeResourceUpload(
|
|
588
|
+
async finalizeResourceUpload(
|
|
589
|
+
uploadId: string,
|
|
590
|
+
commitMessage: string,
|
|
591
|
+
userToken?: string
|
|
592
|
+
): Promise<{
|
|
531
593
|
resource_id: string;
|
|
532
594
|
version?: string;
|
|
533
595
|
url?: string;
|
|
534
596
|
commit_hash?: string;
|
|
535
597
|
download_url?: string;
|
|
536
598
|
}> {
|
|
537
|
-
const resp = await this.post<{
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
599
|
+
const resp = await this.post<{
|
|
600
|
+
code: number;
|
|
601
|
+
result: string;
|
|
602
|
+
data: {
|
|
603
|
+
resource_id: string;
|
|
604
|
+
version?: string;
|
|
605
|
+
url?: string;
|
|
606
|
+
commit_hash?: string;
|
|
607
|
+
download_url?: string;
|
|
608
|
+
};
|
|
609
|
+
}>(
|
|
610
|
+
'/csp/api/resources/finalize',
|
|
611
|
+
{ upload_id: uploadId, commit_message: commitMessage },
|
|
612
|
+
this.authConfig(userToken)
|
|
542
613
|
);
|
|
543
614
|
return resp.data;
|
|
544
615
|
}
|
|
@@ -37,7 +37,9 @@ export async function manageSubscription(params: unknown): Promise<ToolResult<Ma
|
|
|
37
37
|
// Subscribe to resources
|
|
38
38
|
const subResult = await apiClient.subscribe(
|
|
39
39
|
typedParams.resource_ids,
|
|
40
|
-
typedParams.auto_sync
|
|
40
|
+
typedParams.auto_sync,
|
|
41
|
+
undefined,
|
|
42
|
+
typedParams.user_token
|
|
41
43
|
);
|
|
42
44
|
|
|
43
45
|
logger.info({ count: subResult.subscriptions.length }, 'Resources subscribed successfully');
|
|
@@ -50,7 +52,11 @@ export async function manageSubscription(params: unknown): Promise<ToolResult<Ma
|
|
|
50
52
|
|
|
51
53
|
if (shouldAutoSync && subResult.subscriptions.length > 0) {
|
|
52
54
|
logger.info({ resourceIds: typedParams.resource_ids }, 'Auto-syncing newly subscribed resources...');
|
|
53
|
-
const syncResult = await syncResources({
|
|
55
|
+
const syncResult = await syncResources({
|
|
56
|
+
mode: 'incremental',
|
|
57
|
+
scope: typedParams.scope || 'global',
|
|
58
|
+
user_token: typedParams.user_token,
|
|
59
|
+
});
|
|
54
60
|
if (syncResult.success && syncResult.data) {
|
|
55
61
|
const sd = syncResult.data;
|
|
56
62
|
syncSummary = `Auto-sync: ${sd.summary.synced} synced, ${sd.summary.cached} cached, ${sd.summary.failed} failed`;
|
|
@@ -97,7 +103,7 @@ export async function manageSubscription(params: unknown): Promise<ToolResult<Ma
|
|
|
97
103
|
logger.debug({ resourceIds: typedParams.resource_ids }, 'Unsubscribing from resources...');
|
|
98
104
|
|
|
99
105
|
// Cancel server-side subscription
|
|
100
|
-
await apiClient.unsubscribe(typedParams.resource_ids);
|
|
106
|
+
await apiClient.unsubscribe(typedParams.resource_ids, typedParams.user_token);
|
|
101
107
|
logger.info({ count: typedParams.resource_ids.length }, 'Server-side subscriptions removed');
|
|
102
108
|
|
|
103
109
|
// Uninstall local files and MCP config for each resource
|
|
@@ -156,7 +162,7 @@ export async function manageSubscription(params: unknown): Promise<ToolResult<Ma
|
|
|
156
162
|
logger.debug({ scope: typedParams.scope || 'all' }, 'Listing subscriptions...');
|
|
157
163
|
|
|
158
164
|
// Get subscriptions list
|
|
159
|
-
const subs = await apiClient.getSubscriptions({});
|
|
165
|
+
const subs = await apiClient.getSubscriptions({}, typedParams.user_token);
|
|
160
166
|
|
|
161
167
|
result = {
|
|
162
168
|
action: 'list',
|
|
@@ -188,7 +194,9 @@ export async function manageSubscription(params: unknown): Promise<ToolResult<Ma
|
|
|
188
194
|
|
|
189
195
|
const batchSubResult = await apiClient.subscribe(
|
|
190
196
|
typedParams.resource_ids,
|
|
191
|
-
typedParams.auto_sync
|
|
197
|
+
typedParams.auto_sync,
|
|
198
|
+
undefined,
|
|
199
|
+
typedParams.user_token
|
|
192
200
|
);
|
|
193
201
|
|
|
194
202
|
logger.info({ count: batchSubResult.subscriptions.length }, 'Batch subscription completed');
|
|
@@ -201,7 +209,11 @@ export async function manageSubscription(params: unknown): Promise<ToolResult<Ma
|
|
|
201
209
|
|
|
202
210
|
if (shouldBatchAutoSync && batchSubResult.subscriptions.length > 0) {
|
|
203
211
|
logger.info({ count: batchSubResult.subscriptions.length }, 'Auto-syncing batch subscribed resources...');
|
|
204
|
-
const batchSyncResult = await syncResources({
|
|
212
|
+
const batchSyncResult = await syncResources({
|
|
213
|
+
mode: 'incremental',
|
|
214
|
+
scope: typedParams.scope || 'global',
|
|
215
|
+
user_token: typedParams.user_token,
|
|
216
|
+
});
|
|
205
217
|
if (batchSyncResult.success && batchSyncResult.data) {
|
|
206
218
|
const sd = batchSyncResult.data;
|
|
207
219
|
batchSyncSummary = `Auto-sync: ${sd.summary.synced} synced, ${sd.summary.cached} cached, ${sd.summary.failed} failed`;
|
|
@@ -325,6 +337,13 @@ export const manageSubscriptionTool = {
|
|
|
325
337
|
description: 'Enable update notifications',
|
|
326
338
|
default: true,
|
|
327
339
|
},
|
|
340
|
+
user_token: {
|
|
341
|
+
type: 'string',
|
|
342
|
+
description:
|
|
343
|
+
'CSP API token for the current user. Read this from the CSP_API_TOKEN environment ' +
|
|
344
|
+
'variable configured in the user\'s mcp.json. When provided, this token is used ' +
|
|
345
|
+
'for all CSP API calls in this request instead of the server-level fallback token.',
|
|
346
|
+
},
|
|
328
347
|
},
|
|
329
348
|
required: ['action'],
|
|
330
349
|
},
|
|
@@ -84,11 +84,14 @@ export async function searchResources(params: unknown): Promise<ToolResult<Searc
|
|
|
84
84
|
// Search via API
|
|
85
85
|
logger.debug({ team: typedParams.team, type: typedParams.type, keyword: typedParams.keyword }, 'Searching resources...');
|
|
86
86
|
|
|
87
|
-
const searchResults = await apiClient.searchResources(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
const searchResults = await apiClient.searchResources(
|
|
88
|
+
{
|
|
89
|
+
team: typedParams.team,
|
|
90
|
+
type: typedParams.type,
|
|
91
|
+
keyword: typedParams.keyword,
|
|
92
|
+
},
|
|
93
|
+
typedParams.user_token
|
|
94
|
+
);
|
|
92
95
|
|
|
93
96
|
// Check subscription and installation status for each result
|
|
94
97
|
const enhancedResults = await Promise.all(
|
|
@@ -170,6 +173,13 @@ export const searchResourcesTool = {
|
|
|
170
173
|
type: 'string',
|
|
171
174
|
description: 'Search keyword (searches in name, description, tags)',
|
|
172
175
|
},
|
|
176
|
+
user_token: {
|
|
177
|
+
type: 'string',
|
|
178
|
+
description:
|
|
179
|
+
'CSP API token for the current user. Read this from the CSP_API_TOKEN environment ' +
|
|
180
|
+
'variable configured in the user\'s mcp.json. When provided, this token is used ' +
|
|
181
|
+
'for all CSP API calls in this request instead of the server-level fallback token.',
|
|
182
|
+
},
|
|
173
183
|
},
|
|
174
184
|
required: ['keyword'],
|
|
175
185
|
},
|
|
@@ -303,9 +303,10 @@ export async function syncResources(params: unknown): Promise<ToolResult<SyncRes
|
|
|
303
303
|
logToolStep('sync_resources', 'Tool invocation started', { params: typedParams });
|
|
304
304
|
|
|
305
305
|
try {
|
|
306
|
-
const mode
|
|
307
|
-
const scope
|
|
308
|
-
const types
|
|
306
|
+
const mode = typedParams.mode || 'incremental';
|
|
307
|
+
const scope = typedParams.scope || 'global';
|
|
308
|
+
const types = typedParams.types;
|
|
309
|
+
const userToken = typedParams.user_token;
|
|
309
310
|
|
|
310
311
|
logToolStep('sync_resources', 'Parameters validated', { mode, scope, types });
|
|
311
312
|
|
|
@@ -313,7 +314,7 @@ export async function syncResources(params: unknown): Promise<ToolResult<SyncRes
|
|
|
313
314
|
logToolStep('sync_resources', 'Step 1: Fetching subscriptions from API', { scope, types });
|
|
314
315
|
const t1 = Date.now();
|
|
315
316
|
|
|
316
|
-
const subscriptions = await apiClient.getSubscriptions({ types });
|
|
317
|
+
const subscriptions = await apiClient.getSubscriptions({ types }, userToken);
|
|
317
318
|
|
|
318
319
|
logToolStep('sync_resources', 'Subscriptions fetched', {
|
|
319
320
|
total: subscriptions.total,
|
|
@@ -405,7 +406,7 @@ export async function syncResources(params: unknown): Promise<ToolResult<SyncRes
|
|
|
405
406
|
resourceType: sub.type,
|
|
406
407
|
});
|
|
407
408
|
const tDl = Date.now();
|
|
408
|
-
const downloadResult = await apiClient.downloadResource(sub.id);
|
|
409
|
+
const downloadResult = await apiClient.downloadResource(sub.id, userToken);
|
|
409
410
|
logToolStep('sync_resources', 'Download complete', {
|
|
410
411
|
resourceId: sub.id,
|
|
411
412
|
fileCount: downloadResult.files.length,
|
|
@@ -656,6 +657,13 @@ export const syncResourcesTool = {
|
|
|
656
657
|
type: 'array',
|
|
657
658
|
description: 'Filter by resource types (empty = all types)',
|
|
658
659
|
},
|
|
660
|
+
user_token: {
|
|
661
|
+
type: 'string',
|
|
662
|
+
description:
|
|
663
|
+
'CSP API token for the current user. Read this from the CSP_API_TOKEN environment ' +
|
|
664
|
+
'variable configured in the user\'s mcp.json. When provided, this token is used ' +
|
|
665
|
+
'for all CSP API calls in this request instead of the server-level fallback token.',
|
|
666
|
+
},
|
|
659
667
|
},
|
|
660
668
|
},
|
|
661
669
|
handler: syncResources,
|