perplexity-mcp-server
Version:
A Perplexity API Model Context Protocol (MCP) server that unlocks Perplexity's search-augmented AI capabilities for LLM agents. Features robust error handling, secure input validation, and transparent reasoning with the showThinking parameter. Built with
106 lines (105 loc) • 4.24 kB
JavaScript
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
import winston from "winston";
// Handle ESM module dirname
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Resolve logs directory relative to project root (2 levels up from utils/)
const projectRoot = path.resolve(__dirname, '..', '..');
const logsDir = path.join(projectRoot, 'logs');
class Logger {
constructor() {
this.isInitialized = false;
// Create a basic logger initially. It will be configured later.
this.logger = winston.createLogger({
level: 'info', // Default level before initialization
format: winston.format.json(),
transports: [
// Removed initial Console transport.
// MCP clients (Ex. Claude Desktop) communicate over stdio/stdout and can show errors if
// unexpected console logs are present. All logging should go to files.
]
});
}
initialize(logLevel = 'info') {
if (this.isInitialized) {
this.warn("Logger already initialized.");
return;
}
// Ensure logs directory exists
try {
if (!fs.existsSync(logsDir)) {
fs.mkdirSync(logsDir, { recursive: true });
}
}
catch (error) {
this.error("Failed to create logs directory", {
error: error instanceof Error ? error.message : String(error),
path: logsDir
});
// Continue without file logging if directory creation fails
this.isInitialized = true; // Mark as initialized to prevent re-attempts
return;
}
// Common format for file transports
const commonFormat = winston.format.combine(winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.printf(({ timestamp, level, message, context, stack }) => {
const contextStr = context ? `\n Context: ${JSON.stringify(context, null, 2)}` : "";
const stackStr = stack ? `\n Stack: ${stack}` : "";
return `[${timestamp}] ${level}: ${message}${contextStr}${stackStr}`;
}));
// Configure the logger with file transports
this.logger.configure({
level: logLevel,
format: winston.format.json(), // Keep default format or adjust if needed
transports: [
// Ensure no console transport is added here either, for the same reasons as above.
new winston.transports.File({
filename: path.join(logsDir, 'combined.log'),
format: commonFormat
}),
new winston.transports.File({
filename: path.join(logsDir, 'error.log'),
level: 'error',
format: commonFormat
}),
new winston.transports.File({
filename: path.join(logsDir, 'warn.log'),
level: 'warn',
format: commonFormat
}),
new winston.transports.File({
filename: path.join(logsDir, 'info.log'),
level: 'info',
format: commonFormat
}),
new winston.transports.File({
filename: path.join(logsDir, 'debug.log'),
level: 'debug',
format: commonFormat
})
]
});
this.isInitialized = true;
this.info("Logger initialized with file transports.", { level: logLevel });
}
static getInstance() {
if (!Logger.instance) {
Logger.instance = new Logger();
}
return Logger.instance;
}
debug(message, context) {
this.logger.debug(message, { context });
}
info(message, context) {
this.logger.info(message, { context });
}
warn(message, context) {
this.logger.warn(message, { context });
}
error(message, context) {
this.logger.error(message, { context });
}
}
export const logger = Logger.getInstance();