ConduitMcp.Reflect (ConduitMCP v0.11.0)

Copy Markdown View Source

The single boundary helper for client-supplied text that is echoed back to the client or written to the log.

Three separate hazards, one function:

  • Type. Interpolating a non-binary — protocolVersion: {}, a requestId object — raises inside the handler, and the surrounding rescue turns a client mistake into a misleading "Internal server error".
  • Size. A 1 MB string sits comfortably inside the transports' length: 1_000_000 parser cap, and an unclamped echo reflects it in full into both the error message and the log line.
  • Control characters. An unfiltered echo of "tools/call\x00\x01\x02" reaches the operator's log with terminal escapes and NULs intact.

text/2 applies to_string/1, strips control characters (C0/C1, DEL, zero-width and bidi controls, and the Unicode line separators — keeping nothing, not even tabs or newlines, which is what lets a value forge log lines), and clamps both the byte length and the grapheme length. Both clamps are needed: a grapheme cluster is unbounded in size, so one base character plus 400 000 combining marks is a single grapheme and ~800 KB of output.

Summary

Functions

Renders value as text that is safe to put in an error message or a log line: coerced to a string, control characters removed, clamped to max graphemes and to a hard byte ceiling (default 200).

Functions

text(value, max \\ 200)

@spec text(term(), pos_integer()) :: String.t()

Renders value as text that is safe to put in an error message or a log line: coerced to a string, control characters removed, clamped to max graphemes and to a hard byte ceiling (default 200).

Examples

iex> ConduitMcp.Reflect.text("tools/call")
"tools/call"

iex> ConduitMcp.Reflect.text("tools/call\x00\x01")
"tools/call"

iex> ConduitMcp.Reflect.text(%{})
"%{}"

iex> ConduitMcp.Reflect.text(String.duplicate("a", 500)) |> String.length()
200