SolarWinds Logs MCP Server
by jakenuts
- src
- tools
import { SolarWindsApiClient } from '../api-client.js';
import { SolarWindsSearchParams } from '../utils/types.js';
/**
* Search SolarWinds Observability logs
* @param apiClient SolarWinds Observability API client
* @param args Search parameters
* @returns Search results as formatted text
*/
export async function searchLogs(
apiClient: SolarWindsApiClient,
args: Record<string, any>
): Promise<string> {
try {
// Get current date/time
const now = new Date();
const oneDayAgo = new Date(now);
oneDayAgo.setDate(oneDayAgo.getDate() - 1);
// Validate and convert arguments to search parameters
const params: SolarWindsSearchParams = {
// Default limit to 50 results
pageSize: args.pageSize !== undefined ? args.pageSize : 50,
// Default direction to backward (oldest to newest)
direction: args.direction || 'backward'
};
// Only add time parameters if explicitly provided
// This allows API calls to work without time constraints
if (args.startTime !== undefined) {
params.startTime = args.startTime;
}
if (args.endTime !== undefined) {
params.endTime = args.endTime;
}
// Add optional parameters if provided
if (args.filter !== undefined) params.filter = args.filter;
if (args.group !== undefined) params.group = args.group;
if (args.entityId !== undefined) params.entityId = args.entityId;
if (args.skipToken !== undefined) params.skipToken = args.skipToken;
// Perform the search
const response = await apiClient.searchEvents(params);
// Format the response
let result = '';
// Add search parameters
result += 'Search Parameters:\n';
if (params.filter) result += `Query: ${params.filter}\n`;
if (params.entityId) result += `Entity ID: ${params.entityId}\n`;
if (params.group) result += `Group: ${params.group}\n`;
if (params.startTime) result += `Start Time: ${params.startTime}\n`;
if (params.endTime) result += `End Time: ${params.endTime}\n`;
if (params.direction) result += `Direction: ${params.direction}\n`;
if (params.pageSize) result += `Page Size: ${params.pageSize}\n`;
result += '\n';
// Add search tips
result += 'Search Tips:\n';
result += '- The search is performed across all fields, including nested JSON fields in the message.\n';
result += '- Use AND/OR operators to combine terms (e.g., "error AND timeout").\n';
result += '- The search does not support field-specific queries like "field:value".\n';
result += '- To search for logs with a specific ID in a nested field (like Context.CorrelationId),\n';
result += ' simply include the ID in your search query.\n';
result += '\n';
// Add search metadata
result += `Found ${response.logs.length} logs\n`;
if (response.pageInfo.nextPage) result += 'More logs available. Use skipToken to get the next page.\n';
result += '\n';
// Add events
if (response.logs.length === 0) {
result += 'No logs found matching the search criteria.\n';
} else {
result += 'Logs:\n';
for (const log of response.logs) {
result += `[${log.time}] ${log.hostname} ${log.program || ''}: ${log.message}\n`;
}
}
return result;
} catch (error) {
throw error;
}
}