const fs = require('fs');
const path = require('path');
const AdmZip = require('adm-zip');
// 辅助函数:格式化字节大小
function formatBytes(bytes, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
// ZIP操作类
class ZipOperations {
// 创建ZIP文件
static create(zipPath, files, recursive = false) {
try {
const zip = new AdmZip();
files.forEach(fileOrDir => {
if (fs.existsSync(fileOrDir)) {
const stats = fs.statSync(fileOrDir);
if (stats.isDirectory()) {
if (recursive) {
// 递归添加目录
zip.addLocalFolder(fileOrDir);
} else {
// 只添加目录本身
zip.addLocalFolder(fileOrDir, path.basename(fileOrDir));
}
} else {
// 添加单个文件
zip.addLocalFile(fileOrDir);
}
}
});
zip.writeZip(zipPath);
return {
success: true,
message: `ZIP文件创建成功: ${zipPath}`
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
// 提取ZIP文件
static extract(zipPath, destDir = '.') {
try {
const zip = new AdmZip(zipPath);
if (!fs.existsSync(destDir)) {
fs.mkdirSync(destDir, { recursive: true });
}
zip.extractAllTo(destDir, true);
return {
success: true,
message: `ZIP文件提取成功: ${zipPath} -> ${destDir}`
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
// 列出ZIP文件内容
static list(zipPath, verbose = false) {
try {
const zip = new AdmZip(zipPath);
const zipEntries = zip.getEntries();
const entries = zipEntries.map(entry => ({
name: entry.entryName,
size: entry.header.size,
compressedSize: entry.header.compressedSize,
modifiedTime: entry.header.time.toISOString(),
isDirectory: entry.isDirectory
}));
return {
success: true,
message: `ZIP文件内容列出成功: ${zipPath}`,
entries: entries
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
// 向ZIP文件添加文件
static add(zipPath, files, recursive = false) {
try {
const zip = new AdmZip(zipPath);
files.forEach(fileOrDir => {
if (fs.existsSync(fileOrDir)) {
const stats = fs.statSync(fileOrDir);
if (stats.isDirectory()) {
if (recursive) {
zip.addLocalFolder(fileOrDir);
} else {
zip.addLocalFolder(fileOrDir, path.basename(fileOrDir));
}
} else {
zip.addLocalFile(fileOrDir);
}
}
});
zip.writeZip(zipPath);
return {
success: true,
message: `文件已成功添加到ZIP: ${zipPath}`
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
// 从ZIP文件中删除文件
static delete(zipPath, entries) {
try {
const zip = new AdmZip(zipPath);
const deletedEntries = [];
const notFoundEntries = [];
entries.forEach(entryName => {
if (zip.deleteFile(entryName)) {
deletedEntries.push(entryName);
} else {
notFoundEntries.push(entryName);
}
});
zip.writeZip(zipPath);
return {
success: true,
message: `文件已成功从ZIP中删除: ${zipPath}`,
deletedEntries: deletedEntries,
notFoundEntries: notFoundEntries
};
} catch (error) {
return {
success: false,
error: error.message
};
}
}
// 查看ZIP文件中的单个文件内容
static view(zipPath, entryName) {
try {
const zip = new AdmZip(zipPath);
const entry = zip.getEntry(entryName);
if (entry) {
const content = entry.getData().toString('utf8');
return {
success: true,
message: `ZIP文件内容查看成功: ${entryName}`,
content: content
};
} else {
return {
success: false,
error: `未找到条目: ${entryName}`
};
}
} catch (error) {
return {
success: false,
error: error.message
};
}
}
// 验证ZIP文件
static verify(zipPath) {
try {
const zip = new AdmZip(zipPath);
const zipEntries = zip.getEntries();
const verifiedEntries = [];
const failedEntries = [];
zipEntries.forEach(entry => {
try {
// 尝试读取文件内容,验证完整性
entry.getData();
verifiedEntries.push(entry.entryName);
} catch (error) {
failedEntries.push({ name: entry.entryName, error: error.message });
}
});
if (failedEntries.length > 0) {
return {
success: false,
message: `ZIP文件验证失败,存在损坏的条目`,
verifiedEntries: verifiedEntries,
failedEntries: failedEntries
};
} else {
return {
success: true,
message: `ZIP文件验证通过,所有条目完好`,
verifiedEntries: verifiedEntries,
failedEntries: failedEntries
};
}
} catch (error) {
return {
success: false,
error: error.message
};
}
}
}
// MCP工具定义
const tools = [
{
name: 'zip.create',
title: '创建ZIP文件',
description: '创建一个新的ZIP文件,并添加指定的文件或目录',
inputSchema: {
type: 'object',
properties: {
zipPath: {
type: 'string',
description: '要创建的ZIP文件路径'
},
files: {
type: 'array',
items: {
type: 'string'
},
description: '要添加到ZIP文件的文件或目录路径列表'
},
recursive: {
type: 'boolean',
description: '是否递归添加目录内容',
default: false
}
},
required: ['zipPath', 'files']
},
outputSchema: {
type: 'object',
properties: {
success: {
type: 'boolean',
description: '操作是否成功'
},
message: {
type: 'string',
description: '操作结果消息'
}
}
}
},
{
name: 'zip.extract',
title: '提取ZIP文件',
description: '将ZIP文件的内容提取到指定目录',
inputSchema: {
type: 'object',
properties: {
zipPath: {
type: 'string',
description: '要提取的ZIP文件路径'
},
destDir: {
type: 'string',
description: '提取目标目录,默认为当前目录'
}
},
required: ['zipPath']
},
outputSchema: {
type: 'object',
properties: {
success: {
type: 'boolean',
description: '操作是否成功'
},
message: {
type: 'string',
description: '操作结果消息'
}
}
}
},
{
name: 'zip.list',
title: '列出ZIP内容',
description: '列出ZIP文件中的所有条目',
inputSchema: {
type: 'object',
properties: {
zipPath: {
type: 'string',
description: '要列出内容的ZIP文件路径'
},
verbose: {
type: 'boolean',
description: '是否显示详细信息',
default: false
}
},
required: ['zipPath']
},
outputSchema: {
type: 'object',
properties: {
success: {
type: 'boolean',
description: '操作是否成功'
},
message: {
type: 'string',
description: '操作结果消息'
},
entries: {
type: 'array',
items: {
type: 'object',
properties: {
name: {
type: 'string',
description: '条目名称'
},
size: {
type: 'number',
description: '文件大小(字节)'
},
compressedSize: {
type: 'number',
description: '压缩后大小(字节)'
},
modifiedTime: {
type: 'string',
format: 'date-time',
description: '修改时间'
},
isDirectory: {
type: 'boolean',
description: '是否为目录'
}
}
},
description: 'ZIP文件中的条目列表'
}
}
}
},
{
name: 'zip.add',
title: '向ZIP添加文件',
description: '向现有ZIP文件添加新的文件或目录',
inputSchema: {
type: 'object',
properties: {
zipPath: {
type: 'string',
description: '要添加文件的ZIP文件路径'
},
files: {
type: 'array',
items: {
type: 'string'
},
description: '要添加到ZIP文件的文件或目录路径列表'
},
recursive: {
type: 'boolean',
description: '是否递归添加目录内容',
default: false
}
},
required: ['zipPath', 'files']
},
outputSchema: {
type: 'object',
properties: {
success: {
type: 'boolean',
description: '操作是否成功'
},
message: {
type: 'string',
description: '操作结果消息'
}
}
}
},
{
name: 'zip.delete',
title: '从ZIP删除文件',
description: '从ZIP文件中删除指定的条目',
inputSchema: {
type: 'object',
properties: {
zipPath: {
type: 'string',
description: '要删除条目的ZIP文件路径'
},
entries: {
type: 'array',
items: {
type: 'string'
},
description: '要从ZIP文件中删除的条目名称列表'
}
},
required: ['zipPath', 'entries']
},
outputSchema: {
type: 'object',
properties: {
success: {
type: 'boolean',
description: '操作是否成功'
},
message: {
type: 'string',
description: '操作结果消息'
},
deletedEntries: {
type: 'array',
items: {
type: 'string'
},
description: '成功删除的条目名称列表'
},
notFoundEntries: {
type: 'array',
items: {
type: 'string'
},
description: '未找到的条目名称列表'
}
}
}
},
{
name: 'zip.view',
title: '查看ZIP中的文件内容',
description: '查看ZIP文件中单个文件的内容',
inputSchema: {
type: 'object',
properties: {
zipPath: {
type: 'string',
description: 'ZIP文件路径'
},
entryName: {
type: 'string',
description: '要查看的ZIP条目名称'
}
},
required: ['zipPath', 'entryName']
},
outputSchema: {
type: 'object',
properties: {
success: {
type: 'boolean',
description: '操作是否成功'
},
message: {
type: 'string',
description: '操作结果消息'
},
content: {
type: 'string',
description: 'ZIP条目内容'
}
}
}
},
{
name: 'zip.verify',
title: '验证ZIP文件',
description: '验证ZIP文件的完整性',
inputSchema: {
type: 'object',
properties: {
zipPath: {
type: 'string',
description: '要验证的ZIP文件路径'
}
},
required: ['zipPath']
},
outputSchema: {
type: 'object',
properties: {
success: {
type: 'boolean',
description: '操作是否成功'
},
message: {
type: 'string',
description: '操作结果消息'
},
verifiedEntries: {
type: 'array',
items: {
type: 'string'
},
description: '验证通过的条目名称列表'
},
failedEntries: {
type: 'array',
items: {
type: 'object',
properties: {
name: {
type: 'string',
description: '验证失败的条目名称'
},
error: {
type: 'string',
description: '验证失败的原因'
}
}
},
description: '验证失败的条目列表'
}
}
}
}
];
// JSON-RPC 2.0 处理器
class JsonRpcHandler {
constructor() {
this.isInitialized = false;
}
// 处理JSON-RPC请求
handleRequest(request) {
try {
// 解析请求
const parsedRequest = typeof request === 'string' ? JSON.parse(request) : request;
// 检查JSON-RPC版本
if (parsedRequest.jsonrpc !== '2.0') {
return this.createErrorResponse(parsedRequest.id, -32600, 'Invalid Request', 'JSON-RPC version must be 2.0');
}
// 检查方法
if (!parsedRequest.method || typeof parsedRequest.method !== 'string') {
return this.createErrorResponse(parsedRequest.id, -32600, 'Invalid Request', 'Method must be a string');
}
// 处理MCP方法
if (parsedRequest.method === 'tools/list') {
return this.handleToolsList(parsedRequest);
} else if (parsedRequest.method === 'tools/call') {
return this.handleToolsCall(parsedRequest);
}
// 处理其他JSON-RPC方法(保持向后兼容)
switch (parsedRequest.method) {
case 'initialize':
return this.handleInitialize(parsedRequest);
case 'create':
return this.handleCreate(parsedRequest);
case 'extract':
return this.handleExtract(parsedRequest);
case 'list':
return this.handleList(parsedRequest);
case 'add':
return this.handleAdd(parsedRequest);
case 'delete':
return this.handleDelete(parsedRequest);
case 'view':
return this.handleView(parsedRequest);
case 'verify':
return this.handleVerify(parsedRequest);
default:
return this.createErrorResponse(parsedRequest.id, -32601, 'Method not found', `Method ${parsedRequest.method} not supported`);
}
} catch (error) {
return this.createErrorResponse(null, -32700, 'Parse error', error.message);
}
}
// 处理initialize方法(保持向后兼容)
handleInitialize(request) {
this.isInitialized = true;
return this.createSuccessResponse(request.id, {
protocolVersion: '2025-06-18',
capabilities: {
commands: tools.map(tool => tool.name.replace('zip.', ''))
},
serverInfo: {
name: 'zip-mcp',
version: '1.0.0',
description: 'ZIP MCP - 支持常用ZIP命令功能的工具'
}
});
}
// 处理tools/list方法
handleToolsList(request) {
return this.createSuccessResponse(request.id, {
tools: tools
});
}
// 处理tools/call方法
handleToolsCall(request) {
const { tool, params } = request.params || {};
if (!tool) {
return this.createErrorResponse(request.id, -32602, 'Invalid params', 'Missing required parameter: tool');
}
// 调用对应的工具
switch (tool) {
case 'zip.create':
return this.handleCreate({ ...request, params: params });
case 'zip.extract':
return this.handleExtract({ ...request, params: params });
case 'zip.list':
return this.handleList({ ...request, params: params });
case 'zip.add':
return this.handleAdd({ ...request, params: params });
case 'zip.delete':
return this.handleDelete({ ...request, params: params });
case 'zip.view':
return this.handleView({ ...request, params: params });
case 'zip.verify':
return this.handleVerify({ ...request, params: params });
default:
return this.createErrorResponse(request.id, -32601, 'Method not found', `Tool ${tool} not supported`);
}
}
// 处理create方法
handleCreate(request) {
const { zipPath, files, recursive = false } = request.params || {};
if (!zipPath || !files || !Array.isArray(files)) {
return this.createErrorResponse(request.id, -32602, 'Invalid params', 'Missing required parameters: zipPath and files');
}
const result = ZipOperations.create(zipPath, files, recursive);
return result.success
? this.createSuccessResponse(request.id, result)
: this.createErrorResponse(request.id, -32603, 'Execution error', result.error);
}
// 处理extract方法
handleExtract(request) {
const { zipPath, destDir = '.' } = request.params || {};
if (!zipPath) {
return this.createErrorResponse(request.id, -32602, 'Invalid params', 'Missing required parameter: zipPath');
}
const result = ZipOperations.extract(zipPath, destDir);
return result.success
? this.createSuccessResponse(request.id, result)
: this.createErrorResponse(request.id, -32603, 'Execution error', result.error);
}
// 处理list方法
handleList(request) {
const { zipPath, verbose = false } = request.params || {};
if (!zipPath) {
return this.createErrorResponse(request.id, -32602, 'Invalid params', 'Missing required parameter: zipPath');
}
const result = ZipOperations.list(zipPath, verbose);
return result.success
? this.createSuccessResponse(request.id, result)
: this.createErrorResponse(request.id, -32603, 'Execution error', result.error);
}
// 处理add方法
handleAdd(request) {
const { zipPath, files, recursive = false } = request.params || {};
if (!zipPath || !files || !Array.isArray(files)) {
return this.createErrorResponse(request.id, -32602, 'Invalid params', 'Missing required parameters: zipPath and files');
}
const result = ZipOperations.add(zipPath, files, recursive);
return result.success
? this.createSuccessResponse(request.id, result)
: this.createErrorResponse(request.id, -32603, 'Execution error', result.error);
}
// 处理delete方法
handleDelete(request) {
const { zipPath, entries } = request.params || {};
if (!zipPath || !entries || !Array.isArray(entries)) {
return this.createErrorResponse(request.id, -32602, 'Invalid params', 'Missing required parameters: zipPath and entries');
}
const result = ZipOperations.delete(zipPath, entries);
return result.success
? this.createSuccessResponse(request.id, result)
: this.createErrorResponse(request.id, -32603, 'Execution error', result.error);
}
// 处理view方法
handleView(request) {
const { zipPath, entryName } = request.params || {};
if (!zipPath || !entryName) {
return this.createErrorResponse(request.id, -32602, 'Invalid params', 'Missing required parameters: zipPath and entryName');
}
const result = ZipOperations.view(zipPath, entryName);
return result.success
? this.createSuccessResponse(request.id, result)
: this.createErrorResponse(request.id, -32603, 'Execution error', result.error);
}
// 处理verify方法
handleVerify(request) {
const { zipPath } = request.params || {};
if (!zipPath) {
return this.createErrorResponse(request.id, -32602, 'Invalid params', 'Missing required parameter: zipPath');
}
const result = ZipOperations.verify(zipPath);
return result.success
? this.createSuccessResponse(request.id, result)
: this.createErrorResponse(request.id, -32603, 'Execution error', result.error);
}
// 创建成功响应
createSuccessResponse(id, result) {
return {
jsonrpc: '2.0',
id: id,
result: result
};
}
// 创建错误响应
createErrorResponse(id, code, message, data) {
return {
jsonrpc: '2.0',
id: id,
error: {
code: code,
message: message,
data: data
}
};
}
}
// 初始化JSON-RPC处理器
const jsonRpcHandler = new JsonRpcHandler();
// 监听标准输入
process.stdin.setEncoding('utf8');
let inputBuffer = '';
process.stdin.on('data', (chunk) => {
inputBuffer += chunk;
// 处理每一行输入
const lines = inputBuffer.split('\n');
inputBuffer = lines.pop(); // 保留最后一行,可能不完整
lines.forEach(line => {
if (line.trim()) {
try {
const response = jsonRpcHandler.handleRequest(line);
process.stdout.write(JSON.stringify(response) + '\n');
} catch (error) {
// 记录错误到stderr,但不影响服务运行
process.stderr.write(`Error processing request: ${error.message}\n`);
}
}
});
});
// 监听进程结束事件
process.on('SIGINT', () => {
process.exit(0);
});
process.on('SIGTERM', () => {
process.exit(0);
});
// 确保进程不会意外退出
process.on('uncaughtException', (error) => {
process.stderr.write(`Uncaught exception: ${error.message}\n`);
});
process.on('unhandledRejection', (reason, promise) => {
process.stderr.write(`Unhandled rejection: ${reason}\n`);
});