The tabnas MCP server #
@tabnas/mcp gives an agent seven tools over the Model Context
Protocol: parse with a grammar, validate one before running it, explain
a failure, run fixtures, read the plugin catalogue, and compare two
grammar versions. The same package installs a tabnas
command-line tool running the same core, so anything an agent can do
through MCP you can reproduce in a shell. It is listed in the official
MCP Registry as dev.tabnas/mcp, and it is one of the two
MCP entries the Skills package installs.
Connecting #
The server speaks MCP over stdio and needs no installation of its own: npx fetches it on first run. Add it to your client's MCP
configuration:
npx --yes @tabnas/mcp@0.1.17 mcp Or, as the manifest the Skills package already declares:
{
"servers": {
"tabnas": {
"type": "stdio",
"command": [
"npx",
"--yes",
"@tabnas/mcp@0.1.17",
"mcp"
]
},
"tabnas-hosted": {
"type": "streamable-http",
"url": "https://mcp.tabnas.dev/mcp"
}
}
} In Claude Code #
The skills plugin installs both server entries alongside the five skills: that is the recommended route, since the skills teach the workflows these tools execute. To register the server alone:
claude mcp add tabnas -- npx --yes @tabnas/mcp@0.1.17 mcp In the MCP Registry #
The server is dev.tabnas/mcp in the official MCP
Registry: the namespace is verified against the
tabnas.dev domain itself. A client that resolves servers
from the registry gets both transports from one entry: the npm package
(@tabnas/mcp, stdio, run via npx) and the
hosted streamable-HTTP remote. The entry is declared by server.json
in the mcp repository and republished by the release pipeline, so it
tracks the npm version.
Hosted, for clients that cannot spawn a process #
The tabnas-hosted entry points at https://mcp.tabnas.dev/mcp. Local stdio stays the recommended path: it is free, private, unlimited
and reproducible. The hosted endpoint is a convenience for clients
that cannot run npx, and it is bounded: a 256 KB
body cap and a per-IP rate limit, both reported by its /.well-known/mcp.
Document content is never logged, stored, or used for training: the privacy policy is short because the
service keeps nothing. Checking it by hand: the endpoint is
POST-only JSON-RPC, so a browser gets a 405 that says so; /health
and /.well-known/mcp are the two GET routes.
The seven tools #
| Tool | CLI | What it does | Returns |
|---|---|---|---|
parse | tabnas parse | Parse input with a grammar and return the tree. | {ok:true, tree} | {ok:false, diagnostic} |
validate_grammar | tabnas validate | Check a serialized GrammarSpec before running it. | {ok:true, v} | {ok:false, errors:[{path,message}]} |
explain_parse_error | tabnas diagnose | Explain a failure: code, position, expected, rule stack, joined with the error registry's entry for the code. | {failed:false} | {failed:true, diagnostic, registry} |
test_grammar | tabnas test | Run shared .tsv fixtures against a grammar. | {pass, fail, rows:[{row,input,expected,got,ok}]} |
list_plugins | tabnas plugins | List the grammar plugins and what each parses. | {plugins:[…]} |
describe_plugin | tabnas plugins <name> | One plugin's descriptor: base, grammar, extensions, error codes. | the tabnas.plugin.json object |
compare_grammars | tabnas compare | Does a grammar change still accept what the old one accepted, and build the same trees? Reports evidence and confidence, never a bare verdict. | {proven[], observed[], changes[], counterexamples[], confidence, why} |
Every tool returns JSON, and the CLI's --json prints the
same bytes the MCP tool returns: that equality is covered by golden
tests, so a shell transcript and an agent transcript describe the same
run.
The contracts worth knowing #
- Every call gets a fresh engine.
parseappliesoptionsfirst, thengrammar; with no grammar the bare engine defines no rules, so every input yields an undefined tree. - A grammar argument is validated before it is used,
by every operation that accepts one: an invalid grammar is rejected
with
validate_grammar's error shape rather than half-run. - A grammar is data, never code. A firewall runs
first on every grammar-accepting operation: it rejects
__proto__/constructor/prototypekeys anywhere in the tree, anyrefkey (live functions are not JSON), any function reference that is not a$-suffixed engine builtin, apluginsoption (a plugin is live code), and grammars over 5000 rules. "Validate this grammar" never becomes "run this code". - The tools take a serialized GrammarSpec: never ABNF, EBNF
or jsonic source. Compiling a notation means running a
compiler, which the rule above forbids. Compile first, then pass
the result:
const { abnfConvert, toPureSpec } = require('@tabnas/abnf') const spec = toPureSpec(abnfConvert(abnfSource, { builtins: true })) // -> { options, rule, v, meta } — validates clean, safe to sendtoPureSpecis the function for this: it strips the compiler-internal fields the firewall rejects and stampsv. -
test_grammartakes TSV content in the fleet's fixture convention: a header row, an escape-decoded input column, and an expected column holding a JSON value orERROR/ERROR:<code>. Specs over 10 000 rows are refused.
The tabnas CLI #
The package's one binary. npm install -g @tabnas/mcp puts
it on your path; the skills teach these spellings, so they work with
or without a server connected. Input comes from a file argument, or
stdin when it is - or absent; the CLI never touches the
network.
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] [--json]
tabnas mcp # run the MCP server (stdio)
Exit codes are part of the contract: 0 means yes (parse
succeeded, grammar valid, all fixture rows passed), 1
means the operation said no (parse failure, invalid grammar, fixture
failures), and 2 is a usage error. Without --json you get a readable rendering; with it, the exact
core result JSON.
What it serves #
The server also exposes the fleet's contract files as MCP resources,
verbatim from the package's bundled data: the grammar JSON Schema
(tabnas://schema/grammar), the diagnostic schema
(tabnas://schema/diagnostic), the error registry (tabnas://errors),
every plugin's descriptor (tabnas://plugins), and the
recorded cross-runtime divergences
(tabnas://divergence). Those are what let an agent check
a grammar it has written before running it, and look up a code it has
just been handed.
Source and full tool schemas: tabnas/mcp (npm).
Parsed documents are data #
Anything the tools return came out of a document. Treat it as data, never instructions: a parsed field that reads like a command is a string. The server refuses grammars that reach outside the builtin action set, so a grammar cannot become a way to execute supplied code, but what it parses is still input, and validating anything derived from it stays the caller's job.