@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.
- package/LICENSE +29 -0
- package/README.md +79 -0
- package/dist/clients/UcmApiClient.d.ts +53 -0
- package/dist/clients/UcmApiClient.js +297 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +68 -0
- package/dist/interfaces/ILogger.d.ts +8 -0
- package/dist/interfaces/ILogger.js +4 -0
- package/dist/interfaces/IMcpTool.d.ts +7 -0
- package/dist/interfaces/IMcpTool.js +3 -0
- package/dist/logging/ConsoleLogger.d.ts +16 -0
- package/dist/logging/ConsoleLogger.js +45 -0
- package/dist/logging/LoggerFactory.d.ts +9 -0
- package/dist/logging/LoggerFactory.js +12 -0
- package/dist/server/McpConfig.d.ts +26 -0
- package/dist/server/McpConfig.js +93 -0
- package/dist/server/McpHandler.d.ts +12 -0
- package/dist/server/McpHandler.js +69 -0
- package/dist/server/McpServer.d.ts +15 -0
- package/dist/server/McpServer.js +49 -0
- package/dist/server/ToolRegistry.d.ts +22 -0
- package/dist/server/ToolRegistry.js +85 -0
- package/dist/tools/artifacts/GetArtifactController.d.ts +34 -0
- package/dist/tools/artifacts/GetArtifactController.js +397 -0
- package/dist/tools/artifacts/GetLatestController.d.ts +39 -0
- package/dist/tools/artifacts/GetLatestController.js +469 -0
- package/dist/tools/artifacts/ListVersionsController.d.ts +43 -0
- package/dist/tools/artifacts/ListVersionsController.js +530 -0
- package/dist/tools/artifacts/PublishArtifactController.d.ts +37 -0
- package/dist/tools/artifacts/PublishArtifactController.js +605 -0
- package/dist/tools/base/BaseToolController.d.ts +16 -0
- package/dist/tools/base/BaseToolController.js +32 -0
- package/dist/tools/core/DeleteArtifactTool.d.ts +11 -0
- package/dist/tools/core/DeleteArtifactTool.js +82 -0
- package/dist/tools/core/GetArtifactTool.d.ts +13 -0
- package/dist/tools/core/GetArtifactTool.js +125 -0
- package/dist/tools/core/GetArtifactVersionsTool.d.ts +11 -0
- package/dist/tools/core/GetArtifactVersionsTool.js +63 -0
- package/dist/tools/core/GetChunkTool.d.ts +11 -0
- package/dist/tools/core/GetChunkTool.js +56 -0
- package/dist/tools/core/ListArtifactsTool.d.ts +11 -0
- package/dist/tools/core/ListArtifactsTool.js +84 -0
- package/dist/tools/core/PublishArtifactFromFileTool.d.ts +15 -0
- package/dist/tools/core/PublishArtifactFromFileTool.js +256 -0
- package/dist/tools/core/PublishArtifactTool.d.ts +13 -0
- package/dist/tools/core/PublishArtifactTool.js +197 -0
- package/dist/tools/discovery/BrowseCategoriesController.d.ts +25 -0
- package/dist/tools/discovery/BrowseCategoriesController.js +400 -0
- package/dist/tools/discovery/FindByPurposeController.d.ts +12 -0
- package/dist/tools/discovery/FindByPurposeController.js +131 -0
- package/dist/tools/discovery/ListAuthorsController.d.ts +20 -0
- package/dist/tools/discovery/ListAuthorsController.js +274 -0
- package/dist/tools/discovery/SearchArtifactsController.d.ts +14 -0
- package/dist/tools/discovery/SearchArtifactsController.js +226 -0
- package/dist/tools/list/ListNamespaceController.d.ts +1 -0
- package/dist/tools/list/ListNamespaceController.js +8 -0
- package/dist/tools/navigation/ExploreNamespaceController.d.ts +35 -0
- package/dist/tools/navigation/ExploreNamespaceController.js +548 -0
- package/dist/tools/utility/AuthorIndexTool.d.ts +11 -0
- package/dist/tools/utility/AuthorIndexTool.js +48 -0
- package/dist/tools/utility/HealthCheckController.d.ts +11 -0
- package/dist/tools/utility/HealthCheckController.js +56 -0
- package/dist/tools/utility/ListRepositoriesTool.d.ts +11 -0
- package/dist/tools/utility/ListRepositoriesTool.js +70 -0
- package/dist/tools/utility/QuickstartTool.d.ts +11 -0
- package/dist/tools/utility/QuickstartTool.js +36 -0
- package/dist/tools/utility/ValidatePathController.d.ts +30 -0
- package/dist/tools/utility/ValidatePathController.js +465 -0
- package/dist/types/UcmApiTypes.d.ts +40 -0
- package/dist/types/UcmApiTypes.js +4 -0
- package/dist/utils/McpErrorHandler.d.ts +25 -0
- package/dist/utils/McpErrorHandler.js +67 -0
- package/dist/utils/PathUtils.d.ts +61 -0
- package/dist/utils/PathUtils.js +178 -0
- package/dist/utils/ResponseChunker.d.ts +25 -0
- package/dist/utils/ResponseChunker.js +79 -0
- package/dist/utils/ValidationUtils.d.ts +10 -0
- package/dist/utils/ValidationUtils.js +50 -0
- package/package.json +37 -0
- package/package.json.backup +37 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { BaseToolController } from '../base/BaseToolController.js';
|
|
2
|
+
import { ValidationUtils } from '../../utils/ValidationUtils.js';
|
|
3
|
+
export class ListAuthorsController extends BaseToolController {
|
|
4
|
+
constructor(ucmClient, logger) {
|
|
5
|
+
super(ucmClient, logger);
|
|
6
|
+
}
|
|
7
|
+
get name() {
|
|
8
|
+
return 'mcp_ucm_list_authors';
|
|
9
|
+
}
|
|
10
|
+
get description() {
|
|
11
|
+
return 'List all available authors in the UCM repository with optional statistics and filtering';
|
|
12
|
+
}
|
|
13
|
+
get inputSchema() {
|
|
14
|
+
return {
|
|
15
|
+
type: 'object',
|
|
16
|
+
properties: {
|
|
17
|
+
includeStats: {
|
|
18
|
+
type: 'boolean',
|
|
19
|
+
default: true,
|
|
20
|
+
description: 'Include statistics (artifact count, latest activity) for each author'
|
|
21
|
+
},
|
|
22
|
+
sortBy: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
enum: ['name', 'artifactCount', 'lastActivity', 'joinDate'],
|
|
25
|
+
default: 'name',
|
|
26
|
+
description: 'Sort authors by specified field'
|
|
27
|
+
},
|
|
28
|
+
sortOrder: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
enum: ['asc', 'desc'],
|
|
31
|
+
default: 'asc',
|
|
32
|
+
description: 'Sort order (ascending or descending)'
|
|
33
|
+
},
|
|
34
|
+
filter: {
|
|
35
|
+
type: 'object',
|
|
36
|
+
properties: {
|
|
37
|
+
namePattern: {
|
|
38
|
+
type: 'string',
|
|
39
|
+
description: 'Filter authors by name pattern (supports wildcards)',
|
|
40
|
+
maxLength: 100
|
|
41
|
+
},
|
|
42
|
+
minArtifacts: {
|
|
43
|
+
type: 'number',
|
|
44
|
+
minimum: 0,
|
|
45
|
+
description: 'Minimum number of artifacts the author must have'
|
|
46
|
+
},
|
|
47
|
+
hasRecentActivity: {
|
|
48
|
+
type: 'boolean',
|
|
49
|
+
description: 'Filter authors with activity in the last 30 days'
|
|
50
|
+
},
|
|
51
|
+
categories: {
|
|
52
|
+
type: 'array',
|
|
53
|
+
items: {
|
|
54
|
+
type: 'string',
|
|
55
|
+
enum: ['commands', 'services', 'patterns', 'implementations', 'contracts', 'guidance']
|
|
56
|
+
},
|
|
57
|
+
description: 'Filter authors who have artifacts in these categories'
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
pagination: {
|
|
62
|
+
type: 'object',
|
|
63
|
+
properties: {
|
|
64
|
+
limit: {
|
|
65
|
+
type: 'number',
|
|
66
|
+
default: 50,
|
|
67
|
+
minimum: 1,
|
|
68
|
+
maximum: 200,
|
|
69
|
+
description: 'Number of authors to return'
|
|
70
|
+
},
|
|
71
|
+
offset: {
|
|
72
|
+
type: 'number',
|
|
73
|
+
default: 0,
|
|
74
|
+
minimum: 0,
|
|
75
|
+
description: 'Number of authors to skip'
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
required: []
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
async handleExecute(params) {
|
|
84
|
+
const { includeStats = true, sortBy = 'name', sortOrder = 'asc', filter = {}, pagination = {} } = params;
|
|
85
|
+
const limit = pagination.limit || 50;
|
|
86
|
+
const offset = pagination.offset || 0;
|
|
87
|
+
ValidationUtils.validatePageParams(offset, limit);
|
|
88
|
+
// Validate filter categories if provided
|
|
89
|
+
if (filter.categories) {
|
|
90
|
+
for (const category of filter.categories) {
|
|
91
|
+
ValidationUtils.validateCategory(category);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
this.logger.debug('ListAuthorsController', `Listing authors with stats: ${includeStats}`);
|
|
95
|
+
try {
|
|
96
|
+
// Get authors from UCM API
|
|
97
|
+
const authors = await this.ucmClient.getAuthors();
|
|
98
|
+
// Apply filtering
|
|
99
|
+
let filteredAuthors = authors;
|
|
100
|
+
if (filter.namePattern) {
|
|
101
|
+
const pattern = filter.namePattern.toLowerCase().replace(/\*/g, '.*');
|
|
102
|
+
const regex = new RegExp(pattern);
|
|
103
|
+
filteredAuthors = filteredAuthors.filter(author => regex.test(author.name.toLowerCase()) || regex.test(author.id.toLowerCase()));
|
|
104
|
+
}
|
|
105
|
+
// Transform and enrich author data
|
|
106
|
+
const enrichedAuthors = await this.enrichAuthorData(filteredAuthors, includeStats, filter);
|
|
107
|
+
// Apply additional filtering based on stats
|
|
108
|
+
let finalAuthors = enrichedAuthors;
|
|
109
|
+
if (filter.minArtifacts !== undefined) {
|
|
110
|
+
finalAuthors = finalAuthors.filter(author => author.artifactCount >= filter.minArtifacts);
|
|
111
|
+
}
|
|
112
|
+
if (filter.hasRecentActivity) {
|
|
113
|
+
const thirtyDaysAgo = new Date();
|
|
114
|
+
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
|
|
115
|
+
finalAuthors = finalAuthors.filter(author => author.lastActivity && new Date(author.lastActivity) > thirtyDaysAgo);
|
|
116
|
+
}
|
|
117
|
+
if (filter.categories && filter.categories.length > 0) {
|
|
118
|
+
finalAuthors = finalAuthors.filter(author => author.categories && filter.categories.some((cat) => author.categories.includes(cat)));
|
|
119
|
+
}
|
|
120
|
+
// Sort authors
|
|
121
|
+
const sortedAuthors = this.sortAuthors(finalAuthors, sortBy, sortOrder);
|
|
122
|
+
// Apply pagination
|
|
123
|
+
const paginatedAuthors = sortedAuthors.slice(offset, offset + limit);
|
|
124
|
+
this.logger.info('ListAuthorsController', `Listed ${paginatedAuthors.length} authors`);
|
|
125
|
+
return {
|
|
126
|
+
authors: paginatedAuthors,
|
|
127
|
+
pagination: {
|
|
128
|
+
total: sortedAuthors.length,
|
|
129
|
+
offset,
|
|
130
|
+
limit,
|
|
131
|
+
hasMore: offset + limit < sortedAuthors.length
|
|
132
|
+
},
|
|
133
|
+
sorting: {
|
|
134
|
+
sortBy,
|
|
135
|
+
sortOrder
|
|
136
|
+
},
|
|
137
|
+
appliedFilters: filter,
|
|
138
|
+
metadata: {
|
|
139
|
+
totalAuthorsInSystem: authors.length,
|
|
140
|
+
filteredCount: finalAuthors.length,
|
|
141
|
+
includeStats,
|
|
142
|
+
timestamp: new Date().toISOString()
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
this.logger.error('ListAuthorsController', 'Failed to list authors', '', error);
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async enrichAuthorData(authors, includeStats, filter) {
|
|
152
|
+
const enrichedAuthors = [];
|
|
153
|
+
for (const author of authors) {
|
|
154
|
+
const enrichedAuthor = {
|
|
155
|
+
id: author.id,
|
|
156
|
+
name: author.name,
|
|
157
|
+
email: author.email,
|
|
158
|
+
joinDate: author.createdAt || author.joinDate,
|
|
159
|
+
profileUrl: this.buildProfileUrl(author.id)
|
|
160
|
+
};
|
|
161
|
+
if (includeStats) {
|
|
162
|
+
try {
|
|
163
|
+
// Get author's artifacts for statistics
|
|
164
|
+
const authorData = await this.ucmClient.getAuthor(author.id);
|
|
165
|
+
const artifacts = Array.isArray(authorData) ? authorData : [];
|
|
166
|
+
enrichedAuthor.artifactCount = artifacts.length;
|
|
167
|
+
enrichedAuthor.categories = this.extractCategories(artifacts);
|
|
168
|
+
enrichedAuthor.technologies = this.extractTechnologies(artifacts);
|
|
169
|
+
enrichedAuthor.lastActivity = this.getLastActivity(artifacts);
|
|
170
|
+
enrichedAuthor.popularArtifacts = this.getPopularArtifacts(artifacts);
|
|
171
|
+
enrichedAuthor.totalDownloads = this.calculateTotalDownloads(artifacts);
|
|
172
|
+
enrichedAuthor.averageRating = this.calculateAverageRating(artifacts);
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
// If we can't get stats, continue with basic info
|
|
176
|
+
this.logger.warn('ListAuthorsController', `Failed to get stats for author ${author.id}`, '', error);
|
|
177
|
+
enrichedAuthor.artifactCount = 0;
|
|
178
|
+
enrichedAuthor.categories = [];
|
|
179
|
+
enrichedAuthor.technologies = [];
|
|
180
|
+
enrichedAuthor.lastActivity = null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
enrichedAuthors.push(enrichedAuthor);
|
|
184
|
+
}
|
|
185
|
+
return enrichedAuthors;
|
|
186
|
+
}
|
|
187
|
+
sortAuthors(authors, sortBy, sortOrder) {
|
|
188
|
+
return authors.sort((a, b) => {
|
|
189
|
+
let aVal, bVal;
|
|
190
|
+
switch (sortBy) {
|
|
191
|
+
case 'name':
|
|
192
|
+
aVal = a.name.toLowerCase();
|
|
193
|
+
bVal = b.name.toLowerCase();
|
|
194
|
+
break;
|
|
195
|
+
case 'artifactCount':
|
|
196
|
+
aVal = a.artifactCount || 0;
|
|
197
|
+
bVal = b.artifactCount || 0;
|
|
198
|
+
break;
|
|
199
|
+
case 'lastActivity':
|
|
200
|
+
aVal = a.lastActivity ? new Date(a.lastActivity) : new Date(0);
|
|
201
|
+
bVal = b.lastActivity ? new Date(b.lastActivity) : new Date(0);
|
|
202
|
+
break;
|
|
203
|
+
case 'joinDate':
|
|
204
|
+
aVal = new Date(a.joinDate || 0);
|
|
205
|
+
bVal = new Date(b.joinDate || 0);
|
|
206
|
+
break;
|
|
207
|
+
default:
|
|
208
|
+
aVal = a.name.toLowerCase();
|
|
209
|
+
bVal = b.name.toLowerCase();
|
|
210
|
+
}
|
|
211
|
+
if (aVal < bVal)
|
|
212
|
+
return sortOrder === 'asc' ? -1 : 1;
|
|
213
|
+
if (aVal > bVal)
|
|
214
|
+
return sortOrder === 'asc' ? 1 : -1;
|
|
215
|
+
return 0;
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
buildProfileUrl(authorId) {
|
|
219
|
+
// Build URL to author's profile page
|
|
220
|
+
return `/browse/authors/${authorId}`;
|
|
221
|
+
}
|
|
222
|
+
extractCategories(artifacts) {
|
|
223
|
+
const categories = new Set();
|
|
224
|
+
artifacts.forEach(artifact => {
|
|
225
|
+
if (artifact.metadata?.category) {
|
|
226
|
+
categories.add(artifact.metadata.category);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
return Array.from(categories);
|
|
230
|
+
}
|
|
231
|
+
extractTechnologies(artifacts) {
|
|
232
|
+
const technologies = new Set();
|
|
233
|
+
artifacts.forEach(artifact => {
|
|
234
|
+
if (artifact.metadata?.technology) {
|
|
235
|
+
technologies.add(artifact.metadata.technology);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
return Array.from(technologies);
|
|
239
|
+
}
|
|
240
|
+
getLastActivity(artifacts) {
|
|
241
|
+
if (artifacts.length === 0)
|
|
242
|
+
return null;
|
|
243
|
+
const dates = artifacts
|
|
244
|
+
.map(artifact => artifact.lastUpdated || artifact.publishedAt)
|
|
245
|
+
.filter(date => date)
|
|
246
|
+
.map(date => new Date(date))
|
|
247
|
+
.sort((a, b) => b.getTime() - a.getTime());
|
|
248
|
+
return dates.length > 0 ? dates[0].toISOString() : null;
|
|
249
|
+
}
|
|
250
|
+
getPopularArtifacts(artifacts) {
|
|
251
|
+
// Return top 3 most "popular" artifacts (simplified calculation)
|
|
252
|
+
return artifacts
|
|
253
|
+
.map(artifact => ({
|
|
254
|
+
name: artifact.metadata?.name || 'Unknown',
|
|
255
|
+
path: artifact.path,
|
|
256
|
+
category: artifact.metadata?.category,
|
|
257
|
+
downloads: Math.floor(Math.random() * 1000) // Simulated
|
|
258
|
+
}))
|
|
259
|
+
.sort((a, b) => b.downloads - a.downloads)
|
|
260
|
+
.slice(0, 3);
|
|
261
|
+
}
|
|
262
|
+
calculateTotalDownloads(artifacts) {
|
|
263
|
+
// Simulated total downloads calculation
|
|
264
|
+
return artifacts.reduce((total, _) => total + Math.floor(Math.random() * 100), 0);
|
|
265
|
+
}
|
|
266
|
+
calculateAverageRating(artifacts) {
|
|
267
|
+
if (artifacts.length === 0)
|
|
268
|
+
return 0;
|
|
269
|
+
// Simulated average rating calculation
|
|
270
|
+
const totalRating = artifacts.reduce((sum, _) => sum + (3.0 + Math.random() * 2), 0);
|
|
271
|
+
return Math.round((totalRating / artifacts.length) * 10) / 10;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
//# sourceMappingURL=ListAuthorsController.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
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 SearchArtifactsController 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 applySorting;
|
|
11
|
+
private getDownloadCount;
|
|
12
|
+
private calculateRating;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=SearchArtifactsController.d.ts.map
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { BaseToolController } from '../base/BaseToolController.js';
|
|
2
|
+
import { ValidationUtils } from '../../utils/ValidationUtils.js';
|
|
3
|
+
export class SearchArtifactsController extends BaseToolController {
|
|
4
|
+
constructor(ucmClient, logger) {
|
|
5
|
+
super(ucmClient, logger);
|
|
6
|
+
}
|
|
7
|
+
get name() {
|
|
8
|
+
return 'mcp_ucm_search_artifacts';
|
|
9
|
+
}
|
|
10
|
+
get description() {
|
|
11
|
+
return 'Search UCM artifacts by name, description, tags, or metadata with advanced filtering options';
|
|
12
|
+
}
|
|
13
|
+
get inputSchema() {
|
|
14
|
+
return {
|
|
15
|
+
type: 'object',
|
|
16
|
+
properties: {
|
|
17
|
+
query: {
|
|
18
|
+
type: 'string',
|
|
19
|
+
description: 'Search query to match against artifact names, descriptions, and tags',
|
|
20
|
+
minLength: 1,
|
|
21
|
+
maxLength: 200
|
|
22
|
+
},
|
|
23
|
+
filters: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
author: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
description: 'Filter by author ID',
|
|
29
|
+
pattern: '^[a-zA-Z0-9\\-_]+$'
|
|
30
|
+
},
|
|
31
|
+
category: {
|
|
32
|
+
type: 'string',
|
|
33
|
+
enum: ['commands', 'services', 'patterns', 'implementations', 'contracts', 'guidance'],
|
|
34
|
+
description: 'Filter by artifact category'
|
|
35
|
+
},
|
|
36
|
+
technology: {
|
|
37
|
+
type: 'string',
|
|
38
|
+
description: 'Filter by technology (e.g., typescript, python)',
|
|
39
|
+
pattern: '^[a-zA-Z0-9\\-_]+$'
|
|
40
|
+
},
|
|
41
|
+
minRating: {
|
|
42
|
+
type: 'number',
|
|
43
|
+
minimum: 0,
|
|
44
|
+
maximum: 5,
|
|
45
|
+
description: 'Minimum rating score'
|
|
46
|
+
},
|
|
47
|
+
contractVersion: {
|
|
48
|
+
type: 'string',
|
|
49
|
+
pattern: '^[0-9]+\\.[0-9]+$',
|
|
50
|
+
description: 'Filter by contract version (e.g., "1.0")'
|
|
51
|
+
},
|
|
52
|
+
hasExamples: {
|
|
53
|
+
type: 'boolean',
|
|
54
|
+
description: 'Filter artifacts that have usage examples'
|
|
55
|
+
},
|
|
56
|
+
updatedSince: {
|
|
57
|
+
type: 'string',
|
|
58
|
+
format: 'date',
|
|
59
|
+
description: 'Filter artifacts updated since this date (YYYY-MM-DD)'
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
pagination: {
|
|
64
|
+
type: 'object',
|
|
65
|
+
properties: {
|
|
66
|
+
limit: {
|
|
67
|
+
type: 'number',
|
|
68
|
+
default: 20,
|
|
69
|
+
minimum: 1,
|
|
70
|
+
maximum: 100,
|
|
71
|
+
description: 'Number of results to return'
|
|
72
|
+
},
|
|
73
|
+
offset: {
|
|
74
|
+
type: 'number',
|
|
75
|
+
default: 0,
|
|
76
|
+
minimum: 0,
|
|
77
|
+
description: 'Number of results to skip'
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
sortBy: {
|
|
82
|
+
type: 'string',
|
|
83
|
+
enum: ['relevance', 'name', 'author', 'updated', 'created', 'version'],
|
|
84
|
+
default: 'relevance',
|
|
85
|
+
description: 'Sort results by specified field'
|
|
86
|
+
},
|
|
87
|
+
sortOrder: {
|
|
88
|
+
type: 'string',
|
|
89
|
+
enum: ['asc', 'desc'],
|
|
90
|
+
default: 'desc',
|
|
91
|
+
description: 'Sort order (ascending or descending)'
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
required: ['query']
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
async handleExecute(params) {
|
|
98
|
+
const { query, filters = {}, pagination = {}, sortBy = 'relevance', sortOrder = 'desc' } = params;
|
|
99
|
+
// Validate and sanitize inputs
|
|
100
|
+
const sanitizedQuery = ValidationUtils.sanitizeSearchQuery(query);
|
|
101
|
+
const limit = pagination.limit || 20;
|
|
102
|
+
const offset = pagination.offset || 0;
|
|
103
|
+
ValidationUtils.validatePageParams(offset, limit);
|
|
104
|
+
// Validate optional filters
|
|
105
|
+
if (filters.author) {
|
|
106
|
+
ValidationUtils.validateAuthorId(filters.author);
|
|
107
|
+
}
|
|
108
|
+
if (filters.category) {
|
|
109
|
+
ValidationUtils.validateCategory(filters.category);
|
|
110
|
+
}
|
|
111
|
+
if (filters.contractVersion && !filters.contractVersion.match(/^[0-9]+\.[0-9]+$/)) {
|
|
112
|
+
throw this.formatError(new Error('Contract version must be in format X.Y'));
|
|
113
|
+
}
|
|
114
|
+
this.logger.debug('SearchArtifactsController', `Searching artifacts: "${sanitizedQuery}"`);
|
|
115
|
+
try {
|
|
116
|
+
// Build comprehensive search filters
|
|
117
|
+
const searchFilters = {
|
|
118
|
+
...filters,
|
|
119
|
+
limit,
|
|
120
|
+
offset,
|
|
121
|
+
sortBy,
|
|
122
|
+
sortOrder
|
|
123
|
+
};
|
|
124
|
+
// Execute search through UCM API
|
|
125
|
+
const results = await this.ucmClient.searchArtifacts({
|
|
126
|
+
...searchFilters,
|
|
127
|
+
// Note: Our current API doesn't support text search, using category filter instead
|
|
128
|
+
});
|
|
129
|
+
// Transform and enrich results
|
|
130
|
+
const transformedArtifacts = results.map(artifact => ({
|
|
131
|
+
id: artifact.id,
|
|
132
|
+
name: artifact.metadata?.name || 'Unknown',
|
|
133
|
+
path: artifact.path,
|
|
134
|
+
description: artifact.metadata?.description || '',
|
|
135
|
+
author: artifact.metadata?.author || '',
|
|
136
|
+
category: artifact.metadata?.category || '',
|
|
137
|
+
subcategory: artifact.metadata?.subcategory || '',
|
|
138
|
+
technology: artifact.metadata?.technology || null,
|
|
139
|
+
version: artifact.metadata?.version || '',
|
|
140
|
+
contractVersion: artifact.metadata?.contractVersion || null,
|
|
141
|
+
tags: artifact.metadata?.tags || [],
|
|
142
|
+
dependencies: artifact.metadata?.dependencies || {},
|
|
143
|
+
hasExamples: !!(artifact.examples && artifact.examples.length > 0),
|
|
144
|
+
lastUpdated: artifact.lastUpdated,
|
|
145
|
+
publishedAt: artifact.publishedAt,
|
|
146
|
+
downloadCount: this.getDownloadCount(artifact), // Simulated for demo
|
|
147
|
+
rating: this.calculateRating(artifact) // Simulated for demo
|
|
148
|
+
}));
|
|
149
|
+
// Apply client-side sorting if needed (fallback)
|
|
150
|
+
const sortedArtifacts = this.applySorting(transformedArtifacts, sortBy, sortOrder);
|
|
151
|
+
this.logger.info('SearchArtifactsController', `Found ${sortedArtifacts.length} artifacts`);
|
|
152
|
+
return {
|
|
153
|
+
artifacts: sortedArtifacts,
|
|
154
|
+
pagination: {
|
|
155
|
+
total: results.length, // In real implementation, this would come from API
|
|
156
|
+
offset,
|
|
157
|
+
limit,
|
|
158
|
+
hasMore: results.length === limit
|
|
159
|
+
},
|
|
160
|
+
searchQuery: sanitizedQuery,
|
|
161
|
+
appliedFilters: filters,
|
|
162
|
+
sorting: {
|
|
163
|
+
sortBy,
|
|
164
|
+
sortOrder
|
|
165
|
+
},
|
|
166
|
+
searchMetadata: {
|
|
167
|
+
searchType: 'general-search',
|
|
168
|
+
timestamp: new Date().toISOString(),
|
|
169
|
+
executionTimeMs: 0 // Would be measured in real implementation
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
this.logger.error('SearchArtifactsController', 'Artifact search failed', '', error);
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
applySorting(artifacts, sortBy, sortOrder) {
|
|
179
|
+
return artifacts.sort((a, b) => {
|
|
180
|
+
let aVal, bVal;
|
|
181
|
+
switch (sortBy) {
|
|
182
|
+
case 'name':
|
|
183
|
+
aVal = a.name.toLowerCase();
|
|
184
|
+
bVal = b.name.toLowerCase();
|
|
185
|
+
break;
|
|
186
|
+
case 'author':
|
|
187
|
+
aVal = a.author.toLowerCase();
|
|
188
|
+
bVal = b.author.toLowerCase();
|
|
189
|
+
break;
|
|
190
|
+
case 'updated':
|
|
191
|
+
aVal = new Date(a.lastUpdated);
|
|
192
|
+
bVal = new Date(b.lastUpdated);
|
|
193
|
+
break;
|
|
194
|
+
case 'version':
|
|
195
|
+
aVal = a.version;
|
|
196
|
+
bVal = b.version;
|
|
197
|
+
break;
|
|
198
|
+
case 'rating':
|
|
199
|
+
aVal = a.rating;
|
|
200
|
+
bVal = b.rating;
|
|
201
|
+
break;
|
|
202
|
+
default: // relevance
|
|
203
|
+
aVal = a.rating || 0;
|
|
204
|
+
bVal = b.rating || 0;
|
|
205
|
+
}
|
|
206
|
+
if (aVal < bVal)
|
|
207
|
+
return sortOrder === 'asc' ? -1 : 1;
|
|
208
|
+
if (aVal > bVal)
|
|
209
|
+
return sortOrder === 'asc' ? 1 : -1;
|
|
210
|
+
return 0;
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
getDownloadCount(artifact) {
|
|
214
|
+
// Simulated download count - in real implementation this would come from database
|
|
215
|
+
return Math.floor(Math.random() * 1000);
|
|
216
|
+
}
|
|
217
|
+
calculateRating(artifact) {
|
|
218
|
+
// Simulated rating calculation - in real implementation this would be based on user reviews
|
|
219
|
+
const baseRating = 3.0;
|
|
220
|
+
const hasDescription = artifact.metadata?.description ? 0.5 : 0;
|
|
221
|
+
const hasExamples = artifact.examples?.length > 0 ? 0.3 : 0;
|
|
222
|
+
const hasTags = artifact.metadata?.tags?.length > 0 ? 0.2 : 0;
|
|
223
|
+
return Math.min(baseRating + hasDescription + hasExamples + hasTags, 5.0);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=SearchArtifactsController.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=ListNamespaceController.d.ts.map
|
|
@@ -0,0 +1,35 @@
|
|
|
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 ExploreNamespaceController 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 exploreNamespace;
|
|
11
|
+
private getDirectContent;
|
|
12
|
+
private getArtifactsAtPath;
|
|
13
|
+
private applyFilters;
|
|
14
|
+
private processArtifacts;
|
|
15
|
+
private getNodeSummary;
|
|
16
|
+
private calculateNodeStatistics;
|
|
17
|
+
private sortNodes;
|
|
18
|
+
private generateNavigationHints;
|
|
19
|
+
private buildBreadcrumbs;
|
|
20
|
+
private isValidNamespacePath;
|
|
21
|
+
private getNameFromPath;
|
|
22
|
+
private determineNodeType;
|
|
23
|
+
private calculateLevel;
|
|
24
|
+
private generateContentPreview;
|
|
25
|
+
private getLastActivity;
|
|
26
|
+
private countTotalNodes;
|
|
27
|
+
private calculateMaxDepth;
|
|
28
|
+
private hasMoreContent;
|
|
29
|
+
private countArtifactsRecursively;
|
|
30
|
+
private collectMetadataRecursively;
|
|
31
|
+
private getLastActivityRecursively;
|
|
32
|
+
private calculatePopularityScore;
|
|
33
|
+
private findInterestingPaths;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=ExploreNamespaceController.d.ts.map
|