Skip to main content
Glama

json_search_kv

Search key-value pairs in JSON files within a directory, filtering by specified key and optional value. Supports recursive search, match types, and customizable parameters for file size, depth, and results.

Instructions

Search for key-value pairs in JSON files within a directory. Requires maxBytes (default 10KB), maxDepth (default 2), and maxResults (default 10) parameters. Returns all key-value pairs that match the search pattern. The path must be within allowed directories.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
directoryPathYesDirectory to search in
keyYesKey to search for
matchTypeNoHow to match values - only applies if value is providedexact
maxBytesYesMaximum bytes to read from each file. Must be a positive integer. Handler default: 10KB.
maxDepthYesMaximum directory depth to search. Must be a positive integer. Handler default: 2.
maxResultsYesMaximum number of results to return. Must be a positive integer. Handler default: 10.
recursiveNoWhether to search recursively in subdirectories
valueNoOptional value to match against the key

Implementation Reference

  • The core handler function implementing the logic for the 'json_search_kv' tool. It searches for specified key-value pairs in JSON files within a directory (recursively up to maxDepth), validates paths, reads JSON files, and returns matching files with paths to the keys.
    export async function handleJsonSearchKv( args: unknown, allowedDirectories: string[], symlinksMap: Map<string, string>, noFollowSymlinks: boolean ) { const parsed = parseArgs(JsonSearchKvArgsSchema, args, 'json_search_kv'); const validDirPath = await validatePath(parsed.directoryPath, allowedDirectories, symlinksMap, noFollowSymlinks); const { key, value, recursive = true, matchType = 'exact', maxBytes, maxResults = 10, maxDepth } = parsed; const effectiveMaxDepth = maxDepth ?? 2; // Default depth 2 /** * Check if a value matches the search criteria */ function isValueMatch(foundValue: any): boolean { if (value === undefined) return true; if (typeof foundValue === 'string' && typeof value === 'string') { switch (matchType) { case 'contains': return foundValue.includes(value); case 'startsWith': return foundValue.startsWith(value); case 'endsWith': return foundValue.endsWith(value); default: return foundValue === value; } } return isEqual(foundValue, value); } /** * Search for key/value pairs in a JSON object */ function searchInObject(obj: any, currentPath: string[] = []): string[] { const matches: string[] = []; if (isPlainObject(obj)) { for (const [k, v] of Object.entries(obj)) { const newPath = [...currentPath, k]; // Check if this key matches if (k === key && isValueMatch(v)) { matches.push(newPath.join('.')); } // Recursively search in nested objects and arrays if (isPlainObject(v) || Array.isArray(v)) { matches.push(...searchInObject(v, newPath)); } } } else if (Array.isArray(obj)) { obj.forEach((item, index) => { const newPath = [...currentPath, index.toString()]; matches.push(...searchInObject(item, newPath)); }); } return matches; } /** * Process a single JSON file */ async function processFile(filePath: string): Promise<{ file: string; matches: string[] } | null> { try { // Pass maxBytes from parsed args to readJsonFile // Use the maxBytes variable destructured earlier const jsonData = await readJsonFile(filePath, maxBytes); const matches = searchInObject(jsonData); return matches.length > 0 ? { file: filePath, matches } : null; } catch (error) { // Skip files that can't be read or aren't valid JSON return null; } } /** * Recursively get all JSON files in directory */ async function getJsonFiles(dir: string, currentDepth: number): Promise<string[]> { // Check depth limit if (currentDepth >= effectiveMaxDepth) { return []; } const entries = await fs.readdir(dir, { withFileTypes: true }); const files: string[] = []; for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory() && recursive) { const validSubPath = await validatePath(fullPath, allowedDirectories, symlinksMap, noFollowSymlinks); files.push(...await getJsonFiles(validSubPath, currentDepth + 1)); } else if (entry.isFile() && entry.name.endsWith('.json')) { const validFilePath = await validatePath(fullPath, allowedDirectories, symlinksMap, noFollowSymlinks); files.push(validFilePath); } } return files; } try { // Get all JSON files in the directory const jsonFiles = await getJsonFiles(validDirPath, 0); // Start at depth 0 // Process files and collect results const results = []; for (const file of jsonFiles) { if (results.length >= maxResults) break; const result = await processFile(file); if (result) { results.push(result); } } return { content: [{ type: "text", text: JSON.stringify({ totalFiles: jsonFiles.length, matchingFiles: results.length, results }, null, 2) }], }; } catch (error) { if (error instanceof Error) { throw new Error(`JSON key/value search failed: ${error.message}`); } throw error; } }
  • TypeBox schema defining the input parameters for the json_search_kv tool, including directory, key, optional value, matching options, and limits.
    export const JsonSearchKvArgsSchema = Type.Object({ directoryPath: Type.String({ description: 'Directory to search in' }), key: Type.String({ description: 'Key to search for' }), value: Type.Optional(Type.Any({ description: 'Optional value to match against the key' })), recursive: Type.Optional(Type.Boolean({ default: true, description: 'Whether to search recursively in subdirectories' })), matchType: Type.Optional( Type.Union([ Type.Literal('exact'), Type.Literal('contains'), Type.Literal('startsWith'), Type.Literal('endsWith') ], { default: 'exact', description: 'How to match values - only applies if value is provided' }) ), maxBytes: Type.Integer({ minimum: 1, description: 'Maximum bytes to read from each file. Must be a positive integer. Handler default: 10KB.' }), maxResults: Type.Integer({ minimum: 1, description: 'Maximum number of results to return. Must be a positive integer. Handler default: 10.' }), maxDepth: Type.Integer({ minimum: 1, description: 'Maximum directory depth to search. Must be a positive integer. Handler default: 2.' }) }); export type JsonSearchKvArgs = Static<typeof JsonSearchKvArgsSchema>;
  • index.ts:293-294 (registration)
    Registers the handleJsonSearchKv function as the executor for the 'json_search_kv' tool in the toolHandlers object.
    json_search_kv: (a: unknown) => handleJsonSearchKv(a, allowedDirectories, symlinksMap, noFollowSymlinks),
  • index.ts:51-51 (registration)
    Imports the handleJsonSearchKv handler from json-handlers.
    handleJsonSearchKv,
  • index.ts:333-333 (registration)
    Defines the tool metadata (name and description) in the allTools list used for server.addTool.
    { name: "json_search_kv", description: "Search key/value in JSON" },
  • Maps the json_search_kv tool name to its schema in the central toolSchemas export.
    json_search_kv: JsonSearchKvArgsSchema,

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/rawr-ai/mcp-filesystem'

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