Skip to content

Commit e82efae

Browse files
committed
Add DNS and email Radar tools
1 parent 2c9cae3 commit e82efae

File tree

4 files changed

+328
-91
lines changed

4 files changed

+328
-91
lines changed

apps/radar/README.md

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,23 @@ Internet traffic insights, trends and other utilities.
1010

1111
Currently available tools:
1212

13-
| **Category** | **Tool** | **Description** |
14-
| ---------------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
15-
| **HTTP Requests** | `get_http_requests_data` | Fetches HTTP request data (timeseries, summaries, and grouped timeseries across dimensions like `deviceType`, `botClass`) |
16-
| **Layer 7 Attacks** | `get_l7_attack_data` | Fetches L7 attack data (timeseries, summaries, and grouped timeseries across dimensions like `mitigationProduct`, `ipVersion`) |
17-
| **Layer 3 Attacks** | `get_l3_attack_data` | Fetches L3 attack data (timeseries, summaries, and grouped timeseries across dimensions like `protocol`, `duration`) |
18-
| **Internet Speed** | `get_internet_speed_data` | Retrieve summary of bandwidth, latency, jitter, and packet loss, from the previous 90 days of Cloudflare Speed Test. |
19-
| **Autonomous Systems** | `list_autonomous_systems` | Lists ASes; filter by location and sort by population size |
20-
| | `get_as_details` | Retrieves detailed info for a specific ASN |
21-
| **IP Addresses** | `get_ip_details` | Provides details about a specific IP address |
22-
| **Traffic Anomalies** | `get_traffic_anomalies` | Lists traffic anomalies; filter by AS, location, start date, and end date |
23-
| **Domains** | `get_domains_ranking` | Get top or trending domains |
24-
| | `get_domain_rank_details` | Get domain rank details |
25-
| **Internet Services** | `get_internet_services_ranking` | Get top Internet services |
26-
| **URL Scanner** | `scan_url` | Scans a URL via [Cloudflare’s URL Scanner](https://developers.cloudflare.com/radar/investigate/url-scanner/) |
13+
| **Category** | **Tool** | **Description** |
14+
| ---------------------- | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
15+
| **Autonomous Systems** | `list_autonomous_systems` | Lists ASes; filter by location and sort by population size |
16+
| | `get_as_details` | Retrieves detailed info for a specific ASN |
17+
| **Domains** | `get_domains_ranking` | Gets top or trending domains |
18+
| | `get_domain_rank_details` | Gets domain rank details |
19+
| **DNS** | `get_dns_data` | Retrieves DNS query data to 1.1.1.1, including timeseries, summaries, and breakdowns by dimensions like `queryType` or `responseCode`. |
20+
| **Email Routing** | `get_email_routing_data` | Retrieves Email Routing data, including timeseries, and breakdowns by dimensions like `encrypted` or `arc`. |
21+
| **Email Security** | `get_email_security_data` | Retrieves Email Security data, including timeseries, and breakdowns by dimensions like `spam` or `threatCategory`. |
22+
| **HTTP** | `get_http_data` | Retrieves HTTP request data, including timeseries, and breakdowns by dimensions like `deviceType` or `botClass`. |
23+
| **IP Addresses** | `get_ip_details` | Provides details about a specific IP address |
24+
| **Internet Services** | `get_internet_services_ranking` | Gets top Internet services |
25+
| **Internet Speed** | `get_internet_speed_data` | Retrieves summary of bandwidth, latency, jitter, and packet loss, from the previous 90 days of Cloudflare Speed Test. |
26+
| **Layer 3 Attacks** | `get_l3_attack_data` | Retrieves L3 attack data, including timeseries, summaries, and breakdowns by dimensions like `protocol` or `duration`. |
27+
| **Layer 7 Attacks** | `get_l7_attack_data` | Retrieves L7 attack data, including timeseries, summaries, and breakdowns by dimensions like `mitigationProduct` or `target location`. |
28+
| **Traffic Anomalies** | `get_traffic_anomalies` | Lists traffic anomalies and outages; filter by AS, location, start date, and end date |
29+
| **URL Scanner** | `scan_url` | Scans a URL via [Cloudflare’s URL Scanner](https://developers.cloudflare.com/radar/investigate/url-scanner/) |
2730

2831
This MCP server is still a work in progress, and we plan to add more tools in the future.
2932

apps/radar/src/tools/radar.ts

Lines changed: 150 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,30 @@ import {
66
AsnParam,
77
AsOrderByParam,
88
ContinentArrayParam,
9-
DataFormatParam,
109
DateEndArrayParam,
1110
DateEndParam,
1211
DateListParam,
1312
DateRangeArrayParam,
1413
DateRangeParam,
1514
DateStartArrayParam,
1615
DateStartParam,
16+
DnsDimensionParam,
1717
DomainParam,
1818
DomainRankingTypeParam,
19+
EmailRoutingDimensionParam,
20+
EmailSecurityDimensionParam,
1921
HttpDimensionParam,
2022
InternetServicesCategoryParam,
23+
InternetSpeedDimensionParam,
24+
InternetSpeedOrderByParam,
2125
IpParam,
2226
L3AttackDimensionParam,
2327
L7AttackDimensionParam,
2428
LocationArrayParam,
2529
LocationListParam,
2630
LocationParam,
2731
} from '../types/radar'
32+
import { resolveAndInvoke } from '../utils'
2833

2934
import type { RadarMCP } from '../index'
3035

@@ -306,31 +311,68 @@ export function registerRadarTools(agent: RadarMCP) {
306311
)
307312

308313
agent.server.tool(
309-
'get_http_requests_data',
310-
'Retrieve HTTP requests traffic trends.',
314+
'get_http_data',
315+
'Retrieve HTTP traffic trends.',
311316
{
312317
dateRange: DateRangeArrayParam.optional(),
313318
dateStart: DateStartArrayParam.optional(),
314319
dateEnd: DateEndArrayParam.optional(),
315320
asn: AsnArrayParam,
316321
continent: ContinentArrayParam,
317322
location: LocationArrayParam,
318-
format: DataFormatParam,
319323
dimension: HttpDimensionParam,
320324
},
321-
async ({ dateStart, dateEnd, dateRange, asn, location, continent, format, dimension }) => {
325+
async ({ dateStart, dateEnd, dateRange, asn, location, continent, dimension }) => {
322326
try {
323-
if (format !== 'timeseries' && !dimension) {
324-
throw new Error(`The '${format}' format requires a 'dimension' to group the data.`)
327+
const client = getCloudflareClient(agent.props.accessToken)
328+
const r = await resolveAndInvoke(client.radar.http, dimension, {
329+
asn,
330+
continent,
331+
location,
332+
dateRange,
333+
dateStart,
334+
dateEnd,
335+
})
336+
337+
return {
338+
content: [
339+
{
340+
type: 'text',
341+
text: JSON.stringify({
342+
result: r,
343+
}),
344+
},
345+
],
325346
}
347+
} catch (error) {
348+
return {
349+
content: [
350+
{
351+
type: 'text',
352+
text: `Error getting HTTP data: ${error instanceof Error && error.message}`,
353+
},
354+
],
355+
}
356+
}
357+
}
358+
)
326359

360+
agent.server.tool(
361+
'get_dns_queries_data',
362+
'Retrieve trends in DNS queries to the 1.1.1.1 resolver.',
363+
{
364+
dateRange: DateRangeArrayParam.optional(),
365+
dateStart: DateStartArrayParam.optional(),
366+
dateEnd: DateEndArrayParam.optional(),
367+
asn: AsnArrayParam,
368+
continent: ContinentArrayParam,
369+
location: LocationArrayParam,
370+
dimension: DnsDimensionParam,
371+
},
372+
async ({ dateStart, dateEnd, dateRange, asn, location, continent, dimension }) => {
373+
try {
327374
const client = getCloudflareClient(agent.props.accessToken)
328-
const endpoint = (...args: any) =>
329-
format === 'timeseries'
330-
? client.radar.http[format](...args)
331-
: client.radar.http[format][dimension!](...args)
332-
333-
const r = await endpoint({
375+
const r = await resolveAndInvoke(client.radar.dns, dimension, {
334376
asn,
335377
continent,
336378
location,
@@ -354,7 +396,7 @@ export function registerRadarTools(agent: RadarMCP) {
354396
content: [
355397
{
356398
type: 'text',
357-
text: `Error getting HTTP data: ${error instanceof Error && error.message}`,
399+
text: `Error getting DNS data: ${error instanceof Error && error.message}`,
358400
},
359401
],
360402
}
@@ -372,22 +414,12 @@ export function registerRadarTools(agent: RadarMCP) {
372414
asn: AsnArrayParam,
373415
continent: ContinentArrayParam,
374416
location: LocationArrayParam,
375-
format: DataFormatParam,
376417
dimension: L7AttackDimensionParam,
377418
},
378-
async ({ dateStart, dateEnd, dateRange, asn, location, continent, format, dimension }) => {
419+
async ({ dateStart, dateEnd, dateRange, asn, location, continent, dimension }) => {
379420
try {
380-
if (format !== 'timeseries' && !dimension) {
381-
throw new Error(`The '${format}' format requires a 'dimension' to group the data.`)
382-
}
383-
384421
const client = getCloudflareClient(agent.props.accessToken)
385-
const endpoint = (...args: any) =>
386-
format === 'timeseries'
387-
? client.radar.attacks.layer7[format](...args)
388-
: client.radar.attacks.layer7[format][dimension!](...args)
389-
390-
const r = await endpoint({
422+
const r = await resolveAndInvoke(client.radar.attacks.layer7, dimension, {
391423
asn,
392424
continent,
393425
location,
@@ -429,22 +461,12 @@ export function registerRadarTools(agent: RadarMCP) {
429461
asn: AsnArrayParam,
430462
continent: ContinentArrayParam,
431463
location: LocationArrayParam,
432-
format: DataFormatParam,
433464
dimension: L3AttackDimensionParam,
434465
},
435-
async ({ dateStart, dateEnd, dateRange, asn, location, continent, format, dimension }) => {
466+
async ({ dateStart, dateEnd, dateRange, asn, location, continent, dimension }) => {
436467
try {
437-
if (format !== 'timeseries' && !dimension) {
438-
throw new Error(`The '${format}' format requires a 'dimension' to group the data.`)
439-
}
440-
441468
const client = getCloudflareClient(agent.props.accessToken)
442-
const endpoint = (...args: any) =>
443-
format === 'timeseries'
444-
? client.radar.attacks.layer3[format](...args)
445-
: client.radar.attacks.layer3[format][dimension!](...args)
446-
447-
const r = await endpoint({
469+
const r = await resolveAndInvoke(client.radar.attacks.layer3, dimension, {
448470
asn,
449471
continent,
450472
location,
@@ -476,6 +498,88 @@ export function registerRadarTools(agent: RadarMCP) {
476498
}
477499
)
478500

501+
agent.server.tool(
502+
'get_email_routing_data',
503+
'Retrieve Email Routing trends.',
504+
{
505+
dateRange: DateRangeArrayParam.optional(),
506+
dateStart: DateStartArrayParam.optional(),
507+
dateEnd: DateEndArrayParam.optional(),
508+
dimension: EmailRoutingDimensionParam,
509+
},
510+
async ({ dateStart, dateEnd, dateRange, dimension }) => {
511+
try {
512+
const client = getCloudflareClient(agent.props.accessToken)
513+
const r = await resolveAndInvoke(client.radar.email.routing, dimension, {
514+
dateRange,
515+
dateStart,
516+
dateEnd,
517+
})
518+
519+
return {
520+
content: [
521+
{
522+
type: 'text',
523+
text: JSON.stringify({
524+
result: r,
525+
}),
526+
},
527+
],
528+
}
529+
} catch (error) {
530+
return {
531+
content: [
532+
{
533+
type: 'text',
534+
text: `Error getting Email Routing data: ${error instanceof Error && error.message}`,
535+
},
536+
],
537+
}
538+
}
539+
}
540+
)
541+
542+
agent.server.tool(
543+
'get_email_security_data',
544+
'Retrieve Email Security trends.',
545+
{
546+
dateRange: DateRangeArrayParam.optional(),
547+
dateStart: DateStartArrayParam.optional(),
548+
dateEnd: DateEndArrayParam.optional(),
549+
dimension: EmailSecurityDimensionParam,
550+
},
551+
async ({ dateStart, dateEnd, dateRange, dimension }) => {
552+
try {
553+
const client = getCloudflareClient(agent.props.accessToken)
554+
const r = await resolveAndInvoke(client.radar.email.security, dimension, {
555+
dateRange,
556+
dateStart,
557+
dateEnd,
558+
})
559+
560+
return {
561+
content: [
562+
{
563+
type: 'text',
564+
text: JSON.stringify({
565+
result: r,
566+
}),
567+
},
568+
],
569+
}
570+
} catch (error) {
571+
return {
572+
content: [
573+
{
574+
type: 'text',
575+
text: `Error getting Email Security data: ${error instanceof Error && error.message}`,
576+
},
577+
],
578+
}
579+
}
580+
}
581+
)
582+
479583
agent.server.tool(
480584
'get_internet_speed_data',
481585
'Retrieve summary of bandwidth, latency, jitter, and packet loss, from the previous 90 days of Cloudflare Speed Test.',
@@ -484,11 +588,17 @@ export function registerRadarTools(agent: RadarMCP) {
484588
asn: AsnArrayParam,
485589
continent: ContinentArrayParam,
486590
location: LocationArrayParam,
591+
dimension: InternetSpeedDimensionParam,
592+
orderBy: InternetSpeedOrderByParam.optional(),
487593
},
488-
async ({ dateEnd, asn, location, continent }) => {
594+
async ({ dateEnd, asn, location, continent, dimension, orderBy }) => {
595+
if (orderBy && dimension === 'summary') {
596+
throw new Error('Order by is only allowed for top locations and ASes')
597+
}
598+
489599
try {
490600
const client = getCloudflareClient(agent.props.accessToken)
491-
const r = await client.radar.quality.speed.summary({
601+
const r = await resolveAndInvoke(client.radar.quality.speed, dimension, {
492602
asn,
493603
continent,
494604
location,

0 commit comments

Comments
 (0)