x402scan-mcp 0.0.2 → 0.0.4

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.
Files changed (2) hide show
  1. package/dist/index.js +218 -2
  2. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -745,7 +745,7 @@ async function queryEndpoint(url, httpClient, options = {}) {
745
745
  function registerQueryEndpointTool(server) {
746
746
  server.tool(
747
747
  "query_endpoint",
748
- "Probe an x402-protected endpoint to get pricing and schema without making a payment. Returns payment requirements, accepted networks, and Bazaar schema if available.",
748
+ "Probe an x402-protected endpoint to get pricing and requirements without payment. Returns payment options, Bazaar schema, and Sign-In-With-X auth requirements (x402 v2) if available.",
749
749
  {
750
750
  url: z.string().url().describe("The x402-protected endpoint URL to probe"),
751
751
  method: z.enum(["GET", "POST", "PUT", "DELETE", "PATCH"]).default("GET").describe("HTTP method to use"),
@@ -811,6 +811,29 @@ function registerQueryEndpointTool(server) {
811
811
  hasBazaarExtension: true
812
812
  };
813
813
  }
814
+ if (pr.extensions?.["sign-in-with-x"]) {
815
+ const siwx = pr.extensions["sign-in-with-x"];
816
+ const info = siwx.info || {};
817
+ const requiredFields = ["domain", "uri", "version", "chainId", "nonce", "issuedAt"];
818
+ const missingFields = requiredFields.filter((f) => !info[f]);
819
+ const validationErrors = [];
820
+ if (!siwx.info) {
821
+ validationErrors.push('Missing "info" object in sign-in-with-x extension');
822
+ } else if (missingFields.length > 0) {
823
+ validationErrors.push(`Missing required fields in info: ${missingFields.join(", ")}`);
824
+ }
825
+ if (!siwx.schema) {
826
+ validationErrors.push('Missing "schema" object in sign-in-with-x extension');
827
+ }
828
+ response.signInWithX = {
829
+ required: true,
830
+ valid: validationErrors.length === 0,
831
+ validationErrors: validationErrors.length > 0 ? validationErrors : void 0,
832
+ info: siwx.info,
833
+ schema: siwx.schema,
834
+ usage: "Use create_siwe_proof or fetch_with_siwe tools to authenticate"
835
+ };
836
+ }
814
837
  if (pr.error) {
815
838
  response.serverError = pr.error;
816
839
  }
@@ -1014,6 +1037,197 @@ function registerExecuteCallTool(server) {
1014
1037
  );
1015
1038
  }
1016
1039
 
1040
+ // src/tools/siwe.ts
1041
+ import { z as z4 } from "zod";
1042
+ import { SiweMessage, generateNonce } from "siwe";
1043
+ var NETWORKS = {
1044
+ mainnet: "eip155:1",
1045
+ base: "eip155:8453",
1046
+ "base-sepolia": "eip155:84532",
1047
+ optimism: "eip155:10",
1048
+ arbitrum: "eip155:42161",
1049
+ polygon: "eip155:137",
1050
+ sepolia: "eip155:11155111"
1051
+ };
1052
+ function parseChainId(network) {
1053
+ const parts = network.split(":");
1054
+ return parseInt(parts[1], 10);
1055
+ }
1056
+ function registerCreateSiweProofTool(server) {
1057
+ server.tool(
1058
+ "create_siwe_proof",
1059
+ "Create a CAIP-122 compliant Sign-In-With-X proof for wallet authentication (x402 v2 extension). Returns a flat proof object for the SIGN-IN-WITH-X header.",
1060
+ {
1061
+ domain: z4.string().describe('Domain requesting auth (e.g., "api.example.com")'),
1062
+ uri: z4.string().url().describe('Full URI of the resource (e.g., "https://api.example.com")'),
1063
+ statement: z4.string().optional().default("Authenticate to API").describe("Human-readable statement"),
1064
+ network: z4.enum(["mainnet", "base", "base-sepolia", "optimism", "arbitrum", "polygon", "sepolia"]).optional().default("base").describe("Network name (default: base)"),
1065
+ expirationMinutes: z4.number().optional().default(5).describe("Proof validity in minutes (default: 5)")
1066
+ },
1067
+ async ({ domain, uri, statement, network, expirationMinutes }) => {
1068
+ try {
1069
+ const { account, address } = await getOrCreateWallet();
1070
+ const caip2Network = NETWORKS[network];
1071
+ const numericChainId = parseChainId(caip2Network);
1072
+ const nonce = generateNonce();
1073
+ const issuedAt = (/* @__PURE__ */ new Date()).toISOString();
1074
+ const expirationTime = new Date(
1075
+ Date.now() + expirationMinutes * 60 * 1e3
1076
+ ).toISOString();
1077
+ const siweMessage = new SiweMessage({
1078
+ domain,
1079
+ address,
1080
+ statement,
1081
+ uri,
1082
+ version: "1",
1083
+ chainId: numericChainId,
1084
+ nonce,
1085
+ issuedAt,
1086
+ expirationTime,
1087
+ resources: [uri]
1088
+ });
1089
+ const message = siweMessage.prepareMessage();
1090
+ const signature = await account.signMessage({ message });
1091
+ const proof = {
1092
+ domain,
1093
+ address,
1094
+ statement,
1095
+ uri,
1096
+ version: "1",
1097
+ chainId: caip2Network,
1098
+ nonce,
1099
+ issuedAt,
1100
+ expirationTime,
1101
+ resources: [uri],
1102
+ signature
1103
+ };
1104
+ return mcpSuccess({
1105
+ proof: JSON.stringify(proof),
1106
+ address,
1107
+ network: caip2Network,
1108
+ expiresAt: expirationTime,
1109
+ usage: "Add to request headers as: SIGN-IN-WITH-X: <proof>"
1110
+ });
1111
+ } catch (err) {
1112
+ return mcpError(err, { tool: "create_siwe_proof" });
1113
+ }
1114
+ }
1115
+ );
1116
+ }
1117
+
1118
+ // src/tools/fetch_with_siwe.ts
1119
+ import { z as z5 } from "zod";
1120
+ import { SiweMessage as SiweMessage2, generateNonce as generateNonce2 } from "siwe";
1121
+ var NETWORKS2 = {
1122
+ mainnet: "eip155:1",
1123
+ base: "eip155:8453",
1124
+ "base-sepolia": "eip155:84532",
1125
+ optimism: "eip155:10",
1126
+ arbitrum: "eip155:42161",
1127
+ polygon: "eip155:137",
1128
+ sepolia: "eip155:11155111"
1129
+ };
1130
+ function parseChainId2(network) {
1131
+ const parts = network.split(":");
1132
+ return parseInt(parts[1], 10);
1133
+ }
1134
+ async function createSiwxProof(account, domain, uri, network) {
1135
+ const numericChainId = parseChainId2(network);
1136
+ const nonce = generateNonce2();
1137
+ const issuedAt = (/* @__PURE__ */ new Date()).toISOString();
1138
+ const expirationTime = new Date(Date.now() + 3e5).toISOString();
1139
+ const siweMessage = new SiweMessage2({
1140
+ domain,
1141
+ address: account.address,
1142
+ statement: "Authenticate to API",
1143
+ uri,
1144
+ version: "1",
1145
+ chainId: numericChainId,
1146
+ nonce,
1147
+ issuedAt,
1148
+ expirationTime,
1149
+ resources: [uri]
1150
+ });
1151
+ const message = siweMessage.prepareMessage();
1152
+ const signature = await account.signMessage({ message });
1153
+ return JSON.stringify({
1154
+ domain,
1155
+ address: account.address,
1156
+ statement: "Authenticate to API",
1157
+ uri,
1158
+ version: "1",
1159
+ chainId: network,
1160
+ nonce,
1161
+ issuedAt,
1162
+ expirationTime,
1163
+ resources: [uri],
1164
+ signature
1165
+ });
1166
+ }
1167
+ function registerFetchWithSiweTool(server) {
1168
+ server.tool(
1169
+ "fetch_with_siwe",
1170
+ "Make an HTTP request with automatic CAIP-122 Sign-In-With-X wallet authentication (x402 v2 extension).",
1171
+ {
1172
+ url: z5.string().url().describe("The URL to fetch"),
1173
+ method: z5.enum(["GET", "POST", "PUT", "DELETE", "PATCH"]).optional().default("GET"),
1174
+ body: z5.unknown().optional().describe("Request body for POST/PUT/PATCH"),
1175
+ headers: z5.record(z5.string()).optional().describe("Additional headers"),
1176
+ network: z5.enum(["mainnet", "base", "base-sepolia", "optimism", "arbitrum", "polygon", "sepolia"]).optional().default("base").describe("Network name (default: base)")
1177
+ },
1178
+ async ({ url, method, body, headers, network }) => {
1179
+ try {
1180
+ const { account } = await getOrCreateWallet();
1181
+ const parsedUrl = new URL(url);
1182
+ const caip2Network = NETWORKS2[network];
1183
+ const proof = await createSiwxProof(
1184
+ account,
1185
+ parsedUrl.host,
1186
+ parsedUrl.origin,
1187
+ caip2Network
1188
+ );
1189
+ const response = await fetch(url, {
1190
+ method,
1191
+ headers: {
1192
+ "Content-Type": "application/json",
1193
+ "SIGN-IN-WITH-X": proof,
1194
+ ...headers
1195
+ },
1196
+ body: body ? JSON.stringify(body) : void 0
1197
+ });
1198
+ const responseHeaders = Object.fromEntries(response.headers.entries());
1199
+ if (!response.ok) {
1200
+ let errorBody;
1201
+ try {
1202
+ errorBody = await response.json();
1203
+ } catch {
1204
+ errorBody = await response.text();
1205
+ }
1206
+ return mcpError(`HTTP ${response.status}`, {
1207
+ statusCode: response.status,
1208
+ headers: responseHeaders,
1209
+ body: errorBody
1210
+ });
1211
+ }
1212
+ let data;
1213
+ const contentType = response.headers.get("content-type");
1214
+ if (contentType?.includes("application/json")) {
1215
+ data = await response.json();
1216
+ } else {
1217
+ data = await response.text();
1218
+ }
1219
+ return mcpSuccess({
1220
+ statusCode: response.status,
1221
+ headers: responseHeaders,
1222
+ data
1223
+ });
1224
+ } catch (err) {
1225
+ return mcpError(err, { tool: "fetch_with_siwe", url });
1226
+ }
1227
+ }
1228
+ );
1229
+ }
1230
+
1017
1231
  // src/index.ts
1018
1232
  async function main() {
1019
1233
  log.clear();
@@ -1026,7 +1240,9 @@ async function main() {
1026
1240
  registerQueryEndpointTool(server);
1027
1241
  registerValidatePaymentTool(server);
1028
1242
  registerExecuteCallTool(server);
1029
- log.info("Registered 4 tools: check_balance, query_endpoint, validate_payment, execute_call");
1243
+ registerCreateSiweProofTool(server);
1244
+ registerFetchWithSiweTool(server);
1245
+ log.info("Registered 6 tools: check_balance, query_endpoint, validate_payment, execute_call, create_siwe_proof, fetch_with_siwe");
1030
1246
  const transport = new StdioServerTransport();
1031
1247
  await server.connect(transport);
1032
1248
  log.info(`Connected to transport, ready for requests. Log file: ${log.path}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x402scan-mcp",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Generic MCP server for calling x402-protected APIs with automatic payment handling",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -23,6 +23,7 @@
23
23
  "@modelcontextprotocol/sdk": "^1.6.1",
24
24
  "@x402/core": "^2.0.0",
25
25
  "@x402/evm": "^2.0.0",
26
+ "siwe": "^2.3.2",
26
27
  "viem": "^2.31.3",
27
28
  "zod": "^3.25.1"
28
29
  },