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.
- package/README.md +88 -0
- package/index.js +120 -0
- 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
|
+
}
|