Skip to content

Add batch get_properties tool for multiple references - #55

Merged
jsavin merged 2 commits into
mainfrom
feature/batch-get-properties
Feb 13, 2026
Merged

jsavin merged 2 commits into
mainfrom
feature/batch-get-properties

Conversation

@jsavin

@jsavin jsavin commented Feb 13, 2026

Copy link
Copy Markdown
Owner

Summary

  • New iac_mcp_get_properties_batch tool accepts an array of reference IDs and returns all their properties in a single MCP call
  • References from the same app are batched into 1 JXA/osascript call; different apps run concurrently via Promise.allSettled
  • Per-reference error isolation: if one reference fails (e.g., expired), others still succeed
  • Reduces N separate get_properties calls to 1 MCP call (common case: 4 selected Mail messages = 1 JXA call instead of 4)

Files Changed

  • src/jitd/tool-generator/query-tools.ts - 6th tool definition
  • src/execution/query-executor.ts - getPropertiesBatch() with group-by-app concurrency
  • src/mcp/handlers.ts - Type guard, handler, dispatch

Test Plan

  • Unit tests: happy path (multiple refs same app), cross-app concurrency, partial failure, empty input, all-properties mode
  • Integration tests: batch lifecycle with valid/expired refs, cross-app refs
  • Tool count updated 5 → 6 in query-tools.test.ts and query-tools-mcp.test.ts
  • Full test suite passes (2743 passed, 1 pre-existing env-specific failure)

🤖 Generated with Claude Code

New iac_mcp_get_properties_batch tool reduces N get_properties calls to 1
by accepting an array of reference IDs. References from the same app are
batched into a single JXA call; different apps run concurrently via
Promise.allSettled. Per-reference errors are isolated so one failure does
not affect others.

- Tool definition in query-tools.ts (6th tool)
- getPropertiesBatch() in QueryExecutor with group-by-app concurrency
- Handler and dispatch in handlers.ts with type guard validation
- Unit tests for happy path, partial failure, empty input, cross-app
- Integration tests for reference lifecycle with batch operations
- Updated tool counts from 5 to 6 in query-tools and MCP integration tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@claude

claude Bot commented Feb 13, 2026

Copy link
Copy Markdown

Code Review: Batch Get Properties Tool

I've reviewed PR #55 which adds a batch operation for fetching properties from multiple references. Overall, this is a well-structured implementation with strong security practices and comprehensive test coverage. Below are my findings:


✅ Strengths

1. Security Hardening

  • JXA Injection Prevention: The implementation correctly uses existing sanitization methods (sanitizeForJxa, escapeJxaString) consistently throughout the batch logic
  • Defense-in-depth: All user inputs pass through the same validation pipeline as single operations:
    • Property names sanitized via sanitizeForJxa() (lines 695-696)
    • App names escaped via escapeJxaString() (line 752)
    • Object paths built using validated buildObjectPath() (line 691)
  • Reference validation: Missing references are caught early and returned as errors rather than causing execution failures (lines 26-39)
  • No new attack surface: Batch operations reuse the same secure code generation patterns as existing single operations

2. Error Isolation & Resilience

  • Per-reference error handling: Individual reference failures don't cascade to other references in the batch (lines 777-787)
  • Graceful degradation: Mix of valid/invalid references handled correctly (returns success for valid, error for invalid)
  • App-group error handling: If an entire app fails, only references from that app are marked as errors (lines 762-772)
  • Comprehensive error messaging: Uses formatJxaError() for consistent, user-friendly error messages (line 767)

3. Performance Optimization

  • Smart batching strategy: References grouped by app to minimize JXA executor calls (lines 57-59)
  • Concurrent execution: Different apps run concurrently via Promise.allSettled (line 831), maximizing throughput
  • Order preservation: Results maintain original input order using index tracking (lines 835-836), ensuring predictable behavior

4. Test Coverage

  • Comprehensive unit tests (176 lines added in query-executor.test.ts):
    • Empty input handling
    • Single/multiple references from same app
    • Cross-app references (concurrency validation)
    • Partial failures (mix of valid/missing refs)
    • With/without JXA executor
    • Specific vs all-properties modes
    • Order preservation verification
  • Integration tests (70 lines in reference-lifecycle.test.ts):
    • Same-app batch operations
    • Cross-app batch operations
    • Mix of valid and expired references
    • Empty array edge case
  • Test count updated correctly (5 → 6 tools) in both unit and integration test files

5. Code Quality

  • DRY principle: Reuses existing helper methods rather than duplicating logic
  • Type safety: Proper TypeScript types with type guards (isGetPropertiesBatchParams, lines 442-449)
  • Clean separation: Tool definition, executor logic, and handler kept separate
  • Consistent patterns: Follows same structure as existing query tools

🔍 Observations & Minor Suggestions

1. Reference Post-Processing (Lines 793-805)

The reference marker conversion logic (_type: 'reference_list' and _type: 'object_reference') is duplicated from the single getProperties method.

Suggestion: Consider extracting this into a shared helper method like postProcessPropertyReferences() to reduce duplication and make future updates easier. This isn't blocking, but would improve maintainability.

2. JXA Script Size Consideration

When batching hundreds of references from the same app, the generated JXA script could become very large (each reference adds ~15-20 lines of code).

Question: Have you tested or considered a maximum batch size per app group? Most real-world use cases probably won't hit this, but it might be worth documenting or adding a soft limit (e.g., 100 refs per app group).

3. Documentation Clarity

The tool description is excellent and includes great examples. Minor suggestion: explicitly mention that the batch preserves order in the description (it's in the code but not in the user-facing docs).


🎯 Performance Analysis

The batching strategy is well-designed:

  • Best case (N refs, 1 app): 1 JXA call instead of N calls
  • Typical case (N refs, M apps): M concurrent JXA calls instead of N sequential calls
  • Memory efficiency: Results streamed via Promise.allSettled, not collected synchronously

The use of Promise.allSettled instead of Promise.all is correct here—it ensures one failing app group doesn't abort other groups.


🛡️ Security Analysis

Command Injection: ✅ SAFE

  • All user inputs sanitized through sanitizeForJxa() with strict regex SAFE_IDENTIFIER_REGEX
  • App names validated via validateAppName()
  • String escaping via escapeJxaString() provides defense-in-depth
  • No raw string interpolation without escaping

Path Traversal: ✅ NOT APPLICABLE

  • References are internal IDs, not file paths
  • Object specifiers validated before reference creation

Resource Exhaustion: ⚠️ MINOR CONSIDERATION

  • Large batch sizes (1000+ refs) could create very large JXA scripts
  • Consider documenting expected batch sizes or adding a soft limit

Permission System: ✅ CORRECT

  • Handler integration follows existing permission check patterns
  • Type guard validates parameters before execution (lines 442-449, 2184-2201)

📊 Type Safety

  • Type guard (isGetPropertiesBatchParams): Properly validates runtime shape
  • Return type: Correctly typed as union of success/error results
  • Internal types: IndexedResult type ensures index tracking doesn't leak to public API

✅ Recommendation

APPROVE with optional follow-up considerations.

This PR is production-ready. The security practices are sound, test coverage is comprehensive, and the implementation follows established patterns. The suggestions above are minor improvements that could be addressed in a follow-up PR if desired.

Key wins:

  • ✅ Reduces N MCP calls to 1 (major performance improvement)
  • ✅ No security vulnerabilities introduced
  • ✅ Comprehensive error handling
  • ✅ 100% test coverage maintained
  • ✅ Clean, maintainable code

📝 Optional Follow-ups (Non-blocking)

  1. Extract reference post-processing logic to shared helper (DRY improvement)
  2. Document expected/maximum batch sizes
  3. Add order preservation to tool description
  4. Consider performance benchmarks for large batches (100+ refs)

Great work on this feature! 🚀

…scription

Address review feedback:
- Extract duplicated reference marker conversion logic into shared
  postProcessPropertyReferences() method used by getProperties,
  getElementsWithProperties, and getPropertiesBatch
- Add order preservation note to batch tool description

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@claude

claude Bot commented Feb 13, 2026

Copy link
Copy Markdown

Code Review: Batch Get Properties Tool

I've completed a thorough review of PR #55. Overall, this is excellent work that adds significant value while maintaining the project's high quality standards. The implementation is well-architected, secure, and thoroughly tested.


✅ Strengths

1. Solid Architecture & Design

  • Smart batching strategy: Grouping by app and running groups concurrently via Promise.allSettled is the right approach
  • DRY refactoring: Extracting postProcessPropertyReferences() eliminates duplication across getProperties, getElementsWithProperties, and the new batch method
  • Error isolation: Per-reference error handling ensures one failed reference doesn't break the entire batch
  • Order preservation: Using indexed results to maintain input order is clean and correct

2. Security - Excellent ✅

  • Proper input sanitization: All user-controlled data flows through existing sanitizeForJxa() and escapeJxaString() methods (lines 652-655, 709)
  • No injection vulnerabilities: JXA code generation follows the same secure patterns as existing tools
  • Defense-in-depth: Validation at multiple layers (type guards, parameter checks, sanitization)
  • No path traversal risks: References are opaque IDs from the reference store, not user-controlled paths

3. Test Coverage - Exceptional ✅

The PR includes comprehensive testing:

  • Unit tests (176 lines added): Empty input, missing refs, partial failures, order preservation, same-app batching, cross-app concurrency, per-reference errors, all-properties mode
  • Integration tests (70 lines added): Valid/expired refs, cross-app refs, batch lifecycle
  • All critical paths covered: Happy path, error cases, edge cases
  • Tool count updated in both test files (5→6)

This meets the project's 100% coverage requirement.

4. TypeScript Type Safety ✅

  • Proper type guards (isGetPropertiesBatchParams)
  • Union return type correctly represents success/error variants
  • Internal IndexedResult type for compile-time safety during reordering

5. Performance & Resilience

  • Concurrent execution across apps maximizes throughput
  • Single JXA call per app minimizes overhead (4 Mail messages = 1 call instead of 4)
  • Graceful degradation: Returns empty properties when jxaExecutor is unavailable (testing mode)
  • Promise.allSettled: Ensures one app failure doesn't crash other app groups

🔍 Minor Observations (Not Blocking)

1. JXA Code Duplication (Low Priority)

Location: query-executor.ts:667-704

The all-properties JXA generation (lines 667-704) is nearly identical to the existing buildPropertyAccessorIIFE method. This creates a small duplication.

Impact: Low - the logic is complex and context-specific to batch operations. Extracting might not improve clarity.

Recommendation: Consider documenting why this is duplicated (if architectural) or extracting to a shared helper in a future refactor.


2. Edge Case: Empty Properties Array

Location: query-executor.ts:650

Current behavior:

if (properties && properties.length > 0) { ... }

Question: What happens if caller passes properties: [] (empty array)?

  • Falls through to all-properties branch (line 666)
  • Likely correct behavior, but worth verifying intent

Recommendation: Add a test case for properties: [] to explicitly document expected behavior.


3. Error Message Consistency

Location: handlers.ts:2194, query-executor.ts:615

Two different error messages for missing references:

  • Handler validation: "Each reference must be a string (format: ref_<uuid>)"
  • Executor lookup: "Reference not found: {id}. The referenced object may have been closed or deleted."

Impact: Minimal - both are clear and correct for their context.

Recommendation: Consider if handler validation should also check format (ref_* prefix) to catch malformed IDs earlier.


4. Potential for Large Batches

Location: query-executor.ts:708-714

No explicit limit on batch size. A batch of 1000 references could generate very large JXA scripts.

Current mitigation: Individual property/app name validation limits total size
Risk: Low - most real-world use cases (selected messages, files) are <100 items

Recommendation: Consider adding a configurable MAX_BATCH_SIZE constant (e.g., 100-500) with a clear error message if exceeded. This provides:

  • Protection against accidental large batches
  • Clear feedback to users/LLMs
  • Future-proofing for edge cases

🎯 JITD Architecture Alignment

This PR perfectly aligns with the JITD philosophy:

  • ✅ App-agnostic tool (works with any scriptable app)
  • ✅ Dynamically handles any reference type
  • ✅ No hard-coded app integrations
  • ✅ Follows existing query tool patterns

📊 Performance Analysis

Before: Fetching properties for 4 Mail messages

  • 4 separate get_properties calls
  • 4 JXA executions
  • 4 osascript process spawns
  • ~200-400ms (assuming ~50-100ms per call)

After: Same operation

  • 1 get_properties_batch call
  • 1 JXA execution
  • 1 osascript process
  • ~50-100ms

Improvement: ~2-4x faster for common use cases


✅ Final Verdict

APPROVE with minor suggestions for future iterations.

This PR is production-ready:

  • ✅ No security vulnerabilities
  • ✅ No bugs detected
  • ✅ Meets 100% test coverage requirement
  • ✅ TypeScript type safety maintained
  • ✅ Follows project conventions
  • ✅ Excellent documentation (tool description, JSDoc comments)
  • ✅ Significant performance improvement

The minor observations above are not blockers - they're opportunities for incremental polish in future PRs if desired.

Great work! 🎉


Optional Follow-ups (Post-Merge)

  1. Consider adding MAX_BATCH_SIZE constant with validation
  2. Add test case for properties: [] edge case
  3. Evaluate if all-properties JXA generation should be extracted to reduce duplication

None of these are urgent or necessary for this PR.

@jsavin
jsavin merged commit 15d9424 into main Feb 13, 2026
2 checks passed
@jsavin
jsavin deleted the feature/batch-get-properties branch February 13, 2026 07:12
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.

1 participant