web-intel-mcp 0.2.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 +74 -0
- package/index.js +294 -0
- package/package.json +23 -0
package/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# web-intel-mcp
|
|
2
|
+
|
|
3
|
+
MCP server that gives AI agents web intelligence capabilities. DNS lookups, SSL checks, tech stack detection, page metadata, link safety analysis — all in one package.
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
AI agents can't do DNS lookups, read SSL certificates, or detect web technologies natively. This MCP server adds those capabilities with zero configuration.
|
|
8
|
+
|
|
9
|
+
## Tools
|
|
10
|
+
|
|
11
|
+
| Tool | What it does |
|
|
12
|
+
|---|---|
|
|
13
|
+
| `web_investigate` | Complete website investigation — DNS, SSL, tech stack, metadata, link safety in one call |
|
|
14
|
+
| `web_dns` | DNS records (A, AAAA, MX, NS, TXT, CNAME) + mail provider detection |
|
|
15
|
+
| `web_ssl` | SSL certificate details — issuer, expiry, validity, SANs |
|
|
16
|
+
| `web_tech_stack` | Detect frameworks, CMS, analytics, CDN, hosting, security headers |
|
|
17
|
+
| `web_meta` | Page title, description, Open Graph, Twitter cards, canonical URL |
|
|
18
|
+
| `web_link_safety` | URL safety verdict — safe/suspicious/dangerous with risk score |
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
### Claude Code
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
claude mcp add web-intel node /path/to/web-intel-mcp/index.js
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Or add to `.mcp.json`:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"mcpServers": {
|
|
33
|
+
"web-intel": {
|
|
34
|
+
"command": "node",
|
|
35
|
+
"args": ["/path/to/web-intel-mcp/index.js"]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### npx (no install)
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"mcpServers": {
|
|
46
|
+
"web-intel": {
|
|
47
|
+
"command": "npx",
|
|
48
|
+
"args": ["web-intel-mcp"]
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Examples
|
|
55
|
+
|
|
56
|
+
**"Check if this website is legit"**
|
|
57
|
+
→ Agent calls `web_investigate` → gets trust score, SSL status, tech stack, safety signals
|
|
58
|
+
|
|
59
|
+
**"What DNS records does example.com have?"**
|
|
60
|
+
→ Agent calls `web_dns` → gets A, MX, NS records + mail provider
|
|
61
|
+
|
|
62
|
+
**"Is this link safe to click?"**
|
|
63
|
+
→ Agent calls `web_link_safety` → gets safe/suspicious/dangerous verdict
|
|
64
|
+
|
|
65
|
+
**"What framework is this site built with?"**
|
|
66
|
+
→ Agent calls `web_tech_stack` → gets Next.js, React, Cloudflare, etc.
|
|
67
|
+
|
|
68
|
+
## No API keys needed
|
|
69
|
+
|
|
70
|
+
Everything runs locally using Node.js built-in `dns` and `https` modules. No external API dependencies. No rate limits. No costs.
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
MIT
|
package/index.js
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// x402 Web Intel — MCP Server
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Standalone MCP server that gives any AI agent web intelligence capabilities.
|
|
7
|
+
// No API keys. No platform dependency. Just add to your MCP config and go.
|
|
8
|
+
//
|
|
9
|
+
// Tools:
|
|
10
|
+
// web_investigate — Complete website investigation (DNS, SSL, tech, meta, safety, carbon)
|
|
11
|
+
// web_dns — DNS records + mail provider detection
|
|
12
|
+
// web_ssl — SSL certificate details
|
|
13
|
+
// web_tech_stack — Detect web technologies and security headers
|
|
14
|
+
// web_meta — Page metadata, OG tags, Twitter cards
|
|
15
|
+
// web_link_safety — URL safety verdict (safe/suspicious/dangerous)
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
19
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
import dns from "node:dns/promises";
|
|
22
|
+
import https from "node:https";
|
|
23
|
+
|
|
24
|
+
const server = new McpServer({
|
|
25
|
+
name: "web-intel",
|
|
26
|
+
version: "0.2.0",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Core functions (self-contained, no external API dependency)
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
async function dnsLookup(domain) {
|
|
34
|
+
const [a, aaaa, mx, ns, txt, cname] = await Promise.allSettled([
|
|
35
|
+
dns.resolve(domain, "A"), dns.resolve(domain, "AAAA"),
|
|
36
|
+
dns.resolve(domain, "MX"), dns.resolve(domain, "NS"),
|
|
37
|
+
dns.resolve(domain, "TXT"), dns.resolve(domain, "CNAME"),
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
const mxRecords = mx.status === "fulfilled" ? mx.value : [];
|
|
41
|
+
const mxStr = mxRecords.map((r) => r.exchange).join(" ").toLowerCase();
|
|
42
|
+
let mailProvider = null;
|
|
43
|
+
if (mxStr.includes("google") || mxStr.includes("gmail")) mailProvider = "Google Workspace";
|
|
44
|
+
else if (mxStr.includes("outlook") || mxStr.includes("microsoft")) mailProvider = "Microsoft 365";
|
|
45
|
+
else if (mxStr.includes("protonmail")) mailProvider = "ProtonMail";
|
|
46
|
+
else if (mxStr.includes("zoho")) mailProvider = "Zoho Mail";
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
A: a.status === "fulfilled" ? a.value : [],
|
|
50
|
+
AAAA: aaaa.status === "fulfilled" ? aaaa.value : [],
|
|
51
|
+
MX: mxRecords.sort((a, b) => a.priority - b.priority),
|
|
52
|
+
NS: ns.status === "fulfilled" ? ns.value : [],
|
|
53
|
+
TXT: txt.status === "fulfilled" ? txt.value.map((t) => t.join("")) : [],
|
|
54
|
+
CNAME: cname.status === "fulfilled" ? cname.value : [],
|
|
55
|
+
mail_provider: mailProvider,
|
|
56
|
+
has_ipv6: aaaa.status === "fulfilled" && aaaa.value.length > 0,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function sslCheck(domain) {
|
|
61
|
+
return new Promise((resolve, reject) => {
|
|
62
|
+
const req = https.request({ hostname: domain, port: 443, method: "HEAD", timeout: 10000 }, (res) => {
|
|
63
|
+
const cert = res.socket.getPeerCertificate();
|
|
64
|
+
const now = new Date();
|
|
65
|
+
const validFrom = new Date(cert.valid_from);
|
|
66
|
+
const validTo = new Date(cert.valid_to);
|
|
67
|
+
const daysRemaining = Math.floor((validTo - now) / 86400000);
|
|
68
|
+
|
|
69
|
+
resolve({
|
|
70
|
+
issuer: cert.issuer?.O || cert.issuer?.CN || "Unknown",
|
|
71
|
+
subject: cert.subject?.CN || domain,
|
|
72
|
+
valid_from: validFrom.toISOString(),
|
|
73
|
+
valid_to: validTo.toISOString(),
|
|
74
|
+
days_remaining: daysRemaining,
|
|
75
|
+
is_valid: daysRemaining > 0,
|
|
76
|
+
is_expiring_soon: daysRemaining > 0 && daysRemaining < 30,
|
|
77
|
+
san: cert.subjectaltname ? cert.subjectaltname.split(", ").map((s) => s.replace("DNS:", "")) : [],
|
|
78
|
+
});
|
|
79
|
+
res.destroy();
|
|
80
|
+
});
|
|
81
|
+
req.on("error", reject);
|
|
82
|
+
req.on("timeout", () => { req.destroy(); reject(new Error("timeout")); });
|
|
83
|
+
req.end();
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function techStack(url) {
|
|
88
|
+
const response = await fetch(url, {
|
|
89
|
+
signal: AbortSignal.timeout(10_000),
|
|
90
|
+
headers: { "User-Agent": "Mozilla/5.0 (compatible; web-intel-mcp/0.2)" },
|
|
91
|
+
redirect: "follow",
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const headers = Object.fromEntries(response.headers.entries());
|
|
95
|
+
const html = await response.text();
|
|
96
|
+
const head = html.slice(0, 15000).toLowerCase();
|
|
97
|
+
const techs = [];
|
|
98
|
+
|
|
99
|
+
if (headers.server) techs.push({ name: headers.server, category: "server" });
|
|
100
|
+
if (headers["x-powered-by"]) techs.push({ name: headers["x-powered-by"], category: "framework" });
|
|
101
|
+
if (head.includes("__next")) techs.push({ name: "Next.js", category: "framework" });
|
|
102
|
+
if (head.includes("__nuxt")) techs.push({ name: "Nuxt.js", category: "framework" });
|
|
103
|
+
if (head.includes("wp-content") || head.includes("wordpress")) techs.push({ name: "WordPress", category: "cms" });
|
|
104
|
+
if (head.includes("shopify")) techs.push({ name: "Shopify", category: "ecommerce" });
|
|
105
|
+
if (head.includes("wix.com")) techs.push({ name: "Wix", category: "builder" });
|
|
106
|
+
if (head.includes("google-analytics") || head.includes("gtag")) techs.push({ name: "Google Analytics", category: "analytics" });
|
|
107
|
+
if (head.includes("gtm.js") || head.includes("googletagmanager")) techs.push({ name: "GTM", category: "analytics" });
|
|
108
|
+
if (headers["cf-ray"] || headers.server?.includes("cloudflare")) techs.push({ name: "Cloudflare", category: "cdn" });
|
|
109
|
+
if (headers["x-vercel-id"]) techs.push({ name: "Vercel", category: "hosting" });
|
|
110
|
+
if (headers["fly-request-id"]) techs.push({ name: "Fly.io", category: "hosting" });
|
|
111
|
+
if (head.includes("tailwind")) techs.push({ name: "Tailwind", category: "css" });
|
|
112
|
+
if (head.includes("bootstrap")) techs.push({ name: "Bootstrap", category: "css" });
|
|
113
|
+
if (head.includes("react")) techs.push({ name: "React", category: "js" });
|
|
114
|
+
if (head.includes("vue")) techs.push({ name: "Vue.js", category: "js" });
|
|
115
|
+
if (head.includes("jquery")) techs.push({ name: "jQuery", category: "js" });
|
|
116
|
+
|
|
117
|
+
const security = {};
|
|
118
|
+
if (headers["strict-transport-security"]) security.hsts = true;
|
|
119
|
+
if (headers["content-security-policy"]) security.csp = true;
|
|
120
|
+
if (headers["x-frame-options"]) security.xframe = headers["x-frame-options"];
|
|
121
|
+
|
|
122
|
+
return { technologies: techs, security_headers: security, html_size: html.length };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function pageMeta(url) {
|
|
126
|
+
const response = await fetch(url, {
|
|
127
|
+
signal: AbortSignal.timeout(10_000),
|
|
128
|
+
headers: { "User-Agent": "Mozilla/5.0 (compatible; web-intel-mcp/0.2)" },
|
|
129
|
+
redirect: "follow",
|
|
130
|
+
});
|
|
131
|
+
const html = await response.text();
|
|
132
|
+
const head = html.match(/<head[^>]*>([\s\S]*?)<\/head>/i)?.[1] || html.slice(0, 15000);
|
|
133
|
+
|
|
134
|
+
const getMeta = (name) => {
|
|
135
|
+
const m = head.match(new RegExp(`<meta[^>]*(?:name|property)=["']${name}["'][^>]*content=["']([^"']+)["']`, "i"))
|
|
136
|
+
|| head.match(new RegExp(`<meta[^>]*content=["']([^"']+)["'][^>]*(?:name|property)=["']${name}["']`, "i"));
|
|
137
|
+
return m?.[1] || null;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
title: head.match(/<title[^>]*>([^<]+)<\/title>/i)?.[1]?.trim() || null,
|
|
142
|
+
description: getMeta("description"),
|
|
143
|
+
og_title: getMeta("og:title"),
|
|
144
|
+
og_description: getMeta("og:description"),
|
|
145
|
+
og_image: getMeta("og:image"),
|
|
146
|
+
og_type: getMeta("og:type"),
|
|
147
|
+
twitter_card: getMeta("twitter:card"),
|
|
148
|
+
canonical: head.match(/<link[^>]*rel=["']canonical["'][^>]*href=["']([^"']+)["']/i)?.[1] || null,
|
|
149
|
+
language: html.match(/<html[^>]*lang=["']([^"']+)["']/i)?.[1] || null,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function linkSafety(url) {
|
|
154
|
+
const signals = [];
|
|
155
|
+
let riskScore = 0;
|
|
156
|
+
const parsed = new URL(url);
|
|
157
|
+
|
|
158
|
+
if (parsed.protocol !== "https:") { signals.push("no-https"); riskScore += 30; }
|
|
159
|
+
const badTlds = [".tk", ".ml", ".ga", ".cf", ".gq", ".xyz", ".top", ".click", ".loan"];
|
|
160
|
+
if (badTlds.some((t) => parsed.hostname.endsWith(t))) { signals.push("suspicious-tld"); riskScore += 20; }
|
|
161
|
+
if (parsed.hostname.match(/^\d+\.\d+\.\d+\.\d+$/)) { signals.push("ip-address"); riskScore += 15; }
|
|
162
|
+
if (url.length > 200) { signals.push("long-url"); riskScore += 10; }
|
|
163
|
+
if (parsed.hostname.split(".").length > 5) { signals.push("deep-subdomains"); riskScore += 15; }
|
|
164
|
+
|
|
165
|
+
const verdict = riskScore >= 50 ? "dangerous" : riskScore >= 25 ? "suspicious" : "safe";
|
|
166
|
+
return { verdict, risk_score: Math.max(0, Math.min(100, riskScore)), signals };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
// MCP Tools
|
|
171
|
+
// ---------------------------------------------------------------------------
|
|
172
|
+
|
|
173
|
+
server.tool(
|
|
174
|
+
"web_investigate",
|
|
175
|
+
"Complete website investigation — DNS, SSL, tech stack, metadata, link safety. One call gives you everything about a website. Use this when you need to understand, verify, or analyze any website.",
|
|
176
|
+
{ url: z.string().describe("Full URL to investigate (e.g. 'https://example.com')") },
|
|
177
|
+
async ({ url }) => {
|
|
178
|
+
const domain = new URL(url).hostname;
|
|
179
|
+
|
|
180
|
+
const [dnsResult, sslResult, techResult, metaResult] = await Promise.allSettled([
|
|
181
|
+
dnsLookup(domain),
|
|
182
|
+
sslCheck(domain),
|
|
183
|
+
techStack(url),
|
|
184
|
+
pageMeta(url),
|
|
185
|
+
]);
|
|
186
|
+
|
|
187
|
+
const dnsData = dnsResult.status === "fulfilled" ? dnsResult.value : null;
|
|
188
|
+
const sslData = sslResult.status === "fulfilled" ? sslResult.value : null;
|
|
189
|
+
const techData = techResult.status === "fulfilled" ? techResult.value : null;
|
|
190
|
+
const metaData = metaResult.status === "fulfilled" ? metaResult.value : null;
|
|
191
|
+
const safety = linkSafety(url);
|
|
192
|
+
|
|
193
|
+
// Trust scoring
|
|
194
|
+
let trust = 50;
|
|
195
|
+
const flags = [];
|
|
196
|
+
if (safety.verdict === "dangerous") { trust -= 30; flags.push("Dangerous URL signals"); }
|
|
197
|
+
else if (safety.verdict === "safe") trust += 10;
|
|
198
|
+
if (sslData?.is_valid) trust += 10; else if (sslData && !sslData.is_valid) { trust -= 20; flags.push("Invalid SSL"); }
|
|
199
|
+
if (sslData?.is_expiring_soon) flags.push(`SSL expires in ${sslData.days_remaining}d`);
|
|
200
|
+
if (dnsData?.A?.length === 0) { trust -= 15; flags.push("No DNS A records"); }
|
|
201
|
+
if (dnsData?.mail_provider) trust += 5;
|
|
202
|
+
if (metaData?.title) trust += 5; else flags.push("No page title");
|
|
203
|
+
trust = Math.max(0, Math.min(100, trust));
|
|
204
|
+
|
|
205
|
+
const verdict = trust >= 70 ? "trustworthy" : trust >= 40 ? "questionable" : "risky";
|
|
206
|
+
|
|
207
|
+
const report = `## ${domain} — ${verdict} (${trust}/100)
|
|
208
|
+
${flags.length > 0 ? `**Flags:** ${flags.join(", ")}\n` : ""}
|
|
209
|
+
### DNS
|
|
210
|
+
- IP: ${dnsData?.A?.[0] || "unknown"}
|
|
211
|
+
- Nameservers: ${dnsData?.NS?.join(", ") || "unknown"}
|
|
212
|
+
- Mail: ${dnsData?.mail_provider || "unknown"}
|
|
213
|
+
- IPv6: ${dnsData?.has_ipv6 ? "yes" : "no"}
|
|
214
|
+
|
|
215
|
+
### SSL
|
|
216
|
+
- Issuer: ${sslData?.issuer || "unknown"}
|
|
217
|
+
- Expires: ${sslData?.days_remaining ?? "?"}d (${sslData?.is_valid ? "valid" : "INVALID"})
|
|
218
|
+
- SANs: ${sslData?.san?.slice(0, 5).join(", ") || "unknown"}
|
|
219
|
+
|
|
220
|
+
### Technology
|
|
221
|
+
${techData?.technologies?.map((t) => `- ${t.name} (${t.category})`).join("\n") || "- Unknown"}
|
|
222
|
+
|
|
223
|
+
### Page
|
|
224
|
+
- Title: ${metaData?.title || "none"}
|
|
225
|
+
- Description: ${metaData?.description?.slice(0, 100) || "none"}
|
|
226
|
+
- Language: ${metaData?.language || "unknown"}
|
|
227
|
+
|
|
228
|
+
### Safety
|
|
229
|
+
- Verdict: ${safety.verdict} (risk ${safety.risk_score}/100)
|
|
230
|
+
${safety.signals.length > 0 ? safety.signals.map((s) => `- ${s}`).join("\n") : "- No issues detected"}`;
|
|
231
|
+
|
|
232
|
+
return { content: [{ type: "text", text: report }] };
|
|
233
|
+
},
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
server.tool(
|
|
237
|
+
"web_dns",
|
|
238
|
+
"Get DNS records for any domain — A, AAAA, MX, NS, TXT, CNAME. Also detects mail provider (Google Workspace, Microsoft 365, etc). Use when you need to check domain configuration or troubleshoot DNS issues.",
|
|
239
|
+
{ domain: z.string().describe("Domain name (e.g. 'example.com')") },
|
|
240
|
+
async ({ domain }) => {
|
|
241
|
+
const result = await dnsLookup(domain);
|
|
242
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
243
|
+
},
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
server.tool(
|
|
247
|
+
"web_ssl",
|
|
248
|
+
"Check SSL certificate for any domain — issuer, expiry, validity, SANs. Use when you need to verify HTTPS configuration or check if a certificate is expiring.",
|
|
249
|
+
{ domain: z.string().describe("Domain name (e.g. 'example.com')") },
|
|
250
|
+
async ({ domain }) => {
|
|
251
|
+
const result = await sslCheck(domain);
|
|
252
|
+
const text = `${domain}: ${result.issuer}, ${result.days_remaining}d remaining, ${result.is_valid ? "valid" : "INVALID"}${result.is_expiring_soon ? " ⚠ EXPIRING SOON" : ""}\nSANs: ${result.san.join(", ")}`;
|
|
253
|
+
return { content: [{ type: "text", text }] };
|
|
254
|
+
},
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
server.tool(
|
|
258
|
+
"web_tech_stack",
|
|
259
|
+
"Detect technologies used by any website — frameworks, CMS, analytics, CDN, hosting, CSS, JS libraries, security headers. Use when you need to understand what a website is built with.",
|
|
260
|
+
{ url: z.string().describe("Full URL (e.g. 'https://example.com')") },
|
|
261
|
+
async ({ url }) => {
|
|
262
|
+
const result = await techStack(url);
|
|
263
|
+
const lines = result.technologies.map((t) => `- ${t.name} (${t.category})`);
|
|
264
|
+
if (Object.keys(result.security_headers).length > 0) {
|
|
265
|
+
lines.push("", "Security headers:", ...Object.entries(result.security_headers).map(([k, v]) => `- ${k}: ${v}`));
|
|
266
|
+
}
|
|
267
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
268
|
+
},
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
server.tool(
|
|
272
|
+
"web_meta",
|
|
273
|
+
"Extract page metadata — title, description, Open Graph tags, Twitter cards, canonical URL, language. Use for SEO analysis or understanding page content without rendering.",
|
|
274
|
+
{ url: z.string().describe("Full URL (e.g. 'https://example.com')") },
|
|
275
|
+
async ({ url }) => {
|
|
276
|
+
const result = await pageMeta(url);
|
|
277
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
278
|
+
},
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
server.tool(
|
|
282
|
+
"web_link_safety",
|
|
283
|
+
"Check if a URL is safe — detects suspicious TLDs, missing HTTPS, IP addresses, unusual patterns. Returns safe/suspicious/dangerous verdict. Use before recommending links to users.",
|
|
284
|
+
{ url: z.string().describe("URL to check (e.g. 'https://example.com')") },
|
|
285
|
+
async ({ url }) => {
|
|
286
|
+
const result = linkSafety(url);
|
|
287
|
+
const text = `${url}: ${result.verdict} (risk ${result.risk_score}/100)${result.signals.length > 0 ? "\nSignals: " + result.signals.join(", ") : ""}`;
|
|
288
|
+
return { content: [{ type: "text", text }] };
|
|
289
|
+
},
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
// Start
|
|
293
|
+
const transport = new StdioServerTransport();
|
|
294
|
+
await server.connect(transport);
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "web-intel-mcp",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "MCP server for web intelligence — DNS, SSL, tech stack, metadata, link safety. One tool to investigate any website. No API keys needed.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"web-intel-mcp": "./index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": ["index.js", "README.md"],
|
|
10
|
+
"keywords": ["mcp", "mcp-server", "web-intel", "dns", "ssl", "tech-stack", "security", "claude-code", "model-context-protocol"],
|
|
11
|
+
"author": "Tedysek01",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/Tedysek01/web-intel-mcp"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@modelcontextprotocol/sdk": "^1.12.1"
|
|
19
|
+
},
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=18"
|
|
22
|
+
}
|
|
23
|
+
}
|