creditclaudit-mcp-server
Version:
MCP server for tenant credit assessment using CreditClaudit methodology
273 lines (253 loc) • 9.21 kB
JavaScript
#!/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);
});