Skip to content

feat(server/mcp): add opt-in GCF response encoding for tool results - #3833

Open
blackwell-systems wants to merge 2 commits into
googleapis:mainfrom
blackwell-systems:feat/response-encoding-gcf
Open

feat(server/mcp): add opt-in GCF response encoding for tool results#3833
blackwell-systems wants to merge 2 commits into
googleapis:mainfrom
blackwell-systems:feat/response-encoding-gcf

Conversation

@blackwell-systems

@blackwell-systems blackwell-systems commented Aug 16, 2026

Copy link
Copy Markdown

Description

Toolbox returns each tool's result set as one JSON object per row. For the uniform record arrays these tools return (query rows, table listings, schema descriptions), the field names repeat in full on every row, and that repetition dominates the token cost once the result crosses the LLM boundary.

This PR adds an opt-in --response-encoding flag (json default, or gcf). When set to gcf, a result set is returned as a single Graph Compact Format generic-profile block instead of N JSON blocks: the repeated field names are factored into one header and the per-row framing is dropped. On a representative 30-row × 7-column result (a Cloud SQL instance listing) this is 30.1% fewer tokens (GPT-4o/o200k) and 31.3% fewer (Claude), losslessly.

The change is at a single shared seam and covers every data-returning tool with no per-tool changes. JSON remains the default and is completely unaffected.

What this adds

  • --response-encoding flag — a validated enum (json / gcf) mirroring the existing --logging-format flag, plumbed exactly like --sql-commenter (flag → ServerConfigServer → request context).
  • internal/util/gcf.goEncodeGCFToolResult, which converts each orderedmap.Row to a GCF ordered map (preserving query column order) and encodes the set.
  • The five MCP protocol handlers gate their per-row serialization loop on the context flag: one GCF block when enabled and beneficial, the existing per-row JSON otherwise.
  • Dependency: github.com/blackwell-systems/gcf-gozero runtime dependencies, MIT-licensed.

Conservative by construction — it can only help

GCF is offered only when all of the following hold; otherwise the original JSON is returned unchanged:

  1. It encodes without error (EncodeGenericChecked, which surfaces the numeric-domain error rather than panicking).
  2. It is strictly smaller than the JSON it would replace (a never-grow guard, so a small result is never enlarged — the JSON is kept).
  3. It decodes back to the same rows — a lossless round-trip verified against the input (order-aware on columns, integer-exact). A value GCF cannot carry exactly (e.g. a byte blob) falls back to JSON rather than being altered.

So enabling gcf can never grow, drop, or garble a tool result. Integers beyond the float64-safe range round-trip exactly.

Benchmark (honest floor and scale)

Measured on real result shapes, lossless in every case:

Result shape JSON tokens (o200k) GCF tokens (o200k) Change
Tiny result (few rows/cols) JSON kept (never-grow declines it)
30-row × 7-col instance listing 1,810 1,266 −30.1%

The saving grows with row count and column count (more repeated field names to factor). On tiny or high-entropy results GCF does not win, and the never-grow guard keeps the JSON — the feature has a floor of "no worse than today," not a regression.

About GCF

Graph Compact Format is a compact, lossless wire format for structured data crossing the model boundary. It is Apache-2.0/MIT-compatible (MIT), zero-dependency, and implemented across seven languages, all validated against a shared conformance suite (281 fixtures, 43B+ verified round-trips).

  • Comprehension, not just tokens. Fewer tokens only helps if the model reads the format at least as well. On the standard-workload comprehension study most relevant here — 500-order nested arrays (the shape of a tool result), 27 runs across 11 models / 4 providers, deterministic answers with no LLM judge — GCF's generic profile is read at 100% accuracy on every frontier model (Claude Opus/Sonnet/Haiku, GPT-5.5, Gemini 2.5/3.1/3.5), averages equal-or-better than JSON on every model, and pulls ahead of both JSON and TOON on smaller models (e.g. GCF 95% vs JSON 74% vs TOON 85% on Gemini 2.5 Flash). TOON is consistently the weakest. Full methodology and reproducible harness: gcformat.com/guide/benchmarks.

  • Grounded in original research. The format was derived from tokenization/attention research (Tokenizer–Attention Coupling and related work), then validated after the fact by controlled from-scratch training studies. See gcformat.com.

  • Already in adoption. GCF compresses LLM-bound output in a range of shipped MCP servers and gateways:

    • Chrome DevTools MCP — built by the Google Chrome DevTools team (the most-starred MCP server on GitHub); GCF is a merged, opt-in data format there.
    • OmniRoute — a 40K+ star AI gateway that vendored GCF's encoder directly into its compression engine.
    • Speakeasy — OpenAPI tooling (customers include Google, Verizon, and Mistral); oq --format gcf, merged after a dependency audit.
    • NetClaw — a network-automation platform that replaced TOON with GCF across all its MCP servers.
    • Elasticsearch MCP server and Wazuh MCP server — search and SIEM servers, both merged via an opt-in RESPONSE_FORMAT=gcf.

    Full list: gcformat.com/ecosystem/adopters.

Design notes

  • Opt-in and additive. No behavior changes unless --response-encoding=gcf is set. The JSON path is byte-for-byte unchanged.
  • Single seam. The gate lives at the shared per-row serialization point in each protocol handler, so it covers Postgres, MySQL, BigQuery, AlloyDB, and every other source with no per-source or per-tool code.
  • Column order preserved. DB column order is semantically meaningful and is carried through end to end.
  • Pinned dependency, zero transitive additions.

Tests

  • internal/util/gcf_test.go — round-trip + header factoring + column-order preservation, the never-grow fallback, int64 > 2⁵³ exactness, and a byte-column fail-safe.
  • internal/server/config_test.go — the --response-encoding enum (accepts json/gcf case-insensitively, rejects invalid values, defaults to json).
  • internal/server/mcp/v20250618/method_test.go — handler-level gating: default encoding returns per-row JSON blocks; gcf returns a single GCF block.
  • tests/sqlite/sqlite_integration_test.go — an end-to-end integration test: boots the server with --response-encoding=gcf against a real SQLite source and asserts a query tool's result comes back as a single GCF block over the MCP transport.

go build, go vet, gofmt, and golangci-lint are clean; the affected test packages pass.

Docs

  • --response-encoding added to the CLI reference flags table.
  • A "Response Encoding" section under Additional Features in the README.
  • An FAQ entry ("How do I reduce the token cost of large tool results?").

PR Checklist

  • Ensure you have manually reviewed the entire diff before requesting a review
  • Ensure the tests and linter pass
  • Code coverage does not decrease (if any source code was changed)
  • Appropriate docs were updated (if necessary)

@blackwell-systems
blackwell-systems requested a review from a team as a code owner August 16, 2026 17:49
Adds a `--response-encoding` flag (`json` default / `gcf`) that, when set to
`gcf`, returns each tool's result set as a single Graph Compact Format
(https://gcformat.com) generic-profile block instead of one JSON block per row.
The repeated field names of the uniform record arrays these tools return are
factored into a single header, cutting token cost on large query results (~30%
fewer o200k/Claude tokens on a 30-row instance list) with no per-tool changes.

The substitution is conservative, so enabling it can only help: GCF is used at
the shared per-row serialization point in the five protocol handlers only when
it encodes without error, is strictly smaller than the JSON (never-grow), and
decodes back to the same rows (lossless, verified against the input and
order-aware on columns); otherwise the JSON is kept. Integers are preserved
exactly via EncodeGenericChecked, and a column type GCF cannot round-trip
(e.g. a byte blob) falls back to JSON rather than being altered.

- `--response-encoding` is a validated enum, mirroring `--logging-format`.
- Plumbing mirrors `--sql-commenter`: flag -> ServerConfig -> Server -> context.
- internal/util/gcf.go: EncodeGCFToolResult converts orderedmap.Row ->
  gcf.OrderedMap (preserving column order) and applies the guards.
- Dependency: github.com/blackwell-systems/gcf-go v1.7.1 (zero-dependency).
- Tests: util round-trip / order / never-grow / int64 / byte-safety, config
  enum validation, and a handler-level json-vs-gcf gating test.
@blackwell-systems
blackwell-systems force-pushed the feat/response-encoding-gcf branch from 73fcad3 to a267d80 Compare August 16, 2026 17:50

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for Graph Compact Format (GCF) response encoding via the --response-encoding=gcf flag, which reduces the token cost of large, uniform tool results by factoring repeated field names into a single header. The changes include CLI flag registration, documentation updates, MCP handler integrations across multiple protocol versions, and comprehensive unit and integration tests. Feedback on the implementation highlights a precision loss bug in scalarEqual when comparing large int64 values with float64 values, which could compromise the lossless round-trip validation. Additionally, it is recommended to normalize the response encoding string to lowercase during validation in the Set method to avoid potential comparison mismatches.

Comment thread internal/util/gcf.go
Comment thread internal/server/config.go
… guard

Addresses review feedback on the response-encoding PR:

- scalarEqual no longer collapses a large int64 to a lossy float64 when comparing
  mixed numeric types. A value beyond the +/-2^53 float-exact range can no longer
  be falsely reported equal to a rounded float64, which would let a lossy encoding
  slip past the losslessness guard. Integers compare exactly as int64; an integer
  and a float compare equal only when the float represents the integer exactly.
- responseEncoding.Set stores the lowercased value, so a flag given as GCF matches
  the downstream "gcf" comparison directly rather than relying on String() to
  normalize it.

Adds tests for both.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants