#
tokens: 7988/50000 15/15 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .gitignore
├── .prettierrc
├── api-extended-json.json
├── COMMAND.md
├── eslint.config.mjs
├── example.png
├── examples
│   ├── client-example.ts
│   └── large-file-example.ts
├── jest.config.js
├── package-lock.json
├── package.json
├── README.md
├── src
│   ├── index.ts
│   ├── jsonUtils.test.ts
│   ├── jsonUtils.ts
│   ├── server.ts
│   └── types.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------

```
1 | {
2 |   "semi": true,
3 |   "trailingComma": "all",
4 |   "singleQuote": true,
5 |   "printWidth": 100,
6 |   "tabWidth": 2
7 | }
8 | 
```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
 1 | node_modules/
 2 | dist/
 3 | large-example.json
 4 | .DS_Store
 5 | *.log
 6 | npm-debug.log*
 7 | yarn-debug.log*
 8 | yarn-error.log*
 9 | .env
10 | coverage/
11 | .vscode/
12 | .idea/
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
 1 | # JSON Query MCP
 2 | 
 3 | A Model Context Protocol (MCP) server for querying large JSON files.
 4 | This server provides tools for working with large JSON data that can be used by LLM models implementing the [Model Context Protocol](https://modelcontextprotocol.io).
 5 | 
 6 | ## Features
 7 | 
 8 | - Query JSON files using JSONPath expressions
 9 | - Search for keys similar to a query string
10 | - Search for values similar to a query string
11 | 
12 | ## Example
13 | 
14 | Here is an example of the Cursor Agent using the tool to read a a very large (>1M character) JSON Swagger
15 | definition, and extracting a small portion to write a typescript interface.
16 | 
17 | ![Example](./example.png)
18 | 
19 | ## Usage
20 | 
21 | npx json-query-mcp
22 | 
23 | ## Installation in Cursor
24 | 
25 | Add the following to your cursor mcp json
26 | (on macOS this is `/Users/$USER/.cursor/mcp.json`)
27 | 
28 | ```mcp.json
29 | {
30 |   "mcpServers": {
31 |     ... other mcp servers
32 |     "json-query": {
33 |       "command": "npx",
34 |       "args": [<local path to this repo>],
35 |     },
36 |   }
37 | }
38 | ```
39 | 
40 | ## Development
41 | 
42 | ```bash
43 | # Run in development mode
44 | npm run dev
45 | 
46 | # Run tests
47 | npm test
48 | 
49 | # Format code
50 | npm run format
51 | 
52 | # Lint code
53 | npm run lint
54 | 
55 | # Fix lints
56 | npm run fix
57 | ```
58 | 
59 | ## License
60 | 
61 | MIT
62 | 
```

--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------

```javascript
1 | module.exports = {
2 |   preset: 'ts-jest',
3 |   testEnvironment: 'node',
4 |   testMatch: ['**/*.test.ts'],
5 | };
6 | 
```

--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------

```typescript
 1 | export interface JsonPathResult {
 2 |   path: string;
 3 |   value: unknown;
 4 | }
 5 | 
 6 | export interface SearchResult {
 7 |   path: string;
 8 |   similarity: number;
 9 |   value?: unknown;
10 | }
11 | 
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2022",
 4 |     "module": "nodenext",
 5 |     "moduleResolution": "nodenext",
 6 |     "lib": ["ES2022"],
 7 |     "outDir": "./dist",
 8 |     "rootDir": "./src",
 9 |     "strict": true,
10 |     "esModuleInterop": true,
11 |     "skipLibCheck": true,
12 |     "resolveJsonModule": true,
13 |     "forceConsistentCasingInFileNames": true
14 |   },
15 |   "include": ["src/**/*", "eslint.config.mjs"],
16 |   "exclude": ["node_modules", "jest.config.js"]
17 | }
18 | 
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #!/usr/bin/env node
 2 | import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
 3 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
 4 | import { program } from 'commander';
 5 | import { createServerWithTools } from './server.js';
 6 | 
 7 | import packageJSON from '../package.json';
 8 | 
 9 | function setupExitWatchdog(server: McpServer) {
10 |   // eslint-disable-next-line @typescript-eslint/no-misused-promises
11 |   process.stdin.on('close', async () => {
12 |     setTimeout(() => process.exit(0), 15000);
13 |     await server.close();
14 |     process.exit(0);
15 |   });
16 | }
17 | 
18 | program
19 |   .version('Version ' + packageJSON.version)
20 |   .name(packageJSON.name)
21 |   .action(async () => {
22 |     const server = createServerWithTools({
23 |       name: 'json-query',
24 |       version: packageJSON.version,
25 |     });
26 |     setupExitWatchdog(server);
27 | 
28 |     const transport = new StdioServerTransport();
29 |     await server.connect(transport);
30 | 
31 |     console.error('MCP server started');
32 |   });
33 | program.parse(process.argv);
34 | 
```

--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------

```
 1 | import eslint from '@eslint/js';
 2 | import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
 3 | import tseslint from 'typescript-eslint';
 4 | 
 5 | export default tseslint.config(
 6 |   eslint.configs.recommended,
 7 |   tseslint.configs.strictTypeChecked,
 8 |   tseslint.configs.stylisticTypeChecked,
 9 |   eslintPluginPrettierRecommended,
10 |   {
11 |     ignores: [
12 |       '**/*.json',
13 |       '**/dist/',
14 |       '**/package.json',
15 |       '**/package-lock.json',
16 |       '**/examples/**',
17 |       'node_modules/**',
18 |       'jest.config.js',
19 |       'eslint.config.mjs',
20 |     ],
21 |   },
22 |   {
23 |     languageOptions: {
24 |       parserOptions: {
25 |         projectService: true,
26 |         tsconfigRootDir: import.meta.dirname,
27 |       },
28 |     },
29 |   },
30 |   {
31 |     files: ['**/*.js', '**/*.jsx', '**/*.json'],
32 |     extends: [tseslint.configs.disableTypeChecked],
33 |   },
34 |   {
35 |     files: ['**/*.test.js', '**/*.test.ts', '**/*.test.tsx'],
36 |     rules: {
37 |       '@typescript-eslint/no-unused-expressions': 'off',
38 |       '@typescript-eslint/no-empty-function': 'off',
39 |     },
40 |   },
41 |   {
42 |     rules: {
43 |       'prettier/prettier': 'error',
44 |       curly: ['error', 'multi-line'],
45 |     },
46 |   },
47 | );
48 | 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "json-query-mcp",
 3 |   "version": "1.0.0",
 4 |   "description": "MCP server for querying large JSON files",
 5 |   "main": "dist/index.js",
 6 |   "scripts": {
 7 |     "build": "tsc",
 8 |     "start": "node dist/index.js",
 9 |     "dev": "ts-node src/index.ts",
10 |     "lint": "eslint . --ext .ts",
11 |     "fix": "eslint . --ext .ts --fix",
12 |     "format": "prettier --write \"src/**/*.ts\"",
13 |     "test": "jest",
14 |     "prestart": "npm run build",
15 |     "preinstall": "npm run build"
16 |   },
17 |   "keywords": [
18 |     "mcp",
19 |     "json",
20 |     "jsonpath",
21 |     "search"
22 |   ],
23 |   "author": "Michael Graczyk",
24 |   "license": "MIT",
25 |   "dependencies": {
26 |     "@modelcontextprotocol/sdk": "^1.10.0",
27 |     "commander": "^13.1.0",
28 |     "jsonpath-plus": "^10.3.0",
29 |     "string-similarity": "^4.0.4",
30 |     "zod": "^3.24.3"
31 |   },
32 |   "devDependencies": {
33 |     "@eslint/js": "^9.17.0",
34 |     "@types/jest": "^29.5.14",
35 |     "@types/node": "^22.14.1",
36 |     "@types/string-similarity": "^4.0.2",
37 |     "eslint": "^9.24.0",
38 |     "eslint-config-prettier": "^10.1.2",
39 |     "eslint-plugin-prettier": "^5.2.6",
40 |     "jest": "^29.7.0",
41 |     "prettier": "^3.5.3",
42 |     "ts-jest": "^29.3.2",
43 |     "ts-node": "^10.9.2",
44 |     "typescript": "^5.8.3",
45 |     "typescript-eslint": "^8.30.1"
46 |   },
47 |   "bin": "dist/index.js"
48 | }
49 | 
```

--------------------------------------------------------------------------------
/COMMAND.md:
--------------------------------------------------------------------------------

```markdown
 1 | Finishing implementing this MCP server (https://modelcontextprotocol.io/llms-full.txt) that does the following.
 2 | The server implements "json query" tools. This will be used to provide content from a very large json file to a model.
 3 | The MCP server should provide tools that do the following:
 4 | 1. Query by JSONPath. Given a JSONPath, extract all the path evaluation against the provided json file
 5 | 2. Search keys by string. Given a string, search for any keys that are close to that string. Returns a jsonpath to N matching keys sorted in relevance order (N=5 by default)
 6 | 3. Search values. Given a value, search for any values that are close to that string. Returns JSONPaths of N matching values in relevance order (N=5 by default)
 7 | 
 8 | Write it in typescript using npm and node.
 9 | Please follow all common best practices and conventions.
10 | Don't do anything clever or strange.
11 | Document the code and make sure package.json is production ready.
12 | Use eslint and prettier for formatting with default but strict configurations.
13 | 
14 | You still need to implement the tools and connect them to the server.
15 | You should use the types from "@modelcontextprotocol/sdk/types.js" wherever possible.
16 | Read the (https://modelcontextprotocol.io/llms-full.txt) to understand what is required.
17 | Do not modify the readme or do anything else.
18 | 
```

--------------------------------------------------------------------------------
/src/jsonUtils.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import path from 'path';
 2 | import { JsonUtils } from './jsonUtils';
 3 | 
 4 | const exampleJsonPath = path.resolve(__dirname, '../example.json');
 5 | 
 6 | describe('JsonUtils', () => {
 7 |   describe('queryByJsonPath', () => {
 8 |     it('should return matching results for a valid path', async () => {
 9 |       const results = await JsonUtils.queryByJsonPath('$.store.book[*].title', exampleJsonPath);
10 | 
11 |       expect(results).toHaveLength(3);
12 |       expect(results.map((r) => r.value)).toEqual([
13 |         'Moby Dick',
14 |         'The Great Gatsby',
15 |         'A Brief History of Time',
16 |       ]);
17 |     });
18 |   });
19 | 
20 |   describe('searchKeys', () => {
21 |     it('should find keys similar to the query', async () => {
22 |       const results = await JsonUtils.searchKeys('author', exampleJsonPath);
23 | 
24 |       expect(results.length).toBeGreaterThan(0);
25 | 
26 |       const authorMatch = results.find((r) => r.path.includes('author'));
27 |       expect(authorMatch).toBeDefined();
28 |       expect(authorMatch?.similarity).toBeGreaterThan(0.5);
29 |     });
30 |   });
31 | 
32 |   describe('searchValues', () => {
33 |     it('should find values similar to the query', async () => {
34 |       const results = await JsonUtils.searchValues('Fitzgerald', exampleJsonPath);
35 | 
36 |       expect(results.length).toBeGreaterThan(0);
37 | 
38 |       const fitzgeraldMatch = results.find(
39 |         (r) => typeof r.value === 'string' && r.value.includes('Fitzgerald'),
40 |       );
41 |       expect(fitzgeraldMatch).toBeDefined();
42 |       expect(fitzgeraldMatch?.similarity).toBeGreaterThan(0.5);
43 |     });
44 |   });
45 | });
46 | 
```

--------------------------------------------------------------------------------
/examples/client-example.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import path from 'path';
  2 | import { fileURLToPath } from 'url';
  3 | import fs from 'fs';
  4 | 
  5 | // For ES Modules
  6 | const __filename = fileURLToPath(import.meta.url);
  7 | const __dirname = path.dirname(__filename);
  8 | 
  9 | async function main(): Promise<void> {
 10 |   const exampleJsonPath = path.resolve(__dirname, '../example.json');
 11 | 
 12 |   // Example MCP client request for queryByJsonPath
 13 |   const request = {
 14 |     version: '0.1',
 15 |     tool_calls: [
 16 |       {
 17 |         name: 'queryByJsonPath',
 18 |         parameters: {
 19 |           path: '$.store.book[*].title',
 20 |           jsonFile: exampleJsonPath,
 21 |         },
 22 |       },
 23 |       {
 24 |         name: 'searchKeys',
 25 |         parameters: {
 26 |           query: 'author',
 27 |           jsonFile: exampleJsonPath,
 28 |           limit: 3,
 29 |         },
 30 |       },
 31 |       {
 32 |         name: 'searchValues',
 33 |         parameters: {
 34 |           query: 'Fitzgerald',
 35 |           jsonFile: exampleJsonPath,
 36 |           limit: 3,
 37 |         },
 38 |       },
 39 |     ],
 40 |   };
 41 | 
 42 |   try {
 43 |     const response = await fetch('http://localhost:3000/v1/tools', {
 44 |       method: 'POST',
 45 |       headers: {
 46 |         'Content-Type': 'application/json',
 47 |       },
 48 |       body: JSON.stringify(request),
 49 |     });
 50 | 
 51 |     if (!response.ok) {
 52 |       throw new Error(`HTTP error! status: ${response.status}`);
 53 |     }
 54 | 
 55 |     const data = await response.json();
 56 |     console.log('MCP Response:');
 57 |     console.log(JSON.stringify(data, null, 2));
 58 |   } catch (error) {
 59 |     console.error('Error calling MCP server:', error);
 60 |   }
 61 | }
 62 | 
 63 | main().catch(console.error);
 64 | 
 65 | // Example output:
 66 | //
 67 | // MCP Response:
 68 | // {
 69 | //   "version": "0.1",
 70 | //   "results": [
 71 | //     [
 72 | //       {
 73 | //         "path": "$.store.book[0].title",
 74 | //         "value": "Moby Dick"
 75 | //       },
 76 | //       {
 77 | //         "path": "$.store.book[1].title",
 78 | //         "value": "The Great Gatsby"
 79 | //       },
 80 | //       {
 81 | //         "path": "$.store.book[2].title",
 82 | //         "value": "A Brief History of Time"
 83 | //       }
 84 | //     ],
 85 | //     [
 86 | //       {
 87 | //         "path": "$.store.book[0].author",
 88 | //         "similarity": 0.7272727272727273
 89 | //       },
 90 | //       {
 91 | //         "path": "$.store.book[1].author",
 92 | //         "similarity": 0.7272727272727273
 93 | //       },
 94 | //       {
 95 | //         "path": "$.store.book[2].author",
 96 | //         "similarity": 0.7272727272727273
 97 | //       }
 98 | //     ],
 99 | //     [
100 | //       {
101 | //         "path": "$.store.book[1].author",
102 | //         "similarity": 0.5882352941176471,
103 | //         "value": "F. Scott Fitzgerald"
104 | //       }
105 | //     ]
106 | //   ]
107 | // }
108 | 
```

--------------------------------------------------------------------------------
/src/server.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
  2 | import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
  3 | 
  4 | import { z } from 'zod';
  5 | import path from 'path';
  6 | 
  7 | import { JsonUtils } from './jsonUtils.js';
  8 | 
  9 | interface Options {
 10 |   name: string;
 11 |   version: string;
 12 | }
 13 | 
 14 | const PATH_ARG_DESCRIPTION = "Absolute path to the JSON file.";
 15 | 
 16 | const getErrorResponse = (error: unknown): CallToolResult => {
 17 |   const errorMessage = error instanceof Error ? error.message : String(error);
 18 |   return {
 19 |     content: [
 20 |       {
 21 |         type: 'text',
 22 |         text: `Error: ${errorMessage}`,
 23 |       },
 24 |     ],
 25 |     isError: true,
 26 |   };
 27 | };
 28 | 
 29 | export function createServerWithTools(options: Options): McpServer {
 30 |   const { name, version } = options;
 31 | 
 32 |   const server = new McpServer({ name, version });
 33 | 
 34 |   // Tool 1: Query by JSONPath
 35 |   server.tool(
 36 |     'json_query_jsonpath',
 37 |     'Query a JSON file using JSONPath. Use to get values precisely from large JSON files.',
 38 |     {
 39 |       file_path: z.string().describe(PATH_ARG_DESCRIPTION),
 40 |       jsonpath: z.string().min(1).describe('JSONPath expression to evaluate'),
 41 |     },
 42 |     async ({ file_path, jsonpath }) => {
 43 |       try {
 44 |         const resolvedPath = path.resolve(file_path);
 45 | 
 46 |         const results = await JsonUtils.queryByJsonPath(jsonpath, resolvedPath);
 47 | 
 48 |         return {
 49 |           content: [
 50 |             {
 51 |               type: 'text',
 52 |               text: JSON.stringify(results, null, 2),
 53 |             },
 54 |           ],
 55 |         };
 56 |       } catch (error) {
 57 |         return getErrorResponse(error);
 58 |       }
 59 |     },
 60 |   );
 61 | 
 62 |   // Tool 2: Search keys
 63 |   server.tool(
 64 |     'json_query_search_keys',
 65 |     'Search for keys in a JSON file. Use when you do not know the path to a key in a large JSON file, but have some idea what the key is.',
 66 |     {
 67 |       file_path: z.string().describe(PATH_ARG_DESCRIPTION),
 68 |       query: z.string().min(1).describe('Search term for finding matching keys'),
 69 |       limit: z
 70 |         .number()
 71 |         .int()
 72 |         .min(1)
 73 |         .max(100)
 74 |         .optional()
 75 |         .default(5)
 76 |         .describe('Maximum number of results to return (default: 5)'),
 77 |     },
 78 |     async ({ file_path, query, limit }) => {
 79 |       try {
 80 |         const resolvedPath = path.resolve(file_path);
 81 | 
 82 |         const results = await JsonUtils.searchKeys(query, resolvedPath, limit);
 83 | 
 84 |         return {
 85 |           content: [
 86 |             {
 87 |               type: 'text',
 88 |               text: JSON.stringify(results, null, 2),
 89 |             },
 90 |           ],
 91 |         };
 92 |       } catch (error) {
 93 |         return getErrorResponse(error);
 94 |       }
 95 |     },
 96 |   );
 97 | 
 98 |   // Tool 3: Search values
 99 |   server.tool(
100 |     'json_query_search_values',
101 |     'Search for values in a JSON file. Use when you do not know the path to a value in a large JSON file, but have some idea what the value is.',
102 |     {
103 |       file_path: z.string().describe(PATH_ARG_DESCRIPTION),
104 |       query: z.string().min(1).describe('Search term for finding matching values'),
105 |       limit: z
106 |         .number()
107 |         .int()
108 |         .min(1)
109 |         .max(100)
110 |         .optional()
111 |         .default(5)
112 |         .describe('Maximum number of results to return (default: 5)'),
113 |     },
114 |     async ({ file_path, query, limit }) => {
115 |       try {
116 |         const resolvedPath = path.resolve(file_path);
117 | 
118 |         const results = await JsonUtils.searchValues(query, resolvedPath, limit);
119 | 
120 |         return {
121 |           content: [
122 |             {
123 |               type: 'text',
124 |               text: JSON.stringify(results, null, 2),
125 |             },
126 |           ],
127 |         };
128 |       } catch (error) {
129 |         return getErrorResponse(error);
130 |       }
131 |     },
132 |   );
133 | 
134 |   return server;
135 | }
136 | 
```

--------------------------------------------------------------------------------
/examples/large-file-example.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import fs from 'fs';
  2 | import path from 'path';
  3 | 
  4 | // This example demonstrates creating and querying a larger JSON file
  5 | 
  6 | async function generateLargeJson(
  7 |   filePath: string,
  8 |   itemCount = 1000,
  9 | ): Promise<void> {
 10 |   const data = {
 11 |     items: Array.from({ length: itemCount }, (_, i) => ({
 12 |       id: `item-${i}`,
 13 |       name: `Product ${i}`,
 14 |       description: `This is a description for product ${i}`,
 15 |       price: Math.round(Math.random() * 10000) / 100,
 16 |       categories: [
 17 |         `category-${Math.floor(Math.random() * 10)}`,
 18 |         `category-${Math.floor(Math.random() * 10)}`,
 19 |       ],
 20 |       metadata: {
 21 |         created: new Date().toISOString(),
 22 |         status: ['active', 'inactive', 'archived'][
 23 |           Math.floor(Math.random() * 3)
 24 |         ],
 25 |         tags: Array.from(
 26 |           { length: Math.floor(Math.random() * 5) + 1 },
 27 |           () => `tag-${Math.floor(Math.random() * 20)}`,
 28 |         ),
 29 |       },
 30 |     })),
 31 |     stats: {
 32 |       totalCount: itemCount,
 33 |       activeTags: Array.from({ length: 20 }, (_, i) => `tag-${i}`),
 34 |       priceRanges: {
 35 |         budget: { min: 0, max: 49.99 },
 36 |         standard: { min: 50, max: 99.99 },
 37 |         premium: { min: 100, max: Infinity },
 38 |       },
 39 |     },
 40 |   };
 41 | 
 42 |   await fs.promises.writeFile(filePath, JSON.stringify(data, null, 2));
 43 |   console.log(`Generated large JSON file (${itemCount} items) at: ${filePath}`);
 44 | }
 45 | 
 46 | async function queryMcpServer(jsonFilePath: string): Promise<void> {
 47 |   const queryExamples = [
 48 |     {
 49 |       type: 'queryByJsonPath',
 50 |       title: 'Get products with price > 90',
 51 |       parameters: {
 52 |         path: '$.items[?(@.price > 90)]',
 53 |         jsonFile: jsonFilePath,
 54 |       },
 55 |     },
 56 |     {
 57 |       type: 'queryByJsonPath',
 58 |       title: 'Get all active products',
 59 |       parameters: {
 60 |         path: '$.items[?(@.metadata.status == "active")]',
 61 |         jsonFile: jsonFilePath,
 62 |       },
 63 |     },
 64 |     {
 65 |       type: 'searchKeys',
 66 |       title: 'Search for keys related to "tag"',
 67 |       parameters: {
 68 |         query: 'tag',
 69 |         jsonFile: jsonFilePath,
 70 |         limit: 3,
 71 |       },
 72 |     },
 73 |     {
 74 |       type: 'searchValues',
 75 |       title: 'Search for values containing "Product 5"',
 76 |       parameters: {
 77 |         query: 'Product 5',
 78 |         jsonFile: jsonFilePath,
 79 |         limit: 3,
 80 |       },
 81 |     },
 82 |   ];
 83 | 
 84 |   for (const example of queryExamples) {
 85 |     console.log(`\nRunning: ${example.title}`);
 86 | 
 87 |     try {
 88 |       const response = await fetch('http://localhost:3000/v1/tools', {
 89 |         method: 'POST',
 90 |         headers: {
 91 |           'Content-Type': 'application/json',
 92 |         },
 93 |         body: JSON.stringify({
 94 |           version: '0.1',
 95 |           tool_calls: [
 96 |             {
 97 |               name: example.type,
 98 |               parameters: example.parameters,
 99 |             },
100 |           ],
101 |         }),
102 |       });
103 | 
104 |       if (!response.ok) {
105 |         throw new Error(`HTTP error! status: ${response.status}`);
106 |       }
107 | 
108 |       const data = await response.json();
109 |       console.log('Result:');
110 | 
111 |       // Format the output to avoid overwhelming console
112 |       if (example.type === 'queryByJsonPath') {
113 |         console.log(`Found ${data.results[0].length} matches`);
114 |         console.log('First 3 matches:');
115 |         console.log(JSON.stringify(data.results[0].slice(0, 3), null, 2));
116 |       } else {
117 |         console.log(JSON.stringify(data.results[0], null, 2));
118 |       }
119 |     } catch (error) {
120 |       console.error(`Error executing ${example.title}:`, error);
121 |     }
122 |   }
123 | }
124 | 
125 | async function main(): Promise<void> {
126 |   const largeJsonPath = path.resolve(__dirname, '../large-example.json');
127 | 
128 |   // Generate a large JSON file for testing
129 |   await generateLargeJson(largeJsonPath, 1000);
130 | 
131 |   // Run various queries against the large file
132 |   await queryMcpServer(largeJsonPath);
133 | }
134 | 
135 | main().catch(console.error);
136 | 
```

--------------------------------------------------------------------------------
/src/jsonUtils.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import fs from 'fs/promises';
  2 | import { JSONPath } from 'jsonpath-plus';
  3 | import stringSimilarity from 'string-similarity';
  4 | import { JsonPathResult, SearchResult } from './types.js';
  5 | 
  6 | // eslint-disable-next-line @typescript-eslint/no-extraneous-class
  7 | export class JsonUtils {
  8 |   private static async readJsonFile(filePath: string): Promise<unknown> {
  9 |     try {
 10 |       const content = await fs.readFile(filePath, 'utf-8');
 11 |       return JSON.parse(content);
 12 |     } catch (error) {
 13 |       if (error instanceof Error) {
 14 |         throw new Error(`Failed to read or parse JSON file: ${error}`);
 15 |       } else {
 16 |         throw new Error('Failed to read or parse JSON file');
 17 |       }
 18 |     }
 19 |   }
 20 | 
 21 |   static async queryByJsonPath(path: string, jsonFile: string): Promise<JsonPathResult[]> {
 22 |     const data = await this.readJsonFile(jsonFile);
 23 | 
 24 |     // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
 25 |     const results = JSONPath({
 26 |       path,
 27 |       json: data as object,
 28 |       resultType: 'all',
 29 |     }) as { path: string; value: unknown }[];
 30 | 
 31 |     return results.map((result) => ({
 32 |       path: result.path,
 33 |       value: result.value,
 34 |     }));
 35 |   }
 36 | 
 37 |   static async searchKeys(query: string, jsonFile: string, limit = 5): Promise<SearchResult[]> {
 38 |     const data = await this.readJsonFile(jsonFile);
 39 |     const keyPaths: { path: string; key: string }[] = [];
 40 | 
 41 |     const collectKeys = (obj: unknown, path = '$'): void => {
 42 |       if (obj && typeof obj === 'object') {
 43 |         if (Array.isArray(obj)) {
 44 |           obj.forEach((item, index) => {
 45 |             collectKeys(item, `${path}[${index.toString()}]`);
 46 |           });
 47 |         } else {
 48 |           Object.entries(obj).forEach(([key, value]) => {
 49 |             const newPath = path === '$' ? `$.${key}` : `${path}.${key}`;
 50 |             keyPaths.push({ path: newPath, key });
 51 |             collectKeys(value, newPath);
 52 |           });
 53 |         }
 54 |       }
 55 |     };
 56 | 
 57 |     collectKeys(data);
 58 | 
 59 |     const matches = keyPaths.map((item) => ({
 60 |       path: item.path,
 61 |       similarity: stringSimilarity.compareTwoStrings(query.toLowerCase(), item.key.toLowerCase()),
 62 |     }));
 63 | 
 64 |     return matches.sort((a, b) => b.similarity - a.similarity).slice(0, limit);
 65 |   }
 66 | 
 67 |   static async searchValues(query: string, jsonFile: string, limit = 5): Promise<SearchResult[]> {
 68 |     const data = await this.readJsonFile(jsonFile);
 69 |     const valuePaths: { path: string; value: unknown }[] = [];
 70 | 
 71 |     const collectValues = (obj: unknown, path = '$'): void => {
 72 |       if (obj && typeof obj === 'object') {
 73 |         if (Array.isArray(obj)) {
 74 |           obj.forEach((item, index) => {
 75 |             const newPath = `${path}[${index.toString()}]`;
 76 |             if (typeof item === 'string' || typeof item === 'number') {
 77 |               valuePaths.push({ path: newPath, value: item });
 78 |             }
 79 |             collectValues(item, newPath);
 80 |           });
 81 |         } else {
 82 |           Object.entries(obj).forEach(([key, value]) => {
 83 |             const newPath = path === '$' ? `$.${key}` : `${path}.${key}`;
 84 |             if (typeof value === 'string' || typeof value === 'number') {
 85 |               valuePaths.push({ path: newPath, value });
 86 |             }
 87 |             collectValues(value, newPath);
 88 |           });
 89 |         }
 90 |       }
 91 |     };
 92 | 
 93 |     collectValues(data);
 94 | 
 95 |     const stringQuery = String(query).toLowerCase();
 96 |     const matches = valuePaths
 97 |       .filter((item) => typeof item.value === 'string' || typeof item.value === 'number')
 98 |       .map((item) => ({
 99 |         path: item.path,
100 |         similarity: stringSimilarity.compareTwoStrings(
101 |           stringQuery,
102 |           String(item.value).toLowerCase(),
103 |         ),
104 |         value: item.value,
105 |       }));
106 | 
107 |     return matches.sort((a, b) => b.similarity - a.similarity).slice(0, limit);
108 |   }
109 | }
110 | 
```