json_mcp_server/json_tools/
handler.rs

1use crate::json_tools::{operations::JsonOperations, query::JsonQuery, streaming::JsonStreaming};
2use crate::mcp::protocol::{Tool, ToolCall, ToolResult};
3use crate::mcp::server::ToolHandler;
4use serde_json::{json, Value};
5use std::collections::HashMap;
6
7pub struct JsonToolsHandler {
8    operations: JsonOperations,
9    query: JsonQuery,
10    streaming: JsonStreaming,
11}
12
13impl JsonToolsHandler {
14    pub fn new() -> Self {
15        Self {
16            operations: JsonOperations::new(),
17            query: JsonQuery::new(),
18            streaming: JsonStreaming::new(),
19        }
20    }
21
22    fn create_json_help_tool() -> Tool {
23        Tool {
24            name: "json-help".to_string(),
25            description: "Get comprehensive help about all available JSON tools and their usage patterns. This tool provides guidance on how to effectively use the JSON MCP server for various tasks.".to_string(),
26            input_schema: json!({
27                "type": "object",
28                "properties": {
29                    "topic": {
30                        "type": "string",
31                        "description": "Specific topic to get help about. Options: 'overview', 'reading', 'writing', 'querying', 'streaming', 'examples', 'tools'",
32                        "enum": ["overview", "reading", "writing", "querying", "streaming", "examples", "tools"]
33                    }
34                },
35                "required": []
36            })
37        }
38    }
39
40    async fn handle_json_help(&self, args: &HashMap<String, Value>) -> anyhow::Result<ToolResult> {
41        let topic = args.get("topic")
42            .and_then(|v| v.as_str())
43            .unwrap_or("overview");
44
45        let help_text = match topic {
46            "overview" => {
47                r#"# JSON MCP Server Help
48
49This server provides comprehensive JSON file operations for LLMs. Available tools:
50
51## Core Tools:
52- **json-read**: Read and parse JSON files of any size with automatic streaming for large files
53- **json-write**: Write or update JSON files with various merge strategies  
54- **json-query**: Query JSON files using JSONPath expressions
55- **json-validate**: Validate JSON structure and content
56- **json-help**: Get help about tools (this tool)
57
58## Required Parameters by Tool:
59- **json-read**: `file_path` (required)
60- **json-write**: `file_path`, `data` (both required)
61- **json-query**: `file_path`, `query` (both required)
62- **json-validate**: `file_path` (required)
63- **json-help**: none (all parameters optional)
64
65## Quick Start Examples:
66```json
67{"name": "json-read", "arguments": {"file_path": "./data.json"}}
68{"name": "json-write", "arguments": {"file_path": "./output.json", "data": {"key": "value"}}}
69{"name": "json-query", "arguments": {"file_path": "./data.json", "query": "$.users[0].name"}}
70{"name": "json-validate", "arguments": {"file_path": "./data.json"}}
71{"name": "json-help", "arguments": {"topic": "reading"}}
72```
73
74## Key Features:
75- Support for extremely large JSON files via automatic streaming
76- JSONPath querying for complex data extraction
77- Multiple write/update modes (replace, merge, append)
78- Validation and error handling with detailed diagnostics
79- LLM-optimized responses and formatting
80- Robust error handling for malformed JSON
81
82Use 'json-help' with specific topics for detailed guidance:
83- topic: 'reading' - Learn about reading JSON files
84- topic: 'writing' - Learn about writing/updating JSON files  
85- topic: 'querying' - Learn about JSONPath queries
86- topic: 'streaming' - Learn about handling large files
87- topic: 'examples' - See practical usage examples
88- topic: 'tools' - Get detailed help for individual tools"#
89            },
90            "reading" => {
91                r#"# Reading JSON Files
92
93## json-read Tool
94Reads and parses JSON files with automatic streaming for large files.
95
96**Parameters:**
97- `file_path` (required): Path to JSON file
98- `json_path` (optional): JSONPath to extract specific data
99- `format` (optional): Output format - "pretty", "compact", "raw" (default: "pretty")
100- `offset` (optional): Starting position for streaming (default: 0)
101- `limit` (optional): Maximum number of items to return (default: 1000)
102
103**Examples:**
104```json
105{
106  "name": "json-read",
107  "arguments": {
108    "file_path": "./data.json"
109  }
110}
111```
112
113```json
114{
115  "name": "json-read", 
116  "arguments": {
117    "file_path": "./users.json",
118    "json_path": "$.users[*].name",
119    "format": "compact"
120  }
121}
122```
123
124**Use Cases:**
125- Load entire JSON files (automatically streams if large)
126- Extract specific fields or arrays
127- Read configuration files
128- Parse API responses stored as JSON
129- Handle large datasets efficiently"#
130            },
131            "writing" => {
132                r#"# Writing JSON Files
133
134## json-write Tool
135Creates or updates JSON files with flexible merge strategies.
136
137**Parameters:**
138- `file_path` (required): Path to JSON file
139- `data` (required): JSON data to write
140- `mode` (optional): Write mode - "replace", "merge", "append" (default: "replace")
141- `create_path` (optional): Create directory if needed (default: true)
142- `backup` (optional): Create backup before writing (default: false)
143
144**Write Modes:**
145- **replace**: Completely replace file content
146- **merge**: Merge with existing JSON (objects only)
147- **append**: Append to arrays or create new array
148
149**Examples:**
150```json
151{
152  "name": "json-write",
153  "arguments": {
154    "file_path": "./config.json",
155    "data": {"setting": "value", "enabled": true}
156  }
157}
158```
159
160```json
161{
162  "name": "json-write",
163  "arguments": {
164    "file_path": "./users.json", 
165    "data": {"name": "John", "age": 30},
166    "mode": "append"
167  }
168}
169```"#
170            },
171            "querying" => {
172                r#"# Querying JSON with JSONPath
173
174## json-query Tool
175Execute complex JSONPath queries on JSON files.
176
177**Parameters:**
178- `file_path` (required): Path to JSON file
179- `query` (required): JSONPath expression
180- `format` (optional): Output format - "json", "text", "table" (default: "json")
181
182**JSONPath Syntax:**
183- `$` - Root element
184- `.` - Child element  
185- `[]` - Array index or filter
186- `*` - Wildcard
187- `..` - Recursive descent
188- `[?()]` - Filter expression
189
190**Examples:**
191```json
192{
193  "name": "json-query",
194  "arguments": {
195    "file_path": "./data.json",
196    "query": "$.users[?(@.age > 25)].name"
197  }
198}
199```
200
201```json
202{
203  "name": "json-query", 
204  "arguments": {
205    "file_path": "./products.json",
206    "query": "$..products[*].price",
207    "format": "table"
208  }
209}
210```
211
212**Common Patterns:**
213- `$.array[*]` - All array elements
214- `$..field` - All fields named 'field' anywhere
215- `$.object.field` - Specific nested field
216- `$[*]` - All root level elements"#
217            },
218            "streaming" => {
219                r#"# Streaming Large JSON Files
220
221## json-read Tool (Automatic Streaming)
222The json-read tool automatically handles large files via streaming without loading everything into memory.
223
224**Streaming Features:**
225- Automatically detects when to use streaming mode
226- Handle files larger than available RAM
227- Fast queries on massive datasets  
228- Memory-efficient processing
229- Progressive results for interactive use
230
231**Parameters for Large Files:**
232- `file_path` (required): Path to large JSON file
233- `json_path` (optional): JSONPath to filter data during streaming
234- `limit` (optional): Maximum number of results (default: 1000)
235- `offset` (optional): Skip number of results (default: 0)
236- `format` (optional): Output format - "pretty", "compact", "raw"
237
238**Examples:**
239```json
240{
241  "name": "json-read",
242  "arguments": {
243    "file_path": "./large-dataset.json",
244    "json_path": "$.records[*].id",
245    "limit": 100
246  }
247}
248```
249
250**Best Practices:**
251- Use specific JSONPath queries to filter early
252- Set reasonable limits for large datasets
253- Use offset for pagination
254- Stream arrays rather than large objects when possible"#
255            },
256            "examples" => {
257                r#"# Practical JSON Tool Examples
258
259## Example 1: Configuration Management
260```json
261// Read config
262{"name": "json-read", "arguments": {"file_path": "./config.json"}}
263
264// Update specific setting
265{"name": "json-write", "arguments": {
266  "file_path": "./config.json",
267  "data": {"database": {"host": "localhost", "port": 5432}},
268  "mode": "merge"
269}}
270```
271
272## Example 2: Data Analysis
273```json
274// Query user data
275{"name": "json-query", "arguments": {
276  "file_path": "./users.json", 
277  "query": "$.users[?(@.active == true)]",
278  "format": "table"
279}}
280
281// Count by category
282{"name": "json-query", "arguments": {
283  "file_path": "./products.json",
284  "query": "$..category"
285}}
286```
287
288## Example 3: Large File Processing
289```json
290// Read large log file with automatic streaming
291{"name": "json-read", "arguments": {
292  "file_path": "./logs.json",
293  "json_path": "$.logs[?(@.level == 'ERROR')]",
294  "limit": 50
295}}
296```
297
298## Example 4: API Response Handling
299```json
300// Extract specific fields
301{"name": "json-read", "arguments": {
302  "file_path": "./api-response.json",
303  "json_path": "$.data.items[*].{id: id, name: name}"
304}}
305```
306
307## Example 5: Batch Updates
308```json
309// Add new items to array
310{"name": "json-write", "arguments": {
311  "file_path": "./items.json",
312  "data": [{"id": 123, "name": "New Item"}],
313  "mode": "append"
314}}
315```"#
316            },
317            "tools" => {
318                r#"# Individual Tool Help
319
320## json-read
321**Purpose**: Read and parse JSON files with automatic streaming
322**Required**: `file_path`
323**Optional**: `query`, `limit`, `offset`
324**Example**: `{"file_path": "./data.json", "query": "$.users"}`
325
326## json-write  
327**Purpose**: Write or update JSON files with various merge strategies
328**Required**: `file_path`, `data`
329**Optional**: `mode`, `create_dirs`, `pretty`
330**Example**: `{"file_path": "./output.json", "data": {"key": "value"}, "mode": "replace"}`
331
332## json-query
333**Purpose**: Execute JSONPath queries on JSON files
334**Required**: `file_path`, `query`
335**Optional**: `format`
336**Example**: `{"file_path": "./data.json", "query": "$.users[?(@.age > 25)].name"}`
337
338## json-validate
339**Purpose**: Validate JSON file syntax and structure
340**Required**: `file_path`
341**Optional**: `schema`
342**Example**: `{"file_path": "./data.json"}`
343
344## json-help
345**Purpose**: Get help about tools and usage patterns
346**Required**: none
347**Optional**: `topic`
348**Example**: `{"topic": "reading"}` or `{}`
349
350## Common Error Fixes:
351- **"file_path is required"** → Add: `"file_path": "./your-file.json"`
352- **"data is required"** → Add: `"data": {"your": "json data"}`
353- **"query is required"** → Add: `"query": "$.your.jsonpath"`"#
354            },
355            _ => "Unknown help topic. Available topics: overview, reading, writing, querying, streaming, examples, tools"
356        };
357
358        Ok(ToolResult::success(help_text.to_string()))
359    }
360}
361
362#[async_trait::async_trait]
363impl ToolHandler for JsonToolsHandler {
364    async fn get_tools(&self) -> anyhow::Result<Vec<Tool>> {
365        let mut tools = Vec::new();
366        
367        // Add all tool categories
368        tools.extend(self.operations.get_tools().await?);
369        tools.extend(self.query.get_tools().await?);
370        tools.extend(self.streaming.get_tools().await?);
371        
372        // Add help tool
373        tools.push(Self::create_json_help_tool());
374        
375        Ok(tools)
376    }
377
378    async fn call_tool(&self, tool_call: ToolCall) -> anyhow::Result<ToolResult> {
379        match tool_call.name.as_str() {
380            "json-help" => self.handle_json_help(&tool_call.arguments).await,
381            name if name.starts_with("json-write") || name.starts_with("json-validate") => {
382                self.operations.call_tool(tool_call).await
383            },
384            name if name.starts_with("json-query") => {
385                self.query.call_tool(tool_call).await
386            },
387            name if name.starts_with("json-read") => {
388                self.streaming.call_tool(tool_call).await
389            },
390            _ => Ok(ToolResult::error(format!("Unknown tool: {}", tool_call.name))),
391        }
392    }
393}