wire

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 20, 2026 License: MIT Imports: 8 Imported by: 0

README

gcx1 — Go reference encoder/decoder for the GCX1 wire format

GCX1 is a tab-delimited, line-oriented, round-trippable wire format for MCP (Model Context Protocol) tool responses. It is an opt-in alternative to JSON that saves a median −27.4% tiktoken tokens across 20 representative tool responses, with 100% round-trip integrity.

This package is the Go reference implementation. A TypeScript decoder ships as @gortex/wire on npm.

Install

go get github.com/zzet/gortex/pkg/wire

Go 1.25+. Zero runtime dependencies beyond the standard library.

Encode

import "github.com/zzet/gortex/pkg/wire"

h := wire.Header{
    Tool:   "search_symbols",
    Fields: []string{"id", "kind", "name", "path", "line"},
    Meta:   map[string]string{"total": "5", "truncated": "false"},
}

enc, err := wire.NewEncoder(w, h)
if err != nil { return err }

for _, row := range rows {
    if err := enc.WriteRow(row.ID, row.Kind, row.Name, row.Path, row.Line); err != nil {
        return err
    }
}

For tools without a hand-tuned encoder, use the generic fallback:

wire.EncodeAny(w, "my_tool", anyJSONShape)

Decode

dec := wire.NewDecoder(r)
h, err := dec.Header()
if err != nil { return err }

for {
    row, err := dec.NextRow()
    if err == io.EOF { break }
    if err != nil { return err }
    // row is map[string]string keyed by h.Fields
}

Multi-section payloads are supported: call dec.NextSection() after NextRow returns io.EOF to advance to the next header.

Round-trip guarantee

Every GCX1 payload decodes back to an equivalent JSON value. Rerun the benchmark (go run ./bench/wire-format from the parent repo) to verify 20/20 round-trip integrity on the reference fixture set.

Versioning

The GCX1 header prefix is stable for the lifetime of major version 1. Field layouts for declared tools are frozen within GCX1. Additions ship as GCX2.

Documentation

Overview

Package wire implements the GCX1 compact wire format for Gortex MCP tool responses. GCX1 is a tab-delimited, line-oriented, round-trippable format with a self-describing header. See docs/wire-format.md.

Index

Constants

View Source
const CommentPrefix = "#"

CommentPrefix marks human-readable comment lines. Comments have no effect on decoding and may be dropped by intermediaries.

View Source
const FieldSep = "\t"

FieldSep is the row column delimiter. Tab never appears in code symbol names and is treated as whitespace by GPT tokenizers.

View Source
const RowSep = "\n"

RowSep terminates a row. Newline is also tokenizer-friendly and survives transports that re-flow whitespace but preserve line breaks.

View Source
const Version = 1

Version is the current GCX protocol version. Bump on any backward-incompatible change to the header syntax or escape rules.

Variables

View Source
var Tag = fmt.Sprintf("GCX%d", Version)

Tag is the four-byte header magic that starts every GCX payload. It is a literal prefix ("GCX" + Version as a decimal digit); the decoder rejects anything that does not match.

Functions

func EncodeAny

func EncodeAny(w io.Writer, tool string, v any) error

EncodeAny writes v to w as a GCX1 payload using a generic shape-inference encoder. It is the fallback used when no hand-tuned encoder has been registered for the tool. Supported shapes:

  • A JSON object ("record") becomes a single header + single row where fields are the top-level keys (sorted alphabetically) and values are their scalar representations. Nested objects / arrays are JSON-serialised into the cell.

  • A JSON array of objects ("rows") becomes a header with the union of top-level keys across elements (sorted) and one row per element.

  • A JSON array of scalars becomes a header with a single "value" field and one row per element.

  • A scalar value becomes a header with a single "value" field and one row.

Anything else is rejected so an unencodable shape cannot silently degrade the wire format. Callers should prefer hand-tuned encoders for hot-path tools; the generic fallback exists for breadth.

Types

type Decoder

type Decoder struct {
	// contains filtered or unexported fields
}

Decoder reads a GCX1 payload. It parses the header on first call to Header() (or implicitly on the first NextRow()), then streams rows as ordered field maps. Comments and blank lines are skipped.

Decoder is not safe for concurrent use.

Multi-section payloads are supported: after NextRow returns io.EOF, callers can invoke NextSection to read the next header if the stream is not exhausted.

func NewDecoder

func NewDecoder(r io.Reader) *Decoder

NewDecoder wraps r in a bufio.Scanner with a buffer large enough for representative tool responses. The default Scanner line limit (64 KiB) is too small for payloads such as get_symbol_source on large functions; we raise it to 4 MiB. Payloads exceeding that cap should be streamed on the sender side instead.

func (*Decoder) All

func (d *Decoder) All() ([]map[string]string, error)

All consumes the stream and returns every row as a slice. Convenient for tests and small responses; prefer NextRow for streaming.

func (*Decoder) Header

func (d *Decoder) Header() (Header, error)

Header returns the parsed header, reading from the underlying reader if it has not been parsed yet.

func (*Decoder) NextRow

func (d *Decoder) NextRow() (map[string]string, error)

NextRow returns the next data row as a map keyed by the declared field names. It returns (nil, io.EOF) when the current section ends (either at end of stream or when the next header line is encountered). Comment lines and blank lines are skipped. Rows with fewer values than declared fields fill missing keys with "". Rows with more values than declared fields return an error.

func (*Decoder) NextSection

func (d *Decoder) NextSection() (Header, error)

NextSection advances to the next logical payload and returns its header. Callers invoke NextSection after NextRow returns io.EOF to check whether the stream contains additional sections. It returns io.EOF when no further section exists.

type Encoder

type Encoder struct {
	// contains filtered or unexported fields
}

Encoder writes a GCX1 payload. It emits the header immediately on construction so callers can start streaming rows without a separate Start() call. Row values are stringified with fmt.Sprint-compatible rules (bool → "true"/"false", int → decimal, float → %g, nil → "").

Encoder is not safe for concurrent use.

func NewEncoder

func NewEncoder(w io.Writer, h Header) *Encoder

NewEncoder writes the header for h to w and returns a ready encoder. Any error from writing the header is sticky: subsequent WriteRow and WriteComment calls return it without attempting further I/O.

func (*Encoder) Close

func (e *Encoder) Close() error

Close is a no-op retained for symmetry with bufio-style encoders and to let callers evolve to a buffering impl without churn.

func (*Encoder) Fields

func (e *Encoder) Fields() []string

Fields returns the declared field order.

func (*Encoder) Tool

func (e *Encoder) Tool() string

Tool returns the tool name declared in the header.

func (*Encoder) WriteComment

func (e *Encoder) WriteComment(s string) error

WriteComment writes a "# ..." line. Intermediate newlines in s are escaped so the comment stays on one physical line.

func (*Encoder) WriteRow

func (e *Encoder) WriteRow(values ...any) error

WriteRow writes one record. The number of values must match the declared field count. Pass fewer for trailing empty columns; excess values are an error.

type Format

type Format int

Format selects the output encoding for an MCP tool response.

const (
	// FormatJSON is the default MCP response encoding.
	FormatJSON Format = iota
	// FormatGCX is the GCX1 compact round-trippable wire format.
	FormatGCX
	// FormatText is the legacy one-line-per-result text output
	// previously selected by `compact: true`. Lossy.
	FormatText
)

func ParseFormat

func ParseFormat(s string) Format

ParseFormat maps an MCP tool-argument string to a Format. Unknown values return FormatJSON to stay safe. The caller should separately honour the deprecated `compact: true` boolean by mapping it to FormatText.

func (Format) String

func (f Format) String() string
type Header struct {
	Version int               // always Version for now
	Tool    string            // MCP tool name
	Fields  []string          // declared column order for the row stream
	Meta    map[string]string // additional k=v pairs (rows=, ms=, ...)
}

Header is the first line of a GCX1 payload. It declares the tool that produced the payload, the field order of the row stream, and optional free-form metadata.

Wire layout:

GCX1 tool=<tool> fields=<f1>,<f2>,... [k=v]...

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL