skim-mcp 0.1.4 → 0.1.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 +2 -0
- package/dist/index.js +28 -3
- package/package.json +11 -11
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
|
---
|
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.6",
|
|
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",
|
|
@@ -13,21 +13,15 @@
|
|
|
13
13
|
"README.md",
|
|
14
14
|
"LICENSE"
|
|
15
15
|
],
|
|
16
|
-
"scripts": {
|
|
17
|
-
"build": "tsc -p tsconfig.build.json && node -e \"require('fs').chmodSync('dist/index.js', 0o755)\"",
|
|
18
|
-
"typecheck": "tsc --noEmit",
|
|
19
|
-
"dev": "tsx src/index.ts",
|
|
20
|
-
"prepublishOnly": "pnpm build"
|
|
21
|
-
},
|
|
22
16
|
"dependencies": {
|
|
23
17
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
24
18
|
"viem": "^2.21.0",
|
|
25
19
|
"x402-fetch": "^0.6.0",
|
|
26
|
-
"zod": "
|
|
20
|
+
"zod": "^3.25.76"
|
|
27
21
|
},
|
|
28
22
|
"devDependencies": {
|
|
29
|
-
"@types/node": "
|
|
30
|
-
"tsx": "
|
|
23
|
+
"@types/node": "^25.3.3",
|
|
24
|
+
"tsx": "^4.21.0",
|
|
31
25
|
"typescript": "^5.9.0"
|
|
32
26
|
},
|
|
33
27
|
"publishConfig": {
|
|
@@ -55,5 +49,11 @@
|
|
|
55
49
|
"license": "MIT",
|
|
56
50
|
"engines": {
|
|
57
51
|
"node": ">=18"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "tsc -p tsconfig.build.json && node -e \"require('fs').chmodSync('dist/index.js', 0o755)\"",
|
|
55
|
+
"typecheck": "tsc --noEmit",
|
|
56
|
+
"dev": "tsx src/index.ts",
|
|
57
|
+
"audit:buyers": "node buyer-audit.mjs"
|
|
58
58
|
}
|
|
59
|
-
}
|
|
59
|
+
}
|