@zxkane/quip-mcp-server
Version:
MCP server for interacting with Quip spreadsheets
342 lines • 12.4 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.logger = exports.Logger = exports.LogLevel = void 0;
exports.configureLogger = configureLogger;
/**
* Structured logging for the Quip MCP Server
* Using Winston logging library
*/
const winston_1 = __importDefault(require("winston"));
const path = __importStar(require("path"));
const fs = __importStar(require("fs"));
/**
* Log levels
*/
var LogLevel;
(function (LogLevel) {
LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
LogLevel[LogLevel["INFO"] = 1] = "INFO";
LogLevel[LogLevel["WARN"] = 2] = "WARN";
LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
})(LogLevel || (exports.LogLevel = LogLevel = {}));
// Map our LogLevel enum to Winston's levels
const levelMap = {
[LogLevel.DEBUG]: 'debug',
[LogLevel.INFO]: 'info',
[LogLevel.WARN]: 'warn',
[LogLevel.ERROR]: 'error'
};
// Map Winston's levels back to our LogLevel enum
const reverseLevelMap = {
'debug': LogLevel.DEBUG,
'info': LogLevel.INFO,
'warn': LogLevel.WARN,
'error': LogLevel.ERROR
};
/**
* Logger class for structured logging using Winston
*/
class Logger {
/**
* Create a new logger
*
* @param config Logger configuration
* @param context Additional context to include in all log entries
*/
constructor(config = {}, context = {}) {
// Store original console methods
this.originalConsole = {
debug: console.debug,
info: console.info,
warn: console.warn,
error: console.error
};
const level = config.level ?? LogLevel.WARN;
const shouldUseTimestamps = config.timestamps ?? true;
const shouldUseJSON = config.json ?? false;
const logFile = config.logFile;
const maxFileSize = config.maxFileSize ?? 10 * 1024 * 1024; // 10MB default
const maxFiles = config.maxFiles ?? 5;
// Create format based on configuration
let format;
if (shouldUseJSON) {
format = winston_1.default.format.combine(shouldUseTimestamps ? winston_1.default.format.timestamp() : winston_1.default.format.simple(), winston_1.default.format.json());
}
else {
const customFormat = winston_1.default.format.printf(({ level, message, timestamp, ...rest }) => {
const prefix = shouldUseTimestamps && timestamp ? `[${timestamp}] ${level.toUpperCase()}: ` : `${level.toUpperCase()}: `;
let output = `${prefix}${message}`;
// Add any additional data as JSON
const additionalData = { ...rest };
delete additionalData.level;
delete additionalData.message;
delete additionalData.timestamp;
if (Object.keys(additionalData).length > 0) {
output += ` ${JSON.stringify(additionalData)}`;
}
return output;
});
format = winston_1.default.format.combine(shouldUseTimestamps ? winston_1.default.format.timestamp() : winston_1.default.format.simple(), customFormat);
}
// Create transports array
const transports = [
new winston_1.default.transports.Console()
];
// Add file transport if logFile is specified
if (logFile) {
// Ensure directory exists
const logDir = path.dirname(logFile);
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir, { recursive: true });
}
transports.push(new winston_1.default.transports.File({
filename: logFile,
maxsize: maxFileSize,
maxFiles: maxFiles,
tailable: true
}));
}
// Create winston logger
this.logger = winston_1.default.createLogger({
level: levelMap[level],
format: format,
transports: transports
});
this.context = context;
}
/**
* Create a child logger with additional context
*
* @param context Additional context to include in all log entries
* @returns New logger instance with combined context
*/
child(context) {
// Create new logger with the same configuration
const childLogger = new Logger({
level: reverseLevelMap[this.logger.level],
timestamps: true,
json: false
}, { ...this.context, ...context });
return childLogger;
}
/**
* Log a message at DEBUG level
*
* @param message Log message
* @param data Additional data to include in the log entry
*/
debug(message, data = {}) {
this.log(LogLevel.DEBUG, message, data);
}
/**
* Log a message at INFO level
*
* @param message Log message
* @param data Additional data to include in the log entry
*/
info(message, data = {}) {
this.log(LogLevel.INFO, message, data);
}
/**
* Log a message at WARN level
*
* @param message Log message
* @param data Additional data to include in the log entry
*/
warn(message, data = {}) {
this.log(LogLevel.WARN, message, data);
}
/**
* Log a message at ERROR level
*
* @param message Log message
* @param data Additional data to include in the log entry
*/
error(message, data = {}) {
this.log(LogLevel.ERROR, message, data);
}
/**
* Log a message at the specified level
*
* @param level Log level
* @param message Log message
* @param data Additional data to include in the log entry
*/
log(level, message, data = {}) {
// Skip if level is below configured minimum
if (level < reverseLevelMap[this.logger.level]) {
return;
}
// Build log entry
const logData = {
message
};
// Add context and data
if (Object.keys(this.context).length > 0) {
logData.context = this.context;
}
if (Object.keys(data).length > 0) {
logData.data = data;
}
// Log using winston - avoid passing message twice
this.logger.log(levelMap[level], message, {
context: logData.context,
data: logData.data
});
}
/**
* Configure global console methods to use this logger
*/
installAsGlobal() {
// Override console methods
console.debug = (message, ...args) => {
if (typeof message === 'string') {
this.debug(message, args.length > 0 ? { args } : {});
}
else {
this.originalConsole.debug(message, ...args);
}
};
console.info = (message, ...args) => {
if (typeof message === 'string') {
this.info(message, args.length > 0 ? { args } : {});
}
else {
this.originalConsole.info(message, ...args);
}
};
console.warn = (message, ...args) => {
if (typeof message === 'string') {
this.warn(message, args.length > 0 ? { args } : {});
}
else {
this.originalConsole.warn(message, ...args);
}
};
console.error = (message, ...args) => {
if (typeof message === 'string') {
this.error(message, args.length > 0 ? { args } : {});
}
else {
this.originalConsole.error(message, ...args);
}
};
}
/**
* Reconfigure an existing logger with new settings
*
* @param config Logger configuration
*/
reconfigure(config = {}) {
const level = config.level ?? LogLevel.WARN;
const shouldUseTimestamps = config.timestamps ?? true;
const shouldUseJSON = config.json ?? false;
const logFile = config.logFile;
const maxFileSize = config.maxFileSize ?? 10 * 1024 * 1024; // 10MB default
const maxFiles = config.maxFiles ?? 5;
// Create format based on configuration
let format;
if (shouldUseJSON) {
format = winston_1.default.format.combine(shouldUseTimestamps ? winston_1.default.format.timestamp() : winston_1.default.format.simple(), winston_1.default.format.json());
}
else {
const customFormat = winston_1.default.format.printf(({ level, message, timestamp, ...rest }) => {
const prefix = shouldUseTimestamps && timestamp ? `[${timestamp}] ${level.toUpperCase()}: ` : `${level.toUpperCase()}: `;
let output = `${prefix}${message}`;
// Add any additional data as JSON
const additionalData = { ...rest };
delete additionalData.level;
delete additionalData.message;
delete additionalData.timestamp;
if (Object.keys(additionalData).length > 0) {
output += ` ${JSON.stringify(additionalData)}`;
}
return output;
});
format = winston_1.default.format.combine(shouldUseTimestamps ? winston_1.default.format.timestamp() : winston_1.default.format.simple(), customFormat);
}
// Create transports array
const transports = [
new winston_1.default.transports.Console()
];
// Add file transport if logFile is specified
if (logFile) {
// Ensure directory exists
const logDir = path.dirname(logFile);
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir, { recursive: true });
}
transports.push(new winston_1.default.transports.File({
filename: logFile,
maxsize: maxFileSize,
maxFiles: maxFiles,
tailable: true
}));
}
// Update winston logger configuration
this.logger.configure({
level: levelMap[level],
format: format,
transports: transports
});
}
}
exports.Logger = Logger;
// Create default logger instance
exports.logger = new Logger();
/**
* Configure logger from command line options
*
* @param options Command line options
* @returns Configured logger instance
*/
function configureLogger(options = {}) {
const config = {
level: options.debug ? LogLevel.DEBUG : LogLevel.WARN,
json: options.json || false,
timestamps: true,
logFile: options.logFile
};
// Update the existing logger instead of creating a new one
exports.logger.reconfigure(config);
// Install as global logger
exports.logger.installAsGlobal();
return exports.logger;
}
//# sourceMappingURL=logger.js.map