Writing spintax templates with AI

You pay a model once to write the template. After that the engine renders as many variants as you want, offline and for free. This page is about the part between those two facts: what to give the model, what to ask for, how to check what comes back, and the mistakes every model makes in spintax.

The short version

Give the model the documentation, give it your finished article, ask for one template. In that order. There are two roads to that template now: Spintax Studio does the drafting and the checks for you, or you wire a model up to the documentation by hand. Both end in the same place — a template the engine has already checked.

The method this page teaches is to hand over finished text: the model then marks up sentences you already approved instead of writing them. Ask for both at once — the writing and the markup — and you get a template assembled out of sentences nobody ever read as sentences. Write the article first, in one clean version you would publish as is, then hand it over. (Studio will also take a bare brief and draft from scratch — quicker, but then you are reviewing the model's writing, not only its markup.)

The simplest way: Spintax Studio

Spintax Studio is the free, native Windows editor for the family, and since version 0.2.0.0 it drafts templates for you — it does the setup and the checks below without you assembling any of it.

Paste the text you already have — a product page, a letter, a description — or write a short brief, and Generate turns it into a spintax template. The draft is not taken on trust: it arrives already checked by the same engine that renders the preview, and it lands in an answer box, never in your document. Applying it with Insert or Replace is your own act. When the engine still finds problems, Fix sends your document back to the model together with the diagnostics — line and column — to repair.

The connection is yours: your provider, your account, and your key when the endpoint needs one. There is no key or server of ours, the app ships no model of its own, and the feature is off until you turn it on — the app's privacy policy states exactly what it sends and to whom. Prefer no connection at all? Copy prompt hands you the same prepared prompt to paste into any model by hand — the one printed below — and whatever you paste back is validated and rendered like anything else, though without Fix's automatic repair round.

So Studio folds the setup and the checks into two buttons — the documentation, the prompt, the run, and the validate-and-repair pass. The one thing it cannot do for you is read the variants; that judgement stays yours. The rest of this page is that same method by hand: for any model on any platform, and for agents that draft spintax themselves.

What to give the model

The site publishes its documentation in machine-readable form, so you do not have to explain spintax to anything. Pick the surface that matches your tooling.

SurfaceWhat it isUse it when
/llms-full.txt The documentation core in one file: the syntax reference, the nesting guide and all seven authoring guides. Just over 100 KB. Default. One fetch, or one paste into a chat.
spintax-authoring The same method condensed to two pages, written for a model rather than a reader. Your agent supports skills, or the context budget is tight.
/docs/variables.md and 19 others Any documentation page as clean Markdown. Add .md to the URL, or send Accept: text/markdown. One narrow question, mid-task.
https://spintax.net/mcp The MCP server: three tools that validate, render and analyze a template. You want the model to check its own work before you see it.

There are two other skills next to the authoring one. spintax-syntax covers the constructs and the trap in each. spintax-engines covers installing and calling an engine from JavaScript, PHP or Python. For authoring work you want the first one.

The prompt

This is the prompt we use. It assumes the model can fetch a URL; if yours cannot, paste llms-full.txt above it and delete the two lines that mention fetching.

You are converting a finished article into ONE spintax template.

Read the syntax and authoring rules first:
https://spintax.net/llms-full.txt
If you cannot fetch URLs, say so and I will paste the file instead.

Priorities, in this order:
1. Every rendered variant must be grammatical.
2. Every rendered variant must read like something a person wrote.
3. Variety comes last. A difference the reader would not notice
   is not worth a branch.

Hard rules:
- Never put a branch boundary inside a phrase whose grammar binds:
  subject and verb, preposition and object, article and noun.
- Do not repeat fixed text in both branches. Fork the smallest span
  that differs.
- Use %VAR% for anything that changes per site or per product.
- Use #def, not #set, when two references to the same value must agree.
- Any number followed by a noun goes through {plural %n%: …}. Never
  hand-roll the forms as {item|items}.
- Use only the syntax in the rules above. Do not borrow from ICU,
  Handlebars or Jinja.
- Do not spin brand names, product names, prices, URLs or legal
  wording. Vary the copy around them.
- Do not invent facts. If a slot needs a fact I did not give you,
  make it a variable.

Give me back:
1. The template, in one code block.
2. The variables you introduced, with an example value for each.
3. Anything in the article you could not express in spintax, and why.

Here is the article:
[paste your article here]

Two things about it are deliberate.

It points at the rules instead of restating them. The syntax is not in the prompt, and should not be. The documentation is one fetch away and it stays current; a copy pasted into a prompt six months ago does not.

It states priorities before rules. Left alone, a model optimizes for visible variety, because that is what a template looks like it is for. Grammar and readability have to be ranked above it explicitly, or you get four options in every slot and mush in the output.

Three ways to run it by hand

These are the manual equivalents of Studio's Generate — the same documentation and the same prompt, run against the model yourself. Pick the one that matches your tooling.

A chat window

Works with any model that has a large context window. Paste llms-full.txt, paste the prompt, paste your article. The whole documentation core is around 100 KB, which is comfortable for current models but not free: if you are working through a long session, the authoring skill costs a fraction of that and covers the same method.

You will not get validation here. The model has no way to run the template, so treat its output as a draft and take it to the playground next.

An agent that can read the docs

In Claude Code, Cursor or any agent that browses, point it at the skill and let it pull individual pages as needed:

Load https://spintax.net/.well-known/agent-skills/spintax-authoring/SKILL.md
and follow it. Fetch the guides it links when you need the detail.

Every link inside the skill already points at a .md mirror, so the agent never has to parse a web page to read our documentation.

The MCP server

This is the setup worth the extra five minutes. https://spintax.net/mcp is a live MCP server, no authentication, three tools:

  • validate_spintax — diagnostics with severity, a stable code, and 1-based line and column. No errors means the template is safe to render.
  • render_spintax — up to 20 variants, with a seed for reproducible output and a variable map for the context.
  • analyze_spintax — what the template needs and contains: referenced variables, #set and #def definitions, includes, construct counts.

In Claude Code, one line:

claude mcp add --transport http spintax https://spintax.net/mcp

In a client that takes a config file:

{
  "mcpServers": {
    "spintax": { "type": "http", "url": "https://spintax.net/mcp" }
  }
}

Or run the same server on your own machine — no account needed:

{
  "mcpServers": {
    "spintax": { "command": "npx", "args": ["-y", "@spintax/mcp"] }
  }
}

@spintax/mcp is the same three tools over stdio, from the same engine — the door to reach for when the hosted one does not fit. It has no size caps (real templates outrun 8 KB), makes no network calls at runtime (offline once installed, air-gapped included — and it is the only MCP path where Cloudflare is blocked, so the hosted endpoint cannot be reached at all), and it can resolve #include partials from disk with --include-root, which a hosted server must never do. Both doors run the same tool module, so what validates on one validates on the other. Full setup and the hosted-vs-local decision: the spintax MCP server.

Then add this to the end of the prompt:

You have the spintax MCP server. Before you show me anything:
call validate_spintax and fix every diagnostic with severity "error",
then call render_spintax with count 5 and read the variants yourself.
If a variant reads wrong, fix the template and repeat.

The difference is not cosmetic. Without the tools, the first template you see is the model's first guess, and you spend your review finding unclosed braces. With them, the model has already found the unclosed braces and you spend your review on whether the copy is any good.

The hosted door's limits, so you can plan around them: 8 KB of template per call, 20 variants per render, and #include is disabled — it pays for its own CPU. Compose big documents from sections and validate them one at a time, or switch to the local server, which has none of these. The full description of the hosted server lives in its server card.

The loop that catches problems

Whatever tooling you use, the review is the same three steps, and the third one is the one people skip.

  1. Validate. Structural mistakes are cheap to find and the engine finds them for you: the playground underlines them as you type, validate_spintax returns them as data.
  2. Render twenty, not one. A template with six branches has hundreds of outcomes. One render tells you almost nothing, and it is usually the render that made you approve a broken template.
  3. Read them. Actually read the twenty. Check every conditional branch in both states, every plural form including the counts that select the rare one (in Russian: 1, 2, 5, 11, 21), and the shortest and longest permutation outcomes, where separator and spacing artefacts show up.

When something reads wrong, fix the template rather than the variant, then render twenty again. Studio runs the mechanical half of this — Generate validates, its preview renders the variants, Fix sends the document and its diagnostics back to the model — but the reading is still yours. By hand, a model with the MCP tools can drive the checks itself, which is the whole reason to connect them.

What models get wrong

These are not random errors. They repeat across models, because they come from what the model learned elsewhere. Knowing the list turns a vague "this template feels off" into a two-minute check.

Note that almost none of them throw. The engine is lenient by design: a construct it does not recognize loses its braces and lands in the text as ordinary prose. The template validates clean and renders wrong, which is exactly why the loop ends with reading variants and not with a green check — and why a draft Studio hands you as valid still wants your eyes. Valid means it passed the engine's structural checks, not that the copy is good.

1. It cuts the branch through bound grammar

The most common failure, and the one that produces text that is wrong rather than merely dull. A subject and its verb, a preposition and its object, an article and its noun cannot be varied independently.

{Our platform|Our tools} {is|are} built for small teams.
→ four combinations, two of them ungrammatical

{Our platform is|Our tools are} built for small teams.
→ two combinations, both correct

Detail: grammar-safe synonymization.

2. It reaches for #set when the references must agree

A #set value is a macro: it re-rolls at every reference. Models pick it by default because it reads like an assignment in a programming language.

#set %tool% = {Slack|Jira|Linear}
Connect %tool% in one click. %tool% syncs both ways.
→ "Connect Slack in one click. Linear syncs both ways."

#def %tool% = {Slack|Jira|Linear}
Connect %tool% in one click. %tool% syncs both ways.
→ "Connect Jira in one click. Jira syncs both ways."

Detail: variables and multi-site reuse.

3. It writes plurals in the syntax of some other library

Models have seen far more ICU MessageFormat than spintax, and it shows. They also default to two forms, which is wrong for Russian, Ukrainian, Belarusian and Serbian.

We support {count, plural, one {# language} other {# languages}}.
→ "We support count, plural, one # language other # languages."

We support %n% {plural %n%: language|languages}.
→ "We support 3 languages." / "We support 1 language."

The two halves of this mistake behave very differently, which is worth knowing. Borrowed ICU is the quiet kind of wrong: no diagnostics at all, because a brace group without a pipe is legal, so the engine drops the braces and prints what was inside. Wrong arity is the loud kind: Russian takes three forms, {plural %n%: язык|языка|языков}, and supplying two under a Russian locale returns a plural.arity error and leaves the construct unrendered in the output. Same habit, and only one half of it is caught for you.

Detail: plural agreement.

4. It invents a conditional

Handlebars and Jinja habits produce {if …} blocks that the engine renders as literal text.

We ship worldwide. {if %HasCrypto%}We also accept crypto.{/if}
→ "We ship worldwide. If 1We also accept crypto. /if"

We ship worldwide. {?HasCrypto?We also accept crypto.}
→ "We ship worldwide. We also accept crypto."

The first render is what a leaked construct looks like: the braces are gone, the variable was substituted where nobody wanted it, and the closing tag became a word.

A conditional is driven by a value. When the choice is genuinely arbitrary, {a|b} is correct and a conditional is not.

Detail: conditional spintax.

5. It confuses square brackets with a choice

[a|b|c] shuffles and joins all three items, separated by a space unless you configure otherwise. It does not pick one. A model that means "pick one" and writes brackets produces a sentence with three adjectives where you wanted one.

Try our [fast|simple|affordable] tool.
→ "Try our fast simple affordable tool."

Try our {fast|simple|affordable} tool.
→ "Try our simple tool."

Try our [<minsize=2;maxsize=3;sep=", ";lastsep=" and ">fast|simple|affordable] tool.
→ "Try our simple, affordable and fast tool."
→ "Try our simple and fast tool."

Detail: permutations in practice.

6. It spins every clause

Given no priority, a model treats density of variation as the goal and puts a branch in every phrase. The output stops sounding like anything.

{Our|The} {platform|system} {helps|lets} {teams|groups} {ship|release}
{faster|quicker|sooner}.

The variants that matter are the ones a reader would notice. Two well-placed branches in a paragraph beat twelve inside one sentence. This is what the priority block in the prompt is for.

7. It repeats the fixed part in both branches

Duplicated text is an invitation to drift: the next edit changes one branch and forgets the other.

{Spintax (spin syntax) is|Spintax — "spin syntax" — is} a template language.
→ "Spintax" and "is" duplicated across both branches

Spintax {(spin syntax)|— "spin syntax" —} is a template language.
→ one copy of the fixed words, only the difference forked

Detail: the reverse authoring mindset.

8. It drops external values straight into the context

Variable values are markup-capable by default: the engine re-parses a value that contains {, [ or %. A product name from a database, a user-supplied string, a title with a stray brace, and your template renders something you never wrote. Pass untrusted values through neutralize() before they reach the context.

Detail: variables and multi-site reuse.

9. It writes one enormous template

Ask for a whole landing page and you get a single unreviewable block. Past a screenful, split it: an item template renders one unit, a section template assembles items, an orchestrator assembles sections. Each piece stays testable on its own, and the MCP server's 8 KB limit stops being a limit.

Detail: template composition.

Before you ship the template

  • Twenty variants rendered and actually read.
  • Every conditional seen in both states.
  • Every plural form seen, including the rare bucket.
  • No #set where two references must agree.
  • Every variable the template references has a value at render time.
  • Untrusted values neutralized.
  • The shortest and the longest permutation outcome both punctuate correctly.

What this costs

One template is one conversation with a model, and the rendering that follows is free: the engine runs locally, deterministically, with no API call per variant. That is the entire economic argument for this workflow, and it is worked through model by model, with current prices, in what AI content actually costs.

Then render it

The template you just made runs unchanged on every engine in the family: @spintax/core in JavaScript, spintax/core in PHP, spintax-core in Python, and the WordPress plugin. Same syntax, same output, one shared test corpus. See the four engines to pick one.