@forgemeshlabs/seo-authority-mcp 0.1.2 → 0.1.3
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/GLAMA.md +3 -5
- package/README.md +7 -8
- package/SECURITY.md +1 -1
- package/dist/client.js +116 -45
- package/dist/client.js.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/tools.js +4 -4
- package/dist/tools.js.map +1 -1
- package/package.json +3 -2
- package/server.json +2 -16
- package/smithery.yaml +2 -12
- package/tool_manifest.json +38 -2
- package/x402-guard.cjs +141 -0
package/GLAMA.md
CHANGED
|
@@ -26,9 +26,7 @@ Environment schema:
|
|
|
26
26
|
{
|
|
27
27
|
"type": "object",
|
|
28
28
|
"properties": {
|
|
29
|
-
"
|
|
30
|
-
"SEO_X402_PRIVATE_KEY": { "type": "string", "description": "Optional secret EVM private key for automatic payment." },
|
|
31
|
-
"SEO_MAX_PAYMENT_USDC": { "type": "string", "default": "0.15" }
|
|
29
|
+
"SEO_X402_PRIVATE_KEY": { "type": "string", "description": "Optional secret EVM private key for automatic payment." }
|
|
32
30
|
},
|
|
33
31
|
"required": []
|
|
34
32
|
}
|
|
@@ -40,5 +38,5 @@ Runtime notes:
|
|
|
40
38
|
- Node.js 22 or newer.
|
|
41
39
|
- Without a private key, paid tools return structured x402 challenge metadata and spend nothing.
|
|
42
40
|
- With a private key, the MCP settles exact USDC payments on Base and requires a non-empty transaction hash.
|
|
43
|
-
- The
|
|
44
|
-
-
|
|
41
|
+
- The backend is hard-coded to `seo.forgemesh.io`.
|
|
42
|
+
- Each automatic settlement is capped at $0.15 (covers every current tool) and $10 per session; `X402_MAX_PRICE_USD` / `X402_SESSION_BUDGET_USD` can only lower these.
|
package/README.md
CHANGED
|
@@ -37,8 +37,7 @@ Automatic x402 settlement:
|
|
|
37
37
|
"command": "npx",
|
|
38
38
|
"args": ["-y", "@forgemeshlabs/seo-authority-mcp"],
|
|
39
39
|
"env": {
|
|
40
|
-
"SEO_X402_PRIVATE_KEY": "YOUR_DEDICATED_LOW_BALANCE_WALLET_KEY"
|
|
41
|
-
"SEO_MAX_PAYMENT_USDC": "0.15"
|
|
40
|
+
"SEO_X402_PRIVATE_KEY": "YOUR_DEDICATED_LOW_BALANCE_WALLET_KEY"
|
|
42
41
|
}
|
|
43
42
|
}
|
|
44
43
|
}
|
|
@@ -68,8 +67,8 @@ Paid tools use exact USDC settlement on Base. Without `SEO_X402_PRIVATE_KEY`, ea
|
|
|
68
67
|
## Docker
|
|
69
68
|
|
|
70
69
|
```bash
|
|
71
|
-
docker build -t seo-authority-mcp:0.1.
|
|
72
|
-
docker run --rm -i --read-only --cap-drop=ALL seo-authority-mcp:0.1.
|
|
70
|
+
docker build -t seo-authority-mcp:0.1.3 .
|
|
71
|
+
docker run --rm -i --read-only --cap-drop=ALL seo-authority-mcp:0.1.3
|
|
73
72
|
```
|
|
74
73
|
|
|
75
74
|
For automatic payment, pass the private key at runtime using your secret manager. Do not bake it into the image.
|
|
@@ -79,10 +78,10 @@ For automatic payment, pass the private key at runtime using your secret manager
|
|
|
79
78
|
| Variable | Required | Default | Purpose |
|
|
80
79
|
|---|---|---|---|
|
|
81
80
|
| `SEO_X402_PRIVATE_KEY` | No | unset | Enables automatic payment |
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
84
|
-
|
|
85
|
-
|
|
81
|
+
| `X402_MAX_PRICE_USD` | No | `0.15` | Per-call payment ceiling; can only LOWER the built-in cap |
|
|
82
|
+
| `X402_SESSION_BUDGET_USD` | No | `10` | Cumulative per-process ceiling; can only LOWER the built-in cap |
|
|
83
|
+
|
|
84
|
+
The backend is fixed to `https://seo.forgemesh.io`. The server refuses to sign for any payee other than the ForgeMesh SEO wallet, any network other than Base mainnet, any asset other than USDC, or any amount over the cap. Requests are same-origin, time-limited (60s), size-capped (2 MB) and never follow redirects. Tool arguments are validated before any network call. Use a dedicated, low-balance wallet.
|
|
86
85
|
|
|
87
86
|
## Links
|
|
88
87
|
|
package/SECURITY.md
CHANGED
|
@@ -10,4 +10,4 @@ Report vulnerabilities privately to clawdbotworker@gmail.com. Do not include wal
|
|
|
10
10
|
|
|
11
11
|
## Wallet safety
|
|
12
12
|
|
|
13
|
-
Use a dedicated low-balance wallet. Keep `SEO_X402_PRIVATE_KEY` in the MCP client's secret environment configuration, never in source control. Automatic payments are capped per call
|
|
13
|
+
Use a dedicated low-balance wallet. Keep `SEO_X402_PRIVATE_KEY` in the MCP client's secret environment configuration, never in source control. Automatic payments are capped per call ($0.15) and per session ($10); `X402_MAX_PRICE_USD` and `X402_SESSION_BUDGET_USD` can only lower those caps. The server signs only for the ForgeMesh SEO payee, Base mainnet, USDC.
|
package/dist/client.js
CHANGED
|
@@ -1,41 +1,115 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
1
2
|
import { x402Client, x402HTTPClient } from "@x402/core/client";
|
|
2
3
|
import { ExactEvmScheme } from "@x402/evm/exact/client";
|
|
3
4
|
import { toClientEvmSigner } from "@x402/evm";
|
|
4
5
|
import { privateKeyToAccount } from "viem/accounts";
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
const
|
|
6
|
+
// x402-guard is vendored plain CommonJS at the package root (shipped via package.json "files").
|
|
7
|
+
const { createGuard } = createRequire(import.meta.url)("../x402-guard.cjs");
|
|
8
|
+
const guard = createGuard({
|
|
9
|
+
baseUrl: "https://seo.forgemesh.io",
|
|
10
|
+
payTo: ["0xCc73d69a4D04fA41Afa3727C45140d51D71F615c"],
|
|
11
|
+
maxPriceUsd: 0.15, // highest listed tool price
|
|
12
|
+
sessionBudgetUsd: 10
|
|
13
|
+
});
|
|
14
|
+
const MAX_TEXT = 2_000;
|
|
15
|
+
const MAX_HTML = 524_288;
|
|
16
|
+
function fail(field, why) {
|
|
17
|
+
throw new Error(`Invalid argument "${field}": ${why}`);
|
|
18
|
+
}
|
|
19
|
+
function str(field, v, max = MAX_TEXT, optional = false) {
|
|
20
|
+
if (v === undefined && optional)
|
|
21
|
+
return undefined;
|
|
22
|
+
if (typeof v !== "string" || v.length === 0)
|
|
23
|
+
fail(field, "must be a non-empty string");
|
|
24
|
+
if (v.length > max)
|
|
25
|
+
fail(field, `must be at most ${max} characters`);
|
|
26
|
+
return v;
|
|
27
|
+
}
|
|
28
|
+
function domainLike(field, v) {
|
|
29
|
+
const s = str(field, v);
|
|
30
|
+
if (/\s/.test(s))
|
|
31
|
+
fail(field, "must not contain whitespace");
|
|
32
|
+
return s;
|
|
33
|
+
}
|
|
34
|
+
function httpsUrl(field, v) {
|
|
35
|
+
const s = str(field, v);
|
|
36
|
+
let u;
|
|
37
|
+
try {
|
|
38
|
+
u = new URL(s);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return fail(field, "must be an absolute https URL");
|
|
42
|
+
}
|
|
43
|
+
if (u.protocol !== "https:")
|
|
44
|
+
fail(field, "must be an absolute https URL");
|
|
45
|
+
return s;
|
|
46
|
+
}
|
|
47
|
+
function list(field, v, min, max) {
|
|
48
|
+
if (!Array.isArray(v))
|
|
49
|
+
fail(field, "must be an array");
|
|
50
|
+
if (v.length < min || v.length > max)
|
|
51
|
+
fail(field, `must contain ${min} to ${max} items`);
|
|
52
|
+
return v;
|
|
53
|
+
}
|
|
54
|
+
function obj(field, v) {
|
|
55
|
+
if (!v || typeof v !== "object" || Array.isArray(v))
|
|
56
|
+
fail(field, "must be an object");
|
|
57
|
+
return v;
|
|
58
|
+
}
|
|
59
|
+
function page(field, v, requireHtml = false) {
|
|
60
|
+
const p = obj(field, v);
|
|
61
|
+
return {
|
|
62
|
+
url: httpsUrl(`${field}.url`, p.url),
|
|
63
|
+
title: str(`${field}.title`, p.title, MAX_TEXT, true),
|
|
64
|
+
text: str(`${field}.text`, p.text, MAX_HTML, true),
|
|
65
|
+
html: requireHtml ? str(`${field}.html`, p.html, MAX_HTML) : str(`${field}.html`, p.html, MAX_HTML, true)
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function pages(field, v, min, requireHtml = false) {
|
|
69
|
+
return list(field, v, min, 50).map((p, i) => page(`${field}[${i}]`, p, requireHtml));
|
|
70
|
+
}
|
|
71
|
+
function optPages(field, v) {
|
|
72
|
+
return v === undefined ? undefined : pages(field, v, 0);
|
|
73
|
+
}
|
|
15
74
|
function encodeQuery(path, params) {
|
|
16
|
-
const url = new URL(path,
|
|
75
|
+
const url = new URL(path, "https://seo.forgemesh.io/");
|
|
17
76
|
for (const [key, value] of Object.entries(params))
|
|
18
77
|
url.searchParams.set(key, value);
|
|
19
78
|
return `${url.pathname}${url.search}`;
|
|
20
79
|
}
|
|
80
|
+
// Validates every argument before any network call and forwards only known fields.
|
|
21
81
|
export function requestForTool(name, args) {
|
|
22
82
|
switch (name) {
|
|
23
83
|
case "get_seo_capabilities": return { path: "/capabilities" };
|
|
24
|
-
case "get_domain_authority": return { path: encodeQuery("/v1/domain-authority", { domain:
|
|
25
|
-
case "compare_domain_authority": return { path: "/v1/authority-compare", method: "POST", body: { domains: args.domains } };
|
|
26
|
-
case "score_page_seo": return { path: "/v1/url-seo-score", method: "POST", body: { url: args.url, html: args.html } };
|
|
27
|
-
case "correct_seo_query": return { path: encodeQuery("/v1/query-correct", { q:
|
|
28
|
-
case "expand_seo_query": return { path: encodeQuery("/v1/query-expand", { q:
|
|
29
|
-
case "find_keyword_opportunities":
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
84
|
+
case "get_domain_authority": return { path: encodeQuery("/v1/domain-authority", { domain: domainLike("domain", args.domain) }) };
|
|
85
|
+
case "compare_domain_authority": return { path: "/v1/authority-compare", method: "POST", body: { domains: list("domains", args.domains, 2, 20).map((d, i) => domainLike(`domains[${i}]`, d)) } };
|
|
86
|
+
case "score_page_seo": return { path: "/v1/url-seo-score", method: "POST", body: { url: httpsUrl("url", args.url), html: str("html", args.html, MAX_HTML) } };
|
|
87
|
+
case "correct_seo_query": return { path: encodeQuery("/v1/query-correct", { q: str("query", args.query) }) };
|
|
88
|
+
case "expand_seo_query": return { path: encodeQuery("/v1/query-expand", { q: str("query", args.query) }) };
|
|
89
|
+
case "find_keyword_opportunities": {
|
|
90
|
+
const score = args.domain_authority_score;
|
|
91
|
+
if (score !== undefined && (typeof score !== "number" || !Number.isFinite(score) || score < 0 || score > 100))
|
|
92
|
+
fail("domain_authority_score", "must be a number from 0 to 100");
|
|
93
|
+
return { path: "/v1/keyword-opportunity", method: "POST", body: { seed: str("seed", args.seed), audience: str("audience", args.audience, MAX_TEXT, true), domain_authority_score: score } };
|
|
94
|
+
}
|
|
95
|
+
case "find_competitor_content_gaps": {
|
|
96
|
+
const site = obj("site", args.site);
|
|
97
|
+
return { path: "/v1/competitor-gap", method: "POST", body: {
|
|
98
|
+
site: { domain: domainLike("site.domain", site.domain), pages: pages("site.pages", site.pages, 1) },
|
|
99
|
+
competitors: list("competitors", args.competitors, 1, 10).map((c, i) => {
|
|
100
|
+
const comp = obj(`competitors[${i}]`, c);
|
|
101
|
+
return { domain: domainLike(`competitors[${i}].domain`, comp.domain), pages: pages(`competitors[${i}].pages`, comp.pages, 1) };
|
|
102
|
+
})
|
|
103
|
+
} };
|
|
104
|
+
}
|
|
105
|
+
case "create_seo_content_brief": return { path: "/v1/content-brief", method: "POST", body: { keyword: str("keyword", args.keyword), competitor_pages: optPages("competitor_pages", args.competitor_pages), site_pages: optPages("site_pages", args.site_pages) } };
|
|
106
|
+
case "audit_site_seo": return { path: "/v1/site-audit", method: "POST", body: { pages: pages("pages", args.pages, 1, true) } };
|
|
107
|
+
case "find_internal_link_opportunities": return { path: "/v1/internal-link-opportunities", method: "POST", body: { domain: domainLike("domain", args.domain), pages: pages("pages", args.pages, 2) } };
|
|
34
108
|
default: throw new Error(`Unknown tool: ${name}`);
|
|
35
109
|
}
|
|
36
110
|
}
|
|
37
|
-
function parseChallenge(
|
|
38
|
-
const raw =
|
|
111
|
+
function parseChallenge(headers, body) {
|
|
112
|
+
const raw = headers.get("payment-required");
|
|
39
113
|
if (!raw)
|
|
40
114
|
return null;
|
|
41
115
|
try {
|
|
@@ -62,40 +136,37 @@ function findTx(value) {
|
|
|
62
136
|
}
|
|
63
137
|
return null;
|
|
64
138
|
}
|
|
65
|
-
function
|
|
66
|
-
|
|
139
|
+
function parseBody(text) {
|
|
140
|
+
try {
|
|
141
|
+
return JSON.parse(text);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
67
146
|
}
|
|
68
147
|
export async function callSeoTool(name, args) {
|
|
69
148
|
const spec = requestForTool(name, args);
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
if (
|
|
74
|
-
if (!
|
|
75
|
-
throw new Error(`SEO API ${
|
|
76
|
-
return
|
|
149
|
+
const headers = { accept: "application/json" };
|
|
150
|
+
const first = await guard.fetchBounded(spec.path, { method: spec.method || "GET", headers: { ...headers, ...(spec.body ? { "content-type": "application/json" } : {}) }, ...(spec.body ? { body: JSON.stringify(spec.body) } : {}) });
|
|
151
|
+
const firstBody = parseBody(first.text);
|
|
152
|
+
if (first.status !== 402) {
|
|
153
|
+
if (!first.ok)
|
|
154
|
+
throw new Error(`SEO API ${first.status}: ${first.text.slice(0, 200)}`);
|
|
155
|
+
return firstBody;
|
|
77
156
|
}
|
|
78
|
-
const challenge = parseChallenge(
|
|
157
|
+
const challenge = parseChallenge(first.headers, firstBody);
|
|
79
158
|
const privateKey = process.env.SEO_X402_PRIVATE_KEY;
|
|
80
159
|
if (!privateKey)
|
|
81
160
|
return safeChallenge(challenge);
|
|
82
161
|
if (!/^0x[a-fA-F0-9]{64}$/.test(privateKey))
|
|
83
162
|
throw new Error("SEO_X402_PRIVATE_KEY must be a 32-byte hex private key");
|
|
84
|
-
const amount = BigInt(challenge?.accepts?.[0]?.amount || 0);
|
|
85
|
-
if (amount <= 0n || amount > maxAtomic)
|
|
86
|
-
throw new Error(`Payment exceeds SEO_MAX_PAYMENT_USDC (${Number(maxAtomic) / 1_000_000})`);
|
|
87
163
|
const account = privateKeyToAccount(privateKey);
|
|
88
|
-
const httpClient = new x402HTTPClient(new x402Client().register("eip155:*", new ExactEvmScheme(toClientEvmSigner(account))));
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
const paid = await fetch(url, init(spec, httpClient.encodePaymentSignatureHeader(payload)));
|
|
92
|
-
const paidBody = await paid.json().catch(() => null);
|
|
93
|
-
if (!paid.ok)
|
|
94
|
-
throw new Error(`Paid SEO API ${paid.status}: ${JSON.stringify(paidBody).slice(0, 300)}`);
|
|
95
|
-
const receipt = httpClient.getPaymentSettleResponse(header => paid.headers.get(header));
|
|
96
|
-
const transaction = findTx(receipt);
|
|
164
|
+
const httpClient = new x402HTTPClient(new x402Client().register("eip155:*", new ExactEvmScheme(toClientEvmSigner(account))).registerPolicy(guard.policy));
|
|
165
|
+
const { _payment, ...paidBody } = await guard.callPaid(httpClient, spec.path, { method: spec.method || "GET", body: spec.body, headers });
|
|
166
|
+
const transaction = findTx(_payment);
|
|
97
167
|
if (!transaction)
|
|
98
168
|
throw new Error("Facilitator returned no settlement transaction hash");
|
|
169
|
+
const amount = BigInt(challenge?.accepts?.[0]?.amount || 0);
|
|
99
170
|
return { ...paidBody, x402_settlement: { amount_usdc: Number(amount) / 1_000_000, network: challenge?.accepts?.[0]?.network, transaction } };
|
|
100
171
|
}
|
|
101
172
|
//# sourceMappingURL=client.js.map
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import { createRequire } from "node:module";
|
|
5
6
|
import { callSeoTool } from "./client.js";
|
|
6
7
|
import { tools } from "./tools.js";
|
|
7
|
-
const
|
|
8
|
+
const { version } = createRequire(import.meta.url)("../package.json");
|
|
9
|
+
const server = new Server({ name: "seo-authority-mcp", version }, { capabilities: { tools: {} } });
|
|
8
10
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
|
|
9
11
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
10
12
|
try {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AACnG,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AACnG,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,MAAM,EAAE,OAAO,EAAE,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAAwB,CAAC;AAC7F,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;AACnG,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;AAC1E,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAC,OAAO,EAAC,EAAE;IAC9D,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAA4B,CAAC,CAAC;QACjH,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;IACrE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QACzE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC9H,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC"}
|
package/dist/tools.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
const readOnly = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true };
|
|
2
|
-
const string = (description) => ({ type: "string", description });
|
|
2
|
+
const string = (description, maxLength = 2000) => ({ type: "string", maxLength, description });
|
|
3
3
|
const page = {
|
|
4
4
|
type: "object",
|
|
5
|
-
properties: { url: string("Absolute page URL."), title: string("Page title."), text: string("Visible page text."), html: string("Page HTML, when required.") },
|
|
5
|
+
properties: { url: string("Absolute page URL."), title: string("Page title."), text: string("Visible page text.", 524288), html: string("Page HTML, when required.", 524288) },
|
|
6
6
|
required: ["url"]
|
|
7
7
|
};
|
|
8
8
|
export const tools = [
|
|
9
9
|
{ name: "get_seo_capabilities", description: "Retrieve free capability, provenance, and unavailable-metric disclosures before choosing a paid SEO tool.", annotations: readOnly, inputSchema: { type: "object", properties: {} } },
|
|
10
10
|
{ name: "get_domain_authority", description: "Measure one domain's open-web SEO authority using Common Crawl harmonic centrality and PageRank graph data; costs $0.01 USDC.", annotations: readOnly, inputSchema: { type: "object", properties: { domain: string("Domain or URL to measure, such as example.com.") }, required: ["domain"] } },
|
|
11
|
-
{ name: "compare_domain_authority", description: "Compare open-web SEO authority for 2 to 20 domains and rank the results; costs $0.02 USDC.", annotations: readOnly, inputSchema: { type: "object", properties: { domains: { type: "array", description: "Two to twenty domains or URLs.", minItems: 2, maxItems: 20, items: { type: "string" } } }, required: ["domains"] } },
|
|
12
|
-
{ name: "score_page_seo", description: "Score supplied page HTML for deterministic on-page SEO checks without claiming page authority; costs $0.01 USDC.", annotations: readOnly, inputSchema: { type: "object", properties: { url: string("Canonical URL for the supplied page."), html: string("Complete page HTML, up to 512 KB.") }, required: ["url", "html"] } },
|
|
11
|
+
{ name: "compare_domain_authority", description: "Compare open-web SEO authority for 2 to 20 domains and rank the results; costs $0.02 USDC.", annotations: readOnly, inputSchema: { type: "object", properties: { domains: { type: "array", description: "Two to twenty domains or URLs.", minItems: 2, maxItems: 20, items: { type: "string", maxLength: 2000 } } }, required: ["domains"] } },
|
|
12
|
+
{ name: "score_page_seo", description: "Score supplied page HTML for deterministic on-page SEO checks without claiming page authority; costs $0.01 USDC.", annotations: readOnly, inputSchema: { type: "object", properties: { url: string("Canonical URL for the supplied page."), html: string("Complete page HTML, up to 512 KB.", 524288) }, required: ["url", "html"] } },
|
|
13
13
|
{ name: "correct_seo_query", description: "Correct likely spelling errors in an SEO search query; costs $0.005 USDC.", annotations: readOnly, inputSchema: { type: "object", properties: { query: string("SEO query to correct.") }, required: ["query"] } },
|
|
14
14
|
{ name: "expand_seo_query", description: "Generate deterministic long-tail and intent-oriented variants of an SEO query; costs $0.01 USDC.", annotations: readOnly, inputSchema: { type: "object", properties: { query: string("Seed SEO query to expand.") }, required: ["query"] } },
|
|
15
15
|
{ name: "find_keyword_opportunities", description: "Generate and prioritize long-tail keyword opportunities without fabricating volume, CPC, SERP, or difficulty metrics; costs $0.05 USDC.", annotations: readOnly, inputSchema: { type: "object", properties: { seed: string("Seed topic or keyword."), audience: string("Optional target audience."), domain_authority_score: { type: "number", minimum: 0, maximum: 100, description: "Optional known authority score used for prioritization." } }, required: ["seed"] } },
|
package/dist/tools.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAEA,MAAM,QAAQ,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAW,CAAC;AACpH,MAAM,MAAM,GAAG,CAAC,WAAmB,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forgemeshlabs/seo-authority-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"mcpName": "io.github.forgemeshlabs/seo-authority-mcp",
|
|
5
5
|
"description": "SEO authority, keyword opportunities, competitor gaps, content briefs, site audits, and internal-link intelligence via x402.",
|
|
6
6
|
"type": "module",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
12
|
"dist",
|
|
13
|
+
"x402-guard.cjs",
|
|
13
14
|
"tool_manifest.json",
|
|
14
15
|
"README.md",
|
|
15
16
|
"GLAMA.md",
|
|
@@ -28,7 +29,7 @@
|
|
|
28
29
|
"test": "vitest run --cache=false"
|
|
29
30
|
},
|
|
30
31
|
"dependencies": {
|
|
31
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
32
|
+
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
32
33
|
"@x402/core": "^2.16.0",
|
|
33
34
|
"@x402/evm": "^2.16.0",
|
|
34
35
|
"viem": "^2.38.0"
|
package/server.json
CHANGED
|
@@ -6,36 +6,22 @@
|
|
|
6
6
|
"url": "https://github.com/forgemeshlabs/seo-authority-mcp",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.3",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "@forgemeshlabs/seo-authority-mcp",
|
|
14
|
-
"version": "0.1.
|
|
14
|
+
"version": "0.1.3",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|
|
18
18
|
"environmentVariables": [
|
|
19
|
-
{
|
|
20
|
-
"name": "SEO_API_BASE",
|
|
21
|
-
"description": "Optional backend override; defaults to https://seo.forgemesh.io.",
|
|
22
|
-
"isRequired": false,
|
|
23
|
-
"isSecret": false,
|
|
24
|
-
"format": "string"
|
|
25
|
-
},
|
|
26
19
|
{
|
|
27
20
|
"name": "SEO_X402_PRIVATE_KEY",
|
|
28
21
|
"description": "Optional EVM wallet private key for automatic x402 USDC settlement.",
|
|
29
22
|
"isRequired": false,
|
|
30
23
|
"isSecret": true,
|
|
31
24
|
"format": "string"
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
"name": "SEO_MAX_PAYMENT_USDC",
|
|
35
|
-
"description": "Maximum automatic payment per tool call; defaults to 0.15 USDC.",
|
|
36
|
-
"isRequired": false,
|
|
37
|
-
"isSecret": false,
|
|
38
|
-
"format": "string"
|
|
39
25
|
}
|
|
40
26
|
]
|
|
41
27
|
}
|
package/smithery.yaml
CHANGED
|
@@ -3,25 +3,15 @@ startCommand:
|
|
|
3
3
|
configSchema:
|
|
4
4
|
type: object
|
|
5
5
|
properties:
|
|
6
|
-
seoApiBase:
|
|
7
|
-
type: string
|
|
8
|
-
description: SEO API base URL. Defaults to https://seo.forgemesh.io.
|
|
9
6
|
seoX402PrivateKey:
|
|
10
7
|
type: string
|
|
11
8
|
description: Optional EVM private key for automatic x402 settlement.
|
|
12
|
-
seoMaxPaymentUsdc:
|
|
13
|
-
type: string
|
|
14
|
-
description: Maximum automatic USDC payment per call. Defaults to 0.15.
|
|
15
9
|
commandFunction: |-
|
|
16
10
|
(config) => ({
|
|
17
11
|
command: 'npx',
|
|
18
12
|
args: ['-y', '@forgemeshlabs/seo-authority-mcp'],
|
|
19
13
|
env: {
|
|
20
|
-
|
|
21
|
-
SEO_X402_PRIVATE_KEY: config.seoX402PrivateKey || '',
|
|
22
|
-
SEO_MAX_PAYMENT_USDC: config.seoMaxPaymentUsdc || '0.15'
|
|
14
|
+
SEO_X402_PRIVATE_KEY: config.seoX402PrivateKey || ''
|
|
23
15
|
}
|
|
24
16
|
})
|
|
25
|
-
exampleConfig:
|
|
26
|
-
seoApiBase: https://seo.forgemesh.io
|
|
27
|
-
seoMaxPaymentUsdc: '0.15'
|
|
17
|
+
exampleConfig: {}
|
package/tool_manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"server": "seo-authority-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"source": "https://seo.forgemesh.io/openapi.json",
|
|
5
5
|
"tools": [
|
|
6
6
|
{
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"properties": {
|
|
32
32
|
"domain": {
|
|
33
33
|
"type": "string",
|
|
34
|
+
"maxLength": 2000,
|
|
34
35
|
"description": "Domain or URL to measure, such as example.com."
|
|
35
36
|
}
|
|
36
37
|
},
|
|
@@ -57,7 +58,8 @@
|
|
|
57
58
|
"minItems": 2,
|
|
58
59
|
"maxItems": 20,
|
|
59
60
|
"items": {
|
|
60
|
-
"type": "string"
|
|
61
|
+
"type": "string",
|
|
62
|
+
"maxLength": 2000
|
|
61
63
|
}
|
|
62
64
|
}
|
|
63
65
|
},
|
|
@@ -80,10 +82,12 @@
|
|
|
80
82
|
"properties": {
|
|
81
83
|
"url": {
|
|
82
84
|
"type": "string",
|
|
85
|
+
"maxLength": 2000,
|
|
83
86
|
"description": "Canonical URL for the supplied page."
|
|
84
87
|
},
|
|
85
88
|
"html": {
|
|
86
89
|
"type": "string",
|
|
90
|
+
"maxLength": 524288,
|
|
87
91
|
"description": "Complete page HTML, up to 512 KB."
|
|
88
92
|
}
|
|
89
93
|
},
|
|
@@ -107,6 +111,7 @@
|
|
|
107
111
|
"properties": {
|
|
108
112
|
"query": {
|
|
109
113
|
"type": "string",
|
|
114
|
+
"maxLength": 2000,
|
|
110
115
|
"description": "SEO query to correct."
|
|
111
116
|
}
|
|
112
117
|
},
|
|
@@ -129,6 +134,7 @@
|
|
|
129
134
|
"properties": {
|
|
130
135
|
"query": {
|
|
131
136
|
"type": "string",
|
|
137
|
+
"maxLength": 2000,
|
|
132
138
|
"description": "Seed SEO query to expand."
|
|
133
139
|
}
|
|
134
140
|
},
|
|
@@ -151,10 +157,12 @@
|
|
|
151
157
|
"properties": {
|
|
152
158
|
"seed": {
|
|
153
159
|
"type": "string",
|
|
160
|
+
"maxLength": 2000,
|
|
154
161
|
"description": "Seed topic or keyword."
|
|
155
162
|
},
|
|
156
163
|
"audience": {
|
|
157
164
|
"type": "string",
|
|
165
|
+
"maxLength": 2000,
|
|
158
166
|
"description": "Optional target audience."
|
|
159
167
|
},
|
|
160
168
|
"domain_authority_score": {
|
|
@@ -187,6 +195,7 @@
|
|
|
187
195
|
"properties": {
|
|
188
196
|
"domain": {
|
|
189
197
|
"type": "string",
|
|
198
|
+
"maxLength": 2000,
|
|
190
199
|
"description": "Your domain."
|
|
191
200
|
},
|
|
192
201
|
"pages": {
|
|
@@ -198,18 +207,22 @@
|
|
|
198
207
|
"properties": {
|
|
199
208
|
"url": {
|
|
200
209
|
"type": "string",
|
|
210
|
+
"maxLength": 2000,
|
|
201
211
|
"description": "Absolute page URL."
|
|
202
212
|
},
|
|
203
213
|
"title": {
|
|
204
214
|
"type": "string",
|
|
215
|
+
"maxLength": 2000,
|
|
205
216
|
"description": "Page title."
|
|
206
217
|
},
|
|
207
218
|
"text": {
|
|
208
219
|
"type": "string",
|
|
220
|
+
"maxLength": 524288,
|
|
209
221
|
"description": "Visible page text."
|
|
210
222
|
},
|
|
211
223
|
"html": {
|
|
212
224
|
"type": "string",
|
|
225
|
+
"maxLength": 524288,
|
|
213
226
|
"description": "Page HTML, when required."
|
|
214
227
|
}
|
|
215
228
|
},
|
|
@@ -234,6 +247,7 @@
|
|
|
234
247
|
"properties": {
|
|
235
248
|
"domain": {
|
|
236
249
|
"type": "string",
|
|
250
|
+
"maxLength": 2000,
|
|
237
251
|
"description": "Competitor domain."
|
|
238
252
|
},
|
|
239
253
|
"pages": {
|
|
@@ -245,18 +259,22 @@
|
|
|
245
259
|
"properties": {
|
|
246
260
|
"url": {
|
|
247
261
|
"type": "string",
|
|
262
|
+
"maxLength": 2000,
|
|
248
263
|
"description": "Absolute page URL."
|
|
249
264
|
},
|
|
250
265
|
"title": {
|
|
251
266
|
"type": "string",
|
|
267
|
+
"maxLength": 2000,
|
|
252
268
|
"description": "Page title."
|
|
253
269
|
},
|
|
254
270
|
"text": {
|
|
255
271
|
"type": "string",
|
|
272
|
+
"maxLength": 524288,
|
|
256
273
|
"description": "Visible page text."
|
|
257
274
|
},
|
|
258
275
|
"html": {
|
|
259
276
|
"type": "string",
|
|
277
|
+
"maxLength": 524288,
|
|
260
278
|
"description": "Page HTML, when required."
|
|
261
279
|
}
|
|
262
280
|
},
|
|
@@ -293,6 +311,7 @@
|
|
|
293
311
|
"properties": {
|
|
294
312
|
"keyword": {
|
|
295
313
|
"type": "string",
|
|
314
|
+
"maxLength": 2000,
|
|
296
315
|
"description": "Primary keyword or topic."
|
|
297
316
|
},
|
|
298
317
|
"competitor_pages": {
|
|
@@ -303,18 +322,22 @@
|
|
|
303
322
|
"properties": {
|
|
304
323
|
"url": {
|
|
305
324
|
"type": "string",
|
|
325
|
+
"maxLength": 2000,
|
|
306
326
|
"description": "Absolute page URL."
|
|
307
327
|
},
|
|
308
328
|
"title": {
|
|
309
329
|
"type": "string",
|
|
330
|
+
"maxLength": 2000,
|
|
310
331
|
"description": "Page title."
|
|
311
332
|
},
|
|
312
333
|
"text": {
|
|
313
334
|
"type": "string",
|
|
335
|
+
"maxLength": 524288,
|
|
314
336
|
"description": "Visible page text."
|
|
315
337
|
},
|
|
316
338
|
"html": {
|
|
317
339
|
"type": "string",
|
|
340
|
+
"maxLength": 524288,
|
|
318
341
|
"description": "Page HTML, when required."
|
|
319
342
|
}
|
|
320
343
|
},
|
|
@@ -332,18 +355,22 @@
|
|
|
332
355
|
"properties": {
|
|
333
356
|
"url": {
|
|
334
357
|
"type": "string",
|
|
358
|
+
"maxLength": 2000,
|
|
335
359
|
"description": "Absolute page URL."
|
|
336
360
|
},
|
|
337
361
|
"title": {
|
|
338
362
|
"type": "string",
|
|
363
|
+
"maxLength": 2000,
|
|
339
364
|
"description": "Page title."
|
|
340
365
|
},
|
|
341
366
|
"text": {
|
|
342
367
|
"type": "string",
|
|
368
|
+
"maxLength": 524288,
|
|
343
369
|
"description": "Visible page text."
|
|
344
370
|
},
|
|
345
371
|
"html": {
|
|
346
372
|
"type": "string",
|
|
373
|
+
"maxLength": 524288,
|
|
347
374
|
"description": "Page HTML, when required."
|
|
348
375
|
}
|
|
349
376
|
},
|
|
@@ -381,18 +408,22 @@
|
|
|
381
408
|
"properties": {
|
|
382
409
|
"url": {
|
|
383
410
|
"type": "string",
|
|
411
|
+
"maxLength": 2000,
|
|
384
412
|
"description": "Absolute page URL."
|
|
385
413
|
},
|
|
386
414
|
"title": {
|
|
387
415
|
"type": "string",
|
|
416
|
+
"maxLength": 2000,
|
|
388
417
|
"description": "Page title."
|
|
389
418
|
},
|
|
390
419
|
"text": {
|
|
391
420
|
"type": "string",
|
|
421
|
+
"maxLength": 524288,
|
|
392
422
|
"description": "Visible page text."
|
|
393
423
|
},
|
|
394
424
|
"html": {
|
|
395
425
|
"type": "string",
|
|
426
|
+
"maxLength": 524288,
|
|
396
427
|
"description": "Page HTML, when required."
|
|
397
428
|
}
|
|
398
429
|
},
|
|
@@ -422,6 +453,7 @@
|
|
|
422
453
|
"properties": {
|
|
423
454
|
"domain": {
|
|
424
455
|
"type": "string",
|
|
456
|
+
"maxLength": 2000,
|
|
425
457
|
"description": "Site domain."
|
|
426
458
|
},
|
|
427
459
|
"pages": {
|
|
@@ -433,18 +465,22 @@
|
|
|
433
465
|
"properties": {
|
|
434
466
|
"url": {
|
|
435
467
|
"type": "string",
|
|
468
|
+
"maxLength": 2000,
|
|
436
469
|
"description": "Absolute page URL."
|
|
437
470
|
},
|
|
438
471
|
"title": {
|
|
439
472
|
"type": "string",
|
|
473
|
+
"maxLength": 2000,
|
|
440
474
|
"description": "Page title."
|
|
441
475
|
},
|
|
442
476
|
"text": {
|
|
443
477
|
"type": "string",
|
|
478
|
+
"maxLength": 524288,
|
|
444
479
|
"description": "Visible page text."
|
|
445
480
|
},
|
|
446
481
|
"html": {
|
|
447
482
|
"type": "string",
|
|
483
|
+
"maxLength": 524288,
|
|
448
484
|
"description": "Page HTML, when required."
|
|
449
485
|
}
|
|
450
486
|
},
|
package/x402-guard.cjs
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* x402-guard — client-side payment and HTTP guard for ForgeMesh MCP servers (vendored copy per repo).
|
|
4
|
+
*
|
|
5
|
+
* const { createGuard } = require("./x402-guard");
|
|
6
|
+
* const guard = createGuard({
|
|
7
|
+
* baseUrl: "https://x402.coinopai.com", // the ONLY origin this server talks to
|
|
8
|
+
* payTo: ["0x1304EC1A8945365e43A5c18a734065f107B417cA"], // wallets a 402 may name
|
|
9
|
+
* maxPriceUsd: 1.00, // per-call ceiling (env X402_MAX_PRICE_USD overrides, lower only)
|
|
10
|
+
* sessionBudgetUsd: 10.00, // cumulative ceiling per process (env X402_SESSION_BUDGET_USD)
|
|
11
|
+
* });
|
|
12
|
+
* const client = new x402Client().register("eip155:*", new ExactEvmScheme(signer)).registerPolicy(guard.policy);
|
|
13
|
+
* const data = await guard.callPaid(httpClient, "/api/route", { method: "POST", body: {...} });
|
|
14
|
+
*
|
|
15
|
+
* What it enforces, before any signature is produced:
|
|
16
|
+
* network = eip155:8453 (Base mainnet), asset = USDC on Base, payTo in the allowlist,
|
|
17
|
+
* amount <= maxPriceUsd, running total <= sessionBudgetUsd.
|
|
18
|
+
* What it enforces on every HTTP call: same origin as baseUrl, https, no redirects,
|
|
19
|
+
* REQUEST_TIMEOUT_MS, MAX_RESPONSE_BYTES. No global monkeypatching anywhere.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const BASE_MAINNET = "eip155:8453";
|
|
23
|
+
const USDC_BASE = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
|
|
24
|
+
const REQUEST_TIMEOUT_MS = 60_000;
|
|
25
|
+
const MAX_RESPONSE_BYTES = 2_000_000;
|
|
26
|
+
|
|
27
|
+
function usdFromRequirement(r) {
|
|
28
|
+
const raw = r.amount ?? r.maxAmountRequired ?? r.value;
|
|
29
|
+
if (raw === undefined || raw === null) return NaN;
|
|
30
|
+
const s = String(raw);
|
|
31
|
+
if (s.startsWith("$")) return Number(s.slice(1));
|
|
32
|
+
return Number(BigInt(s)) / 1e6; // USDC atomic units
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function lowerCap(defaultUsd, envName) {
|
|
36
|
+
const env = Number(process.env[envName]);
|
|
37
|
+
return Number.isFinite(env) && env > 0 ? Math.min(env, defaultUsd) : defaultUsd;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function createGuard(opts) {
|
|
41
|
+
if (!opts || !opts.baseUrl) throw new Error("x402-guard: baseUrl is required");
|
|
42
|
+
const base = new URL(opts.baseUrl);
|
|
43
|
+
if (base.protocol !== "https:") throw new Error("x402-guard: baseUrl must be https");
|
|
44
|
+
const origin = base.origin;
|
|
45
|
+
// An empty allowlist means "this server never signs payments": the policy refuses every 402 and
|
|
46
|
+
// only fetchBounded is useful. Pass payTo: [] explicitly for free-only servers.
|
|
47
|
+
if (!Array.isArray(opts.payTo)) throw new Error("x402-guard: payTo allowlist is required (use [] for a server that never signs)");
|
|
48
|
+
const payTo = new Set(opts.payTo.map((a) => String(a).toLowerCase()));
|
|
49
|
+
const maxPriceUsd = lowerCap(opts.maxPriceUsd ?? 1.0, "X402_MAX_PRICE_USD");
|
|
50
|
+
const sessionBudgetUsd = lowerCap(opts.sessionBudgetUsd ?? 10.0, "X402_SESSION_BUDGET_USD");
|
|
51
|
+
let spentUsd = 0; // reserved at signing time, never released: a signed authorization can be settled even if our retry fails
|
|
52
|
+
|
|
53
|
+
function policy(_version, requirements) {
|
|
54
|
+
if (payTo.size === 0) throw new Error("x402-guard refused to sign: this server has no payee allowlist and never signs payments");
|
|
55
|
+
const kept = requirements.filter((r) => {
|
|
56
|
+
if (r.network !== BASE_MAINNET) return false;
|
|
57
|
+
if (String(r.asset || "").toLowerCase() !== USDC_BASE) return false;
|
|
58
|
+
if (!payTo.has(String(r.payTo || "").toLowerCase())) return false;
|
|
59
|
+
const usd = usdFromRequirement(r);
|
|
60
|
+
if (!Number.isFinite(usd) || usd <= 0 || usd > maxPriceUsd) return false;
|
|
61
|
+
if (spentUsd + usd > sessionBudgetUsd) return false;
|
|
62
|
+
return true;
|
|
63
|
+
});
|
|
64
|
+
if (kept.length === 0) {
|
|
65
|
+
const seen = requirements.map((r) => `${r.network} ${r.payTo} $${usdFromRequirement(r)}`).join("; ");
|
|
66
|
+
throw new Error(`x402-guard refused to sign: no payment option passed the guard (network must be ${BASE_MAINNET}, asset USDC, payTo in allowlist, price <= $${maxPriceUsd}, session total <= $${sessionBudgetUsd}). Offered: ${seen}`);
|
|
67
|
+
}
|
|
68
|
+
// Hand the selector exactly one option so the amount we reserve is the amount that gets signed.
|
|
69
|
+
const chosen = kept.reduce((a, b) => (usdFromRequirement(b) < usdFromRequirement(a) ? b : a));
|
|
70
|
+
spentUsd += usdFromRequirement(chosen);
|
|
71
|
+
return [chosen];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function fetchBounded(url, init = {}) {
|
|
75
|
+
const target = new URL(String(url), origin);
|
|
76
|
+
if (target.origin !== origin) throw new Error(`x402-guard: refusing request to ${target.origin}; this server only talks to ${origin}`);
|
|
77
|
+
const ctrl = new AbortController();
|
|
78
|
+
const timer = setTimeout(() => ctrl.abort(), REQUEST_TIMEOUT_MS);
|
|
79
|
+
try {
|
|
80
|
+
const res = await fetch(target.toString(), { ...init, signal: ctrl.signal, redirect: "error" });
|
|
81
|
+
const declared = Number(res.headers.get("content-length") || 0);
|
|
82
|
+
if (declared > MAX_RESPONSE_BYTES) { ctrl.abort(); throw new Error(`x402-guard: response too large (${declared} bytes)`); }
|
|
83
|
+
const chunks = [];
|
|
84
|
+
let received = 0;
|
|
85
|
+
if (res.body) {
|
|
86
|
+
const reader = res.body.getReader();
|
|
87
|
+
for (;;) {
|
|
88
|
+
const { done, value } = await reader.read();
|
|
89
|
+
if (done) break;
|
|
90
|
+
received += value.byteLength;
|
|
91
|
+
if (received > MAX_RESPONSE_BYTES) { ctrl.abort(); throw new Error("x402-guard: response too large"); }
|
|
92
|
+
chunks.push(value);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const bytes = Buffer.concat(chunks);
|
|
96
|
+
return { status: res.status, ok: res.ok, headers: res.headers, bytes, text: bytes.toString("utf8") };
|
|
97
|
+
} finally {
|
|
98
|
+
clearTimeout(timer);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function parseJson(res) {
|
|
103
|
+
try { return JSON.parse(res.text); } catch { throw new Error(`HTTP ${res.status}: non-JSON response`); }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// GET or POST a paid route: unpaid request → 402 → guarded signature → retry. Returns parsed JSON;
|
|
107
|
+
// on a paid response attaches the settlement receipt as `_payment` when the body is an object.
|
|
108
|
+
async function callPaid(httpClient, path, { method = "GET", body, query, headers = {} } = {}) {
|
|
109
|
+
const url = new URL(path, origin);
|
|
110
|
+
if (query) for (const [k, v] of Object.entries(query)) if (v !== undefined && v !== null) url.searchParams.set(k, String(v));
|
|
111
|
+
const init = { method, headers: { ...headers, ...(body ? { "content-type": "application/json" } : {}) }, ...(body ? { body: JSON.stringify(body) } : {}) };
|
|
112
|
+
const res = await fetchBounded(url, init);
|
|
113
|
+
if (res.status !== 402) {
|
|
114
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.text.slice(0, 200)}`);
|
|
115
|
+
return parseJson(res);
|
|
116
|
+
}
|
|
117
|
+
let challenge;
|
|
118
|
+
try { challenge = JSON.parse(res.text); } catch { challenge = undefined; }
|
|
119
|
+
const paymentRequired = httpClient.getPaymentRequiredResponse((n) => res.headers.get(n), challenge);
|
|
120
|
+
const payload = await httpClient.createPaymentPayload(paymentRequired); // policy runs (and reserves budget) inside
|
|
121
|
+
const paid = await fetchBounded(url, { ...init, headers: { ...init.headers, ...httpClient.encodePaymentSignatureHeader(payload) } });
|
|
122
|
+
if (!paid.ok) throw new Error(`Payment failed — HTTP ${paid.status}: ${paid.text.slice(0, 200)}`);
|
|
123
|
+
let settle = null;
|
|
124
|
+
try { settle = httpClient.getPaymentSettleResponse((n) => paid.headers.get(n)) || null; } catch { settle = null; }
|
|
125
|
+
const contentType = paid.headers.get("content-type") || "";
|
|
126
|
+
// Non-JSON paid responses (audio, images) come back as raw bytes, never decoded as text.
|
|
127
|
+
if (!contentType.includes("json")) return { _binary: true, content_type: contentType, bytes: paid.bytes, _payment: settle };
|
|
128
|
+
const data = parseJson(paid);
|
|
129
|
+
if (settle && data && typeof data === "object" && !Array.isArray(data)) return { ...data, _payment: settle };
|
|
130
|
+
return data;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
policy,
|
|
135
|
+
fetchBounded,
|
|
136
|
+
callPaid,
|
|
137
|
+
limits: () => ({ maxPriceUsd, sessionBudgetUsd, spentUsd, origin, payTo: [...payTo] }),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
module.exports = { createGuard, REQUEST_TIMEOUT_MS, MAX_RESPONSE_BYTES, BASE_MAINNET, USDC_BASE };
|