
Company News
Socket Joins New OpenJS Program to Fund Node.js Security Work
Socket is joining the OpenJS Security Stewardship Program to fund Node.js vulnerability research, maintainer remediation, and security releases.
@spintax/mcp
Advanced tools
Local MCP server for spintax templates — validate, render and analyze over stdio with @spintax/core, no network and no size caps. Ships the shared tool module and JSON-RPC dispatcher the hosted server at spintax.net/mcp is built to run from.
A local MCP server for spintax templates: an agent on your machine
can validate, render and analyze a template through @spintax/core,
the reference engine — over stdio, with no network call and no size caps.
There is also a hosted door at https://spintax.net/mcp (in the official registry as
net.spintax/mcp). Use this package instead when you want:
#include — resolving partials from disk, which a hosted server must never do.This package holds the tool module and the dispatcher both doors are meant to run — one source of tool definitions, so a renamed tool or a changed result shape cannot differ between them. The tool list here is asserted, byte for byte, against the list the hosted server currently serves; the hosted server's own switch to this module is the next change on that side.
// Claude Desktop, Claude Code, Cursor, … — an MCP client config entry
{
"mcpServers": {
"spintax": { "command": "npx", "args": ["-y", "@spintax/mcp"] }
}
}
With partials on disk:
{
"mcpServers": {
"spintax": {
"command": "npx",
"args": ["-y", "@spintax/mcp", "--include-root", "/abs/path/to/partials"]
}
}
}
Or install it and run the binary directly:
npm install -g @spintax/mcp
spintax-mcp --help
| Tool | What it answers |
|---|---|
validate_spintax | Diagnostics with severity, a stable code and 1-based line/column. No error ⇒ structurally sound; read the warnings too. |
render_spintax | N variants. With a seed it is deterministic — variant i uses seed <seed>#<i>. |
analyze_spintax | Which variables the template needs, which directives it defines, best-effort construct counts. |
Two things worth knowing, because they are the traps this server exists to make visible:
locale
the engine files no arity verdict — the template may well be right for the locale you will
render with, and failing it here would fail a good template. What it does file is a
plural.locale-missing warning whenever a block's form count is not the 2 that render
defaults to, because that block will otherwise reach your finished text as {plural …}. So
"no error" is not the whole answer: name the locale, or read the warnings.{…} marking markup the parser could not read. Run validate_spintax first.| Flag | Default | Notes |
|---|---|---|
--include-root <dir> | — | Resolve #include against <dir>. Without it, an #include line is inert: it stays in the output verbatim. |
--max-variants <n> | 50 | Cap for render_spintax's count. |
--max-depth <n> | 20 | #include / nesting depth guard. |
--max-include-bytes <n> | 1048576 | Refuse an #include file larger than this. |
--max-message-chars <n> | 8388608 | Refuse a single JSON-RPC message longer than this. |
Each has an SPINTAX_MCP_* environment fallback (SPINTAX_MCP_INCLUDE_ROOT, …), used only when
the flag is absent.
There is deliberately no cap on the template itself — removing it is the point. One limit can
still meet a very large one, and it is a flag rather than a secret: --max-message-chars bounds the
whole JSON-RPC line a template arrives in. It exists because a message past a few megabytes almost
always means a client has lost the newline framing, not that someone wrote an 8 MB template; if you
did, raise it.
#include from diskA ref is untrusted template data, not a path an operator typed, so --include-root is a jail and
not a hint. A ref must look like partials/opener.txt — [A-Za-z0-9._-] segments joined by / —
and the resolved real path must still sit inside the root, which is re-checked after resolving
symlinks. That last step is what stops a link or junction inside the root from reading
~/.ssh/id_rsa. Non-files are refused (a FIFO would hang a synchronous resolver forever), a size
cap applies, and a leading BOM is stripped. Nothing raises: every refusal is a miss.
Because the engine drops a cyclic or too-deep #include to an empty string before the resolver
is asked, render_spintax attaches an include report telling the two apart:
"include": {
"root": "/abs/path/to/partials",
"maxDepth": 20,
"resolved": ["opener.txt", "cta.txt"],
"missing": [{ "ref": "signoff.txt", "reason": "not-found" }],
"suppressed": [{ "ref": "loop.txt", "reason": "cycle" }],
"truncated": false
}
It is best effort by construction: an #include produced by a spin choice
({#include "a"|plain text}) is invisible to static analysis, so a suppressed one can go
unreported. With a root configured, validate_spintax also gets the refs that really resolve as
its allow-list, which turns a broken partial into a diagnostic with a line and column — as long as
at least one sibling resolves (the engine files those verdicts only for a non-empty allow-list).
The package's main entry is the transport-free half — the dispatcher, the tool builder and the engine wrappers — and it imports no Node builtin, so it runs on Cloudflare Workers and in a browser unchanged. That is not a side effect; it is why the hosted server can share it:
import { buildTools, createDispatcher } from '@spintax/mcp';
const tools = buildTools({ maxVariants: 20, maxTemplateChars: 8192 });
const mcp = createDispatcher({
serverInfo: { name: 'my-server', title: 'My server', version: '1.0.0' },
instructions: 'Spintax tools.',
tools,
limits: { maxVariants: 20, maxTemplateChars: 8192 },
});
const outcome = await mcp.dispatch(await request.json(), {
get: name => request.headers.get(name),
});
// { kind: 'accepted' } → HTTP 202, no body
// { kind: 'response', body, httpStatus } → JSON with that status
Caps are parameters, never constants: they are interpolated into tool descriptions and JSON
Schemas, so a server that hardcoded them would publish schemas that lie. The headerAdapter
argument is how header mirroring stays out of the shared code — stdio has no header layer at all,
which the MCP spec is explicit about, so the transport simply passes nothing.
The wire protocol is hand-rolled (revision 2026-07-28, plus initialize for the four earlier
revisions) and the only runtime dependency is @spintax/core. No SDK: that decision belongs to the
hosted server's ADR 0005 and this package continues it.
MIT — see LICENSE.
FAQs
Local MCP server for spintax templates — validate, render and analyze over stdio with @spintax/core, no network and no size caps. Ships the shared tool module and JSON-RPC dispatcher the hosted server at spintax.net/mcp is built to run from.
The npm package @spintax/mcp receives a total of 45 weekly downloads. As such, @spintax/mcp popularity was classified as not popular.
We found that @spintax/mcp demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Company News
Socket is joining the OpenJS Security Stewardship Program to fund Node.js vulnerability research, maintainer remediation, and security releases.

Security News
Two compromised GitHub Actions were re-enabled with malicious tags intact, exposing thousands of downstream repositories to Mini Shai-Hulud.

Research
/Security News
A malicious Firefox extension fetches its payload after installation to evade detection, steal Google session cookies, and automate account takeover.