UNPKG

ultimate-mcp-server

Version:

The definitive all-in-one Model Context Protocol server for AI-assisted coding across 30+ platforms

494 lines 18.2 kB
/** * Content Management Tools * MCP tools for comprehensive content operations inspired by contentful-mcp */ import { z } from 'zod'; import { ContentManager } from '../content-management/content-manager.js'; import { Logger } from '../utils/logger.js'; const logger = new Logger('ContentManagementTools'); // Global content manager instance let contentManager = null; // Initialize content manager function getContentManager() { if (!contentManager) { contentManager = new ContentManager({ maxItemsPerPage: 3, // Follow contentful-mcp pattern enableVersioning: true, enableComments: true, cacheEnabled: true }); } return contentManager; } // Space management tools export const createSpace = { name: 'content_create_space', description: 'Create a new content space for organizing content', inputSchema: z.object({ name: z.string().describe('Space name'), description: z.string().optional().describe('Space description') }).strict(), handler: async (args) => { const manager = getContentManager(); const space = await manager.createSpace(args.name, args.description); return { space: { id: space.id, name: space.name, description: space.description, environments: space.environments.map(e => ({ id: e.id, name: e.name, isActive: e.isActive })), createdAt: space.createdAt, updatedAt: space.updatedAt }, message: `Space '${space.name}' created successfully with ID: ${space.id}` }; } }; export const listSpaces = { name: 'content_list_spaces', description: 'List all content spaces', inputSchema: z.object({}).strict(), handler: async () => { const manager = getContentManager(); const spaces = await manager.listSpaces(); return { spaces: spaces.map(space => ({ id: space.id, name: space.name, description: space.description, environmentCount: space.environments.length, createdAt: space.createdAt })), total: spaces.length }; } }; // Content type tools export const createContentType = { name: 'content_create_type', description: 'Create a new content type schema', inputSchema: z.object({ spaceId: z.string().describe('Space ID'), name: z.string().describe('Content type name'), description: z.string().optional().describe('Description'), displayField: z.string().optional().describe('Field to use as display name'), fields: z.array(z.object({ id: z.string().describe('Field ID'), name: z.string().describe('Field display name'), type: z.enum([ 'Symbol', 'Text', 'RichText', 'Integer', 'Number', 'Boolean', 'Date', 'Location', 'Media', 'Reference', 'Array', 'Object', 'JSON' ]).describe('Field type'), required: z.boolean().optional().default(false), localized: z.boolean().optional().default(false), validations: z.array(z.object({ type: z.string(), params: z.any().optional() })).optional() })).describe('Field definitions') }).strict(), handler: async (args) => { const manager = getContentManager(); const contentType = await manager.createContentType(args.spaceId, args.name, args.fields, { description: args.description, displayField: args.displayField }); return { contentType: { id: contentType.id, name: contentType.name, description: contentType.description, displayField: contentType.displayField, fieldCount: contentType.fields.length, fields: contentType.fields.map(f => ({ id: f.id, name: f.name, type: f.type, required: f.required })) }, message: `Content type '${contentType.name}' created with ${contentType.fields.length} fields` }; } }; // Entry management tools export const createEntry = { name: 'content_create_entry', description: 'Create a new content entry', inputSchema: z.object({ spaceId: z.string().describe('Space ID'), contentType: z.string().describe('Content type ID'), fields: z.record(z.any()).describe('Field values'), status: z.enum(['draft', 'published']).optional().default('draft'), locale: z.string().optional().describe('Locale code'), tags: z.array(z.string()).optional().describe('Tags for organization') }).strict(), handler: async (args) => { const manager = getContentManager(); const entry = await manager.createEntry(args.spaceId, args.contentType, args.fields, { status: args.status, locale: args.locale, tags: args.tags }); return { entry: { id: entry.id, type: entry.type, status: entry.status, version: entry.version, fields: entry.fields, locale: entry.locale, tags: entry.tags, createdAt: entry.createdAt }, message: `Entry created with ID: ${entry.id}` }; } }; export const updateEntry = { name: 'content_update_entry', description: 'Update an existing content entry', inputSchema: z.object({ entryId: z.string().describe('Entry ID to update'), fields: z.record(z.any()).optional().describe('Updated field values'), status: z.enum(['draft', 'published', 'archived']).optional(), tags: z.array(z.string()).optional() }).strict(), handler: async (args) => { const manager = getContentManager(); const updates = {}; if (args.fields) updates.fields = args.fields; if (args.status) updates.status = args.status; if (args.tags) updates.tags = args.tags; const entry = await manager.updateEntry(args.entryId, updates); return { entry: { id: entry.id, type: entry.type, status: entry.status, version: entry.version, updatedAt: entry.updatedAt }, message: `Entry ${entry.id} updated to version ${entry.version}` }; } }; export const searchEntries = { name: 'content_search_entries', description: 'Search and filter content entries with pagination', inputSchema: z.object({ contentType: z.string().optional().describe('Filter by content type'), status: z.array(z.enum(['draft', 'published', 'archived'])).optional(), tags: z.array(z.string()).optional().describe('Filter by tags'), search: z.string().optional().describe('Search in fields'), locale: z.string().optional(), limit: z.number().optional().default(3).describe('Items per page (max 10)'), offset: z.number().optional().default(0).describe('Skip items') }).strict(), handler: async (args) => { const manager = getContentManager(); const result = await manager.searchEntries({ contentType: args.contentType, status: args.status, tags: args.tags, search: args.search, locale: args.locale }, { limit: Math.min(args.limit, 10), // Cap at 10 offset: args.offset }); return { entries: result.entries.map(entry => ({ id: entry.id, type: entry.type, status: entry.status, version: entry.version, fields: entry.fields, tags: entry.tags, updatedAt: entry.updatedAt })), pagination: { limit: result.pagination.limit, offset: result.pagination.offset, total: result.pagination.total, hasNext: result.pagination.hasNext, hasPrevious: result.pagination.hasPrevious }, message: `Found ${result.pagination.total} entries, showing ${result.entries.length}` }; } }; // Asset management tools export const uploadAsset = { name: 'content_upload_asset', description: 'Upload a file as a content asset', inputSchema: z.object({ spaceId: z.string().describe('Space ID'), filePath: z.string().describe('Local file path'), title: z.string().describe('Asset title'), description: z.string().optional(), tags: z.array(z.string()).optional() }).strict(), handler: async (args) => { const manager = getContentManager(); const asset = await manager.uploadAsset(args.spaceId, args.filePath, args.title, { description: args.description, tags: args.tags }); return { asset: { id: asset.id, title: asset.title, description: asset.description, file: { url: asset.file.url, fileName: asset.file.fileName, contentType: asset.file.contentType, size: asset.file.size }, status: asset.status, createdAt: asset.createdAt }, message: `Asset '${asset.title}' uploaded successfully` }; } }; export const getAsset = { name: 'content_get_asset', description: 'Get asset details by ID', inputSchema: z.object({ assetId: z.string().describe('Asset ID') }).strict(), handler: async (args) => { const manager = getContentManager(); const asset = await manager.getAsset(args.assetId); if (!asset) { return { found: false, message: `Asset ${args.assetId} not found` }; } return { found: true, asset: { id: asset.id, title: asset.title, description: asset.description, file: asset.file, status: asset.status, version: asset.version, tags: asset.tags, metadata: asset.metadata, createdAt: asset.createdAt, updatedAt: asset.updatedAt, publishedAt: asset.publishedAt } }; } }; // Comment management tools export const addComment = { name: 'content_add_comment', description: 'Add a comment to an entry (max 512 characters)', inputSchema: z.object({ entryId: z.string().describe('Entry ID to comment on'), author: z.string().describe('Comment author'), body: z.string().max(512).describe('Comment text (max 512 chars)'), parentId: z.string().optional().describe('Parent comment ID for replies') }).strict(), handler: async (args) => { const manager = getContentManager(); const comment = await manager.addComment(args.entryId, args.author, args.body, args.parentId); return { comment: { id: comment.id, entryId: comment.entryId, author: comment.author, body: comment.body, status: comment.status, parentId: comment.parentId, createdAt: comment.createdAt }, message: args.parentId ? `Reply added to comment ${args.parentId}` : `Comment added to entry ${args.entryId}` }; } }; export const getEntryComments = { name: 'content_get_comments', description: 'Get all comments for an entry', inputSchema: z.object({ entryId: z.string().describe('Entry ID') }).strict(), handler: async (args) => { const manager = getContentManager(); const comments = await manager.getEntryComments(args.entryId); return { comments: comments.map(comment => ({ id: comment.id, author: comment.author, body: comment.body, status: comment.status, createdAt: comment.createdAt, replyCount: comment.replies?.length || 0 })), total: comments.length }; } }; // Bulk operations export const bulkOperation = { name: 'content_bulk_operation', description: 'Execute bulk operations on multiple entries', inputSchema: z.object({ action: z.enum(['publish', 'unpublish', 'archive', 'delete', 'validate']) .describe('Bulk action to perform'), entryIds: z.array(z.string()).describe('Entry IDs to operate on'), options: z.object({ skipValidation: z.boolean().optional(), force: z.boolean().optional(), locale: z.string().optional() }).optional() }).strict(), handler: async (args) => { const manager = getContentManager(); const operation = { action: args.action, entryIds: args.entryIds, options: args.options }; const result = await manager.executeBulkOperation(operation); return { action: args.action, total: args.entryIds.length, succeeded: result.succeeded, failed: result.failed, summary: { successCount: result.succeeded.length, failureCount: result.failed.length, successRate: `${Math.round((result.succeeded.length / args.entryIds.length) * 100)}%` }, message: `Bulk ${args.action} completed: ${result.succeeded.length} succeeded, ${result.failed.length} failed` }; } }; // Import/Export tools export const importContent = { name: 'content_import', description: 'Import content from JSON or CSV', inputSchema: z.object({ spaceId: z.string().describe('Target space ID'), data: z.string().describe('Import data as string'), format: z.enum(['json', 'csv']).describe('Data format'), mapping: z.record(z.string()).optional() .describe('Field mapping (source -> target)'), updateExisting: z.boolean().optional().default(false), locale: z.string().optional() }).strict(), handler: async (args) => { const manager = getContentManager(); const result = await manager.importContent(args.spaceId, Buffer.from(args.data), { format: args.format, mapping: args.mapping, updateExisting: args.updateExisting, locale: args.locale }); return { imported: result.imported, errors: result.errors, success: result.errors.length === 0, message: `Imported ${result.imported} entries${result.errors.length > 0 ? ` with ${result.errors.length} errors` : ''}` }; } }; export const exportContent = { name: 'content_export', description: 'Export content to JSON or CSV format', inputSchema: z.object({ spaceId: z.string().describe('Space ID to export from'), format: z.enum(['json', 'csv']).describe('Export format'), filter: z.object({ contentType: z.string().optional(), status: z.array(z.enum(['draft', 'published', 'archived'])).optional(), tags: z.array(z.string()).optional() }).optional(), fields: z.array(z.string()).optional() .describe('Specific fields to export'), includeMetadata: z.boolean().optional().default(false) }).strict(), handler: async (args) => { const manager = getContentManager(); const data = await manager.exportContent(args.spaceId, { format: args.format, filter: args.filter, fields: args.fields, includeMetadata: args.includeMetadata }); return { data, format: args.format, size: data.length, message: `Exported content in ${args.format} format (${data.length} bytes)` }; } }; // Statistics tool export const getContentStats = { name: 'content_get_stats', description: 'Get content management statistics', inputSchema: z.object({ spaceId: z.string().optional().describe('Space ID for space-specific stats') }).strict(), handler: async (args) => { const manager = getContentManager(); const stats = await manager.getStats(args.spaceId); return { stats: { totalEntries: stats.totalEntries, totalAssets: stats.totalAssets, totalComments: stats.totalComments, entryTypes: stats.entryTypes, statusBreakdown: stats.statusBreakdown, storageUsed: `${Math.round(stats.storageUsed / 1024)} KB`, lastActivity: stats.lastActivity }, spaceId: args.spaceId || 'all', message: `Content statistics${args.spaceId ? ` for space ${args.spaceId}` : ' for all spaces'}` }; } }; // Export all content management tools export const contentManagementTools = [ // Space management createSpace, listSpaces, // Content types createContentType, // Entries createEntry, updateEntry, searchEntries, // Assets uploadAsset, getAsset, // Comments addComment, getEntryComments, // Bulk operations bulkOperation, // Import/Export importContent, exportContent, // Statistics getContentStats ]; //# sourceMappingURL=content-management-tools.js.map