返回文章列表

skill和mcp

6 min read

什么是skill#

可复用工作流,核心是 SKILL.md,适合沉淀任务步骤、领域知识、参考资料和脚本

PLAIN
左右滑动查看完整代码
my-skill/
├── SKILL.md 核心描述文件
├── scripts/ 可执行脚本
├── references/ 参考文档
└── assets/ 静态资源

SKILL.md 标注好技能名称 和作用,之后定义流程,到了哪一步应该做什么事,有什么注意事项,你能调用哪些工具,什么流程调用什么脚本去做

skill示例#

主文件

MARKDOWN
左右滑动查看完整代码
---
name: weekly-code-report
description: 当用户要求根据 Git 提交记录和实际代码改动生成中文日报、周报、工作日志或进展总结时使用。默认收集本周周一 00:00 到当前时间的提交,也支持用户指定时间范围;生成报告前必须同时参考 commit 信息和代码 diff,并将最终报告写入 Markdown 文档文件。
---

# 每周代码日报生成

使用这个技能,根据 Git 历史和实际代码改动生成中文开发日报或周报。

## 工作流程

1. 确认目标目录是 Git 仓库。
2. 除非用户指定时间范围,否则使用默认范围:
   - 开始时间:当前自然周周一 00:00,本地时间。
   - 结束时间:当前时间。
   - 示例:如果今天是周二,就收集周一到周二当前时间为止的提交。
3. 在目标仓库中运行 `scripts/collect-weekly-git-report.ps1`,收集提交元数据、文件统计和 diff。
4. 先阅读脚本生成的素材 Markdown,再撰写报告。
5. 最终报告必须使用中文,并且只能基于 commit 信息和实际代码变更生成。
6. 最终报告必须写入目标仓库根目录的 Markdown 文档,不要只在对话中输出正文。
7. 文档命名使用脚本素材中的“建议报告文件”,格式为 `YYYY年MM月第N周.md`,例如 `2026年06月第2周.md`
8. 最终回复只说明已生成的文档路径和必要备注,不要粘贴完整报告正文。
9. 如果时间范围内没有提交,仍按命名规则生成报告文档,在文档中明确说明“本时间范围内没有提交记录”,不要编造工作内容。

## 命令

在目标 Git 仓库中运行:

```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File "<skill-dir>\scripts\collect-weekly-git-report.ps1" -Output ".\weekly-code-report-source.md"
```

可选参数:

```powershell
-Since "2026-06-08 00:00:00"
-Until "2026-06-10 18:00:00"
-Author "name-or-email"
-MaxCommits 80
-MaxDiffChars 80000
```

## 报告规则

- 优先输出具体工作项,不要只罗列 commit。
- 将相关提交合并成有意义的条目。
- 如果 diff 能看出关键文件或模块,需要在报告中体现。
- 必要时区分已完成工作、进行中工作、问题修复、测试验证和风险后续。
- 不要包含敏感信息或大段代码。
- 不要声称代码未体现的业务影响。
- 如果 commit 信息很模糊,需要结合文件统计和 diff 谨慎推断,并使用保守表述。
- 生成最终报告文档前,先从素材 Markdown 头部读取“建议报告文件”。
- 若目标仓库根目录已存在同名报告,先读取确认内容是否属于同一时间范围,再在现有文件基础上更新;不要覆盖无关文档。

## 输出格式

默认参考 `references/report-format.md`

最终报告应适合日报或站会使用,保持简洁:

```txt
今日/本周工作总结
主要完成
问题修复
代码质量/工程化
测试与验证
风险与后续
提交范围
```

## 边界情况

- 如果仓库在时间范围内没有提交,生成命名规范的报告文档,正文写明“本时间范围内没有提交记录”,然后停止。
- 如果当前目录不是 Git 仓库,请用户提供目标仓库路径。
- 如果 diff 被脚本截断,需要说明报告基于截断前可见的 diff 上下文。
- 如果存在多个作者,而用户要求生成个人日报,需要询问作者过滤条件;在安全明确时可以使用当前 Git 用户。
- `collect-weekly-git-report.ps1` 需要保持 UTF-8 编码并兼容 Windows PowerShell 5.1;不要把 Markdown 反引号直接写进双引号字符串。

脚本

MARKDOWN
左右滑动查看完整代码
param(
  [string]$Since = "",
  [string]$Until = "",
  [string]$Author = "",
  [string]$Output = "",
  [int]$MaxCommits = 80,
  [int]$MaxDiffChars = 80000
)

$ErrorActionPreference = "Stop"

function Get-WeekStart {
  param([datetime]$Now)

  # PowerShell 中 Sunday 是 0,Monday 是 1;这里转换成距离周一的天数。
  $daysSinceMonday = (([int]$Now.DayOfWeek + 6) % 7)
  return $Now.Date.AddDays(-$daysSinceMonday)
}

function Convert-ToGitDate {
  param([datetime]$Date)
  return $Date.ToString("yyyy-MM-dd HH:mm:ss zzz")
}

function Get-ReportFileName {
  param([datetime]$Date)

  # 以周一作为一周开始,计算当前日期在当月属于第几周。
  $firstDayOfMonth = Get-Date -Year $Date.Year -Month $Date.Month -Day 1 -Hour 0 -Minute 0 -Second 0
  $daysBeforeFirstMonday = (([int]$firstDayOfMonth.DayOfWeek + 6) % 7)
  $weekOfMonth = [Math]::Floor((($Date.Day - 1) + $daysBeforeFirstMonday) / 7) + 1
  return "{0:yyyy}年{0:MM}月第{1}周.md" -f $Date, $weekOfMonth
}

function Limit-Text {
  param(
    [string]$Text,
    [int]$Limit
  )

  if ($Limit -le 0 -or $Text.Length -le $Limit) {
    return $Text
  }

  return $Text.Substring(0, $Limit) + "`n`n[内容已截断,原始长度: $($Text.Length) 字符]"
}

$repoRoot = (& git rev-parse --show-toplevel 2>$null)
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($repoRoot)) {
  throw "当前目录不是 Git 仓库,请在目标仓库根目录或子目录中运行。"
}

$now = Get-Date

if ([string]::IsNullOrWhiteSpace($Since)) {
  $sinceDate = Get-WeekStart -Now $now
} else {
  $sinceDate = [datetime]::Parse($Since)
}

if ([string]::IsNullOrWhiteSpace($Until)) {
  $untilDate = $now
} else {
  $untilDate = [datetime]::Parse($Until)
}

$sinceText = Convert-ToGitDate -Date $sinceDate
$untilText = Convert-ToGitDate -Date $untilDate

$logArgs = @(
  "log",
  "--since=$sinceText",
  "--until=$untilText",
  "--date=iso-strict",
  "--pretty=format:%H%x09%h%x09%ad%x09%an%x09%ae%x09%s",
  "--reverse"
)

if (-not [string]::IsNullOrWhiteSpace($Author)) {
  $logArgs += "--author=$Author"
}

$commitLines = @(& git @logArgs)
if ($LASTEXITCODE -ne 0) {
  throw "读取 Git 提交记录失败。"
}

if ($commitLines.Count -gt $MaxCommits) {
  $commitLines = $commitLines | Select-Object -Last $MaxCommits
  $commitLimitNote = "提交数量超过 MaxCommits,仅保留最近 $MaxCommits 条。"
} else {
  $commitLimitNote = ""
}

$markdown = New-Object System.Text.StringBuilder
[void]$markdown.AppendLine("# 本周代码提交素材")
[void]$markdown.AppendLine("")
[void]$markdown.AppendLine("- 生成时间:$(Convert-ToGitDate -Date $now)")
[void]$markdown.AppendLine("- 仓库路径:$repoRoot")
[void]$markdown.AppendLine("- 统计范围:$sinceText ~ $untilText")
[void]$markdown.AppendLine("- 建议报告文件:$(Get-ReportFileName -Date $untilDate)")
if (-not [string]::IsNullOrWhiteSpace($Author)) {
  [void]$markdown.AppendLine("- 作者过滤:$Author")
}
if (-not [string]::IsNullOrWhiteSpace($commitLimitNote)) {
  [void]$markdown.AppendLine("- 注意:$commitLimitNote")
}
[void]$markdown.AppendLine("")

if ($commitLines.Count -eq 0) {
  [void]$markdown.AppendLine("## 提交列表")
  [void]$markdown.AppendLine("")
  [void]$markdown.AppendLine("本时间范围内没有提交记录。")
} else {
  [void]$markdown.AppendLine("## 提交列表")
  [void]$markdown.AppendLine("")
  [void]$markdown.AppendLine("| 时间 | 提交 | 作者 | 信息 |")
  [void]$markdown.AppendLine("| --- | --- | --- | --- |")

  $commits = @()
  foreach ($line in $commitLines) {
    if ([string]::IsNullOrWhiteSpace($line)) {
      continue
    }

    $parts = $line -split "`t", 6
    if ($parts.Count -lt 6) {
      continue
    }

    $commit = [pscustomobject]@{
      Hash = $parts[0]
      ShortHash = $parts[1]
      Date = $parts[2]
      AuthorName = $parts[3]
      AuthorEmail = $parts[4]
      Subject = $parts[5]
    }
    $commits += $commit

    $safeSubject = $commit.Subject.Replace("|", "\|")
    [void]$markdown.AppendLine(('| {0} | `{1}` | {2} | {3} |' -f $commit.Date, $commit.ShortHash, $commit.AuthorName, $safeSubject))
  }

  [void]$markdown.AppendLine("")
  [void]$markdown.AppendLine("## 逐提交代码变更")

  foreach ($commit in $commits) {
    [void]$markdown.AppendLine("")
    [void]$markdown.AppendLine(('### `{0}` {1}' -f $commit.ShortHash, $commit.Subject))
    [void]$markdown.AppendLine("")
    [void]$markdown.AppendLine("- 作者:$($commit.AuthorName) <$($commit.AuthorEmail)>")
    [void]$markdown.AppendLine("- 时间:$($commit.Date)")
    [void]$markdown.AppendLine(('- 完整提交:`{0}`' -f $commit.Hash))
    [void]$markdown.AppendLine("")

    $stat = (& git show --stat --find-renames --format= $commit.Hash)
    [void]$markdown.AppendLine("#### 文件统计")
    [void]$markdown.AppendLine("")
    [void]$markdown.AppendLine('```txt')
    [void]$markdown.AppendLine(($stat -join "`n"))
    [void]$markdown.AppendLine('```')
    [void]$markdown.AppendLine("")

    $nameStatus = (& git show --name-status --find-renames --format= $commit.Hash)
    [void]$markdown.AppendLine("#### 文件状态")
    [void]$markdown.AppendLine("")
    [void]$markdown.AppendLine('```txt')
    [void]$markdown.AppendLine(($nameStatus -join "`n"))
    [void]$markdown.AppendLine('```')
    [void]$markdown.AppendLine("")

    $diff = (& git show --find-renames --format= --unified=80 --no-ext-diff $commit.Hash) -join "`n"
    $diff = Limit-Text -Text $diff -Limit $MaxDiffChars

    [void]$markdown.AppendLine("#### Diff")
    [void]$markdown.AppendLine("")
    [void]$markdown.AppendLine('```diff')
    [void]$markdown.AppendLine($diff)
    [void]$markdown.AppendLine('```')
  }
}

$content = $markdown.ToString()

if ([string]::IsNullOrWhiteSpace($Output)) {
  $content
} else {
  $resolvedOutput = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Output)
  [System.IO.File]::WriteAllText($resolvedOutput, $content, [System.Text.UTF8Encoding]::new($false))
  Write-Output "已生成:$resolvedOutput"
}

参考文档

MARKDOWN
左右滑动查看完整代码
# 报告格式参考

生成日报或周报时,优先使用下面结构。

## 日报格式

```md
# 开发日报

## 今日完成

- 

## 问题修复

- 

## 代码质量 / 工程化

- 

## 测试与验证

- 

## 风险与后续

- 

## 提交范围

- 时间范围:
- 提交数量:
- 主要提交:
```

## 周内日报格式

如果用户说“本周日报”或“本周到今天”,按周一到今天归纳:

```md
# 本周开发进展

## 时间范围

- 

## 主要完成

- 

## 重点代码变更

- 

## 问题修复

- 

## 测试与验证

- 

## 风险与后续

- 

## 提交依据

- 
```

## 写作规则

- 用中文。
- 先总结业务或技术结果,再补充关键代码依据。
- 不要逐条复制 commit message。
- 不要贴大段代码。
- 没有测试信息时写“提交记录中未体现明确测试变更”。
- diff 被截断时写“以下总结基于截断前可见 diff”。

之后就能在codex @这个技能了

什么是mcp#

Model Context Protocol 是由 Anthropic 开源的一种标准化协议

我自己的理解是 mcp 其实就是把之前封装到agent里面的tool,给解耦出来了,然后起一个服务给AI,让AI知道我这个服务里有哪些工具,工具分别都是干嘛的,需要什么参数,返回什么结果

其实就是为了服务化,我这个工具只需要暴漏我自己的能力,不关心调用我的是Claude 还是 gpt

打个比方,在没有mcp之前,tool就像是焊死在机器人上的机械臂,当我想把这个手臂给其他机器人用是不行的,而有了mcp之后,tool就变成了一个有通用标准的模块化外设,只要接入它,就能指挥它

那这个mcp到底长啥样?怎么查看呢?除了查看源码的方式,还有一种方式,就是使用 mcp inspector

<font style="color:rgb(2, 8, 23);">MCP Inspector</font>#

这个东西也是 Anthropic 出的

控制台输入命令

MARKDOWN
左右滑动查看完整代码
 npx @modelcontextprotocol/inspector

会打开一个本地服务,可用于调试mcp

用谷歌的chrome-devtools-mcp 演示一下

ok,连接上了,这可以看到一大堆工具

像什么点击页面元素,拖动页面元素,关闭页面,还有打开一个新页面,都是chrome-devtools-mcp暴漏出来的能力

做个示例#

选择<font style="color:rgb(2, 8, 23);">navigate_page这个工具</font>

PLAIN
左右滑动查看完整代码
Go to a URL, or back, forward, or reload. Use project URL if not specified otherwise.
// 这是它工具的描述 AI 根据这个描述判断它能干些啥

<font style="color:rgb(75, 85, 99);"></font>

<font style="color:rgb(2, 8, 23);"></font>

注意,地址需要加上协议,不能只填域名

点击run 就会重新开一个浏览器窗口,并打开百度

chrome-devtools-mcp 源码#

可以看到工具的名称,描述,还有调用的参数

自己写一个mcp服务实现系统的自动登录#

为了了解这玩意儿到底是个啥,还是自己写一个吧

安装这三个包

JSON
左右滑动查看完整代码
{
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.29.0", // 创建mcp服务的sdk
    "puppeteer": "^25.1.0", // 控制浏览器的
    "zod": "^4.4.3"// 字段校验
  },
  "scripts": {
    "start": "npx tsx index.ts"
  }
}

这是最新的api

我这里只是做个演示,其实正常的像这种mcp 直接对接后端比较好,比如说查报表,做单子,批量增加资料什么的,我定义对应的工具,在这里统一字段,然后调用后端api去实现,当然我只是说说大致流程,实际肯定更复杂

TYPESCRIPT
左右滑动查看完整代码
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod"; 
import puppeteer, { Browser } from "puppeteer";

let browser: Browser;

// 2. 初始化 MCP 服务
const server = new McpServer({
  name: "erp-system-mcp",
  version: "1.0.0",
});
async function getActivePage(browser: Browser) {
  let pages = await browser.pages();
  const activePage = pages.find((p) => p.url() !== "about:blank") || pages[0];
  return activePage || (await browser.newPage());
}
// 3. 注册工具
server.registerTool(
  "auto_login",
  {
    description: "自动登录云飞扬系统",
    inputSchema: {
      accountSet: z.string().describe("账套"),
      username: z.string().describe("用户名"),
      password: z.string().describe("密码"),
    },
  },
  async ({ accountSet, username, password }) => {
    const page = await getActivePage(browser); // 总是拿到正确的 page

    try {
      await page.goto("http://localhost:8081/#/login", {
        waitUntil: "networkidle0",
      });
      await page.setViewport({ width: 1920, height: 1080 });

      await page.evaluate(() => {
        window.dispatchEvent(new Event("resize"));
      });

      await new Promise((r) => setTimeout(r, 500));
      const inputs = await page.$$('input:not([type="hidden"])');

      console.error(`DEBUG: Found ${inputs.length} visible inputs`);

      for (let i = 0; i < 3; i++) {
        await inputs[i].click({ count: 3 });
        await inputs[i].press("Backspace");
      }
      await inputs[0].type(accountSet, { delay: 100 });
      await inputs[1].type(username, { delay: 100 });
      await inputs[2].type(password, { delay: 100 });

      const loginButtonSelector = 'button[type="submit"]';
      await page.waitForSelector(loginButtonSelector, { visible: true });
      await page.click(loginButtonSelector);

      try {
        await page.waitForSelector("::-p-text(工作台)", { timeout: 8000 });
        return { content: [{ type: "text", text: `登录成功,已进入工作台` }] };
      } catch {
        // 如果没跳到工作台,检查页面上有没有错误提示
        return {
          isError: true,
          content: [
            { type: "text", text: `登录请求已发出,但未检测到首页跳转` },
          ],
        };
      }
    } catch (error) {
      return {
        isError: true,
        content: [{ type: "text", text: `操作失败: ${String(error)}` }],
      };
    }
  },
);

async function main() {

  browser = await puppeteer.launch({
    headless: false, 
  });

  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error(" ERP MCP 服务及内置浏览器已成功启动!");
}

main().catch(console.error);

改一下配置,然后连接mcp工具,看到自定义的工具了

这就走通了

codex 安装使用 mcp#

安装一下

PLAIN
左右滑动查看完整代码
codex mcp add chrome-devtools -- npx -y chrome-devtools-mcp@latest

安装完成之后,codex 配置文件会多出一条mcp的配置

再次启动codex的时候,也会同时加载启动这个mcp 服务

查看一下它的工具

让它操作一下浏览器