nanoparse-mcp 1.0.9 → 2.0.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,78 @@
1
+ ## 2.0.1 — 2026-11-14
2
+
3
+ - Price: $0.005 → **$0.002** per parse. Copy only: the hosted endpoint takes the
4
+ amount from the live 402 challenge, so no client change is needed.
5
+
6
+ # Changelog
7
+
8
+ All notable changes to nanoparse-mcp will be documented in this file.
9
+
10
+ ---
11
+
12
+ ## [2.0.0] - 2026-08-28
13
+
14
+ ### Changed
15
+
16
+ - Repo restructured as the public home for the **hosted** MCP endpoint
17
+ (`https://nanoparse.app/mcp`): docs + real examples, no client code.
18
+ - Retired v1 local stdio client moved to `archive/v1-local-client/`
19
+ (was `src/`), with a DEPRECATED banner. The client held a wallet private
20
+ key locally (`NANOPARSE_WALLET_KEY`) and is replaced by the hosted
21
+ endpoint's own payment flow.
22
+ - npm package `nanoparse-mcp` deprecated — use the hosted endpoint.
23
+ - README: real Litmus output example (arXiv parse), worked agent
24
+ conversation, cost comparison vs Firecrawl (verified Aug 2026), new
25
+ `examples/` directory (`example-output.json`, `agent-conversation.md`,
26
+ `costs.md`).
27
+ - Price: $0.01 → **$0.005** per parse (2026-10-06), with the README and
28
+ examples realigned.
29
+ - `package.json`: **removed `main`, `bin`, `files: ["dist/"]` and the build
30
+ scripts.** After the client source moved to `archive/v1-local-client/` they
31
+ pointed at `dist/index.js`, which no longer exists — so `npm run build` failed
32
+ and a publish would have shipped a package containing only LICENSE, README and
33
+ package.json. Runtime dependencies (`@modelcontextprotocol/sdk`, `viem`) went
34
+ with the client.
35
+
36
+ ### Published 2026-10-06
37
+
38
+ First publish of the 2.0.0 line. Versions ≤ 1.0.9 ship the retired local stdio
39
+ client and are deprecated on the registry.
40
+
41
+ ## [1.0.9] - 2026-08-23
42
+
43
+ ### Changed
44
+
45
+ - Price: $0.0175 → **$0.01** per parse (tool descriptions + README)
46
+ - README: removed dead card-pack section (`/payments` not live); docs links now point to live pages (`/quickstart`, `/compare`)
47
+ - Server version string synced with package version
48
+
49
+ ## [1.0.8] - 2026-08-23
50
+
51
+ ### Changed
52
+
53
+ - Price: $0.0175 → **$0.01** per parse in `nanoparse_fetch` tool description
54
+
55
+ ## [1.0.7] - 2026-08-17
56
+
57
+ ### Changed
58
+
59
+ - Local stdio client retired — hosted MCP endpoint (`https://nanoparse.app/mcp`) is the only supported integration
60
+ - README rewritten as customer guide to the hosted MCP
61
+
62
+ ## [1.0.2] - 2026-08-02
63
+
64
+ ### Fixed
65
+
66
+ - Added missing `#!/usr/bin/env node` shebang to `dist/index.js`. Plain `npx nanoparse-mcp` now works without requiring `node dist/index.js`. Added `postbuild` script to prepend shebang after `tsc` compilation and set execute permissions.
67
+
68
+ ## [1.0.1] - 2026-08-01
69
+
70
+ ### Changed
71
+
72
+ - Updated `nanoparse_fetch` tool description
73
+
74
+ ## [1.0.0] - 2026-07-26 — Initial release
75
+
76
+ - MCP server with stdio transport, single tool: `nanoparse_fetch(url, debug?)`
77
+ - x402 payment signing via viem (wallet key from `NANOPARSE_WALLET_KEY` env var)
78
+ - Thin client only — browser rendering and content scoring run on NanoParse's infrastructure
package/README.md CHANGED
@@ -1,9 +1,18 @@
1
1
  # NanoParse MCP
2
2
 
3
- **Give your AI agent clean, structured web content.** NanoParse MCP is a hosted Model Context Protocol server at `https://nanoparse.app/mcp` that turns any URL into clean Markdown your agent can actually use — with machine-readable trust signals attached.
3
+ [![npm version](https://img.shields.io/npm/v/nanoparse-mcp)](https://www.npmjs.com/package/nanoparse-mcp)
4
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
5
+ [![Website](https://img.shields.io/badge/web-nanoparse.app-22D3EE)](https://nanoparse.app)
6
+ [![MCP Score](https://checkmcp.dev/badge/nanoparse-app.svg?url=https%3A%2F%2Fnanoparse.app%2Fmcp)](https://checkmcp.dev/mcp/nanoparse-app)
7
+ [![MCPLookup Trust Index](https://mcplookup.com/badge/io.github.nanoparse-dev/nanoparse-mcp)](https://mcplookup.com/server/io.github.nanoparse-dev/nanoparse-mcp)
8
+ [![Listed on HookMCP](https://hookmcp.com/badge.svg)](https://hookmcp.com)
9
+
10
+ **The easiest way for AI agents to read the web.** NanoParse MCP is a hosted Model Context Protocol server at `https://nanoparse.app/mcp` that turns any URL into clean Markdown your agent can actually use — with machine-readable trust signals attached.
4
11
 
5
12
  No install. No wallet key. No `.env`. Connect once and your agent can read the web.
6
13
 
14
+ **Why agents choose NanoParse over Firecrawl or Jina Reader:** every page comes back with **Litmus** — 20 machine-readable signals that tell your agent *whether to trust and cite the page* before it reads a word. Pay $0.002 per parse via x402 once the free tier runs out. Full JavaScript/SPA rendering, native GFM tables. No account, no subscription, no API key.
15
+
7
16
  ## Quick start
8
17
 
9
18
  Add this to your MCP client config:
@@ -12,6 +21,7 @@ Add this to your MCP client config:
12
21
  {
13
22
  "mcpServers": {
14
23
  "nanoparse": {
24
+ "type": "http",
15
25
  "url": "https://nanoparse.app/mcp"
16
26
  }
17
27
  }
@@ -35,33 +45,83 @@ That's it. Your agent can now fetch pages from inside any MCP-compatible client
35
45
 
36
46
  `nanoparse_status` tells your agent exactly where it stands: free parses remaining, wallet and USDC balance, and whether the next call will succeed without payment. Agents call it before spending.
37
47
 
48
+ ## Real output
49
+
50
+ Every parse returns `markdown` + `metadata` + `litmus`. Here is the full `litmus` object from a real parse of an arXiv paper (`examples/example-output.json` — markdown truncated there, litmus complete and unmodified):
51
+
52
+ ```json
53
+ {
54
+ "litmus": {
55
+ "source": { "type": "academic", "authority": 0.85, "rationale": "academic source" },
56
+ "freshness": { "published": null, "age": "unknown", "stale_warning": "No publish date found — verify independently", "modified": null, "has_correction": false, "correction_note": null },
57
+ "tldr": "Abstract page for arXiv paper 2401.14295: Demystifying Chains, Trees, and Graphs of Thoughts",
58
+ "key_figures": [ { "value": "2,247", "context": "Thu, 25 Jan 2024 16:34:00 UTC (2,247 KB)" } ],
59
+ "structural_trust_score": { "level": "medium", "trust_factors": ["High-authority source", "No publish date found — verify independently"] },
60
+ "hedge_language": { "density": "low", "markers_found": 0, "per_500_words": 0 },
61
+ "content_density": { "thin_content": false, "substantive_sentence_ratio": 0.76 },
62
+ "syndication": { "detected": false, "note": null },
63
+ "paywall": { "detected": false, "note": null },
64
+ "content_type": "academic_paper",
65
+ "reading_time": { "minutes": 4 },
66
+ "is_archived": { "archive_org": true, "archive_is": false },
67
+ "outbound_authority": { "score": 0.54, "top_domains": ["ui.adsabs.harvard.edu", "scholar.google.com"] },
68
+ "boilerplate_ratio": { "ratio": 0.08, "confidence": "high" }
69
+ }
70
+ }
71
+ ```
72
+
73
+ The agent learns *before reading a word*: this is an academic source (0.85 authority), structural trust is **medium** — high authority, but no publish date found, so verify independently. It also gets the one-sentence summary, the key numbers, and confirmation there's no paywall or syndication flag. See [`examples/agent-conversation.md`](examples/agent-conversation.md) for a full worked round-trip.
74
+
38
75
  ## Payments
39
76
 
40
- - **First 10 parses per device are free.** No account, no API key, no human required.
41
- - **After that: $0.01 per parse** (flat, no subscriptions) via **x402** — an open micropayment protocol. Your agent's wallet signs the payment automatically. To give your agent a Coinbase wallet, run `npx @coinbase/payments-mcp` and connect it alongside NanoParse.
77
+ - **10 free parses per month with a signed wallet** (sign the claim message — no payment), or 3 anonymous free tries per network. No account, no API key, no human required.
78
+ - **After that: $0.002 per parse** (flat, no subscriptions) via **x402** — an open micropayment protocol. Your agent's wallet signs the payment automatically. To give your agent a Coinbase wallet, run `npx @coinbase/payments-mcp` and connect it alongside NanoParse.
79
+
80
+ Copy-paste recipe for both paths — the wallet-MCP one with no code, and a ~40-line REST client — including the exact `402 → sign → retry` loop and the error codes worth branching on: [`examples/pay-with-x402.md`](examples/pay-with-x402.md).
42
81
 
43
82
  ## How payments work under the hood
44
83
 
45
- When an agent without free quota calls `nanoparse_fetch`, the endpoint responds with an **HTTP 402 — Payment Required** carrying x402 payment instructions. The agent's wallet signs a $0.01 USDC (Base) transfer, the facilitator settles it, and the parse proceeds. The hosted endpoint handles the entire challenge → settlement flow — the agent never needs a private key on your machine.
84
+ When an agent without free quota calls `nanoparse_fetch`, the endpoint responds with an **HTTP 402 — Payment Required** carrying x402 payment instructions. The agent's wallet signs a $0.002 USDC (Base) transfer, the facilitator settles it, and the parse proceeds. The hosted endpoint handles the entire challenge → settlement flow — the agent never needs a private key on your machine.
85
+
86
+ ## Pricing vs Firecrawl
87
+
88
+ | | NanoParse | Firecrawl Hobby |
89
+ |---|---|---|
90
+ | Billing | **$0.002 per parse, flat** | $16/mo subscription, credits expire monthly |
91
+ | 5,000 parses | **$25.00** | $16 base — but ~$145 with JSON/extraction modes (9× credit burn) |
92
+ | Account / API key | **None** | Required |
93
+ | Agent can pay directly (x402) | **Yes** | No |
94
+
95
+ Full math, including when Firecrawl's base rate wins at scale: [`examples/costs.md`](examples/costs.md).
46
96
 
47
97
  ## Example
48
98
 
49
99
  ```
50
- You: Fetch https://www.ft.com/content/... and summarize the argument.
100
+ You: Fetch https://arxiv.org/abs/2401.14295 and summarize the argument.
51
101
 
52
102
  Agent: [calls nanoparse_fetch(url)]
53
- → clean Markdown + Litmus signals
54
- → summarizes from primary content, cites freshness and authority
103
+ → clean Markdown + Litmus (academic, authority 0.85, 4-min read,
104
+ no date found — verify, no paywall)
105
+ → summarizes from primary content, cites authority and freshness
55
106
  ```
56
107
 
57
108
  ## Repository note
58
109
 
59
- The `nanoparse-mcp` npm package previously published here was the first-generation local client, which held a wallet private key and signed x402 payments on your machine. It is retired: the hosted MCP endpoint now handles the full payment flow itself, so the local client is no longer needed. **Use the hosted endpoint above** — it is the current, supported integration.
110
+ The `nanoparse-mcp` npm package and the local stdio client were the
111
+ first-generation integration, which held a wallet private key
112
+ (`NANOPARSE_WALLET_KEY`) and signed x402 payments on your machine. Both are
113
+ **retired**: the hosted endpoint above now handles the full payment flow
114
+ itself, so no local client or wallet key is needed. The retired client code
115
+ is preserved in [`archive/v1-local-client/`](archive/v1-local-client/) for
116
+ history, and the npm package is deprecated. **Use the hosted endpoint** — it
117
+ is the current, supported integration.
60
118
 
61
119
  ## Docs
62
120
 
63
121
  - Quick start & integration: [nanoparse.app/quickstart](https://nanoparse.app/quickstart)
64
122
  - Compare & pricing: [nanoparse.app/compare](https://nanoparse.app/compare)
65
123
  - API + MCP endpoint: [nanoparse.app](https://nanoparse.app)
124
+ - Security policy: [SECURITY.md](SECURITY.md) — report a vulnerability to hello@nanoparse.app
125
+ (also published at `https://nanoparse.app/.well-known/security.txt`)
66
126
 
67
127
  MIT licensed.
@@ -0,0 +1,88 @@
1
+ # Worked example: an agent reading an academic paper
2
+
3
+ This is a real `nanoparse_fetch` round-trip against the hosted endpoint
4
+ (`https://nanoparse.app/mcp`), showing what the agent sees and how it reasons
5
+ over the Litmus signals. The full response JSON is in
6
+ [`example-output.json`](./example-output.json).
7
+
8
+ ## The request
9
+
10
+ ```
11
+ You: Fetch https://arxiv.org/abs/2401.14295 and tell me what this paper
12
+ claims, and whether I can trust it as a source.
13
+ ```
14
+
15
+ The agent calls `nanoparse_fetch(url: "https://arxiv.org/abs/2401.14295")`
16
+ and receives clean Markdown plus the Litmus object — before reading a single
17
+ word of the paper.
18
+
19
+ ## What Litmus told the agent, up front
20
+
21
+ ```json
22
+ {
23
+ "litmus": {
24
+ "source": {
25
+ "type": "academic",
26
+ "authority": 0.85,
27
+ "rationale": "academic source"
28
+ },
29
+ "freshness": {
30
+ "published": null,
31
+ "age": "unknown",
32
+ "stale_warning": "No publish date found — verify independently"
33
+ },
34
+ "tldr": "Abstract page for arXiv paper 2401.14295: Demystifying Chains, Trees, and Graphs of Thoughts",
35
+ "key_figures": [
36
+ { "value": "2,247", "context": "Thu, 25 Jan 2024 16:34:00 UTC (2,247 KB)" },
37
+ { "value": "2,290", "context": "Sat, 30 Mar 2024 16:36:33 UTC (2,290 KB)" }
38
+ ],
39
+ "structural_trust_score": {
40
+ "level": "medium",
41
+ "trust_factors": [
42
+ "High-authority source",
43
+ "No publish date found — verify independently"
44
+ ]
45
+ },
46
+ "content_type": "academic_paper",
47
+ "reading_time": { "minutes": 4 },
48
+ "hedge_language": { "density": "low", "markers_found": 0, "per_500_words": 0 },
49
+ "syndication": { "detected": false },
50
+ "paywall": { "detected": false }
51
+ }
52
+ }
53
+ ```
54
+
55
+ ## How the agent reasons about it
56
+
57
+ - **Structural trust: medium** — Litmus flags this as high-authority
58
+ (academic, 0.85) but with no publish date found, so the verdict is "verify
59
+ independently," not "trust blindly." The agent treats it as a solid source
60
+ it still needs to date-check.
61
+ - **Authority 0.85, academic** — high-trust source class. Claims from this
62
+ paper can be weighed accordingly. (Litmus scores the *source class*, not
63
+ the truth of the content — the agent still reads and thinks.)
64
+ - **Freshness warning** — no publish date on the abstract page. The agent
65
+ checks the arXiv listing in the markdown and finds version dates
66
+ (Jan 2024 → Feb 2025), so it cites the paper as "2024, updated 2025"
67
+ instead of guessing.
68
+ - **Key figures** — file sizes are noise, so the agent ignores them; but it
69
+ now knows the paper exists in multiple revisions.
70
+ - **No syndication, no paywall, low hedge density, 4-minute read** — a
71
+ clean, direct academic source. No red flags.
72
+
73
+ ## The agent's answer
74
+
75
+ > The paper **"Demystifying Chains, Trees, and Graphs of Thoughts"**
76
+ > (arXiv 2401.14295) compares chain-of-thought, tree-of-thought, and
77
+ > graph-of-thought prompting across 12 datasets. It claims tree/graph
78
+ > reasoning does not consistently beat plain chain-of-thought, and that
79
+ > success depends on problem type and search strategy.
80
+ >
81
+ > Source note: academic (authority 0.85), first posted Jan 2024 with
82
+ > revisions through Feb 2025. No paywall or syndication flags. Solid
83
+ > source for a research summary; treat its negative claims about
84
+ > tree-of-thought as one study, not settled consensus.
85
+
86
+ That reasoning — *"can I trust this, how fresh is it, what kind of source
87
+ is it, is there anything to verify"* — is exactly what Litmus pre-computes
88
+ so the agent doesn't have to guess from raw text.
@@ -0,0 +1,46 @@
1
+ # Cost comparison: NanoParse vs Firecrawl
2
+
3
+ Pricing verified 2026-08-28 (Firecrawl); NanoParse updated to the current flat rate. NanoParse: **$0.002 per parse, flat** — no
4
+ subscription, no account, no API key. Litmus signals, full JS/SPA rendering,
5
+ and GFM tables are included in every parse — there are no feature
6
+ multipliers.
7
+
8
+ Firecrawl (published plans, Aug 2026):
9
+
10
+ | Plan | Price | Credits/mo | Base cost/page |
11
+ |---|---|---|---|
12
+ | Free | $0 | 1,000 | — |
13
+ | Hobby | $16/mo | 5,000 | $0.0032 |
14
+ | Standard | $83/mo | 100,000 | $0.00083 |
15
+ | Growth | $333/mo | 500,000 | $0.00067 |
16
+ | Scale | $599/mo | 1,000,000 | $0.0006 |
17
+
18
+ Two things the base rate hides:
19
+
20
+ 1. **Credits do not roll over.** A Hobby subscription is $16 every month —
21
+ use 1,000 of 5,000 credits and you still paid $16. NanoParse bills only
22
+ what you use: $5 for exactly 1,000 parses.
23
+ 2. **Feature multipliers.** JSON extraction and enhanced mode each add
24
+ credits per page (up to 9 credits/page combined). At Hobby that makes a
25
+ fully-featured parse ~$0.029/page — ~6× NanoParse's flat rate. Litmus,
26
+ structured output, and rendering are included in NanoParse's $0.002 with
27
+ no multiplier.
28
+
29
+ ## What a month of 5,000 parses costs
30
+
31
+ | | NanoParse | Firecrawl Hobby |
32
+ |---|---|---|
33
+ | Subscription | None — pay per parse | $16/mo, billed every month |
34
+ | 5,000 plain scrapes | **$25.00** | $16 (base rate) |
35
+ | 5,000 with extraction/features | **$25.00** | ~$145 (9× credit burn) |
36
+ | Use only 1,000 of your quota | **$5.00** | $16 (credits expire) |
37
+ | Account / API key needed | No | Yes |
38
+ | Agent can pay directly (x402) | Yes | No |
39
+
40
+ ## When Firecrawl is genuinely cheaper
41
+
42
+ At scale with plain, credit-cheap scraping, Firecrawl's base rate is lower
43
+ per page — that's their volume bet. NanoParse's bet is different: no
44
+ subscription, no rollover loss, no multiplier, no account, and an agent can
45
+ pay for itself with x402. If you parse occasionally, or in bursts, or via
46
+ agents, NanoParse charges exactly for what you use.
@@ -0,0 +1,101 @@
1
+ {
2
+ "url": "https://arxiv.org/abs/2401.14295",
3
+ "parsed_at": "2026-08-28T00:00:00Z",
4
+ "note": "Real response from POST https://nanoparse.app/fetch (admin round, same public response shape). Markdown truncated to the first 3,000 chars for readability; metadata and litmus are complete and unmodified.",
5
+ "markdown_truncated": true,
6
+ "markdown": "# [2401.14295] Demystifying Chains, Trees, and Graphs of Thoughts\n\n--\n\n \n \n # Computer Science > Computation and Language\n \n\n \n **arXiv:2401.14295 (cs)\n \n\n- \n\n \n \n [Submitted on 25 Jan 2024 ([v1](https://arxiv.org/abs/2401.14295v1)), last revised 1 Apr 2026 (this version, v6)]\n # Title:Demystifying Chains, Trees, and Graphs of Thoughts\n Authors:[Maciej Besta](https://arxiv.org/search/cs?searchtype=author&query=Besta,+M), [Florim Memedi](https://arxiv.org/search/cs?searchtype=author&query=Memedi,+F), [Zhenyu Zhang](https://arxiv.org/search/cs?searchtype=author&query=Zhang,+Z), [Robert Gerstenberger](https://arxiv.org/search/cs?searchtype=author&query=Gerstenberger,+R), [Guangyuan Piao](https://arxiv.org/search/cs?searchtype=author&query=Piao,+G), [Nils Blach](https://arxiv.org/search/cs?searchtype=author&query=Blach,+N), [Piotr Nyczyk](https://arxiv.org/search/cs?searchtype=author&query=Nyczyk,+P), [Marcin Copik](https://arxiv.org/search/cs?searchtype=author&query=Copik,+M), [Grzegorz Kwa\u015bniewski](https://arxiv.org/search/cs?searchtype=author&query=Kwa%C5%9Bniewski,+G), [J\u00fcrgen M\u00fcller](https://arxiv.org/search/cs?searchtype=author&query=M%C3%BCller,+J), [Lukas Gianinazzi](https://arxiv.org/search/cs?searchtype=author&query=Gianinazzi,+L), [Ales Kubicek](https://arxiv.org/search/cs?searchtype=author&query=Kubicek,+A), [Hubert Niewiadomski](https://arxiv.org/search/cs?searchtype=author&query=Niewiadomski,+H), [Aidan O'Mahony](https://arxiv.org/search/cs?searchtype=author&query=O'Mahony,+A), [Onur Mutlu](https://arxiv.org/search/cs?searchtype=author&query=Mutlu,+O), [Torsten Hoefler](https://arxiv.org/search/cs?searchtype=author&query=Hoefler,+T) \n [View PDF](/pdf/2401.14295)\n [HTML (experimental)](https://arxiv.org/html/2401.14295v6)\n\n **\n Abstract:The field of natural language processing (NLP) has witnessed significant progress in recent years, with a notable focus on improving large language models' (LLM) performance through innovative prompting techniques. Among these, prompt engineering coupled with structures has emerged as a promising paradigm, with designs such as Chain-of-Thought, Tree of Thoughts, or Graph of Thoughts, in which the overall LLM reasoning is guided by a structure such as a graph. As illustrated with numerous examples, this paradigm significantly enhances the LLM's capability to solve numerous tasks, ranging from logical or mathematical reasoning to planning or creative writing. To facilitate the understanding of this growing field and pave the way for future developments, we devise a general blueprint for effective and efficient LLM reasoning schemes. For this, we conduct an in-depth analysis of the prompt execution pipeline, clarifying and clearly defining different concepts. We then build the first taxonomy of structure-enhanced LLM reasoning schemes. We focus on identifying fundamental classes of harnessed structures, and we analyze the representations of the",
7
+ "metadata": {
8
+ "title": "[2401.14295] Demystifying Chains, Trees, and Graphs of Thoughts",
9
+ "author": null,
10
+ "description": "Abstract page for arXiv paper 2401.14295: Demystifying Chains, Trees, and Graphs of Thoughts",
11
+ "published": null,
12
+ "image": "/static/browse/0.3.4/images/arxiv-logo-fb.png",
13
+ "site": "arXiv.org",
14
+ "domain": "arxiv.org",
15
+ "wordCount": 806
16
+ },
17
+ "litmus": {
18
+ "source": {
19
+ "type": "academic",
20
+ "authority": 0.85,
21
+ "rationale": "academic source"
22
+ },
23
+ "freshness": {
24
+ "published": null,
25
+ "age": "unknown",
26
+ "stale_warning": "No publish date found \u2014 verify independently",
27
+ "modified": null,
28
+ "has_correction": false,
29
+ "correction_note": null
30
+ },
31
+ "tldr": "Abstract page for arXiv paper 2401.14295: Demystifying Chains, Trees, and Graphs of Thoughts",
32
+ "key_figures": [
33
+ {
34
+ "value": "5%",
35
+ "context": "/search/cs?searchtype=author&query=Kwa%C5%9Bniewski,+G), [J\u00fcrgen M\u00fcller](https://arxiv.org/search/cs?s"
36
+ },
37
+ {
38
+ "value": "3%",
39
+ "context": "rg/search/cs?searchtype=author&query=M%C3%BCller,+J), [Lukas Gianinazzi](https://arxiv.org/search/cs?s"
40
+ },
41
+ {
42
+ "value": "2,247",
43
+ "context": "Thu, 25 Jan 2024 16:34:00 UTC (2,247 KB)** **[[v2]](/abs/2401.14295v2) Sat,"
44
+ },
45
+ {
46
+ "value": "2,290",
47
+ "context": "Sat, 30 Mar 2024 16:33:36 UTC (2,290 KB)** **[[v3]](/abs/2401.14295v3) Fri,"
48
+ },
49
+ {
50
+ "value": "2,297",
51
+ "context": ") Sat, 8 Feb 2025 09:02:41 UTC (2,297 KB)** **[[v5]](/abs/2401.14295v5) Fri,"
52
+ }
53
+ ],
54
+ "structural_trust_score": {
55
+ "level": "medium",
56
+ "trust_factors": [
57
+ "High-authority source",
58
+ "No publish date found \u2014 verify independently"
59
+ ]
60
+ },
61
+ "hedge_language": {
62
+ "density": "low",
63
+ "markers_found": 0,
64
+ "per_500_words": 0
65
+ },
66
+ "content_density": {
67
+ "thin_content": false,
68
+ "substantive_sentence_ratio": 0.7647058823529411
69
+ },
70
+ "syndication": {
71
+ "detected": false,
72
+ "note": null
73
+ },
74
+ "paywall": {
75
+ "detected": false,
76
+ "note": null
77
+ },
78
+ "content_type": "academic_paper",
79
+ "reading_time": {
80
+ "minutes": 4
81
+ },
82
+ "is_archived": {
83
+ "archive_org": true,
84
+ "archive_is": false
85
+ },
86
+ "outbound_authority": {
87
+ "score": 0.54,
88
+ "top_domains": [
89
+ "ui.adsabs.harvard.edu",
90
+ "scholar.google.com",
91
+ "api.semanticscholar.org",
92
+ "info.arxiv.org",
93
+ "core.ac.uk"
94
+ ]
95
+ },
96
+ "boilerplate_ratio": {
97
+ "ratio": 0.08,
98
+ "confidence": "high"
99
+ }
100
+ }
101
+ }
@@ -0,0 +1,272 @@
1
+ # Paying for a parse with x402
2
+
3
+ NanoParse answers an unauthenticated call with **HTTP 402** and machine-readable
4
+ terms — the price, the token, the chain, the recipient, and what a valid signature
5
+ must look like. No account, no API key, no card, no subscription.
6
+
7
+ - **10 free parses per month with a signed wallet**, or 3 anonymous free tries per
8
+ network.
9
+ - **After that: $0.002 USDC on Base per parse**, flat.
10
+ - Settlement runs **after** a page is read. A page that cannot be read — bot wall,
11
+ consent wall, paywall, JS shell with no text — is reported with a named reason
12
+ and **never charged**.
13
+
14
+ Every parse returns clean Markdown plus **Litmus** — 20 machine-readable signals
15
+ (source authority, freshness, structural trust, content density, syndication,
16
+ paywall state) in the same response, not as a second billed call.
17
+
18
+ Two ways to pay. Pick one.
19
+
20
+ ---
21
+
22
+ ## Option A — give the agent a wallet, write no code
23
+
24
+ Add NanoParse to your MCP client, and give the agent a wallet that can sign:
25
+
26
+ ```json
27
+ {
28
+ "mcpServers": {
29
+ "nanoparse": {
30
+ "url": "https://nanoparse.app/mcp"
31
+ }
32
+ }
33
+ }
34
+ ```
35
+
36
+ ```
37
+ npx @coinbase/payments-mcp
38
+ ```
39
+
40
+ (The Coinbase agentic wallet MCP — self-custody, funded via Apple Pay. Docs:
41
+ <https://docs.cdp.coinbase.com/agentic-wallet/mcp/welcome>.)
42
+
43
+ Then the loop is two calls:
44
+
45
+ 1. `nanoparse_fetch(url)`. Once the free tier is spent, the tool returns an
46
+ `isError` result whose **`structuredContent` is the PaymentRequired object** —
47
+ `x402Version`, `accepts[]`, `extensions`. It is the quote, not a failure.
48
+ 2. Sign `accepts[0]` and retry the same tool with
49
+ `payment_signature` = the base64url JSON envelope. This argument works in
50
+ **every** MCP client; there are no custom headers to set. A client that can
51
+ set headers may instead send the same payload as `Payment-Signature`, or put
52
+ the envelope object in `_meta["x402/payment"]`.
53
+
54
+ `nanoparse_status(address)` is free and tells the agent what it needs to know
55
+ before spending: free parses remaining, wallet and USDC balance, whether the next
56
+ call will go through without payment.
57
+
58
+ ---
59
+
60
+ ## Option B — a REST client that pays its own way
61
+
62
+ ~40 lines of Node with [`viem`](https://viem.sh). No SDK, no server-side key:
63
+ the wallet is the caller's.
64
+
65
+ ```js
66
+ // read-page.mjs — read one page, paying $0.002 in USDC on Base only if asked.
67
+ // Usage: npm i viem && WALLET_KEY=0x... node read-page.mjs https://example.com
68
+ //
69
+ // 1. Try the free tier (10/month with a signed wallet, or 3 anonymous tries): a plain POST.
70
+ // 2. A 402 is the quote, not an error. Read the terms out of it.
71
+ // 3. Sign an EIP-3009 permit over those terms (gas is the facilitator's).
72
+ // 4. Retry with the signed envelope; poll /status (free) until the page lands.
73
+ import { privateKeyToAccount } from "viem/accounts";
74
+
75
+ const ENDPOINT = "https://nanoparse.app/fetch";
76
+ const account = privateKeyToAccount(process.env.WALLET_KEY);
77
+ const b64 = (o) => Buffer.from(JSON.stringify(o)).toString("base64");
78
+
79
+ async function readPage(url) {
80
+ const post = (headers) =>
81
+ fetch(ENDPOINT, {
82
+ method: "POST",
83
+ headers: { "Content-Type": "application/json", ...headers },
84
+ body: JSON.stringify({ url }),
85
+ });
86
+
87
+ // 1. Free tier first — no payment, no key, no account.
88
+ let res = await post();
89
+ if (res.status !== 402) {
90
+ if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
91
+ return poll(await res.json());
92
+ }
93
+
94
+ // 2. The terms. `amount` is micro-USDC ("2000" = $0.002) and `payTo` is the
95
+ // only address a permit should ever be signed for.
96
+ const terms = (await res.json()).payment_required.accepts[0];
97
+ const now = Math.floor(Date.now() / 1000);
98
+ const nonce = "0x" + Buffer.from(crypto.getRandomValues(new Uint8Array(32))).toString("hex");
99
+ const authorization = {
100
+ from: account.address,
101
+ to: terms.payTo,
102
+ value: terms.amount,
103
+ validAfter: String(now - 5),
104
+ validBefore: String(now + 895), // within maxTimeoutSeconds from the challenge
105
+ nonce,
106
+ };
107
+
108
+ // 3. EIP-3009 transferWithAuthorization. The EIP-712 domain belongs to the
109
+ // token contract, so take it from the challenge: extra.name is the domain
110
+ // name "USD Coin" — signing against the ticker "USDC" fails verification.
111
+ const signature = await account.signTypedData({
112
+ domain: {
113
+ name: terms.extra.name,
114
+ version: terms.extra.version,
115
+ chainId: 8453, // Base
116
+ verifyingContract: terms.asset,
117
+ },
118
+ types: {
119
+ TransferWithAuthorization: [
120
+ { name: "from", type: "address" },
121
+ { name: "to", type: "address" },
122
+ { name: "value", type: "uint256" },
123
+ { name: "validAfter", type: "uint256" },
124
+ { name: "validBefore", type: "uint256" },
125
+ { name: "nonce", type: "bytes32" },
126
+ ],
127
+ },
128
+ primaryType: "TransferWithAuthorization",
129
+ message: {
130
+ ...authorization,
131
+ value: BigInt(authorization.value),
132
+ validAfter: BigInt(authorization.validAfter),
133
+ validBefore: BigInt(authorization.validBefore),
134
+ },
135
+ });
136
+
137
+ // 4. Retry with the envelope the challenge quoted: accepted[] echoed verbatim.
138
+ res = await post({
139
+ "Payment-Signature": b64({ x402Version: 2, accepted: terms, payload: { signature, authorization } }),
140
+ });
141
+ const job = await res.json();
142
+ if (!res.ok) throw new Error(`${job.error_code}: ${job.message}`);
143
+ return poll(job); // 202 { jobId, statusUrl }
144
+ }
145
+
146
+ // /status is free and unauthenticated: watch a job without spending anything.
147
+ async function poll(job) {
148
+ for (;;) {
149
+ const out = await (await fetch(job.statusUrl)).json();
150
+ if (out.status === "completed") return out; // { markdown, metadata, litmus }
151
+ if (out.status !== "processing") throw new Error(`${out.status}: ${out.error_code ?? ""}`);
152
+ await new Promise((r) => setTimeout(r, 2000));
153
+ }
154
+ }
155
+
156
+ console.log((await readPage(process.argv[2])).markdown);
157
+ ```
158
+
159
+ ---
160
+
161
+ ## The 402 is the quote
162
+
163
+ Captured live, free tier exhausted, no payment attached:
164
+
165
+ ```json
166
+ {
167
+ "error": "payment_required",
168
+ "error_code": "free_tier_exhausted",
169
+ "free_calls_used": 10,
170
+ "free_calls_limit": 10,
171
+ "payment_required": {
172
+ "x402Version": 2,
173
+ "accepts": [
174
+ {
175
+ "scheme": "exact",
176
+ "network": "eip155:8453",
177
+ "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
178
+ "amount": "2000",
179
+ "payTo": "0x57a1d06873db575baeae1f6af30862a75c7bd570",
180
+ "maxTimeoutSeconds": 60,
181
+ "extra": { "assetTransferMethod": "eip3009", "name": "USD Coin", "version": "2" }
182
+ }
183
+ ]
184
+ }
185
+ }
186
+ ```
187
+
188
+ The same terms travel base64url-encoded in the `payment-required` response header,
189
+ and the recipient is repeated as a CAIP-style `pay-to: ethereum:base:0x…` header.
190
+ `access-control-allow-origin: *` and the `Content-Type, Payment-Signature,
191
+ Authorization` request headers are allowed on this route, so a browser-side agent
192
+ can send a signed payment too.
193
+
194
+ Four fields carry the weight:
195
+
196
+ - **`amount`** is micro-USDC — `"2000"` is $0.002 at six decimals. There is no
197
+ currency string to parse.
198
+ - **`network`** is a CAIP-2 chain id (`eip155:8453` is Base) and **`asset`** is the
199
+ USDC contract on it, so the client never guesses which USDC.
200
+ - **`extra.name` / `extra.version`** are the EIP-712 domain of the token contract:
201
+ `"USD Coin"` v2. Signing against `"USDC"` produces a signature that fails
202
+ verification — the domain is part of the digest.
203
+ - **`payTo`** is the only address a permit should ever be signed for. A payload
204
+ pointing anywhere else is rejected before any signature is checked.
205
+
206
+ ## What the server checks before the facilitator is called
207
+
208
+ A wrong shape costs nothing — it answers `400 invalid_payment_payload`:
209
+
210
+ - `x402Version` is exactly `2`.
211
+ - `accepted` repeats the terms verbatim: `scheme`, `network`, `asset`, `amount`,
212
+ `payTo`.
213
+ - `payload.signature` is `0x` + 130 hex characters (65-byte packed `r‖s‖v`).
214
+ - `payload.authorization.value` equals `amount` exactly.
215
+ - `payload.authorization.nonce` is `0x` + 64 hex characters, and single-use
216
+ on-chain — which is what makes "retry the same payload" safe rather than hopeful.
217
+ - `validAfter` is in the past and `validBefore` in the future.
218
+
219
+ ## Retry policy, as error codes
220
+
221
+ | `error_code` | What to do |
222
+ |---|---|
223
+ | `insufficient_usdc` | Fund the wallet and sign a **fresh** envelope. |
224
+ | `invalid_payment_payload`, `invalid_payment` | The shape or signature is wrong. Fix it; retrying unchanged cannot succeed. |
225
+ | `permit_expired`, `permit_not_yet_valid` | Sign a fresh permit. |
226
+ | `settlement_failed` | Ambiguous settle. Retry with the **same** envelope — it is idempotent. |
227
+ | `screening_declined` | Compliance screening declined. Do not retry. |
228
+ | `location_blocked` | A jurisdiction the facilitator does not serve. |
229
+ | `self_send_not_allowed` | Payer and recipient are the same address. |
230
+ | `content_insufficient` | The page rendered but yielded almost no text (JS shell, login wall). **Not charged.** |
231
+ | `domain_blocked` | The host repeatedly returned nothing parseable. **Not charged.** Do not retry. |
232
+
233
+ There is no refund path because nothing is charged up front: settlement runs after
234
+ a successful read, so a failed render simply never settles.
235
+
236
+ ## Check the recipient before you sign
237
+
238
+ `/.well-known/x402` publishes the address the endpoint claims, and the 402 repeats
239
+ it. Compare them before signing anything:
240
+
241
+ ```js
242
+ const accept = terms; // accepts[0] from the 402
243
+ const disc = await (await fetch("https://nanoparse.app/.well-known/x402")).json();
244
+ if (!disc.ownershipProofs.map((a) => a.toLowerCase()).includes(accept.payTo.toLowerCase()))
245
+ throw new Error("recipient not published by the endpoint");
246
+ ```
247
+
248
+ That buys consistency, not identity — both documents come from the same origin —
249
+ but it is one GET and it catches a swapped recipient.
250
+
251
+ ## What was verified
252
+
253
+ - The client under **Option B** was run verbatim against the live endpoint on
254
+ 2026-10-06 with a fresh, unfunded throwaway wallet: free tier exhausted → `402`
255
+ → signed envelope → retry → `insufficient_usdc: Payer's USDC balance is too low
256
+ for this call (needs 2000 micro-USDC on Base)`. The shape and signature were
257
+ accepted; nothing was charged.
258
+ - The **Option A** MCP loop was verified the same way: `tools/call` returned
259
+ `isError: true` with `structuredContent` = the PaymentRequired object, and the
260
+ retry carrying `payment_signature` answered
261
+ `-32000 / insufficient_usdc` for the unfunded payer.
262
+ - The settle leg — USDC actually moving to the treasury on Base — is exercised
263
+ end to end by our own labeled canary through this same paid path.
264
+
265
+ ## Docs
266
+
267
+ - Quick start: <https://nanoparse.app/quickstart>
268
+ - Machine spec: <https://nanoparse.app/openapi.json>
269
+ - Agent-facing description: <https://nanoparse.app/llms.txt>
270
+ - x402 discovery: <https://nanoparse.app/.well-known/x402>
271
+
272
+ MIT licensed.
package/package.json CHANGED
@@ -1,20 +1,7 @@
1
1
  {
2
2
  "name": "nanoparse-mcp",
3
- "version": "1.0.9",
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
- "postbuild": "node -e \"var f='dist/index.js';var c=require('fs').readFileSync(f,'utf8');require('fs').writeFileSync(f,'#!/usr/bin/env node\\n'+c);require('fs').chmodSync(f,'755')\"",
16
- "prepublishOnly": "npm run build"
17
- },
3
+ "version": "2.0.1",
4
+ "description": "NanoParse MCP \u2014 hosted Model Context Protocol server for AI agents. Any URL becomes clean Markdown with attribution signals. $0.002 per page via x402, no account or API key.",
18
5
  "keywords": [
19
6
  "mcp",
20
7
  "model-context-protocol",
@@ -22,19 +9,28 @@
22
9
  "web-scraping",
23
10
  "markdown",
24
11
  "x402",
25
- "ai-agent"
12
+ "ai-agent",
13
+ "litmus"
26
14
  ],
15
+ "license": "MIT",
16
+ "homepage": "https://nanoparse.app",
27
17
  "repository": {
28
18
  "type": "git",
29
19
  "url": "https://github.com/nanoparse-dev/nanoparse-mcp"
30
20
  },
31
- "license": "MIT",
32
- "dependencies": {
33
- "@modelcontextprotocol/sdk": "^1.0.0",
34
- "viem": "^2.55.8"
21
+ "bugs": {
22
+ "url": "https://github.com/nanoparse-dev/nanoparse-mcp/issues"
23
+ },
24
+ "files": [
25
+ "README.md",
26
+ "LICENSE",
27
+ "CHANGELOG.md",
28
+ "examples/"
29
+ ],
30
+ "engines": {
31
+ "node": ">=18"
35
32
  },
36
- "devDependencies": {
37
- "@types/node": "^22.0.0",
38
- "typescript": "^5.5.0"
33
+ "publishConfig": {
34
+ "access": "public"
39
35
  }
40
36
  }
package/dist/fetch.d.ts DELETED
@@ -1,34 +0,0 @@
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
@@ -1 +0,0 @@
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 DELETED
@@ -1,37 +0,0 @@
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
package/dist/fetch.js.map DELETED
@@ -1 +0,0 @@
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 DELETED
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js DELETED
@@ -1,80 +0,0 @@
1
- #!/usr/bin/env node
2
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
- import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
5
- import { fetchPage } from "./fetch.js";
6
- const server = new Server({
7
- name: "nanoparse-mcp",
8
- version: "1.0.9",
9
- }, {
10
- capabilities: {
11
- tools: {},
12
- },
13
- });
14
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
15
- tools: [
16
- {
17
- name: "nanoparse_fetch",
18
- description: "Fetch a web page and return clean, structured Markdown with metadata. " +
19
- "First 10 parses free per device. After that, pay $0.01 USDC on Base via x402. " +
20
- "No account or API key required.",
21
- inputSchema: {
22
- type: "object",
23
- properties: {
24
- url: {
25
- type: "string",
26
- description: "The URL to fetch and convert to Markdown",
27
- },
28
- debug: {
29
- type: "boolean",
30
- description: "If true, include extraction diagnostics in the response (contentSelector, metadataSource, cleaning stats, timing)",
31
- default: false,
32
- },
33
- },
34
- required: ["url"],
35
- },
36
- },
37
- ],
38
- }));
39
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
40
- if (request.params.name !== "nanoparse_fetch") {
41
- throw new Error(`Unknown tool: ${request.params.name}`);
42
- }
43
- const args = request.params.arguments;
44
- if (!args.url || typeof args.url !== "string") {
45
- throw new Error('Missing required parameter: "url"');
46
- }
47
- try {
48
- const result = await fetchPage(args.url, args.debug ?? false);
49
- return {
50
- content: [
51
- {
52
- type: "text",
53
- text: result.markdown,
54
- },
55
- ],
56
- structuredContent: result,
57
- };
58
- }
59
- catch (err) {
60
- const message = err instanceof Error ? err.message : String(err);
61
- return {
62
- content: [
63
- {
64
- type: "text",
65
- text: `NanoParse error: ${message}`,
66
- },
67
- ],
68
- isError: true,
69
- };
70
- }
71
- });
72
- async function main() {
73
- const transport = new StdioServerTransport();
74
- await server.connect(transport);
75
- }
76
- main().catch((err) => {
77
- console.error("nanoparse-mcp server failed to start:", String(err));
78
- process.exit(1);
79
- });
80
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
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,gFAAgF;gBAChF,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"}
package/dist/payment.d.ts DELETED
@@ -1,30 +0,0 @@
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
@@ -1 +0,0 @@
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 DELETED
@@ -1,51 +0,0 @@
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
@@ -1 +0,0 @@
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"}