@utaba/ucm-mcp-server
Version:
Universal Context Manager MCP Server - AI-native artifact management
124 lines • 6.02 kB
JavaScript
import { BaseToolController } from '../base/BaseToolController.js';
import { ResponseChunker } from '../../utils/ResponseChunker.js';
import { parsePath } from '../../utils/PathUtils.js';
export class GetArtifactTool extends BaseToolController {
trustedAuthors;
constructor(ucmClient, logger, publishingAuthorId, trustedAuthors = []) {
super(ucmClient, logger, publishingAuthorId);
this.trustedAuthors = trustedAuthors;
}
get name() {
return 'mcp_ucm_get_artifact';
}
get description() {
return 'Retrieve UCM artifact content with metadata. Large files are automatically chunked.';
}
get inputSchema() {
return {
type: 'object',
properties: {
path: {
type: 'string',
description: `Full artifact path with optional version (e.g., "${this.publishingAuthorId || '1234567890'}/main/commands/user/CreateUserCommand.ts" or "${this.publishingAuthorId || '1234567890'}/main/commands/user/CreateUserCommand.ts@1.0.0")`,
minLength: 1,
maxLength: 200
},
includeMetadata: {
type: 'boolean',
description: 'Include artifact metadata in response',
default: true
}
},
required: ['path']
};
}
async handleExecute(params) {
let { path, includeMetadata = true } = params;
this.logger.debug('GetArtifactTool', `Retrieving artifact: ${path}`);
try {
// Parse the path to extract components
if (!path)
path = "";
const pathComponents = parsePath(path);
// Validate that path contains all required components including repository
if (!pathComponents.author || !pathComponents.repository || !pathComponents.category || !pathComponents.subcategory || !pathComponents.filename) {
throw new Error(`Path must include all components: author/repository/category/subcategory/filename.ext. Expected format: "author/repository/category/subcategory/filename.ext" or "author/repository/category/subcategory/filename.ext@version". Received: "${path}"`);
}
// Get artifact from API
const artifact = await this.ucmClient.getArtifact(pathComponents.author, pathComponents.repository, pathComponents.category, pathComponents.subcategory, pathComponents.filename, pathComponents.version);
// Extract content - it should always be a string in the API response
if (!artifact.content) {
throw new Error('Artifact has no content');
}
const content = artifact.content;
// Check if author is trusted
const author = artifact.metadata?.author || pathComponents.author;
const isTrusted = this.trustedAuthors.includes(author);
// Build response
const response = {
content,
path: artifact.path || path,
filename: pathComponents.filename // filename comes from parsed path
};
// Add security warning for untrusted authors
if (!isTrusted && this.publishingAuthorId && this.publishingAuthorId != pathComponents.author) {
response.securityWarning = `⚠️ SECURITY WARNING: This artifact is from author '${author}' who is not in your trusted authors list. Please review the code carefully before using it.`;
}
// Add metadata if requested
if (includeMetadata) {
response.metadata = {
id: artifact.id,
version: artifact.metadata?.version || pathComponents.version || 'latest',
description: artifact.metadata?.description,
author: author,
repository: pathComponents.repository,
category: artifact.metadata?.category || pathComponents.category,
subcategory: artifact.metadata?.subcategory || pathComponents.subcategory,
technology: artifact.metadata?.technology,
tags: artifact.metadata?.tags || [],
contractVersion: artifact.metadata?.contractVersion,
size: content.length,
mimeType: this.detectMimeType(pathComponents.filename),
lastUpdated: artifact.lastUpdated,
publishedAt: artifact.publishedAt,
};
}
// Apply chunking if needed
const chunkedResponse = ResponseChunker.chunk(response);
this.logger.info('GetArtifactTool', `Artifact retrieved: ${path}`, '', {
size: content.length,
chunked: chunkedResponse.chunk ? true : false
});
return chunkedResponse;
}
catch (error) {
this.logger.error('GetArtifactTool', `Failed to retrieve artifact: ${path}`, '', error);
throw error;
}
}
detectMimeType(filename) {
const ext = filename.split('.').pop()?.toLowerCase();
const mimeTypes = {
'ts': 'application/typescript',
'js': 'application/javascript',
'json': 'application/json',
'md': 'text/markdown',
'txt': 'text/plain',
'yaml': 'text/yaml',
'yml': 'text/yaml',
'html': 'text/html',
'css': 'text/css',
'py': 'text/x-python',
'java': 'text/x-java',
'cs': 'text/x-csharp',
'go': 'text/x-go',
'rs': 'text/x-rust',
'cpp': 'text/x-c++',
'c': 'text/x-c',
'sh': 'text/x-shellscript',
'xml': 'application/xml'
};
return mimeTypes[ext || ''] || 'text/plain';
}
}
//# sourceMappingURL=GetArtifactTool.js.map