UNPKG

mcp-talent-server

Version:

Model Context Protocol server for talent management tools

503 lines 21 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import dotenv from 'dotenv'; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js'; import { connectToDatabase } from './services/database.js'; import { GalleryZipExportDescription, GalleryZipExporter, galleryZipExportSchema } from './tools/gallery-zip-export.js'; import { StructuredSheetSearchDescription, StructuredSheetSearchTool, structuredSheetSearchSchema } from './tools/structured-sheets-search.js'; import { VectorSearchDescription, VectorSearchTool, vectorSearchSchema } from './tools/vector-search.js'; import { getSchemas, SheetSchemaTracker } from './models/sheet-schema-tracker.model.js'; import { sheetSchemaDescription } from './tools/sheet-schema.js'; import { RosterFileContent, RosterFilesLinksListDescription, RosterFilesListDescription, RosterMainDescription, RosterTool } from './tools/roster-tool.js'; import DocumentModel from './models/document.model.js'; import Sheet, { SheetData } from './models/sheet.model.js'; import { OrignalSheetsColumnsListDescription, OrignalSheetsListDescription, OrignalSheetContentSearchDescription } from './tools/sheet.js'; dotenv.config({ debug: false }); class TalentMCPServer { server; galleryZipExporter; structuredSheetSearch; vectorSearch; rosterTool; constructor() { this.server = new Server({ name: 'talent-management-server', version: '1.0.0', }, { capabilities: { tools: {}, }, }); this.galleryZipExporter = new GalleryZipExporter(); this.structuredSheetSearch = new StructuredSheetSearchTool(); this.vectorSearch = new VectorSearchTool(); this.rosterTool = new RosterTool(); this.setupToolHandlers(); this.setupErrorHandling(); } setupToolHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'structured_sheets', description: 'List all sheets', inputSchema: { type: "object", properties: {}, additionalProperties: false }, }, { name: 'structured_sheet_schema', description: 'Get schema for a sheet', inputSchema: { type: "object", properties: { sheetName: { type: "string", description: "Name of the sheet to get schema for" } }, required: ["sheetName"] }, }, { name: 'gallery_zip_export', description: GalleryZipExportDescription, inputSchema: { type: "object", properties: { folderNames: { type: "array", items: { type: "string" }, description: "Folder names to search for" }, imageNames: { type: "array", items: { type: "string" }, description: "Image names to search for" }, foldersSearchQuery: { type: "string", description: "MongoDB query object to search folders" }, imagesSearchQuery: { type: "string", description: "MongoDB query object to search images" }, includeSubfolders: { type: "boolean", default: true, description: "Whether to include images from subfolders" }, maxImages: { type: "number", minimum: 1, maximum: 100, default: 50, description: "Maximum number of images to include in zip" } }, required: ["folderNames", "imageNames", "foldersSearchQuery", "imagesSearchQuery"] }, }, { name: "structured_sheets_schema", description: sheetSchemaDescription, inputSchema: { type: "object", properties: {}, additionalProperties: false }, }, { name: 'structured_sheets_search', description: StructuredSheetSearchDescription, inputSchema: { type: "object", properties: { aggregationPipeline: { type: "array", items: { type: "object" }, description: "Pre-generated MongoDB aggregation pipeline to execute directly (optional)" }, limit: { type: "number", default: 10, description: "Number of results to return" }, skip: { type: "number", default: 0, description: "Number of results to skip" } }, required: ["aggregationPipeline"] }, }, { name: 'vector_search', description: VectorSearchDescription, inputSchema: { type: "object", properties: { query: { type: "string", minLength: 1, description: "Natural language search query to find relevant content in the knowledge base" }, limit: { type: "number", default: 10, description: "Number of results to return" } }, required: ["query"] }, }, // { // name: 'roster_tool', // description: RosterToolDescription, // inputSchema: { // type: "object", // properties: { // query: { // type: "string", // description: "Natural language search query to find relevant content in the knowledge base" // }, // limit: { // type: "number", // default: 10, // description: "Number of results to return" // } // }, // required: ["query"] // }, // }, { name: "roster_main", description: RosterMainDescription, inputSchema: { type: "object", properties: {}, additionalProperties: false }, }, { name: "roster_files_list", description: RosterFilesListDescription, inputSchema: { type: "object", properties: {}, additionalProperties: false }, }, { name: "roster_file_content", description: RosterFileContent, inputSchema: { type: "object", properties: { id: { type: "string", description: "Id of the file to get content for" } }, required: ["id"] }, }, { name: "roster_files_links_list", description: RosterFilesLinksListDescription, inputSchema: { type: "object", properties: {}, additionalProperties: false }, }, { name: "orignal_sheets_list", description: OrignalSheetsListDescription, inputSchema: { type: "object", properties: {}, additionalProperties: false }, }, { name: "orignal_sheet_columns", description: OrignalSheetsColumnsListDescription, inputSchema: { type: "object", properties: { sheetId: { type: "string", description: "Id of the sheet to get content for" } }, required: ["sheetId"] }, }, { name: "orignal_sheet_content_search", description: OrignalSheetContentSearchDescription, inputSchema: { type: "object", properties: { aggregationPipeline: { type: "array", items: { type: "object" }, description: "Pre-generated MongoDB aggregation pipeline to execute directly (optional)" }, }, required: ["aggregationPipeline"] }, } ], }; }); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case 'gallery_zip_export': return await this.handleGalleryZipExport(args); case 'structured_sheets': return await this.handleSheets(args); case 'structured_sheet_schema': return await this.handleSheetSchema(args); case 'structured_sheets_schema': return await this.handleSheetsSchema(); case 'structured_sheets_search': return await this.handleStructuredSheetsSearch(args); case 'vector_search': return await this.handleVectorSearch(args); // case 'roster_tool': // return await this.handleRosterTool(args); case 'roster_main': return await this.handleRosterMain(); case 'roster_files_list': return await this.handleRosterFilesList(); case 'roster_file_content': return await this.handleRosterFileContent(args); case 'roster_files_links_list': return await this.handleRosterFilesLinksList(); case 'orignal_sheets_list': return await this.handleOrignalSheetsList(); case 'orignal_sheet_columns': return await this.handleOrignalSheetColumns(args); case 'orignal_sheet_content_search': return await this.handleOrignalSheetContentSearch(args); default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`); } } catch (error) { if (error instanceof McpError) { throw error; } throw new McpError(ErrorCode.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } }); } async handleGalleryZipExport(args) { const validatedArgs = galleryZipExportSchema.parse(args); const result = await this.galleryZipExporter.exportGallerySearch(validatedArgs); return { content: [ { type: 'text', text: JSON.stringify({ success: true, message: `Successfully created zip file with ${result.stats.totalImages} images.`, downloadUrl: result.downloadUrl, filename: result.filename, stats: result.stats, instructions: 'Click the download URL to get your zip file. The file will include all images organized in their original folder structure, plus an export-metadata.json file with details about the export.', }, null, 2), }, ], }; } async handleSheetSchema(args) { const result = await SheetSchemaTracker.findOne({ sheetName: args.sheetName }); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } async handleSheets(args) { const result = await SheetSchemaTracker.find({}); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } async handleSheetsSchema() { const result = await getSchemas(); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } async handleStructuredSheetsSearch(args) { const validatedArgs = structuredSheetSearchSchema.parse(args); const userId = args.userId; const sessionId = args.sessionId; const result = await this.structuredSheetSearch.searchInAllSheets(validatedArgs, userId, sessionId); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } async handleVectorSearch(args) { const validatedArgs = vectorSearchSchema.parse(args); const result = await this.vectorSearch.search(validatedArgs); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } // private async handleRosterTool(args: any) { // const validatedArgs = rosterToolSchema.parse(args); // const result = await this.rosterTool.search(validatedArgs); // return { // content: [ // { // type: 'text', // text: JSON.stringify(result, null, 2), // }, // ], // }; // } async handleRosterMain() { const result = await DocumentModel.findOne({ fileName: "All Roster Blast Responses- By Category" }).select("content"); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } async handleRosterFilesList() { const result = await DocumentModel.find().select("_id fileName"); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } async handleRosterFileContent(args) { const result = await DocumentModel.findOne({ _id: args.id }).select("content"); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } async handleRosterFilesLinksList() { const result = await DocumentModel.find().select("_id fileName metadata"); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } async handleOrignalSheetsList() { const result = await Sheet.find({ deleted: false }).select("name description columns.name"); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } async handleOrignalSheetColumns(args) { const result = await Sheet.findOne({ _id: args.sheetId }).select("columns"); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } async handleOrignalSheetContentSearch(args) { const result = await SheetData.aggregate(args.aggregationPipeline); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } setupErrorHandling() { this.server.onerror = (error) => { console.error('[MCP Error]', error); }; process.on('SIGINT', async () => { await this.server.close(); process.exit(0); }); } async start() { try { // Connect to database const mongoUri = process.env.MONGODB_URI || 'mongodb+srv://usama:8YQ4KYmRAyb!m6L@cluster0.yneqevx.mongodb.net/talent_pitch_memory_bank?authSource=admin'; await connectToDatabase(mongoUri); // Start the server const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('Talent MCP Server running on stdio'); } catch (error) { console.error('Failed to start server:', error); process.exit(1); } } } // Start the server export const talentServer = new TalentMCPServer(); talentServer.start().catch((error) => { console.error('Server startup failed:', error); process.exit(1); }); //# sourceMappingURL=index.js.map