archive-org-mcp 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/api.js +54 -0
- package/dist/server.js +2 -3
- package/package.json +1 -1
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -24,4 +24,9 @@ node dist/index.js
|
|
|
24
24
|
|
|
25
25
|
Public free APIs only. See `src/api.ts` for exact endpoints.
|
|
26
26
|
|
|
27
|
+
## Licence — no paywall
|
|
27
28
|
|
|
29
|
+
Every tool in this package is free: no licence key, no premium tier, no hosted
|
|
30
|
+
licence gateway. The paywall machinery this repo once carried (per-tool licence
|
|
31
|
+
checks and paid hosted endpoints) was removed repo-wide on 2026-10-17. Nothing
|
|
32
|
+
that this package installs or calls asks you to pay.
|
package/dist/api.js
CHANGED
|
@@ -289,3 +289,57 @@ export async function searchProfile(args) {
|
|
|
289
289
|
lines.push(" every figure above is tallied from the rows the search returned, and the sample size is stated rather than implied.");
|
|
290
290
|
return lines.join("\n");
|
|
291
291
|
}
|
|
292
|
+
|
|
293
|
+
const CDX = 'http://web.archive.org/cdx/search/cdx';
|
|
294
|
+
export async function snapshotTimeline(args = {}) {
|
|
295
|
+
const raw = String(args?.url ?? '').trim();
|
|
296
|
+
if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(raw))
|
|
297
|
+
throw new Error('Give a full URL including the scheme, like https://example.com/page.html');
|
|
298
|
+
const limit = Math.min(Math.max(Number(args?.limit ?? 500) || 500, 1), 3000);
|
|
299
|
+
const qs = new URLSearchParams({
|
|
300
|
+
url: raw,
|
|
301
|
+
output: 'json',
|
|
302
|
+
limit: String(limit),
|
|
303
|
+
fl: 'timestamp,original,mimetype,statuscode,digest,length',
|
|
304
|
+
});
|
|
305
|
+
if (args?.from)
|
|
306
|
+
qs.set('from', `${args.from}0101`);
|
|
307
|
+
if (args?.to)
|
|
308
|
+
qs.set('to', `${args.to}1231`);
|
|
309
|
+
const res = await fetch(`${CDX}?${qs}`, {
|
|
310
|
+
headers: { 'User-Agent': 'mrfentmen-archive-org-mcp/1.0', Accept: 'application/json' },
|
|
311
|
+
signal: AbortSignal.timeout(40000),
|
|
312
|
+
});
|
|
313
|
+
if (!res.ok)
|
|
314
|
+
throw new Error(`Wayback CDX returned ${res.status}`);
|
|
315
|
+
const body = (await res.json());
|
|
316
|
+
if (!Array.isArray(body) || body.length < 2)
|
|
317
|
+
throw new Error(`The Wayback Machine has no captures for ${raw} in that window`);
|
|
318
|
+
const [head, ...rows] = body;
|
|
319
|
+
const idx = (name) => head.indexOf(name);
|
|
320
|
+
const ts = idx('timestamp'), mime = idx('mimetype'), code = idx('statuscode'), digest = idx('digest'), len = idx('length');
|
|
321
|
+
const ok = rows.filter((r) => String(r[code]) === '200');
|
|
322
|
+
const digests = new Set(ok.map((r) => r[digest]).filter(Boolean));
|
|
323
|
+
const mimes = new Map();
|
|
324
|
+
let bytes = 0;
|
|
325
|
+
const perYear = new Map();
|
|
326
|
+
for (const r of rows) {
|
|
327
|
+
mimes.set(r[mime], (mimes.get(r[mime]) ?? 0) + 1);
|
|
328
|
+
bytes += Number(r[len] ?? 0);
|
|
329
|
+
const year = String(r[ts]).slice(0, 4);
|
|
330
|
+
perYear.set(year, (perYear.get(year) ?? 0) + 1);
|
|
331
|
+
}
|
|
332
|
+
const topMimes = [...mimes.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5);
|
|
333
|
+
return [
|
|
334
|
+
`${raw} — ${rows.length} capture(s) read`,
|
|
335
|
+
`Successful (200): ${ok.length} · distinct content digests: ${digests.size} · repeats of an archived page: ${ok.length - digests.size}`,
|
|
336
|
+
`Window: ${String(rows[0]?.[ts]).slice(0, 4)} to ${String(rows[rows.length - 1]?.[ts]).slice(0, 4)}`,
|
|
337
|
+
`Payload recorded: ${(bytes / 1048576).toFixed(2)} MB across the captures`,
|
|
338
|
+
`Media types: ${topMimes.map(([m, n]) => `${m} (${n})`).join(', ') || 'none reported'}`,
|
|
339
|
+
'',
|
|
340
|
+
'Captures per year:',
|
|
341
|
+
...[...perYear.entries()].sort().map(([y, n]) => `${y}: ${n}`),
|
|
342
|
+
'',
|
|
343
|
+
`Oldest: http://web.archive.org/web/${rows[0]?.[ts]}/${raw}`,
|
|
344
|
+
].join('\n');
|
|
345
|
+
}
|
package/dist/server.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { m0_fmtSize, m0_formatItem, m0_getItemDetails, m0_searchItems, m1_formatSnapshot, m1_getAvailability, m1_getSnapshots, m1_getSnapshotText, searchProfile } from "./api.js";
|
|
3
|
+
import { m0_fmtSize, m0_formatItem, m0_getItemDetails, m0_searchItems, m1_formatSnapshot, m1_getAvailability, m1_getSnapshots, m1_getSnapshotText, searchProfile, snapshotTimeline } from "./api.js";
|
|
4
4
|
const text = (value) => ({ content: [{ type: 'text', text: value }] });
|
|
5
5
|
const textError = (t) => ({ content: [{ type: "text", text: t }], isError: true });
|
|
6
6
|
const READ_ONLY = { readOnlyHint: true, openWorldHint: true };
|
|
@@ -119,9 +119,8 @@ export function createServer() {
|
|
|
119
119
|
}),
|
|
120
120
|
annotations: READ_ONLY,
|
|
121
121
|
}, async (args) => {
|
|
122
|
-
// TODO: implement timeline locally (paywall removed)
|
|
123
122
|
try {
|
|
124
|
-
return
|
|
123
|
+
return text(await snapshotTimeline(args));
|
|
125
124
|
}
|
|
126
125
|
catch (e) {
|
|
127
126
|
return textError(String(e));
|
package/package.json
CHANGED
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/mrfentmen/awesome-mcps",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "1.0.
|
|
9
|
+
"version": "1.0.6",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "archive-org-mcp",
|
|
14
|
-
"version": "1.0.
|
|
14
|
+
"version": "1.0.6",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
}
|