@gatesolve/mcp-server 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/README.md +61 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +134 -0
- package/package.json +32 -0
- package/src/index.ts +177 -0
- package/tsconfig.json +14 -0
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# GateSolve MCP Server
|
|
2
|
+
|
|
3
|
+
Model Context Protocol (MCP) server for [GateSolve](https://gatesolve.dev) — CAPTCHA solving for AI agents via x402 micropayments.
|
|
4
|
+
|
|
5
|
+
## Tools
|
|
6
|
+
|
|
7
|
+
### `solve_captcha`
|
|
8
|
+
Solve a CAPTCHA (Turnstile, reCAPTCHA v2/v3, hCaptcha). Returns payment requirements or solution token.
|
|
9
|
+
|
|
10
|
+
### `list_captcha_types`
|
|
11
|
+
List supported CAPTCHA types with pricing, accuracy, and required fields.
|
|
12
|
+
|
|
13
|
+
### `check_gatesolve_status`
|
|
14
|
+
Check API health and service status.
|
|
15
|
+
|
|
16
|
+
## Setup
|
|
17
|
+
|
|
18
|
+
### Claude Desktop / Cursor / Windsurf
|
|
19
|
+
|
|
20
|
+
Add to your MCP config:
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"mcpServers": {
|
|
25
|
+
"gatesolve": {
|
|
26
|
+
"command": "npx",
|
|
27
|
+
"args": ["@gatesolve/mcp-server"]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### From source
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
git clone https://github.com/arsonx-dev/gatesolve-mcp.git
|
|
37
|
+
cd gatesolve-mcp
|
|
38
|
+
npm install
|
|
39
|
+
npm run build
|
|
40
|
+
npm start
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Environment Variables
|
|
44
|
+
|
|
45
|
+
| Variable | Default | Description |
|
|
46
|
+
|----------|---------|-------------|
|
|
47
|
+
| `GATESOLVE_API_URL` | `https://gatesolve.dev` | GateSolve API base URL |
|
|
48
|
+
|
|
49
|
+
## How It Works
|
|
50
|
+
|
|
51
|
+
1. Agent calls `solve_captcha` with the CAPTCHA type, site key, and page URL
|
|
52
|
+
2. GateSolve returns a 402 Payment Required with USDC amount
|
|
53
|
+
3. Agent pays via x402 (USDC on Base network)
|
|
54
|
+
4. Agent calls `solve_captcha` again with the payment token
|
|
55
|
+
5. Returns the solved CAPTCHA token
|
|
56
|
+
|
|
57
|
+
No API keys. No accounts. Just x402.
|
|
58
|
+
|
|
59
|
+
## License
|
|
60
|
+
|
|
61
|
+
MIT
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
const GATESOLVE_API = process.env.GATESOLVE_API_URL || "https://gatesolve.dev";
|
|
6
|
+
const server = new McpServer({
|
|
7
|
+
name: "gatesolve",
|
|
8
|
+
version: "0.1.0",
|
|
9
|
+
});
|
|
10
|
+
// Tool: solve_captcha
|
|
11
|
+
server.tool("solve_captcha", "Solve a CAPTCHA using GateSolve. Supports Cloudflare Turnstile, reCAPTCHA v2/v3, and hCaptcha. Payment is handled via x402 micropayments (USDC on Base network).", {
|
|
12
|
+
type: z
|
|
13
|
+
.enum(["cloudflare-turnstile", "recaptcha-v2", "recaptcha-v3", "hcaptcha"])
|
|
14
|
+
.describe("The type of CAPTCHA to solve"),
|
|
15
|
+
siteKey: z.string().describe("The site key / widget key for the CAPTCHA"),
|
|
16
|
+
pageUrl: z.string().url().describe("The URL of the page containing the CAPTCHA"),
|
|
17
|
+
paymentToken: z
|
|
18
|
+
.string()
|
|
19
|
+
.optional()
|
|
20
|
+
.describe("x402 payment token (USDC on Base). If omitted, returns payment requirements."),
|
|
21
|
+
}, async ({ type, siteKey, pageUrl, paymentToken }) => {
|
|
22
|
+
try {
|
|
23
|
+
const headers = {
|
|
24
|
+
"Content-Type": "application/json",
|
|
25
|
+
};
|
|
26
|
+
if (paymentToken) {
|
|
27
|
+
headers["X-402-Pay"] = paymentToken;
|
|
28
|
+
}
|
|
29
|
+
const response = await fetch(`${GATESOLVE_API}/api/v1/solve`, {
|
|
30
|
+
method: "POST",
|
|
31
|
+
headers,
|
|
32
|
+
body: JSON.stringify({ type, siteKey, pageUrl }),
|
|
33
|
+
});
|
|
34
|
+
const data = await response.json();
|
|
35
|
+
if (response.status === 402) {
|
|
36
|
+
return {
|
|
37
|
+
content: [
|
|
38
|
+
{
|
|
39
|
+
type: "text",
|
|
40
|
+
text: `Payment required to solve this CAPTCHA.\n\nPayment details:\n- Network: ${data.accepts?.network || "base"}\n- Token: ${data.accepts?.token || "USDC"}\n- Amount: ${data.accepts?.amount || "0.02"} USDC\n- Receiver: ${data.accepts?.receiver}\n- Protocol: x402 v${data.x402Version || "1.0"}\n\nProvide the payment token in the 'paymentToken' parameter to complete the solve.`,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (response.status === 200) {
|
|
46
|
+
return {
|
|
47
|
+
content: [
|
|
48
|
+
{
|
|
49
|
+
type: "text",
|
|
50
|
+
text: `CAPTCHA solved successfully!\n\nToken: ${data.token}\nType: ${data.type}\nSolved in: ${data.solvedIn}\nStatus: ${data.status}`,
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
content: [
|
|
57
|
+
{
|
|
58
|
+
type: "text",
|
|
59
|
+
text: `Error: ${data.message || "Unknown error"} (status: ${response.status})`,
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
isError: true,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
return {
|
|
67
|
+
content: [
|
|
68
|
+
{
|
|
69
|
+
type: "text",
|
|
70
|
+
text: `Failed to connect to GateSolve API: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
isError: true,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
// Tool: list_captcha_types
|
|
78
|
+
server.tool("list_captcha_types", "List all supported CAPTCHA types with pricing, accuracy, and required fields.", {}, async () => {
|
|
79
|
+
try {
|
|
80
|
+
const response = await fetch(`${GATESOLVE_API}/api/v1/types`);
|
|
81
|
+
const data = await response.json();
|
|
82
|
+
const lines = data.types.map((t) => `**${t.name}** (${t.id})\n Price: ${t.price} USDC | Avg: ${t.avgSolveTime} | Accuracy: ${t.accuracy}\n Required: ${t.requiredFields.join(", ")}\n Optional: ${t.optionalFields.join(", ") || "none"}\n ${t.description}`);
|
|
83
|
+
return {
|
|
84
|
+
content: [
|
|
85
|
+
{
|
|
86
|
+
type: "text",
|
|
87
|
+
text: `GateSolve Supported CAPTCHA Types:\n\n${lines.join("\n\n")}\n\nPayment: x402 micropayments (USDC on ${data.payment.network})`,
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
return {
|
|
94
|
+
content: [
|
|
95
|
+
{
|
|
96
|
+
type: "text",
|
|
97
|
+
text: `Failed to fetch captcha types: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
isError: true,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
// Tool: check_status
|
|
105
|
+
server.tool("check_gatesolve_status", "Check GateSolve API health and service status.", {}, async () => {
|
|
106
|
+
try {
|
|
107
|
+
const response = await fetch(`${GATESOLVE_API}/api/health`);
|
|
108
|
+
const data = await response.json();
|
|
109
|
+
return {
|
|
110
|
+
content: [
|
|
111
|
+
{
|
|
112
|
+
type: "text",
|
|
113
|
+
text: `GateSolve Status: ${data.status}\n\nServices:\n- API: ${data.services.api}\n- Database: ${data.services.database}\n- Solver: ${data.services.solver}\n\nMetrics:\n- Total solves: ${data.metrics.totalSolves}\n- Last 24h: ${data.metrics.last24hSolves}\n- Avg solve time: ${data.metrics.avgSolveMs ? `${(data.metrics.avgSolveMs / 1000).toFixed(1)}s` : "N/A"}\n\nLatency: ${data.latencyMs}ms | Version: ${data.version}`,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
return {
|
|
120
|
+
content: [
|
|
121
|
+
{
|
|
122
|
+
type: "text",
|
|
123
|
+
text: `GateSolve API unreachable: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
isError: true,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
async function main() {
|
|
131
|
+
const transport = new StdioServerTransport();
|
|
132
|
+
await server.connect(transport);
|
|
133
|
+
}
|
|
134
|
+
main().catch(console.error);
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gatesolve/mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for GateSolve CAPTCHA solving via x402 micropayments",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"gatesolve-mcp": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"start": "node dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"mcp",
|
|
16
|
+
"captcha",
|
|
17
|
+
"gatesolve",
|
|
18
|
+
"x402",
|
|
19
|
+
"ai-agent",
|
|
20
|
+
"turnstile",
|
|
21
|
+
"recaptcha",
|
|
22
|
+
"hcaptcha"
|
|
23
|
+
],
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"typescript": "^5.0.0",
|
|
30
|
+
"@types/node": "^20.0.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
|
|
7
|
+
const GATESOLVE_API = process.env.GATESOLVE_API_URL || "https://gatesolve.dev";
|
|
8
|
+
|
|
9
|
+
const server = new McpServer({
|
|
10
|
+
name: "gatesolve",
|
|
11
|
+
version: "0.1.0",
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
// Tool: solve_captcha
|
|
15
|
+
server.tool(
|
|
16
|
+
"solve_captcha",
|
|
17
|
+
"Solve a CAPTCHA using GateSolve. Supports Cloudflare Turnstile, reCAPTCHA v2/v3, and hCaptcha. Payment is handled via x402 micropayments (USDC on Base network).",
|
|
18
|
+
{
|
|
19
|
+
type: z
|
|
20
|
+
.enum(["cloudflare-turnstile", "recaptcha-v2", "recaptcha-v3", "hcaptcha"])
|
|
21
|
+
.describe("The type of CAPTCHA to solve"),
|
|
22
|
+
siteKey: z.string().describe("The site key / widget key for the CAPTCHA"),
|
|
23
|
+
pageUrl: z.string().url().describe("The URL of the page containing the CAPTCHA"),
|
|
24
|
+
paymentToken: z
|
|
25
|
+
.string()
|
|
26
|
+
.optional()
|
|
27
|
+
.describe(
|
|
28
|
+
"x402 payment token (USDC on Base). If omitted, returns payment requirements."
|
|
29
|
+
),
|
|
30
|
+
},
|
|
31
|
+
async ({ type, siteKey, pageUrl, paymentToken }) => {
|
|
32
|
+
try {
|
|
33
|
+
const headers: Record<string, string> = {
|
|
34
|
+
"Content-Type": "application/json",
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
if (paymentToken) {
|
|
38
|
+
headers["X-402-Pay"] = paymentToken;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const response = await fetch(`${GATESOLVE_API}/api/v1/solve`, {
|
|
42
|
+
method: "POST",
|
|
43
|
+
headers,
|
|
44
|
+
body: JSON.stringify({ type, siteKey, pageUrl }),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const data = await response.json();
|
|
48
|
+
|
|
49
|
+
if (response.status === 402) {
|
|
50
|
+
return {
|
|
51
|
+
content: [
|
|
52
|
+
{
|
|
53
|
+
type: "text" as const,
|
|
54
|
+
text: `Payment required to solve this CAPTCHA.\n\nPayment details:\n- Network: ${data.accepts?.network || "base"}\n- Token: ${data.accepts?.token || "USDC"}\n- Amount: ${data.accepts?.amount || "0.02"} USDC\n- Receiver: ${data.accepts?.receiver}\n- Protocol: x402 v${data.x402Version || "1.0"}\n\nProvide the payment token in the 'paymentToken' parameter to complete the solve.`,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (response.status === 200) {
|
|
61
|
+
return {
|
|
62
|
+
content: [
|
|
63
|
+
{
|
|
64
|
+
type: "text" as const,
|
|
65
|
+
text: `CAPTCHA solved successfully!\n\nToken: ${data.token}\nType: ${data.type}\nSolved in: ${data.solvedIn}\nStatus: ${data.status}`,
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
content: [
|
|
73
|
+
{
|
|
74
|
+
type: "text" as const,
|
|
75
|
+
text: `Error: ${data.message || "Unknown error"} (status: ${response.status})`,
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
isError: true,
|
|
79
|
+
};
|
|
80
|
+
} catch (error) {
|
|
81
|
+
return {
|
|
82
|
+
content: [
|
|
83
|
+
{
|
|
84
|
+
type: "text" as const,
|
|
85
|
+
text: `Failed to connect to GateSolve API: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
isError: true,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
// Tool: list_captcha_types
|
|
95
|
+
server.tool(
|
|
96
|
+
"list_captcha_types",
|
|
97
|
+
"List all supported CAPTCHA types with pricing, accuracy, and required fields.",
|
|
98
|
+
{},
|
|
99
|
+
async () => {
|
|
100
|
+
try {
|
|
101
|
+
const response = await fetch(`${GATESOLVE_API}/api/v1/types`);
|
|
102
|
+
const data = await response.json();
|
|
103
|
+
|
|
104
|
+
const lines = data.types.map(
|
|
105
|
+
(t: {
|
|
106
|
+
id: string;
|
|
107
|
+
name: string;
|
|
108
|
+
price: string;
|
|
109
|
+
avgSolveTime: string;
|
|
110
|
+
accuracy: string;
|
|
111
|
+
requiredFields: string[];
|
|
112
|
+
optionalFields: string[];
|
|
113
|
+
description: string;
|
|
114
|
+
}) =>
|
|
115
|
+
`**${t.name}** (${t.id})\n Price: ${t.price} USDC | Avg: ${t.avgSolveTime} | Accuracy: ${t.accuracy}\n Required: ${t.requiredFields.join(", ")}\n Optional: ${t.optionalFields.join(", ") || "none"}\n ${t.description}`
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
content: [
|
|
120
|
+
{
|
|
121
|
+
type: "text" as const,
|
|
122
|
+
text: `GateSolve Supported CAPTCHA Types:\n\n${lines.join("\n\n")}\n\nPayment: x402 micropayments (USDC on ${data.payment.network})`,
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
};
|
|
126
|
+
} catch (error) {
|
|
127
|
+
return {
|
|
128
|
+
content: [
|
|
129
|
+
{
|
|
130
|
+
type: "text" as const,
|
|
131
|
+
text: `Failed to fetch captcha types: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
isError: true,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
// Tool: check_status
|
|
141
|
+
server.tool(
|
|
142
|
+
"check_gatesolve_status",
|
|
143
|
+
"Check GateSolve API health and service status.",
|
|
144
|
+
{},
|
|
145
|
+
async () => {
|
|
146
|
+
try {
|
|
147
|
+
const response = await fetch(`${GATESOLVE_API}/api/health`);
|
|
148
|
+
const data = await response.json();
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
content: [
|
|
152
|
+
{
|
|
153
|
+
type: "text" as const,
|
|
154
|
+
text: `GateSolve Status: ${data.status}\n\nServices:\n- API: ${data.services.api}\n- Database: ${data.services.database}\n- Solver: ${data.services.solver}\n\nMetrics:\n- Total solves: ${data.metrics.totalSolves}\n- Last 24h: ${data.metrics.last24hSolves}\n- Avg solve time: ${data.metrics.avgSolveMs ? `${(data.metrics.avgSolveMs / 1000).toFixed(1)}s` : "N/A"}\n\nLatency: ${data.latencyMs}ms | Version: ${data.version}`,
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
};
|
|
158
|
+
} catch (error) {
|
|
159
|
+
return {
|
|
160
|
+
content: [
|
|
161
|
+
{
|
|
162
|
+
type: "text" as const,
|
|
163
|
+
text: `GateSolve API unreachable: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
isError: true,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
async function main() {
|
|
173
|
+
const transport = new StdioServerTransport();
|
|
174
|
+
await server.connect(transport);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
main().catch(console.error);
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"rootDir": "src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"declaration": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["src"]
|
|
14
|
+
}
|