@nullclear/mcp 1.1.2 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +360 -24
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -12,14 +12,14 @@ import {
|
|
|
12
12
|
ReadResourceRequestSchema
|
|
13
13
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
14
14
|
import { NullClearClient, createNullClearFetch, NullClearEscrowAbortedError } from "@nullclear/x402-arbiter";
|
|
15
|
-
var ARBITER_URL = process.env.ARBITER_URL || "https://api.nullclear.dev";
|
|
16
|
-
var
|
|
17
|
-
var
|
|
15
|
+
var ARBITER_URL = (process.env.ARBITER_URL || "https://api.nullclear.dev").replace(/\/$/, "");
|
|
16
|
+
var CONFIGURED_PRIVATE_KEY = process.env.NULLCLEAR_PRIVATE_KEY || process.env.BUYER_PRIVATE_KEY;
|
|
17
|
+
var MCP_SESSION_ID = `mcp_stdio_${process.pid}_${Math.random().toString(36).slice(2, 8)}`;
|
|
18
18
|
var client = new NullClearClient({ arbiterUrl: ARBITER_URL });
|
|
19
19
|
var server = new Server(
|
|
20
20
|
{
|
|
21
21
|
name: "nullclear-mcp-server",
|
|
22
|
-
version: "1.
|
|
22
|
+
version: "1.2.0"
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
capabilities: {
|
|
@@ -29,14 +29,82 @@ var server = new Server(
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
);
|
|
32
|
+
async function callBazaarOrProxyWithFreeTrialFirst(endpointUrl, method = "GET", body) {
|
|
33
|
+
const headers = {
|
|
34
|
+
Accept: "application/json",
|
|
35
|
+
"x-nullclear-free-trial": "true",
|
|
36
|
+
"x-mcp-session": MCP_SESSION_ID
|
|
37
|
+
};
|
|
38
|
+
if (body !== void 0 && method !== "GET") {
|
|
39
|
+
headers["Content-Type"] = "application/json";
|
|
40
|
+
}
|
|
41
|
+
const trialRes = await fetch(endpointUrl, {
|
|
42
|
+
method,
|
|
43
|
+
headers,
|
|
44
|
+
body: body !== void 0 && method !== "GET" ? typeof body === "string" ? body : JSON.stringify(body) : void 0
|
|
45
|
+
});
|
|
46
|
+
if (trialRes.status === 200) {
|
|
47
|
+
const text = await trialRes.text();
|
|
48
|
+
let parsed = text;
|
|
49
|
+
try {
|
|
50
|
+
parsed = JSON.parse(text);
|
|
51
|
+
} catch {
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
status: 200,
|
|
55
|
+
data: parsed,
|
|
56
|
+
tier: trialRes.headers.get("x-nullclear-tier") || "free-trial",
|
|
57
|
+
remainingFreeCalls: trialRes.headers.get("x-nullclear-remaining-free-calls"),
|
|
58
|
+
receipt: trialRes.headers.get("X-NullClear-Attestation") || trialRes.headers.get("X-NullClear-Receipt-Id")
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (trialRes.status === 402 && CONFIGURED_PRIVATE_KEY) {
|
|
62
|
+
const nullclearFetch = createNullClearFetch({
|
|
63
|
+
accountOrPrivateKey: CONFIGURED_PRIVATE_KEY,
|
|
64
|
+
chainId: 8453,
|
|
65
|
+
arbiterUrl: ARBITER_URL
|
|
66
|
+
});
|
|
67
|
+
const paidRes = await nullclearFetch(endpointUrl, {
|
|
68
|
+
method,
|
|
69
|
+
headers: body !== void 0 && method !== "GET" ? { "Content-Type": "application/json" } : void 0,
|
|
70
|
+
body: body !== void 0 && method !== "GET" ? typeof body === "string" ? body : JSON.stringify(body) : void 0
|
|
71
|
+
});
|
|
72
|
+
const text = await paidRes.text();
|
|
73
|
+
let parsed = text;
|
|
74
|
+
try {
|
|
75
|
+
parsed = JSON.parse(text);
|
|
76
|
+
} catch {
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
status: paidRes.status,
|
|
80
|
+
data: parsed,
|
|
81
|
+
tier: "x402-paid",
|
|
82
|
+
remainingFreeCalls: "0",
|
|
83
|
+
receipt: paidRes.headers.get("X-NullClear-Attestation")
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
const errText = await trialRes.text();
|
|
87
|
+
let errParsed = errText;
|
|
88
|
+
try {
|
|
89
|
+
errParsed = JSON.parse(errText);
|
|
90
|
+
} catch {
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
status: trialRes.status,
|
|
94
|
+
data: errParsed,
|
|
95
|
+
tier: "exhausted",
|
|
96
|
+
remainingFreeCalls: "0",
|
|
97
|
+
receipt: null
|
|
98
|
+
};
|
|
99
|
+
}
|
|
32
100
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
33
101
|
return {
|
|
34
102
|
tools: [
|
|
35
103
|
{
|
|
36
|
-
name: "
|
|
37
|
-
description: "
|
|
104
|
+
name: "nullclear_token_security_audit",
|
|
105
|
+
description: "Audit any Base ERC-20 token address for honeypots, buy/sell taxes, verified bytecode, and x402 compatibility via NullClear Bazaar. Works out-of-the-box with zero configuration (10 free daily calls included).",
|
|
38
106
|
annotations: {
|
|
39
|
-
title: "
|
|
107
|
+
title: "Base Token Honeypot & Security Audit",
|
|
40
108
|
readOnlyHint: true,
|
|
41
109
|
destructiveHint: false,
|
|
42
110
|
idempotentHint: true,
|
|
@@ -44,23 +112,97 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
44
112
|
},
|
|
45
113
|
inputSchema: {
|
|
46
114
|
type: "object",
|
|
47
|
-
required: ["
|
|
115
|
+
required: ["tokenAddress"],
|
|
48
116
|
properties: {
|
|
49
|
-
|
|
117
|
+
tokenAddress: {
|
|
50
118
|
type: "string",
|
|
51
|
-
description: "
|
|
119
|
+
description: "Base ERC-20 token contract address (e.g. '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' for USDC)",
|
|
120
|
+
default: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: "nullclear_wallet_risk_score",
|
|
127
|
+
description: "Check any wallet or agent address on Base for OFAC sanctions, Sybil wash-trading concentration flags, and ERC-8004 counterparty reputation. Works out-of-the-box with zero configuration (10 free daily calls included).",
|
|
128
|
+
annotations: {
|
|
129
|
+
title: "Wallet & Agent Sanctions / Sybil Risk Score",
|
|
130
|
+
readOnlyHint: true,
|
|
131
|
+
destructiveHint: false,
|
|
132
|
+
idempotentHint: true,
|
|
133
|
+
openWorldHint: true
|
|
134
|
+
},
|
|
135
|
+
inputSchema: {
|
|
136
|
+
type: "object",
|
|
137
|
+
required: ["walletAddress"],
|
|
138
|
+
properties: {
|
|
139
|
+
walletAddress: {
|
|
140
|
+
type: "string",
|
|
141
|
+
description: "0x wallet address or ERC-8004 agent ID to inspect"
|
|
52
142
|
},
|
|
53
143
|
chainId: {
|
|
54
144
|
type: "number",
|
|
55
|
-
description: "Base chain ID (8453
|
|
145
|
+
description: "Base chain ID (default 8453)",
|
|
56
146
|
default: 8453
|
|
57
147
|
}
|
|
58
148
|
}
|
|
59
149
|
}
|
|
60
150
|
},
|
|
151
|
+
{
|
|
152
|
+
name: "nullclear_crypto_market_oracle",
|
|
153
|
+
description: "Fetch multi-exchange spread-verified crypto spot prices (ETH/USDC, BTC/USDC, SOL/USDC, BASE/USDC) with cryptographic NullClear invariant receipts. Works out-of-the-box with zero configuration (10 free daily calls included).",
|
|
154
|
+
annotations: {
|
|
155
|
+
title: "Spread-Verified Crypto Market Price Oracle",
|
|
156
|
+
readOnlyHint: true,
|
|
157
|
+
destructiveHint: false,
|
|
158
|
+
idempotentHint: true,
|
|
159
|
+
openWorldHint: true
|
|
160
|
+
},
|
|
161
|
+
inputSchema: {
|
|
162
|
+
type: "object",
|
|
163
|
+
required: ["pair"],
|
|
164
|
+
properties: {
|
|
165
|
+
pair: {
|
|
166
|
+
type: "string",
|
|
167
|
+
description: "Trading pair symbol (e.g. 'ETH/USDC', 'BTC/USDC', 'SOL/USDC')",
|
|
168
|
+
default: "ETH/USDC"
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: "nullclear_safe_fetch",
|
|
175
|
+
description: "Zero-config drop-in URL fetcher with automatic Canonical LLM Refusal Guard, HTTP 5xx crash guard, empty-JSON guard, and cryptographic execution receipt. Protects agents from hallucinated or broken API responses.",
|
|
176
|
+
annotations: {
|
|
177
|
+
title: "Auto-Guarded URL Fetcher (Zero Schema Required)",
|
|
178
|
+
readOnlyHint: true,
|
|
179
|
+
destructiveHint: false,
|
|
180
|
+
idempotentHint: true,
|
|
181
|
+
openWorldHint: true
|
|
182
|
+
},
|
|
183
|
+
inputSchema: {
|
|
184
|
+
type: "object",
|
|
185
|
+
required: ["url"],
|
|
186
|
+
properties: {
|
|
187
|
+
url: {
|
|
188
|
+
type: "string",
|
|
189
|
+
description: "Target HTTPS URL to fetch and verify through NullClear Auto-Guard"
|
|
190
|
+
},
|
|
191
|
+
method: {
|
|
192
|
+
type: "string",
|
|
193
|
+
enum: ["GET", "POST"],
|
|
194
|
+
default: "GET"
|
|
195
|
+
},
|
|
196
|
+
body: {
|
|
197
|
+
type: "string",
|
|
198
|
+
description: "Optional JSON string body for POST requests"
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
},
|
|
61
203
|
{
|
|
62
204
|
name: "nullclear_escrow_x402_call",
|
|
63
|
-
description: "Execute an x402-gated HTTP API call through NullClear deferred schema escrow. The arbiter guarantees payment is only settled if upstream returns valid data matching your schema contract.
|
|
205
|
+
description: "Execute an x402-gated HTTP API call through NullClear deferred schema escrow. The arbiter guarantees payment is only settled if upstream returns valid data matching your schema contract. Works out-of-the-box with 10 free daily trial calls before requiring NULLCLEAR_PRIVATE_KEY.",
|
|
64
206
|
annotations: {
|
|
65
207
|
title: "Execute Escrowed x402 Call with Invariants",
|
|
66
208
|
readOnlyHint: false,
|
|
@@ -150,7 +292,33 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
150
292
|
},
|
|
151
293
|
buyerPrivateKey: {
|
|
152
294
|
type: "string",
|
|
153
|
-
description: "Optional buyer private key (defaults to
|
|
295
|
+
description: "Optional buyer private key (defaults to Sponsored Free Tier, then NULLCLEAR_PRIVATE_KEY)"
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
name: "nullclear_check_reputation",
|
|
302
|
+
description: "Inspect an AI agent's trust score (0-100), clearing reliability, Sybil wash-trade penalty flags, and corporate KYA backing on Base",
|
|
303
|
+
annotations: {
|
|
304
|
+
title: "Inspect AI Agent Trust & Sybil Score",
|
|
305
|
+
readOnlyHint: true,
|
|
306
|
+
destructiveHint: false,
|
|
307
|
+
idempotentHint: true,
|
|
308
|
+
openWorldHint: true
|
|
309
|
+
},
|
|
310
|
+
inputSchema: {
|
|
311
|
+
type: "object",
|
|
312
|
+
required: ["agentId"],
|
|
313
|
+
properties: {
|
|
314
|
+
agentId: {
|
|
315
|
+
type: "string",
|
|
316
|
+
description: "The ERC-8004 agent token ID or registered identifier (e.g. '124595')"
|
|
317
|
+
},
|
|
318
|
+
chainId: {
|
|
319
|
+
type: "number",
|
|
320
|
+
description: "Base chain ID (8453 for Mainnet, 84532 for Sepolia)",
|
|
321
|
+
default: 8453
|
|
154
322
|
}
|
|
155
323
|
}
|
|
156
324
|
}
|
|
@@ -339,17 +507,15 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
|
339
507
|
|
|
340
508
|
NullClear is the autonomous deferred clearing and reputation protocol for AI agent commerce on Base Mainnet.
|
|
341
509
|
|
|
342
|
-
###
|
|
343
|
-
|
|
510
|
+
### Zero-Config Free Trial
|
|
511
|
+
All MCP tools work out-of-the-box with **10 free daily calls**\u2014no wallet or private key needed to get started.
|
|
344
512
|
|
|
345
|
-
###
|
|
513
|
+
### First-Class Capability Tools
|
|
514
|
+
- \`nullclear_token_security_audit\`: Audit any Base token for honeypots, taxes, and bytecode verification.
|
|
515
|
+
- \`nullclear_wallet_risk_score\`: Check any wallet or agent for sanctions, Sybil wash-trading, and ERC-8004 reputation.
|
|
516
|
+
- \`nullclear_crypto_market_oracle\`: Multi-exchange spread-verified price feed (ETH, BTC, SOL).
|
|
517
|
+
- \`nullclear_safe_fetch\`: Drop-in URL fetcher with Canonical LLM Refusal Guard & crash protection.
|
|
346
518
|
- \`nullclear_escrow_x402_call\`: Execute deferred schema escrow with numeric bounds and LLM refusal guards.
|
|
347
|
-
- \`nullclear_check_reputation\`: Check an ERC-8004 agent's Sybil trust score (0-100) and wash-trading flags.
|
|
348
|
-
- \`nullclear_verify_kya_mandate\`: Verify an agent's corporate entity backing and spending limits.
|
|
349
|
-
|
|
350
|
-
### Test Sandbox
|
|
351
|
-
- Market Feed (Success): \`https://api.nullclear.dev/v1/sandbox/market-feed\` ($0.01 USDC)
|
|
352
|
-
- Hallucinating Seller (Fails & Voids): \`https://api.nullclear.dev/v1/sandbox/hallucinating-seller\`
|
|
353
519
|
`
|
|
354
520
|
}
|
|
355
521
|
]
|
|
@@ -360,6 +526,157 @@ Never sign away funds unconditionally. Using EIP-3009 signed authorizations and
|
|
|
360
526
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
361
527
|
const { name, arguments: args } = request.params;
|
|
362
528
|
try {
|
|
529
|
+
if (name === "nullclear_token_security_audit") {
|
|
530
|
+
const tokenAddress = String(args?.tokenAddress || "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913");
|
|
531
|
+
const url = `${ARBITER_URL}/v1/bazaar/token-security?tokenAddress=${encodeURIComponent(tokenAddress)}`;
|
|
532
|
+
const res = await callBazaarOrProxyWithFreeTrialFirst(url, "GET");
|
|
533
|
+
if (res.status === 402) {
|
|
534
|
+
return {
|
|
535
|
+
content: [
|
|
536
|
+
{
|
|
537
|
+
type: "text",
|
|
538
|
+
text: JSON.stringify(
|
|
539
|
+
{
|
|
540
|
+
status: "free_trial_exhausted",
|
|
541
|
+
message: "You have used all 10 daily free trial calls. Set NULLCLEAR_PRIVATE_KEY in your MCP environment variables to continue with automatic $0.005 USDC x402 settlement.",
|
|
542
|
+
tokenAddress,
|
|
543
|
+
details: res.data
|
|
544
|
+
},
|
|
545
|
+
null,
|
|
546
|
+
2
|
|
547
|
+
)
|
|
548
|
+
}
|
|
549
|
+
]
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
return {
|
|
553
|
+
content: [
|
|
554
|
+
{
|
|
555
|
+
type: "text",
|
|
556
|
+
text: JSON.stringify(
|
|
557
|
+
{
|
|
558
|
+
status: "success",
|
|
559
|
+
tier: res.tier,
|
|
560
|
+
remainingFreeCalls: res.remainingFreeCalls,
|
|
561
|
+
receipt: res.receipt,
|
|
562
|
+
result: res.data
|
|
563
|
+
},
|
|
564
|
+
null,
|
|
565
|
+
2
|
|
566
|
+
)
|
|
567
|
+
}
|
|
568
|
+
]
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
if (name === "nullclear_wallet_risk_score") {
|
|
572
|
+
const walletAddress = String(args?.walletAddress || "0xB586eFcCa41dF27ad3873BEcA54da0c30bf04d5c");
|
|
573
|
+
const chainId = Number(args?.chainId || 8453);
|
|
574
|
+
const url = `${ARBITER_URL}/v1/bazaar/wallet-risk?walletAddress=${encodeURIComponent(walletAddress)}&chainId=${chainId}`;
|
|
575
|
+
const res = await callBazaarOrProxyWithFreeTrialFirst(url, "GET");
|
|
576
|
+
if (res.status === 402) {
|
|
577
|
+
return {
|
|
578
|
+
content: [
|
|
579
|
+
{
|
|
580
|
+
type: "text",
|
|
581
|
+
text: JSON.stringify(
|
|
582
|
+
{
|
|
583
|
+
status: "free_trial_exhausted",
|
|
584
|
+
message: "You have used all 10 daily free trial calls. Set NULLCLEAR_PRIVATE_KEY in your MCP environment variables to continue with automatic $0.005 USDC x402 settlement.",
|
|
585
|
+
walletAddress,
|
|
586
|
+
details: res.data
|
|
587
|
+
},
|
|
588
|
+
null,
|
|
589
|
+
2
|
|
590
|
+
)
|
|
591
|
+
}
|
|
592
|
+
]
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
return {
|
|
596
|
+
content: [
|
|
597
|
+
{
|
|
598
|
+
type: "text",
|
|
599
|
+
text: JSON.stringify(
|
|
600
|
+
{
|
|
601
|
+
status: "success",
|
|
602
|
+
tier: res.tier,
|
|
603
|
+
remainingFreeCalls: res.remainingFreeCalls,
|
|
604
|
+
receipt: res.receipt,
|
|
605
|
+
result: res.data
|
|
606
|
+
},
|
|
607
|
+
null,
|
|
608
|
+
2
|
|
609
|
+
)
|
|
610
|
+
}
|
|
611
|
+
]
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
if (name === "nullclear_crypto_market_oracle") {
|
|
615
|
+
const pair = String(args?.pair || "ETH/USDC");
|
|
616
|
+
const url = `${ARBITER_URL}/v1/bazaar/market-oracle?pair=${encodeURIComponent(pair)}`;
|
|
617
|
+
const res = await callBazaarOrProxyWithFreeTrialFirst(url, "GET");
|
|
618
|
+
if (res.status === 402) {
|
|
619
|
+
return {
|
|
620
|
+
content: [
|
|
621
|
+
{
|
|
622
|
+
type: "text",
|
|
623
|
+
text: JSON.stringify(
|
|
624
|
+
{
|
|
625
|
+
status: "free_trial_exhausted",
|
|
626
|
+
message: "You have used all 10 daily free trial calls. Set NULLCLEAR_PRIVATE_KEY in your MCP environment variables to continue with automatic $0.01 USDC x402 settlement.",
|
|
627
|
+
pair,
|
|
628
|
+
details: res.data
|
|
629
|
+
},
|
|
630
|
+
null,
|
|
631
|
+
2
|
|
632
|
+
)
|
|
633
|
+
}
|
|
634
|
+
]
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
return {
|
|
638
|
+
content: [
|
|
639
|
+
{
|
|
640
|
+
type: "text",
|
|
641
|
+
text: JSON.stringify(
|
|
642
|
+
{
|
|
643
|
+
status: "success",
|
|
644
|
+
tier: res.tier,
|
|
645
|
+
remainingFreeCalls: res.remainingFreeCalls,
|
|
646
|
+
receipt: res.receipt,
|
|
647
|
+
result: res.data
|
|
648
|
+
},
|
|
649
|
+
null,
|
|
650
|
+
2
|
|
651
|
+
)
|
|
652
|
+
}
|
|
653
|
+
]
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
if (name === "nullclear_safe_fetch") {
|
|
657
|
+
const targetUrl = String(args?.url || "");
|
|
658
|
+
const method = args?.method || "GET";
|
|
659
|
+
const proxyUrl = `${ARBITER_URL}/${targetUrl}`;
|
|
660
|
+
const res = await callBazaarOrProxyWithFreeTrialFirst(proxyUrl, method, args?.body);
|
|
661
|
+
return {
|
|
662
|
+
content: [
|
|
663
|
+
{
|
|
664
|
+
type: "text",
|
|
665
|
+
text: JSON.stringify(
|
|
666
|
+
{
|
|
667
|
+
status: res.status === 200 ? "verified_success" : "auto_guard_blocked",
|
|
668
|
+
httpStatus: res.status,
|
|
669
|
+
verified: res.status === 200,
|
|
670
|
+
receipt: res.receipt,
|
|
671
|
+
data: res.data
|
|
672
|
+
},
|
|
673
|
+
null,
|
|
674
|
+
2
|
|
675
|
+
)
|
|
676
|
+
}
|
|
677
|
+
]
|
|
678
|
+
};
|
|
679
|
+
}
|
|
363
680
|
if (name === "nullclear_check_reputation") {
|
|
364
681
|
const agentId = String(args?.agentId);
|
|
365
682
|
const chainId = Number(args?.chainId || 8453);
|
|
@@ -398,7 +715,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
398
715
|
const rejectRefusals = args?.rejectRefusals !== void 0 ? Boolean(args.rejectRefusals) : true;
|
|
399
716
|
const fallbacks = args?.fallbacks || void 0;
|
|
400
717
|
const spendPolicy = args?.spendPolicy || void 0;
|
|
401
|
-
const buyerPrivateKey = args?.buyerPrivateKey ||
|
|
718
|
+
const buyerPrivateKey = args?.buyerPrivateKey || CONFIGURED_PRIVATE_KEY;
|
|
402
719
|
const nullclearFetch = createNullClearFetch({
|
|
403
720
|
accountOrPrivateKey: buyerPrivateKey,
|
|
404
721
|
chainId: 8453,
|
|
@@ -433,7 +750,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
433
750
|
text: JSON.stringify(
|
|
434
751
|
{
|
|
435
752
|
status: "cleared_and_settled",
|
|
436
|
-
|
|
753
|
+
tier: buyerPrivateKey ? "x402-paid" : "free-trial",
|
|
754
|
+
message: "Response successfully validated against schema. Payment cleared.",
|
|
437
755
|
attestation: response.headers.get("X-NullClear-Attestation") || "verified",
|
|
438
756
|
data: parsedData
|
|
439
757
|
},
|
|
@@ -445,6 +763,24 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
445
763
|
};
|
|
446
764
|
} catch (err) {
|
|
447
765
|
if (err instanceof NullClearEscrowAbortedError) {
|
|
766
|
+
if (err.stage === "FREE_TRIAL_EXHAUSTED") {
|
|
767
|
+
return {
|
|
768
|
+
content: [
|
|
769
|
+
{
|
|
770
|
+
type: "text",
|
|
771
|
+
text: JSON.stringify(
|
|
772
|
+
{
|
|
773
|
+
status: "free_trial_exhausted",
|
|
774
|
+
message: "Daily 10 free trial calls exhausted. Set NULLCLEAR_PRIVATE_KEY in your MCP configuration to continue with automatic zero-loss x402 settlement.",
|
|
775
|
+
stage: err.stage
|
|
776
|
+
},
|
|
777
|
+
null,
|
|
778
|
+
2
|
|
779
|
+
)
|
|
780
|
+
}
|
|
781
|
+
]
|
|
782
|
+
};
|
|
783
|
+
}
|
|
448
784
|
return {
|
|
449
785
|
content: [
|
|
450
786
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nullclear/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"mcpName": "io.github.Nullclear-Protocol/nullclear",
|
|
5
5
|
"description": "Model Context Protocol server for autonomous agents using NullClear",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@modelcontextprotocol/sdk": "^1.6.1",
|
|
42
|
-
"@nullclear/shared": "1.
|
|
43
|
-
"@nullclear/x402-arbiter": "1.
|
|
42
|
+
"@nullclear/shared": "1.2.0",
|
|
43
|
+
"@nullclear/x402-arbiter": "1.2.0",
|
|
44
44
|
"viem": "^2.23.0",
|
|
45
45
|
"zod": "^3.24.2"
|
|
46
46
|
},
|