关于 tabnas
tabnas 为解析引擎提供 TypeScript MCP 服务和 CLI,面向语法开发与测试。两种入口共享核心,并确保 JSON 结果一致;语法以数据形式验证,不执行代码。
主要功能
- 01解析输入并生成语法树
- 02验证语法规范并诊断错误
- 03运行 TSV 语法测试夹具
- 04列出并查看语法插件
- 05比较语法接受范围与输出
- 06提供语法和诊断资源
适用场景
- 01检查输入能否按给定语法解析
- 02定位解析失败原因
- 03用 TSV 夹具回归测试语法
- 04评估语法修改的兼容性
MCP 相关
MCP 服务通过 stdio 提供七种语法操作,并公开语法、诊断、错误和插件等资源。托管端点也通过 streamable HTTP 提供相同工具。
配置
远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。
https://mcp.tabnas.dev/mcpclaude mcp add --transport http dev-tabnas https://mcp.tabnas.dev/mcp{"mcpServers":{"dev-tabnas":{"type":"http","url":"https://mcp.tabnas.dev/mcp"}}}在 Cursor 中打开{"servers":{"dev-tabnas":{"type":"http","url":"https://mcp.tabnas.dev/mcp"}}}在 VS Code 中打开{"mcpServers":{"dev-tabnas":{"type":"http","url":"https://mcp.tabnas.dev/mcp"}}}{"mcpServers":{"dev-tabnas":{"serverUrl":"https://mcp.tabnas.dev/mcp"}}}命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。
工具
7| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| input | string | 是 | 要解析的源文本。 |
| grammar | object | 否 | 序列化的 GrammarSpec(纯 JSON,不含函数)。架构:资源 tabnas://schema/grammar。使用前会验证语法;包含 `ref` 键或非内置函数引用的语法会被拒绝。 |
| options | object | 否 | 在语法之前应用于新实例的 TabnasOptions。仅限数据:如果包含 plugins 条目则会被拒绝。 |
详细介绍
tabnas/mcp
tabnas Agent 工具:一套 TypeScript 代码,两种前端:
一个 MCP 服务(stdio)和统一的
tabnas CLI,共享同一个核心(ts/src/core.ts),确保二者永远不会出现结果不一致的情况。对于每项操作,CLI 的 --json 输出和 MCP
工具结果都逐字节完全一致,测试套件会对此进行验证。
此软件包的网站页面:各客户端的配置、工具契约、托管端点的限制:tabnas.dev/mcp。
以 @tabnas/mcp 发布。此仓库仅包含 TypeScript:它是在引擎之上的工具,而不是一个保持功能一致的软件包,因此没有 Go 移植版。
安装
npm install -g @tabnas/mcp # the `tabnas` CLI
npx --yes @tabnas/mcp # run the MCP server directly (stdio)
MCP 客户端配置(stdio):
{
"mcpServers": {
"tabnas": {
"command": "npx",
"args": ["--yes", "@tabnas/mcp@<x.y.z>", "mcp"]
}
}
}
将 <x.y.z> 替换为当前版本:npm view @tabnas/mcp version。此 README 不写明版本号:仓库无法固定写入自身已发布版本的确切版本号,因为更新版本号的提交会成为下一次发布的内容,导致 README 永远落后一个版本。
服务器由 CLI 的 mcp 子命令启动
(tabnas mcp),这与 skills 软件包的 mcp.json
调用的命令完全一致:npx --yes @tabnas/mcp@<x.y.z> mcp。(--yes 很重要:缓存未命中时,否则 npx 会在 MCP 传输占用的 stdin 上提示用户。请固定确切版本,避免工具在已安装的客户端中悄然变更:skills/mcp.json 中写有实际版本,由 tools/sync-mcp-pin.js 从注册表写入,并由
tools/validate.js --online 检查。)
七个工具
| 工具 | 解答的问题 | 结果 |
|---|---|---|
parse | 此输入能否解析,以及解析成什么树? | {ok:true, tree} | {ok:false, diagnostic} |
validate_grammar | 此序列化的 GrammarSpec 是否有效? | {ok:true, v} | {ok:false, errors:[{path,message}]} |
explain_parse_error | 此解析为何失败? | {failed:false} | {failed:true, diagnostic, registry} |
test_grammar | 这些 TSV 测试用例是否通过? | {pass, fail, rows:[{row,input,expected,got,ok}]} |
list_plugins | 有哪些语法插件? | {plugins:[...]} |
describe_plugin | 某个插件的完整描述符 | tabnas.plugin.json 对象 |
compare_grammars | 语法变更后,是否仍接受旧语法接受的内容,并生成相同的树? | {normalForm, proven[], observed[], changes[], counterexamples[], confidence, why} |
关于契约的说明:
- 每次调用都会为每项操作创建一个全新的引擎实例。
parse会先应用options,再应用grammar。如果没有提供语法,实例 与new Tabnas()完全相同:基础引擎不定义任何规则,因此每个输入 都会生成 undefined 树(序列化为{"ok":true})。 - 每项接受
grammar的操作都会在使用前验证语法;无效语法会以validate_grammar错误格式{ok:false, errors:[{path,message}]}拒绝。 validate_grammar的验证层包括:ADR-10 安全扫描(见下文)、根据捆绑的grammar.schema.json进行结构验证(Ajv, draft 2020-12),然后在全新实例中加载引擎,并将其抛出的 消息作为错误返回。v是语法声明的内置配置架构版本(缺省时为 1)。- 安全性(ADR-10): 序列化的语法及其选项是数据,
绝不是代码。每项接受语法的操作以及请求选项都会先经过防火墙检查,
拒绝以下内容:树中任意位置出现名为
__proto__、constructor或prototype的自有键(防止原型污染, 因为引擎的语法合并没有__proto__防护);ref键(实时函数不能是 JSON);任何非$结尾引擎内置项的函数引用;plugins键,无论它是请求选项还是grammar.options中的键(插件是实时 代码);以及规则数超过 5000 的语法(CPU 限制)。“验证此语法”绝不会变成“运行 此代码”或“污染此进程”。 - 非诊断类的引擎抛错(例如,
options.parser.start被设为 非函数)会被捕获,并以相同的干净格式{ok:false, errors:[{path:"",message}]}返回,因此 CLI 和 MCP 工具的行为一致。 explain_parse_error会将诊断与捆绑的错误代码注册表条目({code, message, hint})关联;对于注册表未知的代码(例如插件声明的代码),registry为null。test_grammar接收符合 fleet 测试用例约定(@tabnas/support)的 TSV 内容:第 1 行是标题行,输入列会进行转义解码,预期列为 JSON 或ERROR/ERROR:<code>。列默认取位置 0 和 1;options.inputCol/options.expectedCol可按位置或标题名称选择列。拒绝超过 10000 行的测试规格。
MCP 资源(原样提供自捆绑的 data/):
tabnas://schema/grammar、tabnas://schema/diagnostic、
tabnas://errors、tabnas://plugins、tabnas://divergence。
CLI
tabnas parse [file|-] [--grammar g.json] [--json]
tabnas validate --grammar g.json [--json]
tabnas diagnose [file|-] [--grammar g.json] [--json]
tabnas test --spec fixtures.tsv [--grammar g.json] [--json]
tabnas plugins [name] [--json]
tabnas compare --a old.json --b new.json [--corpus dir|file] [--depth n] [--json]
tabnas mcp # run the MCP server (stdio)
输入来自 file;当参数为 - 或未提供时,则来自标准输入。
CLI 不会访问网络。tabnas mcp 会启动 stdio MCP
服务(与 npx @tabnas/mcp 启动的是同一服务);它会在
stdout 上输出 JSON-RPC,且不会在该处输出其他内容。
--json 会准确输出核心结果 JSON:与同一请求下 MCP 工具返回的字节完全相同(键顺序稳定;由 ts/test/golden.test.js 强制执行的黄金契约)。不使用 --json 时,会输出易读的呈现结果;解析失败时,会输出引擎自身呈现的错误消息。
退出代码:
| Code | 含义 |
|---|---|
| 0 | 成功:解析成功/语法有效/所有 fixture 行均通过 |
| 1 | 操作未通过:解析失败、语法无效、fixture 失败、未知插件 |
| 2 | 用法错误:未知标志或命令、文件缺失/不可读、语法 JSON 格式错误 |
传入尚未成为 GrammarSpec 的语法
每个工具都接受序列化后的 GrammarSpec,即纯 JSON。它不接受 ABNF、EBNF、GBNF 或 jsonic 源码,而且永远不会接受:编译这些格式意味着 运行编译器,而工具有一条不可逾越的规则:语法是数据, 而不是代码(ADR-10)。请先编译,再传入结果。
const { abnfConvert, toPureSpec } = require('@tabnas/abnf')
const spec = toPureSpec(abnfConvert(abnfSource, { builtins: true }))
// -> { options, rule, v, meta } — validates clean, safe to send
使用 toPureSpec。 这是专为此用途设计的函数;另外两个显而易见的
替代方案都不合适:
abnfCompile()返回的是 jsonic 文本,不是对象。适合写入 语法文件,但不是工具参数所需的内容。- 单独调用
abnfConvert()会返回带有ref(使用builtins: true转换时为空)和标记字段的 spec。防火墙会拒绝ref的存在,而不仅仅是其内容;这是有意为之,因为在安全边界上,不值得推敲“足够为空”究竟是什么属性,而且m标记不属于序列化格式。toPureSpec会剥离这两者并添加v。
对于只需判断接受/拒绝、无需构建树的语法,toRecognitionSpec 作用相同,但不包含构建树的内置项。
其他前端的等效方式是 tabnas parse --grammar g.json,
其中 g.json 是构建步骤写出的文件。
语法兼容性(compare)
分别报告两个问题,因为它们失败的方式不同:
- 接受性。 候选语法是否仍接受基线语法接受的内容?
- 输出。 对于两者都接受的输入,生成的树是否相同?
用户感受最明显的是第二点。即使仍接受所有历史文档,但树的结构发生变化,也会悄无声息地破坏所有下游使用方;而仅测试接受性的测试却会报告成功。
报告提供证据和置信度,而非空洞的结论。 它特意不提供 compatible: true 字段。语言包含关系通常不可判定,因此如果工具输出这样的字段,最终会在生产环境中出错:
proven[]。静态确立的内容及其依据。任何超出可判定子集的内容都会标记为not-proven;这是对本工具能力的说明,并非声称语法不兼容。observed[]。实际运行的内容及其覆盖范围。changes[]/counterexamples[]。具体差异及对应输入。confidence+why。对未发现问题这一结果应有多大信心。带有明确原因的confidence: "low"仍是一次成功运行。
真正实用的检查是备选项的顺序。备选项按顺序匹配,命中第一个即停止,因此插入一个较早的备选项可能会遮蔽较晚的备选项,从而缩小接受的语言范围,而集合比较却会将其视为新增。compare
检查的是位置,而非成员关系,并报告过去可达、现在被遮蔽的备选项。
--corpus 接受 .tsv fixture 文件或包含此类文件的目录,并通过
@tabnas/support 加载;fixture 运行器也使用同一个加载器。真实输入是唯一能衡量文档实际表现的层级:
tabnas compare --a v1.json --b v2.json --corpus ../json/test/spec
发现任何变化时,退出代码为 1,因此可用作发布门禁。
托管端点
mcp.tabnas.dev 通过可流式传输的 HTTP(POST /mcp,以及 GET /health 和 GET /.well-known/mcp)提供相同的七种工具,供无法运行 npx 的 Agent 使用。本地 stdio 仍是推荐方式:免费、私密、可复现且不受限制。
托管服务使用相同的核心,因此响应完全一致;同时它也有使用边界,因为它会在共享基础设施上解析攻击者可控的文本。请求正文上限为 256 KB,每个 IP 每分钟最多 60 个请求;这两项限制都会由 /.well-known/mcp 预先报告,并在拒绝请求时(limit_exceeded / rate_limited)连同本地替代方案一并说明。速率限制由 Cloudflare 执行,按每个数据中心内的每个 IP进行近似计数,因此它是抵御持续滥用的屏障,而非精确配额;短时间突发请求可能超过 60 次,之后才开始拒绝。文档内容绝不会被记录、存储或用于训练;遥测只记录形态信息:工具名称、大小区间、时长、状态、错误代码。
捆绑数据
已发布的包不包含整个 fleet 的契约文件(解析器的 npm 文件不包含 schema/;插件包不会附带 tabnas.plugin.json),因此本仓库会在 data/ 中提交生成的副本:语法和诊断 schema、错误代码注册表、DIVERGENCE.md,以及 fleet 中每个插件的描述符(按名称排序的 plugins.json)。从同级检出目录(本仓库旁的 ../<repo>)重新生成:
cd ts && npm run gen-data
构建会将 data/ 编译到 ts/src/data-bundle.ts(生成文件,由 git 忽略),代码读取这个静态导入,而绝不访问文件系统,因为托管 Worker 没有文件系统。测试套件会在重新生成内容过时或嵌入内容过时时失败,并将嵌入的集合与目录进行核对,而不是依赖手动维护的列表。应当推导,而不是重复(ADR-10)。
data/grammar.schema.json 也会以相同方式处理,但原因不同:Ajv 通过生成 JavaScript 并调用 new Function 来验证,而 Cloudflare Workers 完全禁止这样做,因此 tools/build-validator.js 会在构建时将 schema 预编译到 ts/src/grammar-validator.js。使用相同的 Ajv,产生相同的错误结构,只是提前编译。
构建与测试
cd ts
npm install
npm test # builds first, then runs every test/*.test.js
npm test 会以 test/workerd.test.js 结束,该测试会在真实的 workerd 中启动真正的 wrangler.json,并通过 HTTP 与之通信。这是唯一一个测试,其失败意味着“托管端点无法部署”,而且需要 wrangler devDependency(以及 npm 随之安装的 workerd 二进制文件)。它会多花几秒钟;Node 级别的测试无法替代它,因为本仓库过去所有阻碍部署的缺陷,在 Node 测试中都曾通过。
在 fleet 布局中工作(本仓库旁边有同级检出目录)时,运行 npm install 后为同级目录创建符号链接,以便针对源代码进行测试(每次安装时 npm 都会替换这些链接,因此安装后需要重新创建):
rm -rf node_modules/@tabnas/parser node_modules/@tabnas/support
ln -s ../../../../parser/ts node_modules/@tabnas/parser
ln -s ../../../../support/ts node_modules/@tabnas/support
CI
.github/workflows/ci.yml 调用组织的 tabnas/.github 中的 polyglot-ci.yml(仅 ts,并将 parser 和 support 克隆为同级目录),由 a14741c 中的 ci/ci.yml 提升而来。应直接在 .github/workflows/ 中修改此文件,并通过经过审查的拉取请求提交(管理员 ADR-8,修订于 2026-09-24)。
CI 会运行 test/workerd.test.js,该测试会在真实的 workerd 中启动真正的 wrangler.json,因此每次推送都会检查托管端点是否可部署,而不是等到部署时才发现问题。
许可证
MIT。请参阅 LICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。