get_marathon
Provide a marathon ID or slug to receive race details, countdown, and Schema.org structured data.
Instructions
Get detailed information about a specific marathon including countdown and Schema.org data
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Marathon ID or slug, e.g. "tokyo", "boston", "berlin2026", "kobe2026" |
Implementation Reference
- index.js:181-210 (handler)Handler function for the get_marathon tool. Fetches marathon details from the API by ID/slug, with a fallback that strips the year suffix. Returns race details including date, city, country, timezone, countdown days, weather, course profile, and links.
async ({ id }) => { let data; try { data = await fetchJSON(`${BASE_URL}/api/marathons/${id}.json`); } catch { // Fallback: try slug without year for Tier 1 marathons const slug = id.replace(/\d{4}$/, ''); data = await fetchJSON(`${BASE_URL}/api/marathons/${slug}.json`); } const m = data.marathon; const raceDate = new Date(m.date); const now = new Date(); const daysUntil = Math.ceil((raceDate - now) / (1000 * 60 * 60 * 24)); let text = `## ${m.name.en}\n\nDate: ${m.date}\nCity: ${m.city}\n`; if (m.country) text += `Country/Region: ${m.country}\n`; text += `Timezone: ${m.timezone}\n`; text += `Days until race: ${daysUntil > 0 ? daysUntil : 'Race has passed'}\n`; if (m.weather) { text += `\n### Race Day Weather\nTemperature: ${m.weather.avgTempC}°C / ${m.weather.avgTempF}°F\n`; text += `Humidity: ${m.weather.humidity}% | Wind: ${m.weather.windKmh} km/h | Rain: ${m.weather.precipPct}%\n`; text += `Conditions: ${m.weather.conditions}\n`; } if (m.course) { text += `\n### Course Profile\nType: ${m.course.type} | Elevation: ${m.course.elevationGain}m | Terrain: ${m.course.terrain}\n`; text += `${m.course.profile}\n`; } text += `\nPage: ${m.links.page}\nCountdown: ${m.links.countdown}\n`; return { content: [{ type: 'text', text }] }; } ); - index.js:176-210 (registration)Registration of get_marathon tool via server.tool(). Defines the tool name, description, and input schema (id: string) along with the async handler.
// Tool: get_marathon server.tool( 'get_marathon', 'Get detailed information about a specific marathon including countdown and Schema.org data', { id: z.string().describe('Marathon ID or slug, e.g. "tokyo", "boston", "berlin2026", "kobe2026"') }, async ({ id }) => { let data; try { data = await fetchJSON(`${BASE_URL}/api/marathons/${id}.json`); } catch { // Fallback: try slug without year for Tier 1 marathons const slug = id.replace(/\d{4}$/, ''); data = await fetchJSON(`${BASE_URL}/api/marathons/${slug}.json`); } const m = data.marathon; const raceDate = new Date(m.date); const now = new Date(); const daysUntil = Math.ceil((raceDate - now) / (1000 * 60 * 60 * 24)); let text = `## ${m.name.en}\n\nDate: ${m.date}\nCity: ${m.city}\n`; if (m.country) text += `Country/Region: ${m.country}\n`; text += `Timezone: ${m.timezone}\n`; text += `Days until race: ${daysUntil > 0 ? daysUntil : 'Race has passed'}\n`; if (m.weather) { text += `\n### Race Day Weather\nTemperature: ${m.weather.avgTempC}°C / ${m.weather.avgTempF}°F\n`; text += `Humidity: ${m.weather.humidity}% | Wind: ${m.weather.windKmh} km/h | Rain: ${m.weather.precipPct}%\n`; text += `Conditions: ${m.weather.conditions}\n`; } if (m.course) { text += `\n### Course Profile\nType: ${m.course.type} | Elevation: ${m.course.elevationGain}m | Terrain: ${m.course.terrain}\n`; text += `${m.course.profile}\n`; } text += `\nPage: ${m.links.page}\nCountdown: ${m.links.countdown}\n`; return { content: [{ type: 'text', text }] }; } ); - index.js:180-181 (schema)Input schema for get_marathon: expects a single 'id' parameter (string) described as 'Marathon ID or slug, e.g. "tokyo", "boston", "berlin2026", "kobe2026"'.
{ id: z.string().describe('Marathon ID or slug, e.g. "tokyo", "boston", "berlin2026", "kobe2026"') }, async ({ id }) => {