Skip to main content
Glama

Claude Desktop Commander MCP

search-handlers.ts8.23 kB
import { searchManager } from '../search-manager.js'; import { StartSearchArgsSchema, GetMoreSearchResultsArgsSchema, StopSearchArgsSchema } from '../tools/schemas.js'; import { ServerResult } from '../types.js'; import { capture } from '../utils/capture.js'; /** * Handle start_search command */ export async function handleStartSearch(args: unknown): Promise<ServerResult> { const parsed = StartSearchArgsSchema.safeParse(args); if (!parsed.success) { return { content: [{ type: "text", text: `Invalid arguments for start_search: ${parsed.error}` }], isError: true, }; } try { const result = await searchManager.startSearch({ rootPath: parsed.data.path, pattern: parsed.data.pattern, searchType: parsed.data.searchType, filePattern: parsed.data.filePattern, ignoreCase: parsed.data.ignoreCase, maxResults: parsed.data.maxResults, includeHidden: parsed.data.includeHidden, contextLines: parsed.data.contextLines, timeout: parsed.data.timeout_ms, earlyTermination: parsed.data.earlyTermination, literalSearch: parsed.data.literalSearch, }); const searchTypeText = parsed.data.searchType === 'content' ? 'content search' : 'file search'; let output = `Started ${searchTypeText} session: ${result.sessionId}\n`; output += `Pattern: "${parsed.data.pattern}"\n`; output += `Path: ${parsed.data.path}\n`; output += `Status: ${result.isComplete ? 'COMPLETED' : 'RUNNING'}\n`; output += `Runtime: ${Math.round(result.runtime)}ms\n`; output += `Total results: ${result.totalResults}\n\n`; if (result.results.length > 0) { output += "Initial results:\n"; for (const searchResult of result.results.slice(0, 10)) { if (searchResult.type === 'content') { output += `📄 ${searchResult.file}:${searchResult.line} - ${searchResult.match?.substring(0, 100)}${searchResult.match && searchResult.match.length > 100 ? '...' : ''}\n`; } else { output += `📁 ${searchResult.file}\n`; } } if (result.results.length > 10) { output += `... and ${result.results.length - 10} more results\n`; } } if (result.isComplete) { output += `\n✅ Search completed.`; } else { output += `\n🔄 Search in progress. Use get_more_search_results to get more results.`; } return { content: [{ type: "text", text: output }], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); capture('search_session_start_error', { error: errorMessage }); return { content: [{ type: "text", text: `Error starting search session: ${errorMessage}` }], isError: true, }; } } /** * Handle get_more_search_results command */ export async function handleGetMoreSearchResults(args: unknown): Promise<ServerResult> { const parsed = GetMoreSearchResultsArgsSchema.safeParse(args); if (!parsed.success) { return { content: [{ type: "text", text: `Invalid arguments for get_more_search_results: ${parsed.error}` }], isError: true, }; } try { const results = searchManager.readSearchResults( parsed.data.sessionId, parsed.data.offset, parsed.data.length ); // Only return error if we have no results AND there's an actual error // Permission errors should not block returning found results if (results.isError && results.totalResults === 0 && results.error?.trim()) { return { content: [{ type: "text", text: `Search session ${parsed.data.sessionId} encountered an error: ${results.error}` }], isError: true, }; } // Format results for display let output = `Search session: ${parsed.data.sessionId}\n`; output += `Status: ${results.isComplete ? 'COMPLETED' : 'IN PROGRESS'}\n`; output += `Runtime: ${Math.round(results.runtime / 1000)}s\n`; output += `Total results found: ${results.totalResults} (${results.totalMatches} matches)\n`; const offset = parsed.data.offset; if (offset < 0) { // Negative offset - tail behavior output += `Showing last ${results.returnedCount} results\n\n`; } else { // Positive offset - range behavior const startPos = offset; const endPos = startPos + results.returnedCount - 1; output += `Showing results ${startPos}-${endPos}\n\n`; } if (results.results.length === 0) { if (results.isComplete) { output += results.totalResults === 0 ? "No matches found." : "No results in this range."; } else { output += "No results yet, search is still running..."; } } else { output += "Results:\n"; for (const result of results.results) { if (result.type === 'content') { output += `📄 ${result.file}:${result.line} - ${result.match?.substring(0, 100)}${result.match && result.match.length > 100 ? '...' : ''}\n`; } else { output += `📁 ${result.file}\n`; } } } // Add pagination hints if (offset >= 0 && results.hasMoreResults) { const nextOffset = offset + results.returnedCount; output += `\n📖 More results available. Use get_more_search_results with offset: ${nextOffset}`; } if (results.isComplete) { output += `\n✅ Search completed.`; // Warn users if search was incomplete due to permission issues if (results.wasIncomplete) { output += `\n⚠️ Warning: Some files were inaccessible due to permissions. Results may be incomplete.`; } } return { content: [{ type: "text", text: output }], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [{ type: "text", text: `Error reading search results: ${errorMessage}` }], isError: true, }; } } /** * Handle stop_search command */ export async function handleStopSearch(args: unknown): Promise<ServerResult> { const parsed = StopSearchArgsSchema.safeParse(args); if (!parsed.success) { return { content: [{ type: "text", text: `Invalid arguments for stop_search: ${parsed.error}` }], isError: true, }; } try { const success = searchManager.terminateSearch(parsed.data.sessionId); if (success) { return { content: [{ type: "text", text: `Search session ${parsed.data.sessionId} terminated successfully.` }], }; } else { return { content: [{ type: "text", text: `Search session ${parsed.data.sessionId} not found or already completed.` }], }; } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [{ type: "text", text: `Error terminating search session: ${errorMessage}` }], isError: true, }; } } /** * Handle list_searches command */ export async function handleListSearches(): Promise<ServerResult> { try { const sessions = searchManager.listSearchSessions(); if (sessions.length === 0) { return { content: [{ type: "text", text: "No active searches." }], }; } let output = `Active Searches (${sessions.length}):\n\n`; for (const session of sessions) { const status = session.isComplete ? (session.isError ? '❌ ERROR' : '✅ COMPLETED') : '🔄 RUNNING'; output += `Session: ${session.id}\n`; output += ` Type: ${session.searchType}\n`; output += ` Pattern: "${session.pattern}"\n`; output += ` Status: ${status}\n`; output += ` Runtime: ${Math.round(session.runtime / 1000)}s\n`; output += ` Results: ${session.totalResults}\n\n`; } return { content: [{ type: "text", text: output }], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [{ type: "text", text: `Error listing search sessions: ${errorMessage}` }], isError: true, }; } }

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/wonderwhy-er/DesktopCommanderMCP'

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