Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 70 additions & 46 deletions src/services/cyberfraudService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ import type {
CyberfraudOverviewResponse,
CyberfraudAccountInfoResponse,
CyberfraudCustomRulesResponse,
TrafficDataInput,
TrafficDataResponse,
TrafficOvertimeInput,
TrafficOvertimeResponse,
TrafficMetricsInput,
TrafficMetricsResponse,
TrafficTopsInput,
TrafficTopsResponse,
} from '../types/cyberfraud';

const API_BASE = `${HUMAN_API_BASE}/cyberfraud`;
Expand All @@ -27,43 +31,40 @@ function buildAttackReportingUrl(endpoint: string, params: Record<string, any>)
return `${API_BASE}/attack-reporting${endpoint}?${queryParams.toString()}`;
}

function buildTrafficDataQuery(params: {
from: number;
to: number;
appId?: string[];
source?: string[];
overtime?: string[];
tops?: string[];
traffic?: string[];
pageType?: string[];
count?: string[];
withoutTotals?: boolean;
metricsEnrichment?: any;
function buildTrafficDashboardUrl(endpoint: string, from: number, to: number) {
const queryParams = new URLSearchParams();
queryParams.append('from', from.toString());
queryParams.append('to', to.toString());
return `${API_BASE}/traffic${endpoint}?${queryParams.toString()}`;
}

function buildTrafficTimeRange(startTime: string, endTime: string) {
const clamped = clampAttackReportingTimes(startTime, endTime);
return {
from: Math.floor(new Date(clamped.startTime).getTime() / 1000),
to: Math.floor(new Date(clamped.endTime).getTime() / 1000),
};
}

function buildTrafficDashboardBody(params: {
trafficSource: string[];
filters?: Record<string, unknown>;
searchQuery?: Record<string, unknown>[];
seriesFields?: string[];
limit?: number;
includeNulls?: boolean;
}) {
const query = new URLSearchParams();
query.append('from', params.from.toString());
query.append('to', params.to.toString());
const sources = params.source && params.source.length > 0 ? params.source : ['web', 'mobile'];
sources.forEach((s) => query.append('source[]', s));
if (params.appId) params.appId.forEach((id) => query.append('appId[]', id));
const allOvertime = [
'legitimate',
'blocked',
'potentialBlock',
'whitelist',
'blacklist',
'goodKnownBots',
'captchaSolved',
];
const overtime = params.overtime && params.overtime.length > 0 ? params.overtime : allOvertime;
overtime.forEach((o) => query.append('overtime[]', o));
if (params.tops) params.tops.forEach((t) => query.append('tops[]', t));
if (params.traffic) params.traffic.forEach((t) => query.append('traffic[]', t));
if (params.pageType) params.pageType.forEach((p) => query.append('pageType[]', p));
if (params.count) params.count.forEach((c) => query.append('count[]', c));
if (params.withoutTotals) query.append('withoutTotals', 'true');
if (params.metricsEnrichment) query.append('metricsEnrichment', JSON.stringify(params.metricsEnrichment));
return `${API_BASE}/traffic-data?${query.toString()}`;
const body: Record<string, unknown> = {
trafficSource: params.trafficSource,
};

if (params.filters) body.filters = params.filters;
if (params.searchQuery) body.searchQuery = params.searchQuery;
if (params.seriesFields) body.seriesFields = params.seriesFields;
if (params.limit !== undefined) body.limit = params.limit;
if (params.includeNulls !== undefined) body.includeNulls = params.includeNulls;

return body;
}

export class CyberfraudService {
Expand Down Expand Up @@ -101,13 +102,36 @@ export class CyberfraudService {
return (await res.json()) as CyberfraudCustomRulesResponse;
}

async getTrafficData(params: TrafficDataInput): Promise<TrafficDataResponse> {
const { startTime, endTime, ...rest } = params;
const clamped = clampAttackReportingTimes(startTime, endTime);
const from = Math.floor(new Date(clamped.startTime).getTime() / 1000);
const to = Math.floor(new Date(clamped.endTime).getTime() / 1000);
const url = buildTrafficDataQuery({ from, to, ...rest });
const res = await this.http.request(url);
return (await res.json()) as TrafficDataResponse;
async getTrafficOvertime(params: TrafficOvertimeInput): Promise<TrafficOvertimeResponse> {
const { startTime, endTime, trafficSource, filters, searchQuery, seriesFields } = params;
const { from, to } = buildTrafficTimeRange(startTime, endTime);
const url = buildTrafficDashboardUrl('/overtime', from, to);
const body = buildTrafficDashboardBody({ trafficSource, filters, searchQuery, seriesFields });
const res = await this.http.request(url, { method: 'POST', body });
return (await res.json()) as TrafficOvertimeResponse;
}

async getTrafficMetrics(params: TrafficMetricsInput): Promise<TrafficMetricsResponse> {
const { startTime, endTime, trafficSource, filters, searchQuery } = params;
const { from, to } = buildTrafficTimeRange(startTime, endTime);
const url = buildTrafficDashboardUrl('/metrics', from, to);
const body = buildTrafficDashboardBody({ trafficSource, filters, searchQuery });
const res = await this.http.request(url, { method: 'POST', body });
return (await res.json()) as TrafficMetricsResponse;
}

async getTrafficTops(params: TrafficTopsInput): Promise<TrafficTopsResponse> {
const { startTime, endTime, field, trafficSource, filters, searchQuery, limit, includeNulls } = params;
const { from, to } = buildTrafficTimeRange(startTime, endTime);
const url = buildTrafficDashboardUrl(`/tops/${encodeURIComponent(field)}`, from, to);
const body = buildTrafficDashboardBody({
trafficSource,
filters,
searchQuery,
limit,
includeNulls,
});
const res = await this.http.request(url, { method: 'POST', body });
return (await res.json()) as TrafficTopsResponse;
}
}
88 changes: 0 additions & 88 deletions src/tools/getTrafficData.ts

This file was deleted.

72 changes: 72 additions & 0 deletions src/tools/getTrafficMetrics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp';
import { TrafficMetricsInputSchema, TrafficMetricsInput, TrafficMetricsOutputSchema } from '../types/cyberfraud';
import { mcpToolHandler } from '../utils/mcpToolHandler';
import { makeStructuredResponseSchema } from '../utils/makeStructuredResponseSchema';
import type { CyberfraudService } from '../services/cyberfraudService';
import { DATE_FORMAT_EXAMPLE_END, DATE_FORMAT_EXAMPLE_START } from '../utils/constants';

export function registerCyberfraudGetTrafficMetrics(server: McpServer, cyberfraudService: CyberfraudService) {
server.registerTool(
'human_get_traffic_metrics',
{
description: `Fetches aggregated traffic totals from HUMAN Security's Cyberfraud Traffic Dashboard API. Use this tool for KPIs, executive summaries, and high-level traffic health checks.

🎯 QUICK DECISION GUIDE:
├── Need totals and KPIs? → Use this tool
├── Need trends over time? → Use human_get_traffic_overtime
├── Need top values for a dimension? → Use human_get_traffic_tops
├── Need security-only totals? → Add filters.trafficTags: ["blocked", "potentialBlock"]
├── Need login-specific totals? → Add filters.pageType: ["login_attempt"]
└── Need multi-platform totals? → Set trafficSource: ["web", "mobile"]

❌ CRITICAL RULES:
• trafficSource is required (["web"], ["mobile"], or both)
• startTime/endTime must be valid ISO 8601 strings
• startTime cannot be after endTime or in the future
• Filters stack multiplicatively and can significantly reduce result scope
• app_id is derived from the API token — do not pass appIds

✅ HIGH-VALUE PATTERNS:

1. EXECUTIVE DASHBOARD:
{"startTime": "${DATE_FORMAT_EXAMPLE_START}", "endTime": "${DATE_FORMAT_EXAMPLE_END}", "trafficSource": ["web", "mobile"]}
→ Total, blocked, legitimate, and known bot counts

2. SECURITY KPIs:
{"startTime": "${DATE_FORMAT_EXAMPLE_START}", "endTime": "${DATE_FORMAT_EXAMPLE_END}", "trafficSource": ["web"], "filters": {"trafficTags": ["blocked", "blacklist", "potentialBlock"]}}
→ Threat-focused totals for the period

3. LOGIN SECURITY SUMMARY:
{"startTime": "${DATE_FORMAT_EXAMPLE_START}", "endTime": "${DATE_FORMAT_EXAMPLE_END}", "trafficSource": ["web"], "filters": {"pageType": ["login_attempt"]}}
→ Login journey traffic totals

⚠️ TECHNICAL INSIGHTS:
• Returns aggregated totals for the full requested time range
• content.results includes total, totalBlocked, legitimate, blocked, goodKnownBots, web, mobile, and more
• content.labels provides human-readable labels for metrics
• Best for single-number KPI reporting rather than charts

🚀 OPTIMAL WORKFLOWS:

1. EXECUTIVE REPORTING:
- Query broad trafficSource coverage
- Compare total vs totalBlocked vs legitimate
- Use labels for presentation-ready output

2. SECURITY HEALTH CHECK:
- Filter to blocked/potentialBlock/blacklist tags
- Compare against unfiltered totals from a separate call
- Focus on login or checkout pageType when relevant

Response provides aggregated traffic intelligence optimized for KPI dashboards, executive summaries, and quick health assessments.`,
inputSchema: TrafficMetricsInputSchema.shape,
outputSchema: makeStructuredResponseSchema(TrafficMetricsOutputSchema).shape,
annotations: {
title: 'HUMAN Get Traffic Metrics',
readOnlyHint: true,
openWorldHint: true,
},
},
async (params: TrafficMetricsInput) => mcpToolHandler(async () => cyberfraudService.getTrafficMetrics(params)),
);
}
73 changes: 73 additions & 0 deletions src/tools/getTrafficOvertime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp';
import { TrafficOvertimeInputSchema, TrafficOvertimeInput, TrafficOvertimeOutputSchema } from '../types/cyberfraud';
import { mcpToolHandler } from '../utils/mcpToolHandler';
import { makeStructuredResponseSchema } from '../utils/makeStructuredResponseSchema';
import type { CyberfraudService } from '../services/cyberfraudService';
import { DATE_FORMAT_EXAMPLE_END, DATE_FORMAT_EXAMPLE_START } from '../utils/constants';

export function registerCyberfraudGetTrafficOvertime(server: McpServer, cyberfraudService: CyberfraudService) {
server.registerTool(
'human_get_traffic_overtime',
{
description: `Fetches minute-bucketed traffic overtime data from HUMAN Security's Cyberfraud Traffic Dashboard API. Use this tool for trend charts, attack timelines, and time-series security analysis.

🎯 QUICK DECISION GUIDE:
├── Need trend charts over time? → Use this tool
├── Need high-level totals only? → Use human_get_traffic_metrics
├── Need top values for a dimension? → Use human_get_traffic_tops
├── Need attack type breakdown over time? → Add seriesFields: ["knownBot"] or filters.trafficTags
├── Need login-focused timeline? → Add filters.pageType: ["login_attempt"]
└── Need multi-platform view? → Set trafficSource: ["web", "mobile"]

❌ CRITICAL RULES:
• trafficSource is required (["web"], ["mobile"], or both)
• startTime/endTime must be valid ISO 8601 strings
• startTime cannot be after endTime or in the future
• Filters stack multiplicatively and can significantly reduce result scope
• app_id is derived from the API token — do not pass appIds

✅ HIGH-VALUE PATTERNS:

1. SECURITY TIMELINE:
{"startTime": "${DATE_FORMAT_EXAMPLE_START}", "endTime": "${DATE_FORMAT_EXAMPLE_END}", "trafficSource": ["web", "mobile"], "filters": {"trafficTags": ["blocked", "potentialBlock"]}}
→ Blocked traffic volume over time

2. KNOWN BOT BREAKDOWN:
{"startTime": "${DATE_FORMAT_EXAMPLE_START}", "endTime": "${DATE_FORMAT_EXAMPLE_END}", "trafficSource": ["web"], "filters": {"trafficTags": ["goodKnownBots"]}, "seriesFields": ["knownBot"]}
→ Known bot traffic split by bot name over time

3. LOGIN ATTEMPT MONITORING:
{"startTime": "${DATE_FORMAT_EXAMPLE_START}", "endTime": "${DATE_FORMAT_EXAMPLE_END}", "trafficSource": ["web"], "filters": {"pageType": ["login_attempt"], "trafficTags": ["blocked"]}}
→ Login attack timeline

⚠️ TECHNICAL INSIGHTS:
• Returns one row per minute with traffic tag counts
• seriesFields adds extra per-value columns in each minute bucket
• Response uses { result, message, content.results[] } envelope
• Longer time ranges produce more minute buckets

🚀 OPTIMAL WORKFLOWS:

1. INCIDENT TIMELINE:
- Start with a focused 6-24 hour window
- Filter to blocked/potentialBlock trafficTags
- Expand window if more context is needed

2. BOT TRAFFIC ANALYSIS:
- Filter to goodKnownBots
- Add seriesFields: ["knownBot"]
- Compare bot families over time

Response provides minute-level traffic intelligence optimized for dashboards, SOC monitoring, and executive trend reporting.`,
inputSchema: TrafficOvertimeInputSchema.shape,
outputSchema: makeStructuredResponseSchema(TrafficOvertimeOutputSchema).shape,
annotations: {
title: 'HUMAN Get Traffic Overtime',
readOnlyHint: true,
openWorldHint: true,
},
},
async (params: TrafficOvertimeInput) =>
mcpToolHandler(async () => cyberfraudService.getTrafficOvertime(params)),
);
}
Loading