web-refinery-mcp 0.1.0

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.
Files changed (3) hide show
  1. package/README.md +88 -0
  2. package/index.js +120 -0
  3. package/package.json +20 -0
package/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # Web Refinery MCP Server
2
+
3
+ MCP server that gives AI assistants the ability to extract clean content from any URL via the Web Refinery API. Payments are handled automatically using x402 USDC on Base.
4
+
5
+ ## Tools
6
+
7
+ | Tool | Cost | Description |
8
+ |------|------|-------------|
9
+ | `web_refinery_basic` | $0.003 USDC | Title, markdown content, word count, reading time |
10
+ | `web_refinery_structured` | $0.005 USDC | Full metadata: author, date, language, site name, etc. |
11
+
12
+ ## Setup
13
+
14
+ ```bash
15
+ cd mcp-server
16
+ npm install
17
+ ```
18
+
19
+ You need a wallet with Base mainnet ETH (gas) and USDC (payment).
20
+
21
+ ## Claude Desktop
22
+
23
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
24
+
25
+ ```json
26
+ {
27
+ "mcpServers": {
28
+ "web-refinery": {
29
+ "command": "node",
30
+ "args": ["/absolute/path/to/mcp-server/index.js"],
31
+ "env": {
32
+ "EVM_PRIVATE_KEY": "0x..."
33
+ }
34
+ }
35
+ }
36
+ }
37
+ ```
38
+
39
+ ## Cursor
40
+
41
+ Add to `.cursor/mcp.json` in your project root:
42
+
43
+ ```json
44
+ {
45
+ "mcpServers": {
46
+ "web-refinery": {
47
+ "command": "node",
48
+ "args": ["/absolute/path/to/mcp-server/index.js"],
49
+ "env": {
50
+ "EVM_PRIVATE_KEY": "0x..."
51
+ }
52
+ }
53
+ }
54
+ }
55
+ ```
56
+
57
+ ## Claude Code
58
+
59
+ Add to `~/.claude/claude_code_config.json`:
60
+
61
+ ```json
62
+ {
63
+ "mcpServers": {
64
+ "web-refinery": {
65
+ "command": "node",
66
+ "args": ["/absolute/path/to/mcp-server/index.js"],
67
+ "env": {
68
+ "EVM_PRIVATE_KEY": "0x..."
69
+ }
70
+ }
71
+ }
72
+ }
73
+ ```
74
+
75
+ ## Environment Variables
76
+
77
+ | Variable | Required | Default | Description |
78
+ |----------|----------|---------|-------------|
79
+ | `EVM_PRIVATE_KEY` | Yes | — | 0x-prefixed private key with Base USDC |
80
+ | `WEB_REFINERY_URL` | No | Production URL | API base URL override |
81
+
82
+ ## How It Works
83
+
84
+ 1. AI assistant calls `web_refinery_basic` or `web_refinery_structured` with a URL
85
+ 2. MCP server sends request to Web Refinery API
86
+ 3. API returns HTTP 402 with x402 payment requirements
87
+ 4. `@x402/axios` automatically signs a USDC payment and retries
88
+ 5. Clean article content is returned to the assistant
package/index.js ADDED
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Web Refinery MCP Server
5
+ *
6
+ * Exposes Web Refinery API as MCP tools for Claude Desktop, Cursor, etc.
7
+ * Uses @x402/axios for automatic USDC payment on Base mainnet.
8
+ *
9
+ * Environment:
10
+ * EVM_PRIVATE_KEY - 0x-prefixed Base wallet private key with USDC
11
+ * WEB_REFINERY_URL - API base URL (optional)
12
+ */
13
+
14
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
15
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
16
+ import axios from 'axios';
17
+ import { wrapAxiosWithPayment, x402Client } from '@x402/axios';
18
+ import { ExactEvmScheme } from '@x402/evm/exact/client';
19
+ import { privateKeyToAccount } from 'viem/accounts';
20
+ import { z } from 'zod';
21
+
22
+ const EVM_PRIVATE_KEY = process.env.EVM_PRIVATE_KEY;
23
+ const BASE_URL = process.env.WEB_REFINERY_URL || 'https://web-refinery-407929312862.asia-northeast3.run.app';
24
+
25
+ function createApi() {
26
+ const instance = axios.create({
27
+ baseURL: BASE_URL,
28
+ timeout: 15_000,
29
+ });
30
+
31
+ if (!EVM_PRIVATE_KEY) {
32
+ console.error('[web-refinery-mcp] Warning: EVM_PRIVATE_KEY not set — x402 payments will fail');
33
+ return instance;
34
+ }
35
+
36
+ const account = privateKeyToAccount(EVM_PRIVATE_KEY);
37
+ const client = new x402Client();
38
+ client.register('eip155:*', new ExactEvmScheme(account));
39
+ return wrapAxiosWithPayment(instance, client);
40
+ }
41
+
42
+ function formatResult(data) {
43
+ const d = data.data;
44
+ const meta = data.meta;
45
+ const lines = [];
46
+
47
+ lines.push(`# ${d.title}`);
48
+ lines.push('');
49
+
50
+ if (d.author) lines.push(`**Author:** ${d.author}`);
51
+ if (d.publishedAt) lines.push(`**Published:** ${d.publishedAt}`);
52
+ if (d.siteName) lines.push(`**Site:** ${d.siteName}`);
53
+ if (d.language) lines.push(`**Language:** ${d.language}`);
54
+ lines.push(`**Words:** ${d.wordCount} | **Reading time:** ${d.readingTime} min | **Type:** ${d.contentType}`);
55
+ if (d.warning) lines.push(`**Warning:** ${d.warning}`);
56
+ lines.push(`**Processed in:** ${meta.processingMs}ms`);
57
+ lines.push('');
58
+ lines.push('---');
59
+ lines.push('');
60
+ lines.push(d.content.markdown);
61
+
62
+ return lines.join('\n');
63
+ }
64
+
65
+ function formatError(err) {
66
+ if (err.response) {
67
+ const body = err.response.data;
68
+ const code = body?.error?.code || `HTTP_${err.response.status}`;
69
+ const msg = body?.error?.message || err.message;
70
+ return `Error [${code}]: ${msg}`;
71
+ }
72
+ if (err.code === 'ECONNABORTED') {
73
+ return 'Error [TIMEOUT]: Request timed out after 15s';
74
+ }
75
+ return `Error: ${err.message}`;
76
+ }
77
+
78
+ async function main() {
79
+ const api = createApi();
80
+ const server = new McpServer({
81
+ name: 'web-refinery',
82
+ version: '0.1.0',
83
+ });
84
+
85
+ server.tool(
86
+ 'web_refinery_basic',
87
+ 'Extract clean article content from any URL. Removes ads, navigation, and noise. Returns title, markdown content, word count, and reading time. Costs $0.003 USDC per request.',
88
+ { url: z.string().describe('The webpage URL to extract content from') },
89
+ async ({ url }) => {
90
+ try {
91
+ const resp = await api.get('/v1/refine', { params: { url, format: 'basic' } });
92
+ return { content: [{ type: 'text', text: formatResult(resp.data) }] };
93
+ } catch (err) {
94
+ return { content: [{ type: 'text', text: formatError(err) }], isError: true };
95
+ }
96
+ },
97
+ );
98
+
99
+ server.tool(
100
+ 'web_refinery_structured',
101
+ 'Extract detailed article content from any URL with full metadata. Returns title, author, publish date, markdown content, word count, language, and more. Costs $0.005 USDC per request.',
102
+ { url: z.string().describe('The webpage URL to extract content from') },
103
+ async ({ url }) => {
104
+ try {
105
+ const resp = await api.get('/v1/refine', { params: { url, format: 'structured' } });
106
+ return { content: [{ type: 'text', text: formatResult(resp.data) }] };
107
+ } catch (err) {
108
+ return { content: [{ type: 'text', text: formatError(err) }], isError: true };
109
+ }
110
+ },
111
+ );
112
+
113
+ const transport = new StdioServerTransport();
114
+ await server.connect(transport);
115
+ }
116
+
117
+ main().catch(err => {
118
+ console.error('[web-refinery-mcp] Fatal:', err.message);
119
+ process.exit(1);
120
+ });
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "web-refinery-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for Web Refinery API — clean web content extraction with x402 USDC payments",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "bin": {
8
+ "web-refinery-mcp": "./index.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node index.js"
12
+ },
13
+ "dependencies": {
14
+ "@modelcontextprotocol/sdk": "^1.9.0",
15
+ "@x402/axios": "^2.6.0",
16
+ "@x402/evm": "^2.6.0",
17
+ "axios": "^1.7.0",
18
+ "viem": "^2.0.0"
19
+ }
20
+ }