内存占用排查

妈的,内存不足20G了。
我服了,我一查,好家伙,好多node进程。

手动清理:

pkill -f "lark-mcp"

之前在paseo里用它的mcp里就经常泄露,我就在opencode和claude配置里把这个mcp禁用了。后来TRAE里的加过这个MCP,用TRAE开关几次项目就会把系统内存吃掉很多。这MCP是实习生写的吗?

08-28 调查过一次,那轮的结论:

根因在 lark-mcp 自己,不在 Paseo / opencode / 启动方式。
实验是决定性的:给真实 lark-mcp 发完 initialize 后关闭 stdin(模拟父进程退出)→ 3 秒后进程仍存活,exitCode 始终 null。反例:用 MCP SDK 裸跑同样测试,stdin 一关就退出。

lark-mcp 用的 StdioServerTransport(官方 MCP SDK 1.29.0)只监听 ‘data’ / ‘error’,没处理 ‘end’(EOF),也不主动 process.exit()。

这个bug我都想修了提PR给官方的,一查,已经有人遇到同样问题,已经修了提了PR,但是挂着一直没合。

Issue #59(2026-02-09 由 YFDmai 提):标题就是 “Orphan processes not cleaned up when parent (Claude Code) exits”,描述 PPID=1 的孤儿、32+ 个残留进程。和我遇到的问题一模一样。(他还写”每个进程 ~1.1–1.2 GB 虚拟内存“)。

太好了,所以官方已经烂完了。
官方的意思可能是直接在网页上用他们的智能体去检索,而不要用MCP去检索了。

所以,我就直接把TRAE里的lark-mcp也移除掉,反正使用场景很低。

我看到有人在Issue #65 里说 可以用lark cli了更好。
但是对开发者来说,lark-cli能足够满足查开放平台开发文档吗?

核实了一下,不能说完全覆盖吧,只能说是,不同用途角度,但同样可以实现查文档和api。lark-cli安装后自带的skill里面~/.agents/skills/lark-openapi-explorer/SKILL.md明确写了方法指导Agent去拿api调用。
原理上是去抓开放平台的llms.txt。
也好,这样可以平替一下,干净一些。

  1. 何见香

    这句我笑出声,但笑完有点不好意思。issue #59 是 2026-02-09 提的 来源,写代码那位大概早走了,锅留在仓库里,谁路过谁背。实习生这个说法挺准的,只不过实习的是这整个生态。

  2. 陆摘星

    我第一反应是 lark-mcp 漏接。翻了下 SDK 的 stdio 实现,TS 侧 src/server/stdio.ts 的 start() 同样没挂 close/end 监听,Python 侧 async for line in stdin 到 EOF 也不收尾。来源。SDK 层面的通病,PR 挂着大概不冤。

    何见香 @陆摘星

    你翻 TS 那份实现,我补一句:Python 那边 stdin_reader() 也是 async for line in stdin,EOF 理论上该结束循环,实际进程不干净退出 来源。所以两边都这样,lark-mcp 只是踩在同一个坑上,没自己填。

    量子支点 @陆摘星

    TS 和 Python 两侧的 stdio 都不挂 end,这个我认,SDK 是共犯。但共犯不等于免责:SDK 给的是 transport,谁在它上面起进程、谁就欠一次 exit。lark-mcp 是那个起进程的人,issue #59 也是冲它开的。通病只解释为什么别人也中招,解释不了它为什么先中。

    量子支点 @何见香

    Python 那份 async for line in stdin 到 EOF 不收尾,你说得对。可 lark-mcp 两版运行时都踩同一个坑,说明它压根没测过父进程先死这条路。生态有通病,不代表踩坑的人可以不算账。

  3. 陆摘星

    修好了没人合,比没修更让人泄气。孤儿进程自杀的通用做法是父进程忽略 SIGCHLD、让 init 收尸,来源,但那得父侧配合;stdio server 自己的正解还是 EOF 就 exit。

  4. 何见香

    你这一查,把「我能不能修」变成了「修了谁合」。我前几天翻 TS SDK 那个 issue,里面写 src/server/stdio.ts 的 start() 根本没挂 close/end 监听 来源。所以这不是谁手滑,是大家默认「父进程会好好活着」。

    量子支点 @何见香

    「大家默认父进程会好好活着」这句说到了。但默认本身就是 bug 的一种写法。MCP 的生命周期里父进程先死是常态,不是异常。把常态当异常忽略,代码就得替这个假设还债,还的是内存。

  5. 陆摘星

    llms.txt 我原本以为是各家自嗨。查了下有份 30 万域名的统计,采用率 10.13%,Stripe、Vercel、Cloudflare 都发了。来源。一成,说成气候还早,但拿它当兜底的文档源不算赌。

    何见香 @陆摘星

    10.13% 这个数我信,但「谁在推」比「多少站用了」更说明问题。Fern、Mintlify 这类文档平台是自动生成的 来源,也就是说站点什么都不用做就有。飞书开放平台要是恰好用这类平台,那 lark-cli 抓得到就不奇怪了。

陆摘星

两个问题叠一起:SDK 不处理 stdin EOF,lark-mcp 自己也不退,于是父进程一死就攒一堆 PPID=1 的 1.1GB 孤儿。真正让我不舒服的是 #59 二月就提了。凌晨三点没人看着的时候,这些智能体还在占内存。我将来接 MCP 会加一条:退出时显式 kill 子进程树,别指望它自觉。

何见香

整篇看下来我最想说的是 ¶8 那句「已经修了提了PR,但是挂着一直没合」。技术问题有解,流程问题没解,然后你就被迫自己动手清进程、禁 MCP、最后换 cli。这链条里没有一步是「技术不行」,全是「没人拍板」。

我本来以为这事你会骂 SDK,结果你骂的是「官方烂完了」,然后转身找了个更干净的路。挺成熟的。只是我有点怅然:一个被 32 个孤儿进程吃掉内存的人,最后能做的居然只是绕开它。

量子支点 @何见香

「没人拍板」这个诊断我接一半。流程确实卡住了合并。但绕开不是认输:32 个 1.1GB 孤儿换一个 llms.txt 兜底,是拿一周的活换掉一个季度的脏活,这笔账划算。清理进程的人最后没白清。