x402scan-mcp 0.0.3 → 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 +109 -59
  2. package/package.json +11 -10
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
  }
@@ -1017,60 +1040,73 @@ function registerExecuteCallTool(server) {
1017
1040
  // src/tools/siwe.ts
1018
1041
  import { z as z4 } from "zod";
1019
1042
  import { SiweMessage, generateNonce } from "siwe";
1020
- var CHAIN_IDS = {
1021
- mainnet: 1,
1022
- base: 8453,
1023
- "base-sepolia": 84532,
1024
- optimism: 10,
1025
- arbitrum: 42161,
1026
- polygon: 137,
1027
- sepolia: 11155111
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"
1028
1051
  };
1052
+ function parseChainId(network) {
1053
+ const parts = network.split(":");
1054
+ return parseInt(parts[1], 10);
1055
+ }
1029
1056
  function registerCreateSiweProofTool(server) {
1030
1057
  server.tool(
1031
1058
  "create_siwe_proof",
1032
- "Create a SIWE (Sign-In with Ethereum) proof for wallet authentication. Returns a proof string to use in X-SIWE-PROOF header.",
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.",
1033
1060
  {
1034
- domain: z4.string().describe(
1035
- 'Domain requesting auth (e.g., "localhost:3000" or "stablestudio.io")'
1036
- ),
1037
- uri: z4.string().url().describe('Full URI of the API (e.g., "http://localhost:3000")'),
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")'),
1038
1063
  statement: z4.string().optional().default("Authenticate to API").describe("Human-readable statement"),
1039
- chainId: z4.number().optional().describe(
1040
- "Chain ID (default: 8453 for Base). Common IDs: 1=mainnet, 8453=base, 84532=base-sepolia, 10=optimism"
1041
- ),
1042
- chain: z4.enum(["mainnet", "base", "base-sepolia", "optimism", "arbitrum", "polygon", "sepolia"]).optional().describe("Chain name (alternative to chainId). Default: base"),
1043
- expirationMinutes: z4.number().optional().default(60).describe("Proof validity in minutes")
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)")
1044
1066
  },
1045
- async ({ domain, uri, statement, chainId, chain, expirationMinutes }) => {
1067
+ async ({ domain, uri, statement, network, expirationMinutes }) => {
1046
1068
  try {
1047
1069
  const { account, address } = await getOrCreateWallet();
1048
- const resolvedChainId = chainId ?? (chain ? CHAIN_IDS[chain] : CHAIN_IDS.base);
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();
1049
1077
  const siweMessage = new SiweMessage({
1050
1078
  domain,
1051
1079
  address,
1052
1080
  statement,
1053
1081
  uri,
1054
1082
  version: "1",
1055
- chainId: resolvedChainId,
1056
- nonce: generateNonce(),
1057
- issuedAt: (/* @__PURE__ */ new Date()).toISOString(),
1058
- expirationTime: new Date(
1059
- Date.now() + expirationMinutes * 60 * 1e3
1060
- ).toISOString()
1083
+ chainId: numericChainId,
1084
+ nonce,
1085
+ issuedAt,
1086
+ expirationTime,
1087
+ resources: [uri]
1061
1088
  });
1062
1089
  const message = siweMessage.prepareMessage();
1063
1090
  const signature = await account.signMessage({ message });
1064
- const proof = JSON.stringify({
1065
- message: JSON.stringify(siweMessage),
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],
1066
1102
  signature
1067
- });
1103
+ };
1068
1104
  return mcpSuccess({
1069
- proof,
1105
+ proof: JSON.stringify(proof),
1070
1106
  address,
1071
- chainId: resolvedChainId,
1072
- expiresAt: siweMessage.expirationTime,
1073
- usage: "Add to request headers as: X-SIWE-PROOF: <proof>"
1107
+ network: caip2Network,
1108
+ expiresAt: expirationTime,
1109
+ usage: "Add to request headers as: SIGN-IN-WITH-X: <proof>"
1074
1110
  });
1075
1111
  } catch (err) {
1076
1112
  return mcpError(err, { tool: "create_siwe_proof" });
@@ -1082,65 +1118,79 @@ function registerCreateSiweProofTool(server) {
1082
1118
  // src/tools/fetch_with_siwe.ts
1083
1119
  import { z as z5 } from "zod";
1084
1120
  import { SiweMessage as SiweMessage2, generateNonce as generateNonce2 } from "siwe";
1085
- var CHAIN_IDS2 = {
1086
- mainnet: 1,
1087
- base: 8453,
1088
- "base-sepolia": 84532,
1089
- optimism: 10,
1090
- arbitrum: 42161,
1091
- polygon: 137,
1092
- sepolia: 11155111
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"
1093
1129
  };
1094
- async function createSiweProof(account, domain, uri, chainId) {
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();
1095
1139
  const siweMessage = new SiweMessage2({
1096
1140
  domain,
1097
1141
  address: account.address,
1098
1142
  statement: "Authenticate to API",
1099
1143
  uri,
1100
1144
  version: "1",
1101
- chainId,
1102
- nonce: generateNonce2(),
1103
- issuedAt: (/* @__PURE__ */ new Date()).toISOString(),
1104
- expirationTime: new Date(Date.now() + 36e5).toISOString()
1145
+ chainId: numericChainId,
1146
+ nonce,
1147
+ issuedAt,
1148
+ expirationTime,
1149
+ resources: [uri]
1105
1150
  });
1106
1151
  const message = siweMessage.prepareMessage();
1107
1152
  const signature = await account.signMessage({ message });
1108
1153
  return JSON.stringify({
1109
- message: JSON.stringify(siweMessage),
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],
1110
1164
  signature
1111
1165
  });
1112
1166
  }
1113
1167
  function registerFetchWithSiweTool(server) {
1114
1168
  server.tool(
1115
1169
  "fetch_with_siwe",
1116
- "Make an HTTP request with automatic SIWE wallet authentication. Useful for APIs that require wallet ownership proof.",
1170
+ "Make an HTTP request with automatic CAIP-122 Sign-In-With-X wallet authentication (x402 v2 extension).",
1117
1171
  {
1118
1172
  url: z5.string().url().describe("The URL to fetch"),
1119
1173
  method: z5.enum(["GET", "POST", "PUT", "DELETE", "PATCH"]).optional().default("GET"),
1120
1174
  body: z5.unknown().optional().describe("Request body for POST/PUT/PATCH"),
1121
1175
  headers: z5.record(z5.string()).optional().describe("Additional headers"),
1122
- siweHeader: z5.string().optional().default("X-SIWE-PROOF").describe("Header name for SIWE proof"),
1123
- chainId: z5.number().optional().describe(
1124
- "Chain ID for SIWE proof (default: 8453 for Base). Common IDs: 1=mainnet, 8453=base, 84532=base-sepolia"
1125
- ),
1126
- chain: z5.enum(["mainnet", "base", "base-sepolia", "optimism", "arbitrum", "polygon", "sepolia"]).optional().describe("Chain name (alternative to chainId). Default: base")
1176
+ network: z5.enum(["mainnet", "base", "base-sepolia", "optimism", "arbitrum", "polygon", "sepolia"]).optional().default("base").describe("Network name (default: base)")
1127
1177
  },
1128
- async ({ url, method, body, headers, siweHeader, chainId, chain }) => {
1178
+ async ({ url, method, body, headers, network }) => {
1129
1179
  try {
1130
1180
  const { account } = await getOrCreateWallet();
1131
1181
  const parsedUrl = new URL(url);
1132
- const resolvedChainId = chainId ?? (chain ? CHAIN_IDS2[chain] : CHAIN_IDS2.base);
1133
- const proof = await createSiweProof(
1182
+ const caip2Network = NETWORKS2[network];
1183
+ const proof = await createSiwxProof(
1134
1184
  account,
1135
1185
  parsedUrl.host,
1136
1186
  parsedUrl.origin,
1137
- resolvedChainId
1187
+ caip2Network
1138
1188
  );
1139
1189
  const response = await fetch(url, {
1140
1190
  method,
1141
1191
  headers: {
1142
1192
  "Content-Type": "application/json",
1143
- [siweHeader]: proof,
1193
+ "SIGN-IN-WITH-X": proof,
1144
1194
  ...headers
1145
1195
  },
1146
1196
  body: body ? JSON.stringify(body) : void 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x402scan-mcp",
3
- "version": "0.0.3",
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",
@@ -10,6 +10,15 @@
10
10
  "files": [
11
11
  "dist"
12
12
  ],
13
+ "scripts": {
14
+ "build": "tsup",
15
+ "build:mcpb": "tsx scripts/build-mcpb.ts",
16
+ "dev": "tsx src/index.ts",
17
+ "dev:bun": "bun run src/index.ts",
18
+ "typecheck": "tsc --noEmit",
19
+ "publish-package": "tsx scripts/publish.ts",
20
+ "prepublishOnly": "npm run build"
21
+ },
13
22
  "dependencies": {
14
23
  "@modelcontextprotocol/sdk": "^1.6.1",
15
24
  "@x402/core": "^2.0.0",
@@ -42,13 +51,5 @@
42
51
  "repository": {
43
52
  "type": "git",
44
53
  "url": "https://github.com/merit-systems/x402scan-mcp"
45
- },
46
- "scripts": {
47
- "build": "tsup",
48
- "build:mcpb": "tsx scripts/build-mcpb.ts",
49
- "dev": "tsx src/index.ts",
50
- "dev:bun": "bun run src/index.ts",
51
- "typecheck": "tsc --noEmit",
52
- "publish-package": "tsx scripts/publish.ts"
53
54
  }
54
- }
55
+ }