UNPKG

mcp-wayback-machine

Version:

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

97 lines 3.68 kB
/** * Save URL tool - Archives a URL to the Wayback Machine */ import { z } from 'zod'; import { fetchWithTimeout, HttpError } from '../utils/http.js'; import { validateUrl } from '../utils/validation.js'; import { waybackRateLimiter } from '../utils/rate-limit.js'; export const SaveUrlSchema = z.object({ url: z.string().url().describe('The URL to save to the Wayback Machine'), }); /** * Save a URL to the Wayback Machine */ export async function saveUrl(input) { const { url } = input; try { // Validate URL const validatedUrl = validateUrl(url); // Check rate limit await waybackRateLimiter.waitForSlot(); // Make the save request const saveApiUrl = `https://web.archive.org/save/${encodeURIComponent(validatedUrl)}`; waybackRateLimiter.recordRequest(); const response = await fetchWithTimeout(saveApiUrl, { method: 'GET', headers: { 'User-Agent': 'mcp-wayback-machine/0.1.0', }, timeout: 60000, // 60 seconds for save operations }); // The save endpoint returns HTML, but includes a Location header // with the archived URL when successful const location = response.headers.get('Location'); const contentLocation = response.headers.get('Content-Location'); const archivedUrl = location || contentLocation; if (archivedUrl && archivedUrl.includes('/web/')) { // Extract timestamp from the archived URL const match = archivedUrl.match(/\/web\/(\d{14})\//); const timestamp = match ? match[1] : undefined; return { success: true, message: `Successfully submitted ${validatedUrl} for archiving`, archivedUrl: `https://web.archive.org${archivedUrl}`, timestamp, }; } // Try the save API endpoint (alternative method) const saveApiUrl2 = 'https://web.archive.org/save'; waybackRateLimiter.recordRequest(); const response2 = await fetchWithTimeout(saveApiUrl2, { method: 'POST', headers: { 'User-Agent': 'mcp-wayback-machine/0.1.0', 'Content-Type': 'application/x-www-form-urlencoded', }, body: `url=${encodeURIComponent(validatedUrl)}`, timeout: 60000, }); // Try to parse as JSON try { const data = await response2.json(); return { success: true, message: `Successfully submitted ${validatedUrl} for archiving`, jobId: data.job_id, archivedUrl: data.url, timestamp: data.timestamp, }; } catch { // If not JSON, assume success if we got a 200 return { success: true, message: `Successfully submitted ${validatedUrl} for archiving. Check status in a few moments.`, }; } } catch (error) { if (error instanceof HttpError) { if (error.status === 429) { return { success: false, message: 'Rate limit exceeded. Please try again later.', }; } return { success: false, message: `Failed to save URL: ${error.message}`, }; } return { success: false, message: `Failed to save URL: ${error instanceof Error ? error.message : 'Unknown error'}`, }; } } //# sourceMappingURL=save.js.map