auditor-mcp 0.1.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 +413 -0
- package/package.json +44 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { config as loadEnv } from "dotenv";
|
|
5
|
+
import { dirname, resolve } from "path";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
import { readFile } from "fs/promises";
|
|
8
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
9
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
10
|
+
import { wrapFetchWithPayment, x402Client, x402HTTPClient } from "@x402/fetch";
|
|
11
|
+
import { Mppx as MppxClient } from "mppx/client";
|
|
12
|
+
import { stellar as stellarMpp } from "@stellar/mpp/charge/client";
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
|
|
15
|
+
// src/stellar/constants.ts
|
|
16
|
+
var STELLAR_PUBNET_CAIP2 = "stellar:pubnet";
|
|
17
|
+
var STELLAR_TESTNET_CAIP2 = "stellar:testnet";
|
|
18
|
+
var DEFAULT_TESTNET_RPC_URL = "https://soroban-testnet.stellar.org";
|
|
19
|
+
var STELLAR_DESTINATION_ADDRESS_REGEX = /^(?:[GC][ABCD][A-Z2-7]{54}|M[ABCD][A-Z2-7]{67})$/;
|
|
20
|
+
var STELLAR_ASSET_ADDRESS_REGEX = /^(?:[C][ABCD][A-Z2-7]{54})$/;
|
|
21
|
+
var STELLAR_NETWORK_TO_PASSPHRASE = /* @__PURE__ */ new Map([
|
|
22
|
+
[STELLAR_PUBNET_CAIP2, "Public Global Stellar Network ; September 2015"],
|
|
23
|
+
[STELLAR_TESTNET_CAIP2, "Test SDF Network ; September 2015"]
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
// src/stellar/exact/client/scheme.ts
|
|
27
|
+
import { nativeToScVal, TransactionBuilder, contract } from "@stellar/stellar-sdk";
|
|
28
|
+
import { Api as Api2 } from "@stellar/stellar-sdk/rpc";
|
|
29
|
+
|
|
30
|
+
// src/stellar/shared.ts
|
|
31
|
+
import { Address, xdr } from "@stellar/stellar-sdk";
|
|
32
|
+
import { Api, assembleTransaction } from "@stellar/stellar-sdk/rpc";
|
|
33
|
+
function handleSimulationResult(simulation) {
|
|
34
|
+
if (!simulation) throw new Error("Simulation result is undefined");
|
|
35
|
+
if (Api.isSimulationRestore(simulation)) {
|
|
36
|
+
throw new Error(`Stellar simulation result has type "RESTORE"`);
|
|
37
|
+
}
|
|
38
|
+
if (Api.isSimulationError(simulation)) {
|
|
39
|
+
throw new Error(`Stellar simulation failed${simulation.error ? `: ${simulation.error}` : ""}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/stellar/utils.ts
|
|
44
|
+
import { rpc } from "@stellar/stellar-sdk";
|
|
45
|
+
var DEFAULT_ESTIMATED_LEDGER_SECONDS = 5;
|
|
46
|
+
var RPC_LEDGERS_SAMPLE_SIZE = 20;
|
|
47
|
+
function isStellarNetwork(network) {
|
|
48
|
+
return STELLAR_NETWORK_TO_PASSPHRASE.has(network);
|
|
49
|
+
}
|
|
50
|
+
function validateStellarDestinationAddress(address) {
|
|
51
|
+
return STELLAR_DESTINATION_ADDRESS_REGEX.test(address);
|
|
52
|
+
}
|
|
53
|
+
function validateStellarAssetAddress(address) {
|
|
54
|
+
return STELLAR_ASSET_ADDRESS_REGEX.test(address);
|
|
55
|
+
}
|
|
56
|
+
function getNetworkPassphrase(network) {
|
|
57
|
+
const passphrase = STELLAR_NETWORK_TO_PASSPHRASE.get(network);
|
|
58
|
+
if (!passphrase) throw new Error(`Unknown Stellar network: ${network}`);
|
|
59
|
+
return passphrase;
|
|
60
|
+
}
|
|
61
|
+
function getRpcUrl(network, rpcConfig) {
|
|
62
|
+
const custom = rpcConfig?.url;
|
|
63
|
+
switch (network) {
|
|
64
|
+
case STELLAR_TESTNET_CAIP2:
|
|
65
|
+
return custom || DEFAULT_TESTNET_RPC_URL;
|
|
66
|
+
case STELLAR_PUBNET_CAIP2:
|
|
67
|
+
if (!custom) throw new Error("Stellar mainnet requires a non-empty rpcUrl.");
|
|
68
|
+
return custom;
|
|
69
|
+
default:
|
|
70
|
+
throw new Error(`Unknown Stellar network: ${network}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function getRpcClient(network, rpcConfig) {
|
|
74
|
+
const rpcUrl = getRpcUrl(network, rpcConfig);
|
|
75
|
+
return new rpc.Server(rpcUrl, { allowHttp: network === STELLAR_TESTNET_CAIP2 });
|
|
76
|
+
}
|
|
77
|
+
async function getEstimatedLedgerCloseTimeSeconds(server2) {
|
|
78
|
+
try {
|
|
79
|
+
const latestLedger = await server2.getLatestLedger();
|
|
80
|
+
const { ledgers } = await server2.getLedgers({
|
|
81
|
+
startLedger: latestLedger.sequence,
|
|
82
|
+
pagination: { limit: RPC_LEDGERS_SAMPLE_SIZE }
|
|
83
|
+
});
|
|
84
|
+
if (!ledgers || ledgers.length < 2) return DEFAULT_ESTIMATED_LEDGER_SECONDS;
|
|
85
|
+
const oldestTs = parseInt(ledgers[0].ledgerCloseTime);
|
|
86
|
+
const newestTs = parseInt(ledgers[ledgers.length - 1].ledgerCloseTime);
|
|
87
|
+
return Math.ceil((newestTs - oldestTs) / (ledgers.length - 1));
|
|
88
|
+
} catch {
|
|
89
|
+
return DEFAULT_ESTIMATED_LEDGER_SECONDS;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/stellar/exact/client/scheme.ts
|
|
94
|
+
var DEFAULT_BASE_FEE_STROOPS = 1e4;
|
|
95
|
+
var ExactStellarScheme = class {
|
|
96
|
+
constructor(signer2, rpcConfig) {
|
|
97
|
+
this.signer = signer2;
|
|
98
|
+
this.rpcConfig = rpcConfig;
|
|
99
|
+
}
|
|
100
|
+
signer;
|
|
101
|
+
rpcConfig;
|
|
102
|
+
scheme = "exact";
|
|
103
|
+
async createPaymentPayload(x402Version, paymentRequirements) {
|
|
104
|
+
try {
|
|
105
|
+
this.validateInput(paymentRequirements);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
throw new Error(`Invalid input for Stellar payment: ${error}`);
|
|
108
|
+
}
|
|
109
|
+
const { network, payTo, asset, amount, extra, maxTimeoutSeconds } = paymentRequirements;
|
|
110
|
+
const networkPassphrase = getNetworkPassphrase(network);
|
|
111
|
+
const rpcUrl = getRpcUrl(network, this.rpcConfig);
|
|
112
|
+
if (!extra.areFeesSponsored) {
|
|
113
|
+
throw new Error("Exact scheme requires areFeesSponsored to be true");
|
|
114
|
+
}
|
|
115
|
+
const rpcServer = getRpcClient(network, this.rpcConfig);
|
|
116
|
+
const latestLedger = await rpcServer.getLatestLedger();
|
|
117
|
+
const estimatedLedgerSeconds = await getEstimatedLedgerCloseTimeSeconds(rpcServer);
|
|
118
|
+
const maxLedger = latestLedger.sequence + Math.ceil(maxTimeoutSeconds / estimatedLedgerSeconds);
|
|
119
|
+
const tx = await contract.AssembledTransaction.build({
|
|
120
|
+
contractId: asset,
|
|
121
|
+
method: "transfer",
|
|
122
|
+
args: [
|
|
123
|
+
nativeToScVal(this.signer.address, { type: "address" }),
|
|
124
|
+
nativeToScVal(payTo, { type: "address" }),
|
|
125
|
+
nativeToScVal(amount, { type: "i128" })
|
|
126
|
+
],
|
|
127
|
+
networkPassphrase,
|
|
128
|
+
rpcUrl,
|
|
129
|
+
parseResultXdr: (result) => result
|
|
130
|
+
});
|
|
131
|
+
handleSimulationResult(tx.simulation);
|
|
132
|
+
let missingSigners = tx.needsNonInvokerSigningBy();
|
|
133
|
+
if (!missingSigners.includes(this.signer.address) || missingSigners.length > 1) {
|
|
134
|
+
throw new Error(
|
|
135
|
+
`Expected to sign with [${this.signer.address}], got [${missingSigners.join(", ")}]`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
await tx.signAuthEntries({
|
|
139
|
+
address: this.signer.address,
|
|
140
|
+
signAuthEntry: this.signer.signAuthEntry,
|
|
141
|
+
expiration: maxLedger
|
|
142
|
+
});
|
|
143
|
+
await tx.simulate();
|
|
144
|
+
handleSimulationResult(tx.simulation);
|
|
145
|
+
missingSigners = tx.needsNonInvokerSigningBy();
|
|
146
|
+
if (missingSigners.length > 0) {
|
|
147
|
+
throw new Error(`Unexpected signer(s) still required: [${missingSigners.join(", ")}]`);
|
|
148
|
+
}
|
|
149
|
+
const finalTx = tx.simulation && Api2.isSimulationSuccess(tx.simulation) ? TransactionBuilder.cloneFrom(tx.built, {
|
|
150
|
+
fee: (DEFAULT_BASE_FEE_STROOPS + parseInt(tx.simulation.minResourceFee, 10)).toString(),
|
|
151
|
+
sorobanData: tx.simulationData.transactionData,
|
|
152
|
+
networkPassphrase
|
|
153
|
+
}).build() : tx.built;
|
|
154
|
+
return {
|
|
155
|
+
x402Version,
|
|
156
|
+
payload: { transaction: finalTx.toXDR() }
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
validateInput(req) {
|
|
160
|
+
const { scheme, network, payTo, asset, amount } = req;
|
|
161
|
+
if (typeof amount !== "string" || !Number.isInteger(Number(amount)) || Number(amount) <= 0) {
|
|
162
|
+
throw new Error(`Invalid amount: ${amount}`);
|
|
163
|
+
}
|
|
164
|
+
if (scheme !== "exact") throw new Error(`Unsupported scheme: ${scheme}`);
|
|
165
|
+
if (!isStellarNetwork(network)) throw new Error(`Unsupported network: ${network}`);
|
|
166
|
+
if (!validateStellarDestinationAddress(payTo)) throw new Error(`Invalid payTo: ${payTo}`);
|
|
167
|
+
if (!validateStellarAssetAddress(asset)) throw new Error(`Invalid asset: ${asset}`);
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// src/stellar/signer.ts
|
|
172
|
+
import { Keypair } from "@stellar/stellar-sdk";
|
|
173
|
+
import { basicNodeSigner } from "@stellar/stellar-sdk/contract";
|
|
174
|
+
function createEd25519Signer(privateKey, defaultNetwork = STELLAR_TESTNET_CAIP2) {
|
|
175
|
+
const kp = Keypair.fromSecret(privateKey);
|
|
176
|
+
const networkPassphrase = getNetworkPassphrase(defaultNetwork);
|
|
177
|
+
const address = kp.publicKey();
|
|
178
|
+
const { signAuthEntry, signTransaction } = basicNodeSigner(kp, networkPassphrase);
|
|
179
|
+
return { address, signAuthEntry, signTransaction };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// src/index.ts
|
|
183
|
+
var currentDir = dirname(fileURLToPath(import.meta.url));
|
|
184
|
+
loadEnv({ path: resolve(currentDir, "..", ".env") });
|
|
185
|
+
function requireEnv(name) {
|
|
186
|
+
const value = process.env[name]?.trim();
|
|
187
|
+
if (!value) throw new Error(`Missing required env var: ${name}`);
|
|
188
|
+
return value;
|
|
189
|
+
}
|
|
190
|
+
var STELLAR_SECRET_KEY = requireEnv("STELLAR_SECRET_KEY");
|
|
191
|
+
var AUDIT_GATEWAY_URL = process.env.AUDIT_GATEWAY_URL?.trim() || "https://soroban-node-0.onrender.com/api/audit";
|
|
192
|
+
var MPP_GATEWAY_URL = process.env.MPP_AUDIT_GATEWAY_URL?.trim() || "https://soroban-node-0.onrender.com/api/audit/mpp";
|
|
193
|
+
var rawNetwork = (process.env.STELLAR_NETWORK ?? STELLAR_TESTNET_CAIP2).trim();
|
|
194
|
+
if (rawNetwork !== STELLAR_TESTNET_CAIP2 && rawNetwork !== STELLAR_PUBNET_CAIP2) {
|
|
195
|
+
throw new Error(`Unsupported STELLAR_NETWORK: ${rawNetwork}`);
|
|
196
|
+
}
|
|
197
|
+
var STELLAR_NETWORK = rawNetwork;
|
|
198
|
+
var signer = createEd25519Signer(STELLAR_SECRET_KEY, STELLAR_NETWORK);
|
|
199
|
+
var paymentClient = new x402Client().register(
|
|
200
|
+
"stellar:*",
|
|
201
|
+
new ExactStellarScheme(signer)
|
|
202
|
+
);
|
|
203
|
+
var httpClient = new x402HTTPClient(paymentClient);
|
|
204
|
+
var fetchWithPayment = wrapFetchWithPayment(fetch, httpClient);
|
|
205
|
+
var mppClient = MppxClient.create({
|
|
206
|
+
methods: [stellarMpp.charge({ secretKey: STELLAR_SECRET_KEY })],
|
|
207
|
+
polyfill: false
|
|
208
|
+
});
|
|
209
|
+
var server = new McpServer({
|
|
210
|
+
name: process.env.MCP_SERVER_NAME || "auditor-mcp",
|
|
211
|
+
version: process.env.MCP_SERVER_VERSION || "0.1.0"
|
|
212
|
+
});
|
|
213
|
+
server.tool(
|
|
214
|
+
"audit_soroban_contract",
|
|
215
|
+
[
|
|
216
|
+
"Reads a local Soroban smart contract (.rs file) and submits it for a paid security audit.",
|
|
217
|
+
"Charges 0.15 USDC on Stellar Testnet via the x402 protocol.",
|
|
218
|
+
"Returns a structured audit report with vulnerabilities, warnings, and recommendations."
|
|
219
|
+
].join(" "),
|
|
220
|
+
{
|
|
221
|
+
file_path: z.string().describe("Absolute or relative path to the Soroban .rs contract file to audit")
|
|
222
|
+
},
|
|
223
|
+
async ({ file_path }) => {
|
|
224
|
+
let code;
|
|
225
|
+
try {
|
|
226
|
+
code = await readFile(file_path, "utf8");
|
|
227
|
+
} catch (err) {
|
|
228
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
229
|
+
return {
|
|
230
|
+
content: [{ type: "text", text: `Failed to read file "${file_path}": ${message}` }],
|
|
231
|
+
isError: true
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
if (code.trim().length === 0) {
|
|
235
|
+
return {
|
|
236
|
+
content: [{ type: "text", text: `File "${file_path}" is empty.` }],
|
|
237
|
+
isError: true
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
let response;
|
|
241
|
+
try {
|
|
242
|
+
response = await fetchWithPayment(AUDIT_GATEWAY_URL, {
|
|
243
|
+
method: "POST",
|
|
244
|
+
headers: { "Content-Type": "application/json" },
|
|
245
|
+
body: JSON.stringify({ code })
|
|
246
|
+
});
|
|
247
|
+
} catch (err) {
|
|
248
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
249
|
+
return {
|
|
250
|
+
content: [
|
|
251
|
+
{
|
|
252
|
+
type: "text",
|
|
253
|
+
text: `Payment or network error calling auditor-backend: ${message}`
|
|
254
|
+
}
|
|
255
|
+
],
|
|
256
|
+
isError: true
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
const rawBody = await response.text();
|
|
260
|
+
if (!response.ok) {
|
|
261
|
+
return {
|
|
262
|
+
content: [
|
|
263
|
+
{
|
|
264
|
+
type: "text",
|
|
265
|
+
text: `Auditor backend returned HTTP ${response.status}:
|
|
266
|
+
${rawBody}`
|
|
267
|
+
}
|
|
268
|
+
],
|
|
269
|
+
isError: true
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
let report = null;
|
|
273
|
+
try {
|
|
274
|
+
report = JSON.parse(rawBody);
|
|
275
|
+
} catch {
|
|
276
|
+
return {
|
|
277
|
+
content: [{ type: "text", text: `Audit backend returned non-JSON response:
|
|
278
|
+
${rawBody}` }],
|
|
279
|
+
isError: true
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
const findings = report?.findings ?? [];
|
|
283
|
+
const counts = {};
|
|
284
|
+
if (Array.isArray(findings)) {
|
|
285
|
+
for (const f of findings) {
|
|
286
|
+
counts[f.severity] = (counts[f.severity] ?? 0) + 1;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
const summary = ["CRITICAL", "HIGH", "MEDIUM", "LOW", "INFO"].filter((s) => counts[s]).map((s) => `${s}: ${counts[s]}`).join(" | ");
|
|
290
|
+
return {
|
|
291
|
+
content: [
|
|
292
|
+
{
|
|
293
|
+
type: "text",
|
|
294
|
+
text: JSON.stringify(
|
|
295
|
+
{
|
|
296
|
+
file: file_path,
|
|
297
|
+
walletAddress: signer.address,
|
|
298
|
+
model: report?.model,
|
|
299
|
+
summary: summary || "No vulnerabilities found",
|
|
300
|
+
findings
|
|
301
|
+
},
|
|
302
|
+
null,
|
|
303
|
+
2
|
|
304
|
+
)
|
|
305
|
+
}
|
|
306
|
+
]
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
);
|
|
310
|
+
server.tool(
|
|
311
|
+
"audit_soroban_contract_mpp",
|
|
312
|
+
[
|
|
313
|
+
"Reads a local Soroban smart contract (.rs file) and submits it for a paid security audit.",
|
|
314
|
+
"Charges 0.15 USDC on Stellar Testnet via the Stripe Machine Payments Protocol (MPP).",
|
|
315
|
+
"Returns a structured audit report with vulnerabilities, CWE IDs, severity levels, and fixes."
|
|
316
|
+
].join(" "),
|
|
317
|
+
{
|
|
318
|
+
file_path: z.string().describe("Absolute or relative path to the Soroban .rs contract file to audit")
|
|
319
|
+
},
|
|
320
|
+
async ({ file_path }) => {
|
|
321
|
+
let code;
|
|
322
|
+
try {
|
|
323
|
+
code = await readFile(file_path, "utf8");
|
|
324
|
+
} catch (err) {
|
|
325
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
326
|
+
return {
|
|
327
|
+
content: [{ type: "text", text: `Failed to read file "${file_path}": ${message}` }],
|
|
328
|
+
isError: true
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
if (code.trim().length === 0) {
|
|
332
|
+
return {
|
|
333
|
+
content: [{ type: "text", text: `File "${file_path}" is empty.` }],
|
|
334
|
+
isError: true
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
let response;
|
|
338
|
+
try {
|
|
339
|
+
response = await mppClient.fetch(MPP_GATEWAY_URL, {
|
|
340
|
+
method: "POST",
|
|
341
|
+
headers: { "Content-Type": "application/json" },
|
|
342
|
+
body: JSON.stringify({ code })
|
|
343
|
+
});
|
|
344
|
+
} catch (err) {
|
|
345
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
346
|
+
return {
|
|
347
|
+
content: [
|
|
348
|
+
{
|
|
349
|
+
type: "text",
|
|
350
|
+
text: `MPP payment or network error: ${message}`
|
|
351
|
+
}
|
|
352
|
+
],
|
|
353
|
+
isError: true
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
const rawBody = await response.text();
|
|
357
|
+
if (!response.ok) {
|
|
358
|
+
return {
|
|
359
|
+
content: [
|
|
360
|
+
{
|
|
361
|
+
type: "text",
|
|
362
|
+
text: `Auditor backend returned HTTP ${response.status}:
|
|
363
|
+
${rawBody}`
|
|
364
|
+
}
|
|
365
|
+
],
|
|
366
|
+
isError: true
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
let report = null;
|
|
370
|
+
try {
|
|
371
|
+
report = JSON.parse(rawBody);
|
|
372
|
+
} catch {
|
|
373
|
+
return {
|
|
374
|
+
content: [{ type: "text", text: `Audit backend returned non-JSON:
|
|
375
|
+
${rawBody}` }],
|
|
376
|
+
isError: true
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
const findings = report?.findings ?? [];
|
|
380
|
+
const counts = {};
|
|
381
|
+
if (Array.isArray(findings)) {
|
|
382
|
+
for (const f of findings) {
|
|
383
|
+
counts[f.severity] = (counts[f.severity] ?? 0) + 1;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
const summary = ["CRITICAL", "HIGH", "MEDIUM", "LOW", "INFO"].filter((s) => counts[s]).map((s) => `${s}: ${counts[s]}`).join(" | ");
|
|
387
|
+
return {
|
|
388
|
+
content: [
|
|
389
|
+
{
|
|
390
|
+
type: "text",
|
|
391
|
+
text: JSON.stringify(
|
|
392
|
+
{
|
|
393
|
+
file: file_path,
|
|
394
|
+
protocol: "Stripe MPP / Stellar Testnet",
|
|
395
|
+
walletAddress: signer.address,
|
|
396
|
+
model: report?.model,
|
|
397
|
+
summary: summary || "No vulnerabilities found",
|
|
398
|
+
findings
|
|
399
|
+
},
|
|
400
|
+
null,
|
|
401
|
+
2
|
|
402
|
+
)
|
|
403
|
+
}
|
|
404
|
+
]
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
);
|
|
408
|
+
var transport = new StdioServerTransport();
|
|
409
|
+
await server.connect(transport);
|
|
410
|
+
console.error("auditor-mcp running over stdio");
|
|
411
|
+
console.error(`wallet : ${signer.address}`);
|
|
412
|
+
console.error(`network: ${STELLAR_NETWORK}`);
|
|
413
|
+
console.error(`gateway: ${AUDIT_GATEWAY_URL}`);
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "auditor-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "MCP server that audits Soroban smart contracts via autonomous x402 / Stripe MPP payments on Stellar Testnet",
|
|
6
|
+
"keywords": ["mcp", "soroban", "stellar", "x402", "security", "audit", "smart-contract"],
|
|
7
|
+
"author": "xaviersharwin10",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"homepage": "https://github.com/xaviersharwin10/soroban_node_0",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/xaviersharwin10/soroban_node_0.git"
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"auditor-mcp": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsup src/index.ts --format esm",
|
|
22
|
+
"dev": "tsx src/index.ts",
|
|
23
|
+
"typecheck": "tsc --noEmit"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
27
|
+
"@stellar/mpp": "^0.4.0",
|
|
28
|
+
"@stellar/stellar-sdk": "^14.6.1",
|
|
29
|
+
"@x402/core": "^2.8.0",
|
|
30
|
+
"@x402/fetch": "^2.8.0",
|
|
31
|
+
"dotenv": "^17.3.1",
|
|
32
|
+
"mppx": "^0.4.12",
|
|
33
|
+
"tsup": "^8.5.1",
|
|
34
|
+
"typescript": "^5.7.0",
|
|
35
|
+
"zod": "^3.25.76"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^22.0.0",
|
|
39
|
+
"tsx": "^4.21.0"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=20"
|
|
43
|
+
}
|
|
44
|
+
}
|