Skip to main content
Glama

thermoworks_analyze_live

Monitor live BBQ temperatures from ThermoWorks devices and receive cooking progress analysis with recommendations based on protein type and target temperatures.

Instructions

Get live temperature from a connected ThermoWorks device and analyze cooking progress.

Combines real-time device data with the BBQ cooking knowledge base to provide actionable recommendations.

Requires authentication first via thermoworks_authenticate.

Args:

  • device_serial: Serial number of the device

  • probe_id: Probe number to analyze (default: '1')

  • protein_type: Type of protein being cooked

  • target_temp: Target temperature (optional, uses protein default)

  • response_format: 'markdown' or 'json'

Returns: Current temperature, progress percentage, trend analysis, and recommendations.

Examples:

  • "How's my brisket doing?" -> Analyzes probe 1 against brisket targets

  • "Check the turkey on probe 2" -> protein_type='turkey_whole', probe_id='2'

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
device_serialYesSerial number of the device to analyze
probe_idNoProbe number to analyze (e.g., '1', '2', '3', '4' for Signals)1
protein_typeYesType of protein being cooked (e.g., 'beef_brisket')
target_tempNoTarget temperature. If not provided, uses recommended temp for the protein.
response_formatNoOutput formatmarkdown

Implementation Reference

  • Primary handler implementation: fetches live temperature readings from ThermoWorks device using getDeviceReadings, determines protein target temperature, analyzes progress with analyzeTemperature helper, and returns formatted markdown or JSON response with recommendations.
    async (params: AnalyzeLiveTemperatureInput) => { try { const client = getThermoWorksClient(); if (!client.isAuthenticated()) { return { isError: true, content: [ { type: "text", text: "Not authenticated. Use `thermoworks_authenticate` first.", }, ], }; } // Get live reading const reading = await client.getDeviceReadings(params.device_serial); if (!reading) { return { isError: true, content: [ { type: "text", text: `No readings available for device ${params.device_serial}. Make sure the device is powered on.`, }, ], }; } // Find the specified probe const probeData = reading.probes[params.probe_id]; if (!probeData) { const availableProbes = Object.keys(reading.probes).join(", "); return { isError: true, content: [ { type: "text", text: `Probe ${params.probe_id} not found. Available probes: ${availableProbes}`, }, ], }; } // Get target temperature const proteinType = params.protein_type as ProteinType; const { targetTemp, pullTemp, doneness } = getTargetTemperature(proteinType); const target = params.target_temp || targetTemp; // Analyze the temperature const analysis = analyzeTemperature( probeData.temp, target, proteinType, undefined, // No cook method from device undefined, // No start time undefined // No previous readings yet ); const profile = getProteinProfile(proteinType); if (params.response_format === "json") { const output = { device: { serial: reading.serial, name: reading.name, probe: params.probe_id, probeName: probeData.name, }, reading: { currentTemp: probeData.temp, unit: reading.unit, timestamp: reading.timestamp, }, target: { temp: target, pullTemp, doneness, }, analysis, protein: { type: proteinType, displayName: profile.displayName, }, }; return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }], structuredContent: output, }; } let markdown = `## 🔴 Live Analysis: ${profile.displayName}\n\n`; markdown += `**Device:** ${reading.name} - ${probeData.name || `Probe ${params.probe_id}`}\n`; markdown += `**Current Temp:** ${probeData.temp}°${reading.unit}\n`; markdown += `**Target:** ${target}°F (${DONENESS_INFO[doneness]?.displayName || doneness})\n`; markdown += `**Pull At:** ${pullTemp}°F\n\n`; markdown += `### Progress\n\n`; markdown += `**${analysis.percentComplete}% complete** (${analysis.tempDelta}°F to go)\n\n`; if (analysis.inStallZone) { markdown += `⚠️ **In the stall zone!** Temperature may plateau.\n\n`; } if (analysis.recommendations.length > 0) { markdown += `### Recommendations\n\n`; for (const rec of analysis.recommendations) { markdown += `${rec}\n`; } } return { content: [{ type: "text", text: markdown }], }; } catch (error) { const message = error instanceof Error ? error.message : "Failed to analyze"; return { isError: true, content: [{ type: "text", text: `Error: ${message}` }], }; } }
  • Zod input schema defining parameters: device_serial (required), probe_id (default '1'), protein_type, optional target_temp and response_format.
    * Schema for analyzing live temperature from connected device */ export const AnalyzeLiveTemperatureSchema = z .object({ device_serial: z .string() .describe("Serial number of the device to analyze"), probe_id: z .string() .default("1") .describe("Probe number to analyze (e.g., '1', '2', '3', '4' for Signals)"), protein_type: z .string() .describe("Type of protein being cooked (e.g., 'beef_brisket')"), target_temp: z .number() .optional() .describe("Target temperature. If not provided, uses recommended temp for the protein."), response_format: ResponseFormatSchema.describe("Output format"), }) .strict(); export type AnalyzeLiveTemperatureInput = z.infer<typeof AnalyzeLiveTemperatureSchema>;
  • src/index.ts:1306-1462 (registration)
    MCP server tool registration including title, description, input schema reference, annotations, and inline handler function.
    server.registerTool( "thermoworks_analyze_live", { title: "Analyze Live Temperature", description: `Get live temperature from a connected ThermoWorks device and analyze cooking progress. Combines real-time device data with the BBQ cooking knowledge base to provide actionable recommendations. Requires authentication first via thermoworks_authenticate. Args: - device_serial: Serial number of the device - probe_id: Probe number to analyze (default: '1') - protein_type: Type of protein being cooked - target_temp: Target temperature (optional, uses protein default) - response_format: 'markdown' or 'json' Returns: Current temperature, progress percentage, trend analysis, and recommendations. Examples: - "How's my brisket doing?" -> Analyzes probe 1 against brisket targets - "Check the turkey on probe 2" -> protein_type='turkey_whole', probe_id='2'`, inputSchema: AnalyzeLiveTemperatureSchema, annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: false, openWorldHint: true, }, }, async (params: AnalyzeLiveTemperatureInput) => { try { const client = getThermoWorksClient(); if (!client.isAuthenticated()) { return { isError: true, content: [ { type: "text", text: "Not authenticated. Use `thermoworks_authenticate` first.", }, ], }; } // Get live reading const reading = await client.getDeviceReadings(params.device_serial); if (!reading) { return { isError: true, content: [ { type: "text", text: `No readings available for device ${params.device_serial}. Make sure the device is powered on.`, }, ], }; } // Find the specified probe const probeData = reading.probes[params.probe_id]; if (!probeData) { const availableProbes = Object.keys(reading.probes).join(", "); return { isError: true, content: [ { type: "text", text: `Probe ${params.probe_id} not found. Available probes: ${availableProbes}`, }, ], }; } // Get target temperature const proteinType = params.protein_type as ProteinType; const { targetTemp, pullTemp, doneness } = getTargetTemperature(proteinType); const target = params.target_temp || targetTemp; // Analyze the temperature const analysis = analyzeTemperature( probeData.temp, target, proteinType, undefined, // No cook method from device undefined, // No start time undefined // No previous readings yet ); const profile = getProteinProfile(proteinType); if (params.response_format === "json") { const output = { device: { serial: reading.serial, name: reading.name, probe: params.probe_id, probeName: probeData.name, }, reading: { currentTemp: probeData.temp, unit: reading.unit, timestamp: reading.timestamp, }, target: { temp: target, pullTemp, doneness, }, analysis, protein: { type: proteinType, displayName: profile.displayName, }, }; return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }], structuredContent: output, }; } let markdown = `## 🔴 Live Analysis: ${profile.displayName}\n\n`; markdown += `**Device:** ${reading.name} - ${probeData.name || `Probe ${params.probe_id}`}\n`; markdown += `**Current Temp:** ${probeData.temp}°${reading.unit}\n`; markdown += `**Target:** ${target}°F (${DONENESS_INFO[doneness]?.displayName || doneness})\n`; markdown += `**Pull At:** ${pullTemp}°F\n\n`; markdown += `### Progress\n\n`; markdown += `**${analysis.percentComplete}% complete** (${analysis.tempDelta}°F to go)\n\n`; if (analysis.inStallZone) { markdown += `⚠️ **In the stall zone!** Temperature may plateau.\n\n`; } if (analysis.recommendations.length > 0) { markdown += `### Recommendations\n\n`; for (const rec of analysis.recommendations) { markdown += `${rec}\n`; } } return { content: [{ type: "text", text: markdown }], }; } catch (error) { const message = error instanceof Error ? error.message : "Failed to analyze"; return { isError: true, content: [{ type: "text", text: `Error: ${message}` }], }; } } );
  • Simplified handler and registration for Smithery deployment using inline Zod schema and abbreviated logic.
    server.tool( "thermoworks_analyze_live", "Analyze live temp against cooking targets", { device_serial: z.string(), probe_id: z.string().default("1"), protein_type: z.string(), target_temp: z.number().optional(), }, async ({ device_serial, probe_id, protein_type, target_temp }) => { try { const client = getThermoWorksClient(); if (!client.isAuthenticated()) { return { content: [{ type: "text", text: "Not authenticated" }], isError: true }; } const reading = await client.getDeviceReadings(device_serial); if (!reading) return { content: [{ type: "text", text: "No reading" }], isError: true }; const probe = reading.probes[probe_id]; if (!probe) return { content: [{ type: "text", text: `No probe ${probe_id}` }], isError: true }; const { targetTemp } = getTargetTemperature(protein_type as ProteinType); const target = target_temp || targetTemp; const analysis = analyzeTemperature(probe.temp, target, protein_type as ProteinType); let text = `## ${getProteinProfile(protein_type as ProteinType).displayName}\n\n`; text += `**Current:** ${probe.temp}°${reading.unit} | **Target:** ${target}°F\n`; text += `**Progress:** ${analysis.percentComplete}%\n`; if (analysis.inStallZone) text += `⚠️ In stall zone\n`; return { content: [{ type: "text", text }] }; } catch (error) { const message = error instanceof Error ? error.message : "Error"; return { content: [{ type: "text", text: message }], isError: true }; } } );

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/jweingardt12/bbq-mcp'

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