Rene Fichtmueller f2470f3e56 feat: initial release — AI-powered network intelligence platform
PeerCortex unifies PeeringDB, RIPE Stat, bgproutes.io, RIPE Atlas,
Route Views, IRR, RPKI, and CAIDA into a single AI-queryable MCP Server
for network engineers. Powered by local Ollama.

Core capabilities:
- 34 MCP tools for network intelligence
- 11 data sources unified
- ASPA validation engine (RFC 9582) with leak detection
- Peering partner discovery with AI-ranked matches
- BGP analysis and anomaly detection
- RPKI monitoring and compliance reports
- Latency/traceroute via RIPE Atlas
- Transit analysis and cost comparison
- IX traffic statistics
- AS topology mapping
- ASPA object generator and simulator
- 100% local AI — no cloud dependencies
2026-03-26 07:26:14 +13:00

609 lines
22 KiB
JavaScript

#!/usr/bin/env node
/**
* @module mcp-server/index
* PeerCortex MCP Server — AI-Powered Network Intelligence Platform
*
* Exposes network intelligence tools via the Model Context Protocol (MCP),
* enabling AI assistants like Claude to query PeeringDB, analyze BGP data,
* monitor RPKI compliance, and find peering partners.
*
* @example Start the server:
* ```bash
* # Via stdio (for Claude Code / MCP clients)
* npx peercortex
*
* # Via SSE (for web clients)
* MCP_TRANSPORT=sse MCP_PORT=3100 npx peercortex
* ```
*/
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { asnLookupSchema, prefixLookupSchema, ixLookupSchema, handleASNLookup, handlePrefixLookup, handleIXLookup } from "./tools/lookup.js";
import { peeringDiscoverSchema, peeringEmailSchema, handlePeeringDiscover, handlePeeringEmail } from "./tools/peering.js";
import { bgpAnalysisSchema, bgpAnomalySchema, routeLeakSchema, handleBGPAnalysis, handleAnomalyDetection, handleRouteLeakDetection } from "./tools/bgp.js";
import { rpkiValidateSchema, rpkiComplianceSchema, rpkiIXCoverageSchema, handleRPKIValidation, handleRPKICompliance, handleRPKIIXCoverage } from "./tools/rpki.js";
import { networkCompareSchema, handleNetworkCompare } from "./tools/compare.js";
import { reportGenerateSchema, handleReportGenerate } from "./tools/report.js";
// Latency tools
import { rttMeasurementSchema, tracerouteSchema, handleRTTMeasurement, handleTraceroute } from "./tools/latency.js";
// Transit tools
import { upstreamAnalysisSchema, transitDiversitySchema, peeringVsTransitSchema, handleUpstreamAnalysis, handleTransitDiversity, handlePeeringVsTransit } from "./tools/transit.js";
// Topology tools
import { asGraphSchema, submarineCableSchema, facilityAnalysisSchema, handleASGraph, handleSubmarineCables, handleFacilityAnalysis } from "./tools/topology.js";
// Traffic tools
import { ixTrafficSchema, ixComparisonSchema, portUtilizationSchema, handleIXTraffic, handleIXComparison, handlePortUtilization } from "./tools/traffic.js";
// Security tools
import { hijackDetectionSchema, routeLeakDetectionSchema, bogonCheckSchema, blacklistCheckSchema, handleHijackDetection, handleRouteLeakDetection as handleRouteLeakDetectionSecurity, handleBogonCheck, handleBlacklistCheck } from "./tools/security.js";
// DNS tools
import { reverseDnsSchema, delegationCheckSchema, whoisLookupSchema, handleReverseDns, handleDelegationCheck, handleWhoisLookup } from "./tools/dns.js";
// Atlas tools
import { createMeasurementSchema, getMeasurementResultsSchema, searchProbesSchema, handleCreateMeasurement, handleGetMeasurementResults, handleSearchProbes } from "./tools/atlas.js";
// ── Server Configuration ─────────────────────────────────
const SERVER_NAME = "peercortex";
const SERVER_VERSION = "0.1.0";
// ── Initialize MCP Server ────────────────────────────────
const server = new McpServer({
name: SERVER_NAME,
version: SERVER_VERSION,
});
// ── Register Tools ───────────────────────────────────────
// Tool 1: Network Lookup
server.tool(
"lookup",
"Look up comprehensive information for an ASN, IP prefix, or Internet Exchange. " +
"Queries PeeringDB, RIPE Stat, bgp.he.net, IRR databases, and RPKI validators.",
{
type: asnLookupSchema.shape.asn._def.description
? { asn: asnLookupSchema.shape.asn }
: asnLookupSchema.shape,
},
async (params) => {
try {
const result = await handleASNLookup(params as { asn: string | number });
return {
content: [
{
type: "text" as const,
text: JSON.stringify(result, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text" as const,
text: `Error: ${error instanceof Error ? error.message : "Unknown error"}`,
},
],
isError: true,
};
}
}
);
// Tool 2: Peering Partner Discovery
server.tool(
"peering",
"Find optimal peering partners for an ASN. Analyzes common IXs, facilities, " +
"peering policies, and network types. Can also draft peering request emails.",
peeringDiscoverSchema.shape,
async (params) => {
try {
const result = await handlePeeringDiscover(
params as { asn: string | number; ix?: string; policy?: "open" | "selective" | "restrictive" | "any"; limit?: number }
);
return {
content: [
{
type: "text" as const,
text: JSON.stringify(result, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text" as const,
text: `Error: ${error instanceof Error ? error.message : "Unknown error"}`,
},
],
isError: true,
};
}
}
);
// Tool 3: BGP Analysis
server.tool(
"bgp",
"Analyze BGP routing for an ASN or prefix. Detects route leaks, BGP hijacks, " +
"MOAS conflicts, and path anomalies. Uses RIPE Stat, Route Views, and bgp.he.net.",
bgpAnalysisSchema.shape,
async (params) => {
try {
const result = await handleBGPAnalysis(
params as { resource: string; include_paths?: boolean; include_anomalies?: boolean; time_range?: string }
);
return {
content: [
{
type: "text" as const,
text: JSON.stringify(result, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text" as const,
text: `Error: ${error instanceof Error ? error.message : "Unknown error"}`,
},
],
isError: true,
};
}
}
);
// Tool 4: RPKI Monitoring
server.tool(
"rpki",
"RPKI validation and compliance monitoring. Validate prefix-origin pairs, " +
"generate compliance reports, and analyze RPKI coverage at Internet Exchanges.",
rpkiComplianceSchema.shape,
async (params) => {
try {
const result = await handleRPKICompliance(
params as { asn: string | number; include_recommendations?: boolean }
);
return {
content: [
{
type: "text" as const,
text: JSON.stringify(result, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text" as const,
text: `Error: ${error instanceof Error ? error.message : "Unknown error"}`,
},
],
isError: true,
};
}
}
);
// Tool 5: Network Comparison
server.tool(
"compare",
"Compare two networks side by side. Shows common/unique IXs, facilities, " +
"peering policies, RPKI deployment, and identifies peering opportunities.",
networkCompareSchema.shape,
async (params) => {
try {
const result = await handleNetworkCompare(
params as { asn1: string | number; asn2: string | number; include_ai_analysis?: boolean }
);
return {
content: [
{
type: "text" as const,
text: JSON.stringify(result, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text" as const,
text: `Error: ${error instanceof Error ? error.message : "Unknown error"}`,
},
],
isError: true,
};
}
}
);
// Tool 6: Report Generation
server.tool(
"report",
"Generate comprehensive network analysis reports. Supports peering readiness, " +
"RPKI compliance, network comparison, BGP health, and IX analysis reports. " +
"Output in Markdown, JSON, or plain text.",
reportGenerateSchema.shape,
async (params) => {
try {
const result = await handleReportGenerate(
params as {
type: "peering_readiness" | "rpki_compliance" | "network_comparison" | "bgp_health" | "ix_analysis";
asn?: string | number;
asn2?: string | number;
ix?: string;
format?: "markdown" | "json" | "text";
}
);
return {
content: [
{
type: "text" as const,
text: result.format === "json"
? JSON.stringify(result, null, 2)
: result.content || JSON.stringify(result, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text" as const,
text: `Error: ${error instanceof Error ? error.message : "Unknown error"}`,
},
],
isError: true,
};
}
}
);
// ── Latency Tools ────────────────────────────────────────
server.tool(
"measure_rtt",
"Measure round-trip time (RTT) to a target using RIPE Atlas probes distributed globally.",
rttMeasurementSchema.shape,
async (params) => {
try {
const result = await handleRTTMeasurement(params as Parameters<typeof handleRTTMeasurement>[0]);
return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
} catch (error) {
return { content: [{ type: "text" as const, text: `Error: ${error instanceof Error ? error.message : "Unknown error"}` }], isError: true };
}
}
);
server.tool(
"traceroute",
"Run a traceroute to a target via RIPE Atlas, annotating each hop with ASN, hostname, and IXP identification.",
tracerouteSchema.shape,
async (params) => {
try {
const result = await handleTraceroute(params as Parameters<typeof handleTraceroute>[0]);
return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
} catch (error) {
return { content: [{ type: "text" as const, text: `Error: ${error instanceof Error ? error.message : "Unknown error"}` }], isError: true };
}
}
);
// ── Transit Tools ────────────────────────────────────────
server.tool(
"upstream_analysis",
"Analyze upstream transit providers for an ASN — identify providers, stability, and single-homed prefixes.",
upstreamAnalysisSchema.shape,
async (params) => {
try {
const result = await handleUpstreamAnalysis(params as Parameters<typeof handleUpstreamAnalysis>[0]);
return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
} catch (error) {
return { content: [{ type: "text" as const, text: `Error: ${error instanceof Error ? error.message : "Unknown error"}` }], isError: true };
}
}
);
server.tool(
"transit_diversity",
"Assess transit diversity and resilience — identify single points of failure and geographic gaps.",
transitDiversitySchema.shape,
async (params) => {
try {
const result = await handleTransitDiversity(params as Parameters<typeof handleTransitDiversity>[0]);
return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
} catch (error) {
return { content: [{ type: "text" as const, text: `Error: ${error instanceof Error ? error.message : "Unknown error"}` }], isError: true };
}
}
);
server.tool(
"peering_vs_transit",
"Compare direct peering vs. transit for reaching a target ASN — cost, latency, and path analysis.",
peeringVsTransitSchema.shape,
async (params) => {
try {
const result = await handlePeeringVsTransit(params as Parameters<typeof handlePeeringVsTransit>[0]);
return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
} catch (error) {
return { content: [{ type: "text" as const, text: `Error: ${error instanceof Error ? error.message : "Unknown error"}` }], isError: true };
}
}
);
// ── Topology Tools ───────────────────────────────────────
server.tool(
"as_graph",
"Generate an AS-level topology graph showing providers, customers, and peers around a center ASN.",
asGraphSchema.shape,
async (params) => {
try {
const result = await handleASGraph(params as Parameters<typeof handleASGraph>[0]);
return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
} catch (error) {
return { content: [{ type: "text" as const, text: `Error: ${error instanceof Error ? error.message : "Unknown error"}` }], isError: true };
}
}
);
server.tool(
"submarine_cables",
"Look up submarine cable information — capacity, owners, landing points, and regional connectivity.",
submarineCableSchema.shape,
async (params) => {
try {
const result = await handleSubmarineCables(params as Parameters<typeof handleSubmarineCables>[0]);
return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
} catch (error) {
return { content: [{ type: "text" as const, text: `Error: ${error instanceof Error ? error.message : "Unknown error"}` }], isError: true };
}
}
);
server.tool(
"facility_analysis",
"Analyze facility/colocation presence for an ASN and find interconnection opportunities with a target.",
facilityAnalysisSchema.shape,
async (params) => {
try {
const result = await handleFacilityAnalysis(params as Parameters<typeof handleFacilityAnalysis>[0]);
return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
} catch (error) {
return { content: [{ type: "text" as const, text: `Error: ${error instanceof Error ? error.message : "Unknown error"}` }], isError: true };
}
}
);
// ── Traffic Tools ────────────────────────────────────────
server.tool(
"ix_traffic",
"Get traffic statistics and trends for an Internet Exchange — peak, average, growth, and history.",
ixTrafficSchema.shape,
async (params) => {
try {
const result = await handleIXTraffic(params as Parameters<typeof handleIXTraffic>[0]);
return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
} catch (error) {
return { content: [{ type: "text" as const, text: `Error: ${error instanceof Error ? error.message : "Unknown error"}` }], isError: true };
}
}
);
server.tool(
"ix_comparison",
"Compare traffic statistics across multiple Internet Exchanges side by side.",
ixComparisonSchema.shape,
async (params) => {
try {
const result = await handleIXComparison(params as Parameters<typeof handleIXComparison>[0]);
return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
} catch (error) {
return { content: [{ type: "text" as const, text: `Error: ${error instanceof Error ? error.message : "Unknown error"}` }], isError: true };
}
}
);
server.tool(
"port_utilization",
"Analyze port utilization for an ASN across its IX connections with upgrade recommendations.",
portUtilizationSchema.shape,
async (params) => {
try {
const result = await handlePortUtilization(params as Parameters<typeof handlePortUtilization>[0]);
return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
} catch (error) {
return { content: [{ type: "text" as const, text: `Error: ${error instanceof Error ? error.message : "Unknown error"}` }], isError: true };
}
}
);
// ── Security Tools ───────────────────────────────────────
server.tool(
"hijack_detection",
"Detect active and historical BGP hijacks for a prefix using RPKI ROV and MOAS analysis.",
hijackDetectionSchema.shape,
async (params) => {
try {
const result = await handleHijackDetection(params as Parameters<typeof handleHijackDetection>[0]);
return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
} catch (error) {
return { content: [{ type: "text" as const, text: `Error: ${error instanceof Error ? error.message : "Unknown error"}` }], isError: true };
}
}
);
server.tool(
"route_leak_detection_aspa",
"Detect route leaks using ASPA validation — identifies unauthorized route propagation via bgproutes.io.",
routeLeakDetectionSchema.shape,
async (params) => {
try {
const result = await handleRouteLeakDetectionSecurity(params as Parameters<typeof handleRouteLeakDetectionSecurity>[0]);
return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
} catch (error) {
return { content: [{ type: "text" as const, text: `Error: ${error instanceof Error ? error.message : "Unknown error"}` }], isError: true };
}
}
);
server.tool(
"bogon_check",
"Check for bogon prefix announcements and bogon ASNs in routing paths.",
bogonCheckSchema.shape,
async (params) => {
try {
const result = await handleBogonCheck(params as Parameters<typeof handleBogonCheck>[0]);
return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
} catch (error) {
return { content: [{ type: "text" as const, text: `Error: ${error instanceof Error ? error.message : "Unknown error"}` }], isError: true };
}
}
);
server.tool(
"blacklist_check",
"Check an IP, prefix, or ASN against known blacklists and reputation databases.",
blacklistCheckSchema.shape,
async (params) => {
try {
const result = await handleBlacklistCheck(params as Parameters<typeof handleBlacklistCheck>[0]);
return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
} catch (error) {
return { content: [{ type: "text" as const, text: `Error: ${error instanceof Error ? error.message : "Unknown error"}` }], isError: true };
}
}
);
// ── DNS Tools ────────────────────────────────────────────
server.tool(
"reverse_dns",
"Perform reverse DNS lookups for IP addresses with optional forward-confirmed verification.",
reverseDnsSchema.shape,
async (params) => {
try {
const result = await handleReverseDns(params as Parameters<typeof handleReverseDns>[0]);
return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
} catch (error) {
return { content: [{ type: "text" as const, text: `Error: ${error instanceof Error ? error.message : "Unknown error"}` }], isError: true };
}
}
);
server.tool(
"delegation_check",
"Check DNS delegation for a domain — nameservers, DNSSEC, glue records, and issues.",
delegationCheckSchema.shape,
async (params) => {
try {
const result = await handleDelegationCheck(params as Parameters<typeof handleDelegationCheck>[0]);
return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
} catch (error) {
return { content: [{ type: "text" as const, text: `Error: ${error instanceof Error ? error.message : "Unknown error"}` }], isError: true };
}
}
);
server.tool(
"whois_lookup",
"Perform a WHOIS lookup for an IP address, ASN, or domain.",
whoisLookupSchema.shape,
async (params) => {
try {
const result = await handleWhoisLookup(params as Parameters<typeof handleWhoisLookup>[0]);
return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
} catch (error) {
return { content: [{ type: "text" as const, text: `Error: ${error instanceof Error ? error.message : "Unknown error"}` }], isError: true };
}
}
);
// ── Atlas Tools ──────────────────────────────────────────
server.tool(
"atlas_create_measurement",
"Create a new RIPE Atlas measurement (ping, traceroute, DNS, SSL, NTP, HTTP).",
createMeasurementSchema.shape,
async (params) => {
try {
const result = await handleCreateMeasurement(params as Parameters<typeof handleCreateMeasurement>[0]);
return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
} catch (error) {
return { content: [{ type: "text" as const, text: `Error: ${error instanceof Error ? error.message : "Unknown error"}` }], isError: true };
}
}
);
server.tool(
"atlas_get_results",
"Get results for a RIPE Atlas measurement with summary statistics.",
getMeasurementResultsSchema.shape,
async (params) => {
try {
const result = await handleGetMeasurementResults(params as Parameters<typeof handleGetMeasurementResults>[0]);
return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
} catch (error) {
return { content: [{ type: "text" as const, text: `Error: ${error instanceof Error ? error.message : "Unknown error"}` }], isError: true };
}
}
);
server.tool(
"atlas_search_probes",
"Search for RIPE Atlas probes by ASN, country, prefix, or anchor status.",
searchProbesSchema.shape,
async (params) => {
try {
const result = await handleSearchProbes(params as Parameters<typeof handleSearchProbes>[0]);
return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
} catch (error) {
return { content: [{ type: "text" as const, text: `Error: ${error instanceof Error ? error.message : "Unknown error"}` }], isError: true };
}
}
);
// ── Start Server ─────────────────────────────────────────
async function main(): Promise<void> {
const transport = process.env.MCP_TRANSPORT ?? "stdio";
if (transport === "stdio") {
const stdioTransport = new StdioServerTransport();
await server.connect(stdioTransport);
console.error(`PeerCortex MCP Server v${SERVER_VERSION} running on stdio`);
} else if (transport === "sse") {
// TODO: Implement SSE transport
// const port = parseInt(process.env.MCP_PORT ?? "3100", 10);
// const sseTransport = new SSEServerTransport({ port });
// await server.connect(sseTransport);
console.error("SSE transport not yet implemented. Use stdio transport.");
process.exit(1);
} else {
console.error(`Unknown transport: ${transport}. Use 'stdio' or 'sse'.`);
process.exit(1);
}
}
main().catch((error) => {
console.error("Fatal error:", error);
process.exit(1);
});