Skip to main content
Glama
logs.ts3.91 kB
/** * tilt_logs tool * * Gets logs from a resource with optional filtering and tailing */ import { tool } from '@anthropic-ai/claude-agent-sdk'; import { TiltCliClient } from '../tilt/cli-client.js'; import { resolveTiltTarget } from '../tilt/config.js'; import { TiltConnection } from '../tilt/connection.js'; import { stripAnsiCodes } from '../tilt/transformers.js'; import { TiltResourceNotFoundError } from '../tilt/errors.js'; import { TiltLogsInput, type LogSearch, type TiltToolExtra, } from './schemas.js'; function dedupeFlags(flags: string): string { const seen = new Set<string>(); for (const flag of flags) { seen.add(flag); } return Array.from(seen).join(''); } function buildLogMatcher(search: LogSearch): (line: string) => boolean { const mode = search.mode ?? 'substring'; const caseSensitive = search.caseSensitive ?? true; if (mode === 'regex') { const baseFlags = search.flags ?? ''; const flags = caseSensitive || baseFlags.includes('i') ? baseFlags : `${baseFlags}i`; const finalFlags = dedupeFlags(flags); let regex: RegExp; try { regex = new RegExp(search.query, finalFlags); } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`Invalid search regex: ${message}`); } return (line) => regex.test(line); } const query = caseSensitive ? search.query : search.query.toLowerCase(); return (line) => { const target = caseSensitive ? line : line.toLowerCase(); return target.includes(query); }; } function filterLogs(logs: string, search?: LogSearch): string { if (!search) return logs; if (!logs) return ''; const hasTrailingNewline = logs.endsWith('\n'); const lines = hasTrailingNewline ? logs.slice(0, -1).split('\n') : logs.split('\n'); const matcher = buildLogMatcher(search); const filtered = lines.filter((line) => matcher(line)); if (filtered.length === 0) { return ''; } const joined = filtered.join('\n'); return hasTrailingNewline ? `${joined}\n` : joined; } export const tiltLogs = tool( 'tilt_logs', 'Read logs from a specific resource with optional tailing, Tilt-level filtering, and client-side search. ' + 'Returns plain text log output (same as Tilt CLI). The level parameter filters Tilt internal messages (build/resource warnings/errors), ' + 'not application log content. The source parameter filters by log origin (build vs runtime).', TiltLogsInput.shape, async (args, _extra) => { const extra = (_extra ?? {}) as TiltToolExtra; const { port, host } = resolveTiltTarget({ port: extra.tiltPort, host: extra.tiltHost, }); const binaryPath = extra.tiltBinaryPath; // Check if session is active first const connection = new TiltConnection({ port, host, binaryPath, }); await connection.checkSession(); // Get logs using CLI client const client = new TiltCliClient({ port, host, binaryPath, }); // Validate resource exists before attempting to get logs const resources = await client.getResources(); const resourceExists = resources.some( (r) => r.metadata.name === args.resourceName, ); if (!resourceExists) { throw new TiltResourceNotFoundError(args.resourceName); } const logOptions = { // Note: follow mode disabled - MCP tools must return a response follow: false, tailLines: args.tailLines ?? 100, level: args.level, source: args.source, }; const rawLogs = await client.getLogs(args.resourceName, logOptions); const cleanedLogs = stripAnsiCodes(rawLogs); const filteredLogs = filterLogs(cleanedLogs, args.search); return { content: [ { type: 'text' as const, text: filteredLogs, }, ], }; }, );

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/0xBigBoss/tilt-mcp'

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