@utaba/ucm-mcp-server 1.1.4 → 1.1.6
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/dist/clients/UcmApiClient.d.ts +16 -1
- package/dist/clients/UcmApiClient.js +25 -2
- package/dist/publish/package.json +37 -0
- package/dist/server/ToolRegistry.js +4 -0
- package/dist/tools/core/EditArtifactMetadataTool.d.ts +12 -0
- package/dist/tools/core/EditArtifactMetadataTool.js +173 -0
- package/dist/tools/core/PublishArtifactFromFileTool.js +10 -2
- package/dist/tools/core/PublishArtifactTool.js +10 -2
- package/dist/tools/core/SearchArtifactsTool.js +46 -20
- package/dist/tools/repository/CreateRepositoryTool.js +1 -1
- package/dist/tools/repository/DeleteRepositoryGuidanceTool.js +1 -2
- package/dist/tools/utility/HealthCheckController.js +6 -2
- package/dist/tools/utility/SubmitFeedbackTool.d.ts +16 -0
- package/dist/tools/utility/SubmitFeedbackTool.js +68 -0
- package/package.json +1 -1
- package/package.json.backup +1 -1
|
@@ -30,13 +30,14 @@ export declare class UcmApiClient {
|
|
|
30
30
|
limit?: number;
|
|
31
31
|
}): Promise<ArtifactData[]>;
|
|
32
32
|
searchArtifactsByText(searchParams: {
|
|
33
|
-
searchText
|
|
33
|
+
searchText?: string;
|
|
34
34
|
namespace?: string;
|
|
35
35
|
author?: string;
|
|
36
36
|
repository?: string;
|
|
37
37
|
category?: string;
|
|
38
38
|
subcategory?: string;
|
|
39
39
|
filename?: string;
|
|
40
|
+
tags?: string;
|
|
40
41
|
offset?: number;
|
|
41
42
|
limit?: number;
|
|
42
43
|
}): Promise<{
|
|
@@ -92,5 +93,19 @@ export declare class UcmApiClient {
|
|
|
92
93
|
};
|
|
93
94
|
_links?: any;
|
|
94
95
|
}>;
|
|
96
|
+
submitFeedback(data: {
|
|
97
|
+
title: string;
|
|
98
|
+
body: string;
|
|
99
|
+
reportType: 'issue' | 'feedback';
|
|
100
|
+
tags?: string;
|
|
101
|
+
}): Promise<any>;
|
|
102
|
+
editArtifactMetadata(author: string, repository: string, category: string, subcategory: string, filename: string, data: {
|
|
103
|
+
updateDescription?: string;
|
|
104
|
+
updateNamespace?: string;
|
|
105
|
+
updateFilename?: string;
|
|
106
|
+
updateMimeType?: string;
|
|
107
|
+
updateTechnology?: string;
|
|
108
|
+
updateTags?: string;
|
|
109
|
+
}): Promise<any>;
|
|
95
110
|
}
|
|
96
111
|
//# sourceMappingURL=UcmApiClient.d.ts.map
|
|
@@ -188,7 +188,13 @@ export class UcmApiClient {
|
|
|
188
188
|
// New API structure with query params
|
|
189
189
|
Object.entries(data.queryParams).forEach(([key, value]) => {
|
|
190
190
|
if (value !== undefined && value !== null && value !== '') {
|
|
191
|
-
|
|
191
|
+
// Handle arrays (e.g., tags) by JSON stringifying them
|
|
192
|
+
if (Array.isArray(value)) {
|
|
193
|
+
params.append(key, JSON.stringify(value));
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
params.append(key, String(value));
|
|
197
|
+
}
|
|
192
198
|
}
|
|
193
199
|
});
|
|
194
200
|
// Send raw content as text/plain body
|
|
@@ -216,7 +222,13 @@ export class UcmApiClient {
|
|
|
216
222
|
// New API structure with query params
|
|
217
223
|
Object.entries(data.queryParams).forEach(([key, value]) => {
|
|
218
224
|
if (value !== undefined && value !== null && value !== '') {
|
|
219
|
-
|
|
225
|
+
// Handle arrays (e.g., tags) by JSON stringifying them
|
|
226
|
+
if (Array.isArray(value)) {
|
|
227
|
+
params.append(key, JSON.stringify(value));
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
params.append(key, String(value));
|
|
231
|
+
}
|
|
220
232
|
}
|
|
221
233
|
});
|
|
222
234
|
// Send raw content as text/plain body
|
|
@@ -346,5 +358,16 @@ export class UcmApiClient {
|
|
|
346
358
|
const response = await client.get(url);
|
|
347
359
|
return response.data;
|
|
348
360
|
}
|
|
361
|
+
async submitFeedback(data) {
|
|
362
|
+
const client = this.ensureClient();
|
|
363
|
+
const response = await client.post('/api/v1/feedback', data);
|
|
364
|
+
return response.data;
|
|
365
|
+
}
|
|
366
|
+
async editArtifactMetadata(author, repository, category, subcategory, filename, data) {
|
|
367
|
+
const client = this.ensureClient();
|
|
368
|
+
const url = this.buildApiPath('authors', author, repository, category, subcategory, filename) + '/edit';
|
|
369
|
+
const response = await client.post(url, data);
|
|
370
|
+
return response.data;
|
|
371
|
+
}
|
|
349
372
|
}
|
|
350
373
|
//# sourceMappingURL=UcmApiClient.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ucm-mcp-server",
|
|
3
|
+
"version": "1.1.5",
|
|
4
|
+
"description": "Universal Context Manager MCP Server - AI-native artifact management",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"ucm-mcp-server": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"mcp",
|
|
12
|
+
"model-context-protocol",
|
|
13
|
+
"ucm",
|
|
14
|
+
"universal-context-manager",
|
|
15
|
+
"context-engineering",
|
|
16
|
+
"ai",
|
|
17
|
+
"context-manager",
|
|
18
|
+
"utaba"
|
|
19
|
+
],
|
|
20
|
+
"author": {
|
|
21
|
+
"name": "Utaba AI",
|
|
22
|
+
"url": "https://utaba.ai"
|
|
23
|
+
},
|
|
24
|
+
"license": "BSD-3-Clause",
|
|
25
|
+
"homepage": "https://ucm.utaba.ai",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
28
|
+
"commander": "^14.0.0",
|
|
29
|
+
"axios": "^1.10.0"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=18.0.0"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -5,6 +5,7 @@ import { QuickstartTool } from '../tools/utility/QuickstartTool.js';
|
|
|
5
5
|
import { AuthorIndexTool } from '../tools/utility/AuthorIndexTool.js';
|
|
6
6
|
import { AuthorRecentsTool } from '../tools/utility/AuthorRecentsTool.js';
|
|
7
7
|
import { ListRepositoriesTool } from '../tools/utility/ListRepositoriesTool.js';
|
|
8
|
+
import { SubmitFeedbackTool } from '../tools/utility/SubmitFeedbackTool.js';
|
|
8
9
|
// Import core tools
|
|
9
10
|
import { GetArtifactTool } from '../tools/core/GetArtifactTool.js';
|
|
10
11
|
import { GetChunkTool } from '../tools/core/GetChunkTool.js';
|
|
@@ -14,6 +15,7 @@ import { ListArtifactsTool } from '../tools/core/ListArtifactsTool.js';
|
|
|
14
15
|
import { DeleteArtifactTool } from '../tools/core/DeleteArtifactTool.js';
|
|
15
16
|
import { GetArtifactVersionsTool } from '../tools/core/GetArtifactVersionsTool.js';
|
|
16
17
|
import { SearchArtifactsTool } from '../tools/core/SearchArtifactsTool.js';
|
|
18
|
+
import { EditArtifactMetadataTool } from '../tools/core/EditArtifactMetadataTool.js';
|
|
17
19
|
// Import repository tools
|
|
18
20
|
import { CreateRepositoryTool } from '../tools/repository/CreateRepositoryTool.js';
|
|
19
21
|
import { GetRepositoryTool } from '../tools/repository/GetRepositoryTool.js';
|
|
@@ -41,6 +43,7 @@ export class ToolRegistry {
|
|
|
41
43
|
this.registerTool(new AuthorIndexTool(this.ucmClient, this.logger, this.authorId));
|
|
42
44
|
this.registerTool(new AuthorRecentsTool(this.ucmClient, this.logger, this.authorId));
|
|
43
45
|
this.registerTool(new ListRepositoriesTool(this.ucmClient, this.logger, this.authorId));
|
|
46
|
+
this.registerTool(new SubmitFeedbackTool(this.ucmClient, this.logger, this.authorId));
|
|
44
47
|
// Register core tools
|
|
45
48
|
this.registerTool(new GetArtifactTool(this.ucmClient, this.logger, this.authorId, this.trustedAuthors));
|
|
46
49
|
this.registerTool(new GetChunkTool(this.ucmClient, this.logger, this.authorId));
|
|
@@ -50,6 +53,7 @@ export class ToolRegistry {
|
|
|
50
53
|
this.registerTool(new DeleteArtifactTool(this.ucmClient, this.logger, this.authorId));
|
|
51
54
|
this.registerTool(new GetArtifactVersionsTool(this.ucmClient, this.logger, this.authorId));
|
|
52
55
|
this.registerTool(new SearchArtifactsTool(this.ucmClient, this.logger, this.authorId));
|
|
56
|
+
this.registerTool(new EditArtifactMetadataTool(this.ucmClient, this.logger, this.authorId));
|
|
53
57
|
// Register repository tools
|
|
54
58
|
this.registerTool(new CreateRepositoryTool(this.ucmClient, this.logger, this.authorId));
|
|
55
59
|
this.registerTool(new GetRepositoryTool(this.ucmClient, this.logger, this.authorId));
|
|
@@ -0,0 +1,12 @@
|
|
|
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 EditArtifactMetadataTool 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
|
+
protected validateParams(params: any): void;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=EditArtifactMetadataTool.d.ts.map
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { BaseToolController } from '../base/BaseToolController.js';
|
|
2
|
+
import { parsePath } from '../../utils/PathUtils.js';
|
|
3
|
+
import { McpError, McpErrorCode } from '../../utils/McpErrorHandler.js';
|
|
4
|
+
export class EditArtifactMetadataTool extends BaseToolController {
|
|
5
|
+
constructor(ucmClient, logger, publishingAuthorId) {
|
|
6
|
+
super(ucmClient, logger, publishingAuthorId);
|
|
7
|
+
}
|
|
8
|
+
get name() {
|
|
9
|
+
return 'mcp_ucm_edit_artifact_metadata';
|
|
10
|
+
}
|
|
11
|
+
get description() {
|
|
12
|
+
return 'Edit artifact metadata and optionally move artifacts to new locations. Supports updating description, namespace, filename, MIME type, technology, and tags. Move operations affect all versions while metadata-only updates apply to latest version only.';
|
|
13
|
+
}
|
|
14
|
+
get inputSchema() {
|
|
15
|
+
return {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
path: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
description: `Full artifact path (e.g., "${this.publishingAuthorId || '1234567890'}/main/commands/user/CreateUserCommand.ts")`,
|
|
21
|
+
minLength: 1,
|
|
22
|
+
maxLength: 250
|
|
23
|
+
},
|
|
24
|
+
updateDescription: {
|
|
25
|
+
type: 'string',
|
|
26
|
+
description: 'New description for the artifact (up to 1000 characters)',
|
|
27
|
+
maxLength: 1000
|
|
28
|
+
},
|
|
29
|
+
updateNamespace: {
|
|
30
|
+
type: 'string',
|
|
31
|
+
description: 'New namespace path to move artifact to (e.g., "utaba/main/services/user")',
|
|
32
|
+
maxLength: 200
|
|
33
|
+
},
|
|
34
|
+
updateFilename: {
|
|
35
|
+
type: 'string',
|
|
36
|
+
description: 'New filename for the artifact (e.g., "UserService.ts")',
|
|
37
|
+
maxLength: 100
|
|
38
|
+
},
|
|
39
|
+
updateMimeType: {
|
|
40
|
+
type: 'string',
|
|
41
|
+
description: 'New MIME type for the artifact (e.g., "application/typescript")',
|
|
42
|
+
maxLength: 100
|
|
43
|
+
},
|
|
44
|
+
updateTechnology: {
|
|
45
|
+
type: 'string',
|
|
46
|
+
description: 'New technology stack (e.g., "typescript", "python", "nextjs")',
|
|
47
|
+
maxLength: 50
|
|
48
|
+
},
|
|
49
|
+
updateTags: {
|
|
50
|
+
type: 'string',
|
|
51
|
+
description: 'New comma-separated tags for categorization',
|
|
52
|
+
maxLength: 500
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
required: ['path'],
|
|
56
|
+
additionalProperties: false
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
async handleExecute(params) {
|
|
60
|
+
const { path, updateDescription, updateNamespace, updateFilename, updateMimeType, updateTechnology, updateTags } = params;
|
|
61
|
+
this.logger.debug('EditArtifactMetadataTool', `Editing artifact metadata: ${path}`, '', {
|
|
62
|
+
hasDescriptionUpdate: !!updateDescription,
|
|
63
|
+
hasNamespaceUpdate: !!updateNamespace,
|
|
64
|
+
hasFilenameUpdate: !!updateFilename
|
|
65
|
+
});
|
|
66
|
+
try {
|
|
67
|
+
// Parse the current path to extract components
|
|
68
|
+
const pathComponents = parsePath(path);
|
|
69
|
+
// Validate path components exist including repository
|
|
70
|
+
if (!pathComponents.author || !pathComponents.repository || !pathComponents.category || !pathComponents.subcategory || !pathComponents.filename) {
|
|
71
|
+
throw new McpError(McpErrorCode.InvalidParams, 'Path must contain author, repository, category, subcategory, and filename (e.g., "utaba/main/commands/user/CreateUserCommand.ts")');
|
|
72
|
+
}
|
|
73
|
+
// Validate that at least one update field is provided
|
|
74
|
+
const hasUpdates = !!(updateDescription || updateNamespace || updateFilename || updateMimeType || updateTechnology || updateTags);
|
|
75
|
+
if (!hasUpdates) {
|
|
76
|
+
throw new McpError(McpErrorCode.InvalidParams, 'At least one update field must be provided (updateDescription, updateNamespace, updateFilename, updateMimeType, updateTechnology, or updateTags)');
|
|
77
|
+
}
|
|
78
|
+
// Prepare the edit data
|
|
79
|
+
const editData = {};
|
|
80
|
+
if (updateDescription !== undefined)
|
|
81
|
+
editData.updateDescription = updateDescription;
|
|
82
|
+
if (updateNamespace !== undefined)
|
|
83
|
+
editData.updateNamespace = updateNamespace;
|
|
84
|
+
if (updateFilename !== undefined)
|
|
85
|
+
editData.updateFilename = updateFilename;
|
|
86
|
+
if (updateMimeType !== undefined)
|
|
87
|
+
editData.updateMimeType = updateMimeType;
|
|
88
|
+
if (updateTechnology !== undefined)
|
|
89
|
+
editData.updateTechnology = updateTechnology;
|
|
90
|
+
if (updateTags !== undefined)
|
|
91
|
+
editData.updateTags = updateTags;
|
|
92
|
+
// Call the API to edit the artifact metadata
|
|
93
|
+
const result = await this.ucmClient.editArtifactMetadata(pathComponents.author, pathComponents.repository, pathComponents.category, pathComponents.subcategory, pathComponents.filename, editData);
|
|
94
|
+
// Handle response structure - API might return wrapped in 'data' or direct
|
|
95
|
+
const responseData = result.data || result;
|
|
96
|
+
// Build successful response with operation details
|
|
97
|
+
const isMove = !!(updateNamespace || updateFilename);
|
|
98
|
+
const operation = isMove ? 'move' : 'metadata_update';
|
|
99
|
+
const response = {
|
|
100
|
+
success: true,
|
|
101
|
+
operation,
|
|
102
|
+
artifact: {
|
|
103
|
+
originalPath: path,
|
|
104
|
+
newPath: isMove ?
|
|
105
|
+
`${updateNamespace || `${pathComponents.author}/${pathComponents.repository}/${pathComponents.category}/${pathComponents.subcategory}`}/${updateFilename || pathComponents.filename}` :
|
|
106
|
+
path,
|
|
107
|
+
updatedFields: Object.keys(editData),
|
|
108
|
+
affectedVersions: responseData.affectedVersions || (isMove ? 'all' : 'latest'),
|
|
109
|
+
},
|
|
110
|
+
details: responseData.details || 'Artifact metadata updated successfully',
|
|
111
|
+
_links: responseData._links || {}
|
|
112
|
+
};
|
|
113
|
+
this.logger.info('EditArtifactMetadataTool', `Artifact edited successfully: ${path}`, '', {
|
|
114
|
+
operation,
|
|
115
|
+
updatedFields: Object.keys(editData),
|
|
116
|
+
affectedVersions: response.artifact.affectedVersions
|
|
117
|
+
});
|
|
118
|
+
return response;
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
this.logger.error('EditArtifactMetadataTool', `Failed to edit artifact: ${path}`, '', error);
|
|
122
|
+
// Enhanced error handling for edit operations
|
|
123
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
124
|
+
if (errorMessage?.includes('not found')) {
|
|
125
|
+
throw new McpError(McpErrorCode.InvalidParams, `Artifact not found at path: ${path}. Please verify the path is correct and the artifact exists.`);
|
|
126
|
+
}
|
|
127
|
+
if (errorMessage?.includes('permission') || errorMessage?.includes('unauthorized')) {
|
|
128
|
+
throw new McpError(McpErrorCode.InvalidParams, `Insufficient permissions to edit artifact at: ${path}. You must have write access to both source and destination namespaces for move operations.`);
|
|
129
|
+
}
|
|
130
|
+
if (errorMessage?.includes('destination already exists')) {
|
|
131
|
+
throw new McpError(McpErrorCode.InvalidParams, 'Destination artifact already exists. Choose a different filename or namespace for move operations.');
|
|
132
|
+
}
|
|
133
|
+
if (errorMessage?.includes('Invalid namespace format')) {
|
|
134
|
+
throw new McpError(McpErrorCode.InvalidParams, `Invalid namespace format in updateNamespace. Must follow pattern "author/repository/category/subcategory". Example: "${this.publishingAuthorId || '0000000000'}/main/services/user"`);
|
|
135
|
+
}
|
|
136
|
+
if (errorMessage?.includes('Author not found')) {
|
|
137
|
+
throw new McpError(McpErrorCode.InvalidParams, `Author not found. Ensure the path uses a valid author identifier.`);
|
|
138
|
+
}
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
validateParams(params) {
|
|
143
|
+
super.validateParams(params);
|
|
144
|
+
const { path, updateNamespace, updateFilename, updateDescription } = params;
|
|
145
|
+
// Validate path format
|
|
146
|
+
if (!path || typeof path !== 'string') {
|
|
147
|
+
throw new McpError(McpErrorCode.InvalidParams, 'path is required and must be a string');
|
|
148
|
+
}
|
|
149
|
+
// Basic path validation - should contain at least author/repository/category/subcategory/filename
|
|
150
|
+
const pathParts = path.split('/');
|
|
151
|
+
if (pathParts.length < 5) {
|
|
152
|
+
throw new McpError(McpErrorCode.InvalidParams, 'path must contain at least author/repository/category/subcategory/filename');
|
|
153
|
+
}
|
|
154
|
+
// Validate updateNamespace format if provided
|
|
155
|
+
if (updateNamespace && updateNamespace.length > 0) {
|
|
156
|
+
const namespaceParts = updateNamespace.split('/');
|
|
157
|
+
if (namespaceParts.length !== 4) {
|
|
158
|
+
throw new McpError(McpErrorCode.InvalidParams, 'updateNamespace must follow format "author/repository/category/subcategory"');
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Validate updateFilename if provided
|
|
162
|
+
if (updateFilename && updateFilename.length > 0) {
|
|
163
|
+
if (!/^[^\/\\:*?"<>|]+\.[a-zA-Z0-9]+$/.test(updateFilename)) {
|
|
164
|
+
throw new McpError(McpErrorCode.InvalidParams, 'updateFilename must be a valid filename with extension (e.g., "UserService.ts")');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// Validate description length
|
|
168
|
+
if (updateDescription && updateDescription.length > 1000) {
|
|
169
|
+
throw new McpError(McpErrorCode.InvalidParams, 'updateDescription cannot exceed 1000 characters');
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=EditArtifactMetadataTool.js.map
|
|
@@ -49,13 +49,20 @@ export class PublishArtifactFromFileTool extends BaseToolController {
|
|
|
49
49
|
mimeType: {
|
|
50
50
|
type: 'string',
|
|
51
51
|
description: 'MIME type (auto-detected if not provided)'
|
|
52
|
+
},
|
|
53
|
+
tags: {
|
|
54
|
+
type: 'array',
|
|
55
|
+
description: 'Optional array of tags for categorization',
|
|
56
|
+
items: {
|
|
57
|
+
type: 'string'
|
|
58
|
+
}
|
|
52
59
|
}
|
|
53
60
|
},
|
|
54
61
|
required: ['path', 'filename', 'fileUri']
|
|
55
62
|
};
|
|
56
63
|
}
|
|
57
64
|
async handleExecute(params) {
|
|
58
|
-
const { path, filename, fileUri, description, version, technology, mimeType } = params;
|
|
65
|
+
const { path, filename, fileUri, description, version, technology, mimeType, tags } = params;
|
|
59
66
|
this.logger.debug('PublishArtifactFromFileTool', `Publishing artifact from file: ${fileUri}`, '', {
|
|
60
67
|
path: `${path}/${filename}`,
|
|
61
68
|
version: version
|
|
@@ -89,7 +96,8 @@ export class PublishArtifactFromFileTool extends BaseToolController {
|
|
|
89
96
|
version: version ? version.replace(/^v/, '') : undefined, // Remove v prefix if present, undefined if not provided
|
|
90
97
|
description: description,
|
|
91
98
|
technology: technology,
|
|
92
|
-
mimeType: mimeType || this.detectMimeType(filename)
|
|
99
|
+
mimeType: mimeType || this.detectMimeType(filename),
|
|
100
|
+
tags: tags
|
|
93
101
|
}
|
|
94
102
|
};
|
|
95
103
|
// Always use POST endpoint - the API will detect create vs update automatically
|
|
@@ -47,13 +47,20 @@ export class PublishArtifactTool extends BaseToolController {
|
|
|
47
47
|
mimeType: {
|
|
48
48
|
type: 'string',
|
|
49
49
|
description: 'MIME type (auto-detected if not provided)'
|
|
50
|
+
},
|
|
51
|
+
tags: {
|
|
52
|
+
type: 'array',
|
|
53
|
+
description: 'Optional array of tags for categorization',
|
|
54
|
+
items: {
|
|
55
|
+
type: 'string'
|
|
56
|
+
}
|
|
50
57
|
}
|
|
51
58
|
},
|
|
52
59
|
required: ['path', 'filename', 'content']
|
|
53
60
|
};
|
|
54
61
|
}
|
|
55
62
|
async handleExecute(params) {
|
|
56
|
-
const { path, filename, content, description, version, technology, mimeType } = params;
|
|
63
|
+
const { path, filename, content, description, version, technology, mimeType, tags } = params;
|
|
57
64
|
this.logger.debug('PublishArtifactTool', `Publishing artifact: ${path}/${filename}`, '', {
|
|
58
65
|
version: version,
|
|
59
66
|
size: content.length
|
|
@@ -80,7 +87,8 @@ export class PublishArtifactTool extends BaseToolController {
|
|
|
80
87
|
version: version ? version.replace(/^v/, '') : undefined, // Remove v prefix if present, undefined if not provided
|
|
81
88
|
description: description,
|
|
82
89
|
technology: technology,
|
|
83
|
-
mimeType: mimeType || this.detectMimeType(filename)
|
|
90
|
+
mimeType: mimeType || this.detectMimeType(filename),
|
|
91
|
+
tags: tags
|
|
84
92
|
}
|
|
85
93
|
};
|
|
86
94
|
// Always use POST endpoint - the API will detect create vs update automatically
|
|
@@ -7,7 +7,7 @@ export class SearchArtifactsTool extends BaseToolController {
|
|
|
7
7
|
return 'mcp_ucm_search_artifacts';
|
|
8
8
|
}
|
|
9
9
|
get description() {
|
|
10
|
-
return `Search artifacts by text across multiple metadata fields with privacy filtering. Searches namespace, filename, and
|
|
10
|
+
return `Search artifacts by text across multiple metadata fields with privacy filtering. Searches namespace, filename, description, and tags fields. ${this.publishingAuthorId ? "Your author value is '" + this.publishingAuthorId + "'" : ''}`;
|
|
11
11
|
}
|
|
12
12
|
get inputSchema() {
|
|
13
13
|
return {
|
|
@@ -15,7 +15,7 @@ export class SearchArtifactsTool extends BaseToolController {
|
|
|
15
15
|
properties: {
|
|
16
16
|
searchText: {
|
|
17
17
|
type: 'string',
|
|
18
|
-
description: '
|
|
18
|
+
description: 'Text to search across namespace, repository, category, subcategory, filename, description, and tags (case-insensitive)',
|
|
19
19
|
minLength: 1,
|
|
20
20
|
maxLength: 200
|
|
21
21
|
},
|
|
@@ -49,6 +49,11 @@ export class SearchArtifactsTool extends BaseToolController {
|
|
|
49
49
|
description: 'Optional filename filter (e.g., "CreateUserCommand.ts")',
|
|
50
50
|
maxLength: 100
|
|
51
51
|
},
|
|
52
|
+
tags: {
|
|
53
|
+
type: 'string',
|
|
54
|
+
description: 'Optional tags filter - comma-separated list of tags to search for',
|
|
55
|
+
maxLength: 500
|
|
56
|
+
},
|
|
52
57
|
offset: {
|
|
53
58
|
type: 'number',
|
|
54
59
|
description: 'Number of items to skip for pagination (default: 0)',
|
|
@@ -61,18 +66,29 @@ export class SearchArtifactsTool extends BaseToolController {
|
|
|
61
66
|
maximum: 100
|
|
62
67
|
}
|
|
63
68
|
},
|
|
64
|
-
|
|
69
|
+
anyOf: [
|
|
70
|
+
{ required: ['searchText'] },
|
|
71
|
+
{ required: ['namespace'] },
|
|
72
|
+
{ required: ['author'] },
|
|
73
|
+
{ required: ['repository'] },
|
|
74
|
+
{ required: ['category'] },
|
|
75
|
+
{ required: ['subcategory'] },
|
|
76
|
+
{ required: ['filename'] },
|
|
77
|
+
{ required: ['tags'] }
|
|
78
|
+
]
|
|
65
79
|
};
|
|
66
80
|
}
|
|
67
81
|
async handleExecute(params) {
|
|
68
|
-
const { searchText, namespace, author, repository, category, subcategory, filename, offset, limit } = params;
|
|
69
|
-
this.logger.debug('SearchArtifactsTool', `Searching
|
|
82
|
+
const { searchText, namespace, author, repository, category, subcategory, filename, tags, offset, limit } = params;
|
|
83
|
+
this.logger.debug('SearchArtifactsTool', `Searching with filters`, '', {
|
|
84
|
+
searchText,
|
|
70
85
|
namespace,
|
|
71
86
|
author,
|
|
72
|
-
repository
|
|
87
|
+
repository,
|
|
73
88
|
category,
|
|
74
89
|
subcategory,
|
|
75
90
|
filename,
|
|
91
|
+
tags,
|
|
76
92
|
offset: offset || 0,
|
|
77
93
|
limit: limit || 20
|
|
78
94
|
});
|
|
@@ -82,28 +98,38 @@ export class SearchArtifactsTool extends BaseToolController {
|
|
|
82
98
|
searchText,
|
|
83
99
|
namespace,
|
|
84
100
|
author,
|
|
85
|
-
repository
|
|
101
|
+
repository, // Don't default to 'main' - let it search all repositories when undefined
|
|
86
102
|
category,
|
|
87
103
|
subcategory,
|
|
88
104
|
filename,
|
|
105
|
+
tags,
|
|
89
106
|
offset: offset || 0,
|
|
90
107
|
limit: limit || 20
|
|
91
108
|
});
|
|
92
|
-
this.logger.info('SearchArtifactsTool', `Found ${response.data.length} artifacts matching
|
|
109
|
+
this.logger.info('SearchArtifactsTool', `Found ${response.data.length} artifacts matching filters`, '', {
|
|
93
110
|
totalResults: response.pagination.total,
|
|
94
|
-
filters: { namespace, author, repository, category, subcategory, filename }
|
|
111
|
+
filters: { searchText, namespace, author, repository, category, subcategory, filename, tags }
|
|
95
112
|
});
|
|
96
|
-
// Return structured search results
|
|
113
|
+
// Return structured search results (only include filters that were actually provided)
|
|
114
|
+
const appliedFilters = {};
|
|
115
|
+
if (searchText)
|
|
116
|
+
appliedFilters.searchText = searchText;
|
|
117
|
+
if (namespace)
|
|
118
|
+
appliedFilters.namespace = namespace;
|
|
119
|
+
if (author)
|
|
120
|
+
appliedFilters.author = author;
|
|
121
|
+
if (repository)
|
|
122
|
+
appliedFilters.repository = repository;
|
|
123
|
+
if (category)
|
|
124
|
+
appliedFilters.category = category;
|
|
125
|
+
if (subcategory)
|
|
126
|
+
appliedFilters.subcategory = subcategory;
|
|
127
|
+
if (filename)
|
|
128
|
+
appliedFilters.filename = filename;
|
|
129
|
+
if (tags)
|
|
130
|
+
appliedFilters.tags = tags;
|
|
97
131
|
return {
|
|
98
|
-
|
|
99
|
-
filters: {
|
|
100
|
-
namespace,
|
|
101
|
-
author,
|
|
102
|
-
repository: repository || 'main',
|
|
103
|
-
category,
|
|
104
|
-
subcategory,
|
|
105
|
-
filename
|
|
106
|
-
},
|
|
132
|
+
filters: appliedFilters,
|
|
107
133
|
results: response.data,
|
|
108
134
|
pagination: response.pagination,
|
|
109
135
|
hasMore: response.pagination.hasMore,
|
|
@@ -111,7 +137,7 @@ export class SearchArtifactsTool extends BaseToolController {
|
|
|
111
137
|
};
|
|
112
138
|
}
|
|
113
139
|
catch (error) {
|
|
114
|
-
this.logger.error('SearchArtifactsTool', `Failed to search
|
|
140
|
+
this.logger.error('SearchArtifactsTool', `Failed to search with provided filters`, '', error);
|
|
115
141
|
throw error;
|
|
116
142
|
}
|
|
117
143
|
}
|
|
@@ -9,7 +9,7 @@ export class CreateRepositoryTool extends BaseToolController {
|
|
|
9
9
|
}
|
|
10
10
|
get description() {
|
|
11
11
|
const authorInfo = this.publishingAuthorId ? ` Your author id is '${this.publishingAuthorId}'.` : '';
|
|
12
|
-
return `Create a new repository for a specific author. Repository names must be unique within the author namespace and follow UCM naming conventions (3-200 characters, start with letter, alphanumeric and hyphens only, no consecutive hyphens).${authorInfo}`;
|
|
12
|
+
return `Create a new repository for a specific author. Repository names must be unique within the author namespace and follow UCM naming conventions. (3-200 characters, start with letter, alphanumeric and hyphens only, no consecutive hyphens).${authorInfo}`;
|
|
13
13
|
}
|
|
14
14
|
get inputSchema() {
|
|
15
15
|
return {
|
|
@@ -10,8 +10,7 @@ export class DeleteRepositoryGuidanceTool extends BaseToolController {
|
|
|
10
10
|
return 'mcp_ucm_delete_repository_guidance';
|
|
11
11
|
}
|
|
12
12
|
get description() {
|
|
13
|
-
|
|
14
|
-
return `Provides guidance for deleting a repository via the web UI. Repository deletion is intentionally not available through the MCP tools to prevent accidental deletion. This tool returns the web URL where the user can safely delete the repository with proper confirmation dialogs.${authorInfo}`;
|
|
13
|
+
return `Provides guidance for deleting a repository via the web UI.`;
|
|
15
14
|
}
|
|
16
15
|
get inputSchema() {
|
|
17
16
|
return {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { BaseToolController } from '../base/BaseToolController.js';
|
|
2
|
+
//import packageJson from '../../../../publish/package.json' assert { type: 'json' };
|
|
3
|
+
const version = '1.1.6'; //TODO: tried to sync this with packageJson but it didn't work.
|
|
2
4
|
export class HealthCheckController extends BaseToolController {
|
|
3
5
|
constructor(ucmClient, logger, publishingAuthorId) {
|
|
4
6
|
super(ucmClient, logger, publishingAuthorId);
|
|
@@ -25,7 +27,8 @@ export class HealthCheckController extends BaseToolController {
|
|
|
25
27
|
mcpServer: {
|
|
26
28
|
status: 'running',
|
|
27
29
|
timestamp: new Date().toISOString(),
|
|
28
|
-
uptime: process.uptime()
|
|
30
|
+
uptime: process.uptime(),
|
|
31
|
+
version: version
|
|
29
32
|
},
|
|
30
33
|
ucmApi: {
|
|
31
34
|
status: 'connected',
|
|
@@ -43,7 +46,8 @@ export class HealthCheckController extends BaseToolController {
|
|
|
43
46
|
mcpServer: {
|
|
44
47
|
status: 'running',
|
|
45
48
|
timestamp: new Date().toISOString(),
|
|
46
|
-
uptime: process.uptime()
|
|
49
|
+
uptime: process.uptime(),
|
|
50
|
+
version: version
|
|
47
51
|
},
|
|
48
52
|
ucmApi: {
|
|
49
53
|
status: 'disconnected',
|
|
@@ -0,0 +1,16 @@
|
|
|
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 SubmitFeedbackTool 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: {
|
|
10
|
+
title: string;
|
|
11
|
+
body: string;
|
|
12
|
+
reportType: 'issue' | 'feedback';
|
|
13
|
+
tags?: string;
|
|
14
|
+
}): Promise<any>;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=SubmitFeedbackTool.d.ts.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { BaseToolController } from '../base/BaseToolController.js';
|
|
2
|
+
export class SubmitFeedbackTool extends BaseToolController {
|
|
3
|
+
constructor(ucmClient, logger, publishingAuthorId) {
|
|
4
|
+
super(ucmClient, logger, publishingAuthorId);
|
|
5
|
+
}
|
|
6
|
+
get name() {
|
|
7
|
+
return 'mcp_ucm_submit_user_issue_or_feedback';
|
|
8
|
+
}
|
|
9
|
+
get description() {
|
|
10
|
+
return 'Submit user feedback, bug reports, feature requests ONLY if the user requests it and they want to send details to the UCM development team. Do NOT include sensitive or project related details. Ask the user before submitting the data if it is appropriate.';
|
|
11
|
+
}
|
|
12
|
+
get inputSchema() {
|
|
13
|
+
return {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
title: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
description: 'Brief title for the feedback or issue (1-100 characters)',
|
|
19
|
+
minLength: 1,
|
|
20
|
+
maxLength: 100
|
|
21
|
+
},
|
|
22
|
+
body: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
description: 'Detailed description of the feedback or issue (1-1000 characters)',
|
|
25
|
+
minLength: 1,
|
|
26
|
+
maxLength: 1000
|
|
27
|
+
},
|
|
28
|
+
reportType: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
enum: ['issue', 'feedback'],
|
|
31
|
+
description: 'Type of report: "issue" for bug reports and problems, "feedback" for feature requests and general feedback'
|
|
32
|
+
},
|
|
33
|
+
tags: {
|
|
34
|
+
type: 'string',
|
|
35
|
+
description: 'Optional comma-separated tags for categorization (e.g., "ui,bug,performance")',
|
|
36
|
+
maxLength: 1000
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
required: ['title', 'body', 'reportType']
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
async handleExecute(params) {
|
|
43
|
+
this.logger.info('SubmitFeedbackTool', `Submitting ${params.reportType}: ${params.title}`);
|
|
44
|
+
try {
|
|
45
|
+
const result = await this.ucmClient.submitFeedback({
|
|
46
|
+
title: params.title,
|
|
47
|
+
body: params.body,
|
|
48
|
+
reportType: params.reportType,
|
|
49
|
+
tags: params.tags
|
|
50
|
+
});
|
|
51
|
+
this.logger.info('SubmitFeedbackTool', `Successfully submitted ${params.reportType} with ID: ${result.feedbackId}`);
|
|
52
|
+
return {
|
|
53
|
+
success: true,
|
|
54
|
+
feedbackId: result.feedbackId,
|
|
55
|
+
submittedAt: result.createdAt,
|
|
56
|
+
message: result.message,
|
|
57
|
+
reportType: params.reportType,
|
|
58
|
+
title: params.title
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
63
|
+
this.logger.error('SubmitFeedbackTool', `Failed to submit ${params.reportType}: ${errorMessage}`, '', error);
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=SubmitFeedbackTool.js.map
|
package/package.json
CHANGED