@sentinelsup/mcp 0.1.3 → 0.1.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/README.md +27 -10
- package/index.js +45 -23
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
# Maskbreak MCP Server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Look up limited public IP intelligence — cloud-hosting ranges and Tor exit nodes — from Claude, Cursor, or another MCP client. Powered by the [Maskbreak](https://maskbreak.com) fraud detection API. Requires Node.js 18+ for the local server.
|
|
4
4
|
|
|
5
5
|
## Tools
|
|
6
6
|
|
|
7
7
|
| Tool | What it does |
|
|
8
8
|
|------|--------------|
|
|
9
|
-
| `lookup_ip` |
|
|
9
|
+
| `lookup_ip` | Public IPv4/IPv6 lookup: `known`, `allow` / `review` / `block`, risk score, and nullable signal/network metadata. Public-feed coverage is limited to cloud hosting and Tor. |
|
|
10
10
|
| `service_status` | Maskbreak API health, uptime, and latency |
|
|
11
11
|
|
|
12
12
|
## Setup
|
|
13
13
|
|
|
14
|
-
Grab a free API key at [maskbreak.com/signup](https://maskbreak.com/signup) (1,000 lookups/hour, no card).
|
|
14
|
+
Grab a free API key at [maskbreak.com/signup](https://maskbreak.com/signup) (1,000 lookups/hour, shared with evaluations, no card). Keyless mode uses the free web-tool endpoint, limited to 12 lookups/minute and 80/day per caller IP. Additional endpoint and abuse-protection limits can apply.
|
|
15
|
+
|
|
16
|
+
Neither mode performs a browser visit. VPN/proxy and device evidence require a browser-SDK-backed `/v1/evaluate` request outside these tools; service names are available only when known. Unknown IPs, false signals and `allow` verdicts are not proof of safety.
|
|
15
17
|
|
|
16
18
|
### Claude Code
|
|
17
19
|
|
|
@@ -45,16 +47,18 @@ Prefer a remote server? The same tools are hosted at **`https://maskbreak.com/mc
|
|
|
45
47
|
|
|
46
48
|
## Example
|
|
47
49
|
|
|
48
|
-
> "
|
|
50
|
+
> "What public-feed evidence is available for this IP?"
|
|
51
|
+
|
|
52
|
+
Illustrative unknown-IP response, not a live lookup:
|
|
49
53
|
|
|
50
54
|
```json
|
|
51
55
|
{
|
|
52
|
-
"ip": "
|
|
53
|
-
"known":
|
|
54
|
-
"verdict": "
|
|
55
|
-
"risk_score":
|
|
56
|
-
"signals":
|
|
57
|
-
"network":
|
|
56
|
+
"ip": "192.0.2.1",
|
|
57
|
+
"known": false,
|
|
58
|
+
"verdict": "allow",
|
|
59
|
+
"risk_score": 0,
|
|
60
|
+
"signals": null,
|
|
61
|
+
"network": null
|
|
58
62
|
}
|
|
59
63
|
```
|
|
60
64
|
|
|
@@ -65,6 +69,19 @@ Prefer a remote server? The same tools are hosted at **`https://maskbreak.com/mc
|
|
|
65
69
|
| `SENTINEL_API_KEY` | recommended | — | API key from your [dashboard](https://maskbreak.com/dashboard); unlocks 1,000 lookups/hour |
|
|
66
70
|
| `SENTINEL_BASE_URL` | no | `https://maskbreak.com` | Override for testing |
|
|
67
71
|
|
|
72
|
+
## Failures and local checks
|
|
73
|
+
|
|
74
|
+
Both tools return MCP `isError: true` for HTTP failures, malformed responses or requests that exceed the five-second timeout (including body reads). An unavailable lookup is not an allow decision.
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npm ci --ignore-scripts
|
|
78
|
+
npm test
|
|
79
|
+
npm pack --dry-run --ignore-scripts
|
|
80
|
+
npm audit
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Tests use a real stdio MCP client with loopback HTTP fixtures, without production keys. CI checks Node.js 18, 22 and 24. No package is published by these checks.
|
|
84
|
+
|
|
68
85
|
## Links
|
|
69
86
|
|
|
70
87
|
- [API docs](https://maskbreak.com/api)
|
package/index.js
CHANGED
|
@@ -17,32 +17,55 @@ const { version: VERSION } = JSON.parse(
|
|
|
17
17
|
readFileSync(new URL('./package.json', import.meta.url), 'utf8')
|
|
18
18
|
);
|
|
19
19
|
|
|
20
|
-
const BASE = process.env.SENTINEL_BASE_URL || 'https://maskbreak.com';
|
|
20
|
+
const BASE = (process.env.SENTINEL_BASE_URL || 'https://maskbreak.com').replace(/\/+$/, '');
|
|
21
21
|
const API_KEY = process.env.SENTINEL_API_KEY || '';
|
|
22
|
+
const TIMEOUT_MS = 5000;
|
|
23
|
+
|
|
24
|
+
async function requestJson(path, init = {}, keylessLookup = false) {
|
|
25
|
+
const controller = new AbortController();
|
|
26
|
+
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
27
|
+
try {
|
|
28
|
+
const res = await fetch(`${BASE}${path}`, {
|
|
29
|
+
...init,
|
|
30
|
+
headers: { 'Accept': 'application/json', ...init.headers },
|
|
31
|
+
signal: controller.signal
|
|
32
|
+
});
|
|
33
|
+
let data;
|
|
34
|
+
try {
|
|
35
|
+
data = await res.json();
|
|
36
|
+
} catch (error) {
|
|
37
|
+
if (error.name === 'AbortError') throw error;
|
|
38
|
+
data = null;
|
|
39
|
+
}
|
|
40
|
+
if (!res.ok) {
|
|
41
|
+
const hint = keylessLookup && res.status === 429
|
|
42
|
+
? ' (keyless mode is tightly rate-limited — set SENTINEL_API_KEY for 1,000 lookups/hour; free key at https://maskbreak.com/signup)'
|
|
43
|
+
: '';
|
|
44
|
+
throw new Error(`${data?.error || `HTTP ${res.status}`}${hint}`);
|
|
45
|
+
}
|
|
46
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
|
47
|
+
throw new Error('Invalid JSON response from Maskbreak API');
|
|
48
|
+
}
|
|
49
|
+
return data;
|
|
50
|
+
} catch (error) {
|
|
51
|
+
if (error.name === 'AbortError') throw new Error(`Maskbreak request timed out after ${TIMEOUT_MS}ms`);
|
|
52
|
+
throw error;
|
|
53
|
+
} finally {
|
|
54
|
+
clearTimeout(timer);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
22
57
|
|
|
23
58
|
async function lookupIp(ip) {
|
|
24
|
-
const opts = { headers: { 'Accept': 'application/json' } };
|
|
25
|
-
let res;
|
|
26
59
|
if (API_KEY) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
headers: { ...opts.headers, 'Authorization': `Bearer ${API_KEY}` }
|
|
60
|
+
return requestJson(`/v1/lookup/${encodeURIComponent(ip)}`, {
|
|
61
|
+
headers: { 'Authorization': `Bearer ${API_KEY}` }
|
|
30
62
|
});
|
|
31
|
-
} else {
|
|
32
|
-
res = await fetch(`${BASE}/api/lookup`, {
|
|
33
|
-
method: 'POST',
|
|
34
|
-
headers: { ...opts.headers, 'Content-Type': 'application/json' },
|
|
35
|
-
body: JSON.stringify({ ip })
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
const data = await res.json().catch(() => ({}));
|
|
39
|
-
if (!res.ok) {
|
|
40
|
-
const hint = !API_KEY && res.status === 429
|
|
41
|
-
? ' (keyless mode is tightly rate-limited — set SENTINEL_API_KEY for 1,000 lookups/hour; free key at https://maskbreak.com/signup)'
|
|
42
|
-
: '';
|
|
43
|
-
throw new Error(`${data.error || `HTTP ${res.status}`}${hint}`);
|
|
44
63
|
}
|
|
45
|
-
return
|
|
64
|
+
return requestJson('/api/lookup', {
|
|
65
|
+
method: 'POST',
|
|
66
|
+
headers: { 'Content-Type': 'application/json' },
|
|
67
|
+
body: JSON.stringify({ ip })
|
|
68
|
+
}, true);
|
|
46
69
|
}
|
|
47
70
|
|
|
48
71
|
function text(obj) {
|
|
@@ -53,7 +76,7 @@ const server = new McpServer({ name: 'maskbreak', version: VERSION });
|
|
|
53
76
|
|
|
54
77
|
server.tool(
|
|
55
78
|
'lookup_ip',
|
|
56
|
-
'
|
|
79
|
+
'Look up limited public IP intelligence: cloud-hosting ranges and Tor exit nodes. Returns known, an allow/review/block verdict, a 0-100 risk score, and nullable signals/network metadata. Unknown or false signals do not establish safety. VPN/proxy and device evidence require a browser-SDK-backed visit via /v1/evaluate, which this tool does not perform.',
|
|
57
80
|
{ ip: z.string().describe('Public IPv4 or IPv6 address to check, e.g. "185.220.101.34"') },
|
|
58
81
|
async ({ ip }) => {
|
|
59
82
|
try {
|
|
@@ -70,8 +93,7 @@ server.tool(
|
|
|
70
93
|
{},
|
|
71
94
|
async () => {
|
|
72
95
|
try {
|
|
73
|
-
|
|
74
|
-
return text(await res.json());
|
|
96
|
+
return text(await requestJson('/api/status'));
|
|
75
97
|
} catch (e) {
|
|
76
98
|
return { ...text({ error: e.message }), isError: true };
|
|
77
99
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentinelsup/mcp",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"
|
|
3
|
+
"version": "0.1.4",
|
|
4
|
+
"mcpName": "io.github.sentinelsup/maskbreak-mcp",
|
|
5
|
+
"description": "MCP server for Maskbreak — look up public cloud-hosting and Tor IP signals, with limited reputation evidence and API status, from an MCP client.",
|
|
5
6
|
"keywords": [
|
|
6
7
|
"mcp",
|
|
7
8
|
"model-context-protocol",
|
|
@@ -37,6 +38,9 @@
|
|
|
37
38
|
"engines": {
|
|
38
39
|
"node": ">=18"
|
|
39
40
|
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"test": "node --test test.js"
|
|
43
|
+
},
|
|
40
44
|
"dependencies": {
|
|
41
45
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
42
46
|
"zod": "^3.23.0"
|