LocalizeMcp.TermGrammar (Localize MCP v0.1.0)

View Source

JSON ↔ Elixir term grammar used by localize_invoke.

MCP transports carry JSON. Elixir functions take richly typed arguments — atoms, tuples, structs, dates, decimals — that don't exist in JSON. This module defines a small extension grammar so the agent can encode any term it needs.

Most JSON values map straight through:

| JSON           | Elixir                       |
| -------------- | ---------------------------- |
| null           | nil                          |
| true / false   | true / false                 |
| number         | integer or float             |
| string         | binary                       |
| array          | list                         |
| object         | map with string keys         |

Elixir-only terms use a tagged object with a leading "$kind" key:

| Term                              | Encoded                                                                 |
| --------------------------------- | ----------------------------------------------------------------------- |
| `:foo`                            | `{"$atom": "foo"}`                                                       |
| `~D[2024-05-13]`                  | `{"$date": "2024-05-13"}`                                                |
| `~T[12:34:56]`                    | `{"$time": "12:34:56"}`                                                  |
| `~U[2024-05-13T12:34:56Z]`        | `{"$datetime": "2024-05-13T12:34:56Z"}`                                  |
| `~N[2024-05-13T12:34:56]`         | `{"$naive_datetime": "2024-05-13T12:34:56"}`                             |
| `Decimal.new("3.14")`             | `{"$decimal": "3.14"}`                                                   |
| `{1, "a", :b}`                    | `{"$tuple": [1, "a", {"$atom": "b"}]}`                                   |
| `[locale: :en, format: :short]`   | `{"$keyword": [["locale", {"$atom": "en"}], ["format", {"$atom": "short"}]]}` |
| `%MyStruct{a: 1}`                 | `{"$struct": "MyStruct", "fields": {"a": 1}}`                            |

Round-trip is exact for every shape listed above. Anything else (PIDs, references, ports, funs) is not invocable through the MCP surface by design and returns {:error, :unsupported_term}.

Summary

Functions

Decode a JSON value (as produced by :json.decode/1 or Jason.decode/1) into the corresponding Elixir term.

Encode an Elixir term back into a JSON-friendly tree (i.e. one that :json.encode/1 accepts).

Returns a static reference describing the grammar — the same content as the moduledoc, formatted for the localize_term_grammar tool response.

Functions

decode(input)

@spec decode(term()) :: {:ok, term()} | {:error, term()}

Decode a JSON value (as produced by :json.decode/1 or Jason.decode/1) into the corresponding Elixir term.

Returns {:ok, term} or {:error, reason}. Imposes a 16 KB cap on the encoded form to bound the cost of a malicious payload.

encode(n)

@spec encode(term()) :: nil | boolean() | number() | binary() | [term()] | map()

Encode an Elixir term back into a JSON-friendly tree (i.e. one that :json.encode/1 accepts).

Symmetric with decode/1 over the supported shapes.

reference()

@spec reference() :: %{
  summary: String.t(),
  passthrough: [String.t()],
  tagged_forms: [map()],
  limits: %{max_input_bytes: pos_integer()}
}

Returns a static reference describing the grammar — the same content as the moduledoc, formatted for the localize_term_grammar tool response.