@utaba/ucm-mcp-server 1.0.7 → 1.1.1

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.
@@ -29,6 +29,26 @@ export declare class UcmApiClient {
29
29
  offset?: number;
30
30
  limit?: number;
31
31
  }): Promise<ArtifactData[]>;
32
+ searchArtifactsByText(searchParams: {
33
+ searchText: string;
34
+ namespace?: string;
35
+ author?: string;
36
+ repository?: string;
37
+ category?: string;
38
+ subcategory?: string;
39
+ filename?: string;
40
+ offset?: number;
41
+ limit?: number;
42
+ }): Promise<{
43
+ data: any[];
44
+ pagination: {
45
+ offset: number;
46
+ limit: number;
47
+ total: number;
48
+ hasMore: boolean;
49
+ };
50
+ _links?: any;
51
+ }>;
32
52
  publishArtifact(author: string, repository: string, category: string, subcategory: string, data: any): Promise<ArtifactData>;
33
53
  updateArtifact(author: string, repository: string, category: string, subcategory: string, filename: string, version: string, data: any): Promise<ArtifactData>;
34
54
  deleteArtifact(author: string, repository: string, category: string, subcategory: string, filename: string, version?: string): Promise<any>;
@@ -40,6 +60,7 @@ export declare class UcmApiClient {
40
60
  }>;
41
61
  getQuickstart(): Promise<string>;
42
62
  getAuthorIndex(author: string, repository?: string): Promise<string>;
63
+ getAuthorRecents(author: string): Promise<string>;
43
64
  /**
44
65
  * Cleanup method to properly dispose of HTTP client resources
45
66
  * This helps prevent memory leaks from accumulated AbortSignal listeners
@@ -26,7 +26,7 @@ export class UcmApiClient {
26
26
  const url = error.config?.url;
27
27
  const errorMessage = apiError?.message || apiError?.error || error.message || JSON.stringify(apiError);
28
28
  if (status === 404) {
29
- return Promise.reject(new Error(`Resource not found: ${errorMessage}`));
29
+ return Promise.reject(new Error(`Resource not found at ${url}: ${errorMessage}`));
30
30
  }
31
31
  if (status >= 500) {
32
32
  // For 5xx errors, include API error details if available
@@ -168,6 +168,17 @@ export class UcmApiClient {
168
168
  const response = await client.get(`/api/v1/artifacts?${params}`);
169
169
  return response.data;
170
170
  }
171
+ async searchArtifactsByText(searchParams) {
172
+ const params = new URLSearchParams();
173
+ // Add all parameters to URL
174
+ Object.entries(searchParams).forEach(([key, value]) => {
175
+ if (value !== undefined)
176
+ params.append(key, value.toString());
177
+ });
178
+ const client = this.ensureClient();
179
+ const response = await client.get(`/api/v1/search/artifacts?${params}`);
180
+ return response.data;
181
+ }
171
182
  async publishArtifact(author, repository, category, subcategory, data) {
172
183
  // Default repository to 'main' for MVP
173
184
  const repo = repository || 'main';
@@ -274,6 +285,15 @@ export class UcmApiClient {
274
285
  });
275
286
  return response.data;
276
287
  }
288
+ async getAuthorRecents(author) {
289
+ const client = this.ensureClient();
290
+ // Author-level recent activity (using new resources API structure)
291
+ const url = `/api/v1/resources/author/${author}/recents`;
292
+ const response = await client.get(url, {
293
+ headers: { 'accept': 'text/markdown' }
294
+ });
295
+ return response.data;
296
+ }
277
297
  /**
278
298
  * Cleanup method to properly dispose of HTTP client resources
279
299
  * This helps prevent memory leaks from accumulated AbortSignal listeners
@@ -3,6 +3,7 @@ import { McpError, McpErrorCode } from '../utils/McpErrorHandler.js';
3
3
  import { HealthCheckController } from '../tools/utility/HealthCheckController.js';
4
4
  import { QuickstartTool } from '../tools/utility/QuickstartTool.js';
5
5
  import { AuthorIndexTool } from '../tools/utility/AuthorIndexTool.js';
6
+ import { AuthorRecentsTool } from '../tools/utility/AuthorRecentsTool.js';
6
7
  import { ListRepositoriesTool } from '../tools/utility/ListRepositoriesTool.js';
7
8
  // Import core tools
8
9
  import { GetArtifactTool } from '../tools/core/GetArtifactTool.js';
@@ -12,6 +13,7 @@ import { PublishArtifactFromFileTool } from '../tools/core/PublishArtifactFromFi
12
13
  import { ListArtifactsTool } from '../tools/core/ListArtifactsTool.js';
13
14
  import { DeleteArtifactTool } from '../tools/core/DeleteArtifactTool.js';
14
15
  import { GetArtifactVersionsTool } from '../tools/core/GetArtifactVersionsTool.js';
16
+ import { SearchArtifactsTool } from '../tools/core/SearchArtifactsTool.js';
15
17
  // Import repository tools
16
18
  import { CreateRepositoryTool } from '../tools/repository/CreateRepositoryTool.js';
17
19
  import { GetRepositoryTool } from '../tools/repository/GetRepositoryTool.js';
@@ -37,6 +39,7 @@ export class ToolRegistry {
37
39
  this.registerTool(new QuickstartTool(this.ucmClient, this.logger, this.authorId));
38
40
  this.registerTool(new HealthCheckController(this.ucmClient, this.logger, this.authorId));
39
41
  this.registerTool(new AuthorIndexTool(this.ucmClient, this.logger, this.authorId));
42
+ this.registerTool(new AuthorRecentsTool(this.ucmClient, this.logger, this.authorId));
40
43
  this.registerTool(new ListRepositoriesTool(this.ucmClient, this.logger, this.authorId));
41
44
  // Register core tools
42
45
  this.registerTool(new GetArtifactTool(this.ucmClient, this.logger, this.authorId, this.trustedAuthors));
@@ -46,6 +49,7 @@ export class ToolRegistry {
46
49
  this.registerTool(new ListArtifactsTool(this.ucmClient, this.logger, this.authorId));
47
50
  this.registerTool(new DeleteArtifactTool(this.ucmClient, this.logger, this.authorId));
48
51
  this.registerTool(new GetArtifactVersionsTool(this.ucmClient, this.logger, this.authorId));
52
+ this.registerTool(new SearchArtifactsTool(this.ucmClient, this.logger, this.authorId));
49
53
  // Register repository tools
50
54
  this.registerTool(new CreateRepositoryTool(this.ucmClient, this.logger, this.authorId));
51
55
  this.registerTool(new GetRepositoryTool(this.ucmClient, this.logger, this.authorId));
@@ -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 SearchArtifactsTool 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=SearchArtifactsTool.d.ts.map
@@ -0,0 +1,119 @@
1
+ import { BaseToolController } from '../base/BaseToolController.js';
2
+ export class SearchArtifactsTool extends BaseToolController {
3
+ constructor(ucmClient, logger, publishingAuthorId) {
4
+ super(ucmClient, logger, publishingAuthorId);
5
+ }
6
+ get name() {
7
+ return 'mcp_ucm_search_artifacts';
8
+ }
9
+ get description() {
10
+ return `Search artifacts by text across multiple metadata fields with privacy filtering. Searches namespace, filename, and description fields. ${this.publishingAuthorId ? "Your author value is '" + this.publishingAuthorId + "'" : ''}`;
11
+ }
12
+ get inputSchema() {
13
+ return {
14
+ type: 'object',
15
+ properties: {
16
+ searchText: {
17
+ type: 'string',
18
+ description: 'Required text to search across namespace, repository, category, subcategory, filename, and description (case-insensitive)',
19
+ minLength: 1,
20
+ maxLength: 200
21
+ },
22
+ namespace: {
23
+ type: 'string',
24
+ description: 'Optional namespace filter (e.g., "utaba/main/commands/user")',
25
+ maxLength: 200
26
+ },
27
+ author: {
28
+ type: 'string',
29
+ description: `Optional. Leave empty to search your own author and public authors. '(e.g., "' + this.publishingAuthorId + '")' : ''}`,
30
+ maxLength: 50
31
+ },
32
+ repository: {
33
+ type: 'string',
34
+ description: 'Optional repository filter',
35
+ maxLength: 200
36
+ },
37
+ category: {
38
+ type: 'string',
39
+ description: 'Optional category filter (e.g., "commands", "services", "patterns")',
40
+ maxLength: 50
41
+ },
42
+ subcategory: {
43
+ type: 'string',
44
+ description: 'Optional subcategory filter (e.g., "user", "auth", "database")',
45
+ maxLength: 50
46
+ },
47
+ filename: {
48
+ type: 'string',
49
+ description: 'Optional filename filter (e.g., "CreateUserCommand.ts")',
50
+ maxLength: 100
51
+ },
52
+ offset: {
53
+ type: 'number',
54
+ description: 'Number of items to skip for pagination (default: 0)',
55
+ minimum: 0
56
+ },
57
+ limit: {
58
+ type: 'number',
59
+ description: 'Maximum number of items to return (default: 20, max: 100)',
60
+ minimum: 1,
61
+ maximum: 100
62
+ }
63
+ },
64
+ required: ['searchText']
65
+ };
66
+ }
67
+ async handleExecute(params) {
68
+ const { searchText, namespace, author, repository, category, subcategory, filename, offset, limit } = params;
69
+ this.logger.debug('SearchArtifactsTool', `Searching for: "${searchText}" with filters`, '', {
70
+ namespace,
71
+ author,
72
+ repository: repository || 'main',
73
+ category,
74
+ subcategory,
75
+ filename,
76
+ offset: offset || 0,
77
+ limit: limit || 20
78
+ });
79
+ try {
80
+ // Call the new search API endpoint
81
+ const response = await this.ucmClient.searchArtifactsByText({
82
+ searchText,
83
+ namespace,
84
+ author,
85
+ repository: repository || 'main', // Default to 'main' for MVP
86
+ category,
87
+ subcategory,
88
+ filename,
89
+ offset: offset || 0,
90
+ limit: limit || 20
91
+ });
92
+ this.logger.info('SearchArtifactsTool', `Found ${response.data.length} artifacts matching: "${searchText}"`, '', {
93
+ totalResults: response.pagination.total,
94
+ filters: { namespace, author, repository, category, subcategory, filename }
95
+ });
96
+ // Return structured search results
97
+ return {
98
+ searchText,
99
+ filters: {
100
+ namespace,
101
+ author,
102
+ repository: repository || 'main',
103
+ category,
104
+ subcategory,
105
+ filename
106
+ },
107
+ results: response.data,
108
+ pagination: response.pagination,
109
+ hasMore: response.pagination.hasMore,
110
+ _links: response._links
111
+ };
112
+ }
113
+ catch (error) {
114
+ this.logger.error('SearchArtifactsTool', `Failed to search for: "${searchText}"`, '', error);
115
+ throw error;
116
+ }
117
+ }
118
+ }
119
+ //# sourceMappingURL=SearchArtifactsTool.js.map
@@ -37,8 +37,8 @@ export class AuthorIndexTool extends BaseToolController {
37
37
  catch (error) {
38
38
  let errorMessage = `Failed to retrieve author index for ${author}.`;
39
39
  if (this.publishingAuthorId && author != this.publishingAuthorId) {
40
- errorMessage += `Did you mean '${this.publishingAuthorId}'`;
41
- error.message = error.message += `Did you mean '${this.publishingAuthorId}'`;
40
+ errorMessage += ` did you mean '${this.publishingAuthorId}'`;
41
+ error.message = error.message += ` did you mean '${this.publishingAuthorId}'`;
42
42
  }
43
43
  this.logger.error('AuthorIndexTool', `${errorMessage}`, '', error);
44
44
  throw error;
@@ -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 AuthorRecentsTool 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=AuthorRecentsTool.d.ts.map
@@ -0,0 +1,48 @@
1
+ import { BaseToolController } from '../base/BaseToolController.js';
2
+ export class AuthorRecentsTool extends BaseToolController {
3
+ constructor(ucmClient, logger, publishingAuthorId) {
4
+ super(ucmClient, logger, publishingAuthorId);
5
+ }
6
+ get name() {
7
+ return 'mcp_ucm_get_author_recents';
8
+ }
9
+ get description() {
10
+ return `Generate author activity tracking showing the 20 most recently modified artifacts within an author context. Your author id is '${this.publishingAuthorId}'`;
11
+ }
12
+ get inputSchema() {
13
+ return {
14
+ type: 'object',
15
+ properties: {
16
+ author: {
17
+ type: 'string',
18
+ description: `Author identifier: ${this.publishingAuthorId || '1234567890'}`
19
+ }
20
+ },
21
+ required: ['author']
22
+ };
23
+ }
24
+ async handleExecute(params) {
25
+ const { author } = params;
26
+ this.logger.debug('AuthorRecentsTool', `Retrieving author recents for: ${author}`);
27
+ try {
28
+ // Get author recent activity content from API
29
+ const authorRecentsContent = await this.ucmClient.getAuthorRecents(author);
30
+ this.logger.info('AuthorRecentsTool', 'Author recents retrieved successfully', '', {
31
+ author,
32
+ contentLength: authorRecentsContent.length,
33
+ source: `UCM API /api/v1/resources/author/${author}/recents`
34
+ });
35
+ return authorRecentsContent;
36
+ }
37
+ catch (error) {
38
+ let errorMessage = `Failed to retrieve author recents for ${author}.`;
39
+ if (this.publishingAuthorId && author != this.publishingAuthorId) {
40
+ errorMessage += ` did you mean '${this.publishingAuthorId}'?`;
41
+ error.message = error.message += ` did you mean '${this.publishingAuthorId}'?`;
42
+ }
43
+ this.logger.error('AuthorRecentsTool', `${errorMessage}`, '', error);
44
+ throw error;
45
+ }
46
+ }
47
+ }
48
+ //# sourceMappingURL=AuthorRecentsTool.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@utaba/ucm-mcp-server",
3
- "version": "1.0.7",
3
+ "version": "1.1.1",
4
4
  "description": "Universal Context Manager MCP Server - AI-native artifact management",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ucm-mcp-server",
3
- "version": "1.0.7",
3
+ "version": "1.1.1",
4
4
  "description": "Universal Context Manager MCP Server - AI-native artifact management",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",