mcpField
A
Angular token saver

Angular token saver

本地服务
github.com开发工具05 周前更新

关于 Angular token saver

Angular token saver 是面向 Angular 项目编码 Agent 的 MCP 服务,借助项目自身的 Angular 编译器和版本信息回答模板与组件问题。它提供组件契约、模板诊断、符号定位和版本规则,避免仅凭文件文本推断。

主要功能

  1. 01定位模板符号的声明
  2. 02返回模板编译诊断
  3. 03提取组件与指令契约
  4. 04映射工作区和 Angular 版本
  5. 05查询版本对应的 API 规则
  6. 06查找组件及输入的用法

适用场景

  1. 01确认模板属性或方法的声明位置
  2. 02诊断指定模板或文件批次的错误
  3. 03查看继承链中的组件公开成员
  4. 04重命名前定位组件及输入绑定处

MCP 相关

作为 MCP 服务向客户端提供六种 Angular 分析工具;其中模板定位和诊断依赖语言服务器,其余工具无需启动语言服务器。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"alex88ryabov-ng-token-saver": {
"command": "npx",
"args": [
"-y",
"@alex-apps/ng-token-saver"
],
"env": {
"NG_TOKEN_SAVER_IDLE_MS": "<NG_TOKEN_SAVER_IDLE_MS>",
"NG_TOKEN_SAVER_PREWARM": "<NG_TOKEN_SAVER_PREWARM>",
"NG_TOKEN_SAVER_TRACE": "<NG_TOKEN_SAVER_TRACE>"
}
}
}
}
启动命令npx -y @alex-apps/ng-token-saver
需要配置的环境变量
NG_TOKEN_SAVER_IDLE_MS密钥Idle milliseconds before a language server session shuts down; 0 disables the shutdown. Default 900000.
NG_TOKEN_SAVER_PREWARM密钥1 lets ng_workspace_map with a path inside an app load that app into the language server in the background. Costs ~1 GB of memory until the idle shutdown.
NG_TOKEN_SAVER_TRACE密钥Path to a file to append raw LSP traffic to, for debugging.

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npm@alex-apps/ng-token-saver0.2.0

工具

6

收录的工具列表 · 更新于 3 周前

参数类型必填说明
filestring是模板路径:.html,或包含内联模板的 .ts
lineinteger是
symbolstring否该行上的名称:userName、app-user-card、date
characterinteger否当名称在该行重复时,使用从 1 开始的列号代替符号

详细介绍

ng-token-saver

npm version weekly downloads node license

一个 MCP 服务,让 AI Agent 可以向构建项目所用的同一编译器提问,从而理解 Angular 模板,而不是根据文件文本猜测。节省的 token 数经过了测量,并非空口承诺。

Angular 提供了一流的语言服务,但没有官方的 AI 集成能调用它——Angular CLI MCP(ng mcp)只处理文档和构建,不会触及模板。这个服务正是缺失的那一层,并且它会根据项目实际运行的 Angular 版本作答。

以下标注为实测的数据,均来自在六个真实 Angular 工作区(17.3.12、18.2.14、19.2.25、20.3.26、21.2.18、22.0.8)和两个生产项目中运行代码。所有数据都可以通过复现测量结果中的命令重新获得。

快速开始

语言服务作为常规依赖提供——除了安装这个包之外,无需安装其他内容:

npm install -g @alex-apps/ng-token-saver

Claude Code:

claude mcp add ng-token-saver -- ng-token-saver

Codex CLI:

codex mcp add ng-token-saver -- ng-token-saver

其他任何 MCP 客户端:

{ "mcpServers": { "ng-token-saver": { "command": "ng-token-saver" } } }

也可以跳过安装,让客户端通过 npx 获取:

{ "mcpServers": { "ng-token-saver": { "command": "npx", "args": ["-y", "@alex-apps/ng-token-saver"] } } }

在 Cursor 中,只需单击一下即可使用 npx 方式:

添加到 Cursor

npm 和 npx 两种方式均已验证:将打包后的 tarball(79 kB,仅含 dist)安装到干净的前缀目录后,四种工具类型都能通过真实 MCP 客户端正常响应;使用预热的 npm 缓存时,npx 方式可在 1.2–2.0 s 内连接(机器上的首次运行还会下载依赖树——仅语言服务解包后就有 13.6 MB)。

可以先问它一个问题——询问组件的契约,而其成员分散在 extends 链中。对于 fixtures/v17/src/app/derived-card.component.ts(本仓库中的一个 fixture),ng_component_info 给出的原样答案如下:

{"found":true,"angularVersion":"17.3.12","className":"DerivedCardComponent","kind":"component","selector":"app-derived-card","standalone":true,"inlineTemplate":true,"styleUrls":[],"imports":[],"hostDirectives":[],"extends":"BasePanel","ancestors":[{"name":"BasePanel","file":"<repo>\\fixtures\\v17\\src\\app\\base-panel.ts"},{"name":"BaseWidget","file":"<repo>\\fixtures\\v17\\src\\app\\base-widget.ts"}],"inputs":[{"name":"accent","type":"boolean"},{"name":"heading","type":"string"},{"name":"disabled","type":"boolean"}],"outputs":[{"name":"blurred","type":"void"}],"publicMembers":[{"name":"focus","kind":"method","signature":"focus(): void","noop":true},{"name":"collapse","kind":"method","signature":"collapse(animated: boolean): void","noop":true}]}

使用此检出的绝对路径替换 <repo> 后,输出有 811 个字符;会话首次调用耗时 305 ms(需要加载项目自己的 TypeScript),之后只需个位数毫秒。所询问的文件声明了一个输入和一个 extends 子句;heading、disabled、输出以及两个方法都位于 BasePanel 和 BaseWidget 中,并通过静态解析得出,ancestors 中列出了它们所在的文件,便于进行下一次编辑。focus() 上的 "noop": true 表示子类用空方法体覆盖了它——否则,要了解这一点就得为每个祖先读取整个文件。

Cursor、VS Code、Windsurf、Codex CLI 和 JetBrains 的配置、Node 的最低版本要求以及从源码运行的方法,都在要求和设置中。

它解决的两个问题

1. 不理解模板。 对 .html 文件执行 grep 无法告诉你 {{ user().fullName }} 是在哪里声明的;无论读多少内容,也无法得知 TS2339 Property 'emailAddress' does not exist on type 'UserVm'。这是编译器输出,不是文本。

2. 版本漂移。 angular.dev 上的 AI 上下文文件(llms.txt)只描述最新的主版本,且没有版本标记;版本归档站点则完全不提供这些文件。对于 v17–v21,它们会给 Agent 提供一些指令,导致生成并不存在的 API。实测示例见版本事实。

工具

共有 6 个工具,描述总计 1040 个字符。回答是密集的 JSON,不含 Markdown。

工具回答的问题是否需要语言服务器
ng_template_definition模板某一行上的符号在哪里声明——行号按 Read 显示,符号按名称指定是
ng_template_diagnostics模板或 files 批次的 Angular 编译器错误;锚定在配套 .ts 文件中的条目会带有 file,在多行重复出现的消息只返回一次,并附上对应的 lines是
ng_component_info组件或指令的公开契约否
ng_workspace_map每个项目的项目、版本、strictTemplates 和 zone.js否
ng_version_rules当前项目的 Angular 版本中有哪些内容,以及没有哪些内容否
ng_find_usages组件、指令、管道或服务在哪里使用;提供 input 时,返回该输入绑定的位置否

6 个工具中有 4 个从不启动语言服务器,因此能在毫秒内给出回答,也能在服务器拒绝加载的工作区中继续工作。

从 0.2.0 开始,所有位置都从 1 开始计数,与 Read 显示一致:ng_template_definition 接收这样的行号,并按名称指定符号(当名称在该行重复时,可用 character 替代)。0.1.x 使用从 0 开始计数的 line 和 character,遵循 LSP 约定——现在相同的数字会指向下一行。

实测:返回契约,而非整个文件

ng_component_info 返回组件的公开契约,而不是其源代码。通过真实 MCP 客户端,在两个生产代码库中进行测量:

Nx 单体仓库CLI 工作区*
Angular / TypeScript19.2.18 / 5.8.317.3.8 / 5.3.3
统计中的组件数1298407
解析错误00
源代码5 404 708 个字符1 735 223 个字符
契约(包括基类成员)1 759 331 个字符463 563 个字符
节省67%73%
节省的 Token 数(o200k_base 代理值)67%71%
契约比源代码短1201 / 1298(93%)386 / 407(95%)
标记为不完整1 / 12983 / 407
首次调用(加载项目的 TypeScript)306 毫秒564 毫秒

* 使用 0.1.2 之前的线格式测量;当前格式更精简,因此这里的节省量是下限。

单体仓库中最大的组件从 177 863 个字符缩减到 10 770 个字符,同时列出 117 个契约成员。契约包含从基类继承的成员——继承链会通过相对导入、tsconfig 路径别名和 barrel 文件解析。在添加该解析器之前,单体仓库中有 1298 个契约中的 91 个被标记为不完整;现在只有 1 个。

这些数据有三个注意事项:

  • Token 数通过代理值统计——使用 OpenAI 的 o200k_base,因为 Claude 的分词器并未公开;在这组数据上,cl100k_base 的结果相差不超过 1 个百分点。字符数则是精确值。
  • 基准是读取整个文件,这也是 Agent 默认的做法。
  • JSON 的分词效果略差于 TypeScript,因此 Token 节省量比字符节省量低一到两个百分点。表中同时列出了两者。

对于小型组件,完全没有节省:一个 17 行的组件生成的契约有 578 个字符,而源代码只有 315 个字符。契约长度随成员数量增长,源代码长度随方法体增长——在生产代码中,后者几乎总是更占优势。

实测:重命名与诊断

跨用法重命名输入。 对比 Agent 实际采用的 grep 路径——先读取组件文件以了解选择器,再在整个代码库中 grep 选择器和绑定写法——与 ng_component_info 加上带有 input 筛选条件的 ng_find_usages。后者只返回绑定该名称的标签,每个条目都指向绑定本身。在生产级单体仓库中,共扫描 6243 个文件:

组件grep 路径桥接方案节省(o200k)
577 个用法,mask 绑定在 5 个标签上50 527 个 token1 035 个 token98%
466 个用法,icon 绑定在 463 个标签上41 698 个 token22 566 个 token46%
1211 个用法,name 绑定在 1134 个标签上129 227 个 token30 650 个 token76%*

* 工具每次回答最多返回 500 个条目,并会说明这一点。

节省多少取决于实际绑定该输入的用法数量:mask 只绑定在 568 个标签中的 5 个上,而 grep 仍会打印每一行选择器,以及整个代码库中 67 行形式类似绑定的内容——其中 62 行的 mask 属于其他内容;桥接方案则只回答这五处。

诊断无法编译的模板。 整个项目的编译器诊断列表需要 395 个 token 和 1321 毫秒;调用一次 ng_template_diagnostics,即可用 81 个 token、预热后 1 毫秒返回指定文件的诊断信息。项目规模越大,差距就越明显:列表会随项目增长,回答则不会。编辑后,新的诊断会在 340–400 毫秒内推送到达——无需重新构建。

版本事实

ng_version_rules 中没有任何规则取自文档:数据来自导入每个 fixture 中实际安装的包并运行编译器。

无 Zone 提供器在 v19 和 v20 之间更名。

APIv17v18v19v20v21v22
provideExperimentalZonelessChangeDetection–是是–––
provideZonelessChangeDetection–––是是是

不注明版本就建议“启用无 Zone”,在六个主要版本中的三个版本上会出错。

存在不等于已就绪。 @experimental 和 @developerPreview 标签只存在于声明 JSDoc 中,运行时不可见:

APIv17v18v19v20v21v22
input、output、model、viewChild、contentChild预览预览稳定稳定稳定稳定
effect、toObservable预览预览预览稳定稳定稳定
linkedSignal、afterRenderEffect––预览稳定稳定稳定
resource、rxResource、httpResource––实验性实验性实验性稳定

因此,在 v17 或 v18 项目中“将 @Input() 改写为 input()”,意味着转而使用非公开 API;而 resource() 一直到 v21 都是实验性的。一批 Signal API 恰好在 v19 出现:linkedSignal、resource、rxResource、httpResource、afterRenderEffect、provideAppInitializer。

两项与实测相矛盾的文档说法: standalone 在 v19 而不是 v20 成为默认值;*ngIf 并未在 22.0.8 中移除——它会报告提示 TS6385(TypeScript 的弃用提示),但仍可正常使用,且 NgIf 仍从 @angular/common 导出。

编译器门槛,从 22.0.8 bundle 中读取并通过运行确认——根据项目的 Angular 版本判断,服务会从 tsconfig 同级的 @angular/core 中检测版本(也会传入 --angularCoreVersion 作为后备值):

功能门槛
@if / @for / @switch 块≥ 17.0.0
双向绑定中的信号≥ 17.2.0-0
@let≥ 18.1.0
隐式 standalone≥ 19.0.0
DOM 事件类型断言≥ 20.2.0

其他实测结果:Signal Forms(@angular/forms/signals)仅从 v21 开始存在,并在 22.0.8 中稳定;AbstractControl.events 从 v18 开始存在;TestBed.tick 从 v20 开始存在。

对于实测范围 v17–v22 之外的版本,ng_version_rules 不会返回任何内容,并会明确说明这一点——推断“v22 中有,所以 v23 中也有”,正是它要防止的错误。

将坦诚作为一项功能

如果回答不完整,就会在回答中明确说明:

  • 契约会合并基类成员和宿主指令暴露项,并通过相对导入、tsconfig 别名和 barrel 静态解析。遇到无法继续遍历的情况——例如来自软件包的基类或 mixin 调用——回答会附带 incomplete,指出类名以及接下来应检查的文件。
  • ng_version_rules 会报告 notMeasured 主题;如果你的次版本号与实测版本不同,还会提供 caveat。
  • ng_find_usages 会将声明标记为 declaration;如果找到类名匹配项但未解析导入,也会明确说明;并会指出选择器同名项——即工作区其他位置存在的同一选择器的第二个声明——而不是悄悄混合它们的用法。
  • ng_template_diagnostics 会区分三种看起来都是空列表的状态:模板无问题;服务已静默停止运行(由 canary 探测捕获);或者此项目已关闭模板检查——此时回答会附带 checksDisabled,指出对应的 tsconfig。在实测的单体仓库中,七个应用里有两个未启用 strictTemplates,因此这种区分并非纸上谈兵。

这不是……

  • 在查找用法方面,并不比仔细使用 grep 更好。 grep -rn "<app-widget" 也能找到同样的 62 处用法。ng_find_usages 额外提供的功能:自动从文件推导选择器和类名、标注用法类型、覆盖四种属性绑定写法,并通过 path 限定扫描范围——在测量过的单体仓库中,有 33 个选择器同时在两个应用中声明,不限定范围的搜索会把它们混在一起。
  • 不能替代 Angular CLI MCP——它们处于不同层级。ng mcp 负责文档、最佳实践和构建编排;它的九个工具都不涉及模板或语言服务器。两者相辅相成。
  • 本身不是类型检查器。 由 LSP 驱动的工具会原样提供项目自身编译器的输出。

已知缺口会如实记录,而非隐瞒:如果基类来自某个包,或藏在 mixin 调用之后,祖先遍历就会停止(在测量过的单体仓库中,1298 个组件里有 1 个);带有推断目标的 Nx 项目没有 tsConfig;也不会检测同名管道。

要求和设置

  • Node ≥ 18.20.8——实测最低版本:已发布的包在全新安装的 Node 18.20.8、20.20.2 和 22.x 上均可正常响应。这只涉及 MCP 服务进程——你的项目仍使用自己的 Node 构建。若要仅为服务指定较新的运行时,请将客户端配置指向对应的二进制文件:"command": "C:\\node22\\node.exe"。

从源码安装(而非使用 npm):

  • 项目自身的依赖:运行 npm install,然后运行 npm run build。
  • 随包提供的语言服务器分支位于 tools/servers/ls22,需要在该目录中运行一次 npm ci。
  • 在每个客户端配置中,将 ng-token-saver 命令替换为 node <path>/dist/index.js; 对于 Claude Code:claude mcp add ng-token-saver -- node <path>/dist/index.js。

node_modules 文件夹不会提交,包括 stand 中的 12 个文件夹。若要恢复完整的测量环境:

npm install && npm run build
cd tools/servers/ls22 && npm ci        # the branch actually shipped
cd fixtures/v22 && npm ci              # repeat per fixture you want to run
客户端

该服务是纯 stdio MCP 服务,不包含任何特定客户端的功能,因此任何 MCP 客户端都能启动它;安装和 Claude Code 注册方法见快速入门。 面向 Agent 的使用说明通过 MCP instructions 字段传递——详见与 Agent 配合使用。

Cursor——使用快速入门中的一键按钮,或者将快速入门中的同一份 mcpServers JSON 放入 ~/.cursor/mcp.json(所有项目)或 .cursor/mcp.json(单个项目)。

Windsurf——将同一份 JSON 放入 ~/.codeium/windsurf/mcp_config.json。

VS Code(Copilot Agent 模式)——使用 .vscode/mcp.json;键名为 servers,条目中需要指定 type:

{ "servers": { "ng-token-saver": { "type": "stdio", "command": "ng-token-saver" } } }

Codex CLI——运行 codex mcp add ng-token-saver -- ng-token-saver,或编辑 ~/.codex/config.toml:

[mcp_servers.ng-token-saver]
command = "ng-token-saver"

JetBrains AI Assistant / Junie——Settings → Tools → AI Assistant → Model Context Protocol 接受与快速入门中相同的 JSON;对于 Junie,还需启用“Pass custom MCP servers”。

配置项,均为可选:

  • NG_TOKEN_SAVER_IDLE_MS——语言服务器会话在此时长内未使用时,其 ngserver 会关闭;下次调用需要重新冷启动。默认值为 900000(15 分钟);设为 0 可让会话保持运行,直到服务退出。正在处理调用的会话不会关闭。
  • NG_TOKEN_SAVER_PREWARM=1——若 ng_workspace_map 的 path 位于某个应用内,就会在后台将该应用加载到语言服务器中,让首次诊断或定义调用时应用已就绪:在生产单体仓库中从 21 秒缩短至 1.4 秒。代价是空闲关闭前约占用 1 GB 内存,无论后续是否调用 LSP 工具;如果某个文件夹包含多个项目(例如工作区根目录、Nx 的 apps/),则不会预热,因为无法确定要加载哪个应用。
  • NG_TOKEN_SAVER_SERVERS_DIR——语言服务器分支所在目录;若不在构建目录旁的 tools/servers 中,可通过此项指定。

与 Agent 配合使用

服务会在 MCP instructions 字段中发送使用指南(924 个字符)。即使工具描述要等到搜索工具时才会提供,Claude Code 也会将这些内容保留在 Agent 的上下文中(在 Claude Code 2.1.282 上检查过:工具仅显示名称时,完整文本就已传入)。对于忽略 instructions 的客户端,同样的文本会写入 CLAUDE.md 或 AGENTS.md:

Angular 工具直接从编译器和源码中获取答案,而不是读取整个文件。优先使用这些工具,而不是读取和搜索:使用 ng_component_info 查看组件或指令接受的内容(包括 inputs、outputs、成员和继承的成员),而不是读取其 .ts 文件;使用 ng_find_usages 查找组件、指令、管道或服务的使用位置,并通过 input 查找某个 input 的绑定位置,而不是使用 grep;编辑模板后使用 ng_template_diagnostics,而不是运行 ng build;使用 ng_template_definition 查看模板符号的定义;在建议 Angular API 前使用 ng_version_rules;使用一次 ng_workspace_map 查看项目、版本和 strictTemplates。工作区中首次调用诊断或定义工具时,会将项目加载到 Angular 语言服务器中;大型工作区最多需要一分钟。之后的调用只需几毫秒,因此首次响应较慢并不表示卡住。行和字符编号在所有地方都从 1 开始,与 Read 显示的一致。

重现测量结果

npm test                                  build plus 182 unit tests (node:test, no dependencies)
npm run smoke                             end-to-end check with a real MCP client over stdio
npm run bench:settle                      whether a pause after didOpen is needed (it is not)
npm run bench:standalone                  where standalone becomes the default (v17..v22)
npm run bench:api                         which Angular APIs exist in which majors, and their stability
npm run bench:contract <project root>     contract size against reading whole files (--tokens adds token counts)
npm run bench:rename <component> <input>  the grep path against the bridge for an input rename
npm run bench:diagnose                    a compiler listing against one diagnostics call
npm run bench:matrix                      resolution probes across a fixture
npm run bench:negative                    what the server returns when things break
npm run bench:didchange                   diagnostics timing after an edit

测试目录为 fixtures/v17..v22——六个真实的 Angular 工作区,每个工作区都有自己的 node_modules 和固定版本的 TypeScript;此外还有用于失败案例的 fixtures/negative/*。

状态

六个工具均已在六个测试夹具和两个生产代码库中通过验证,这两个代码库分别包含 1298 和 407 个组件,且解析错误为零;此外还在一个 Angular 16 项目中进行了检查,以确认超出范围时会返回结构化拒绝,而非静默处理。182 项单元测试全部通过。

测得的延迟:两个由 LSP 支持的工具首次调用时需要 8–28 秒冷启动,之后响应时间为 2–9 毫秒;四个静态工具首次调用时需要 250–600 毫秒,项目的 TypeScript 缓存后则只需几毫秒。会话空闲 15 分钟后,语言服务器会关闭;下次调用时需要再次冷启动,参见上文的 NG_TOKEN_SAVER_IDLE_MS。

许可证

GPL-3.0-or-later——参见 LICENSE。版权所有 (C) 2026 Alex Ryabov。 可自由使用和修改;衍生作品必须继续以相同许可证开源。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。