Skip to main content
Glama
jakenuts

SolarWinds Logs MCP Server

by jakenuts

search_logs

Search and filter SolarWinds Observability logs by time, group, entity, or keywords to troubleshoot issues and analyze system events.

Instructions

Search SolarWinds Observability logs with optional filtering

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
filterNoA search query string. Use AND/OR operators to combine terms (e.g., "error AND timeout"). The search is performed across all fields including message, hostname, program, and nested JSON fields like Context.CorrelationId. Field-specific queries like "field:value" are not supported.
groupNoFilter logs by a specific group name
entityIdNoFilter logs by a specific entity ID
startTimeNoUTC start time (ISO 8601 format)
endTimeNoUTC end time (ISO 8601 format)
directionNoSort order: backward (oldest to newest), forward (newest to oldest), or tail (oldest to newest)backward
pageSizeNoMaximum messages to return per page
skipTokenNoToken to skip to the next page of results

Implementation Reference

  • Registers the 'search_logs' MCP tool with description, input schema, and error-handling wrapper that delegates to searchLogs function.
    server.tool(
      'search_logs',
      'Search SolarWinds Observability logs with optional filtering',
      searchParamsSchema,
      async (args) => {
        try {
          const result = await searchLogs(apiClient, args);
          return {
            content: [
              {
                type: 'text',
                text: result,
              },
            ],
          };
        } catch (error) {
          const message = error instanceof Error ? error.message : String(error);
          return {
            content: [
              {
                type: 'text',
                text: `Error searching logs: ${message}`,
              },
            ],
            isError: true,
          };
        }
      }
    );
  • Core implementation of the search_logs tool: processes arguments, calls API, formats output with parameters, tips, metadata, and log entries.
    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;
      }
    }
  • Zod schema defining input parameters for the search_logs tool, including descriptions for MCP tool interface.
    export const searchParamsSchema = {
      filter: z.string().optional().describe('A search query string. Use AND/OR operators to combine terms (e.g., "error AND timeout"). The search is performed across all fields including message, hostname, program, and nested JSON fields like Context.CorrelationId. Field-specific queries like "field:value" are not supported.'),
      group: z.string().optional().describe('Filter logs by a specific group name'),
      entityId: z.string().optional().describe('Filter logs by a specific entity ID'),
      startTime: z.string().optional().describe('UTC start time (ISO 8601 format)'),
      endTime: z.string().optional().describe('UTC end time (ISO 8601 format)'),
      direction: z.enum(['backward', 'forward', 'tail']).default('backward').describe('Sort order: backward (oldest to newest), forward (newest to oldest), or tail (oldest to newest)'),
      pageSize: z.number().optional().default(50).describe('Maximum messages to return per page'),
      skipToken: z.string().optional().describe('Token to skip to the next page of results')
    } as const;
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It states the tool searches logs with filtering, but lacks details on permissions, rate limits, pagination behavior (beyond schema hints), or what the response looks like (e.g., format, error handling). For a search tool with 8 parameters and no annotation coverage, this is insufficient.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that front-loads the core purpose ('search SolarWinds Observability logs') and adds a key feature ('optional filtering'). There's no wasted text, and it's appropriately sized for the tool's complexity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (8 parameters, no annotations, no output schema), the description is incomplete. It doesn't address behavioral aspects like pagination, error handling, or response format, which are crucial for effective use. The schema covers parameters well, but the description fails to provide necessary context for a search operation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema description coverage is 100%, so the schema already documents all parameters thoroughly. The description adds no additional meaning beyond the schema, such as examples of complex queries or practical usage tips. Baseline 3 is appropriate as the schema does the heavy lifting, but the description doesn't compensate with extra insights.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb ('search') and resource ('SolarWinds Observability logs'), making the purpose evident. However, it doesn't explicitly differentiate from the sibling 'visualize_logs' tool, which likely serves a different purpose (visualization vs. searching). The mention of 'optional filtering' adds some specificity but could be more distinct.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives, such as the sibling 'visualize_logs'. It mentions 'optional filtering' but doesn't specify scenarios where filtering is necessary or when other tools might be more appropriate. There's no explicit when/when-not or alternative usage context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/jakenuts/mcp-solarwinds'

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