nanoparse-mcp 1.0.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/LICENSE +21 -0
- package/README.md +102 -0
- package/dist/fetch.d.ts +34 -0
- package/dist/fetch.d.ts.map +1 -0
- package/dist/fetch.js +37 -0
- package/dist/fetch.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +79 -0
- package/dist/index.js.map +1 -0
- package/dist/payment.d.ts +30 -0
- package/dist/payment.d.ts.map +1 -0
- package/dist/payment.js +51 -0
- package/dist/payment.js.map +1 -0
- package/package.json +39 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 NanoParse
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# NanoParse MCP
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/nanoparse-mcp)
|
|
4
|
+
[](./LICENSE)
|
|
5
|
+
|
|
6
|
+
MCP client for [NanoParse](https://nanoparse.app) — pay-per-parse web content extraction for AI agents. No accounts, no API keys. Just paste a URL and get clean, structured Markdown back.
|
|
7
|
+
|
|
8
|
+
## What this is
|
|
9
|
+
|
|
10
|
+
A thin MCP (Model Context Protocol) client that lets AI agents call NanoParse's hosted API. The client handles:
|
|
11
|
+
|
|
12
|
+
- MCP tool registration (`nanoparse_fetch`)
|
|
13
|
+
- HTTP calls to `nanoparse.app/fetch`
|
|
14
|
+
- x402 micropayment signing (USDC on Base)
|
|
15
|
+
|
|
16
|
+
The browser rendering, content scoring, and Markdown conversion stay on NanoParse's infrastructure — this repo is the client layer only.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx nanoparse-mcp
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or install globally:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install -g nanoparse-mcp
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Prerequisites
|
|
31
|
+
|
|
32
|
+
- Node.js 18+
|
|
33
|
+
- A Base wallet with USDC (after your first 50 free parses)
|
|
34
|
+
- Set `NANOPARSE_WALLET_KEY` in your environment
|
|
35
|
+
|
|
36
|
+
## Getting a wallet
|
|
37
|
+
|
|
38
|
+
If you don't already have a Base-compatible wallet for your agent, don't generate one by hand — use [NanoParse/agent-wallet-prompt](https://github.com/NanoParse/agent-wallet-prompt), a copy-paste prompt that walks your coding assistant through generating one safely with an audited library. Come back here once it's funded.
|
|
39
|
+
|
|
40
|
+
## Setup
|
|
41
|
+
|
|
42
|
+
1. Copy the environment template:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
cp .env.example .env
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
2. Edit `.env` and add your Base wallet private key:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
NANOPARSE_WALLET_KEY=0x_your_private_key_here
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
3. Add to your MCP client config:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"mcpServers": {
|
|
59
|
+
"nanoparse": {
|
|
60
|
+
"command": "npx",
|
|
61
|
+
"args": ["nanoparse-mcp"]
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Your agent will auto-discover the `nanoparse_fetch` tool.
|
|
68
|
+
|
|
69
|
+
## Usage
|
|
70
|
+
|
|
71
|
+
Your agent calls `nanoparse_fetch` with a URL:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
nanoparse_fetch("https://example.com/article")
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Optional debug mode:
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
nanoparse_fetch("https://example.com/article", debug=true)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
See [`examples/basic-usage.md`](./examples/basic-usage.md) for a full copy-paste runnable example.
|
|
84
|
+
|
|
85
|
+
### Pricing
|
|
86
|
+
|
|
87
|
+
First 50 calls per IP are free. After that, **$0.0175 per parse** via x402 micropayment in USDC on Base. See [nanoparse.app](https://nanoparse.app) for current pricing and API docs.
|
|
88
|
+
|
|
89
|
+
## Security
|
|
90
|
+
|
|
91
|
+
- Your private key is read from `NANOPARSE_WALLET_KEY` at runtime and is **never logged, printed, or transmitted** except for signing x402 payment requests.
|
|
92
|
+
- The `.env` file is excluded from git via `.gitignore`.
|
|
93
|
+
- Payment signing uses [viem](https://viem.sh), an established, audited Ethereum library.
|
|
94
|
+
- This client never constructs raw blockchain transactions — payment settlement is handled by the [x402 facilitator](https://x402.org).
|
|
95
|
+
|
|
96
|
+
## Versioning
|
|
97
|
+
|
|
98
|
+
This project follows [Semantic Versioning](https://semver.org). See [`CHANGELOG.md`](./CHANGELOG.md) for release history.
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
MIT — see [LICENSE](./LICENSE).
|
package/dist/fetch.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
interface NanoParseResponse {
|
|
2
|
+
success: boolean;
|
|
3
|
+
url: string;
|
|
4
|
+
markdown: string;
|
|
5
|
+
metadata: {
|
|
6
|
+
title?: string;
|
|
7
|
+
author?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
published?: string;
|
|
10
|
+
image?: string;
|
|
11
|
+
favicon?: string;
|
|
12
|
+
site?: string;
|
|
13
|
+
domain?: string;
|
|
14
|
+
wordCount?: number;
|
|
15
|
+
language?: string;
|
|
16
|
+
};
|
|
17
|
+
schemaOrgData?: Record<string, unknown>;
|
|
18
|
+
freeCallsRemaining?: number;
|
|
19
|
+
debug?: {
|
|
20
|
+
contentSelector?: string;
|
|
21
|
+
metadataSource?: string;
|
|
22
|
+
cleaning?: {
|
|
23
|
+
blocksRemoved: number;
|
|
24
|
+
blocksKept: number;
|
|
25
|
+
};
|
|
26
|
+
timing?: {
|
|
27
|
+
metadataFetchMs: number;
|
|
28
|
+
browserRenderMs: number;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export declare function fetchPage(url: string, debug?: boolean): Promise<NanoParseResponse>;
|
|
33
|
+
export {};
|
|
34
|
+
//# sourceMappingURL=fetch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../src/fetch.ts"],"names":[],"mappings":"AAeA,UAAU,iBAAiB;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE;QACR,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,KAAK,CAAC,EAAE;QACN,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,EAAE;YAAE,aAAa,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,CAAA;SAAE,CAAC;QACzD,MAAM,CAAC,EAAE;YAAE,eAAe,EAAE,MAAM,CAAC;YAAC,eAAe,EAAE,MAAM,CAAA;SAAE,CAAC;KAC/D,CAAC;CACH;AAED,wBAAsB,SAAS,CAC7B,GAAG,EAAE,MAAM,EACX,KAAK,UAAQ,GACZ,OAAO,CAAC,iBAAiB,CAAC,CA2C5B"}
|
package/dist/fetch.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { signPayment } from "./payment.js";
|
|
2
|
+
const FETCH_ENDPOINT = "https://nanoparse.app/fetch";
|
|
3
|
+
export async function fetchPage(url, debug = false) {
|
|
4
|
+
const body = { url };
|
|
5
|
+
if (debug)
|
|
6
|
+
body.debug = true;
|
|
7
|
+
// First attempt — may return 402 if payment is required
|
|
8
|
+
let res = await fetch(FETCH_ENDPOINT, {
|
|
9
|
+
method: "POST",
|
|
10
|
+
headers: { "Content-Type": "application/json" },
|
|
11
|
+
body: JSON.stringify(body),
|
|
12
|
+
});
|
|
13
|
+
// Handle x402 payment flow
|
|
14
|
+
if (res.status === 402) {
|
|
15
|
+
const paymentTerms = await res.json();
|
|
16
|
+
if (paymentTerms.error !== "payment_required") {
|
|
17
|
+
throw new Error(`Unexpected 402 response: ${JSON.stringify(paymentTerms)}`);
|
|
18
|
+
}
|
|
19
|
+
const signature = await signPayment(paymentTerms);
|
|
20
|
+
// Retry with payment signature
|
|
21
|
+
res = await fetch(FETCH_ENDPOINT, {
|
|
22
|
+
method: "POST",
|
|
23
|
+
headers: {
|
|
24
|
+
"Content-Type": "application/json",
|
|
25
|
+
"Payment-Signature": signature,
|
|
26
|
+
},
|
|
27
|
+
body: JSON.stringify(body),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (!res.ok) {
|
|
31
|
+
const errorText = await res.text().catch(() => "Unknown error");
|
|
32
|
+
throw new Error(`NanoParse API returned ${res.status}: ${errorText}`);
|
|
33
|
+
}
|
|
34
|
+
const data = await res.json();
|
|
35
|
+
return data;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch.js","sourceRoot":"","sources":["../src/fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,MAAM,cAAc,GAAG,6BAA6B,CAAC;AAuCrD,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,GAAW,EACX,KAAK,GAAG,KAAK;IAEb,MAAM,IAAI,GAA4B,EAAE,GAAG,EAAE,CAAC;IAC9C,IAAI,KAAK;QAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IAE7B,wDAAwD;IACxD,IAAI,GAAG,GAAG,MAAM,KAAK,CAAC,cAAc,EAAE;QACpC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;KAC3B,CAAC,CAAC;IAEH,2BAA2B;IAC3B,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QACvB,MAAM,YAAY,GAAqB,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAExD,IAAI,YAAY,CAAC,KAAK,KAAK,kBAAkB,EAAE,CAAC;YAC9C,MAAM,IAAI,KAAK,CACb,4BAA4B,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,CAC3D,CAAC;QACJ,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC,CAAC;QAElD,+BAA+B;QAC/B,GAAG,GAAG,MAAM,KAAK,CAAC,cAAc,EAAE;YAChC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,mBAAmB,EAAE,SAAS;aAC/B;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;SAC3B,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,CAAC;QAChE,MAAM,IAAI,KAAK,CACb,0BAA0B,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CACrD,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAsB,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IACjD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import { fetchPage } from "./fetch.js";
|
|
5
|
+
const server = new Server({
|
|
6
|
+
name: "nanoparse-mcp",
|
|
7
|
+
version: "1.0.0",
|
|
8
|
+
}, {
|
|
9
|
+
capabilities: {
|
|
10
|
+
tools: {},
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
14
|
+
tools: [
|
|
15
|
+
{
|
|
16
|
+
name: "nanoparse_fetch",
|
|
17
|
+
description: "Fetch a web page and return clean, structured Markdown with metadata. " +
|
|
18
|
+
"First 50 parses free per IP. After that, pay $0.0175 USDC on Base via x402. " +
|
|
19
|
+
"No account or API key required.",
|
|
20
|
+
inputSchema: {
|
|
21
|
+
type: "object",
|
|
22
|
+
properties: {
|
|
23
|
+
url: {
|
|
24
|
+
type: "string",
|
|
25
|
+
description: "The URL to fetch and convert to Markdown",
|
|
26
|
+
},
|
|
27
|
+
debug: {
|
|
28
|
+
type: "boolean",
|
|
29
|
+
description: "If true, include extraction diagnostics in the response (contentSelector, metadataSource, cleaning stats, timing)",
|
|
30
|
+
default: false,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
required: ["url"],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
}));
|
|
38
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
39
|
+
if (request.params.name !== "nanoparse_fetch") {
|
|
40
|
+
throw new Error(`Unknown tool: ${request.params.name}`);
|
|
41
|
+
}
|
|
42
|
+
const args = request.params.arguments;
|
|
43
|
+
if (!args.url || typeof args.url !== "string") {
|
|
44
|
+
throw new Error('Missing required parameter: "url"');
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const result = await fetchPage(args.url, args.debug ?? false);
|
|
48
|
+
return {
|
|
49
|
+
content: [
|
|
50
|
+
{
|
|
51
|
+
type: "text",
|
|
52
|
+
text: result.markdown,
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
structuredContent: result,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
60
|
+
return {
|
|
61
|
+
content: [
|
|
62
|
+
{
|
|
63
|
+
type: "text",
|
|
64
|
+
text: `NanoParse error: ${message}`,
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
isError: true,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
async function main() {
|
|
72
|
+
const transport = new StdioServerTransport();
|
|
73
|
+
await server.connect(transport);
|
|
74
|
+
}
|
|
75
|
+
main().catch((err) => {
|
|
76
|
+
console.error("nanoparse-mcp server failed to start:", String(err));
|
|
77
|
+
process.exit(1);
|
|
78
|
+
});
|
|
79
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB;IACE,IAAI,EAAE,eAAe;IACrB,OAAO,EAAE,OAAO;CACjB,EACD;IACE,YAAY,EAAE;QACZ,KAAK,EAAE,EAAE;KACV;CACF,CACF,CAAC;AAEF,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;IAC5D,KAAK,EAAE;QACL;YACE,IAAI,EAAE,iBAAiB;YACvB,WAAW,EACT,wEAAwE;gBACxE,8EAA8E;gBAC9E,iCAAiC;YACnC,WAAW,EAAE;gBACX,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,GAAG,EAAE;wBACH,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,0CAA0C;qBACxD;oBACD,KAAK,EAAE;wBACL,IAAI,EAAE,SAAS;wBACf,WAAW,EACT,mHAAmH;wBACrH,OAAO,EAAE,KAAK;qBACf;iBACF;gBACD,QAAQ,EAAE,CAAC,KAAK,CAAC;aAClB;SACF;KACF;CACF,CAAC,CAAC,CAAC;AAEJ,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;IAChE,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,iBAAiB,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,SAG3B,CAAC;IAEF,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,OAAO,IAAI,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,CAAC;QAE9D,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,MAAM,CAAC,QAAQ;iBACtB;aACF;YACD,iBAAiB,EAAE,MAAM;SAC1B,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,oBAAoB,OAAO,EAAE;iBACpC;aACF;YACD,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACpE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Payment terms returned by NanoParse in a 402 response body.
|
|
3
|
+
* These are the fields the client must sign before retrying.
|
|
4
|
+
*/
|
|
5
|
+
interface X402PaymentTerms {
|
|
6
|
+
error: "payment_required";
|
|
7
|
+
scheme: "exact";
|
|
8
|
+
network: string;
|
|
9
|
+
asset: string;
|
|
10
|
+
amount: string;
|
|
11
|
+
payTo: string;
|
|
12
|
+
free_calls_used: number;
|
|
13
|
+
free_calls_limit: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Sign x402 payment terms using the wallet key and return the
|
|
17
|
+
* signature for the Payment-Signature header.
|
|
18
|
+
*
|
|
19
|
+
* The signature is created by signing a JSON-serialized
|
|
20
|
+
* representation of the payment terms with the wallet's private key.
|
|
21
|
+
* The x402 facilitator at x402.org verifies and settles the
|
|
22
|
+
* transaction — the client never constructs raw blockchain
|
|
23
|
+
* transactions directly.
|
|
24
|
+
*
|
|
25
|
+
* The wallet key is read from NANOPARSE_WALLET_KEY and is never
|
|
26
|
+
* logged, printed, or included in error output.
|
|
27
|
+
*/
|
|
28
|
+
export declare function signPayment(paymentTerms: X402PaymentTerms): Promise<string>;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=payment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payment.d.ts","sourceRoot":"","sources":["../src/payment.ts"],"names":[],"mappings":"AAkBA;;;GAGG;AACH,UAAU,gBAAgB;IACxB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,WAAW,CAC/B,YAAY,EAAE,gBAAgB,GAC7B,OAAO,CAAC,MAAM,CAAC,CA6BjB"}
|
package/dist/payment.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
2
|
+
import { createWalletClient, http } from "viem";
|
|
3
|
+
import { base } from "viem/chains";
|
|
4
|
+
function getWalletKey() {
|
|
5
|
+
const key = process.env.NANOPARSE_WALLET_KEY;
|
|
6
|
+
if (!key) {
|
|
7
|
+
throw new Error("NANOPARSE_WALLET_KEY environment variable is not set. " +
|
|
8
|
+
"Copy .env.example to .env and add your Base wallet private key.");
|
|
9
|
+
}
|
|
10
|
+
if (!key.startsWith("0x")) {
|
|
11
|
+
return `0x${key}`;
|
|
12
|
+
}
|
|
13
|
+
return key;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Sign x402 payment terms using the wallet key and return the
|
|
17
|
+
* signature for the Payment-Signature header.
|
|
18
|
+
*
|
|
19
|
+
* The signature is created by signing a JSON-serialized
|
|
20
|
+
* representation of the payment terms with the wallet's private key.
|
|
21
|
+
* The x402 facilitator at x402.org verifies and settles the
|
|
22
|
+
* transaction — the client never constructs raw blockchain
|
|
23
|
+
* transactions directly.
|
|
24
|
+
*
|
|
25
|
+
* The wallet key is read from NANOPARSE_WALLET_KEY and is never
|
|
26
|
+
* logged, printed, or included in error output.
|
|
27
|
+
*/
|
|
28
|
+
export async function signPayment(paymentTerms) {
|
|
29
|
+
const key = getWalletKey();
|
|
30
|
+
const account = privateKeyToAccount(key);
|
|
31
|
+
const client = createWalletClient({
|
|
32
|
+
account,
|
|
33
|
+
chain: base,
|
|
34
|
+
transport: http(),
|
|
35
|
+
});
|
|
36
|
+
// Serialize the payment terms deterministically for signing.
|
|
37
|
+
// The x402 facilitator expects a JSON payload with sorted keys.
|
|
38
|
+
const payload = JSON.stringify({
|
|
39
|
+
scheme: paymentTerms.scheme,
|
|
40
|
+
network: paymentTerms.network,
|
|
41
|
+
asset: paymentTerms.asset,
|
|
42
|
+
amount: paymentTerms.amount,
|
|
43
|
+
payTo: paymentTerms.payTo,
|
|
44
|
+
}, Object.keys(paymentTerms).sort());
|
|
45
|
+
const signature = await client.signMessage({
|
|
46
|
+
account,
|
|
47
|
+
message: payload,
|
|
48
|
+
});
|
|
49
|
+
return signature;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=payment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payment.js","sourceRoot":"","sources":["../src/payment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,IAAI,EAAY,MAAM,MAAM,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAEnC,SAAS,YAAY;IACnB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;IAC7C,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CACb,wDAAwD;YACtD,iEAAiE,CACpE,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1B,OAAO,KAAK,GAAG,EAAS,CAAC;IAC3B,CAAC;IACD,OAAO,GAAU,CAAC;AACpB,CAAC;AAiBD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,YAA8B;IAE9B,MAAM,GAAG,GAAG,YAAY,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;IAEzC,MAAM,MAAM,GAAG,kBAAkB,CAAC;QAChC,OAAO;QACP,KAAK,EAAE,IAAI;QACX,SAAS,EAAE,IAAI,EAAE;KAClB,CAAC,CAAC;IAEH,6DAA6D;IAC7D,gEAAgE;IAChE,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAC5B;QACE,MAAM,EAAE,YAAY,CAAC,MAAM;QAC3B,OAAO,EAAE,YAAY,CAAC,OAAO;QAC7B,KAAK,EAAE,YAAY,CAAC,KAAK;QACzB,MAAM,EAAE,YAAY,CAAC,MAAM;QAC3B,KAAK,EAAE,YAAY,CAAC,KAAK;KAC1B,EACD,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,IAAI,EAAE,CACjC,CAAC;IAEF,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC;QACzC,OAAO;QACP,OAAO,EAAE,OAAO;KACjB,CAAC,CAAC;IAEH,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nanoparse-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP client for NanoParse — pay-per-parse web content extraction for AI agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"nanoparse-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"mcp",
|
|
19
|
+
"model-context-protocol",
|
|
20
|
+
"nanoparse",
|
|
21
|
+
"web-scraping",
|
|
22
|
+
"markdown",
|
|
23
|
+
"x402",
|
|
24
|
+
"ai-agent"
|
|
25
|
+
],
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/nanoparse/nanoparse-mcp"
|
|
29
|
+
},
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
33
|
+
"viem": "^2.55.8"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^22.0.0",
|
|
37
|
+
"typescript": "^5.5.0"
|
|
38
|
+
}
|
|
39
|
+
}
|