@utaba/ucm-mcp-server 1.0.5 → 1.0.7

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.
@@ -49,5 +49,27 @@ export declare class UcmApiClient {
49
49
  * Check if the client is still available for use
50
50
  */
51
51
  isAvailable(): boolean;
52
+ createRepository(author: string, data: {
53
+ repositoryName: string;
54
+ displayName?: string;
55
+ description?: string;
56
+ isPublic?: boolean;
57
+ }): Promise<any>;
58
+ getRepository(author: string, repository: string): Promise<any>;
59
+ updateRepository(author: string, repository: string, data: {
60
+ displayName?: string;
61
+ description?: string;
62
+ isPublic?: boolean;
63
+ }): Promise<any>;
64
+ deleteRepository(author: string, repository: string): Promise<any>;
65
+ listRepositories(author: string, offset?: number, limit?: number): Promise<{
66
+ data: any[];
67
+ pagination: {
68
+ offset: number;
69
+ limit: number;
70
+ total: number;
71
+ };
72
+ _links?: any;
73
+ }>;
52
74
  }
53
75
  //# sourceMappingURL=UcmApiClient.d.ts.map
@@ -147,7 +147,7 @@ export class UcmApiClient {
147
147
  const queryString = params.toString();
148
148
  // Build URL based on provided parameters for exploratory browsing
149
149
  // Default repository to 'main' for MVP
150
- const repo = repository || 'main';
150
+ const repo = repository;
151
151
  let metadataApiPath = this.buildApiPath('authors', author, repo, category, subcategory);
152
152
  metadataApiPath += queryString ? `?${queryString}` : '';
153
153
  const client = this.ensureClient();
@@ -293,5 +293,38 @@ export class UcmApiClient {
293
293
  isAvailable() {
294
294
  return this.client !== null;
295
295
  }
296
+ // Repository Management Methods
297
+ async createRepository(author, data) {
298
+ const client = this.ensureClient();
299
+ const response = await client.post(`/api/v1/authors/${author}`, data);
300
+ return response.data;
301
+ }
302
+ async getRepository(author, repository) {
303
+ const client = this.ensureClient();
304
+ const response = await client.get(`/api/v1/authors/${author}/${repository}`);
305
+ return response.data;
306
+ }
307
+ async updateRepository(author, repository, data) {
308
+ const client = this.ensureClient();
309
+ const response = await client.put(`/api/v1/authors/${author}/${repository}`, data);
310
+ return response.data;
311
+ }
312
+ async deleteRepository(author, repository) {
313
+ const client = this.ensureClient();
314
+ const response = await client.delete(`/api/v1/authors/${author}/${repository}`);
315
+ return response.data;
316
+ }
317
+ async listRepositories(author, offset, limit) {
318
+ const params = new URLSearchParams();
319
+ if (offset !== undefined)
320
+ params.append('offset', offset.toString());
321
+ if (limit !== undefined)
322
+ params.append('limit', limit.toString());
323
+ const queryString = params.toString();
324
+ const url = `/api/v1/authors/${author}${queryString ? `?${queryString}` : ''}`;
325
+ const client = this.ensureClient();
326
+ const response = await client.get(url);
327
+ return response.data;
328
+ }
296
329
  }
297
330
  //# sourceMappingURL=UcmApiClient.js.map
package/dist/index.js CHANGED
@@ -6,13 +6,13 @@ import { McpConfig } from './server/McpConfig.js';
6
6
  import { LoggerFactory } from './logging/LoggerFactory.js';
7
7
  // Increase max listeners to prevent AbortSignal memory leak warnings
8
8
  // This is a safety net while we also implement proper cleanup
9
- EventEmitter.defaultMaxListeners = 20;
9
+ EventEmitter.defaultMaxListeners = 40;
10
10
  async function main() {
11
11
  const program = new Command();
12
12
  program
13
13
  .name('ucm-mcp-server')
14
14
  .description('Universal Context Manager - Read the mcp_ucm_quickstart first to avoid mistakes')
15
- .version('1.0.0')
15
+ .version('1.0.5')
16
16
  .option('-u, --ucm-url <url>', 'UCM API base URL (defaults to https://ucm.utaba.ai)')
17
17
  .option('-p, --port <port>', 'Server port', '3001')
18
18
  .option('--log-level <level>', 'Log level', 'ERROR')
@@ -15,7 +15,7 @@ export class McpServer {
15
15
  this.config = config;
16
16
  this.logger = logger;
17
17
  this.ucmClient = new UcmApiClient(config.ucmApiUrl, config.authToken, config.requestTimeout, config.authorId);
18
- this.toolRegistry = new ToolRegistry(this.ucmClient, this.logger, config.trustedAuthors, config.authorId);
18
+ this.toolRegistry = new ToolRegistry(this.ucmClient, this.logger, config.trustedAuthors, config.authorId, config.ucmApiUrl);
19
19
  this.handler = new McpHandler(this.toolRegistry, this.logger, config.authorId);
20
20
  this.server = new Server({
21
21
  name: 'ucm-mcp-server',
@@ -10,8 +10,9 @@ export declare class ToolRegistry {
10
10
  private logger;
11
11
  private trustedAuthors;
12
12
  private authorId?;
13
+ private baseUrl?;
13
14
  private tools;
14
- constructor(ucmClient: UcmApiClient, logger: ILogger, trustedAuthors?: string[], authorId?: string | undefined);
15
+ constructor(ucmClient: UcmApiClient, logger: ILogger, trustedAuthors?: string[], authorId?: string | undefined, baseUrl?: string | undefined);
15
16
  private registerTools;
16
17
  private registerTool;
17
18
  listTools(): Promise<ToolInfo[]>;
@@ -12,17 +12,24 @@ import { PublishArtifactFromFileTool } from '../tools/core/PublishArtifactFromFi
12
12
  import { ListArtifactsTool } from '../tools/core/ListArtifactsTool.js';
13
13
  import { DeleteArtifactTool } from '../tools/core/DeleteArtifactTool.js';
14
14
  import { GetArtifactVersionsTool } from '../tools/core/GetArtifactVersionsTool.js';
15
+ // Import repository tools
16
+ import { CreateRepositoryTool } from '../tools/repository/CreateRepositoryTool.js';
17
+ import { GetRepositoryTool } from '../tools/repository/GetRepositoryTool.js';
18
+ import { UpdateRepositoryTool } from '../tools/repository/UpdateRepositoryTool.js';
19
+ import { DeleteRepositoryGuidanceTool } from '../tools/repository/DeleteRepositoryGuidanceTool.js';
15
20
  export class ToolRegistry {
16
21
  ucmClient;
17
22
  logger;
18
23
  trustedAuthors;
19
24
  authorId;
25
+ baseUrl;
20
26
  tools = new Map();
21
- constructor(ucmClient, logger, trustedAuthors = [], authorId) {
27
+ constructor(ucmClient, logger, trustedAuthors = [], authorId, baseUrl) {
22
28
  this.ucmClient = ucmClient;
23
29
  this.logger = logger;
24
30
  this.trustedAuthors = trustedAuthors;
25
31
  this.authorId = authorId;
32
+ this.baseUrl = baseUrl;
26
33
  this.registerTools();
27
34
  }
28
35
  registerTools() {
@@ -39,6 +46,11 @@ export class ToolRegistry {
39
46
  this.registerTool(new ListArtifactsTool(this.ucmClient, this.logger, this.authorId));
40
47
  this.registerTool(new DeleteArtifactTool(this.ucmClient, this.logger, this.authorId));
41
48
  this.registerTool(new GetArtifactVersionsTool(this.ucmClient, this.logger, this.authorId));
49
+ // Register repository tools
50
+ this.registerTool(new CreateRepositoryTool(this.ucmClient, this.logger, this.authorId));
51
+ this.registerTool(new GetRepositoryTool(this.ucmClient, this.logger, this.authorId));
52
+ this.registerTool(new UpdateRepositoryTool(this.ucmClient, this.logger, this.authorId));
53
+ this.registerTool(new DeleteRepositoryGuidanceTool(this.ucmClient, this.logger, this.authorId, this.baseUrl));
42
54
  this.logger.info('ToolRegistry', `Registered ${this.tools.size} MCP tools`);
43
55
  }
44
56
  registerTool(tool) {
@@ -8,7 +8,7 @@ export class ListArtifactsTool extends BaseToolController {
8
8
  return 'mcp_ucm_list_artifacts';
9
9
  }
10
10
  get description() {
11
- return `List artifacts with exploratory browsing support. Can list author categories, subcategories, or artifacts depending on path depth. ${this.publishingAuthorId ? "Your author value is '" + this.publishingAuthorId + "'" : ''}`;
11
+ return `List artifacts with exploratory browsing support. Can list authors, repositories, categories, subcategories, or artifacts depending on path depth. ${this.publishingAuthorId ? "Your author value is '" + this.publishingAuthorId + "'" : ''}`;
12
12
  }
13
13
  get inputSchema() {
14
14
  return {
@@ -61,9 +61,9 @@ export class ListArtifactsTool extends BaseToolController {
61
61
  listingType = 'subcategories';
62
62
  }
63
63
  // For MVP, validate repository is 'main' if provided
64
- if (pathSegments.length >= 2 && pathComponents.repository && pathComponents.repository !== 'main') {
65
- throw new Error(`Repository must be 'main' for MVP. Received: "${pathComponents.repository}"`);
66
- }
64
+ // if (pathSegments.length >= 2 && pathComponents.repository && pathComponents.repository !== 'main') {
65
+ // throw new Error(`Repository must be 'main' for MVP. Received: "${pathComponents.repository}"`);
66
+ // }
67
67
  this.logger.info('ListArtifactsTool', `Listed ${response.data.length} ${listingType} in: ${path}`);
68
68
  // Return the full response with pagination metadata and context
69
69
  return {
@@ -75,9 +75,12 @@ export class PublishArtifactFromFileTool extends BaseToolController {
75
75
  throw new McpError(McpErrorCode.InvalidParams, `Path must contain author, repository, category, and subcategory (e.g., "${this.publishingAuthorId || '1234567890'}/main/commands/user")`);
76
76
  }
77
77
  // Validate repository is 'main' for MVP
78
- if (pathComponents.repository !== 'main') {
79
- throw new McpError(McpErrorCode.InvalidParams, `Repository must be 'main' for MVP. Received: "${pathComponents.repository}"`);
80
- }
78
+ // if (pathComponents.repository !== 'main') {
79
+ // throw new McpError(
80
+ // McpErrorCode.InvalidParams,
81
+ // `Repository must be 'main' for MVP. Received: "${pathComponents.repository}"`
82
+ // );
83
+ // }
81
84
  // Prepare the publish data - new API expects content in body and metadata in query params
82
85
  const publishData = {
83
86
  content, // Raw text content from file
@@ -66,9 +66,12 @@ export class PublishArtifactTool extends BaseToolController {
66
66
  throw new McpError(McpErrorCode.InvalidParams, 'Path must contain author, repository, category, and subcategory (e.g., "utaba/main/commands/user")');
67
67
  }
68
68
  // Validate repository is 'main' for MVP
69
- if (pathComponents.repository !== 'main') {
70
- throw new McpError(McpErrorCode.InvalidParams, `Repository must be 'main' for MVP. Received: "${pathComponents.repository}"`);
71
- }
69
+ // if (pathComponents.repository !== 'main') {
70
+ // throw new McpError(
71
+ // McpErrorCode.InvalidParams,
72
+ // `Repository must be 'main' for MVP. Received: "${pathComponents.repository}"`
73
+ // );
74
+ // }
72
75
  // Prepare the publish data - new API expects content in body and metadata in query params
73
76
  const publishData = {
74
77
  content, // Raw text content
@@ -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 CreateRepositoryTool extends BaseToolController {
5
+ constructor(ucmClient: UcmApiClient, logger: ILogger, publishingAuthorId?: string);
6
+ get name(): string;
7
+ get description(): string;
8
+ get inputSchema(): any;
9
+ protected validateParams(params: any): void;
10
+ protected handleExecute(params: any): Promise<any>;
11
+ }
12
+ //# sourceMappingURL=CreateRepositoryTool.d.ts.map
@@ -0,0 +1,125 @@
1
+ import { BaseToolController } from '../base/BaseToolController.js';
2
+ import { McpError, McpErrorCode } from '../../utils/McpErrorHandler.js';
3
+ export class CreateRepositoryTool extends BaseToolController {
4
+ constructor(ucmClient, logger, publishingAuthorId) {
5
+ super(ucmClient, logger, publishingAuthorId);
6
+ }
7
+ get name() {
8
+ return 'mcp_ucm_create_repository';
9
+ }
10
+ get description() {
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}`;
13
+ }
14
+ get inputSchema() {
15
+ return {
16
+ type: 'object',
17
+ properties: {
18
+ author: {
19
+ type: 'string',
20
+ description: this.publishingAuthorId ? `Author identifier: ${this.publishingAuthorId}` : 'Author identifier (e.g., "utaba", "1064600359")',
21
+ minLength: 1,
22
+ maxLength: 50
23
+ },
24
+ repositoryName: {
25
+ type: 'string',
26
+ description: 'Unique repository name (3-200 characters, start with letter, alphanumeric and hyphens only)',
27
+ minLength: 3,
28
+ maxLength: 200,
29
+ pattern: '^[a-z][a-z0-9-]{2,199}$'
30
+ },
31
+ displayName: {
32
+ type: 'string',
33
+ description: 'Human-readable display name for the repository (optional)',
34
+ maxLength: 255
35
+ },
36
+ description: {
37
+ type: 'string',
38
+ description: 'Repository description (optional)',
39
+ maxLength: 1000
40
+ },
41
+ isPublic: {
42
+ type: 'boolean',
43
+ description: 'Whether the repository is public (defaults to false, public repositories not yet supported)',
44
+ default: false
45
+ }
46
+ },
47
+ required: ['author', 'repositoryName'],
48
+ additionalProperties: false
49
+ };
50
+ }
51
+ validateParams(params) {
52
+ super.validateParams(params);
53
+ // Additional validation
54
+ if (!params.author || typeof params.author !== 'string' || params.author.trim() === '') {
55
+ throw new McpError(McpErrorCode.InvalidParams, 'Author identifier is required and cannot be empty');
56
+ }
57
+ if (!params.repositoryName || typeof params.repositoryName !== 'string' || params.repositoryName.trim() === '') {
58
+ throw new McpError(McpErrorCode.InvalidParams, 'Repository name is required and cannot be empty');
59
+ }
60
+ // Validate repository name format
61
+ const namePattern = /^[a-z][a-z0-9-]{2,199}$/;
62
+ if (!namePattern.test(params.repositoryName)) {
63
+ throw new McpError(McpErrorCode.InvalidParams, 'Repository name must be 3-200 characters, start with a lowercase letter, and contain only lowercase letters, numbers, and hyphens (no consecutive hyphens)');
64
+ }
65
+ // Check for consecutive hyphens
66
+ if (params.repositoryName.includes('--')) {
67
+ throw new McpError(McpErrorCode.InvalidParams, 'Repository name cannot contain consecutive hyphens');
68
+ }
69
+ // Validate displayName length if provided
70
+ if (params.displayName && params.displayName.length > 255) {
71
+ throw new McpError(McpErrorCode.InvalidParams, 'Display name cannot exceed 255 characters');
72
+ }
73
+ // Validate description length if provided
74
+ if (params.description && params.description.length > 1000) {
75
+ throw new McpError(McpErrorCode.InvalidParams, 'Description cannot exceed 1,000 characters');
76
+ }
77
+ // Validate isPublic - public repositories are not yet supported
78
+ if (params.isPublic === true) {
79
+ throw new McpError(McpErrorCode.InvalidParams, 'Public repositories are not yet supported. Set isPublic to false or omit the parameter.');
80
+ }
81
+ }
82
+ async handleExecute(params) {
83
+ this.logger.info('CreateRepositoryTool', `Creating repository ${params.repositoryName} for author ${params.author}`);
84
+ try {
85
+ const repositoryData = {
86
+ repositoryName: params.repositoryName,
87
+ displayName: params.displayName,
88
+ description: params.description,
89
+ isPublic: params.isPublic || false
90
+ };
91
+ const result = await this.ucmClient.createRepository(params.author, repositoryData);
92
+ this.logger.info('CreateRepositoryTool', `Repository created successfully: ${params.repositoryName}`);
93
+ return {
94
+ content: [
95
+ {
96
+ type: 'text',
97
+ text: JSON.stringify({
98
+ success: true,
99
+ message: `Repository '${params.repositoryName}' created successfully for author '${params.author}'`,
100
+ repository: result,
101
+ webUrl: `${process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000'}/repository/${result.id || params.repositoryName}`
102
+ }, null, 2)
103
+ }
104
+ ]
105
+ };
106
+ }
107
+ catch (error) {
108
+ this.logger.error('CreateRepositoryTool', 'Failed to create repository', '', error);
109
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
110
+ // Handle specific error cases
111
+ if (errorMessage.includes('already exists') || errorMessage.includes('NAME_EXISTS')) {
112
+ throw new McpError(McpErrorCode.InvalidParams, `Repository name '${params.repositoryName}' already exists for author '${params.author}'. Please choose a different name.`);
113
+ }
114
+ if (errorMessage.includes('not found') || errorMessage.includes('404')) {
115
+ throw new McpError(McpErrorCode.InvalidParams, `Author '${params.author}' not found. Please verify the author identifier.`);
116
+ }
117
+ if (errorMessage.includes('validation') || errorMessage.includes('invalid')) {
118
+ throw new McpError(McpErrorCode.InvalidParams, `Repository creation failed due to validation error: ${errorMessage}`);
119
+ }
120
+ // Generic error
121
+ throw new McpError(McpErrorCode.InternalError, `Failed to create repository: ${errorMessage}`);
122
+ }
123
+ }
124
+ }
125
+ //# sourceMappingURL=CreateRepositoryTool.js.map
@@ -0,0 +1,13 @@
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 DeleteRepositoryGuidanceTool extends BaseToolController {
5
+ private baseUrl?;
6
+ constructor(ucmClient: UcmApiClient, logger: ILogger, publishingAuthorId?: string, baseUrl?: string | undefined);
7
+ get name(): string;
8
+ get description(): string;
9
+ get inputSchema(): any;
10
+ protected validateParams(params: any): void;
11
+ protected handleExecute(params: any): Promise<any>;
12
+ }
13
+ //# sourceMappingURL=DeleteRepositoryGuidanceTool.d.ts.map
@@ -0,0 +1,94 @@
1
+ import { BaseToolController } from '../base/BaseToolController.js';
2
+ import { McpError, McpErrorCode } from '../../utils/McpErrorHandler.js';
3
+ export class DeleteRepositoryGuidanceTool extends BaseToolController {
4
+ baseUrl;
5
+ constructor(ucmClient, logger, publishingAuthorId, baseUrl) {
6
+ super(ucmClient, logger, publishingAuthorId);
7
+ this.baseUrl = baseUrl;
8
+ }
9
+ get name() {
10
+ return 'mcp_ucm_delete_repository_guidance';
11
+ }
12
+ get description() {
13
+ const authorInfo = this.publishingAuthorId ? ` Your author id is '${this.publishingAuthorId}'.` : '';
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}`;
15
+ }
16
+ get inputSchema() {
17
+ return {
18
+ type: 'object',
19
+ properties: {
20
+ author: {
21
+ type: 'string',
22
+ description: this.publishingAuthorId ? `Author identifier: ${this.publishingAuthorId}` : 'Author identifier (e.g., "utaba", "1064600359")',
23
+ minLength: 1,
24
+ maxLength: 50
25
+ },
26
+ repository: {
27
+ type: 'string',
28
+ description: 'Repository name to delete (e.g., "main", "project-alpha")',
29
+ minLength: 1,
30
+ maxLength: 200
31
+ }
32
+ },
33
+ required: ['author', 'repository'],
34
+ additionalProperties: false
35
+ };
36
+ }
37
+ validateParams(params) {
38
+ super.validateParams(params);
39
+ if (!params.author || typeof params.author !== 'string' || params.author.trim() === '') {
40
+ throw new McpError(McpErrorCode.InvalidParams, 'Author identifier is required and cannot be empty');
41
+ }
42
+ if (!params.repository || typeof params.repository !== 'string' || params.repository.trim() === '') {
43
+ throw new McpError(McpErrorCode.InvalidParams, 'Repository name is required and cannot be empty');
44
+ }
45
+ }
46
+ async handleExecute(params) {
47
+ this.logger.info('DeleteRepositoryGuidanceTool', `Providing delete guidance for repository ${params.repository} for author ${params.author}`);
48
+ // Get the base URL for the web interface
49
+ const baseUrl = this.baseUrl;
50
+ const repositorySettingsUrl = `${baseUrl}/repository/${params.repository}/settings`;
51
+ const browseUrl = `${baseUrl}/browse/${params.author}`;
52
+ const guidance = {
53
+ action: 'delete_repository_guidance',
54
+ repository: {
55
+ author: params.author,
56
+ name: params.repository
57
+ },
58
+ message: `Repository deletion must be performed through the web interface for safety.`,
59
+ reason: 'Repository deletion is intentionally not available through MCP tools to prevent accidental deletion of valuable content.',
60
+ instructions: [
61
+ `1. Open the repository settings page: ${repositorySettingsUrl}`,
62
+ `2. Scroll to the "Danger Zone" section`,
63
+ `3. Click "Delete Repository" button`,
64
+ `4. Confirm the deletion by typing the repository name`,
65
+ `5. Click "Delete" to permanently remove the repository`
66
+ ],
67
+ urls: {
68
+ repositorySettings: repositorySettingsUrl,
69
+ authorRepositories: browseUrl
70
+ },
71
+ warnings: [
72
+ 'Repository deletion is permanent and cannot be undone',
73
+ 'All artifacts and content in the repository will be permanently lost',
74
+ 'Any links or references to this repository will become invalid',
75
+ 'Make sure to backup any important content before deletion'
76
+ ],
77
+ alternatives: [
78
+ 'Consider renaming the repository instead of deleting it',
79
+ 'Archive the repository by updating its description to indicate it is archived',
80
+ 'Move important artifacts to another repository before deletion'
81
+ ]
82
+ };
83
+ this.logger.info('DeleteRepositoryGuidanceTool', `Delete guidance provided for repository: ${params.repository}`);
84
+ return {
85
+ content: [
86
+ {
87
+ type: 'text',
88
+ text: JSON.stringify(guidance, null, 2)
89
+ }
90
+ ]
91
+ };
92
+ }
93
+ }
94
+ //# sourceMappingURL=DeleteRepositoryGuidanceTool.js.map
@@ -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 GetRepositoryTool extends BaseToolController {
5
+ constructor(ucmClient: UcmApiClient, logger: ILogger, publishingAuthorId?: string);
6
+ get name(): string;
7
+ get description(): string;
8
+ get inputSchema(): any;
9
+ protected validateParams(params: any): void;
10
+ protected handleExecute(params: any): Promise<any>;
11
+ }
12
+ //# sourceMappingURL=GetRepositoryTool.d.ts.map
@@ -0,0 +1,85 @@
1
+ import { BaseToolController } from '../base/BaseToolController.js';
2
+ import { McpError, McpErrorCode } from '../../utils/McpErrorHandler.js';
3
+ export class GetRepositoryTool extends BaseToolController {
4
+ constructor(ucmClient, logger, publishingAuthorId) {
5
+ super(ucmClient, logger, publishingAuthorId);
6
+ }
7
+ get name() {
8
+ return 'mcp_ucm_get_repository';
9
+ }
10
+ get description() {
11
+ const authorInfo = this.publishingAuthorId ? ` Your author id is '${this.publishingAuthorId}'.` : '';
12
+ return `Get detailed information about a specific repository, including metadata, statistics, and available categories. Returns repository details along with its content structure.${authorInfo}`;
13
+ }
14
+ get inputSchema() {
15
+ return {
16
+ type: 'object',
17
+ properties: {
18
+ author: {
19
+ type: 'string',
20
+ description: this.publishingAuthorId ? `Author identifier: ${this.publishingAuthorId}` : 'Author identifier (e.g., "utaba", "1064600359")',
21
+ minLength: 1,
22
+ maxLength: 50
23
+ },
24
+ repository: {
25
+ type: 'string',
26
+ description: 'Repository name (e.g., "main", "project-alpha")',
27
+ minLength: 1,
28
+ maxLength: 200
29
+ }
30
+ },
31
+ required: ['author', 'repository'],
32
+ additionalProperties: false
33
+ };
34
+ }
35
+ validateParams(params) {
36
+ super.validateParams(params);
37
+ if (!params.author || typeof params.author !== 'string' || params.author.trim() === '') {
38
+ throw new McpError(McpErrorCode.InvalidParams, 'Author identifier is required and cannot be empty');
39
+ }
40
+ if (!params.repository || typeof params.repository !== 'string' || params.repository.trim() === '') {
41
+ throw new McpError(McpErrorCode.InvalidParams, 'Repository name is required and cannot be empty');
42
+ }
43
+ }
44
+ async handleExecute(params) {
45
+ this.logger.info('GetRepositoryTool', `Getting repository ${params.repository} for author ${params.author}`);
46
+ try {
47
+ const result = await this.ucmClient.getRepository(params.author, params.repository);
48
+ this.logger.info('GetRepositoryTool', `Repository retrieved successfully: ${params.repository}`);
49
+ // Enhance the response with additional helpful information
50
+ const enhancedResult = {
51
+ success: true,
52
+ message: `Repository '${params.repository}' retrieved successfully for author '${params.author}'`,
53
+ repository: result,
54
+ webUrl: `${process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000'}/browse/${params.author}/${params.repository}`,
55
+ apiEndpoints: {
56
+ browse: `/api/v1/authors/${params.author}/${params.repository}`,
57
+ artifacts: `/api/v1/authors/${params.author}/${params.repository}/{category}/{subcategory}`,
58
+ files: `/api/v1/files/${params.author}/${params.repository}/{category}/{subcategory}/{filename}`
59
+ }
60
+ };
61
+ return {
62
+ content: [
63
+ {
64
+ type: 'text',
65
+ text: JSON.stringify(enhancedResult, null, 2)
66
+ }
67
+ ]
68
+ };
69
+ }
70
+ catch (error) {
71
+ this.logger.error('GetRepositoryTool', 'Failed to get repository', '', error);
72
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
73
+ // Handle specific error cases
74
+ if (errorMessage.includes('not found') || errorMessage.includes('404')) {
75
+ throw new McpError(McpErrorCode.InvalidParams, `Repository '${params.repository}' not found for author '${params.author}'. Please verify the repository name and author identifier.`);
76
+ }
77
+ if (errorMessage.includes('403') || errorMessage.includes('Forbidden')) {
78
+ throw new McpError(McpErrorCode.InvalidParams, `Access denied to repository '${params.repository}' for author '${params.author}'. You may not have permission to view this repository.`);
79
+ }
80
+ // Generic error
81
+ throw new McpError(McpErrorCode.InternalError, `Failed to retrieve repository: ${errorMessage}`);
82
+ }
83
+ }
84
+ }
85
+ //# sourceMappingURL=GetRepositoryTool.js.map
@@ -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 UpdateRepositoryTool extends BaseToolController {
5
+ constructor(ucmClient: UcmApiClient, logger: ILogger, publishingAuthorId?: string);
6
+ get name(): string;
7
+ get description(): string;
8
+ get inputSchema(): any;
9
+ protected validateParams(params: any): void;
10
+ protected handleExecute(params: any): Promise<any>;
11
+ }
12
+ //# sourceMappingURL=UpdateRepositoryTool.d.ts.map
@@ -0,0 +1,125 @@
1
+ import { BaseToolController } from '../base/BaseToolController.js';
2
+ import { McpError, McpErrorCode } from '../../utils/McpErrorHandler.js';
3
+ export class UpdateRepositoryTool extends BaseToolController {
4
+ constructor(ucmClient, logger, publishingAuthorId) {
5
+ super(ucmClient, logger, publishingAuthorId);
6
+ }
7
+ get name() {
8
+ return 'mcp_ucm_update_repository';
9
+ }
10
+ get description() {
11
+ const authorInfo = this.publishingAuthorId ? ` Your author id is '${this.publishingAuthorId}'.` : '';
12
+ return `Update repository metadata including display name, description, and visibility settings. Note: Repository names cannot be changed after creation as they serve as permanent identifiers in the namespace path.${authorInfo}`;
13
+ }
14
+ get inputSchema() {
15
+ return {
16
+ type: 'object',
17
+ properties: {
18
+ author: {
19
+ type: 'string',
20
+ description: this.publishingAuthorId ? `Author identifier: ${this.publishingAuthorId}` : 'Author identifier (e.g., "utaba", "1064600359")',
21
+ minLength: 1,
22
+ maxLength: 50
23
+ },
24
+ repository: {
25
+ type: 'string',
26
+ description: 'Repository to update (e.g., "main", "project-alpha") however the name will not change. Only the displayname can be updated after creation.',
27
+ minLength: 1,
28
+ maxLength: 200
29
+ },
30
+ displayName: {
31
+ type: 'string',
32
+ description: 'New human-readable display name for the repository (optional)',
33
+ maxLength: 255
34
+ },
35
+ description: {
36
+ type: 'string',
37
+ description: 'New repository description (optional)',
38
+ maxLength: 1000
39
+ },
40
+ isPublic: {
41
+ type: 'boolean',
42
+ description: 'Whether the repository should be public (currently not supported, must be false)'
43
+ }
44
+ },
45
+ required: ['author', 'repository'],
46
+ additionalProperties: false
47
+ };
48
+ }
49
+ validateParams(params) {
50
+ super.validateParams(params);
51
+ if (!params.author || typeof params.author !== 'string' || params.author.trim() === '') {
52
+ throw new McpError(McpErrorCode.InvalidParams, 'Author identifier is required and cannot be empty');
53
+ }
54
+ if (!params.repository || typeof params.repository !== 'string' || params.repository.trim() === '') {
55
+ throw new McpError(McpErrorCode.InvalidParams, 'Repository name is required and cannot be empty');
56
+ }
57
+ // Validate at least one field to update is provided
58
+ const updateFields = ['displayName', 'description', 'isPublic'];
59
+ const hasUpdateField = updateFields.some(field => params[field] !== undefined);
60
+ if (!hasUpdateField) {
61
+ throw new McpError(McpErrorCode.InvalidParams, 'At least one field to update must be provided (displayName, description, or isPublic)');
62
+ }
63
+ // Validate displayName length if provided
64
+ if (params.displayName !== undefined && params.displayName.length > 255) {
65
+ throw new McpError(McpErrorCode.InvalidParams, 'Display name cannot exceed 255 characters');
66
+ }
67
+ // Validate description length if provided
68
+ if (params.description !== undefined && params.description.length > 1000) {
69
+ throw new McpError(McpErrorCode.InvalidParams, 'Description cannot exceed 1,000 characters');
70
+ }
71
+ // Validate isPublic - public repositories are not yet supported
72
+ if (params.isPublic === true) {
73
+ throw new McpError(McpErrorCode.InvalidParams, 'Public repositories are not yet supported. Set isPublic to false or omit the parameter.');
74
+ }
75
+ }
76
+ async handleExecute(params) {
77
+ this.logger.info('UpdateRepositoryTool', `Updating repository ${params.repository} for author ${params.author}`);
78
+ try {
79
+ // Build update data from provided parameters
80
+ const updateData = {};
81
+ if (params.displayName !== undefined) {
82
+ updateData.displayName = params.displayName;
83
+ }
84
+ if (params.description !== undefined) {
85
+ updateData.description = params.description;
86
+ }
87
+ if (params.isPublic !== undefined) {
88
+ updateData.isPublic = params.isPublic;
89
+ }
90
+ const result = await this.ucmClient.updateRepository(params.author, params.repository, updateData);
91
+ this.logger.info('UpdateRepositoryTool', `Repository updated successfully: ${params.repository}`);
92
+ return {
93
+ content: [
94
+ {
95
+ type: 'text',
96
+ text: JSON.stringify({
97
+ success: true,
98
+ message: `Repository '${params.repository}' updated successfully for author '${params.author}'`,
99
+ repository: result,
100
+ updatedFields: Object.keys(updateData),
101
+ webUrl: `${process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000'}/repository/${result.id || params.repository}/settings`
102
+ }, null, 2)
103
+ }
104
+ ]
105
+ };
106
+ }
107
+ catch (error) {
108
+ this.logger.error('UpdateRepositoryTool', 'Failed to update repository', '', error);
109
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
110
+ // Handle specific error cases
111
+ if (errorMessage.includes('not found') || errorMessage.includes('404')) {
112
+ throw new McpError(McpErrorCode.InvalidParams, `Repository '${params.repository}' not found for author '${params.author}'. Please verify the repository name and author identifier.`);
113
+ }
114
+ if (errorMessage.includes('403') || errorMessage.includes('Forbidden')) {
115
+ throw new McpError(McpErrorCode.InvalidParams, `Access denied to update repository '${params.repository}' for author '${params.author}'. You may not have permission to modify this repository.`);
116
+ }
117
+ if (errorMessage.includes('validation') || errorMessage.includes('invalid')) {
118
+ throw new McpError(McpErrorCode.InvalidParams, `Repository update failed due to validation error: ${errorMessage}`);
119
+ }
120
+ // Generic error
121
+ throw new McpError(McpErrorCode.InternalError, `Failed to update repository: ${errorMessage}`);
122
+ }
123
+ }
124
+ }
125
+ //# sourceMappingURL=UpdateRepositoryTool.js.map
@@ -41,11 +41,8 @@ export class ListRepositoriesTool extends BaseToolController {
41
41
  limit: limit || 'default'
42
42
  });
43
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);
44
+ // Use listRepositories to get repositories for the author
45
+ const response = await this.ucmClient.listRepositories(author, offset, limit);
49
46
  this.logger.info('ListRepositoriesTool', `Listed ${response.data.length} repositories for author: ${author}`, '', {
50
47
  total: response.pagination?.total,
51
48
  offset: response.pagination?.offset,
@@ -7,7 +7,7 @@ export class QuickstartTool extends BaseToolController {
7
7
  return 'mcp_ucm_quickstart';
8
8
  }
9
9
  get description() {
10
- return '**IMPORTANT** READ THIS FIRST before using any other tools as it will guide you to understand how to use the UCM system and MCP tools effectively';
10
+ return 'IMPORTANT: READ THIS FIRST before using any other tools as it will guide you to understand how to use the UCM system and MCP tools effectively';
11
11
  }
12
12
  get inputSchema() {
13
13
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@utaba/ucm-mcp-server",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Universal Context Manager MCP Server - AI-native artifact management",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -12,9 +12,9 @@
12
12
  "model-context-protocol",
13
13
  "ucm",
14
14
  "universal-context-manager",
15
+ "context-engineering",
15
16
  "ai",
16
17
  "context-manager",
17
- "micro-blocks",
18
18
  "utaba"
19
19
  ],
20
20
  "author": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ucm-mcp-server",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Universal Context Manager MCP Server - AI-native artifact management",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -12,9 +12,9 @@
12
12
  "model-context-protocol",
13
13
  "ucm",
14
14
  "universal-context-manager",
15
+ "context-engineering",
15
16
  "ai",
16
17
  "context-manager",
17
- "micro-blocks",
18
18
  "utaba"
19
19
  ],
20
20
  "author": {