-
Notifications
You must be signed in to change notification settings - Fork 20
[go-fan] Go Module Review: itchyny/gojqΒ #2985
Description
πΉ Go Fan Report: itchyny/gojq
Module Overview
github.com/itchyny/gojq is a pure Go implementation of jq β the lightweight, flexible JSON processor. Unlike shelling out to the jq binary, gojq embeds the full jq engine as a library, enabling safe Go-native JSON transformation with context cancellation, custom functions, and arbitrary-precision integers.
Version in use: v0.12.18 β
(latest)
Current Usage in gh-aw-mcpg
The module is used exclusively in the jq schema middleware that intercepts large MCP tool responses and replaces them with compact schema + preview + filesystem path metadata.
- Files: 2 production files + 2 test files (all in
internal/middleware/) - Import Count: 2 production imports (
jqschema.go,jqschema_bench_test.go) - Key APIs Used:
gojq.Parse()β parse the schema filter stringgojq.Compile()β compile to bytecode at startup(*gojq.Code).RunWithContext()β execute with timeout/cancellationiter.Next()β consume results*gojq.HaltErrorβ distinguish clean halts from errors
The middleware applies a custom jq walk filter that recursively replaces JSON values with their type names (e.g., "test" β "string", 42 β "number"), keeping only the first array element for schema inference.
Research Findings
What's Done Well π
The usage is already quite idiomatic and makes use of key gojq best practices:
- Pre-compile at
init()β The filter is parsed and compiled once at startup, giving 10β100Γ speedup vs re-parsing per request (documented with benchmarks injqschema_bench_test.go). - Context-aware execution β Uses
RunWithContextwith a 5-second default timeout, preventing hangs on malformed queries or deeply nested payloads. HaltErrorhandling β Correctly distinguishesHaltError{Value: nil}(clean halt) from error halts, including exit code reporting.- Thread-safe usage β
*gojq.Codeis safe for concurrent use; the pre-compiled global is correctly shared across goroutines.
Recent Updates (v0.12.18)
- Array element limit raised to 2^29 (536,870,912) elements
- Improved concurrent execution performance (directly benefits the pre-compiled global pattern)
- Enhanced type error messages (referenced in
HaltErrorhandling comments)
Best Practices from Maintainers
- Compile once with
gojq.Compile, run many times β β already done - Use
RunWithContextfor cancellation β β already done - Use
gojq.WithFunctionsfor Go-native extensions β π² not yet leveraged - Use
gojq.WithVariablesfor parameterized filters β π² not yet leveraged
Improvement Opportunities
π Quick Wins
1. Deduplicate identical test helper functions
payloadMetadataToMap in jqschema_test.go and integrationPayloadMetadataToMap in jqschema_integration_test.go are byte-for-byte identical functions living in the same package middleware. One can be removed and tests updated to use the single shared version.
2. UTF-8-safe preview truncation
// Current β slices at byte boundary
preview = string(payloadJSON[:PayloadPreviewSize]) + "..."Since JSON payload bytes are virtually always ASCII-compatible (gojq outputs \uXXXX for non-ASCII), this is safe in practice. But a comment or a utf8-aware boundary check (using strings.LastIndexByte or utf8.ValidString) would make the intent explicit and defensive.
3. Document why the iterator is consumed only once
After iter.Next(), the iterator is not drained. This is correct (gojq is lazy, the filter produces exactly one result), but a brief comment explaining the single-result contract would help future maintainers.
β¨ Feature Opportunities
1. gojq.WithFunctions for richer schema inference
Currently, all numeric values map to "number". Using custom Go functions, the schema could distinguish integers from floats:
gojq.Compile(query, gojq.WithFunctions("isinteger", 0, 0, func(v interface{}) interface{} {
switch v.(type) {
case int, int64, gojq.Number:
return true
}
return false
}))This would enable schemas like {"id": "integer", "price": "float"} β more useful for downstream consumers.
2. Schema merging for heterogeneous arrays
The current filter takes only the first element of each array for schema inference:
elif type == "array" then
if length == 0 then [] else [.[0] | walk(f)] endFor heterogeneous arrays (mixed types), this misses variation. A reduce-based merge would produce more comprehensive type signatures. Worth considering for deeply varied API responses.
3. gojq.WithVariables for a configurable filter
Hard-coded constants like array depth or preview behavior could be exposed as jq variables, enabling runtime configuration without recompilation:
gojq.Compile(query, gojq.WithVariables([]string{"$maxArrayElements"}))
// then:
jqSchemaCode.RunWithContext(ctx, data, maxArrayElements)π Best Practice Alignment
1. Explain the custom walk vs built-in
The code defines its own walk function rather than using gojq's built-in walk(f). This is intentional and correct β the custom walk replaces leaf values with type names AND collapses arrays to one element β behaviors incompatible with standard walk(f) semantics. A comment in the filter string explaining why the custom implementation is needed would prevent well-meaning future simplifications that would break functionality.
2. Expose compile error for health checks
jqSchemaCompileErr is unexported. A small exported HealthCheck() error (or inclusion in the gateway's /health endpoint) would surface this startup failure to monitoring systems before the first tool call fails.
π§ General Improvements
1. Avoid double JSON round-trip for native Go types
In WrapToolHandler, the code does:
payloadJSON, _ := json.Marshal(data) // struct β JSON bytes
// ...
json.Unmarshal(payloadJSON, &jsonData) // JSON bytes β map[string]interface{}If data is already map[string]interface{} or []interface{}, the unmarshal is redundant. A type-switch before the unmarshal step could skip it for native types, saving allocations on hot paths.
Recommendations
| Priority | Action |
|---|---|
| π’ Low | Remove duplicate integrationPayloadMetadataToMap test helper |
| π’ Low | Add comment explaining custom walk vs built-in |
| π‘ Medium | Add UTF-8 boundary comment/assertion for preview truncation |
| π‘ Medium | Type-switch optimization in WrapToolHandler to skip redundant unmarshal |
| π΅ Future | gojq.WithFunctions for integer/float distinction in schema |
| π΅ Future | Schema merging across heterogeneous array elements |
| π΅ Future | Expose jqSchemaCompileErr via health check endpoint |
Next Steps
- Remove
integrationPayloadMetadataToMapand use the sharedpayloadMetadataToMapacross both test files - Add a comment block in
jqSchemaFilterexplaining why a customwalkis used instead of the built-in - Consider a
gojq.WithFunctions-based approach for richer schema typing in a future enhancement
Generated by Go Fan πΉ
Module summary saved to: specs/mods/gojq.md (in cache)
Run ID: Β§23837524610
Note
π Integrity filter blocked 12 items
The following items were blocked because they don't meet the GitHub integrity level.
- BurntSushi/toml@8685fba
get_commit: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved". - golang/term@9d2dc07
get_commit: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved". - stretchr/testify@5f80e4a
get_commit: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved". - santhosh-tekuri/jsonschema@d3bf053
get_commit: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved". - itchyny/gojq@183cbec
get_commit: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved". - spf13/cobra@61968e8
get_commit: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved". - https://github.com/BurntSushi/toml/releases/tag/v1.6.0
get_latest_release: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved". - https://github.com/spf13/cobra/releases/tag/v1.10.2
get_latest_release: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved". - https://github.com/stretchr/testify/releases/tag/v1.11.1
get_latest_release: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved". - https://github.com/santhosh-tekuri/jsonschema/releases/tag/v6.0.2
get_latest_release: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved". - https://github.com/itchyny/gojq/releases/tag/v0.12.18
get_latest_release: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved". - get_file_contents
get_file_contents: has lower integrity than agent requires. The agent cannot read data with integrity below "unapproved".
To allow these resources, lower min-integrity in your GitHub frontmatter:
tools:
github:
min-integrity: approved # merged | approved | unapproved | none
- expires on Apr 8, 2026, 7:44 AM UTC