ctftime_events
Specify a time window with UNIX timestamps to list CTF events. Returns past and upcoming competitions from CTFtime.
Instructions
List CTFtime events in a time window. Uses UNIX timestamps (seconds) for start/finish; returns past and upcoming events.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max events (1-100). | |
| start | No | UNIX timestamp (seconds) for window start. | |
| finish | No | UNIX timestamp (seconds) for window finish. |
Implementation Reference
- src/index.ts:45-94 (registration)Registration of the 'ctftime_events' tool with server.registerTool(), including both the schema definition and the handler.
server.registerTool( "ctftime_events", { description: "List CTFtime events in a time window. Uses UNIX timestamps (seconds) for start/finish; returns past and upcoming events.", inputSchema: { limit: z .number() .int() .min(1) .max(100) .default(20) .describe("Max events (1-100)."), start: z .number() .int() .optional() .describe("UNIX timestamp (seconds) for window start."), finish: z .number() .int() .optional() .describe("UNIX timestamp (seconds) for window finish."), }, }, async ({ limit, start, finish }) => { const url = `${CTFtime_API_BASE}/events/${qs({ limit, start, finish })}`; const data = await getJson<any[]>(url); // Lightly normalize for LLM consumption (keep original fields too). const normalized = data.map((e) => ({ id: e.id, title: e.title, url: e.url, start: e.start, finish: e.finish, format: e.format, onsite: e.onsite, weight: e.weight, restrictions: e.restrictions, ctftime_url: e.ctftime_url, organizers: e.organizers, location: e.location, })); return { content: [{ type: "text", text: JSON.stringify(normalized, null, 2) }], }; } ); - src/index.ts:47-68 (schema)Input schema for ctftime_events: limit (optional, default 20, 1-100), start (optional UNIX timestamp), finish (optional UNIX timestamp).
{ description: "List CTFtime events in a time window. Uses UNIX timestamps (seconds) for start/finish; returns past and upcoming events.", inputSchema: { limit: z .number() .int() .min(1) .max(100) .default(20) .describe("Max events (1-100)."), start: z .number() .int() .optional() .describe("UNIX timestamp (seconds) for window start."), finish: z .number() .int() .optional() .describe("UNIX timestamp (seconds) for window finish."), }, - src/index.ts:70-93 (handler)Handler function: fetches events from CTFtime API, normalizes response data, returns JSON text content.
async ({ limit, start, finish }) => { const url = `${CTFtime_API_BASE}/events/${qs({ limit, start, finish })}`; const data = await getJson<any[]>(url); // Lightly normalize for LLM consumption (keep original fields too). const normalized = data.map((e) => ({ id: e.id, title: e.title, url: e.url, start: e.start, finish: e.finish, format: e.format, onsite: e.onsite, weight: e.weight, restrictions: e.restrictions, ctftime_url: e.ctftime_url, organizers: e.organizers, location: e.location, })); return { content: [{ type: "text", text: JSON.stringify(normalized, null, 2) }], }; } - src/index.ts:9-26 (helper)getJson helper: generic fetch wrapper with JSON parsing, error handling, and User-Agent header.
async function getJson<T>(url: string): Promise<T> { const res = await fetch(url, { headers: { Accept: "application/json", // CTFtime doesn't require a UA header for this API, but it helps with debugging and etiquette. "User-Agent": "mcp-ctftime/0.1.0 (+https://ctftime.org/api/)", }, }); if (!res.ok) { const text = await res.text().catch(() => ""); throw new Error( `CTFtime API error ${res.status} for ${url}${ text ? `: ${text.slice(0, 300)}` : "" }` ); } return (await res.json()) as T; } - src/index.ts:28-36 (helper)qs helper: builds URL query string from params object, skipping undefined values.
function qs(params: Record<string, string | number | undefined>): string { const u = new URLSearchParams(); for (const [k, v] of Object.entries(params)) { if (v === undefined) continue; u.set(k, String(v)); } const s = u.toString(); return s ? `?${s}` : ""; }