docfs
Version:
MCP server for accessing local file system content with intelligent search and listing tools
125 lines • 4.58 kB
JavaScript
/**
* Search Files Tool - Searches for content within files
*/
import { relative } from 'node:path';
import { searchInFiles } from '../utils/filesystem.js';
/**
* Validates and parses input parameters
*/
function parseInput(input) {
const params = input;
const query = params.query;
if (typeof query !== 'string' || query.trim().length === 0) {
throw new Error('Query parameter is required and must be a non-empty string');
}
return {
query: query.trim(),
filePattern: typeof params.filePattern === 'string' ? params.filePattern : undefined,
caseSensitive: typeof params.caseSensitive === 'boolean' ? params.caseSensitive : false,
wholeWord: typeof params.wholeWord === 'boolean' ? params.wholeWord : false,
contextLines: typeof params.contextLines === 'number' ? Math.max(0, Math.min(10, params.contextLines)) : 2,
maxResults: typeof params.maxResults === 'number' ? Math.max(1, Math.min(1000, params.maxResults)) : 100,
maxDepth: typeof params.maxDepth === 'number' ? Math.max(1, params.maxDepth) : undefined,
};
}
function makeRelative(filePath, roots) {
for (const root of roots) {
if (filePath.startsWith(root)) {
return relative(root, filePath);
}
}
return filePath;
}
export const searchFiles = {
name: 'search_files',
description: 'Searches for text content within files and returns JSON data',
inputSchema: {
type: 'object',
required: ['query'],
properties: {
query: {
type: 'string',
description: 'The text to search for within files',
minLength: 1,
},
filePattern: {
type: 'string',
description: 'Optional glob pattern to filter files to search (e.g., "*.js", "*.md")',
},
caseSensitive: {
type: 'boolean',
description: 'Whether the search should be case sensitive (default: false)',
default: false,
},
wholeWord: {
type: 'boolean',
description: 'Whether to match whole words only (default: false)',
default: false,
},
contextLines: {
type: 'number',
description: 'Number of lines of context to show around matches (default: 2, max: 10)',
default: 2,
minimum: 0,
maximum: 10,
},
maxResults: {
type: 'number',
description: 'Maximum number of results to return (default: 100, max: 1000)',
default: 100,
minimum: 1,
maximum: 1000,
},
maxDepth: {
type: 'number',
description: 'Maximum directory depth to traverse when searching (defaults to 10)',
minimum: 1,
},
},
},
async handler(input, context) {
const params = parseInput(input);
try {
const searchResults = await searchInFiles(context.roots, {
query: params.query,
filePattern: params.filePattern,
caseSensitive: params.caseSensitive,
wholeWord: params.wholeWord,
contextLines: params.contextLines,
maxDepth: params.maxDepth,
});
const limitedResults = searchResults.slice(0, params.maxResults);
const mappedResults = limitedResults.map((r) => ({
file: makeRelative(r.file, context.roots),
line: r.line,
content: r.content,
context: r.context,
}));
const output = {
results: mappedResults,
totalMatches: searchResults.length,
};
return {
content: [
{
type: 'text',
text: JSON.stringify(output),
},
],
};
}
catch (error) {
const message = error instanceof Error ? error.message : String(error);
return {
content: [
{
type: 'text',
text: JSON.stringify({ error: message }),
},
],
isError: true,
};
}
},
};
//# sourceMappingURL=searchFiles.js.map