API reference

Format, validate and lint TOML

Check that text is valid TOML 1.0, list problems with line and column, and re-format it with comments kept.

GET POST /api/toml/format MCP tool toml_format

Parses input as TOML 1.0.0 and returns every finding as a diagnostic with a rule ID, severity, line, column and offset: syntax errors, duplicate keys and tables, integers beyond 64 bits (errors); TOML 1.1-only syntax, inconsistent indentation, trailing spaces, mixed line endings (warnings); unnecessary key quotes, split or out-of-order tables, long inline tables, empty tables, long lines (info). Only the first syntax error is reported. An invalid document is a successful response with valid: false, so check valid. For a valid document it also returns the canonical formatting and the data as JSON. Formatting keeps every comment and never changes the data: keys are written key = value with one space around =, indentation and trailing spaces are removed, there is one blank line before each table header, arrays longer than max_line_length are broken one element per line, and the text ends with one line break. Strings, numbers and dates are copied as written. In value, integers beyond ±(2^53 − 1) are decimal strings, inf/nan are the strings "inf", "-inf" and "nan", and dates and times are ISO 8601 strings.

Browser version (runs locally): TOML Format & Validate.

Try it

Credentials
Remember these credentials

Not saved: you will paste them again next time.

No credentials yet? Get a client ID and secret. They are sent only to this site, in an Authorization: Basic header.

POST /api/toml/format with a JSON body

Parameters

Parameter Type Default Description
input required string (≤ 50000 chars) — The TOML text. Send it as a string field or as a raw text/plain POST body.
indent integer (1–8) 2 Spaces per level for arrays that are broken over several lines.
max_line_length integer (0–1000) 120 Arrays longer than this are broken one element per line, and longer lines are reported (line-length). 0 turns both off.
sort_keys boolean false Sort keys within each table, by UTF-16 code units. Tables keep their order (the order of [[array]] tables is data).

Also accepted on every endpoint: format (json | text). Parameters may come from the query string, a JSON object body, an urlencoded or multipart form, or (for input) a text/plain body.

Global parameters
Parameter Type Default Description
format json | text json json returns the documented object. text returns only the primary value as text/plain (see "Text output").

Response 200 application/json

Field Type Description
valid boolean True when there are no error findings.
summary object
summary.valid boolean True when there are no error findings.
summary.errors integer Number of errors.
summary.warnings integer Number of warnings.
summary.infos integer Number of informational findings.
diagnostics array of object Findings in document order.
diagnostics[].rule string Rule ID, e.g. syntax, duplicate-keys, trailing-spaces.
diagnostics[].severity "error" | "warning" | "info" error makes the document invalid; warning and info are style.
diagnostics[].message string What is wrong and how to fix it.
diagnostics[].line integer 1-based line where the problem starts.
diagnostics[].column integer 1-based column (Unicode code points).
diagnostics[].offset integer 0-based UTF-16 offset into the input.
diagnostics[].endLine optional integer 1-based end line, when the problem spans a range.
diagnostics[].endColumn optional integer 1-based end column.
diagnostics[].endOffset optional integer 0-based end offset.
formatted string | null The formatted document, comments kept; null when invalid.
value any The document as JSON (see the value mapping in the docs); null when invalid.
stats object Counts for a valid document; all zero when invalid.
stats.tables integer Tables below the root: [table] sections, dotted-key tables, inline tables and each [[array]] element.
stats.keys integer Keys in all tables.
stats.arrays integer Arrays, including arrays of tables.
stats.depth integer Nesting depth: 1 for a flat document, one more per level of table or array.

Text output (format=text, text/plain): The formatted TOML, or for invalid input one line: invalid: line L, column C: message.

Errors

Errors are JSON: {"error": "<message>", "code": "<code>", "param": "<name>"?}.

Status Code When
400 invalid_parameter input is longer than 50,000 characters, or an option is out of range. Invalid TOML is NOT an error: it returns 200 with valid: false.
400 missing_parameter A required parameter is absent.
400 unknown_parameter A parameter name is not in the table above (typos are rejected, not ignored).
413 payload_too_large The request body exceeds 1 MiB.
405 method_not_allowed A method other than GET, POST or OPTIONS.

Behaviour

  • Idempotent: the same arguments always return the same result.
  • Requires a client ID and secret (authentication). Each call is recorded as one metadata row against your client ID; inputs and outputs are never stored or logged. Responses carry cache-control: no-store.
  • CORS: any origin may call this endpoint from a browser, using your credentials.

Examples

Responses below were produced by running each request through this code at build time. Long strings are shortened.

Normalise layout, keeping comments

curl -X POST 'https://util.caruana.me/api/toml/format' \
  -H 'content-type: application/json' \
  -d '{"input":"# Service settings\ntitle=\"demo\"   \n  \"port\"   =  8080\n[database]\nhosts = [ \"a.example\", \"b.example\" ]  # primary first\n[database.pool]\nsize=10\n"}'

Response 200

{
  "valid": true,
  "summary": {
    "valid": true,
    "errors": 0,
    "warnings": 2,
    "infos": 1
  },
  "diagnostics": [
    {
      "rule": "trailing-spaces",
      "severity": "warning",
      "message": "Trailing whitespace.",
      "line": 2,
      "column": 13,
      "offset": 31,
      "endLine": 2,
      "endColumn": 16,
      "endOffset": 34
    },
    {
      "rule": "inconsistent-indentation",
      "severity": "warning",
      "message": "This key has 2 spaces; the other keys in this table have no indentation.",
      "line": 3,
      "column": 1,
      "offset": 35,
      "endLine": 3,
      "endColumn": 3,
      "endOffset": 37
    },
    {
      "rule": "unnecessary-quotes",
      "severity": "info",
      "message": "The quotes around \"port\" are not needed: write port.",
      "line": 3,
      "column": 3,
      "offset": 37,
      "endLine": 3,
      "endColumn": 9,
      "endOffset": 43
    }
  ],
  "formatted": "# Service settings\ntitle = \"demo\"\nport = 8080\n\n[database]\nhosts = [\"a.example\", \"b.example\"] # primary first\n\n[database.pool]\nsize = 10\n",
  "value": {
    "title": "demo",
    "port": 8080,
    "database": {
      "hosts": [
        "a.example",
        "b.example"
      ],
      "pool": {
        "size": 10
      }
    }
  },
  "stats": {
    "tables": 2,
    "keys": 6,
    "arrays": 1,
    "depth": 3
  }
}
Same call as an MCP tools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "toml_format",
    "arguments": {
      "input": "# Service settings\ntitle=\"demo\"   \n  \"port\"   =  8080\n[database]\nhosts = [ \"a.example\", \"b.example\" ]  # primary first\n[database.pool]\nsize=10\n"
    }
  }
}

A duplicate key

curl -X POST 'https://util.caruana.me/api/toml/format' \
  -H 'content-type: application/json' \
  -d '{"input":"[server]\nhost = \"a\"\nport = 80\nhost = \"b\"\n"}'

Response 200

{
  "valid": false,
  "summary": {
    "valid": false,
    "errors": 1,
    "warnings": 0,
    "infos": 0
  },
  "diagnostics": [
    {
      "rule": "duplicate-keys",
      "severity": "error",
      "message": "Duplicate key host: it is already defined in this table.",
      "line": 4,
      "column": 1,
      "offset": 30,
      "endLine": 4,
      "endColumn": 5,
      "endOffset": 34
    }
  ],
  "formatted": null,
  "value": null,
  "stats": {
    "tables": 0,
    "keys": 0,
    "arrays": 0,
    "depth": 0
  }
}
Same call as an MCP tools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "toml_format",
    "arguments": {
      "input": "[server]\nhost = \"a\"\nport = 80\nhost = \"b\"\n"
    }
  }
}

Sorted keys and a split table

curl -X POST 'https://util.caruana.me/api/toml/format' \
  -H 'content-type: application/json' \
  -d '{"input":"[a.x]\nz = 1\ny = 2\n[b]\nk = true\n[a.y]\nk = 1979-05-27T07:32:00Z\n","sort_keys":true}'

Response 200

{
  "valid": true,
  "summary": {
    "valid": true,
    "errors": 0,
    "warnings": 0,
    "infos": 1
  },
  "diagnostics": [
    {
      "rule": "table-order",
      "severity": "info",
      "message": "[a.y] is separated from [a.x] on line 1 by other tables; both belong to a. Keep related tables together.",
      "line": 6,
      "column": 1,
      "offset": 31
    }
  ],
  "formatted": "[a.x]\ny = 2\nz = 1\n\n[b]\nk = true\n\n[a.y]\nk = 1979-05-27T07:32:00Z\n",
  "value": {
    "a": {
      "x": {
        "z": 1,
        "y": 2
      },
      "y": {
        "k": "1979-05-27T07:32:00.000Z"
      }
    },
    "b": {
      "k": true
    }
  },
  "stats": {
    "tables": 4,
    "keys": 8,
    "arrays": 0,
    "depth": 3
  }
}
Same call as an MCP tools/call
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "toml_format",
    "arguments": {
      "input": "[a.x]\nz = 1\ny = 2\n[b]\nk = true\n[a.y]\nk = 1979-05-27T07:32:00Z\n",
      "sort_keys": true
    }
  }
}