@nullclear/mcp 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nullclear-Protocol
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,28 @@
1
- # Temporary Holding Version
1
+ # @nullclear/mcp
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Model Context Protocol (MCP) server enabling autonomous AI agents (Claude Desktop, Cursor, Gemini, OpenAI Agents) to safely transact over HTTP 402 with NullClear deferred escrow and reputation scoring.
4
+
5
+ ## Exposed Tools
6
+
7
+ 1. `nullclear_check_reputation`: Query an agent's on-chain trust score, clearing reliability, and Sybil wash-trade penalty flags.
8
+ 2. `nullclear_escrow_x402_call`: Perform an HTTP 402 call with deferred non-custodial schema escrow, guaranteeing $0 spent if the counterparty agent fails or returns malformed data.
9
+ 3. `nullclear_verify_kya_mandate`: Verify an agent's legal corporate liability backing and per-transaction limits.
10
+
11
+ ## Configuration
12
+
13
+ Add to your MCP client config (e.g., `claude_desktop_config.json`):
14
+
15
+ ```json
16
+ {
17
+ "mcpServers": {
18
+ "nullclear": {
19
+ "command": "node",
20
+ "args": ["/path/to/nullclear/packages/mcp-server/dist/index.js"],
21
+ "env": {
22
+ "ARBITER_URL": "https://arbiter.nullclear.dev",
23
+ "BUYER_PRIVATE_KEY": "0x..."
24
+ }
25
+ }
26
+ }
27
+ }
28
+ ```
@@ -0,0 +1,38 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+
3
+ declare const server: Server<{
4
+ method: string;
5
+ params?: {
6
+ [x: string]: unknown;
7
+ _meta?: {
8
+ [x: string]: unknown;
9
+ progressToken?: string | number | undefined;
10
+ "io.modelcontextprotocol/related-task"?: {
11
+ taskId: string;
12
+ } | undefined;
13
+ } | undefined;
14
+ } | undefined;
15
+ }, {
16
+ method: string;
17
+ params?: {
18
+ [x: string]: unknown;
19
+ _meta?: {
20
+ [x: string]: unknown;
21
+ progressToken?: string | number | undefined;
22
+ "io.modelcontextprotocol/related-task"?: {
23
+ taskId: string;
24
+ } | undefined;
25
+ } | undefined;
26
+ } | undefined;
27
+ }, {
28
+ [x: string]: unknown;
29
+ _meta?: {
30
+ [x: string]: unknown;
31
+ progressToken?: string | number | undefined;
32
+ "io.modelcontextprotocol/related-task"?: {
33
+ taskId: string;
34
+ } | undefined;
35
+ } | undefined;
36
+ }>;
37
+
38
+ export { server };
package/dist/index.js ADDED
@@ -0,0 +1,291 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import {
7
+ CallToolRequestSchema,
8
+ ListToolsRequestSchema
9
+ } from "@modelcontextprotocol/sdk/types.js";
10
+ import { NullClearClient, createNullClearFetch, NullClearEscrowAbortedError } from "@nullclear/x402-arbiter";
11
+ var ARBITER_URL = process.env.ARBITER_URL || "https://arbiter.nullclear.dev";
12
+ var DEFAULT_BUYER_KEY = process.env.BUYER_PRIVATE_KEY || "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d";
13
+ var client = new NullClearClient({ arbiterUrl: ARBITER_URL });
14
+ var server = new Server(
15
+ {
16
+ name: "nullclear-mcp-server",
17
+ version: "1.0.0"
18
+ },
19
+ {
20
+ capabilities: {
21
+ tools: {}
22
+ }
23
+ }
24
+ );
25
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
26
+ return {
27
+ tools: [
28
+ {
29
+ name: "nullclear_check_reputation",
30
+ description: "Inspect an AI agent's trust score (0-100), clearing reliability, Sybil wash-trade penalty flags, and corporate KYA backing on Base",
31
+ inputSchema: {
32
+ type: "object",
33
+ required: ["agentId"],
34
+ properties: {
35
+ agentId: {
36
+ type: "string",
37
+ description: "The ERC-8004 agent token ID or registered identifier"
38
+ },
39
+ chainId: {
40
+ type: "number",
41
+ description: "Base chain ID (8453 for Mainnet, 84532 for Sepolia)",
42
+ default: 8453
43
+ }
44
+ }
45
+ }
46
+ },
47
+ {
48
+ name: "nullclear_escrow_x402_call",
49
+ 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. If the seller hallucinates or crashes, $0 is spent.",
50
+ inputSchema: {
51
+ type: "object",
52
+ required: ["targetUrl"],
53
+ properties: {
54
+ targetUrl: {
55
+ type: "string",
56
+ description: "Upstream API endpoint demanding x402 payment"
57
+ },
58
+ method: {
59
+ type: "string",
60
+ enum: ["GET", "POST", "PUT", "DELETE"],
61
+ default: "GET"
62
+ },
63
+ sellerAgentId: {
64
+ type: "string",
65
+ description: "Seller's agent ID for reputation check and Sybil wash-trade gating"
66
+ },
67
+ minTrustScore: {
68
+ type: "number",
69
+ description: "Minimum acceptable trust score for seller (0-100, default 50)",
70
+ default: 50
71
+ },
72
+ requiredFields: {
73
+ type: "array",
74
+ items: { type: "string" },
75
+ description: "Dot-notation field paths that must exist in response (e.g. ['data.price'])"
76
+ },
77
+ jsonSchema: {
78
+ type: "object",
79
+ description: "JSON Schema draft-07 contract that response must strictly satisfy"
80
+ },
81
+ buyerPrivateKey: {
82
+ type: "string",
83
+ description: "Optional buyer private key (defaults to BUYER_PRIVATE_KEY environment variable)"
84
+ }
85
+ }
86
+ }
87
+ },
88
+ {
89
+ name: "nullclear_verify_kya_mandate",
90
+ description: "Verify an agent's Know Your Agent (KYA) corporate liability mandate, active validity, and whether a proposed transaction amount is within corporate limits",
91
+ inputSchema: {
92
+ type: "object",
93
+ required: ["agentId", "transactionAmountUsd"],
94
+ properties: {
95
+ agentId: {
96
+ type: "string",
97
+ description: "The ERC-8004 agent token ID"
98
+ },
99
+ chainId: {
100
+ type: "number",
101
+ description: "Base chain ID (default 8453)",
102
+ default: 8453
103
+ },
104
+ transactionAmountUsd: {
105
+ type: "number",
106
+ description: "Amount in USD intended to spend in the transaction"
107
+ }
108
+ }
109
+ }
110
+ }
111
+ ]
112
+ };
113
+ });
114
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
115
+ const { name, arguments: args } = request.params;
116
+ try {
117
+ if (name === "nullclear_check_reputation") {
118
+ const agentId = String(args?.agentId);
119
+ const chainId = Number(args?.chainId || 8453);
120
+ const score = await client.getScore(agentId, chainId);
121
+ return {
122
+ content: [
123
+ {
124
+ type: "text",
125
+ text: JSON.stringify(
126
+ {
127
+ status: "success",
128
+ agentId,
129
+ chainId,
130
+ trustScore: score.score,
131
+ grade: score.grade,
132
+ sybilFlags: score.sybilFlags,
133
+ metrics: score.metrics,
134
+ hasActiveKya: score.metrics.hasActiveKya,
135
+ arbiterAddress: score.arbiterAddress
136
+ },
137
+ null,
138
+ 2
139
+ )
140
+ }
141
+ ]
142
+ };
143
+ }
144
+ if (name === "nullclear_escrow_x402_call") {
145
+ const targetUrl = String(args?.targetUrl);
146
+ const method = args?.method || "GET";
147
+ const sellerAgentId = args?.sellerAgentId ? String(args.sellerAgentId) : void 0;
148
+ const minTrustScore = args?.minTrustScore ? Number(args.minTrustScore) : 50;
149
+ const requiredFields = args?.requiredFields || void 0;
150
+ const jsonSchema = args?.jsonSchema || void 0;
151
+ const buyerPrivateKey = args?.buyerPrivateKey || DEFAULT_BUYER_KEY;
152
+ const nullclearFetch = createNullClearFetch({
153
+ accountOrPrivateKey: buyerPrivateKey,
154
+ chainId: 8453,
155
+ arbiterUrl: ARBITER_URL,
156
+ defaultMinTrustScore: minTrustScore
157
+ });
158
+ try {
159
+ const response = await nullclearFetch(targetUrl, {
160
+ method,
161
+ nullclear: {
162
+ sellerAgentId,
163
+ minTrustScore,
164
+ schema: jsonSchema,
165
+ requiredFields
166
+ }
167
+ });
168
+ const responseText = await response.text();
169
+ let parsedData;
170
+ try {
171
+ parsedData = JSON.parse(responseText);
172
+ } catch {
173
+ parsedData = responseText;
174
+ }
175
+ return {
176
+ content: [
177
+ {
178
+ type: "text",
179
+ text: JSON.stringify(
180
+ {
181
+ status: "cleared_and_settled",
182
+ message: "Response successfully validated against schema. Payment settled.",
183
+ attestation: response.headers.get("X-NullClear-Attestation") || "verified",
184
+ data: parsedData
185
+ },
186
+ null,
187
+ 2
188
+ )
189
+ }
190
+ ]
191
+ };
192
+ } catch (err) {
193
+ if (err instanceof NullClearEscrowAbortedError) {
194
+ return {
195
+ content: [
196
+ {
197
+ type: "text",
198
+ text: JSON.stringify(
199
+ {
200
+ status: "escrow_voided_zero_loss",
201
+ message: "NullClear escrow voided payment authorization. Exactly $0 spent.",
202
+ stage: err.stage,
203
+ buyerCharged: err.buyerCharged,
204
+ validationErrors: err.validationErrors,
205
+ reason: err.reason
206
+ },
207
+ null,
208
+ 2
209
+ )
210
+ }
211
+ ]
212
+ };
213
+ }
214
+ throw err;
215
+ }
216
+ }
217
+ if (name === "nullclear_verify_kya_mandate") {
218
+ const agentId = String(args?.agentId);
219
+ const chainId = Number(args?.chainId || 8453);
220
+ const transactionAmountUsd = Number(args?.transactionAmountUsd || 0);
221
+ const mandate = await client.getKya(agentId, chainId);
222
+ if (!mandate) {
223
+ return {
224
+ content: [
225
+ {
226
+ type: "text",
227
+ text: JSON.stringify({
228
+ status: "unverified",
229
+ agentId,
230
+ chainId,
231
+ verified: false,
232
+ reason: "No corporate liability KYA mandate found for this agent."
233
+ })
234
+ }
235
+ ]
236
+ };
237
+ }
238
+ const now = Math.floor(Date.now() / 1e3);
239
+ const isExpired = mandate.validUntil <= now;
240
+ const exceedsLimit = transactionAmountUsd > mandate.maxTxLimitUsd;
241
+ return {
242
+ content: [
243
+ {
244
+ type: "text",
245
+ text: JSON.stringify(
246
+ {
247
+ status: isExpired || exceedsLimit ? "rejected" : "approved",
248
+ agentId,
249
+ chainId,
250
+ corporateEntity: mandate.corporateEntity,
251
+ entityRegistrationNumber: mandate.entityRegistrationNumber,
252
+ jurisdiction: mandate.jurisdiction,
253
+ maxTxLimitUsd: mandate.maxTxLimitUsd,
254
+ liabilityCoverageUsd: mandate.liabilityCoverageUsd,
255
+ isExpired,
256
+ exceedsLimit,
257
+ approvedForAmount: !isExpired && !exceedsLimit
258
+ },
259
+ null,
260
+ 2
261
+ )
262
+ }
263
+ ]
264
+ };
265
+ }
266
+ throw new Error(`Unknown tool name: ${name}`);
267
+ } catch (err) {
268
+ return {
269
+ isError: true,
270
+ content: [
271
+ {
272
+ type: "text",
273
+ text: `Error executing tool ${name}: ${err.message || String(err)}`
274
+ }
275
+ ]
276
+ };
277
+ }
278
+ });
279
+ async function main() {
280
+ const transport = new StdioServerTransport();
281
+ await server.connect(transport);
282
+ }
283
+ if (process.env.NODE_ENV !== "test") {
284
+ main().catch((err) => {
285
+ console.error("Fatal MCP Server Error:", err);
286
+ process.exit(1);
287
+ });
288
+ }
289
+ export {
290
+ server
291
+ };
package/package.json CHANGED
@@ -1,6 +1,41 @@
1
1
  {
2
2
  "name": "@nullclear/mcp",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "1.0.0",
4
+ "description": "Model Context Protocol server for autonomous agents using NullClear",
5
+ "license": "MIT",
6
+ "homepage": "https://nullclear.dev",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/Nullclear-Protocol/nullclear.git"
10
+ },
11
+ "type": "module",
12
+ "main": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "bin": {
15
+ "nullclear-mcp": "./dist/index.js"
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md"
20
+ ],
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "dependencies": {
25
+ "@modelcontextprotocol/sdk": "^1.6.1",
26
+ "@nullclear/shared": "1.0.0",
27
+ "@nullclear/x402-arbiter": "1.0.0",
28
+ "viem": "^2.23.0",
29
+ "zod": "^3.24.2"
30
+ },
31
+ "devDependencies": {
32
+ "tsup": "^8.3.6",
33
+ "typescript": "^5.7.3"
34
+ },
35
+ "scripts": {
36
+ "build": "tsup",
37
+ "typecheck": "tsc --noEmit",
38
+ "start": "node dist/index.js",
39
+ "lint": "echo \"@nullclear/mcp clean\""
40
+ }
6
41
  }