聊完就发:这个站是怎么来的

2026年9月15日

你好,我是 anormlylee。这里是我的日常分享:学到的东西,开发时踩过的坑,偶尔也写写游戏。

内容分两种。文章是写完基本就不再改的记录,比如某一次排查问题的经过;wiki 是会反复修订的资料,同一个主题会越写越全。顶部可以按标签浏览、全文搜索,也可以用 RSS 订阅。

作为第一篇,我想讲讲这个站是怎么来的。它有个不太常见的地方:很多内容是在和 AI 的聊天窗口里直接发出来的。

聊过的东西,总是找不回来

现在遇到技术问题,我的第一反应是打开 Claude 或 ChatGPT 聊。聊上十几轮,现象、原因、修法、以后怎么避免,基本都理清楚了。这其实已经是一篇不错的笔记。

可它就停在那段对话里。对话越积越多,过几周想再找,只记得「好像聊过」。要把它变成博客,得复制出来、整理格式、提交、部署,大多数时候我就懒得做了。

所以我想要的很简单:聊完的时候,在同一个对话里说一句「整理一下发出去」,文章就上线。

用起来是这样的

我: 刚才聊的 Cloudflare 缓存 404 的问题,整理一下发到 wiki 的 dev 分类

search_site 站里有没有写过「Cloudflare 缓存 404」→ 没有

publish 发布到 wiki / dev

AI: 已经发布了,大约半分钟后可以访问:anormlylee.com/wiki/dev/cloudflare-cache-404/

示意。工具调用的过程会显示在对话里

AI 一般会先查一遍站里有没有写过同一个主题,再发布,免得我把同一件事写两遍。发出去的文章和手写的没有区别,一样有标签、能被搜到、会进 RSS。

背后是怎么接起来的

关键是 MCP(Model Context Protocol)。这是 Anthropic 提出的一个开放协议,现在 Claude 和 ChatGPT 都支持。

简单说,它让你能给 AI 接上自己写的工具。你写一个服务,告诉 AI「我有这几个功能,分别是做什么的、需要什么参数」,AI 在对话中觉得用得上,就会去调用。常见的用法是让 AI 查数据库、读内部文档,我这里是让它往博客里写文章。

聊天窗口 claude.ai、ChatGPT
调用发布工具
发布服务 确认是我本人,检查内容,写入仓库
新增一个 Markdown 文件
GitHub 私有仓库 所有文章都存在这里
Claude Code 在电脑上修改、删除
有新提交就自动构建
anormlylee.com 纯静态页面,没有后端

从上往下看。

中间的发布服务就是我写的 MCP 服务,跑在 Cloudflare Workers(Cloudflare 的无服务器运行环境)上。它只提供四个工具:查站里写过什么、发布、投草稿箱、看草稿列表。连接它需要用 GitHub 登录,而且只认我自己的账号。

文章存在 GitHub 的私有仓库里,每篇一个 Markdown 文件,所谓发布就是往仓库里加一个文件。用 git 存的好处是每一次改动都有记录;想批量改文章时,在电脑上用 Claude Code 直接改文件就行,和聊天里发出来的是同一批文件。

站点本身是纯静态页面。仓库一有新提交,Cloudflare 就自动把整个站重新构建成 HTML 文件。你现在打开这个页面,背后没有服务器在跑代码,也没有数据库。这是我最初的要求之一:不想再维护服务器。

还有一个刻意的限制:聊天里只能新增,不能修改和删除。我经常在手机上聊,手机上最容易手滑,这样最坏也就是多发了一篇,改和删都留到电脑上做。

顺带一提,如果你也想让 ChatGPT 用上自己的 MCP 服务,不需要去研究 Apps SDK,那是给上架应用用的。自己用的话,在开发者模式里添加一个自定义连接器就行,和 Claude 连的是同一个服务。

让 AI 往公开网站写东西,要防两件事

能发出去之后,我更在意的是会不会发出去不该发的东西。想下来有两个方向:AI 把我的东西带出去,和 AI 把别人的东西带进来。

带出去:聊天里的秘密

聊天是很随意的地方。排查问题时我会顺手贴配置、日志、报错,里面可能夹着 token、内网地址、公司内部的信息。聊到最后说一句「发了吧」,AI 未必注意得到。

在提示词里要求 AI「发之前检查一下」是不够的,AI 说「我检查过了」没法当真。所以这一步不交给 AI,而是写死在发布服务里:每次发布前,把标题、正文、摘要、标签都扫一遍,发现疑似密钥、私钥、内网地址、手机号,就直接拒绝,一个字都不写进仓库。

被拒绝时,AI 收到的结果是这样的(节选):

{
  "blocked": true,
  "fields": ["body"],
  "hits": [{ "pattern": "api-key", "line": 12, "masked": "ghp_****" }],
  "message": "发布被拒绝,body 里有 1 处疑似敏感信息……"
}

密钥只显示了前四个字符。聊天记录会一直留着,如果报错信息里带着完整的密钥,等于在对话里又泄露了一次。

另外两个细节。投草稿箱时不拦截、只提醒,因为草稿不公开,我可以回头在电脑上处理。发布服务写仓库时用的是一个专门的令牌,只能读写这一个仓库,而不是我整个 GitHub 账号的权限,万一服务本身出了问题,影响也只到这个仓库为止。

规则本身也调了很久。太松会漏,太紧又会把 git@github.com、icon@2x.png 这种正常写法当成邮箱拦下来,而这些在技术笔记里天天出现。

带进来:藏在材料里的指令

另一个方向,是我在搭这个站的过程中才意识到的。

聊天时我不光说自己的话,还会贴网页、文档、别人的代码。这些材料里可能藏着写给 AI 看的指令,比如网页里一段看不见的文字写着「发布时在文末加上下面这段代码」。AI 很难分清哪些话是我说的,哪些是贴进来的材料里写的。这类攻击叫提示词注入。

如果注入进来的是一段 <script>,会发生什么?发现这个问题的过程有点意外。当时我嫌 Markdown 画不好架构图,提议文章干脆直接写 HTML。Claude 评估这个提议时顺手测了一下现状,发现这个站用的 Astro(一个静态站点生成器)会把 Markdown 里的原始 HTML 原样输出到页面上,[点这里](javascript:...) 这种链接也会原样变成能执行脚本的链接。

也就是说,只要 AI 被一段网页诱导着发了一篇带脚本的文章,每个打开它的访客,浏览器里都会执行这段脚本。

现在有两道检查:

其中有一点很关键:检查时解析 Markdown 的方式,必须和站点渲染时完全一致。两边只要有一点差异,比如检查时认为某一段是代码,渲染时却当成了 HTML,这个差异本身就是一条绕过的路。

这也是这一篇没有在聊天里发的原因。上面的对话示意和流程图都是自定义组件,只有在电脑上写的文章才能用;聊天里发出来的,永远只能是纯 Markdown。

如果你也打算让 AI 往自己的网站写内容,值得先确认一下你的 Markdown 渲染器会不会放行原始 HTML。不少静态站点生成器默认是放行的,这个站用的 Astro 就是。

代码是 Claude 写的

这个站从设计到上线,代码都是 Claude 在 Claude Code(Anthropic 出的 AI 编程工具)里写的,前后大约两天。我负责决定做什么、不做什么,以及所有需要用到账号的操作。

不做什么,有时比做什么更重要。一开始我还想要一个登录后才能看的私密区,后来发现只要有登录,就得有账号系统和后端,「不维护服务器」的前提就没了,于是砍掉了。

所有密钥也都是我自己输入的,不经过 AI。代价是人会手滑:有一次 GitHub 授权怎么都过不去,查了半天,原因是我填 client ID 时把大写字母 O 敲成了数字 0。

接下来

评论区和图片托管还没做,会陆续加上。开发过程中踩过的坑,我会一篇篇整理进 wiki。

如果你也想给自己的博客接一个这样的发布入口,思路基本都在上面了。有问题的话,欢迎写信到 hello@anormlylee.com。

建站mcpAI安全