@utaba/ucm-mcp-server 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +79 -0
  3. package/dist/clients/UcmApiClient.d.ts +53 -0
  4. package/dist/clients/UcmApiClient.js +297 -0
  5. package/dist/index.d.ts +3 -0
  6. package/dist/index.js +68 -0
  7. package/dist/interfaces/ILogger.d.ts +8 -0
  8. package/dist/interfaces/ILogger.js +4 -0
  9. package/dist/interfaces/IMcpTool.d.ts +7 -0
  10. package/dist/interfaces/IMcpTool.js +3 -0
  11. package/dist/logging/ConsoleLogger.d.ts +16 -0
  12. package/dist/logging/ConsoleLogger.js +45 -0
  13. package/dist/logging/LoggerFactory.d.ts +9 -0
  14. package/dist/logging/LoggerFactory.js +12 -0
  15. package/dist/server/McpConfig.d.ts +26 -0
  16. package/dist/server/McpConfig.js +93 -0
  17. package/dist/server/McpHandler.d.ts +12 -0
  18. package/dist/server/McpHandler.js +69 -0
  19. package/dist/server/McpServer.d.ts +15 -0
  20. package/dist/server/McpServer.js +49 -0
  21. package/dist/server/ToolRegistry.d.ts +22 -0
  22. package/dist/server/ToolRegistry.js +85 -0
  23. package/dist/tools/artifacts/GetArtifactController.d.ts +34 -0
  24. package/dist/tools/artifacts/GetArtifactController.js +397 -0
  25. package/dist/tools/artifacts/GetLatestController.d.ts +39 -0
  26. package/dist/tools/artifacts/GetLatestController.js +469 -0
  27. package/dist/tools/artifacts/ListVersionsController.d.ts +43 -0
  28. package/dist/tools/artifacts/ListVersionsController.js +530 -0
  29. package/dist/tools/artifacts/PublishArtifactController.d.ts +37 -0
  30. package/dist/tools/artifacts/PublishArtifactController.js +605 -0
  31. package/dist/tools/base/BaseToolController.d.ts +16 -0
  32. package/dist/tools/base/BaseToolController.js +32 -0
  33. package/dist/tools/core/DeleteArtifactTool.d.ts +11 -0
  34. package/dist/tools/core/DeleteArtifactTool.js +82 -0
  35. package/dist/tools/core/GetArtifactTool.d.ts +13 -0
  36. package/dist/tools/core/GetArtifactTool.js +125 -0
  37. package/dist/tools/core/GetArtifactVersionsTool.d.ts +11 -0
  38. package/dist/tools/core/GetArtifactVersionsTool.js +63 -0
  39. package/dist/tools/core/GetChunkTool.d.ts +11 -0
  40. package/dist/tools/core/GetChunkTool.js +56 -0
  41. package/dist/tools/core/ListArtifactsTool.d.ts +11 -0
  42. package/dist/tools/core/ListArtifactsTool.js +84 -0
  43. package/dist/tools/core/PublishArtifactFromFileTool.d.ts +15 -0
  44. package/dist/tools/core/PublishArtifactFromFileTool.js +256 -0
  45. package/dist/tools/core/PublishArtifactTool.d.ts +13 -0
  46. package/dist/tools/core/PublishArtifactTool.js +197 -0
  47. package/dist/tools/discovery/BrowseCategoriesController.d.ts +25 -0
  48. package/dist/tools/discovery/BrowseCategoriesController.js +400 -0
  49. package/dist/tools/discovery/FindByPurposeController.d.ts +12 -0
  50. package/dist/tools/discovery/FindByPurposeController.js +131 -0
  51. package/dist/tools/discovery/ListAuthorsController.d.ts +20 -0
  52. package/dist/tools/discovery/ListAuthorsController.js +274 -0
  53. package/dist/tools/discovery/SearchArtifactsController.d.ts +14 -0
  54. package/dist/tools/discovery/SearchArtifactsController.js +226 -0
  55. package/dist/tools/list/ListNamespaceController.d.ts +1 -0
  56. package/dist/tools/list/ListNamespaceController.js +8 -0
  57. package/dist/tools/navigation/ExploreNamespaceController.d.ts +35 -0
  58. package/dist/tools/navigation/ExploreNamespaceController.js +548 -0
  59. package/dist/tools/utility/AuthorIndexTool.d.ts +11 -0
  60. package/dist/tools/utility/AuthorIndexTool.js +48 -0
  61. package/dist/tools/utility/HealthCheckController.d.ts +11 -0
  62. package/dist/tools/utility/HealthCheckController.js +56 -0
  63. package/dist/tools/utility/ListRepositoriesTool.d.ts +11 -0
  64. package/dist/tools/utility/ListRepositoriesTool.js +70 -0
  65. package/dist/tools/utility/QuickstartTool.d.ts +11 -0
  66. package/dist/tools/utility/QuickstartTool.js +36 -0
  67. package/dist/tools/utility/ValidatePathController.d.ts +30 -0
  68. package/dist/tools/utility/ValidatePathController.js +465 -0
  69. package/dist/types/UcmApiTypes.d.ts +40 -0
  70. package/dist/types/UcmApiTypes.js +4 -0
  71. package/dist/utils/McpErrorHandler.d.ts +25 -0
  72. package/dist/utils/McpErrorHandler.js +67 -0
  73. package/dist/utils/PathUtils.d.ts +61 -0
  74. package/dist/utils/PathUtils.js +178 -0
  75. package/dist/utils/ResponseChunker.d.ts +25 -0
  76. package/dist/utils/ResponseChunker.js +79 -0
  77. package/dist/utils/ValidationUtils.d.ts +10 -0
  78. package/dist/utils/ValidationUtils.js +50 -0
  79. package/package.json +37 -0
  80. package/package.json.backup +37 -0
@@ -0,0 +1,605 @@
1
+ import { BaseToolController } from '../base/BaseToolController.js';
2
+ import { ValidationUtils } from '../../utils/ValidationUtils.js';
3
+ import { parsePath } from '../../utils/PathUtils.js';
4
+ export class PublishArtifactController extends BaseToolController {
5
+ constructor(ucmClient, logger) {
6
+ super(ucmClient, logger);
7
+ }
8
+ get name() {
9
+ return 'mcp_ucm_publish_artifact';
10
+ }
11
+ get description() {
12
+ return 'Publish a new artifact or version to the UCM repository with comprehensive validation and metadata';
13
+ }
14
+ get inputSchema() {
15
+ return {
16
+ type: 'object',
17
+ properties: {
18
+ path: {
19
+ type: 'string',
20
+ description: 'Target artifact path (e.g., "author/category/subcategory/technology/version" or "author/category/subcategory/version")',
21
+ minLength: 10,
22
+ maxLength: 200
23
+ },
24
+ content: {
25
+ type: 'string',
26
+ description: 'Artifact source content (code, documentation, etc.)',
27
+ minLength: 1,
28
+ maxLength: 1048576 // 1MB limit
29
+ },
30
+ metadata: {
31
+ type: 'object',
32
+ properties: {
33
+ name: {
34
+ type: 'string',
35
+ description: 'Human-readable name for the artifact',
36
+ minLength: 1,
37
+ maxLength: 100
38
+ },
39
+ description: {
40
+ type: 'string',
41
+ description: 'Detailed description of the artifact',
42
+ minLength: 10,
43
+ maxLength: 2000
44
+ },
45
+ version: {
46
+ type: 'string',
47
+ pattern: '^[0-9]+\\.[0-9]+\\.[0-9]+(?:-[a-zA-Z0-9\\-_]+)?$',
48
+ description: 'Semantic version (e.g., "1.0.0", "2.1.0-beta")'
49
+ },
50
+ contractVersion: {
51
+ type: 'string',
52
+ pattern: '^[0-9]+\\.[0-9]+$',
53
+ description: 'API contract version (e.g., "1.0")'
54
+ },
55
+ dependencies: {
56
+ type: 'object',
57
+ properties: {
58
+ services: {
59
+ type: 'array',
60
+ items: { type: 'string' },
61
+ description: 'Required service interfaces'
62
+ },
63
+ commands: {
64
+ type: 'array',
65
+ items: { type: 'string' },
66
+ description: 'Required command dependencies'
67
+ },
68
+ external: {
69
+ type: 'object',
70
+ additionalProperties: { type: 'string' },
71
+ description: 'External package dependencies'
72
+ }
73
+ }
74
+ },
75
+ tags: {
76
+ type: 'array',
77
+ items: {
78
+ type: 'string',
79
+ pattern: '^[a-zA-Z0-9\\-_]+$'
80
+ },
81
+ maxItems: 20,
82
+ description: 'Searchable tags for categorization'
83
+ },
84
+ license: {
85
+ type: 'string',
86
+ description: 'License identifier (e.g., "MIT", "Apache-2.0")'
87
+ },
88
+ maintainers: {
89
+ type: 'array',
90
+ items: { type: 'string' },
91
+ description: 'List of maintainer IDs'
92
+ }
93
+ },
94
+ required: ['name', 'description', 'version']
95
+ },
96
+ examples: {
97
+ type: 'array',
98
+ items: {
99
+ type: 'object',
100
+ properties: {
101
+ title: { type: 'string' },
102
+ description: { type: 'string' },
103
+ code: { type: 'string' },
104
+ language: { type: 'string' },
105
+ complexity: {
106
+ type: 'string',
107
+ enum: ['beginner', 'intermediate', 'advanced']
108
+ },
109
+ runnable: { type: 'boolean', default: true }
110
+ },
111
+ required: ['title', 'code']
112
+ },
113
+ description: 'Usage examples and documentation'
114
+ },
115
+ publishOptions: {
116
+ type: 'object',
117
+ properties: {
118
+ validateContent: {
119
+ type: 'boolean',
120
+ default: true,
121
+ description: 'Perform content validation before publishing'
122
+ },
123
+ generateDocs: {
124
+ type: 'boolean',
125
+ default: false,
126
+ description: 'Auto-generate documentation from content'
127
+ },
128
+ notifyMaintainers: {
129
+ type: 'boolean',
130
+ default: false,
131
+ description: 'Notify existing maintainers of new version'
132
+ },
133
+ replaceExisting: {
134
+ type: 'boolean',
135
+ default: false,
136
+ description: 'Replace existing version if it exists (dangerous)'
137
+ },
138
+ draft: {
139
+ type: 'boolean',
140
+ default: false,
141
+ description: 'Publish as draft (not publicly visible)'
142
+ }
143
+ }
144
+ }
145
+ },
146
+ required: ['path', 'content', 'metadata']
147
+ };
148
+ }
149
+ async handleExecute(params) {
150
+ const { path, content, metadata, examples = [], publishOptions = {} } = params;
151
+ // Validate inputs
152
+ await this.validatePublishRequest(path, content, metadata, examples, publishOptions);
153
+ this.logger.debug('PublishArtifactController', `Publishing artifact to: ${path}`);
154
+ try {
155
+ // Pre-publish validation and preparation
156
+ const validationResult = await this.performPrePublishValidation(path, content, metadata, examples, publishOptions);
157
+ if (!validationResult.valid) {
158
+ throw this.formatError(new Error(`Validation failed: ${validationResult.errors.join(', ')}`));
159
+ }
160
+ // Check for existing artifact and handle conflicts
161
+ await this.handleExistingArtifact(path, publishOptions);
162
+ // Prepare publish data
163
+ const publishData = await this.preparePublishData(content, metadata, examples, publishOptions);
164
+ // Parse the path to get component parts
165
+ const parsed = parsePath(path);
166
+ if (!parsed.author || !parsed.category || !parsed.subcategory || !parsed.filename) {
167
+ throw this.formatError(new Error(`Validation failed: You must supply author,category,subcategory,filename. 1 or more of these parameters are missing.`));
168
+ }
169
+ // Publish to UCM API
170
+ const publishResult = await this.ucmClient.publishArtifact(parsed.author, parsed.category, parsed.subcategory, publishData);
171
+ // Post-publish processing
172
+ const postProcessResult = await this.performPostPublishProcessing(publishResult, publishOptions);
173
+ const response = {
174
+ success: true,
175
+ artifact: {
176
+ id: publishResult.id,
177
+ path: publishResult.path,
178
+ name: metadata.name,
179
+ version: metadata.version,
180
+ author: metadata.author || this.extractAuthorFromPath(path),
181
+ category: metadata.category || this.extractCategoryFromPath(path),
182
+ publishedAt: publishResult.publishedAt || new Date().toISOString()
183
+ },
184
+ validation: validationResult,
185
+ publishing: {
186
+ method: publishOptions.replaceExisting ? 'replaced' : 'created',
187
+ draft: publishOptions.draft || false,
188
+ contentSize: content.length,
189
+ examplesCount: examples.length
190
+ },
191
+ postProcessing: postProcessResult,
192
+ urls: {
193
+ view: this.buildViewUrl(publishResult.path),
194
+ download: this.buildDownloadUrl(publishResult.path),
195
+ api: this.buildApiUrl(publishResult.path)
196
+ },
197
+ metadata: {
198
+ timestamp: new Date().toISOString(),
199
+ clientInfo: 'UCM-MCP-Server',
200
+ publishDurationMs: 0 // Would be measured in real implementation
201
+ }
202
+ };
203
+ this.logger.info('PublishArtifactController', `Successfully published artifact: ${path}`);
204
+ return response;
205
+ }
206
+ catch (error) {
207
+ this.logger.error('PublishArtifactController', `Failed to publish artifact: ${path}`, '', error);
208
+ throw error;
209
+ }
210
+ }
211
+ async validatePublishRequest(path, content, metadata, examples, publishOptions) {
212
+ // Validate path format
213
+ ValidationUtils.validateArtifactPath(path);
214
+ // Validate content size
215
+ ValidationUtils.validateContentSize(content);
216
+ // Validate metadata fields
217
+ if (!metadata.name || typeof metadata.name !== 'string') {
218
+ throw this.formatError(new Error('Metadata name is required'));
219
+ }
220
+ if (!metadata.description || metadata.description.length < 10) {
221
+ throw this.formatError(new Error('Metadata description must be at least 10 characters'));
222
+ }
223
+ if (!metadata.version) {
224
+ throw this.formatError(new Error('Metadata version is required'));
225
+ }
226
+ ValidationUtils.validateVersion(metadata.version);
227
+ // Validate path components match metadata
228
+ const pathComponents = this.parseArtifactPath(path);
229
+ if (metadata.author && metadata.author !== pathComponents.author) {
230
+ throw this.formatError(new Error('Metadata author must match path author'));
231
+ }
232
+ if (metadata.category && metadata.category !== pathComponents.category) {
233
+ throw this.formatError(new Error('Metadata category must match path category'));
234
+ }
235
+ // Validate examples if provided
236
+ if (examples && examples.length > 0) {
237
+ for (let i = 0; i < examples.length; i++) {
238
+ const example = examples[i];
239
+ if (!example.title || !example.code) {
240
+ throw this.formatError(new Error(`Example ${i + 1} missing required title or code`));
241
+ }
242
+ }
243
+ }
244
+ // Validate tags if provided
245
+ if (metadata.tags) {
246
+ for (const tag of metadata.tags) {
247
+ if (typeof tag !== 'string' || !tag.match(/^[a-zA-Z0-9\-_]+$/)) {
248
+ throw this.formatError(new Error(`Invalid tag format: ${tag}`));
249
+ }
250
+ }
251
+ }
252
+ }
253
+ async performPrePublishValidation(path, content, metadata, examples, publishOptions) {
254
+ const validationResult = {
255
+ valid: true,
256
+ errors: [],
257
+ warnings: [],
258
+ suggestions: [],
259
+ contentAnalysis: {},
260
+ securityScan: {}
261
+ };
262
+ // Content validation
263
+ if (publishOptions.validateContent !== false) {
264
+ const contentValidation = await this.validateContent(content, metadata);
265
+ validationResult.contentAnalysis = contentValidation;
266
+ if (!contentValidation.valid) {
267
+ validationResult.valid = false;
268
+ validationResult.errors.push(...contentValidation.errors);
269
+ }
270
+ validationResult.warnings.push(...contentValidation.warnings);
271
+ validationResult.suggestions.push(...contentValidation.suggestions);
272
+ }
273
+ // Security validation
274
+ const securityScan = await this.performSecurityScan(content);
275
+ validationResult.securityScan = securityScan;
276
+ if (securityScan.hasVulnerabilities) {
277
+ validationResult.valid = false;
278
+ validationResult.errors.push(...securityScan.vulnerabilities);
279
+ }
280
+ // Dependency validation
281
+ if (metadata.dependencies) {
282
+ const depValidation = await this.validateDependencies(metadata.dependencies);
283
+ if (!depValidation.valid) {
284
+ validationResult.warnings.push(...depValidation.warnings);
285
+ }
286
+ }
287
+ // Quality checks
288
+ const qualityChecks = this.performQualityChecks(metadata, content, examples);
289
+ validationResult.warnings.push(...qualityChecks.warnings);
290
+ validationResult.suggestions.push(...qualityChecks.suggestions);
291
+ return validationResult;
292
+ }
293
+ async handleExistingArtifact(path, publishOptions) {
294
+ try {
295
+ const parsed = parsePath(path);
296
+ if (!parsed.filename) {
297
+ throw new Error('Filename is required to check existing artifact');
298
+ }
299
+ const existingArtifact = await this.ucmClient.getArtifact(parsed.author, parsed.category, parsed.subcategory, parsed.filename, parsed.version);
300
+ if (existingArtifact && !publishOptions.replaceExisting) {
301
+ throw this.formatError(new Error(`Artifact already exists at path: ${path}. Use replaceExisting option to overwrite.`));
302
+ }
303
+ if (existingArtifact && publishOptions.replaceExisting) {
304
+ this.logger.warn('PublishArtifactController', `Replacing existing artifact at: ${path}`);
305
+ }
306
+ }
307
+ catch (error) {
308
+ // If artifact doesn't exist, that's what we want for new publications
309
+ if (error instanceof Error && error.message?.includes('not found')) {
310
+ return; // This is expected for new artifacts
311
+ }
312
+ throw error;
313
+ }
314
+ }
315
+ async preparePublishData(content, metadata, examples, publishOptions) {
316
+ const publishData = {
317
+ content,
318
+ metadata: {
319
+ ...metadata,
320
+ // Add computed metadata
321
+ contentHash: this.calculateContentHash(content),
322
+ contentType: this.detectContentType(content),
323
+ publishedBy: 'MCP-Server', // In real implementation, would use authenticated user
324
+ publishedAt: new Date().toISOString(),
325
+ draft: publishOptions.draft || false
326
+ }
327
+ };
328
+ // Add examples if provided
329
+ if (examples && examples.length > 0) {
330
+ publishData.examples = examples.map((example, index) => ({
331
+ ...example,
332
+ id: index + 1,
333
+ language: example.language || this.detectLanguageFromContent(example.code)
334
+ }));
335
+ }
336
+ // Generate documentation if requested
337
+ if (publishOptions.generateDocs) {
338
+ publishData.generatedDocs = await this.generateDocumentation(content, metadata);
339
+ }
340
+ return publishData;
341
+ }
342
+ async performPostPublishProcessing(publishResult, publishOptions) {
343
+ const postProcessing = {
344
+ actions: [],
345
+ notifications: [],
346
+ indexing: {
347
+ searchIndexed: false,
348
+ cacheUpdated: false
349
+ },
350
+ analytics: {
351
+ recorded: false
352
+ }
353
+ };
354
+ // Index for search
355
+ try {
356
+ await this.indexForSearch(publishResult);
357
+ postProcessing.indexing.searchIndexed = true;
358
+ postProcessing.actions.push('Indexed for search');
359
+ }
360
+ catch (error) {
361
+ this.logger.warn('PublishArtifactController', 'Failed to index for search', '', error);
362
+ }
363
+ // Update caches
364
+ try {
365
+ await this.updateCaches(publishResult);
366
+ postProcessing.indexing.cacheUpdated = true;
367
+ postProcessing.actions.push('Updated caches');
368
+ }
369
+ catch (error) {
370
+ this.logger.warn('PublishArtifactController', 'Failed to update caches', '', error);
371
+ }
372
+ // Send notifications if requested
373
+ if (publishOptions.notifyMaintainers) {
374
+ try {
375
+ const notifications = await this.sendMaintainerNotifications(publishResult);
376
+ postProcessing.notifications = notifications;
377
+ postProcessing.actions.push('Notified maintainers');
378
+ }
379
+ catch (error) {
380
+ this.logger.warn('PublishArtifactController', 'Failed to send notifications', '', error);
381
+ }
382
+ }
383
+ // Record analytics
384
+ try {
385
+ await this.recordPublishAnalytics(publishResult);
386
+ postProcessing.analytics.recorded = true;
387
+ postProcessing.actions.push('Recorded analytics');
388
+ }
389
+ catch (error) {
390
+ this.logger.warn('PublishArtifactController', 'Failed to record analytics', '', error);
391
+ }
392
+ return postProcessing;
393
+ }
394
+ // Validation helper methods
395
+ async validateContent(content, metadata) {
396
+ const validation = {
397
+ valid: true,
398
+ errors: [],
399
+ warnings: [],
400
+ suggestions: []
401
+ };
402
+ // Check content type consistency
403
+ const detectedType = this.detectContentType(content);
404
+ const pathType = this.extractTechnologyFromMetadata(metadata);
405
+ if (pathType && detectedType !== pathType && detectedType !== 'text') {
406
+ validation.warnings.push(`Content type (${detectedType}) may not match expected type (${pathType})`);
407
+ }
408
+ // Check for potential issues
409
+ if (content.includes('TODO') || content.includes('FIXME')) {
410
+ validation.warnings.push('Content contains TODO or FIXME comments');
411
+ }
412
+ if (content.length < 100) {
413
+ validation.suggestions.push('Consider adding more comprehensive implementation');
414
+ }
415
+ // Syntax validation for code content
416
+ if (this.isCodeContent(detectedType)) {
417
+ const syntaxValidation = this.validateSyntax(content, detectedType);
418
+ if (!syntaxValidation.valid) {
419
+ validation.errors.push(...syntaxValidation.errors);
420
+ validation.valid = false;
421
+ }
422
+ }
423
+ return validation;
424
+ }
425
+ async performSecurityScan(content) {
426
+ const scan = {
427
+ hasVulnerabilities: false,
428
+ vulnerabilities: [],
429
+ warnings: [],
430
+ score: 100
431
+ };
432
+ // Check for potential security issues
433
+ const securityPatterns = [
434
+ { pattern: /password\s*=\s*['"]/i, message: 'Hardcoded password detected' },
435
+ { pattern: /api[_-]?key\s*=\s*['"]/i, message: 'Hardcoded API key detected' },
436
+ { pattern: /secret\s*=\s*['"]/i, message: 'Hardcoded secret detected' },
437
+ { pattern: /eval\s*\(/i, message: 'Use of eval() function detected' },
438
+ { pattern: /innerHTML\s*=/i, message: 'Potential XSS vulnerability with innerHTML' }
439
+ ];
440
+ for (const { pattern, message } of securityPatterns) {
441
+ if (pattern.test(content)) {
442
+ scan.hasVulnerabilities = true;
443
+ scan.vulnerabilities.push(message);
444
+ scan.score -= 20;
445
+ }
446
+ }
447
+ return scan;
448
+ }
449
+ async validateDependencies(dependencies) {
450
+ const validation = {
451
+ valid: true,
452
+ warnings: []
453
+ };
454
+ // Check if external dependencies are reasonable
455
+ if (dependencies.external) {
456
+ const externalCount = Object.keys(dependencies.external).length;
457
+ if (externalCount > 10) {
458
+ validation.warnings.push(`High number of external dependencies (${externalCount})`);
459
+ }
460
+ }
461
+ return validation;
462
+ }
463
+ performQualityChecks(metadata, content, examples) {
464
+ const checks = {
465
+ warnings: [],
466
+ suggestions: []
467
+ };
468
+ // Check metadata completeness
469
+ if (!metadata.tags || metadata.tags.length === 0) {
470
+ checks.suggestions.push('Consider adding tags for better discoverability');
471
+ }
472
+ if (!metadata.dependencies) {
473
+ checks.suggestions.push('Consider documenting dependencies if any exist');
474
+ }
475
+ // Check for examples
476
+ if (!examples || examples.length === 0) {
477
+ checks.suggestions.push('Consider adding usage examples');
478
+ }
479
+ // Check content quality
480
+ if (content.length < 500) {
481
+ checks.suggestions.push('Consider providing more detailed implementation');
482
+ }
483
+ return checks;
484
+ }
485
+ // Helper methods
486
+ parseArtifactPath(path) {
487
+ const parts = path.split('/');
488
+ if (parts.length === 4) {
489
+ // author/category/subcategory/version (technology-agnostic)
490
+ return {
491
+ author: parts[0],
492
+ category: parts[1],
493
+ subcategory: parts[2],
494
+ technology: null,
495
+ version: parts[3]
496
+ };
497
+ }
498
+ else if (parts.length === 5) {
499
+ // author/category/subcategory/technology/version
500
+ return {
501
+ author: parts[0],
502
+ category: parts[1],
503
+ subcategory: parts[2],
504
+ technology: parts[3],
505
+ version: parts[4]
506
+ };
507
+ }
508
+ throw new Error('Invalid artifact path format');
509
+ }
510
+ extractAuthorFromPath(path) {
511
+ return path.split('/')[0];
512
+ }
513
+ extractCategoryFromPath(path) {
514
+ return path.split('/')[1];
515
+ }
516
+ extractTechnologyFromMetadata(metadata) {
517
+ return metadata.technology || null;
518
+ }
519
+ calculateContentHash(content) {
520
+ // Simple hash calculation (in real implementation, would use crypto)
521
+ let hash = 0;
522
+ for (let i = 0; i < content.length; i++) {
523
+ const char = content.charCodeAt(i);
524
+ hash = ((hash << 5) - hash) + char;
525
+ hash = hash & hash; // Convert to 32-bit integer
526
+ }
527
+ return hash.toString(16);
528
+ }
529
+ detectContentType(content) {
530
+ if (content.includes('interface ') || content.includes('export class'))
531
+ return 'typescript';
532
+ if (content.includes('function ') || content.includes('const '))
533
+ return 'javascript';
534
+ if (content.includes('def ') || content.includes('import '))
535
+ return 'python';
536
+ if (content.includes('```') || content.includes('# '))
537
+ return 'markdown';
538
+ return 'text';
539
+ }
540
+ detectLanguageFromContent(code) {
541
+ return this.detectContentType(code);
542
+ }
543
+ isCodeContent(contentType) {
544
+ return ['typescript', 'javascript', 'python', 'java', 'cpp'].includes(contentType);
545
+ }
546
+ validateSyntax(content, contentType) {
547
+ // Simplified syntax validation
548
+ const validation = {
549
+ valid: true,
550
+ errors: []
551
+ };
552
+ // Basic bracket matching
553
+ const brackets = { '(': ')', '[': ']', '{': '}' };
554
+ const stack = [];
555
+ for (const char of content) {
556
+ if (char in brackets) {
557
+ stack.push(brackets[char]);
558
+ }
559
+ else if (Object.values(brackets).includes(char)) {
560
+ if (stack.length === 0 || stack.pop() !== char) {
561
+ validation.valid = false;
562
+ validation.errors.push('Mismatched brackets detected');
563
+ break;
564
+ }
565
+ }
566
+ }
567
+ if (stack.length > 0) {
568
+ validation.valid = false;
569
+ validation.errors.push('Unclosed brackets detected');
570
+ }
571
+ return validation;
572
+ }
573
+ async generateDocumentation(content, metadata) {
574
+ // Simplified documentation generation
575
+ return {
576
+ generated: true,
577
+ sections: ['overview', 'usage', 'api'],
578
+ content: `# ${metadata.name}\n\n${metadata.description}\n\n## Usage\n\nSee examples for usage instructions.`
579
+ };
580
+ }
581
+ buildViewUrl(path) {
582
+ return `/browse/artifacts/${path}`;
583
+ }
584
+ buildDownloadUrl(path) {
585
+ return `/api/v1/authors/${path}/download`;
586
+ }
587
+ buildApiUrl(path) {
588
+ return `/api/v1/authors/${path}`;
589
+ }
590
+ // Post-processing methods (simplified for demo)
591
+ async indexForSearch(publishResult) {
592
+ // Would integrate with search indexing system
593
+ }
594
+ async updateCaches(publishResult) {
595
+ // Would update various caches
596
+ }
597
+ async sendMaintainerNotifications(publishResult) {
598
+ // Would send notifications to maintainers
599
+ return ['Notification sent to maintainers'];
600
+ }
601
+ async recordPublishAnalytics(publishResult) {
602
+ // Would record analytics data
603
+ }
604
+ }
605
+ //# sourceMappingURL=PublishArtifactController.js.map
@@ -0,0 +1,16 @@
1
+ import { IMcpTool } from '../../interfaces/IMcpTool.js';
2
+ import { UcmApiClient } from '../../clients/UcmApiClient.js';
3
+ import { ILogger } from '../../interfaces/ILogger.js';
4
+ export declare abstract class BaseToolController implements IMcpTool {
5
+ protected ucmClient: UcmApiClient;
6
+ protected logger: ILogger;
7
+ protected publishingAuthorId?: string | undefined;
8
+ constructor(ucmClient: UcmApiClient, logger: ILogger, publishingAuthorId?: string | undefined);
9
+ abstract get name(): string;
10
+ abstract get description(): string;
11
+ abstract get inputSchema(): any;
12
+ execute(params: any): Promise<any>;
13
+ protected abstract handleExecute(params: any): Promise<any>;
14
+ protected validateParams(params: any): void;
15
+ }
16
+ //# sourceMappingURL=BaseToolController.d.ts.map
@@ -0,0 +1,32 @@
1
+ import { McpError, McpErrorCode } from '../../utils/McpErrorHandler.js';
2
+ export class BaseToolController {
3
+ ucmClient;
4
+ logger;
5
+ publishingAuthorId;
6
+ constructor(ucmClient, logger, publishingAuthorId) {
7
+ this.ucmClient = ucmClient;
8
+ this.logger = logger;
9
+ this.publishingAuthorId = publishingAuthorId;
10
+ }
11
+ async execute(params) {
12
+ try {
13
+ this.validateParams(params);
14
+ return await this.handleExecute(params);
15
+ }
16
+ catch (error) {
17
+ this.logger.error('BaseToolController', `Tool ${this.name} execution failed`, '', error);
18
+ throw error;
19
+ }
20
+ }
21
+ validateParams(params) {
22
+ // Basic validation - can be overridden by specific tools
23
+ if (this.inputSchema.required) {
24
+ for (const field of this.inputSchema.required) {
25
+ if (params[field] === undefined || params[field] === null) {
26
+ throw new McpError(McpErrorCode.InvalidParams, `Required parameter '${field}' is missing`);
27
+ }
28
+ }
29
+ }
30
+ }
31
+ }
32
+ //# sourceMappingURL=BaseToolController.js.map
@@ -0,0 +1,11 @@
1
+ import { BaseToolController } from '../base/BaseToolController.js';
2
+ import { UcmApiClient } from '../../clients/UcmApiClient.js';
3
+ import { ILogger } from '../../interfaces/ILogger.js';
4
+ export declare class DeleteArtifactTool extends BaseToolController {
5
+ constructor(ucmClient: UcmApiClient, logger: ILogger, publishingAuthorId?: string);
6
+ get name(): string;
7
+ get description(): string;
8
+ get inputSchema(): any;
9
+ protected handleExecute(params: any): Promise<any>;
10
+ }
11
+ //# sourceMappingURL=DeleteArtifactTool.d.ts.map