#json-query #query-engine #jmespath

jpx-engine

JMESPath query engine with introspection, discovery, and advanced features

10 releases

Uses new Rust 2024

new 0.3.4 Feb 28, 2026
0.3.3 Feb 24, 2026
0.2.0 Feb 3, 2026
0.1.3 Feb 3, 2026
0.1.2 Jan 19, 2026

#9 in #jmespath


Used in 3 crates

MIT/Apache

1.5MB
35K SLoC

jpx-engine

Protocol-agnostic JMESPath query engine with 400+ functions.

This crate provides the core "brain" of jpx - everything you can do with JMESPath beyond basic compile and evaluate. It's designed to be transport-agnostic, allowing the CLI (jpx), MCP server (jpx-mcp), or any future REST/gRPC adapters to be thin wrappers over this engine.

Features

Category Description
Evaluation Single, batch, and string-based evaluation with validation
Introspection List functions, search by keyword, describe, find similar
Discovery Cross-server tool discovery with BM25 search indexing
Query Store Named queries for session-scoped reuse
Configuration Declarative jpx.toml config with layered discovery and merge
JSON Utilities Format, diff, patch, merge, stats, paths, keys
Arrow Apache Arrow conversion (optional, via arrow feature)

Cargo Features

  • arrow - Enables Apache Arrow support for columnar data conversion. This adds the arrow module with functions to convert between Arrow RecordBatches and JSON Values. Used by the CLI for Parquet I/O.
  • let-expr - Enables let expression support (variable bindings in JMESPath expressions). Forwarded from jpx-core. Enabled by default.
  • schema - Derives JsonSchema on discovery types for JSON Schema generation. Used by the MCP server for tool schemas.

Quick Start

use jpx_engine::JpxEngine;
use serde_json::json;

let engine = JpxEngine::new();

// Evaluate a JMESPath expression
let result = engine.evaluate("users[*].name", &json!({
    "users": [{"name": "alice"}, {"name": "bob"}]
})).unwrap();
assert_eq!(result, json!(["alice", "bob"]));

Evaluation

The engine supports multiple evaluation modes:

use jpx_engine::JpxEngine;
use serde_json::json;

let engine = JpxEngine::new();

// From parsed JSON
let data = json!({"items": [1, 2, 3]});
let result = engine.evaluate("length(items)", &data).unwrap();
assert_eq!(result, json!(3));

// From JSON string
let result = engine.evaluate_str("length(@)", r#"[1, 2, 3]"#).unwrap();
assert_eq!(result, json!(3));

// Batch evaluation (multiple expressions, same input)
let exprs = vec!["a".to_string(), "b".to_string()];
let batch = engine.batch_evaluate(&exprs, &json!({"a": 1, "b": 2}));
assert_eq!(batch.results[0].result, Some(json!(1)));

// Validation without evaluation
let valid = engine.validate("users[*].name");
assert!(valid.valid);

Function Introspection

Discover and explore the 400+ available functions:

use jpx_engine::JpxEngine;

let engine = JpxEngine::new();

// List all categories
let categories = engine.categories();
assert!(categories.contains(&"String".to_string()));

// List functions in a category
let string_funcs = engine.functions(Some("String"));
assert!(string_funcs.iter().any(|f| f.name == "upper"));

// Search by keyword (fuzzy matching, synonyms)
let results = engine.search_functions("upper", 5);
assert!(results.iter().any(|r| r.function.name == "upper"));

// Get detailed function info
let info = engine.describe_function("upper").unwrap();
assert_eq!(info.category, "String");

// Find similar functions
let similar = engine.similar_functions("upper").unwrap();
assert!(!similar.same_category.is_empty());

JSON Utilities

Beyond JMESPath evaluation, the engine provides JSON manipulation tools:

use jpx_engine::JpxEngine;

let engine = JpxEngine::new();

// Pretty-print JSON
let formatted = engine.format_json(r#"{"a":1}"#, 2).unwrap();
assert!(formatted.contains('\n'));

// Generate JSON Patch (RFC 6902)
let patch = engine.diff(r#"{"a": 1}"#, r#"{"a": 2}"#).unwrap();

// Apply JSON Patch
let result = engine.patch(
    r#"{"a": 1}"#,
    r#"[{"op": "replace", "path": "/a", "value": 2}]"#
).unwrap();

// Apply JSON Merge Patch (RFC 7396)
let merged = engine.merge(
    r#"{"a": 1, "b": 2}"#,
    r#"{"b": 3, "c": 4}"#
).unwrap();

// Analyze JSON structure
let stats = engine.stats(r#"[1, 2, 3]"#).unwrap();
assert_eq!(stats.root_type, "array");

Query Store

Store and reuse named queries within a session:

use jpx_engine::JpxEngine;
use serde_json::json;

let engine = JpxEngine::new();

// Define a reusable query
engine.define_query(
    "active_users".to_string(),
    "users[?active].name".to_string(),
    Some("Get names of active users".to_string())
).unwrap();

// Run it by name
let data = json!({"users": [
    {"name": "alice", "active": true},
    {"name": "bob", "active": false}
]});
let result = engine.run_query("active_users", &data).unwrap();
assert_eq!(result, json!(["alice"]));

// List all stored queries
let queries = engine.list_queries().unwrap();
assert_eq!(queries.len(), 1);

Configuration

Load engine settings from jpx.toml files with layered discovery:

use jpx_engine::{JpxEngine, EngineConfig};

// Discover config from standard locations
// (~/.config/jpx/jpx.toml, ./jpx.toml, $JPX_CONFIG)
let config = EngineConfig::discover().unwrap();
let engine = JpxEngine::from_config(config).unwrap();

// Or use the builder for programmatic configuration
let engine = JpxEngine::builder()
    .strict(false)
    .disable_category("geo")
    .disable_function("env")
    .build()
    .unwrap();

Tool Discovery

Register and search tools across multiple servers (for MCP integration):

use jpx_engine::{JpxEngine, DiscoverySpec};
use serde_json::json;

let engine = JpxEngine::new();

// Register a server's tools
let spec: DiscoverySpec = serde_json::from_value(json!({
    "server": {"name": "my-server", "version": "1.0.0"},
    "tools": [
        {"name": "create_user", "description": "Create a new user", "tags": ["write"]}
    ]
})).unwrap();

let result = engine.register_discovery(spec, false).unwrap();
assert!(result.ok);

// Search across registered tools
let tools = engine.query_tools("user", 10).unwrap();
assert!(!tools.is_empty());

Strict Mode

For standard JMESPath compliance without extensions:

use jpx_engine::JpxEngine;
use serde_json::json;

let engine = JpxEngine::strict();
assert!(engine.is_strict());

// Standard functions work
let result = engine.evaluate("length(@)", &json!([1, 2, 3])).unwrap();
assert_eq!(result, json!(3));

// Extension functions are not available for evaluation
// (but introspection still works for documentation purposes)

Architecture

   jpx-core           (parser, runtime, 400+ functions, registry)
        |
   jpx-engine         (this crate - evaluation, search, discovery, config)
        |
   +----+----+
   |         |
  jpx    jpx-mcp     (CLI and MCP transport)

Thread Safety

The engine uses interior mutability (Arc<RwLock<...>>) for the discovery registry and query store, making it safe to share across threads. The function registry is immutable after construction.

Dependencies

~1.3–7MB
~135K SLoC