@elliotding/ai-agent-mcp 0.1.25 → 0.1.27

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.
Files changed (53) hide show
  1. package/package.json +4 -1
  2. package/.prompt-cache/cmd-cmd-client-sdk-ai-hub-generate-testcase.md +0 -101
  3. package/.prompt-cache/cmd-cmd-client-sdk-ai-hub-submit_zct_job.md +0 -158
  4. package/.prompt-cache/skill-skill-client-sdk-ai-hub-analyze-conf-status.md +0 -311
  5. package/.prompt-cache/skill-skill-client-sdk-ai-hub-analyze-sdk-log.md +0 -64
  6. package/.prompt-cache/skill-skill-client-sdk-ai-hub-analyze-zmb-log-errors.md +0 -84
  7. package/ai-resource-telemetry.json +0 -40
  8. package/src/api/cached-client.ts +0 -144
  9. package/src/api/client.ts +0 -697
  10. package/src/auth/index.ts +0 -11
  11. package/src/auth/middleware.ts +0 -244
  12. package/src/auth/permissions.ts +0 -323
  13. package/src/auth/token-validator.ts +0 -292
  14. package/src/cache/cache-manager.ts +0 -243
  15. package/src/cache/index.ts +0 -6
  16. package/src/cache/redis-client.ts +0 -249
  17. package/src/config/constants.ts +0 -33
  18. package/src/config/index.ts +0 -269
  19. package/src/filesystem/manager.ts +0 -235
  20. package/src/git/multi-source-manager.ts +0 -654
  21. package/src/git/operations.ts +0 -93
  22. package/src/index.ts +0 -157
  23. package/src/monitoring/health.ts +0 -132
  24. package/src/prompts/cache.ts +0 -140
  25. package/src/prompts/generator.ts +0 -143
  26. package/src/prompts/index.ts +0 -20
  27. package/src/prompts/manager.ts +0 -718
  28. package/src/resources/index.ts +0 -13
  29. package/src/resources/loader.ts +0 -563
  30. package/src/server/http.ts +0 -549
  31. package/src/server.ts +0 -206
  32. package/src/session/manager.ts +0 -296
  33. package/src/telemetry/index.ts +0 -10
  34. package/src/telemetry/manager.ts +0 -419
  35. package/src/tools/index.ts +0 -13
  36. package/src/tools/manage-subscription.ts +0 -388
  37. package/src/tools/registry.ts +0 -97
  38. package/src/tools/resolve-prompt-content.ts +0 -113
  39. package/src/tools/search-resources.ts +0 -185
  40. package/src/tools/sync-resources.ts +0 -829
  41. package/src/tools/track-usage.ts +0 -113
  42. package/src/tools/uninstall-resource.ts +0 -199
  43. package/src/tools/upload-resource.ts +0 -431
  44. package/src/transport/sse.ts +0 -308
  45. package/src/types/errors.ts +0 -146
  46. package/src/types/index.ts +0 -7
  47. package/src/types/mcp.ts +0 -61
  48. package/src/types/resources.ts +0 -141
  49. package/src/types/tools.ts +0 -305
  50. package/src/utils/cursor-paths.ts +0 -135
  51. package/src/utils/log-cleaner.ts +0 -92
  52. package/src/utils/logger.ts +0 -333
  53. package/src/utils/validation.ts +0 -262
package/src/api/client.ts DELETED
@@ -1,697 +0,0 @@
1
- /**
2
- * REST API Client
3
- * HTTP client for CSP Resource Server
4
- */
5
-
6
- import axios, { AxiosInstance, AxiosRequestConfig, AxiosError } from 'axios';
7
- import { config } from '../config';
8
- import { logger, logApiRequest, logApiError } from '../utils/logger';
9
- import { createAPIError } from '../types/errors';
10
-
11
- class APIClient {
12
- private client: AxiosInstance;
13
- private readonly maxRetries = 3;
14
- private readonly retryDelay = 1000; // 1 second
15
-
16
- constructor() {
17
- this.client = axios.create({
18
- baseURL: config.csp.apiBaseUrl,
19
- timeout: config.csp.timeout,
20
- headers: {
21
- 'Content-Type': 'application/json',
22
- 'User-Agent': `csp-ai-agent-mcp/0.2.0`,
23
- },
24
- });
25
-
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 — the token must come from the authenticated SSE
30
- // connection, not from environment variables.
31
- this.client.interceptors.request.use(
32
- (requestConfig) => {
33
- if (!requestConfig.headers.Authorization) {
34
- return Promise.reject(
35
- new Error(
36
- 'Authorization token is missing. ' +
37
- 'Ensure the MCP server is connected via SSE with a valid Bearer token in the Authorization header.'
38
- )
39
- );
40
- }
41
-
42
- // Enhanced request logging
43
- logger.debug(
44
- {
45
- type: 'api_request_start',
46
- method: requestConfig.method?.toUpperCase(),
47
- url: requestConfig.url,
48
- params: requestConfig.params,
49
- data: requestConfig.data ? JSON.stringify(requestConfig.data).substring(0, 500) : undefined,
50
- headers: this.sanitizeHeaders(requestConfig.headers as Record<string, string>),
51
- },
52
- `API Request: ${requestConfig.method?.toUpperCase()} ${requestConfig.url}`
53
- );
54
-
55
- // Record start time for duration calculation
56
- (requestConfig as any).startTime = Date.now();
57
-
58
- return requestConfig;
59
- },
60
- (error) => {
61
- logger.error({
62
- type: 'api_request_interceptor_error',
63
- error: error.message
64
- }, 'API request interceptor error');
65
- return Promise.reject(error);
66
- }
67
- );
68
-
69
- // Response interceptor for detailed logging
70
- this.client.interceptors.response.use(
71
- (response) => {
72
- const startTime = (response.config as any).startTime || Date.now();
73
- const duration = Date.now() - startTime;
74
- const method = response.config.method?.toUpperCase() || 'UNKNOWN';
75
- const url = response.config.url || 'unknown';
76
-
77
- // Enhanced response logging
78
- logApiRequest(
79
- method,
80
- url,
81
- response.status,
82
- duration,
83
- response.config.data,
84
- response.data,
85
- response.headers as Record<string, string>
86
- );
87
-
88
- return response;
89
- },
90
- (error: AxiosError) => {
91
- const startTime = (error.config as any)?.startTime || Date.now();
92
- const duration = Date.now() - startTime;
93
- const statusCode = error.response?.status;
94
- const method = error.config?.method?.toUpperCase() || 'UNKNOWN';
95
- const url = error.config?.url || 'unknown';
96
-
97
- // Enhanced error logging
98
- logApiError(
99
- method,
100
- url,
101
- error,
102
- error.config?.data,
103
- statusCode
104
- );
105
-
106
- // Log response details if available
107
- if (error.response) {
108
- logger.error(
109
- {
110
- type: 'api_response_error',
111
- method,
112
- url,
113
- status: statusCode,
114
- statusText: error.response.statusText,
115
- responseData: error.response.data ? JSON.stringify(error.response.data).substring(0, 1000) : undefined,
116
- duration,
117
- },
118
- `API Error Response: ${method} ${url} - ${statusCode}`
119
- );
120
- }
121
-
122
- return Promise.reject(error);
123
- }
124
- );
125
- }
126
-
127
- /**
128
- * Build an AxiosRequestConfig that carries a per-request user token.
129
- * Pass the result as the `config` argument to get/post/put/delete or merge it
130
- * into any existing request config so that the caller's token overrides the
131
- * server-level fallback set in the interceptor.
132
- *
133
- * Usage:
134
- * await apiClient.get('/some/path', apiClient.authConfig(userToken));
135
- * await apiClient.post('/some/path', body, apiClient.authConfig(userToken));
136
- */
137
- authConfig(token: string | undefined, extra?: AxiosRequestConfig): AxiosRequestConfig {
138
- if (!token) return extra ?? {};
139
- return {
140
- ...extra,
141
- headers: {
142
- ...(extra?.headers ?? {}),
143
- Authorization: `Bearer ${token}`,
144
- },
145
- };
146
- }
147
-
148
- /**
149
- * Sanitize headers to hide sensitive information
150
- */
151
- private sanitizeHeaders(headers: Record<string, string>): Record<string, string> {
152
- const sanitized = { ...headers };
153
- if (sanitized['Authorization'] || sanitized['authorization']) {
154
- const key = sanitized['Authorization'] ? 'Authorization' : 'authorization';
155
- const value = sanitized[key];
156
- if (value && value.startsWith('Bearer ')) {
157
- const token = value.substring(7);
158
- sanitized[key] = `Bearer ${token.substring(0, 10)}...${token.substring(token.length - 10)}`;
159
- }
160
- }
161
- return sanitized;
162
- }
163
-
164
- /**
165
- * Execute request with retry logic
166
- */
167
- private async executeWithRetry<T>(
168
- requestFn: () => Promise<T>,
169
- method: string,
170
- url: string,
171
- retryCount = 0
172
- ): Promise<T> {
173
- try {
174
- return await requestFn();
175
- } catch (error) {
176
- const isNetworkError =
177
- error instanceof AxiosError &&
178
- (!error.response || error.code === 'ECONNREFUSED' || error.code === 'ETIMEDOUT');
179
-
180
- if (isNetworkError && retryCount < this.maxRetries) {
181
- const delay = this.retryDelay * Math.pow(2, retryCount);
182
- logger.warn(
183
- {
184
- method,
185
- url,
186
- retryCount: retryCount + 1,
187
- maxRetries: this.maxRetries,
188
- delay,
189
- },
190
- `API request failed, retrying in ${delay}ms...`
191
- );
192
-
193
- await new Promise((resolve) => setTimeout(resolve, delay));
194
- return this.executeWithRetry(requestFn, method, url, retryCount + 1);
195
- }
196
-
197
- // Transform axios error to APIError
198
- if (error instanceof AxiosError) {
199
- throw createAPIError(
200
- method,
201
- url,
202
- error,
203
- error.response?.status,
204
- retryCount
205
- );
206
- }
207
-
208
- throw error;
209
- }
210
- }
211
-
212
- /**
213
- * GET request
214
- */
215
- async get<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
216
- return this.executeWithRetry(
217
- async () => {
218
- const response = await this.client.get<T>(url, config);
219
- return response.data;
220
- },
221
- 'GET',
222
- url
223
- );
224
- }
225
-
226
- /**
227
- * POST request
228
- */
229
- async post<T>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T> {
230
- return this.executeWithRetry(
231
- async () => {
232
- const response = await this.client.post<T>(url, data, config);
233
- return response.data;
234
- },
235
- 'POST',
236
- url
237
- );
238
- }
239
-
240
- /**
241
- * PUT request
242
- */
243
- async put<T>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<T> {
244
- return this.executeWithRetry(
245
- async () => {
246
- const response = await this.client.put<T>(url, data, config);
247
- return response.data;
248
- },
249
- 'PUT',
250
- url
251
- );
252
- }
253
-
254
- /**
255
- * DELETE request
256
- */
257
- async delete<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
258
- return this.executeWithRetry(
259
- async () => {
260
- const response = await this.client.delete<T>(url, config);
261
- return response.data;
262
- },
263
- 'DELETE',
264
- url
265
- );
266
- }
267
-
268
- //===========================================
269
- // CSP Resource Server API Endpoints
270
- //===========================================
271
-
272
- /**
273
- * Get subscription list
274
- *
275
- * @param params Query parameters for filtering subscriptions.
276
- * @param userToken Per-request token from the caller's mcp.json configuration.
277
- * When provided it overrides the server-level fallback token.
278
- */
279
- async getSubscriptions(
280
- params?: {
281
- scope?: 'general' | 'team' | 'user' | 'all';
282
- types?: string[];
283
- detail?: boolean;
284
- },
285
- userToken?: string
286
- ): Promise<{
287
- total: number;
288
- subscriptions: Array<{
289
- id: string;
290
- name: string;
291
- type: string;
292
- team: string;
293
- subscribed_at: string;
294
- auto_sync: boolean;
295
- resource: {
296
- version: string;
297
- hash: string;
298
- download_url: string;
299
- };
300
- }>;
301
- }> {
302
- const response = await this.get<{
303
- code: number;
304
- result: string;
305
- data: {
306
- total: number;
307
- subscriptions: Array<{
308
- id: string;
309
- name: string;
310
- type: string;
311
- team: string;
312
- subscribed_at: string;
313
- auto_sync: boolean;
314
- resource: {
315
- version: string;
316
- hash: string;
317
- download_url: string;
318
- };
319
- }>;
320
- };
321
- }>('/csp/api/resources/subscriptions', this.authConfig(userToken, { params }));
322
-
323
- if (!response.data) {
324
- throw new Error('Invalid API response: missing data field');
325
- }
326
-
327
- return response.data;
328
- }
329
-
330
- /**
331
- * Subscribe to resource
332
- *
333
- * @param userToken Per-request token from the caller's mcp.json configuration.
334
- */
335
- async subscribe(
336
- resourceIds: string[],
337
- autoSync = true,
338
- scope?: 'general' | 'team' | 'user',
339
- userToken?: string
340
- ): Promise<{
341
- success: boolean;
342
- subscriptions: Array<{
343
- id: string;
344
- name: string;
345
- type: string;
346
- subscribed_at: string;
347
- }>;
348
- }> {
349
- const response = await this.post<{
350
- code: number;
351
- result: string;
352
- data: {
353
- success?: boolean;
354
- subscriptions: Array<{
355
- id: string;
356
- name: string;
357
- type: string;
358
- subscribed_at: string;
359
- }>;
360
- };
361
- }>(
362
- '/csp/api/resources/subscriptions/add',
363
- { resource_ids: resourceIds, auto_sync: autoSync, scope },
364
- this.authConfig(userToken)
365
- );
366
-
367
- if (!response.data) {
368
- throw new Error('Invalid API response: missing data field');
369
- }
370
-
371
- return { success: true, subscriptions: response.data.subscriptions };
372
- }
373
-
374
- /**
375
- * Unsubscribe from resource
376
- *
377
- * @param userToken Per-request token from the caller's mcp.json configuration.
378
- */
379
- async unsubscribe(resourceIds: string | string[], userToken?: string): Promise<void> {
380
- const ids = Array.isArray(resourceIds) ? resourceIds : [resourceIds];
381
- const response = await this.delete<{
382
- code: number;
383
- result: string;
384
- data: { removed_count: number };
385
- }>('/csp/api/resources/subscriptions/remove', this.authConfig(userToken, { data: { resource_ids: ids } }));
386
-
387
- if (!response.data) {
388
- throw new Error('Invalid API response: missing data field');
389
- }
390
- }
391
-
392
- /**
393
- * Search resources
394
- *
395
- * @param userToken Per-request token from the caller's mcp.json configuration.
396
- */
397
- async searchResources(
398
- params: {
399
- keyword: string;
400
- team?: string;
401
- type?: string;
402
- detail?: boolean;
403
- page?: number;
404
- page_size?: number;
405
- },
406
- userToken?: string
407
- ): Promise<{
408
- total: number;
409
- page?: number;
410
- page_size?: number;
411
- results: Array<{
412
- id: string;
413
- name: string;
414
- type: string;
415
- team: string;
416
- version: string;
417
- description: string;
418
- score: number;
419
- is_subscribed: boolean;
420
- metadata?: {
421
- module: string;
422
- tags: string[];
423
- author: string;
424
- created_at: string;
425
- updated_at: string;
426
- downloads: number;
427
- };
428
- }>;
429
- }> {
430
- const response = await this.get<{
431
- code: number;
432
- result: string;
433
- data: {
434
- total: number;
435
- page?: number;
436
- page_size?: number;
437
- results: Array<{
438
- id: string;
439
- name: string;
440
- type: string;
441
- team: string;
442
- version: string;
443
- description: string;
444
- score?: number;
445
- is_subscribed?: boolean;
446
- download_url?: string;
447
- metadata?: {
448
- module: string;
449
- tags: string[];
450
- author: string;
451
- created_at: string;
452
- updated_at: string;
453
- downloads: number;
454
- };
455
- }>;
456
- };
457
- }>('/csp/api/resources/search', this.authConfig(userToken, { params }));
458
-
459
- if (!response.data) {
460
- throw new Error('Invalid API response: missing data field');
461
- }
462
-
463
- return {
464
- total: response.data.total,
465
- page: response.data.page,
466
- page_size: response.data.page_size,
467
- results: response.data.results.map((r) => ({
468
- ...r,
469
- score: r.score || 0,
470
- is_subscribed: r.is_subscribed || false,
471
- })),
472
- };
473
- }
474
-
475
- /**
476
- * Download resource — returns all files for the resource.
477
- *
478
- * GET /csp/api/resources/download/{id}
479
- * Response: { data: { resource_id, name, type, version, hash, files: [{path, content}] } }
480
- *
481
- * files[].path is the relative path within the resource directory.
482
- * Single-file resources (command, rule) have exactly one element.
483
- * Multi-file resources (skill, mcp) have all their files included.
484
- *
485
- * @param userToken Per-request token from the caller's mcp.json configuration.
486
- */
487
- async downloadResource(
488
- resourceId: string,
489
- userToken?: string
490
- ): Promise<{
491
- resource_id: string;
492
- name: string;
493
- type: string;
494
- version: string;
495
- hash: string;
496
- files: Array<{ path: string; content: string }>;
497
- }> {
498
- const response = await this.get<{
499
- code: number;
500
- result: string;
501
- data: {
502
- resource_id: string;
503
- name: string;
504
- type: string;
505
- version: string;
506
- hash: string;
507
- files: Array<{ path: string; content: string }>;
508
- };
509
- }>(`/csp/api/resources/download/${resourceId}`, this.authConfig(userToken));
510
- return response.data;
511
- }
512
-
513
- /**
514
- * Get resource detail
515
- *
516
- * @param userToken Per-request token from the caller's mcp.json configuration.
517
- */
518
- async getResourceDetail(
519
- resourceId: string,
520
- userToken?: string
521
- ): Promise<{
522
- id: string;
523
- name: string;
524
- type: string;
525
- team: string;
526
- version: string;
527
- description: string;
528
- metadata: {
529
- module: string;
530
- tags: string[];
531
- author: string;
532
- created_at: string;
533
- updated_at: string;
534
- downloads: number;
535
- file_size: number;
536
- hash: string;
537
- };
538
- download_url: string;
539
- }> {
540
- return this.get(`/csp/api/resources/${resourceId}`, this.authConfig(userToken));
541
- }
542
-
543
- /**
544
- * Stage resource files for upload (Step 1 of two-step upload flow).
545
- *
546
- * POST /csp/api/resources/upload
547
- * Body: { type, name, files: [{ path, content }] }
548
- * Response: { upload_id, status, expires_at, preview_url }
549
- *
550
- * The server validates path traversal, total size (< 10 MB), and name conflicts.
551
- * All file types are supported — mcp packages may include .py, .js, package.json, etc.
552
- *
553
- * @param userToken Per-request token from the caller's mcp.json configuration.
554
- */
555
- async uploadResourceFiles(
556
- params: {
557
- type: string;
558
- name: string;
559
- files: Array<{ path: string; content: string }>;
560
- target_source?: string;
561
- force?: boolean;
562
- },
563
- userToken?: string
564
- ): Promise<{
565
- upload_id: string;
566
- status: string;
567
- expires_at: string;
568
- preview_url?: string;
569
- }> {
570
- const resp = await this.post<{
571
- code: number;
572
- result: string;
573
- data: { upload_id: string; status: string; expires_at: string; preview_url?: string };
574
- }>('/csp/api/resources/upload', params, this.authConfig(userToken));
575
- return resp.data;
576
- }
577
-
578
- /**
579
- * Finalize staged upload — triggers Git commit (Step 2 of two-step upload flow).
580
- *
581
- * POST /csp/api/resources/finalize
582
- * Body: { upload_id, commit_message }
583
- * Response: { resource_id, version, url, commit_hash, download_url }
584
- *
585
- * @param userToken Per-request token from the caller's mcp.json configuration.
586
- */
587
- async finalizeResourceUpload(
588
- uploadId: string,
589
- commitMessage: string,
590
- userToken?: string
591
- ): Promise<{
592
- resource_id: string;
593
- version?: string;
594
- url?: string;
595
- commit_hash?: string;
596
- download_url?: string;
597
- }> {
598
- const resp = await this.post<{
599
- code: number;
600
- result: string;
601
- data: {
602
- resource_id: string;
603
- version?: string;
604
- url?: string;
605
- commit_hash?: string;
606
- download_url?: string;
607
- };
608
- }>(
609
- '/csp/api/resources/finalize',
610
- { upload_id: uploadId, commit_message: commitMessage },
611
- this.authConfig(userToken)
612
- );
613
- return resp.data;
614
- }
615
-
616
- /**
617
- * Report AI resource usage telemetry to the server.
618
- *
619
- * POST /csp/api/resources/telemetry
620
- * Body: { client_version, reported_at, events[], subscribed_rules[], configured_mcps[] }
621
- *
622
- * Called by TelemetryManager.flush() every ~10 seconds and on reconnect.
623
- * Throws on non-2xx so the caller can apply retry logic.
624
- *
625
- * jira_id in each event entry is optional — it is only present when the user
626
- * explicitly passed a Jira ID during the Prompt invocation.
627
- *
628
- * @param payload Telemetry report payload built by TelemetryManager
629
- * @param userToken Per-request Bearer token from the caller's mcp.json configuration
630
- */
631
- async reportTelemetry(
632
- payload: {
633
- client_version: string;
634
- reported_at: string;
635
- events: Array<{
636
- resource_id: string;
637
- resource_type: string;
638
- resource_name: string;
639
- invocation_count: number;
640
- first_invoked_at: string;
641
- last_invoked_at: string;
642
- /** Optional Jira Issue ID (e.g. "PROJ-12345"). Absent when not provided. */
643
- jira_id?: string;
644
- }>;
645
- subscribed_rules: Array<{
646
- resource_id: string;
647
- resource_name: string;
648
- subscribed_at: string;
649
- }>;
650
- configured_mcps: Array<{
651
- resource_id: string;
652
- resource_name: string;
653
- configured_at: string;
654
- }>;
655
- },
656
- userToken: string
657
- ): Promise<void> {
658
- await this.post<{ code: number; result: string; data: unknown }>(
659
- '/csp/api/resources/telemetry',
660
- payload,
661
- this.authConfig(userToken)
662
- );
663
- }
664
-
665
- /**
666
- * @deprecated Use uploadResourceFiles() + finalizeResourceUpload() instead.
667
- */
668
- async uploadResource(params: {
669
- name: string;
670
- type: string;
671
- team: string;
672
- description?: string;
673
- tags?: string[];
674
- }): Promise<{
675
- upload_id: string;
676
- upload_url: string;
677
- expires_at: string;
678
- }> {
679
- return this.post('/csp/api/resources/upload', params);
680
- }
681
-
682
- /**
683
- * @deprecated Use finalizeResourceUpload() instead.
684
- */
685
- async finalizeUpload(uploadId: string, hash: string): Promise<{
686
- resource_id: string;
687
- status: string;
688
- }> {
689
- return this.post('/csp/api/resources/finalize', {
690
- upload_id: uploadId,
691
- hash,
692
- });
693
- }
694
- }
695
-
696
- // Export singleton instance
697
- export const apiClient = new APIClient();
package/src/auth/index.ts DELETED
@@ -1,11 +0,0 @@
1
- /**
2
- * Authentication Module
3
- * Exports all authentication and authorization utilities
4
- */
5
-
6
- // Token validation via CSP API (primary method)
7
- export * from './token-validator';
8
-
9
- // Permissions and middleware
10
- export * from './permissions';
11
- export * from './middleware';