Skip to main content
Glama
GongRzhe

JSON MCP Server

query

Extract and manipulate JSON data by specifying a URL and JSONPath expression, enabling precise querying, filtering, sorting, and transformation of JSON content.

Instructions

Query JSON data using JSONPath syntax

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
jsonPathYesJSONPath expression (e.g. $.store.book[*].author)
urlYesURL of the JSON data source

Implementation Reference

  • Main handler for the 'query' tool. Fetches JSON from URL, applies extended JSONPath query with support for filters, aggregations, sorting, grouping, transformations (numeric, string, date, array), projections, and returns formatted result.
    if (name === "query") {
        const { url, jsonPath } = QueryArgumentsSchema.parse(args);
        const jsonData = await fetchData(url);
        
        // Handle complex filtering with string operations
        const filterMatch = jsonPath.match(/\[\?\((.+?)\)\]/);
        if (filterMatch) {
            const condition = filterMatch[1];
            let baseData = Array.isArray(jsonData) ? jsonData : [jsonData];
            
            // Get the base path before the filter
            const basePath = jsonPath.split('[?')[0];
            if (basePath !== '$') {
                baseData = JSONPath.value(jsonData, basePath);
            }
            
            // Apply filter
            let result = handleComplexFilter(baseData, condition);
            
            // Handle operations after filter
            const afterFilter = jsonPath.split(')]')[1];
            if (afterFilter) {
                if (afterFilter.includes('.{')) {
                    // Handle projection
                    const projectionMatch = afterFilter.match(/\.\{(.+?)\}/);
                    if (projectionMatch) {
                        const fieldPairs = projectionMatch[1].split(',')
                            .map(pair => {
                                const [key, value] = pair.split(':').map(s => s.trim());
                                return { key, value: value || key };
                            });
                        
                        result = result.map((item: Record<string, any>) => {
                            const obj: Record<string, any> = {};
                            fieldPairs.forEach(({ key, value }) => {
                                obj[key] = item[value];
                            });
                            return obj;
                        });
                    }
                }
            }
            
            return {
                content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
            };
        }
    
        // Handle numeric operations
        if (jsonPath.match(/\.(math|round|floor|ceil|abs|sqrt|pow2)/)) {
            let baseData;
            const basePath = jsonPath.split('.math')[0].split('.round')[0]
                .split('.floor')[0].split('.ceil')[0]
                .split('.abs')[0].split('.sqrt')[0].split('.pow2')[0];
            
            if (basePath === '$') {
                baseData = Array.isArray(jsonData) ? jsonData : [jsonData];
            } else {
                baseData = JSONPath.value(jsonData, basePath);
                if (!Array.isArray(baseData)) {
                    baseData = [baseData];
                }
            }
            
            const numericOp = jsonPath.slice(basePath.length);
            const result = handleNumericOperations(baseData, numericOp);
            return {
                content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
            };
        }
        
        // Handle date operations
        if (jsonPath.match(/\.(format|isToday|add)\(/)) {
            const baseData = Array.isArray(jsonData) ? jsonData : [jsonData];
            const dateOp = jsonPath.slice(jsonPath.indexOf('.'));
            const result = handleDateOperations(baseData, dateOp);
            return {
                content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
            };
        }
        
        // Handle string operations
        if (jsonPath.match(/\.(toLowerCase|toUpperCase|startsWith|endsWith|contains|matches)\(/)) {
            const baseData = JSONPath.value(jsonData, jsonPath.split('.')[0]);
            const stringOp = jsonPath.slice(jsonPath.indexOf('.') + 1);
            const result = handleStringOperations(baseData, stringOp);
            return {
                content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
            };
        }
        
        // Handle array transformations
        if (jsonPath.match(/\.(map|flatten|union|intersection)\(/)) {
            const baseData = Array.isArray(jsonData) ? jsonData : [jsonData];
            const transformOp = jsonPath.slice(jsonPath.indexOf('.'));
            const result = handleArrayTransformations(baseData, transformOp);
            return {
                content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
            };
        }
        
        // Handle grouping operations
        if (jsonPath.includes('.groupBy(')) {
            const baseData = Array.isArray(jsonData) ? jsonData : [jsonData];
            const result = handleGrouping(baseData, jsonPath);
            return {
                content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
            };
        }
    
        // Handle aggregation functions
        if (jsonPath.match(/\.(sum|avg|min|max)\(\w+\)/)) {
            const result = handleAggregation(
                Array.isArray(jsonData) ? jsonData : [jsonData],
                jsonPath
            );
            return {
                content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
            };
        }
    
        // Handle array operations (sort, distinct)
        if (jsonPath.includes('.sort(') || jsonPath.includes('.distinct()')) {
            let result = Array.isArray(jsonData) ? jsonData : [jsonData];
            const operations = jsonPath.split(/(?=\.(?:sort|distinct))/);
            
            for (const op of operations) {
                if (op.startsWith('.sort') || op.startsWith('.distinct')) {
                    result = handleArrayOperations(result, op);
                }
            }
            
            return {
                content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
            };
        }
    
        // Handle length() function
        if (jsonPath === "$.length()") {
            return {
                content: [{ type: "text", text: JSON.stringify(getArrayLength(jsonData), null, 2) }],
            };
        }
        
        // Handle complex array operations (reverse, slice)
        if (jsonPath.includes("-1") || jsonPath.includes(":")) {
            let result = Array.isArray(jsonData) ? jsonData : [jsonData];
            
            // Split multiple operations
            const operations = jsonPath.match(/\[.*?\]/g) || [];
            
            for (const op of operations) {
                result = handleArrayOperations(result, op);
            }
            
            // Handle field selection
            if (jsonPath.includes(".")) {
                const fieldMatch = jsonPath.match(/\.([^.\[]+)$/);
                if (fieldMatch) {
                    const field = fieldMatch[1];
                    result = result.map(item => item[field]);
                }
            }
            
            return {
                content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
            };
        }
        
        // Handle object projection
        if (jsonPath.includes(".{")) {
            const allData = Array.isArray(jsonData) ? jsonData : [jsonData];
            const projectionMatch = jsonPath.match(/\.\{(.+?)\}/);
            if (projectionMatch) {
                const fieldPairs = projectionMatch[1].split(',')
                    .map(pair => {
                        const [key, value] = pair.split(':').map(s => s.trim());
                        return { key, value: value || key };
                    });
                
                const result = allData.map((item: Record<string, any>) => {
                    const obj: Record<string, any> = {};
                    fieldPairs.forEach(({ key, value }) => {
                        obj[key] = item[value];
                    });
                    return obj;
                });
                
                return {
                    content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
                };
            }
        }
        
        // Default JSONPath evaluation
        const result = JSONPath.value(jsonData, jsonPath);
        return {
            content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
        };
    }
  • Zod validation schema for 'query' tool input arguments (url and jsonPath). Used in handler to parse arguments.
    const QueryArgumentsSchema = z.object({
        url: z.string().url(),
        jsonPath: z.string(),
    });
  • src/index.ts:406-421 (registration)
    Tool registration in the listTools response, defining name, description, and input schema for the 'query' tool.
    name: "query",
    description: "Query JSON data using JSONPath syntax",
    inputSchema: {
        type: "object",
        properties: {
            url: {
                type: "string",
                description: "URL of the JSON data source",
            },
            jsonPath: {
                type: "string",
                description: "JSONPath expression (e.g. $.store.book[*].author)",
            }
        },
        required: ["url", "jsonPath"],
    },
  • Core helper function to fetch and parse JSON data from the provided URL, used by the query handler.
    async function fetchData(url: string) {
        const response = await fetch(url, {
            headers: {
                'Accept': 'application/json',
                'User-Agent': 'Mozilla/5.0'
            }
        });
        
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }
        
        const data = await response.json();
        return data;
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It states the tool queries JSON data but doesn't disclose behavioral traits like error handling, performance implications, rate limits, or what happens if the URL is invalid or JSONPath is malformed. For a tool with no annotations, this is a significant gap in transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence: 'Query JSON data using JSONPath syntax.' It is front-loaded with the core purpose and has zero waste, making it highly concise and well-structured.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity of querying external JSON data, the description is incomplete. No annotations exist, and there's no output schema, so the agent lacks information on return values, error cases, or behavioral constraints. The description doesn't compensate for these gaps, making it inadequate for safe and effective use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, with clear descriptions for both parameters ('jsonPath' and 'url'). The description adds minimal value beyond the schema, as it only reiterates the use of JSONPath without providing additional syntax or format details. Baseline 3 is appropriate when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Query JSON data using JSONPath syntax.' It specifies the verb ('query'), resource ('JSON data'), and method ('JSONPath syntax'). However, it doesn't explicitly differentiate from the sibling tool 'filter,' which might have overlapping functionality, preventing a score of 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention the sibling tool 'filter' or any other context for usage, such as prerequisites or scenarios where this tool is preferred. This leaves the agent with minimal direction.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/GongRzhe/JSON-MCP-Server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server