首页 / 文章 / 实用笔记:我搭建了一个用于保存工作日志的MCP服务器——详情如下

实用笔记:我搭建了一个用于保存工作日志的MCP服务器——详情如下

《实用笔记》操作指南:我构建了一个用于管理工作日志的MCP服务器——以下内容包括适用于采用该模式的团队的合同、检查清单以及可直接插入的代码片段。

1733 词

本指南将重新梳理从原材料到可运行系统的完整流程,适用于文章《我构建了一个用于记录工作日志的MCP服务器——这是我所学到的一切》。重点在于可操作的步骤、明确的检查点,以及可直接放入代码仓库的代码,无需猜测其用途。 在概览阶段,应在修改代码之前明确输入参数、各步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新运行相应步骤,而无需猜测隐藏的状态。 需同时记录正常流程和异常恢复流程。重试机制、人工审核环节以及错误处理都是产品不可或缺的部分,而非后续需要补充的功能。

它的功能

在“功能说明”阶段工作时,首先列出相关约定:所需输入、成功信号以及部分失败时的处理方式。这样的清单能确保后续的代码修改保持一致性。 优先选择小型、可测试的单元,而非冗长的脚本。当某个步骤失败时,故障应能指向单一责任点,而非复杂的流程链。 需为每次调用记录工具名称、参数哈希值、延迟时间以及执行结果。没有这些记录,调试过程将会浪费大量时间。

## 14:32 #bugfix #websocket
Fixed the race condition in the WebSocket broadcast queue
## 16:10 #testing
Wrote E2E test covering two-client sync

如何构建它(完整步骤)

在编写“如何构建一个阶段”的内容时,首先需列出相关契约:所需的输入参数、成功信号以及部分失败时的处理方式。这样的清单能确保后续的代码修改保持一致性。 将这一阶段视为输入与经过验证的输出之间的契约。为相关产物命名,明确成功判定标准,并杜绝无声的半完成状态。 需记录每次调用的工具名称、参数哈希值、延迟时间以及最终结果。没有这些记录,调试过程将会浪费大量时间。

1. 骨架结构确实非常简单

在完成“1. 骨架阶段”时,首先需明确接口规范:所需输入、成功信号以及部分失败时的处理方式。这份清单能确保后续的代码修改保持一致性。 在功能结果旁记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。 需为每次调用记录工具名称、参数哈希值、延迟时间以及最终结果。没有这些记录,调试过程将会浪费大量时间。 在完成“1. 骨架阶段”时,首先需明确接口规范:所需输入、成功信号以及部分失败时的处理方式。这份清单能确保后续的代码修改保持一致性。 需同时记录正常流程和异常恢复流程。重试机制、人工审核环节以及死信处理都是产品功能的一部分,而非后续需要补充的内容。

npm install @modelcontextprotocol/server zod
import { McpServer } from '@modelcontextprotocol/server';
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
import * as z from 'zod/v4';
const server = new McpServer({ name: 'dev-diary', version: '1.0.0' });
server.registerTool(
  'log_work',
  {
    description: 'Append a timestamped entry to the developer diary...',
    inputSchema: z.object({
      text: z.string().min(1),
      tags: z.array(z.string()).optional(),
      date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
    }),
  },
  async ({ text, tags = [], date }) => {
    // ...append to diary/YYYY-MM-DD.md...
    return { content: [{ type: 'text', text: 'Logged.' }] };
  },
);
await server.connect(new StdioServerTransport());

2. 描述只是提示,而非文档

这两种描述形式作为提示时效果最佳,应将其视为可度量的基准。在扩大范围之前,先记录一个成功的案例、一个失败案例以及回滚说明。 优先选择小型且可测试的单元,而非冗长的脚本。当某一步骤失败时,故障应指向单一责任主体,而非复杂的流程链。 需为每轮及每次会话设定令牌预算。智能工具会大量消耗上下文资源,设置上限可避免演示过程变成意外的费用账单。

// ❌ documentation-style
description: 'Appends an entry to the diary.'
// ✅ prompt-style
description: 'Append a timestamped entry to the developer diary for today.
Use this whenever the user says they finished/did/fixed something and
wants it recorded.'

3. 围绕问题设计工具,而非表格

在处理阶段时的3种设计工具,若将其视为可度量的界面使用效果最佳。在扩大范围之前,先记录一个成功的案例、一个失败案例以及回滚说明。 将此阶段视为输入与已验证输出之间的契约。为相关成果命名,明确成功标准,绝不允许默许不完整的完成状态。 使用具有严格结构定义和明确副作用标注的工具。在自动批准之前,主机必须知晓哪些调用会改变状态。

演示问题(及其优雅的解决方案)

将演示问题与测试阶段视为可度量的对象来处理效果最佳。在扩大范围之前,需记录一份理想的操作流程、一个失败案例以及回滚说明。 在功能结果旁同时记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。 应提供具有明确结构规范和清晰副作用标注的工具。主机需要在自动批准之前知道哪些调用会改变系统状态。 将演示问题与测试阶段视为可度量的对象来处理效果最佳。在扩大范围之前,需记录一份理想的操作流程、一个失败案例以及回滚说明。 需同时记录正常流程与恢复流程。重试机制、人工审核环节以及死信处理都是产品功能的一部分,而非后续需要补充的内容。

import { Client } from '@modelcontextprotocol/client';
import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';
const transport = new StdioClientTransport({
  command: 'node',
  args: ['dist/server.js'],   // spawns the server as a child process
});
const client = new Client({ name: 'demo-client', version: '1.0.0' });
await client.connect(transport);
// Exactly what Claude Desktop does under the hood:
const { tools } = await client.listTools();
await client.callTool({ name: 'log_work', arguments: {
  text: 'Fixed the race condition in the broadcast queue',
  tags: ['bugfix', 'websocket'],
}});
=== 1. listTools ===
 • log_work — Append a timestamped entry to the developer diary...
 • search_diary — Full-text search across every entry...
 • daily_summary — Everything logged on a given date...
 • stats — Totals, active days, streaks, top tags...
=== 2. log_work x3 ===
Logged to 2026-08-22.md at 19:05 (tags: bugfix, websocket)
...
=== 5. stats ===
📊 1 entries across 2 day(s)
🔥 Streak: 2 consecutive day(s)
🏷️ Top tags: #bugfix (1), #websocket (1)

教程中未提及的内容

对于教程中演示的操作,应在修改代码之前明确输入参数、该步骤的负责人以及结束条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 建议使用小型、可测试的单元而非庞大的脚本。当某个步骤失败时,故障应指向单一的责任主体,而非复杂的流程链。 在网关处进行身份验证,在数据层进行重新授权。仅凭承载令牌并不能作为租户边界。

实际连接方法

在“真正实现连接”阶段,应在修改代码之前明确输入参数、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 将此阶段视为输入与已验证输出之间的契约。为相关成果命名,定义成功判定标准,并拒绝默许的半完成状态。 在网关处进行身份验证,在数据层再次授权。仅凭承载令牌并不足以界定租户边界。

{
  "mcpServers": {
    "dev-diary": {
      "command": "node",
      "args": ["/absolute/path/to/dev-diary-mcp/dist/server.js"],
      "env": { "DIARY_DIR": "/home/you/journal" }
    }
  }
}

为何 Markdown-as-database 获胜

至于为何选择 Markdown 作为数据库,应在修改代码之前明确输入参数、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 在功能结果旁记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。 在网关处进行身份验证,在数据层面重新授权。仅凭承载令牌并不足以界定租户边界。 至于为何选择 Markdown 作为数据库,应在修改代码之前明确输入参数、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 需同时记录正常流程和故障恢复流程。重试机制、人工审核环节以及死信处理都是产品的一部分,而非后续需要补充的功能。

尝试一下

在“尝试一下”阶段,首先写下契约内容:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 建议使用小型、可测试的单元而非庞大的脚本。当某个步骤失败时,故障应指向单一的责任模块,而非复杂的流程链。 需为每次调用记录工具名称、参数哈希值、延迟时间以及执行结果。没有这些记录,调试过程将会浪费大量时间。

git clone https://github.com/rogeriolaa/dev-diary-mcp
cd dev-diary-mcp && npm install && npm run build && npm run demo

运营检查清单

将“运营检查清单”阶段视为可度量的指标,效果会更好。在扩大范围之前,先记录一份标准操作示例、一个故障案例以及回滚说明。 应将配置信息与应用程序代码分开。环境文件、密钥存储和功能开关应集中存放于一个位置,以便操作人员无需查看整个系统结构即可进行审计。

提供具有严格数据结构定义及明确副作用标识的工具。在自动批准之前,主机方需要知道哪些调用会修改状态。

只要预算允许,就在持续集成过程中使用测试数据而非真实的付费 API 来执行关键路径的冒烟测试。

需同时记录正常流程与故障恢复流程。重试机制、人工审核环节以及错误处理都是产品不可或缺的部分,而非后续才需要完善的功能。

提供具有严格数据结构定义及明确副作用标识的工具。在自动批准之前,主机方需要知道哪些调用会修改状态。

在升级技术栈之前,应冻结版本、为关键路径保存标准操作记录,并确认回滚步骤。共享环境需要设置速率限制、进行租户身份验证,同时明确密钥轮换的责任人。与其展示花哨的一次性演示,不如追求扎实可靠的性能。

关于0f6d55786c75的批处理说明:请勿将提供者密钥放入代码仓库,为每个会话设置令牌上限,并将转录内容存储在评估测试用例的旁边,以便后续更换模型时仍能保持可比性。