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.
- package/dist/index.js +109 -59
- 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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1040
|
-
|
|
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,
|
|
1067
|
+
async ({ domain, uri, statement, network, expirationMinutes }) => {
|
|
1046
1068
|
try {
|
|
1047
1069
|
const { account, address } = await getOrCreateWallet();
|
|
1048
|
-
const
|
|
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:
|
|
1056
|
-
nonce
|
|
1057
|
-
issuedAt
|
|
1058
|
-
expirationTime
|
|
1059
|
-
|
|
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 =
|
|
1065
|
-
|
|
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
|
-
|
|
1072
|
-
expiresAt:
|
|
1073
|
-
usage: "Add to request headers as:
|
|
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
|
|
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
|
-
|
|
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
|
|
1103
|
-
issuedAt
|
|
1104
|
-
expirationTime
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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,
|
|
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
|
|
1133
|
-
const proof = await
|
|
1182
|
+
const caip2Network = NETWORKS2[network];
|
|
1183
|
+
const proof = await createSiwxProof(
|
|
1134
1184
|
account,
|
|
1135
1185
|
parsedUrl.host,
|
|
1136
1186
|
parsedUrl.origin,
|
|
1137
|
-
|
|
1187
|
+
caip2Network
|
|
1138
1188
|
);
|
|
1139
1189
|
const response = await fetch(url, {
|
|
1140
1190
|
method,
|
|
1141
1191
|
headers: {
|
|
1142
1192
|
"Content-Type": "application/json",
|
|
1143
|
-
|
|
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
|
+
"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
|
+
}
|