Token导航 LogoToken导航TokenDH.com
Win 10 MCP Server Simple Persistant Logging logo
运维云端未说明官方级别未说明来源级核验

Win 10 MCP Server Simple Persistant Logging

MCP Server

一个用于在文件写入时自动创建备份并记录日志的持久化日志系统,适用于防止文件覆盖和数据丢失的场景。

工具数

0

提示词数

0

GitHub Stars

0

资源数

0
持久化存储JavaScriptClaude日志记录Claude DesktopClaude

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

作者 / 组织

trevorwilkerson

提供方

trevorwilkerson

最后核验

2026/5/17 20:21

快速接入

先看主来源和安装命令,再打开仓库或文档;下面只保留这个条目的关键接入事实。

详细介绍

Win-10-MCP-Server——简单持久的日志记录

虽然MCP Inspector显示了MCP服务器的所有通信,但我希望日志能保存在HDD上。 具体来说,因为我修改了index.js中“服务器文件系统/dist”中的“写入文件”案例

问题:Claude变得非常懒惰,可能无法编写它正在处理的整个文件。

例如,它可以写标题,然后说 \[其余代码保持不变…\]

现在我的文件被覆盖了。..

我做了两件事:

  1. 每次调用write_file工具时,我都会复制一个带有日期时间戳的目标文件(如果存在)。
  2. 记录文件名并将其发送回(返回语句)Claude Desktop App,以便用户看到备份文件名。

然后,我手动使用diff工具来验证发生了什么变化。…稍后修复克劳德的错误

这里是代理的绝佳用例。…提示,提示。.

1.创建 logger.mjs

创建一个名为的新文件 logger.mjs 在项目根目录中:

// logger.mjs
import fs from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";

// Determine the directory of the current module
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// Define logs directory relative to the logger.mjs file
const LOG_DIR = path.join(__dirname, "logs");

// Ensure the logs directory exists
async function ensureLogDir() {
    try {
        await fs.mkdir(LOG_DIR, { recursive: true });
    } catch (err) {
        console.error("Failed to create logs directory:", err);
    }
}

ensureLogDir();

// Define log file path
const LOG_FILE = path.join(LOG_DIR, "KAN_server.log");

/**
 * Logs a message to the log file with the specified level.
 *
 * @param {string} level - The severity level of the log (e.g., INFO, ERROR).
 * @param {string} message - The message to log.
 */
export async function logToFile(level, message) {
    const timestamp = new Date().toISOString();
    const line = `[${level.toUpperCase()}] ${timestamp} - ${message}\n`;

    try {
        await fs.appendFile(LOG_FILE, line);
    } catch (err) {
        // If writing fails, log to console.error without disrupting the main flow
        console.error("Failed to write to log file:", err);
    }

}

// Temporary verification (commented out to prevent interference)
// console.log("Logging to:", LOG_FILE); // Do not use, as it can interfere with JSON responses

添加index.js

我更改了我的index.js 它可以在以下目录中找到(Win10): C: \\用户\\TrevorW\\AppData\\Roaming\\npm\\node_modules@modelcontextprotocol\\服务器文件系统\\dist

3.实施 write_file 案例

修改 write_file 操作,确保它处理文件写入和备份创建,并记录所有相关信息。

// index.js

// ...imports and schema definitions...

/**
 * Validates the provided file path to prevent unauthorized access.
 *
 * @param {string} filePath - The file path to validate.
 * @returns {Promise} - The validated and normalized file path.
 * @throws Will throw an error if the path is invalid.
 */
async function validatePath(filePath) {
    // Normalize the path to prevent directory traversal
    const normalizedPath = path.normalize(filePath);

    // Define allowed base directories
    const allowedBase = path.resolve(process.cwd(), "Control_Charts");

    // Ensure the normalized path starts with the allowed base directory
    if (!normalizedPath.startsWith(allowedBase)) {
        throw new Error("Invalid file path. Access denied.");
    }

    // Additional validations can be added here (e.g., file extension checks)

    return normalizedPath;
}

/**
 * Handles various operations based on the provided operation name and arguments.
 *
 * @param {string} operation - The name of the operation to perform.
 * @param {object} args - The arguments required for the operation.
 * @returns {Promise} - The response object to send back.
 */
async function handleOperation(operation, args) {
    switch (operation) {
        case "write_file": {
            try {
                // 1. Parse incoming arguments
                const parsed = WriteFileArgsSchema.safeParse(args);
                if (!parsed.success) {
                    throw new Error(`Invalid arguments for write_file: ${parsed.error}`);
                }

                // 2. Validate the path
                const validPath = await validatePath(parsed.data.path);

                let backupName = null; // Initialize backupName to track if a backup is created

                // 3. Backup existing file if it exists
                try {
                    await fs.access(validPath); // Throws if file doesn't exist

                    // Build date/time string in MMDDYY_HH_MM format
                    const now = new Date();
                    const mm = String(now.getMonth() + 1).padStart(2, '0');
                    const dd = String(now.getDate()).padStart(2, '0');
                    const yy = String(now.getFullYear()).slice(-2);
                    const hh = String(now.getHours()).padStart(2, '0');
                    const mn = String(now.getMinutes()).padStart(2, '0');
                    const dateString = `${mm}${dd}${yy}_${hh}_${mn}`;

                    // Build backup file path
                    const dirName = path.dirname(validPath);
                    const baseName = path.basename(validPath);
                    backupName = `${baseName}.${dateString}__CLDBak`; // Assign to backupName
                    const backupPath = path.join(dirName, backupName);

                    // Rename original file to create backup
                    await fs.rename(validPath, backupPath);

                    // Log the backup creation
                    await logToFile("INFO", `Backup created at ${backupPath}`);
                } catch (err) {
                    if (err.code !== 'ENOENT') { // ENOENT: File does not exist
                        // Log unexpected errors during backup
                        await logToFile("ERROR", `Error accessing or backing up file: ${err.message}`);
                        throw err; // Re-throw unexpected errors
                    }
                    // File doesn't exist; proceed without backup
                    await logToFile("INFO", `No existing file at ${validPath}. No backup created.`);
                }

                // 4. Write the new content
                await fs.writeFile(validPath, parsed.data.content, "utf-8");
                await logToFile("INFO", `Successfully wrote to ${validPath}`);

                // 5. Prepare the response
                let responseText = `Successfully wrote to ${parsed.data.path}`;
                if (backupName) {
                    responseText += `\nKAN: Backup saved to ${backupName}`;
                }

                //  THIS IS THE KEY TO MAKE CLAUDE DESKTOP HAPPY
                //  The response HAS TO BE in JSON, or will bitch at you
                const response = {
                    content: [{
                        type: "text",
                        text: responseText,
                    }],
                };

                // 6. Return the response without any additional logging
                return response;
            } catch (error) {
                // Log the error details
                await logToFile("ERROR", `write_file operation failed: ${error.message}`);
                throw error; // Ensure the error is propagated appropriately
            }
        }

        // ... handle other cases ...      //  Example of the lazyness I am dealing with here ....

        default:
            throw new Error(`Unknown operation: ${operation}`);
    }
}

如果你已经做到了这一点,你可能会说,嘿,Trevor,为什么不使用类似IDE的游标来添加此功能呢。

我的答案是:三个月后我们会有光标吗?在Windows 10完全进入语音模式之前,我可能会学到一些东西。...

目录标签

目录标签

持久化存储JavaScriptClaude日志记录文件备份本地部署数据安全文件管理

支持客户端

Claude DesktopClaude

接入字段

传输方式(transport,传输协议)

未说明

鉴权方式(authType,认证方式)

none

工具数量(toolCount,工具数)

0

资源数量(resourceCount,资源数)

0

提示词数量(promptCount,提示词数)

0

权限和风险

未说明none部署方式未说明

接入前请确认传输方式、认证方式和部署位置,并根据实际工具能力限制访问范围。

安装前确认

不要直接授予不必要的文件、网络或账号权限;先核对安装命令和配置内容。

仍需确认:installCommand

来源信息

继续浏览同类 MCP