@zxkane/quip-mcp-server
Version:
MCP server for interacting with Quip spreadsheets
247 lines • 9.01 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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.MockQuipClient = void 0;
/**
* Mock Quip client implementation for testing without a real Quip token
*/
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const logger_1 = require("./logger");
/**
* Mock Quip client for testing without a real Quip API token
*/
class MockQuipClient {
/**
* Initialize the mock client with sample data
*/
constructor() {
this.mockData = new Map();
logger_1.logger.info('Initializing MockQuipClient');
// Add some sample spreadsheets
this.addMockSpreadsheet({
threadId: 'sample1',
title: 'Sample Spreadsheet 1',
sheets: [
{
name: 'Sheet1',
csv: 'Name,Age,Email\nJohn Doe,30,john@example.com\nJane Smith,25,jane@example.com\nBob Johnson,40,bob@example.com'
},
{
name: 'Sheet2',
csv: 'Product,Price,Quantity\nWidget A,10.99,100\nWidget B,15.99,50\nWidget C,5.99,200'
}
]
});
this.addMockSpreadsheet({
threadId: 'sample2',
title: 'Sample Spreadsheet 2',
sheets: [
{
name: 'Data',
csv: 'Date,Revenue,Expenses,Profit\n2023-01-01,5000,3000,2000\n2023-02-01,5500,3200,2300\n2023-03-01,6000,3500,2500'
}
]
});
// Add a large mock spreadsheet
const largeSheet = this.generateLargeSheet(100, 10);
this.addMockSpreadsheet({
threadId: 'large',
title: 'Large Sample Spreadsheet',
sheets: [
{
name: 'LargeSheet',
csv: largeSheet
}
]
});
}
/**
* Add a mock spreadsheet to the client
*
* @param spreadsheet Mock spreadsheet data
*/
addMockSpreadsheet(spreadsheet) {
this.mockData.set(spreadsheet.threadId, spreadsheet);
logger_1.logger.debug(`Added mock spreadsheet: ${spreadsheet.threadId}`, { title: spreadsheet.title });
}
/**
* Get a thread by ID
*
* @param threadId ID of the thread to retrieve
* @returns Promise resolving to thread information
* @throws Error if the thread is not found
*/
async getThread(threadId) {
logger_1.logger.info(`Getting mock thread: ${threadId}`);
const spreadsheet = this.mockData.get(threadId);
if (!spreadsheet) {
logger_1.logger.error(`Mock thread not found: ${threadId}`);
throw new Error(`Thread not found: ${threadId}`);
}
// Create a mock thread response
return {
thread: {
id: threadId,
title: spreadsheet.title,
type: 'spreadsheet'
},
html: this.generateMockHtml(spreadsheet)
};
}
/**
* Export a thread to XLSX format and save it locally
*
* @param threadId ID of the thread to export
* @param outputPath Local file path where the XLSX file should be saved
* @returns Promise resolving to path to the saved XLSX file
* @throws Error if the thread is not found
*/
async exportThreadToXLSX(threadId, outputPath) {
logger_1.logger.info(`Exporting mock thread ${threadId} to XLSX at ${outputPath}`);
const spreadsheet = this.mockData.get(threadId);
if (!spreadsheet) {
logger_1.logger.error(`Mock thread not found: ${threadId}`);
throw new Error(`Thread not found: ${threadId}`);
}
// In a real implementation, we would convert the CSV to XLSX here
// For the mock, we'll just write a placeholder file
await fs.mkdirp(path.dirname(path.resolve(outputPath)));
await fs.writeFile(outputPath, 'Mock XLSX content');
logger_1.logger.info(`Successfully exported mock XLSX to ${outputPath}`);
return outputPath;
}
/**
* Export a thread to CSV format using HTML parsing as fallback method
*
* @param threadId ID of the thread to export
* @param sheetName Name of the sheet to extract (optional)
* @returns Promise resolving to CSV data as string
* @throws Error if the thread is not found or does not contain a spreadsheet
*/
async exportThreadToCSVFallback(threadId, sheetName) {
logger_1.logger.info(`Using fallback method to export mock thread ${threadId} to CSV, sheet: ${sheetName || 'default'}`);
const spreadsheet = this.mockData.get(threadId);
if (!spreadsheet) {
logger_1.logger.error(`Mock thread not found: ${threadId}`);
throw new Error(`Thread not found: ${threadId}`);
}
// Find the requested sheet or use the first one
let sheet;
if (sheetName) {
sheet = spreadsheet.sheets.find(s => s.name === sheetName);
if (!sheet) {
logger_1.logger.error(`Sheet not found: ${sheetName}`);
throw new Error(`Sheet '${sheetName}' not found in thread ${threadId}`);
}
}
else {
sheet = spreadsheet.sheets[0];
if (!sheet) {
logger_1.logger.error(`No sheets found in thread: ${threadId}`);
throw new Error(`No sheets found in thread ${threadId}`);
}
}
return sheet.csv;
}
/**
* Check if a thread is a spreadsheet
*
* @param threadId ID of the thread to check
* @returns Promise resolving to true if the thread is a spreadsheet, false otherwise
*/
async isSpreadsheet(threadId) {
logger_1.logger.info(`Checking if mock thread ${threadId} is a spreadsheet`);
return this.mockData.has(threadId);
}
/**
* Generate mock HTML for a spreadsheet
*
* @param spreadsheet Mock spreadsheet data
* @returns HTML string
*/
generateMockHtml(spreadsheet) {
let html = `<html><body><h1>${spreadsheet.title}</h1>`;
for (const sheet of spreadsheet.sheets) {
html += `<h2>${sheet.name}</h2>`;
html += '<table>';
const rows = sheet.csv.split('\n');
for (const row of rows) {
html += '<tr>';
const cells = row.split(',');
for (const cell of cells) {
html += `<td>${cell}</td>`;
}
html += '</tr>';
}
html += '</table>';
}
html += '</body></html>';
return html;
}
/**
* Generate a large CSV sheet for testing
*
* @param rows Number of rows
* @param cols Number of columns
* @returns CSV string
*/
generateLargeSheet(rows, cols) {
let csv = '';
// Generate header row
for (let c = 0; c < cols; c++) {
csv += `Column${c + 1}`;
if (c < cols - 1) {
csv += ',';
}
}
csv += '\n';
// Generate data rows
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
csv += `Value${r + 1}-${c + 1}`;
if (c < cols - 1) {
csv += ',';
}
}
if (r < rows - 1) {
csv += '\n';
}
}
return csv;
}
}
exports.MockQuipClient = MockQuipClient;
//# sourceMappingURL=mockClient.js.map