json_mcp_server/json_tools/
handler.rs1use 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 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 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}