@utaba/ucm-mcp-server 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +79 -0
  3. package/dist/clients/UcmApiClient.d.ts +53 -0
  4. package/dist/clients/UcmApiClient.js +297 -0
  5. package/dist/index.d.ts +3 -0
  6. package/dist/index.js +68 -0
  7. package/dist/interfaces/ILogger.d.ts +8 -0
  8. package/dist/interfaces/ILogger.js +4 -0
  9. package/dist/interfaces/IMcpTool.d.ts +7 -0
  10. package/dist/interfaces/IMcpTool.js +3 -0
  11. package/dist/logging/ConsoleLogger.d.ts +16 -0
  12. package/dist/logging/ConsoleLogger.js +45 -0
  13. package/dist/logging/LoggerFactory.d.ts +9 -0
  14. package/dist/logging/LoggerFactory.js +12 -0
  15. package/dist/server/McpConfig.d.ts +26 -0
  16. package/dist/server/McpConfig.js +93 -0
  17. package/dist/server/McpHandler.d.ts +12 -0
  18. package/dist/server/McpHandler.js +69 -0
  19. package/dist/server/McpServer.d.ts +15 -0
  20. package/dist/server/McpServer.js +49 -0
  21. package/dist/server/ToolRegistry.d.ts +22 -0
  22. package/dist/server/ToolRegistry.js +85 -0
  23. package/dist/tools/artifacts/GetArtifactController.d.ts +34 -0
  24. package/dist/tools/artifacts/GetArtifactController.js +397 -0
  25. package/dist/tools/artifacts/GetLatestController.d.ts +39 -0
  26. package/dist/tools/artifacts/GetLatestController.js +469 -0
  27. package/dist/tools/artifacts/ListVersionsController.d.ts +43 -0
  28. package/dist/tools/artifacts/ListVersionsController.js +530 -0
  29. package/dist/tools/artifacts/PublishArtifactController.d.ts +37 -0
  30. package/dist/tools/artifacts/PublishArtifactController.js +605 -0
  31. package/dist/tools/base/BaseToolController.d.ts +16 -0
  32. package/dist/tools/base/BaseToolController.js +32 -0
  33. package/dist/tools/core/DeleteArtifactTool.d.ts +11 -0
  34. package/dist/tools/core/DeleteArtifactTool.js +82 -0
  35. package/dist/tools/core/GetArtifactTool.d.ts +13 -0
  36. package/dist/tools/core/GetArtifactTool.js +125 -0
  37. package/dist/tools/core/GetArtifactVersionsTool.d.ts +11 -0
  38. package/dist/tools/core/GetArtifactVersionsTool.js +63 -0
  39. package/dist/tools/core/GetChunkTool.d.ts +11 -0
  40. package/dist/tools/core/GetChunkTool.js +56 -0
  41. package/dist/tools/core/ListArtifactsTool.d.ts +11 -0
  42. package/dist/tools/core/ListArtifactsTool.js +84 -0
  43. package/dist/tools/core/PublishArtifactFromFileTool.d.ts +15 -0
  44. package/dist/tools/core/PublishArtifactFromFileTool.js +256 -0
  45. package/dist/tools/core/PublishArtifactTool.d.ts +13 -0
  46. package/dist/tools/core/PublishArtifactTool.js +197 -0
  47. package/dist/tools/discovery/BrowseCategoriesController.d.ts +25 -0
  48. package/dist/tools/discovery/BrowseCategoriesController.js +400 -0
  49. package/dist/tools/discovery/FindByPurposeController.d.ts +12 -0
  50. package/dist/tools/discovery/FindByPurposeController.js +131 -0
  51. package/dist/tools/discovery/ListAuthorsController.d.ts +20 -0
  52. package/dist/tools/discovery/ListAuthorsController.js +274 -0
  53. package/dist/tools/discovery/SearchArtifactsController.d.ts +14 -0
  54. package/dist/tools/discovery/SearchArtifactsController.js +226 -0
  55. package/dist/tools/list/ListNamespaceController.d.ts +1 -0
  56. package/dist/tools/list/ListNamespaceController.js +8 -0
  57. package/dist/tools/navigation/ExploreNamespaceController.d.ts +35 -0
  58. package/dist/tools/navigation/ExploreNamespaceController.js +548 -0
  59. package/dist/tools/utility/AuthorIndexTool.d.ts +11 -0
  60. package/dist/tools/utility/AuthorIndexTool.js +48 -0
  61. package/dist/tools/utility/HealthCheckController.d.ts +11 -0
  62. package/dist/tools/utility/HealthCheckController.js +56 -0
  63. package/dist/tools/utility/ListRepositoriesTool.d.ts +11 -0
  64. package/dist/tools/utility/ListRepositoriesTool.js +70 -0
  65. package/dist/tools/utility/QuickstartTool.d.ts +11 -0
  66. package/dist/tools/utility/QuickstartTool.js +36 -0
  67. package/dist/tools/utility/ValidatePathController.d.ts +30 -0
  68. package/dist/tools/utility/ValidatePathController.js +465 -0
  69. package/dist/types/UcmApiTypes.d.ts +40 -0
  70. package/dist/types/UcmApiTypes.js +4 -0
  71. package/dist/utils/McpErrorHandler.d.ts +25 -0
  72. package/dist/utils/McpErrorHandler.js +67 -0
  73. package/dist/utils/PathUtils.d.ts +61 -0
  74. package/dist/utils/PathUtils.js +178 -0
  75. package/dist/utils/ResponseChunker.d.ts +25 -0
  76. package/dist/utils/ResponseChunker.js +79 -0
  77. package/dist/utils/ValidationUtils.d.ts +10 -0
  78. package/dist/utils/ValidationUtils.js +50 -0
  79. package/package.json +37 -0
  80. package/package.json.backup +37 -0
@@ -0,0 +1,93 @@
1
+ // Source: Independent MCP Server configuration management
2
+ // This class centralizes ALL MCP Server configuration and environment variables
3
+ export class McpConfig {
4
+ config;
5
+ constructor(options = {}) {
6
+ const authToken = options.authToken || this.getEnvVar('UCM_AUTH_TOKEN', '');
7
+ const authorId = this.extractAuthorIdFromToken(authToken);
8
+ this.config = {
9
+ ucmApiUrl: options.ucmApiUrl || this.getEnvVar('UCM_API_URL', 'https://ucm.utaba.ai'),
10
+ port: options.port || parseInt(this.getEnvVar('MCP_PORT', '3001')),
11
+ authToken: authToken,
12
+ authorId: options.authorId || authorId,
13
+ logLevel: options.logLevel || this.getEnvVar('MCP_LOG_LEVEL', 'ERROR'),
14
+ requestTimeout: options.requestTimeout || parseInt(this.getEnvVar('MCP_REQUEST_TIMEOUT', '30000')),
15
+ trustedAuthors: options.trustedAuthors || this.parseTrustedAuthors(this.getEnvVar('MCP_TRUSTED_AUTHORS', ''))
16
+ };
17
+ this.validateConfig();
18
+ }
19
+ extractAuthorIdFromToken(authToken) {
20
+ if (!authToken || authToken.trim() === '') {
21
+ return undefined;
22
+ }
23
+ const parts = authToken.split(':');
24
+ if (parts.length >= 2 && parts[0].trim() !== '') {
25
+ return parts[0].trim();
26
+ }
27
+ return undefined;
28
+ }
29
+ getEnvVar(name, defaultValue) {
30
+ const value = process.env[name];
31
+ if (value !== undefined) {
32
+ return value;
33
+ }
34
+ if (defaultValue !== undefined) {
35
+ return defaultValue;
36
+ }
37
+ throw new Error(`Required environment variable ${name} is not set`);
38
+ }
39
+ parseTrustedAuthors(authorsString) {
40
+ if (!authorsString || authorsString.trim() === '') {
41
+ return [];
42
+ }
43
+ const authors = authorsString.split(',').map(author => author.trim()).filter(Boolean);
44
+ const validAuthorPattern = /^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*$/;
45
+ for (const author of authors) {
46
+ if (!validAuthorPattern.test(author)) {
47
+ throw new Error(`Invalid trusted author format: "${author}". Authors must be alphanumeric with optional hyphens (no consecutive hyphens).`);
48
+ }
49
+ }
50
+ return authors;
51
+ }
52
+ validateConfig() {
53
+ if (!this.config.ucmApiUrl) {
54
+ throw new Error('UCM API URL is required');
55
+ }
56
+ if (!this.config.ucmApiUrl.startsWith('http')) {
57
+ throw new Error('UCM API URL must be a valid HTTP/HTTPS URL');
58
+ }
59
+ if (this.config.port < 1 || this.config.port > 65535) {
60
+ throw new Error('Port must be between 1 and 65535');
61
+ }
62
+ if (!['DEBUG', 'INFO', 'WARN', 'ERROR'].includes(this.config.logLevel)) {
63
+ throw new Error('Log level must be one of: DEBUG, INFO, WARN, ERROR');
64
+ }
65
+ }
66
+ // Getters for configuration values
67
+ get ucmApiUrl() {
68
+ return this.config.ucmApiUrl;
69
+ }
70
+ get port() {
71
+ return this.config.port;
72
+ }
73
+ get authToken() {
74
+ return this.config.authToken;
75
+ }
76
+ get logLevel() {
77
+ return this.config.logLevel;
78
+ }
79
+ get requestTimeout() {
80
+ return this.config.requestTimeout;
81
+ }
82
+ get trustedAuthors() {
83
+ return this.config.trustedAuthors;
84
+ }
85
+ get authorId() {
86
+ return this.config.authorId;
87
+ }
88
+ // Get full configuration object (for testing and debugging)
89
+ getFullConfig() {
90
+ return { ...this.config };
91
+ }
92
+ }
93
+ //# sourceMappingURL=McpConfig.js.map
@@ -0,0 +1,12 @@
1
+ import { ILogger } from '../interfaces/ILogger.js';
2
+ import { ToolRegistry } from './ToolRegistry.js';
3
+ export declare class McpHandler {
4
+ private toolRegistry;
5
+ private logger;
6
+ private authorId?;
7
+ constructor(toolRegistry: ToolRegistry, logger: ILogger, authorId?: string | undefined);
8
+ handlePing(): Promise<any>;
9
+ handleToolsList(): Promise<any>;
10
+ handleToolCall(request: any): Promise<any>;
11
+ }
12
+ //# sourceMappingURL=McpHandler.d.ts.map
@@ -0,0 +1,69 @@
1
+ import { McpError, McpErrorCode, McpErrorHandler } from '../utils/McpErrorHandler.js';
2
+ export class McpHandler {
3
+ toolRegistry;
4
+ logger;
5
+ authorId;
6
+ constructor(toolRegistry, logger, authorId) {
7
+ this.toolRegistry = toolRegistry;
8
+ this.logger = logger;
9
+ this.authorId = authorId;
10
+ }
11
+ async handlePing() {
12
+ this.logger.debug('MCP-Handler', 'Received ping request');
13
+ return {};
14
+ }
15
+ async handleToolsList() {
16
+ this.logger.debug('MCP-Handler', 'Received tools/list request');
17
+ try {
18
+ const tools = await this.toolRegistry.listTools();
19
+ this.logger.info('MCP-Handler', `Returning ${tools.length} available tools`);
20
+ return {
21
+ tools: tools.map(tool => ({
22
+ name: tool.name,
23
+ description: tool.description,
24
+ inputSchema: tool.inputSchema
25
+ }))
26
+ };
27
+ }
28
+ catch (error) {
29
+ this.logger.error('MCP-Handler', 'Failed to list tools', '', error);
30
+ throw new McpError(McpErrorCode.InternalError, 'Failed to retrieve tool list');
31
+ }
32
+ }
33
+ async handleToolCall(request) {
34
+ const { name, arguments: args } = request.params;
35
+ this.logger.debug('MCP-Handler', `Received tool call for: ${name}`);
36
+ try {
37
+ if (!name) {
38
+ throw new McpError(McpErrorCode.InvalidParams, 'Tool name is required');
39
+ }
40
+ const result = await this.toolRegistry.executeTool(name, args || {});
41
+ this.logger.info('MCP-Handler', `Tool ${name} executed successfully`);
42
+ // Handle string results directly (e.g., markdown content)
43
+ if (typeof result === 'string') {
44
+ return {
45
+ content: [
46
+ {
47
+ type: 'text',
48
+ text: result
49
+ }
50
+ ]
51
+ };
52
+ }
53
+ // For objects/arrays, use JSON.stringify
54
+ return {
55
+ content: [
56
+ {
57
+ type: 'text',
58
+ text: JSON.stringify(result, null, 2)
59
+ }
60
+ ]
61
+ };
62
+ }
63
+ catch (error) {
64
+ this.logger.error('MCP-Handler', `Tool execution failed for ${name}`, '', error);
65
+ throw McpErrorHandler.formatError(error);
66
+ }
67
+ }
68
+ }
69
+ //# sourceMappingURL=McpHandler.js.map
@@ -0,0 +1,15 @@
1
+ import { McpConfig } from './McpConfig.js';
2
+ import { ILogger } from '../interfaces/ILogger.js';
3
+ export declare class McpServer {
4
+ private config;
5
+ private logger;
6
+ private server;
7
+ private handler;
8
+ private ucmClient;
9
+ private toolRegistry;
10
+ constructor(config: McpConfig, logger: ILogger);
11
+ private setupHandlers;
12
+ start(): Promise<void>;
13
+ stop(): Promise<void>;
14
+ }
15
+ //# sourceMappingURL=McpServer.d.ts.map
@@ -0,0 +1,49 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
4
+ import { McpHandler } from './McpHandler.js';
5
+ import { UcmApiClient } from '../clients/UcmApiClient.js';
6
+ import { ToolRegistry } from './ToolRegistry.js';
7
+ export class McpServer {
8
+ config;
9
+ logger;
10
+ server;
11
+ handler;
12
+ ucmClient;
13
+ toolRegistry;
14
+ constructor(config, logger) {
15
+ this.config = config;
16
+ this.logger = logger;
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);
19
+ this.handler = new McpHandler(this.toolRegistry, this.logger, config.authorId);
20
+ this.server = new Server({
21
+ name: 'ucm-mcp-server',
22
+ version: '1.0.0',
23
+ description: 'UCM Model Context Protocol Server - https://ucm.utaba.ai'
24
+ }, {
25
+ capabilities: {
26
+ tools: {},
27
+ logging: {}
28
+ }
29
+ });
30
+ this.setupHandlers();
31
+ }
32
+ setupHandlers() {
33
+ this.server.setRequestHandler(ListToolsRequestSchema, this.handler.handleToolsList.bind(this.handler));
34
+ this.server.setRequestHandler(CallToolRequestSchema, this.handler.handleToolCall.bind(this.handler));
35
+ }
36
+ async start() {
37
+ const transport = new StdioServerTransport();
38
+ await this.server.connect(transport);
39
+ this.logger.info('MCP-Server', 'MCP Server connected via stdio transport');
40
+ }
41
+ async stop() {
42
+ // Cleanup HTTP client to prevent memory leaks from AbortSignal listeners
43
+ this.ucmClient.cleanup();
44
+ this.logger.info('MCP-Server', 'UCM API client cleaned up');
45
+ await this.server.close();
46
+ this.logger.info('MCP-Server', 'MCP Server stopped');
47
+ }
48
+ }
49
+ //# sourceMappingURL=McpServer.js.map
@@ -0,0 +1,22 @@
1
+ import { UcmApiClient } from '../clients/UcmApiClient.js';
2
+ import { ILogger } from '../interfaces/ILogger.js';
3
+ export interface ToolInfo {
4
+ name: string;
5
+ description: string;
6
+ inputSchema: any;
7
+ }
8
+ export declare class ToolRegistry {
9
+ private ucmClient;
10
+ private logger;
11
+ private trustedAuthors;
12
+ private authorId?;
13
+ private tools;
14
+ constructor(ucmClient: UcmApiClient, logger: ILogger, trustedAuthors?: string[], authorId?: string | undefined);
15
+ private registerTools;
16
+ private registerTool;
17
+ listTools(): Promise<ToolInfo[]>;
18
+ executeTool(name: string, params: any): Promise<any>;
19
+ getToolCount(): number;
20
+ hasToolnamed(name: string): boolean;
21
+ }
22
+ //# sourceMappingURL=ToolRegistry.d.ts.map
@@ -0,0 +1,85 @@
1
+ import { McpError, McpErrorCode } from '../utils/McpErrorHandler.js';
2
+ // Import utility tools
3
+ import { HealthCheckController } from '../tools/utility/HealthCheckController.js';
4
+ import { QuickstartTool } from '../tools/utility/QuickstartTool.js';
5
+ import { AuthorIndexTool } from '../tools/utility/AuthorIndexTool.js';
6
+ import { ListRepositoriesTool } from '../tools/utility/ListRepositoriesTool.js';
7
+ // Import core tools
8
+ import { GetArtifactTool } from '../tools/core/GetArtifactTool.js';
9
+ import { GetChunkTool } from '../tools/core/GetChunkTool.js';
10
+ import { PublishArtifactTool } from '../tools/core/PublishArtifactTool.js';
11
+ import { PublishArtifactFromFileTool } from '../tools/core/PublishArtifactFromFileTool.js';
12
+ import { ListArtifactsTool } from '../tools/core/ListArtifactsTool.js';
13
+ import { DeleteArtifactTool } from '../tools/core/DeleteArtifactTool.js';
14
+ import { GetArtifactVersionsTool } from '../tools/core/GetArtifactVersionsTool.js';
15
+ export class ToolRegistry {
16
+ ucmClient;
17
+ logger;
18
+ trustedAuthors;
19
+ authorId;
20
+ tools = new Map();
21
+ constructor(ucmClient, logger, trustedAuthors = [], authorId) {
22
+ this.ucmClient = ucmClient;
23
+ this.logger = logger;
24
+ this.trustedAuthors = trustedAuthors;
25
+ this.authorId = authorId;
26
+ this.registerTools();
27
+ }
28
+ registerTools() {
29
+ // Register utility tools
30
+ this.registerTool(new QuickstartTool(this.ucmClient, this.logger, this.authorId));
31
+ this.registerTool(new HealthCheckController(this.ucmClient, this.logger, this.authorId));
32
+ this.registerTool(new AuthorIndexTool(this.ucmClient, this.logger, this.authorId));
33
+ this.registerTool(new ListRepositoriesTool(this.ucmClient, this.logger, this.authorId));
34
+ // Register core tools
35
+ this.registerTool(new GetArtifactTool(this.ucmClient, this.logger, this.authorId, this.trustedAuthors));
36
+ this.registerTool(new GetChunkTool(this.ucmClient, this.logger, this.authorId));
37
+ this.registerTool(new PublishArtifactFromFileTool(this.ucmClient, this.logger, this.authorId));
38
+ this.registerTool(new PublishArtifactTool(this.ucmClient, this.logger, this.authorId));
39
+ this.registerTool(new ListArtifactsTool(this.ucmClient, this.logger, this.authorId));
40
+ this.registerTool(new DeleteArtifactTool(this.ucmClient, this.logger, this.authorId));
41
+ this.registerTool(new GetArtifactVersionsTool(this.ucmClient, this.logger, this.authorId));
42
+ this.logger.info('ToolRegistry', `Registered ${this.tools.size} MCP tools`);
43
+ }
44
+ registerTool(tool) {
45
+ if (this.tools.has(tool.name)) {
46
+ this.logger.warn('ToolRegistry', `Tool ${tool.name} already registered, overwriting`);
47
+ }
48
+ this.tools.set(tool.name, tool);
49
+ this.logger.debug('ToolRegistry', `Registered tool: ${tool.name}`);
50
+ }
51
+ async listTools() {
52
+ const toolList = [];
53
+ for (const [, tool] of this.tools) {
54
+ toolList.push({
55
+ name: tool.name,
56
+ description: tool.description,
57
+ inputSchema: tool.inputSchema
58
+ });
59
+ }
60
+ return toolList;
61
+ }
62
+ async executeTool(name, params) {
63
+ const tool = this.tools.get(name);
64
+ if (!tool) {
65
+ throw new McpError(McpErrorCode.MethodNotFound, `Tool '${name}' not found`);
66
+ }
67
+ this.logger.debug('ToolRegistry', `Executing tool: ${name}`);
68
+ try {
69
+ const result = await tool.execute(params);
70
+ this.logger.debug('ToolRegistry', `Tool ${name} completed successfully`);
71
+ return result;
72
+ }
73
+ catch (error) {
74
+ this.logger.error('ToolRegistry', `Tool ${name} execution failed`, '', error);
75
+ throw error;
76
+ }
77
+ }
78
+ getToolCount() {
79
+ return this.tools.size;
80
+ }
81
+ hasToolnamed(name) {
82
+ return this.tools.has(name);
83
+ }
84
+ }
85
+ //# sourceMappingURL=ToolRegistry.js.map
@@ -0,0 +1,34 @@
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 GetArtifactController 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 buildArtifactResponse;
11
+ private enrichMetadata;
12
+ private processExamples;
13
+ private enrichDependencies;
14
+ private getVersionHistory;
15
+ private getRelatedArtifacts;
16
+ private detectContentType;
17
+ private detectLanguage;
18
+ private getIncludedSections;
19
+ private isLatestVersion;
20
+ private calculateQualityScore;
21
+ private estimateComplexity;
22
+ private assessMaturityLevel;
23
+ private sanitizeExternalDependencies;
24
+ private checkDependencyResolution;
25
+ private findMissingDependencies;
26
+ private detectDependencyConflicts;
27
+ private generateChangesSummary;
28
+ private isBreakingChange;
29
+ private getDownloadCount;
30
+ private calculateRating;
31
+ private getLastAccessTime;
32
+ private calculateSimilarity;
33
+ }
34
+ //# sourceMappingURL=GetArtifactController.d.ts.map