UNPKG

mcp-wayback-machine

Version:

MCP server and CLI tool for interacting with the Wayback Machine without API keys

109 lines (107 loc) 4.54 kB
/** * Retrieve tool ΓÇö resolves archived URLs and fetches snapshot content. * Supports URL modifiers (id_, im_, js_, cs_) for different content views. */ import * as z from "zod"; import { AvailabilityResponse } from "../schemas.js"; import { HttpError } from "../utils/http.js"; import { capContent, HttpUrl, Timestamp } from "../utils/validation.js"; export const GetArchivedUrl = z.object({ url: HttpUrl.meta({ description: "The URL to retrieve from the Wayback Machine", }), timestamp: z .union([z.literal("latest"), Timestamp]) .optional() .meta({ description: 'Specific timestamp (YYYYMMDDhhmmss) or "latest" for most recent', }), modifier: z.enum(["id_", "im_", "js_", "cs_"]).optional().meta({ description: "URL modifier: id_ (raw content, no toolbar), im_ (screenshot image), js_ (JavaScript), cs_ (CSS). Default: id_", }), }); /** * * Get an archived version of a URL, optionally fetching the snapshot content. */ export async function getArchivedUrl(input, ctx) { const { url, timestamp, modifier } = input; try { const apiUrl = new URL("https://archive.org/wayback/available"); apiUrl.searchParams.set("url", url); if (timestamp !== undefined && timestamp !== "latest") { apiUrl.searchParams.set("timestamp", timestamp); } const data = await ctx.fetchJSON(apiUrl.toString(), AvailabilityResponse); if (data.archived_snapshots.closest?.available) { const snapshot = data.archived_snapshots.closest; const mod = modifier ?? "id_"; // Reconstruct the archived URL with the requested modifier. // `ts` comes from the Wayback availability API (server-controlled, // 14-digit string); `url` was validated by HttpUrl. const ts = snapshot.timestamp; const snapshotUrl = `https://web.archive.org/web/${ts}${mod}/${url}`; // Fetch the actual snapshot content const snapshotResponse = await ctx.fetch(snapshotUrl); const body = await snapshotResponse.text(); const contentType = snapshotResponse.headers.get("Content-Type") ?? "text/html"; return { success: true, message: `Found archived version of ${url}`, archivedUrl: snapshotUrl, timestamp: ts, available: true, content: capContent(body), contentType, }; } // If no snapshot found, try direct construction for specific timestamps. // `timestamp` here has been validated by the Timestamp schema (14 digits) // so it can't inject extra path segments into the URL. if (timestamp !== undefined && timestamp !== "latest") { const mod = modifier ?? "id_"; const directUrl = `https://web.archive.org/web/${timestamp}${mod}/${url}/`; try { const directResponse = await ctx.fetch(directUrl); const body = await directResponse.text(); const contentType = directResponse.headers.get("Content-Type") ?? "text/html"; return { success: true, message: "Retrieved snapshot via direct URL (availability API had no match)", archivedUrl: directUrl, timestamp, available: false, content: capContent(body), contentType, }; } catch { // Direct fetch also failed return { success: true, message: `No archived version found for ${url} at ${timestamp}`, archivedUrl: directUrl, timestamp, available: false, }; } } return { success: false, message: `No archived versions found for ${url}`, available: false, }; } catch (error) { if (error instanceof HttpError) { return { success: false, message: `Failed to retrieve archived URL: ${error.message}`, }; } return { success: false, message: `Failed to retrieve archived URL: ${error instanceof Error ? error.message : "Unknown error"}`, }; } } //# sourceMappingURL=retrieve.js.map