@elliotding/ai-agent-mcp 0.1.24 → 0.1.26

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 (233) hide show
  1. package/README.md +27 -0
  2. package/package.json +4 -1
  3. package/.prompt-cache/cmd-cmd-client-sdk-ai-hub-generate-testcase.md +0 -101
  4. package/.prompt-cache/cmd-cmd-client-sdk-ai-hub-submit_zct_job.md +0 -158
  5. package/.prompt-cache/skill-skill-client-sdk-ai-hub-analyze-conf-status.md +0 -311
  6. package/.prompt-cache/skill-skill-client-sdk-ai-hub-analyze-sdk-log.md +0 -64
  7. package/.prompt-cache/skill-skill-client-sdk-ai-hub-analyze-zmb-log-errors.md +0 -84
  8. package/ai-resource-telemetry.json +0 -40
  9. package/dist/api/cached-client.d.ts +0 -48
  10. package/dist/api/cached-client.d.ts.map +0 -1
  11. package/dist/api/cached-client.js +0 -126
  12. package/dist/api/cached-client.js.map +0 -1
  13. package/dist/api/client.d.ts +0 -281
  14. package/dist/api/client.d.ts.map +0 -1
  15. package/dist/api/client.js +0 -371
  16. package/dist/api/client.js.map +0 -1
  17. package/dist/auth/index.d.ts +0 -8
  18. package/dist/auth/index.d.ts.map +0 -1
  19. package/dist/auth/index.js +0 -26
  20. package/dist/auth/index.js.map +0 -1
  21. package/dist/auth/middleware.d.ts +0 -36
  22. package/dist/auth/middleware.d.ts.map +0 -1
  23. package/dist/auth/middleware.js +0 -194
  24. package/dist/auth/middleware.js.map +0 -1
  25. package/dist/auth/permissions.d.ts +0 -60
  26. package/dist/auth/permissions.d.ts.map +0 -1
  27. package/dist/auth/permissions.js +0 -262
  28. package/dist/auth/permissions.js.map +0 -1
  29. package/dist/auth/token-validator.d.ts +0 -52
  30. package/dist/auth/token-validator.d.ts.map +0 -1
  31. package/dist/auth/token-validator.js +0 -215
  32. package/dist/auth/token-validator.js.map +0 -1
  33. package/dist/cache/cache-manager.d.ts +0 -49
  34. package/dist/cache/cache-manager.d.ts.map +0 -1
  35. package/dist/cache/cache-manager.js +0 -191
  36. package/dist/cache/cache-manager.js.map +0 -1
  37. package/dist/cache/index.d.ts +0 -6
  38. package/dist/cache/index.d.ts.map +0 -1
  39. package/dist/cache/index.js +0 -12
  40. package/dist/cache/index.js.map +0 -1
  41. package/dist/cache/redis-client.d.ts +0 -45
  42. package/dist/cache/redis-client.d.ts.map +0 -1
  43. package/dist/cache/redis-client.js +0 -210
  44. package/dist/cache/redis-client.js.map +0 -1
  45. package/dist/config/constants.d.ts +0 -28
  46. package/dist/config/constants.d.ts.map +0 -1
  47. package/dist/config/constants.js +0 -31
  48. package/dist/config/constants.js.map +0 -1
  49. package/dist/config/index.d.ts +0 -71
  50. package/dist/config/index.d.ts.map +0 -1
  51. package/dist/config/index.js +0 -190
  52. package/dist/config/index.js.map +0 -1
  53. package/dist/filesystem/manager.d.ts +0 -45
  54. package/dist/filesystem/manager.d.ts.map +0 -1
  55. package/dist/filesystem/manager.js +0 -246
  56. package/dist/filesystem/manager.js.map +0 -1
  57. package/dist/git/multi-source-manager.d.ts +0 -78
  58. package/dist/git/multi-source-manager.d.ts.map +0 -1
  59. package/dist/git/multi-source-manager.js +0 -577
  60. package/dist/git/multi-source-manager.js.map +0 -1
  61. package/dist/git/operations.d.ts +0 -27
  62. package/dist/git/operations.d.ts.map +0 -1
  63. package/dist/git/operations.js +0 -83
  64. package/dist/git/operations.js.map +0 -1
  65. package/dist/index.d.ts +0 -6
  66. package/dist/index.d.ts.map +0 -1
  67. package/dist/index.js +0 -122
  68. package/dist/index.js.map +0 -1
  69. package/dist/monitoring/health.d.ts +0 -35
  70. package/dist/monitoring/health.d.ts.map +0 -1
  71. package/dist/monitoring/health.js +0 -105
  72. package/dist/monitoring/health.js.map +0 -1
  73. package/dist/prompts/cache.d.ts +0 -69
  74. package/dist/prompts/cache.d.ts.map +0 -1
  75. package/dist/prompts/cache.js +0 -163
  76. package/dist/prompts/cache.js.map +0 -1
  77. package/dist/prompts/generator.d.ts +0 -49
  78. package/dist/prompts/generator.d.ts.map +0 -1
  79. package/dist/prompts/generator.js +0 -160
  80. package/dist/prompts/generator.js.map +0 -1
  81. package/dist/prompts/index.d.ts +0 -13
  82. package/dist/prompts/index.d.ts.map +0 -1
  83. package/dist/prompts/index.js +0 -24
  84. package/dist/prompts/index.js.map +0 -1
  85. package/dist/prompts/manager.d.ts +0 -169
  86. package/dist/prompts/manager.d.ts.map +0 -1
  87. package/dist/prompts/manager.js +0 -488
  88. package/dist/prompts/manager.js.map +0 -1
  89. package/dist/resources/index.d.ts +0 -6
  90. package/dist/resources/index.d.ts.map +0 -1
  91. package/dist/resources/index.js +0 -10
  92. package/dist/resources/index.js.map +0 -1
  93. package/dist/resources/loader.d.ts +0 -88
  94. package/dist/resources/loader.d.ts.map +0 -1
  95. package/dist/resources/loader.js +0 -492
  96. package/dist/resources/loader.js.map +0 -1
  97. package/dist/server/http.d.ts +0 -57
  98. package/dist/server/http.d.ts.map +0 -1
  99. package/dist/server/http.js +0 -435
  100. package/dist/server/http.js.map +0 -1
  101. package/dist/server.d.ts +0 -13
  102. package/dist/server.d.ts.map +0 -1
  103. package/dist/server.js +0 -200
  104. package/dist/server.js.map +0 -1
  105. package/dist/session/manager.d.ts +0 -91
  106. package/dist/session/manager.d.ts.map +0 -1
  107. package/dist/session/manager.js +0 -251
  108. package/dist/session/manager.js.map +0 -1
  109. package/dist/telemetry/index.d.ts +0 -3
  110. package/dist/telemetry/index.d.ts.map +0 -1
  111. package/dist/telemetry/index.js +0 -7
  112. package/dist/telemetry/index.js.map +0 -1
  113. package/dist/telemetry/manager.d.ts +0 -151
  114. package/dist/telemetry/manager.d.ts.map +0 -1
  115. package/dist/telemetry/manager.js +0 -367
  116. package/dist/telemetry/manager.js.map +0 -1
  117. package/dist/tools/index.d.ts +0 -12
  118. package/dist/tools/index.d.ts.map +0 -1
  119. package/dist/tools/index.js +0 -28
  120. package/dist/tools/index.js.map +0 -1
  121. package/dist/tools/manage-subscription.d.ts +0 -47
  122. package/dist/tools/manage-subscription.d.ts.map +0 -1
  123. package/dist/tools/manage-subscription.js +0 -314
  124. package/dist/tools/manage-subscription.js.map +0 -1
  125. package/dist/tools/registry.d.ts +0 -40
  126. package/dist/tools/registry.d.ts.map +0 -1
  127. package/dist/tools/registry.js +0 -85
  128. package/dist/tools/registry.js.map +0 -1
  129. package/dist/tools/search-resources.d.ts +0 -35
  130. package/dist/tools/search-resources.d.ts.map +0 -1
  131. package/dist/tools/search-resources.js +0 -159
  132. package/dist/tools/search-resources.js.map +0 -1
  133. package/dist/tools/sync-resources.d.ts +0 -54
  134. package/dist/tools/sync-resources.d.ts.map +0 -1
  135. package/dist/tools/sync-resources.js +0 -733
  136. package/dist/tools/sync-resources.js.map +0 -1
  137. package/dist/tools/track-usage.d.ts +0 -63
  138. package/dist/tools/track-usage.d.ts.map +0 -1
  139. package/dist/tools/track-usage.js +0 -90
  140. package/dist/tools/track-usage.js.map +0 -1
  141. package/dist/tools/uninstall-resource.d.ts +0 -30
  142. package/dist/tools/uninstall-resource.d.ts.map +0 -1
  143. package/dist/tools/uninstall-resource.js +0 -174
  144. package/dist/tools/uninstall-resource.js.map +0 -1
  145. package/dist/tools/upload-resource.d.ts +0 -81
  146. package/dist/tools/upload-resource.d.ts.map +0 -1
  147. package/dist/tools/upload-resource.js +0 -393
  148. package/dist/tools/upload-resource.js.map +0 -1
  149. package/dist/transport/sse.d.ts +0 -29
  150. package/dist/transport/sse.d.ts.map +0 -1
  151. package/dist/transport/sse.js +0 -271
  152. package/dist/transport/sse.js.map +0 -1
  153. package/dist/types/errors.d.ts +0 -60
  154. package/dist/types/errors.d.ts.map +0 -1
  155. package/dist/types/errors.js +0 -112
  156. package/dist/types/errors.js.map +0 -1
  157. package/dist/types/index.d.ts +0 -7
  158. package/dist/types/index.d.ts.map +0 -1
  159. package/dist/types/index.js +0 -23
  160. package/dist/types/index.js.map +0 -1
  161. package/dist/types/mcp.d.ts +0 -50
  162. package/dist/types/mcp.d.ts.map +0 -1
  163. package/dist/types/mcp.js +0 -6
  164. package/dist/types/mcp.js.map +0 -1
  165. package/dist/types/resources.d.ts +0 -109
  166. package/dist/types/resources.d.ts.map +0 -1
  167. package/dist/types/resources.js +0 -7
  168. package/dist/types/resources.js.map +0 -1
  169. package/dist/types/tools.d.ts +0 -235
  170. package/dist/types/tools.d.ts.map +0 -1
  171. package/dist/types/tools.js +0 -6
  172. package/dist/types/tools.js.map +0 -1
  173. package/dist/utils/cursor-paths.d.ts +0 -84
  174. package/dist/utils/cursor-paths.d.ts.map +0 -1
  175. package/dist/utils/cursor-paths.js +0 -166
  176. package/dist/utils/cursor-paths.js.map +0 -1
  177. package/dist/utils/log-cleaner.d.ts +0 -18
  178. package/dist/utils/log-cleaner.d.ts.map +0 -1
  179. package/dist/utils/log-cleaner.js +0 -112
  180. package/dist/utils/log-cleaner.js.map +0 -1
  181. package/dist/utils/logger.d.ts +0 -59
  182. package/dist/utils/logger.d.ts.map +0 -1
  183. package/dist/utils/logger.js +0 -292
  184. package/dist/utils/logger.js.map +0 -1
  185. package/dist/utils/validation.d.ts +0 -58
  186. package/dist/utils/validation.d.ts.map +0 -1
  187. package/dist/utils/validation.js +0 -214
  188. package/dist/utils/validation.js.map +0 -1
  189. package/src/api/cached-client.ts +0 -144
  190. package/src/api/client.ts +0 -697
  191. package/src/auth/index.ts +0 -11
  192. package/src/auth/middleware.ts +0 -244
  193. package/src/auth/permissions.ts +0 -323
  194. package/src/auth/token-validator.ts +0 -292
  195. package/src/cache/cache-manager.ts +0 -243
  196. package/src/cache/index.ts +0 -6
  197. package/src/cache/redis-client.ts +0 -249
  198. package/src/config/constants.ts +0 -33
  199. package/src/config/index.ts +0 -269
  200. package/src/filesystem/manager.ts +0 -235
  201. package/src/git/multi-source-manager.ts +0 -654
  202. package/src/git/operations.ts +0 -93
  203. package/src/index.ts +0 -157
  204. package/src/monitoring/health.ts +0 -132
  205. package/src/prompts/cache.ts +0 -140
  206. package/src/prompts/generator.ts +0 -143
  207. package/src/prompts/index.ts +0 -20
  208. package/src/prompts/manager.ts +0 -613
  209. package/src/resources/index.ts +0 -13
  210. package/src/resources/loader.ts +0 -563
  211. package/src/server/http.ts +0 -549
  212. package/src/server.ts +0 -204
  213. package/src/session/manager.ts +0 -296
  214. package/src/telemetry/index.ts +0 -10
  215. package/src/telemetry/manager.ts +0 -419
  216. package/src/tools/index.ts +0 -12
  217. package/src/tools/manage-subscription.ts +0 -385
  218. package/src/tools/registry.ts +0 -97
  219. package/src/tools/search-resources.ts +0 -185
  220. package/src/tools/sync-resources.ts +0 -827
  221. package/src/tools/track-usage.ts +0 -113
  222. package/src/tools/uninstall-resource.ts +0 -199
  223. package/src/tools/upload-resource.ts +0 -431
  224. package/src/transport/sse.ts +0 -308
  225. package/src/types/errors.ts +0 -146
  226. package/src/types/index.ts +0 -7
  227. package/src/types/mcp.ts +0 -61
  228. package/src/types/resources.ts +0 -141
  229. package/src/types/tools.ts +0 -284
  230. package/src/utils/cursor-paths.ts +0 -135
  231. package/src/utils/log-cleaner.ts +0 -92
  232. package/src/utils/logger.ts +0 -333
  233. package/src/utils/validation.ts +0 -262
@@ -1,385 +0,0 @@
1
- /**
2
- * manage_subscription Tool
3
- * Manage resource subscriptions
4
- */
5
-
6
- import { logger, logToolCall } from '../utils/logger';
7
- import { apiClient } from '../api/client';
8
- import { MCPServerError, createValidationError } from '../types/errors';
9
- import type { ManageSubscriptionParams, ManageSubscriptionResult, ToolResult } from '../types/tools';
10
- import { syncResources } from './sync-resources';
11
- import { uninstallResource } from './uninstall-resource';
12
- import { promptManager } from '../prompts/index.js';
13
-
14
- export async function manageSubscription(params: unknown): Promise<ToolResult<ManageSubscriptionResult>> {
15
- const startTime = Date.now();
16
-
17
- // Type assertion for params
18
- const typedParams = params as ManageSubscriptionParams;
19
-
20
- logger.info({ tool: 'manage_subscription', params }, 'manage_subscription called');
21
-
22
- try {
23
- let result: ManageSubscriptionResult;
24
-
25
- switch (typedParams.action) {
26
- case 'subscribe': {
27
- // Validate resource_ids
28
- if (!typedParams.resource_ids || typedParams.resource_ids.length === 0) {
29
- throw createValidationError(
30
- 'resource_ids',
31
- 'array',
32
- 'resource_ids is required for subscribe action'
33
- );
34
- }
35
-
36
- logger.debug({ resourceIds: typedParams.resource_ids, autoSync: typedParams.auto_sync }, 'Subscribing to resources...');
37
-
38
- // Subscribe to resources
39
- const subResult = await apiClient.subscribe(
40
- typedParams.resource_ids,
41
- typedParams.auto_sync,
42
- undefined,
43
- typedParams.user_token
44
- );
45
-
46
- logger.info({ count: subResult.subscriptions.length }, 'Resources subscribed successfully');
47
-
48
- // Auto-sync newly subscribed resources immediately (default: true)
49
- const shouldAutoSync = typedParams.auto_sync !== false;
50
- let syncSummary: string | undefined;
51
- let syncDetails: Array<{ id: string; name: string; action: string }> | undefined;
52
- let pendingSetup: unknown[] | undefined;
53
-
54
- if (shouldAutoSync && subResult.subscriptions.length > 0) {
55
- logger.info({ resourceIds: typedParams.resource_ids }, 'Auto-syncing newly subscribed resources...');
56
- const syncResult = await syncResources({
57
- mode: 'incremental',
58
- scope: typedParams.scope || 'global',
59
- user_token: typedParams.user_token,
60
- });
61
- if (syncResult.success && syncResult.data) {
62
- const sd = syncResult.data;
63
- syncSummary = `Auto-sync: ${sd.summary.synced} synced, ${sd.summary.cached} cached, ${sd.summary.failed} failed`;
64
- syncDetails = sd.details.map(d => ({ id: d.id, name: d.name, action: d.action }));
65
- if (sd.pending_setup && sd.pending_setup.length > 0) {
66
- pendingSetup = sd.pending_setup;
67
- }
68
- logger.info({ summary: sd.summary }, 'Auto-sync after subscribe completed');
69
- } else {
70
- logger.warn({ error: syncResult.error }, 'Auto-sync after subscribe failed, subscription still recorded');
71
- syncSummary = 'Auto-sync failed — run sync_resources manually if needed';
72
- }
73
- }
74
-
75
- result = {
76
- action: 'subscribe',
77
- success: true,
78
- subscriptions: subResult.subscriptions.map(sub => ({
79
- id: sub.id,
80
- name: sub.name,
81
- type: sub.type,
82
- subscribed_at: sub.subscribed_at,
83
- })),
84
- message: [
85
- `Successfully subscribed to ${subResult.subscriptions.length} resource${subResult.subscriptions.length > 1 ? 's' : ''}.`,
86
- syncSummary,
87
- ].filter(Boolean).join(' '),
88
- ...(syncDetails ? { sync_details: syncDetails } : {}),
89
- ...(pendingSetup ? { pending_setup: pendingSetup } : {}),
90
- };
91
- break;
92
- }
93
-
94
- case 'unsubscribe': {
95
- // Validate resource_ids
96
- if (!typedParams.resource_ids || typedParams.resource_ids.length === 0) {
97
- throw createValidationError(
98
- 'resource_ids',
99
- 'array',
100
- 'resource_ids is required for unsubscribe action'
101
- );
102
- }
103
-
104
- logger.debug({ resourceIds: typedParams.resource_ids }, 'Unsubscribing from resources...');
105
-
106
- // Build a resource_id → type map from the current subscription list so
107
- // uninstall actions can be scoped precisely to rule vs mcp resources.
108
- let idToType: Map<string, string> = new Map();
109
- try {
110
- const currentSubs = await apiClient.getSubscriptions({}, typedParams.user_token);
111
- for (const s of currentSubs.subscriptions) {
112
- idToType.set(s.id, s.type);
113
- }
114
- } catch (e) {
115
- logger.warn({ error: (e as Error).message }, 'Could not fetch subscriptions for type resolution — uninstall will emit both rule+mcp actions as fallback');
116
- }
117
-
118
- // Cancel server-side subscription
119
- await apiClient.unsubscribe(typedParams.resource_ids, typedParams.user_token);
120
- logger.info({ count: typedParams.resource_ids.length }, 'Server-side subscriptions removed');
121
-
122
- // Uninstall local files and MCP config for each resource.
123
- // For Command/Skill: unregister MCP Prompt instead of deleting local files.
124
- const uninstallResults: Array<{ id: string; removed: boolean; detail: string }> = [];
125
- for (const resourceId of typedParams.resource_ids) {
126
- // Determine if this is a Command or Skill by checking the prompt registry.
127
- // API resource IDs are UUIDs (e.g. "0ccd800f..."), NOT prefixed with "cmd-"/"skill-".
128
- // Check whether any registered prompt for this user matches the resource_id.
129
- const matchedPromptName = promptManager.promptNames(typedParams.user_token ?? '').find(
130
- (name) => {
131
- // Prompt names are "<type>/<resource_name>"; check by looking up the registered meta.
132
- const registered = promptManager.getByPromptName(name, typedParams.user_token ?? '');
133
- return registered?.meta?.resource_id === resourceId;
134
- },
135
- );
136
- if (matchedPromptName) {
137
- const parts = matchedPromptName.split('/');
138
- const resourceType = (parts[0] ?? 'command') as 'command' | 'skill';
139
- const resourceName = parts.slice(1).join('/') || matchedPromptName;
140
- promptManager.unregisterPrompt(resourceId, resourceType, resourceName, typedParams.user_token ?? '');
141
- uninstallResults.push({ id: resourceId, removed: true, detail: `Unregistered MCP Prompt for "${resourceName}"` });
142
- logger.info({ resourceId, resourceType, matchedPromptName }, 'MCP Prompt unregistered on unsubscribe');
143
- continue;
144
- }
145
- // Use the last segment of the resource ID as the search pattern
146
- // e.g. "mcp-client-sdk-ai-hub-jenkins" → "jenkins"
147
- // "rule-csp-elliotTest" → "elliotTest"
148
- const namePart = resourceId.split('-').slice(-1)[0] ||
149
- resourceId.split('-').slice(-2).join('-') ||
150
- resourceId;
151
-
152
- // Try full name match first (e.g. "elliotTest"), fallback to last segment
153
- const patternsToTry = Array.from(new Set([
154
- resourceId, // full id
155
- resourceId.replace(/^(skill|cmd|rule|mcp)-[^-]+-/, ''), // strip prefix+source
156
- namePart,
157
- ]));
158
-
159
- const resolvedType = idToType.get(resourceId) as 'rule' | 'mcp' | undefined;
160
- let uninstalled = false;
161
- for (const pattern of patternsToTry) {
162
- const uninstallResult = await uninstallResource({
163
- resource_id_or_name: pattern,
164
- remove_from_account: false, // already unsubscribed above
165
- ...(resolvedType ? { resource_type: resolvedType } : {}),
166
- });
167
- if (uninstallResult.success && uninstallResult.data && uninstallResult.data.removed_resources.length > 0) {
168
- uninstallResults.push({ id: resourceId, removed: true, detail: `Removed local files for "${pattern}"` });
169
- uninstalled = true;
170
- break;
171
- }
172
- }
173
- if (!uninstalled) {
174
- uninstallResults.push({ id: resourceId, removed: false, detail: 'No local files found (may not have been installed)' });
175
- }
176
- }
177
-
178
- const removedCount = uninstallResults.filter(r => r.removed).length;
179
- const notFoundCount = uninstallResults.filter(r => !r.removed).length;
180
-
181
- result = {
182
- action: 'unsubscribe',
183
- success: true,
184
- message: [
185
- `Successfully unsubscribed from ${typedParams.resource_ids.length} resource${typedParams.resource_ids.length > 1 ? 's' : ''}.`,
186
- removedCount > 0 ? `Removed local files for ${removedCount} resource${removedCount > 1 ? 's' : ''}.` : null,
187
- notFoundCount > 0 ? `${notFoundCount} resource${notFoundCount > 1 ? 's were' : ' was'} not installed locally.` : null,
188
- ].filter(Boolean).join(' '),
189
- sync_details: uninstallResults.map(r => ({ id: r.id, name: r.id, action: r.removed ? 'uninstalled' : 'not_found_locally' })),
190
- };
191
-
192
- logger.info({ count: typedParams.resource_ids.length, removedCount }, 'Resources unsubscribed and local files cleaned up');
193
- break;
194
- }
195
-
196
- case 'list': {
197
- logger.debug({ scope: typedParams.scope || 'all' }, 'Listing subscriptions...');
198
-
199
- // Get subscriptions list
200
- const subs = await apiClient.getSubscriptions({}, typedParams.user_token);
201
-
202
- result = {
203
- action: 'list',
204
- success: true,
205
- subscriptions: subs.subscriptions.map(sub => ({
206
- id: sub.id,
207
- name: sub.name,
208
- type: sub.type,
209
- subscribed_at: sub.subscribed_at,
210
- })),
211
- message: `Found ${subs.total} subscription${subs.total !== 1 ? 's' : ''}`,
212
- };
213
-
214
- logger.info({ total: subs.total }, 'Subscriptions listed successfully');
215
- break;
216
- }
217
-
218
- case 'batch_subscribe': {
219
- // Validate resource_ids
220
- if (!typedParams.resource_ids || typedParams.resource_ids.length === 0) {
221
- throw createValidationError(
222
- 'resource_ids',
223
- 'array',
224
- 'resource_ids is required for batch_subscribe action'
225
- );
226
- }
227
-
228
- logger.debug({ count: typedParams.resource_ids.length, autoSync: typedParams.auto_sync }, 'Batch subscribing to resources...');
229
-
230
- const batchSubResult = await apiClient.subscribe(
231
- typedParams.resource_ids,
232
- typedParams.auto_sync,
233
- undefined,
234
- typedParams.user_token
235
- );
236
-
237
- logger.info({ count: batchSubResult.subscriptions.length }, 'Batch subscription completed');
238
-
239
- // Auto-sync newly subscribed resources immediately (default: true)
240
- const shouldBatchAutoSync = typedParams.auto_sync !== false;
241
- let batchSyncSummary: string | undefined;
242
- let batchSyncDetails: Array<{ id: string; name: string; action: string }> | undefined;
243
- let batchPendingSetup: unknown[] | undefined;
244
-
245
- if (shouldBatchAutoSync && batchSubResult.subscriptions.length > 0) {
246
- logger.info({ count: batchSubResult.subscriptions.length }, 'Auto-syncing batch subscribed resources...');
247
- const batchSyncResult = await syncResources({
248
- mode: 'incremental',
249
- scope: typedParams.scope || 'global',
250
- user_token: typedParams.user_token,
251
- });
252
- if (batchSyncResult.success && batchSyncResult.data) {
253
- const sd = batchSyncResult.data;
254
- batchSyncSummary = `Auto-sync: ${sd.summary.synced} synced, ${sd.summary.cached} cached, ${sd.summary.failed} failed`;
255
- batchSyncDetails = sd.details.map(d => ({ id: d.id, name: d.name, action: d.action }));
256
- if (sd.pending_setup && sd.pending_setup.length > 0) {
257
- batchPendingSetup = sd.pending_setup;
258
- }
259
- } else {
260
- batchSyncSummary = 'Auto-sync failed — run sync_resources manually if needed';
261
- }
262
- }
263
-
264
- result = {
265
- action: 'batch_subscribe',
266
- success: true,
267
- subscriptions: batchSubResult.subscriptions.map(sub => ({
268
- id: sub.id,
269
- name: sub.name,
270
- type: sub.type,
271
- subscribed_at: sub.subscribed_at,
272
- })),
273
- message: [
274
- `Successfully batch subscribed to ${batchSubResult.subscriptions.length} resource${batchSubResult.subscriptions.length > 1 ? 's' : ''}.`,
275
- batchSyncSummary,
276
- ].filter(Boolean).join(' '),
277
- ...(batchSyncDetails ? { sync_details: batchSyncDetails } : {}),
278
- ...(batchPendingSetup ? { pending_setup: batchPendingSetup } : {}),
279
- };
280
- break;
281
- }
282
-
283
- case 'batch_unsubscribe': {
284
- // Validate resource_ids
285
- if (!typedParams.resource_ids || typedParams.resource_ids.length === 0) {
286
- throw createValidationError(
287
- 'resource_ids',
288
- 'array',
289
- 'resource_ids is required for batch_unsubscribe action'
290
- );
291
- }
292
-
293
- logger.debug({ count: typedParams.resource_ids.length }, 'Batch unsubscribing from resources...');
294
-
295
- // Delegate entirely to the unsubscribe case for unified cleanup logic
296
- return manageSubscription({ ...typedParams, action: 'unsubscribe' });
297
- }
298
-
299
- default: {
300
- throw createValidationError(
301
- 'action',
302
- 'string',
303
- `Unknown action. Must be one of: subscribe, unsubscribe, list, batch_subscribe, batch_unsubscribe`
304
- );
305
- }
306
- }
307
-
308
- const duration = Date.now() - startTime;
309
- logToolCall('manage_subscription', 'user-id', params as Record<string, unknown>, duration);
310
-
311
- logger.info(
312
- {
313
- action: typedParams.action,
314
- duration,
315
- },
316
- 'manage_subscription completed successfully'
317
- );
318
-
319
- return {
320
- success: true,
321
- data: result,
322
- };
323
- } catch (error) {
324
- logger.error({ error, action: typedParams.action }, 'manage_subscription failed');
325
- return {
326
- success: false,
327
- error: {
328
- code: error instanceof MCPServerError ? error.code : 'UNKNOWN_ERROR',
329
- message: error instanceof Error ? error.message : String(error),
330
- },
331
- };
332
- }
333
- }
334
-
335
- // Tool definition for registry
336
- export const manageSubscriptionTool = {
337
- name: 'manage_subscription',
338
- description:
339
- 'Manage resource subscriptions (subscribe, unsubscribe, list). ' +
340
- 'When action is "subscribe" or "batch_subscribe", the tool automatically syncs ' +
341
- 'the newly subscribed resources to the local machine immediately after subscribing ' +
342
- '(auto_sync defaults to true). Pass auto_sync: false only when the user explicitly ' +
343
- 'says they do NOT want the resource installed right now.',
344
- inputSchema: {
345
- type: 'object' as const,
346
- properties: {
347
- action: {
348
- type: 'string',
349
- description: 'Action to perform',
350
- enum: ['subscribe', 'unsubscribe', 'list', 'batch_subscribe', 'batch_unsubscribe'],
351
- },
352
- resource_ids: {
353
- type: 'array',
354
- description: 'Resource IDs (required for subscribe/unsubscribe actions)',
355
- },
356
- auto_sync: {
357
- type: 'boolean',
358
- description:
359
- 'Whether to immediately sync (install) the subscribed resources to the local machine after subscribing. ' +
360
- 'Defaults to true — omit this field in normal usage. ' +
361
- 'Set to false only when the user explicitly says they want to subscribe but NOT install yet.',
362
- default: true,
363
- },
364
- scope: {
365
- type: 'string',
366
- description: 'Installation scope',
367
- enum: ['global', 'workspace'],
368
- default: 'global',
369
- },
370
- notify: {
371
- type: 'boolean',
372
- description: 'Enable update notifications',
373
- default: true,
374
- },
375
- user_token: {
376
- type: 'string',
377
- description:
378
- 'DO NOT set this field — it is automatically injected by the MCP server from ' +
379
- 'the authenticated SSE connection. The server always provides the correct token.',
380
- },
381
- },
382
- required: ['action'],
383
- },
384
- handler: manageSubscription,
385
- };
@@ -1,97 +0,0 @@
1
- /**
2
- * Tool Registry
3
- * Central registry for MCP tools
4
- */
5
-
6
- import { logger } from '../utils/logger';
7
- import type { ToolDefinition } from '../types/tools';
8
- import type { MCPToolDefinition } from '../types/mcp';
9
-
10
- class ToolRegistry {
11
- private tools: Map<string, ToolDefinition> = new Map();
12
-
13
- /**
14
- * Register a tool
15
- */
16
- registerTool(tool: ToolDefinition): void {
17
- if (this.tools.has(tool.name)) {
18
- throw new Error(`Tool '${tool.name}' is already registered`);
19
- }
20
-
21
- this.tools.set(tool.name, tool);
22
- logger.info({ toolName: tool.name }, `Tool registered: ${tool.name}`);
23
- }
24
-
25
- /**
26
- * Get a tool by name
27
- */
28
- getTool(name: string): ToolDefinition | undefined {
29
- return this.tools.get(name);
30
- }
31
-
32
- /**
33
- * List all registered tools
34
- */
35
- listTools(): ToolDefinition[] {
36
- return Array.from(this.tools.values());
37
- }
38
-
39
- /**
40
- * Get MCP tool definitions (for tools/list response)
41
- */
42
- getMCPToolDefinitions(): MCPToolDefinition[] {
43
- return this.listTools().map((tool) => ({
44
- name: tool.name,
45
- description: tool.description,
46
- inputSchema: tool.inputSchema,
47
- }));
48
- }
49
-
50
- /**
51
- * Check if a tool exists
52
- */
53
- hasTool(name: string): boolean {
54
- return this.tools.has(name);
55
- }
56
-
57
- /**
58
- * Get tool count
59
- */
60
- getToolCount(): number {
61
- return this.tools.size;
62
- }
63
-
64
- /**
65
- * Call a tool by name with arguments
66
- */
67
- async callTool(name: string, args: Record<string, unknown>): Promise<unknown> {
68
- const tool = this.getTool(name);
69
- if (!tool) {
70
- throw new Error(`Tool '${name}' not found`);
71
- }
72
-
73
- logger.debug({ toolName: name, args }, 'Calling tool');
74
-
75
- try {
76
- const result = await tool.handler(args);
77
-
78
- // Debug: Log the result structure
79
- logger.debug({
80
- toolName: name,
81
- resultType: typeof result,
82
- resultKeys: result && typeof result === 'object' ? Object.keys(result) : undefined,
83
- hasSuccess: result && typeof result === 'object' && 'success' in result,
84
- hasData: result && typeof result === 'object' && 'data' in result,
85
- hasError: result && typeof result === 'object' && 'error' in result
86
- }, 'Tool execution result structure');
87
-
88
- return result;
89
- } catch (error) {
90
- logger.error({ toolName: name, error, errorType: typeof error, errorMessage: error instanceof Error ? error.message : String(error) }, 'Tool execution threw error');
91
- throw error;
92
- }
93
- }
94
- }
95
-
96
- // Global singleton
97
- export const toolRegistry = new ToolRegistry();
@@ -1,185 +0,0 @@
1
- /**
2
- * search_resources Tool
3
- * Search for available resources
4
- */
5
-
6
- import { logger, logToolCall } from '../utils/logger';
7
- import { apiClient } from '../api/client';
8
- import { filesystemManager } from '../filesystem/manager';
9
- import { getCursorResourcePath } from '../utils/cursor-paths.js';
10
- import { MCPServerError } from '../types/errors';
11
- import type { SearchResourcesParams, SearchResourcesResult, ToolResult } from '../types/tools';
12
- // Simple in-memory cache
13
- const searchCache = new Map<string, { results: SearchResourcesResult; timestamp: number }>();
14
- const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
15
-
16
- /**
17
- * Generate cache key from search parameters
18
- */
19
- function getCacheKey(params: SearchResourcesParams): string {
20
- return JSON.stringify({
21
- team: params.team || '',
22
- type: params.type || '',
23
- keyword: params.keyword || '',
24
- });
25
- }
26
-
27
- /**
28
- * Get cached search results if available and not expired
29
- */
30
- function getCachedResults(cacheKey: string): SearchResourcesResult | null {
31
- const cached = searchCache.get(cacheKey);
32
- if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
33
- logger.debug({ cacheKey }, 'Search cache hit');
34
- return cached.results;
35
- }
36
-
37
- if (cached) {
38
- // Remove expired cache entry
39
- searchCache.delete(cacheKey);
40
- logger.debug({ cacheKey }, 'Search cache expired, removed');
41
- }
42
-
43
- return null;
44
- }
45
-
46
- /**
47
- * Cache search results
48
- */
49
- function cacheResults(cacheKey: string, results: SearchResourcesResult): void {
50
- searchCache.set(cacheKey, {
51
- results,
52
- timestamp: Date.now(),
53
- });
54
- logger.debug({ cacheKey, total: results.total }, 'Search results cached');
55
- }
56
-
57
- export async function searchResources(params: unknown): Promise<ToolResult<SearchResourcesResult>> {
58
- const startTime = Date.now();
59
-
60
- // Type assertion for params
61
- const typedParams = params as SearchResourcesParams;
62
-
63
- logger.info({ tool: 'search_resources', params }, 'search_resources called');
64
-
65
- try {
66
- // Generate cache key
67
- const cacheKey = getCacheKey(typedParams);
68
-
69
- // Check cache first
70
- const cachedResult = getCachedResults(cacheKey);
71
- if (cachedResult) {
72
- const duration = Date.now() - startTime;
73
- logToolCall('search_resources', 'user-id', params as Record<string, unknown>, duration);
74
-
75
- logger.info({ total: cachedResult.total, duration, cached: true }, 'search_resources completed (cache hit)');
76
-
77
- return {
78
- success: true,
79
- data: cachedResult,
80
- };
81
- }
82
-
83
- // Search via API
84
- logger.debug({ team: typedParams.team, type: typedParams.type, keyword: typedParams.keyword }, 'Searching resources...');
85
-
86
- const searchResults = await apiClient.searchResources(
87
- {
88
- team: typedParams.team,
89
- type: typedParams.type,
90
- keyword: typedParams.keyword,
91
- },
92
- typedParams.user_token
93
- );
94
-
95
- // Check subscription and installation status for each result
96
- const enhancedResults = await Promise.all(
97
- searchResults.results.map(async (resource) => {
98
- // Check if installed locally in the Cursor directory for this resource type
99
- let isInstalled = false;
100
- try {
101
- const resourcePath = getCursorResourcePath(resource.type, resource.name);
102
- isInstalled = await filesystemManager.fileExists(resourcePath);
103
- } catch {
104
- // Unknown type or path check failed — treat as not installed
105
- isInstalled = false;
106
- }
107
-
108
- return {
109
- ...resource,
110
- is_installed: isInstalled,
111
- };
112
- })
113
- );
114
-
115
- // Build final result
116
- const result: SearchResourcesResult = {
117
- total: searchResults.total,
118
- results: enhancedResults,
119
- };
120
-
121
- // Cache the results
122
- cacheResults(cacheKey, result);
123
-
124
- const duration = Date.now() - startTime;
125
- logToolCall('search_resources', 'user-id', params as Record<string, unknown>, duration);
126
-
127
- logger.info(
128
- {
129
- team: typedParams.team,
130
- type: typedParams.type,
131
- keyword: typedParams.keyword,
132
- total: result.total,
133
- duration,
134
- cached: false,
135
- },
136
- 'search_resources completed successfully'
137
- );
138
-
139
- return {
140
- success: true,
141
- data: result,
142
- };
143
- } catch (error) {
144
- logger.error({ error, tool: 'search_resources', errorStack: error instanceof Error ? error.stack : undefined }, 'search_resources failed');
145
- return {
146
- success: false,
147
- error: {
148
- code: error instanceof MCPServerError ? error.code : 'UNKNOWN_ERROR',
149
- message: error instanceof Error ? error.message : String(error),
150
- },
151
- };
152
- }
153
- }
154
-
155
- // Tool definition for registry
156
- export const searchResourcesTool = {
157
- name: 'search_resources',
158
- description: 'Search for available resources by team, type, or keyword',
159
- inputSchema: {
160
- type: 'object' as const,
161
- properties: {
162
- team: {
163
- type: 'string',
164
- description: 'Filter by team (empty = all teams)',
165
- },
166
- type: {
167
- type: 'string',
168
- description: 'Filter by resource type',
169
- enum: ['command', 'skill', 'rule', 'mcp', ''],
170
- },
171
- keyword: {
172
- type: 'string',
173
- description: 'Search keyword (searches in name, description, tags)',
174
- },
175
- user_token: {
176
- type: 'string',
177
- description:
178
- 'DO NOT set this field — it is automatically injected by the MCP server from ' +
179
- 'the authenticated SSE connection. The server always provides the correct token.',
180
- },
181
- },
182
- required: ['keyword'],
183
- },
184
- handler: searchResources,
185
- };