@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,70 @@
1
+ import { BaseToolController } from '../base/BaseToolController.js';
2
+ export class ListRepositoriesTool extends BaseToolController {
3
+ constructor(ucmClient, logger, publishingAuthorId) {
4
+ super(ucmClient, logger, publishingAuthorId);
5
+ }
6
+ get name() {
7
+ return 'mcp_ucm_list_repositories';
8
+ }
9
+ get description() {
10
+ return 'List all repositories for a specific author with pagination and statistics';
11
+ }
12
+ get inputSchema() {
13
+ return {
14
+ type: 'object',
15
+ properties: {
16
+ author: {
17
+ type: 'string',
18
+ description: `Author identifier (e.g., "${this.publishingAuthorId || '1234567890'}")`,
19
+ minLength: 1,
20
+ maxLength: 50
21
+ },
22
+ offset: {
23
+ type: 'number',
24
+ description: 'Number of items to skip for pagination (default: 0)',
25
+ minimum: 0
26
+ },
27
+ limit: {
28
+ type: 'number',
29
+ description: 'Maximum number of items to return (default: 20, max: 100)',
30
+ minimum: 1,
31
+ maximum: 100
32
+ }
33
+ },
34
+ required: ['author']
35
+ };
36
+ }
37
+ async handleExecute(params) {
38
+ const { author, offset, limit } = params;
39
+ this.logger.debug('ListRepositoriesTool', `Listing repositories for author: ${author}`, '', {
40
+ offset: offset || 0,
41
+ limit: limit || 'default'
42
+ });
43
+ try {
44
+ // Use listArtifacts with just author to get repositories
45
+ const response = await this.ucmClient.listArtifacts(author, undefined, // no repository - this will list repositories
46
+ undefined, // no category
47
+ undefined, // no subcategory
48
+ offset, limit);
49
+ this.logger.info('ListRepositoriesTool', `Listed ${response.data.length} repositories for author: ${author}`, '', {
50
+ total: response.pagination?.total,
51
+ offset: response.pagination?.offset,
52
+ limit: response.pagination?.limit
53
+ });
54
+ // Return the full response with pagination metadata and context
55
+ return {
56
+ author,
57
+ listingType: 'repositories',
58
+ data: response.data,
59
+ pagination: response.pagination,
60
+ hasMore: response.pagination.offset + response.pagination.limit < response.pagination.total,
61
+ _links: response._links
62
+ };
63
+ }
64
+ catch (error) {
65
+ this.logger.error('ListRepositoriesTool', `Failed to list repositories for author: ${author}`, '', error);
66
+ throw error;
67
+ }
68
+ }
69
+ }
70
+ //# sourceMappingURL=ListRepositoriesTool.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 QuickstartTool 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=QuickstartTool.d.ts.map
@@ -0,0 +1,36 @@
1
+ import { BaseToolController } from '../base/BaseToolController.js';
2
+ export class QuickstartTool extends BaseToolController {
3
+ constructor(ucmClient, logger, publishingAuthorId) {
4
+ super(ucmClient, logger, publishingAuthorId);
5
+ }
6
+ get name() {
7
+ return 'mcp_ucm_quickstart';
8
+ }
9
+ get description() {
10
+ return 'Get the UCM quickstart guide to understand how to use the UCM system and MCP tools effectively';
11
+ }
12
+ get inputSchema() {
13
+ return {
14
+ type: 'object',
15
+ properties: {},
16
+ required: []
17
+ };
18
+ }
19
+ async handleExecute(params) {
20
+ this.logger.debug('QuickstartTool', 'Retrieving UCM quickstart guide');
21
+ try {
22
+ // Get quickstart content from API
23
+ const quickstartContent = await this.ucmClient.getQuickstart();
24
+ this.logger.info('QuickstartTool', 'Quickstart guide retrieved successfully', '', {
25
+ contentLength: quickstartContent.length,
26
+ source: 'UCM API /api/v1/quickstart'
27
+ });
28
+ return quickstartContent;
29
+ }
30
+ catch (error) {
31
+ this.logger.error('QuickstartTool', 'Failed to retrieve quickstart guide', '', error);
32
+ throw error;
33
+ }
34
+ }
35
+ }
36
+ //# sourceMappingURL=QuickstartTool.js.map
@@ -0,0 +1,30 @@
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 ValidatePathController extends BaseToolController {
5
+ constructor(ucmClient: UcmApiClient, logger: ILogger);
6
+ get name(): string;
7
+ get description(): string;
8
+ get inputSchema(): any;
9
+ protected handleExecute(params: any): Promise<any>;
10
+ private performPathValidation;
11
+ private validateBasicFormat;
12
+ private parsePathComponents;
13
+ private determinePathType;
14
+ private isVersionedPath;
15
+ private looksLikeVersion;
16
+ private validateComponents;
17
+ private validateSemantics;
18
+ private checkPathExistence;
19
+ private checkParentPathExistence;
20
+ private findSimilarPaths;
21
+ private generateSuggestions;
22
+ private applyStrictModeValidation;
23
+ private validateComponentConsistency;
24
+ private isValidAuthorId;
25
+ private isValidSubcategory;
26
+ private isValidTechnology;
27
+ private calculatePathSimilarity;
28
+ private generateSimilarityReason;
29
+ }
30
+ //# sourceMappingURL=ValidatePathController.d.ts.map
@@ -0,0 +1,465 @@
1
+ import { BaseToolController } from '../base/BaseToolController.js';
2
+ import { ValidationUtils } from '../../utils/ValidationUtils.js';
3
+ import { parsePath } from '../../utils/PathUtils.js';
4
+ export class ValidatePathController extends BaseToolController {
5
+ constructor(ucmClient, logger) {
6
+ super(ucmClient, logger);
7
+ }
8
+ get name() {
9
+ return 'mcp_ucm_validate_path';
10
+ }
11
+ get description() {
12
+ return 'Validate UCM artifact path format and optionally check for existence in the repository';
13
+ }
14
+ get inputSchema() {
15
+ return {
16
+ type: 'object',
17
+ properties: {
18
+ path: {
19
+ type: 'string',
20
+ description: 'Artifact path to validate (e.g., "utaba/commands/create-user/typescript/1.0.0")',
21
+ minLength: 1,
22
+ maxLength: 200
23
+ },
24
+ checkExistence: {
25
+ type: 'boolean',
26
+ default: false,
27
+ description: 'Check if the artifact actually exists in the repository'
28
+ },
29
+ validateComponents: {
30
+ type: 'boolean',
31
+ default: true,
32
+ description: 'Validate individual path components (author, category, etc.)'
33
+ },
34
+ suggestCorrections: {
35
+ type: 'boolean',
36
+ default: true,
37
+ description: 'Provide suggestions for fixing invalid paths'
38
+ },
39
+ checkSimilar: {
40
+ type: 'boolean',
41
+ default: false,
42
+ description: 'Find similar existing paths if validation fails'
43
+ },
44
+ strictMode: {
45
+ type: 'boolean',
46
+ default: false,
47
+ description: 'Enable strict validation rules (more restrictive)'
48
+ }
49
+ },
50
+ required: ['path']
51
+ };
52
+ }
53
+ async handleExecute(params) {
54
+ const { path, checkExistence = false, validateComponents = true, suggestCorrections = true, checkSimilar = false, strictMode = false } = params;
55
+ this.logger.debug('ValidatePathController', `Validating path: ${path}`);
56
+ try {
57
+ // Perform comprehensive validation
58
+ const validationResult = await this.performPathValidation(path, {
59
+ checkExistence,
60
+ validateComponents,
61
+ suggestCorrections,
62
+ checkSimilar,
63
+ strictMode
64
+ });
65
+ this.logger.info('ValidatePathController', `Path validation completed: ${validationResult.isValid ? 'VALID' : 'INVALID'}`);
66
+ return validationResult;
67
+ }
68
+ catch (error) {
69
+ this.logger.error('ValidatePathController', `Path validation failed for: ${path}`, '', error);
70
+ throw error;
71
+ }
72
+ }
73
+ async performPathValidation(path, options) {
74
+ const result = {
75
+ path,
76
+ isValid: true,
77
+ errors: [],
78
+ warnings: [],
79
+ suggestions: [],
80
+ pathAnalysis: {},
81
+ existence: null,
82
+ similar: [],
83
+ metadata: {
84
+ timestamp: new Date().toISOString(),
85
+ validationType: options.strictMode ? 'strict' : 'standard',
86
+ checksPerformed: []
87
+ }
88
+ };
89
+ // 1. Basic format validation
90
+ await this.validateBasicFormat(path, result, options);
91
+ // 2. Component validation
92
+ if (options.validateComponents && result.pathAnalysis.components) {
93
+ await this.validateComponents(result.pathAnalysis.components, result, options);
94
+ }
95
+ // 3. Semantic validation
96
+ await this.validateSemantics(path, result, options);
97
+ // 4. Existence check
98
+ if (options.checkExistence) {
99
+ await this.checkPathExistence(path, result);
100
+ }
101
+ // 5. Find similar paths if invalid and requested
102
+ if (!result.isValid && options.checkSimilar) {
103
+ await this.findSimilarPaths(path, result);
104
+ }
105
+ // 6. Generate suggestions if requested
106
+ if (options.suggestCorrections && (!result.isValid || result.warnings.length > 0)) {
107
+ await this.generateSuggestions(path, result, options);
108
+ }
109
+ // Set overall validity
110
+ result.isValid = result.errors.length === 0;
111
+ return result;
112
+ }
113
+ async validateBasicFormat(path, result, options) {
114
+ result.metadata.checksPerformed.push('basic-format');
115
+ // Basic checks
116
+ if (!path || typeof path !== 'string') {
117
+ result.errors.push('Path must be a non-empty string');
118
+ return;
119
+ }
120
+ if (path.trim() !== path) {
121
+ result.errors.push('Path cannot have leading or trailing whitespace');
122
+ }
123
+ if (path.includes('//')) {
124
+ result.errors.push('Path cannot contain double slashes');
125
+ }
126
+ if (path.includes('..')) {
127
+ result.errors.push('Path cannot contain relative path components (..)');
128
+ }
129
+ if (path.startsWith('/') || path.endsWith('/')) {
130
+ result.errors.push('Path cannot start or end with forward slash');
131
+ }
132
+ // Character validation
133
+ const validCharPattern = /^[a-zA-Z0-9\-_.\/]+$/;
134
+ if (!validCharPattern.test(path)) {
135
+ result.errors.push('Path contains invalid characters. Only alphanumeric, hyphens, underscores, dots, and forward slashes are allowed');
136
+ }
137
+ // Parse path components
138
+ const components = path.split('/');
139
+ result.pathAnalysis = {
140
+ components: this.parsePathComponents(components),
141
+ componentCount: components.length,
142
+ pathType: this.determinePathType(components),
143
+ isVersioned: this.isVersionedPath(components)
144
+ };
145
+ // Length validation
146
+ if (path.length > 200) {
147
+ result.errors.push('Path exceeds maximum length of 200 characters');
148
+ }
149
+ if (components.length < 3) {
150
+ result.errors.push('Path must have at least 3 components (author/category/subcategory)');
151
+ }
152
+ if (components.length > 5) {
153
+ result.errors.push('Path has too many components. Maximum is 5 (author/category/subcategory/technology/version)');
154
+ }
155
+ // Strict mode additional checks
156
+ if (options.strictMode) {
157
+ this.applyStrictModeValidation(path, components, result);
158
+ }
159
+ }
160
+ parsePathComponents(components) {
161
+ const parsed = {
162
+ author: components[0] || null,
163
+ category: components[1] || null,
164
+ subcategory: components[2] || null,
165
+ technology: null,
166
+ version: null
167
+ };
168
+ if (components.length === 4) {
169
+ // Could be either technology-agnostic (author/category/subcategory/version)
170
+ // or missing version (author/category/subcategory/technology)
171
+ if (this.looksLikeVersion(components[3])) {
172
+ parsed.version = components[3];
173
+ }
174
+ else {
175
+ parsed.technology = components[3];
176
+ }
177
+ }
178
+ else if (components.length === 5) {
179
+ // author/category/subcategory/technology/version
180
+ parsed.technology = components[3];
181
+ parsed.version = components[4];
182
+ }
183
+ return parsed;
184
+ }
185
+ determinePathType(components) {
186
+ if (components.length === 4 && this.looksLikeVersion(components[3])) {
187
+ return 'technology-agnostic-versioned';
188
+ }
189
+ else if (components.length === 4) {
190
+ return 'technology-specific-unversioned';
191
+ }
192
+ else if (components.length === 5) {
193
+ return 'technology-specific-versioned';
194
+ }
195
+ else if (components.length === 3) {
196
+ return 'subcategory-level';
197
+ }
198
+ else if (components.length === 2) {
199
+ return 'category-level';
200
+ }
201
+ else if (components.length === 1) {
202
+ return 'author-level';
203
+ }
204
+ return 'unknown';
205
+ }
206
+ isVersionedPath(components) {
207
+ const lastComponent = components[components.length - 1];
208
+ return this.looksLikeVersion(lastComponent);
209
+ }
210
+ looksLikeVersion(component) {
211
+ return /^[0-9]+\.[0-9]+\.[0-9]+/.test(component);
212
+ }
213
+ async validateComponents(components, result, options) {
214
+ result.metadata.checksPerformed.push('component-validation');
215
+ // Validate author
216
+ if (components.author) {
217
+ if (!this.isValidAuthorId(components.author)) {
218
+ result.errors.push(`Invalid author ID format: ${components.author}`);
219
+ }
220
+ }
221
+ // Validate category
222
+ if (components.category) {
223
+ try {
224
+ ValidationUtils.validateCategory(components.category);
225
+ }
226
+ catch (error) {
227
+ result.errors.push(`Invalid category: ${components.category}`);
228
+ }
229
+ }
230
+ // Validate subcategory
231
+ if (components.subcategory) {
232
+ if (!this.isValidSubcategory(components.subcategory)) {
233
+ result.errors.push(`Invalid subcategory format: ${components.subcategory}`);
234
+ }
235
+ }
236
+ // Validate technology
237
+ if (components.technology) {
238
+ if (!this.isValidTechnology(components.technology)) {
239
+ result.errors.push(`Invalid technology identifier: ${components.technology}`);
240
+ }
241
+ }
242
+ // Validate version
243
+ if (components.version) {
244
+ try {
245
+ ValidationUtils.validateVersion(components.version);
246
+ }
247
+ catch (error) {
248
+ result.errors.push(`Invalid version format: ${components.version}`);
249
+ }
250
+ }
251
+ // Component consistency checks
252
+ this.validateComponentConsistency(components, result);
253
+ }
254
+ async validateSemantics(path, result, options) {
255
+ result.metadata.checksPerformed.push('semantic-validation');
256
+ const components = result.pathAnalysis.components;
257
+ // Check for common naming patterns and best practices
258
+ if (components.subcategory) {
259
+ if (components.subcategory.includes('_') && components.subcategory.includes('-')) {
260
+ result.warnings.push('Subcategory uses both underscores and hyphens - consider consistent naming');
261
+ }
262
+ if (components.subcategory.length > 50) {
263
+ result.warnings.push('Subcategory name is very long - consider shortening for better usability');
264
+ }
265
+ }
266
+ // Technology-specific validations
267
+ if (components.technology) {
268
+ const knownTechnologies = [
269
+ 'typescript', 'javascript', 'python', 'java', 'csharp', 'go', 'rust',
270
+ 'nextjs', 'react', 'vue', 'angular', 'nodejs', 'deno'
271
+ ];
272
+ if (!knownTechnologies.includes(components.technology.toLowerCase())) {
273
+ result.warnings.push(`Technology '${components.technology}' is not in the list of commonly used technologies`);
274
+ }
275
+ }
276
+ // Version semantic checks
277
+ if (components.version) {
278
+ if (components.version.startsWith('0.')) {
279
+ result.warnings.push('Version indicates pre-release (0.x.x) - ensure this is intentional');
280
+ }
281
+ if (components.version.includes('alpha') || components.version.includes('beta')) {
282
+ result.warnings.push('Version indicates pre-release software');
283
+ }
284
+ }
285
+ // Path semantics
286
+ if (result.pathAnalysis.pathType === 'technology-specific-unversioned') {
287
+ result.warnings.push('Path appears to specify technology but no version - consider adding version');
288
+ }
289
+ }
290
+ async checkPathExistence(path, result) {
291
+ result.metadata.checksPerformed.push('existence-check');
292
+ try {
293
+ const parsed = parsePath(path);
294
+ if (!parsed.filename) {
295
+ throw new Error('Filename is required to check artifact existence');
296
+ }
297
+ const artifact = await this.ucmClient.getArtifact(parsed.author, parsed.category, parsed.subcategory, parsed.filename, parsed.version);
298
+ result.existence = {
299
+ exists: true,
300
+ artifact: {
301
+ id: artifact.id,
302
+ name: artifact.metadata?.name,
303
+ lastUpdated: artifact.lastUpdated,
304
+ publishedAt: artifact.publishedAt
305
+ }
306
+ };
307
+ }
308
+ catch (error) {
309
+ result.existence = {
310
+ exists: false,
311
+ error: error instanceof Error ? error.message : String(error)
312
+ };
313
+ if (result.pathAnalysis.isVersioned) {
314
+ // Try to check if a non-versioned path exists
315
+ await this.checkParentPathExistence(path, result);
316
+ }
317
+ }
318
+ }
319
+ async checkParentPathExistence(path, result) {
320
+ try {
321
+ const pathParts = path.split('/');
322
+ const parentPath = pathParts.slice(0, -1).join('/');
323
+ if (parentPath) {
324
+ const parsed = parsePath(parentPath);
325
+ const versions = await this.ucmClient.getArtifactVersions(parsed.author, parsed.category, parsed.subcategory);
326
+ if (versions && versions.length > 0) {
327
+ result.existence.parentExists = true;
328
+ result.existence.availableVersions = versions.map(v => v.metadata?.version).filter(Boolean);
329
+ result.suggestions.push(`Artifact exists but not this version. Available versions: ${result.existence.availableVersions.join(', ')}`);
330
+ }
331
+ }
332
+ }
333
+ catch (error) {
334
+ // Parent doesn't exist either
335
+ result.existence.parentExists = false;
336
+ }
337
+ }
338
+ async findSimilarPaths(path, result) {
339
+ result.metadata.checksPerformed.push('similar-paths');
340
+ try {
341
+ // Extract search terms from the path
342
+ const components = result.pathAnalysis.components;
343
+ const searchTerms = [
344
+ components.author,
345
+ components.category,
346
+ components.subcategory
347
+ ].filter(Boolean).join(' ');
348
+ if (searchTerms) {
349
+ const searchResults = await this.ucmClient.searchArtifacts({
350
+ category: components.category,
351
+ subcategory: components.subcategory,
352
+ limit: 5
353
+ });
354
+ result.similar = searchResults.map(artifact => ({
355
+ path: artifact.path,
356
+ name: artifact.metadata?.name,
357
+ similarity: this.calculatePathSimilarity(path, artifact.path),
358
+ reason: this.generateSimilarityReason(path, artifact.path)
359
+ })).filter(item => item.similarity > 0.3)
360
+ .sort((a, b) => b.similarity - a.similarity);
361
+ }
362
+ }
363
+ catch (error) {
364
+ result.similar = [];
365
+ this.logger.debug('ValidatePathController', 'Could not find similar paths', '', error instanceof Error ? error.message : String(error));
366
+ }
367
+ }
368
+ async generateSuggestions(path, result, options) {
369
+ const suggestions = result.suggestions;
370
+ // Format-based suggestions
371
+ if (result.errors.some((e) => e.includes('invalid characters'))) {
372
+ suggestions.push('Remove special characters and use only alphanumeric, hyphens, underscores, and dots');
373
+ }
374
+ if (result.errors.some((e) => e.includes('double slashes'))) {
375
+ suggestions.push('Remove double slashes from the path');
376
+ }
377
+ // Component-based suggestions
378
+ const components = result.pathAnalysis.components;
379
+ if (components.author && !this.isValidAuthorId(components.author)) {
380
+ suggestions.push('Author ID should be lowercase alphanumeric with hyphens/underscores only');
381
+ }
382
+ if (result.pathAnalysis.pathType === 'technology-specific-unversioned') {
383
+ suggestions.push('Consider adding a version number as the last component');
384
+ }
385
+ // Existence-based suggestions
386
+ if (result.existence && !result.existence.exists && result.existence.availableVersions) {
387
+ const versions = result.existence.availableVersions;
388
+ if (versions.length > 0) {
389
+ suggestions.push(`Try one of these existing versions: ${versions.slice(0, 3).join(', ')}`);
390
+ }
391
+ }
392
+ // Similar path suggestions
393
+ if (result.similar && result.similar.length > 0) {
394
+ const topSimilar = result.similar[0];
395
+ suggestions.push(`Did you mean: ${topSimilar.path}? (${topSimilar.reason})`);
396
+ }
397
+ }
398
+ applyStrictModeValidation(path, components, result) {
399
+ // Strict mode rules
400
+ for (const component of components) {
401
+ if (component.length < 2) {
402
+ result.errors.push('In strict mode, all path components must be at least 2 characters long');
403
+ }
404
+ if (component.includes('_') && component.includes('-')) {
405
+ result.errors.push('In strict mode, components cannot mix underscores and hyphens');
406
+ }
407
+ if (!/^[a-z0-9\-_\.]+$/.test(component)) {
408
+ result.errors.push('In strict mode, components must be lowercase');
409
+ }
410
+ }
411
+ // Must be versioned in strict mode
412
+ if (!result.pathAnalysis.isVersioned) {
413
+ result.errors.push('In strict mode, paths must include a version number');
414
+ }
415
+ }
416
+ validateComponentConsistency(components, result) {
417
+ // Check for naming consistency
418
+ if (components.subcategory && components.technology) {
419
+ if (components.subcategory.toLowerCase().includes(components.technology.toLowerCase())) {
420
+ result.warnings.push('Subcategory name includes technology - this may be redundant');
421
+ }
422
+ }
423
+ // Check for reasonable relationships
424
+ if (components.category === 'commands' && components.technology === 'html') {
425
+ result.warnings.push('HTML technology unusual for commands category');
426
+ }
427
+ }
428
+ // Helper validation methods
429
+ isValidAuthorId(authorId) {
430
+ return /^[a-zA-Z0-9\-_]+$/.test(authorId) && authorId.length >= 2 && authorId.length <= 50;
431
+ }
432
+ isValidSubcategory(subcategory) {
433
+ return /^[a-zA-Z0-9\-_]+$/.test(subcategory) && subcategory.length >= 2 && subcategory.length <= 50;
434
+ }
435
+ isValidTechnology(technology) {
436
+ return /^[a-zA-Z0-9\-_]+$/.test(technology) && technology.length >= 2 && technology.length <= 20;
437
+ }
438
+ calculatePathSimilarity(path1, path2) {
439
+ const components1 = path1.split('/');
440
+ const components2 = path2.split('/');
441
+ let matches = 0;
442
+ const maxLength = Math.max(components1.length, components2.length);
443
+ for (let i = 0; i < Math.min(components1.length, components2.length); i++) {
444
+ if (components1[i].toLowerCase() === components2[i].toLowerCase()) {
445
+ matches++;
446
+ }
447
+ }
448
+ return matches / maxLength;
449
+ }
450
+ generateSimilarityReason(originalPath, similarPath) {
451
+ const orig = originalPath.split('/');
452
+ const sim = similarPath.split('/');
453
+ if (orig[0] === sim[0]) {
454
+ if (orig[1] === sim[1]) {
455
+ return 'Same author and category';
456
+ }
457
+ return 'Same author';
458
+ }
459
+ if (orig[1] === sim[1]) {
460
+ return 'Same category';
461
+ }
462
+ return 'Similar structure';
463
+ }
464
+ }
465
+ //# sourceMappingURL=ValidatePathController.js.map
@@ -0,0 +1,40 @@
1
+ export interface ArtifactMetadata {
2
+ name: string;
3
+ description: string;
4
+ version: string;
5
+ contractVersion?: string;
6
+ author: string;
7
+ repository: string;
8
+ category: string;
9
+ subcategory: string;
10
+ technology?: string;
11
+ tags?: string[];
12
+ dependencies?: {
13
+ services?: string[];
14
+ commands?: string[];
15
+ external?: Record<string, string>;
16
+ };
17
+ }
18
+ export interface ArtifactData {
19
+ id: string;
20
+ path: string;
21
+ metadata: ArtifactMetadata;
22
+ content?: string;
23
+ examples?: any[];
24
+ lastUpdated: string;
25
+ publishedAt: string;
26
+ }
27
+ export interface AuthorData {
28
+ id: string;
29
+ name: string;
30
+ email?: string;
31
+ createdAt: string;
32
+ artifactCount?: number;
33
+ }
34
+ export interface UcmApiResponse<T = any> {
35
+ data: T;
36
+ success: boolean;
37
+ message?: string;
38
+ error?: string;
39
+ }
40
+ //# sourceMappingURL=UcmApiTypes.d.ts.map
@@ -0,0 +1,4 @@
1
+ // Source: Duplicated from ../types/Artifact.ts for MCP Server independence
2
+ // Sync with Next.js app types when schema changes are made
3
+ export {};
4
+ //# sourceMappingURL=UcmApiTypes.js.map
@@ -0,0 +1,25 @@
1
+ export declare enum McpErrorCode {
2
+ ParseError = -32700,// Invalid JSON received
3
+ InvalidRequest = -32600,// Request is not valid MCP
4
+ MethodNotFound = -32601,// Method does not exist
5
+ InvalidParams = -32602,// Invalid method parameters
6
+ InternalError = -32603,// Internal server error
7
+ ExternalServiceUnavailable = -32001
8
+ }
9
+ export interface McpErrorResponse {
10
+ error: {
11
+ code: McpErrorCode;
12
+ message: string;
13
+ data?: any;
14
+ };
15
+ }
16
+ export declare class McpError extends Error {
17
+ code: McpErrorCode;
18
+ data?: any | undefined;
19
+ constructor(code: McpErrorCode, message: string, data?: any | undefined);
20
+ toResponse(): McpErrorResponse;
21
+ }
22
+ export declare class McpErrorHandler {
23
+ static formatError(error: any): McpError;
24
+ }
25
+ //# sourceMappingURL=McpErrorHandler.d.ts.map