UNPKG

creditclaudit-mcp-server

Version:

MCP server for tenant credit assessment using CreditClaudit methodology

273 lines (253 loc) 9.21 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import fs from 'fs/promises'; import path from 'path'; import { fileURLToPath } from 'url'; // Import pdf-parse dynamically to avoid test code execution let pdfParse; import xlsx from 'xlsx'; import { parse } from 'csv-parse/sync'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // Helper function to read financial data from various formats async function readFinancialFile(filePath) { const ext = path.extname(filePath).toLowerCase(); switch (ext) { case '.pdf': // Lazy load pdf-parse to avoid test code execution if (!pdfParse) { pdfParse = (await import('pdf-parse')).default; } const pdfBuffer = await fs.readFile(filePath); const pdfData = await pdfParse(pdfBuffer); return pdfData.text; case '.xlsx': case '.xls': const workbook = xlsx.readFile(filePath); const sheets = {}; for (const sheetName of workbook.SheetNames) { sheets[sheetName] = xlsx.utils.sheet_to_json(workbook.Sheets[sheetName]); } return sheets; case '.csv': const csvContent = await fs.readFile(filePath, 'utf-8'); return parse(csvContent, { columns: true }); case '.txt': case '.json': return await fs.readFile(filePath, 'utf-8'); default: throw new Error(`Unsupported file format: ${ext}`); } } // Create server instance const server = new Server( { name: 'creditclaudit-mcp', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); // Define available tools server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'analyze_tenant_credit', description: 'Analyze tenant credit using CreditClaudit methodology. Accepts company website URL and financial statement files.', inputSchema: { type: 'object', properties: { companyName: { type: 'string', description: 'Name of the company to analyze', }, websiteUrl: { type: 'string', description: 'Company website URL for business model and competitive analysis', }, financialFiles: { type: 'array', items: { type: 'string', }, description: 'Paths to financial statement files (PDF, Excel, CSV)', }, industry: { type: 'string', description: 'Industry classification (optional, will be determined from website if not provided)', }, }, required: ['companyName'], }, }, { name: 'read_financial_file', description: 'Read and parse a financial statement file', inputSchema: { type: 'object', properties: { filePath: { type: 'string', description: 'Path to the financial file', }, }, required: ['filePath'], }, }, { name: 'get_methodology_info', description: 'Get information about CreditClaudit methodology components', inputSchema: { type: 'object', properties: { component: { type: 'string', enum: ['overview', 'scoring', 'industry', 'financial', 'competitive', 'liquidity', 'management', 'revenue-adjustment', 'industry-reference', 'methodology-guide', 'scoring-reference', 'templates-and-tools', 'calibration-examples', 'calibration-streamlined'], description: 'Methodology component to retrieve', }, }, required: ['component'], }, }, ], }; }); // Handle tool calls server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; switch (name) { case 'read_financial_file': { try { const data = await readFinancialFile(args.filePath); return { content: [ { type: 'text', text: JSON.stringify(data, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error reading file: ${error.message}`, }, ], }; } } case 'get_methodology_info': { // Read methodology documentation const methodologyPath = path.join(__dirname, 'Original', 'instructions.md'); const scoringPath = path.join(__dirname, 'Original', 'scoring-matrices.md'); let content = ''; switch (args.component) { case 'overview': content = await fs.readFile(methodologyPath, 'utf-8'); break; case 'scoring': content = await fs.readFile(scoringPath, 'utf-8'); break; case 'revenue-adjustment': const revenueAdjustmentPath = path.join(__dirname, 'Original', 'revenue-adjustment.md'); content = await fs.readFile(revenueAdjustmentPath, 'utf-8'); break; case 'industry-reference': const industryRefPath = path.join(__dirname, 'Original', 'industry-reference.md'); content = await fs.readFile(industryRefPath, 'utf-8'); break; case 'methodology-guide': const methodologyGuidePath = path.join(__dirname, 'Original', 'methodology-guide.md'); content = await fs.readFile(methodologyGuidePath, 'utf-8'); break; case 'scoring-reference': const scoringRefPath = path.join(__dirname, 'Original', 'scoring-reference.md'); content = await fs.readFile(scoringRefPath, 'utf-8'); break; case 'templates-and-tools': const templatesPath = path.join(__dirname, 'Original', 'templates-and-tools.md'); content = await fs.readFile(templatesPath, 'utf-8'); break; case 'calibration-examples': const calibrationPath = path.join(__dirname, 'Original', 'calibration-examples.md'); content = await fs.readFile(calibrationPath, 'utf-8'); break; case 'calibration-streamlined': const calibrationStreamlinedPath = path.join(__dirname, 'Original', 'calibration-examples-streamlined.md'); content = await fs.readFile(calibrationStreamlinedPath, 'utf-8'); break; default: // Extract specific sections from documentation const fullDoc = await fs.readFile(methodologyPath, 'utf-8'); const sectionMap = { 'industry': '## Industry Risk Assessment', 'financial': '## Financial Risk Assessment', 'competitive': '## Competitive Position Assessment', 'liquidity': '## Liquidity Assessment', 'management': '## Management & Governance Assessment', }; if (sectionMap[args.component]) { const sectionStart = fullDoc.indexOf(sectionMap[args.component]); if (sectionStart !== -1) { const nextSection = fullDoc.indexOf('\n## ', sectionStart + 1); content = fullDoc.substring(sectionStart, nextSection !== -1 ? nextSection : undefined); } } } return { content: [ { type: 'text', text: content, }, ], }; } case 'analyze_tenant_credit': { // This is a placeholder for the full credit analysis implementation // In a complete implementation, this would: // 1. Fetch and analyze the company website // 2. Parse financial statements // 3. Apply CreditClaudit methodology // 4. Generate comprehensive report return { content: [ { type: 'text', text: `Credit analysis for ${args.companyName} initiated.\n\nInputs:\n- Website: ${args.websiteUrl || 'Not provided'}\n- Financial files: ${args.financialFiles?.length || 0} files\n- Industry: ${args.industry || 'To be determined'}\n\nNote: Full analysis implementation pending. This tool will:\n1. Analyze company website for business model and competitive position\n2. Parse financial statements for quantitative scoring\n3. Apply CreditClaudit methodology\n4. Generate comprehensive credit assessment report`, }, ], }; } default: return { content: [ { type: 'text', text: `Unknown tool: ${name}`, }, ], }; } }); // Start the server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error('CreditClaudit MCP server running on stdio'); } main().catch((error) => { console.error('Server error:', error); process.exit(1); });