#json-query #query-engine #jmespath

jpx-engine

JMESPath query engine with introspection, discovery, and advanced features

9 releases

Uses new Rust 2024

new 0.3.3 Feb 24, 2026
0.3.2 Feb 11, 2026
0.2.0 Feb 3, 2026
0.1.3 Feb 3, 2026
0.1.2 Jan 19, 2026

#2584 in Parser implementations


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.2–7MB
~136K SLoC