Skip to main content
Glama
andyl25

Google Cloud MCP Server

by andyl25

logs-time-range

Extract log entries within a specified time range for Google Cloud services. Define start time, end time, filter criteria, and limit results for targeted log analysis.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
endTimeNoEnd time in ISO format (defaults to now)
filterNoAdditional filter criteria
limitNoMaximum number of log entries to return
startTimeYesStart time in ISO format or relative time (e.g., "1h", "2d")

Implementation Reference

  • Main execution logic for the 'logs-time-range' tool: parses time range, builds Logging API filter, fetches and formats log entries.
    async ({ startTime, endTime, filter, limit }, _extra) => { try { const projectId = await getProjectId(); const logging = getLoggingClient(); const start = parseRelativeTime(startTime); const end = endTime ? parseRelativeTime(endTime) : new Date(); // Build filter string let filterStr = `timestamp >= "${start.toISOString()}" AND timestamp <= "${end.toISOString()}"`; if (filter) { filterStr = `${filterStr} AND ${filter}`; } const [entries] = await logging.getEntries({ pageSize: limit, filter: filterStr }); if (!entries || entries.length === 0) { return { content: [{ type: 'text', text: `No log entries found in the specified time range with filter: ${filterStr}` }] }; } const formattedLogs = entries .map((entry) => { try { return formatLogEntry(entry as unknown as LogEntry); } catch (err: unknown) { const errorMessage = err instanceof Error ? err.message : 'Unknown error'; return `## Error Formatting Log Entry\n\nAn error occurred while formatting a log entry: ${errorMessage}`; } }) .join('\n\n'); return { content: [{ type: 'text', text: `# Log Time Range Results\n\nProject: ${projectId}\nTime Range: ${start.toISOString()} to ${end.toISOString()}\nFilter: ${filter || 'None'}\nEntries: ${entries.length}\n\n${formattedLogs}` }] }; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; // Return a user-friendly error message instead of throwing return { content: [{ type: 'text', text: `# Error Querying Logs An error occurred while querying logs: ${errorMessage} Please check your time range format and try again. Valid formats include: - ISO date strings (e.g., "2025-03-01T00:00:00Z") - Relative time expressions: "1h" (1 hour ago), "2d" (2 days ago), "1w" (1 week ago), etc.` }], isError: true }; } } );
  • Input schema using Zod for validation of tool parameters.
    { startTime: z.string().describe('Start time in ISO format or relative time (e.g., "1h", "2d")'), endTime: z.string().optional().describe('End time in ISO format (defaults to now)'), filter: z.string().optional().describe('Additional filter criteria'), limit: z.number().min(1).max(1000).default(50).describe('Maximum number of log entries to return') },
  • Registration of the 'logs-time-range' tool on the MCP server using server.tool().
    server.tool( 'logs-time-range', { startTime: z.string().describe('Start time in ISO format or relative time (e.g., "1h", "2d")'), endTime: z.string().optional().describe('End time in ISO format (defaults to now)'), filter: z.string().optional().describe('Additional filter criteria'), limit: z.number().min(1).max(1000).default(50).describe('Maximum number of log entries to return') }, async ({ startTime, endTime, filter, limit }, _extra) => { try { const projectId = await getProjectId(); const logging = getLoggingClient(); const start = parseRelativeTime(startTime); const end = endTime ? parseRelativeTime(endTime) : new Date(); // Build filter string let filterStr = `timestamp >= "${start.toISOString()}" AND timestamp <= "${end.toISOString()}"`; if (filter) { filterStr = `${filterStr} AND ${filter}`; } const [entries] = await logging.getEntries({ pageSize: limit, filter: filterStr }); if (!entries || entries.length === 0) { return { content: [{ type: 'text', text: `No log entries found in the specified time range with filter: ${filterStr}` }] }; } const formattedLogs = entries .map((entry) => { try { return formatLogEntry(entry as unknown as LogEntry); } catch (err: unknown) { const errorMessage = err instanceof Error ? err.message : 'Unknown error'; return `## Error Formatting Log Entry\n\nAn error occurred while formatting a log entry: ${errorMessage}`; } }) .join('\n\n'); return { content: [{ type: 'text', text: `# Log Time Range Results\n\nProject: ${projectId}\nTime Range: ${start.toISOString()} to ${end.toISOString()}\nFilter: ${filter || 'None'}\nEntries: ${entries.length}\n\n${formattedLogs}` }] }; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; // Return a user-friendly error message instead of throwing return { content: [{ type: 'text', text: `# Error Querying Logs An error occurred while querying logs: ${errorMessage} Please check your time range format and try again. Valid formats include: - ISO date strings (e.g., "2025-03-01T00:00:00Z") - Relative time expressions: "1h" (1 hour ago), "2d" (2 days ago), "1w" (1 week ago), etc.` }], isError: true }; } } );
  • Helper function to format individual log entries into readable markdown.
    export function formatLogEntry(entry: LogEntry): string { // Safely format the timestamp let timestamp: string; try { // Check if timestamp exists and is valid if (!entry.timestamp) { timestamp = 'No timestamp'; } else { const date = new Date(entry.timestamp); timestamp = !isNaN(date.getTime()) ? date.toISOString() : String(entry.timestamp); } } catch (error) { timestamp = String(entry.timestamp || 'Invalid timestamp'); } const severity = entry.severity || 'DEFAULT'; const resourceType = entry.resource?.type || 'unknown'; const resourceLabels = entry.resource?.labels ? Object.entries(entry.resource.labels) .map(([k, v]) => `${k}=${v}`) .join(', ') : ''; const resource = resourceLabels ? `${resourceType}(${resourceLabels})` : resourceType; // Format the payload with better error handling let payload: string; try { if (entry.textPayload !== undefined && entry.textPayload !== null) { payload = String(entry.textPayload); } else if (entry.jsonPayload) { payload = JSON.stringify(entry.jsonPayload, null, 2); } else if (entry.protoPayload) { payload = JSON.stringify(entry.protoPayload, null, 2); } else if (entry.data) { payload = JSON.stringify(entry.data, null, 2); } else { payload = '[No payload]'; } } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; payload = `[Error formatting payload: ${errorMessage}]`; } // Format labels if they exist let labelsStr = ''; if (entry.labels && Object.keys(entry.labels).length > 0) { try { labelsStr = Object.entries(entry.labels) .map(([k, v]) => `${k}=${v}`) .join(', '); } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; labelsStr = `[Error formatting labels: ${errorMessage}]`; } } // Format operation if it exists let operationStr = ''; if (entry.operation) { try { const op = entry.operation as Record<string, any>; operationStr = [ op.id ? `id=${op.id}` : '', op.producer ? `producer=${op.producer}` : '', op.first ? 'first=true' : '', op.last ? 'last=true' : '' ].filter(Boolean).join(', '); } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; operationStr = `[Error formatting operation: ${errorMessage}]`; } } // Create a more detailed and markdown-friendly format return `## ${timestamp} | ${severity} | ${resource} ` + (entry.logName ? `**Log:** ${entry.logName}\n` : '') + (entry.insertId ? `**ID:** ${entry.insertId}\n` : '') + (labelsStr ? `**Labels:** ${labelsStr}\n` : '') + (operationStr ? `**Operation:** ${operationStr}\n` : '') + `\n\`\`\`\n${payload}\n\`\`\``; }
  • Utility to parse startTime/endTime relative strings (e.g. '1h') or ISO dates into Date objects.
    export function parseRelativeTime(timeString: string): Date { // If it's an ISO date string, parse it directly if (timeString.includes('T') && timeString.includes(':')) { const date = new Date(timeString); if (!isNaN(date.getTime())) { return date; } } // Parse relative time format (e.g., "1h", "2d", "30m") const match = timeString.match(/^(\d+)([smhdw])$/); if (!match) { throw new GcpMcpError( `Invalid time format: ${timeString}. Use format like "30s", "5m", "2h", "1d", or "1w".`, 'INVALID_ARGUMENT', 400 ); } const value = parseInt(match[1]); const unit = match[2]; const now = new Date(); switch (unit) { case 's': // seconds return new Date(now.getTime() - value * 1000); case 'm': // minutes return new Date(now.getTime() - value * 60 * 1000); case 'h': // hours return new Date(now.getTime() - value * 60 * 60 * 1000); case 'd': // days return new Date(now.getTime() - value * 24 * 60 * 60 * 1000); case 'w': // weeks return new Date(now.getTime() - value * 7 * 24 * 60 * 60 * 1000); default: throw new GcpMcpError( `Invalid time unit: ${unit}. Use s, m, h, d, or w.`, 'INVALID_ARGUMENT', 400 ); } }

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/andyl25/googlecloud-mcp'

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