skim-mcp 0.1.5 → 0.1.7
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 +3 -0
- package/dist/index.js +28 -3
- package/dist/src/index.d.ts +2 -0
- package/package.json +8 -4
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
`skim-mcp` is the official Model Context Protocol server for [Skim](https://skim402.com) — the canonical [x402](https://x402.org) clean reader API. It exposes one tool, `read_url`, that your agent can call to fetch any web page as agent-ready Markdown plus structured metadata (title, byline, published date, language, excerpt). Each call costs **$0.002 in USDC on Base**, paid automatically by your local wallet over HTTP 402.
|
|
10
10
|
|
|
11
|
+
> **See it before you wire it:** [try Skim free in your browser](https://freeskims.skim402.com) — 10 free skims a day, no wallet, no signup. Paste a URL, see exactly what your agent gets back.
|
|
12
|
+
|
|
11
13
|
> _A 30-second demo GIF coming here soon._
|
|
12
14
|
|
|
13
15
|
---
|
|
@@ -105,6 +107,7 @@ That's it. One tool, one input, one shape of output. Designed to drop into any a
|
|
|
105
107
|
| `SKIM_WALLET_PRIVATE_KEY` | **yes** | — | Hex private key for the Base wallet that pays for reads. With or without `0x` prefix. Use a dedicated wallet — never your personal one. |
|
|
106
108
|
| `SKIM_MAX_PRICE_USD` | no | `0.01` | Hard cap on per-call price in USD. The wallet refuses to sign for anything above this. Skim is currently `$0.002`/call, well under the default cap — leave it alone unless tuning. |
|
|
107
109
|
| `SKIM_API_URL` | no | `https://skim402.com` | Override the API base URL. For self-hosting or local development. |
|
|
110
|
+
| `SKIM_TIMEOUT_MS` | no | `90000` | Hard deadline per call in milliseconds. Aborts stalled requests or payment steps so a single bad call can never hang your agent. Unsettled calls are never charged, so retry is safe. |
|
|
108
111
|
|
|
109
112
|
---
|
|
110
113
|
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,10 @@ import { z } from "zod";
|
|
|
7
7
|
const BASE_URL = (process.env.SKIM_API_URL ?? "https://skim402.com").replace(/\/+$/, "");
|
|
8
8
|
const PRIVATE_KEY = process.env.SKIM_WALLET_PRIVATE_KEY;
|
|
9
9
|
const MAX_PRICE_USD = process.env.SKIM_MAX_PRICE_USD ?? "0.01";
|
|
10
|
+
const TIMEOUT_MS = (() => {
|
|
11
|
+
const parsed = Number(process.env.SKIM_TIMEOUT_MS ?? "90000");
|
|
12
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 90_000;
|
|
13
|
+
})();
|
|
10
14
|
let payFetch = fetch;
|
|
11
15
|
let walletAddress = null;
|
|
12
16
|
if (PRIVATE_KEY) {
|
|
@@ -23,7 +27,7 @@ if (PRIVATE_KEY) {
|
|
|
23
27
|
}
|
|
24
28
|
const server = new McpServer({
|
|
25
29
|
name: "skim-mcp",
|
|
26
|
-
version: "0.1.
|
|
30
|
+
version: "0.1.6",
|
|
27
31
|
});
|
|
28
32
|
server.tool("read_url", "Fetch any URL and return clean, agent-ready Markdown via Skim (skim402.com). Strips nav, ads, and boilerplate; preserves the article body plus structured metadata (title, byline, published date, language, excerpt). Pays $0.002 per call in USDC on Base over the x402 protocol — no API keys, no signup. Use this whenever you need to read web content: articles, docs, blog posts, GitHub READMEs, research papers, etc.", {
|
|
29
33
|
url: z
|
|
@@ -44,11 +48,32 @@ server.tool("read_url", "Fetch any URL and return clean, agent-ready Markdown vi
|
|
|
44
48
|
}
|
|
45
49
|
let res;
|
|
46
50
|
try {
|
|
47
|
-
|
|
51
|
+
// Two layers of hang protection:
|
|
52
|
+
// 1. AbortSignal.timeout aborts the underlying HTTP requests (both the
|
|
53
|
+
// initial 402 handshake and the paid retry — x402-fetch reuses init).
|
|
54
|
+
// 2. Promise.race is a hard watchdog for anything inside the payment
|
|
55
|
+
// client that stalls without honoring the abort signal (e.g. a hung
|
|
56
|
+
// RPC or signing step), so a single bad call can never freeze the
|
|
57
|
+
// whole process.
|
|
58
|
+
const attempt = payFetch(`${BASE_URL}/api/v1/read`, {
|
|
48
59
|
method: "POST",
|
|
49
60
|
headers: { "Content-Type": "application/json" },
|
|
50
61
|
body: JSON.stringify({ url, mode: "basic" }),
|
|
62
|
+
signal: AbortSignal.timeout(TIMEOUT_MS),
|
|
51
63
|
});
|
|
64
|
+
let watchdog;
|
|
65
|
+
const deadline = new Promise((_, reject) => {
|
|
66
|
+
watchdog = setTimeout(() => reject(new Error(`timed out after ${TIMEOUT_MS}ms (SKIM_TIMEOUT_MS) — the request or payment client stalled`)), TIMEOUT_MS + 5_000);
|
|
67
|
+
});
|
|
68
|
+
try {
|
|
69
|
+
res = await Promise.race([attempt, deadline]);
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
clearTimeout(watchdog);
|
|
73
|
+
// If the watchdog won, make sure the losing fetch promise can't
|
|
74
|
+
// surface an unhandled rejection later.
|
|
75
|
+
attempt.catch(() => { });
|
|
76
|
+
}
|
|
52
77
|
}
|
|
53
78
|
catch (err) {
|
|
54
79
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -57,7 +82,7 @@ server.tool("read_url", "Fetch any URL and return clean, agent-ready Markdown vi
|
|
|
57
82
|
content: [
|
|
58
83
|
{
|
|
59
84
|
type: "text",
|
|
60
|
-
text: `Skim request failed: ${msg}. Common causes: wallet has no USDC on Base,
|
|
85
|
+
text: `Skim request failed: ${msg}. Common causes: wallet has no USDC on Base, the price exceeds SKIM_MAX_PRICE_USD (${MAX_PRICE_USD}), or a stalled network/payment connection (the call was aborted after ${TIMEOUT_MS}ms; retry is safe — you are not charged for unsettled calls).`,
|
|
61
86
|
},
|
|
62
87
|
],
|
|
63
88
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skim-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"mcpName": "io.github.JessieJanie/skim402",
|
|
5
5
|
"description": "MCP server for Skim — clean web reader for AI agents. Pays $0.002 per call in USDC over x402. No signup, no API keys.",
|
|
6
6
|
"type": "module",
|
|
@@ -17,10 +17,12 @@
|
|
|
17
17
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
18
18
|
"viem": "^2.21.0",
|
|
19
19
|
"x402-fetch": "^0.6.0",
|
|
20
|
-
"zod": "
|
|
20
|
+
"zod": "3.25.76"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
+
"@anthropic-ai/mcpb": "^2.0.0",
|
|
23
24
|
"@types/node": "^25.3.3",
|
|
25
|
+
"esbuild": "^0.27.0",
|
|
24
26
|
"tsx": "^4.21.0",
|
|
25
27
|
"typescript": "^5.9.0"
|
|
26
28
|
},
|
|
@@ -52,7 +54,9 @@
|
|
|
52
54
|
},
|
|
53
55
|
"scripts": {
|
|
54
56
|
"build": "tsc -p tsconfig.build.json && node -e \"require('fs').chmodSync('dist/index.js', 0o755)\"",
|
|
55
|
-
"typecheck": "tsc --
|
|
56
|
-
"dev": "tsx src/index.ts"
|
|
57
|
+
"typecheck": "tsc --build tsconfig.json",
|
|
58
|
+
"dev": "tsx src/index.ts",
|
|
59
|
+
"mcpb": "node mcpb-build.mjs",
|
|
60
|
+
"audit:buyers": "node buyer-audit.mjs"
|
|
57
61
|
}
|
|
58
62
|
}
|