Skip to content

Commit c256708

Browse files
feat(radar): add normalization parameter to 7 MCP tools (#297)
Add per-tool normalization params to Radar MCP tools that support it, based on what the live Radar API accepts. Tools updated: - get_http_data (PERCENTAGE_CHANGE, MIN0_MAX, PERCENTAGE) - get_dns_queries_data (PERCENTAGE, MIN0_MAX) - get_l7_attack_data (PERCENTAGE_CHANGE, MIN0_MAX, PERCENTAGE, MIN_MAX) - get_l3_attack_data (PERCENTAGE_CHANGE, MIN0_MAX, PERCENTAGE, MIN_MAX) - get_ai_data (PERCENTAGE, MIN0_MAX) - get_certificate_transparency_data (RAW_VALUES, PERCENTAGE) - get_robots_txt_data (PERCENTAGE) Also updates get_netflows_data to use the new NetflowsNormalizationParam with broader values (PERCENTAGE_CHANGE, MIN0_MAX, PERCENTAGE). Introduces a normalizationParam() factory that derives both the Zod enum values and the description from a single dimension-rules object, avoiding duplication between validation and documentation. Co-authored-by: Matt <77928207+mattzcarey@users.noreply.github.com>
1 parent f22a2cc commit c256708

2 files changed

Lines changed: 137 additions & 15 deletions

File tree

apps/radar/src/tools/radar.tools.ts

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99

1010
import {
1111
AiDimensionParam,
12+
AiNormalizationParam,
1213
AnnotationDataSourceParam,
1314
AnnotationEventTypeParam,
1415
As112DimensionParam,
@@ -18,6 +19,7 @@ import {
1819
AsnArrayParam,
1920
AsnParam,
2021
AsOrderByParam,
22+
AttackNormalizationParam,
2123
BgpHijackerAsnParam,
2224
BgpInvalidOnlyParam,
2325
BgpInvolvedAsnParam,
@@ -54,6 +56,7 @@ import {
5456
CtDimensionParam,
5557
CtDurationParam,
5658
CtEntryTypeParam,
59+
CtNormalizationParam,
5760
CtPublicKeyAlgorithmParam,
5861
CtTldParam,
5962
CtValidationLevelParam,
@@ -65,6 +68,7 @@ import {
6568
DateStartArrayParam,
6669
DateStartParam,
6770
DnsDimensionParam,
71+
DnsNormalizationParam,
6872
DomainCategoryArrayParam,
6973
DomainParam,
7074
DomainRankingTypeParam,
@@ -74,6 +78,7 @@ import {
7478
GeoIdArrayParam,
7579
GeoIdParam,
7680
HttpDimensionParam,
81+
HttpNormalizationParam,
7782
InternetQualityMetricParam,
7883
InternetServicesCategoryParam,
7984
InternetSpeedDimensionParam,
@@ -89,8 +94,8 @@ import {
8994
LocationListParam,
9095
LocationParam,
9196
NetflowsDimensionParam,
97+
NetflowsNormalizationParam,
9298
NetflowsProductParam,
93-
NormalizationParam,
9499
OriginArrayParam,
95100
OriginDataDimensionParam,
96101
OriginMetricParam,
@@ -100,6 +105,7 @@ import {
100105
RobotsTxtDimensionParam,
101106
RobotsTxtDirectiveParam,
102107
RobotsTxtDomainCategoryParam,
108+
RobotsTxtNormalizationParam,
103109
RobotsTxtPatternParam,
104110
RobotsTxtUserAgentCategoryParam,
105111
Sha256FingerprintParam,
@@ -473,8 +479,19 @@ export function registerRadarTools(agent: RadarMCP) {
473479
location: LocationArrayParam,
474480
geoId: GeoIdArrayParam,
475481
dimension: HttpDimensionParam,
482+
normalization: HttpNormalizationParam,
476483
},
477-
async ({ dateStart, dateEnd, dateRange, asn, location, continent, geoId, dimension }) => {
484+
async ({
485+
dateStart,
486+
dateEnd,
487+
dateRange,
488+
asn,
489+
location,
490+
continent,
491+
geoId,
492+
dimension,
493+
normalization,
494+
}) => {
478495
try {
479496
const props = getProps(agent)
480497

@@ -486,6 +503,7 @@ export function registerRadarTools(agent: RadarMCP) {
486503
dateRange,
487504
dateStart,
488505
dateEnd,
506+
normalization,
489507
})
490508

491509
return {
@@ -520,8 +538,18 @@ export function registerRadarTools(agent: RadarMCP) {
520538
continent: ContinentArrayParam,
521539
location: LocationArrayParam,
522540
dimension: DnsDimensionParam,
541+
normalization: DnsNormalizationParam,
523542
},
524-
async ({ dateStart, dateEnd, dateRange, asn, location, continent, dimension }) => {
543+
async ({
544+
dateStart,
545+
dateEnd,
546+
dateRange,
547+
asn,
548+
location,
549+
continent,
550+
dimension,
551+
normalization,
552+
}) => {
525553
try {
526554
const props = getProps(agent)
527555

@@ -532,6 +560,7 @@ export function registerRadarTools(agent: RadarMCP) {
532560
dateRange,
533561
dateStart,
534562
dateEnd,
563+
normalization,
535564
})
536565

537566
return {
@@ -566,8 +595,18 @@ export function registerRadarTools(agent: RadarMCP) {
566595
continent: ContinentArrayParam,
567596
location: LocationArrayParam,
568597
dimension: L7AttackDimensionParam,
598+
normalization: AttackNormalizationParam,
569599
},
570-
async ({ dateStart, dateEnd, dateRange, asn, location, continent, dimension }) => {
600+
async ({
601+
dateStart,
602+
dateEnd,
603+
dateRange,
604+
asn,
605+
location,
606+
continent,
607+
dimension,
608+
normalization,
609+
}) => {
571610
try {
572611
const props = getProps(agent)
573612
const client = getCloudflareClient(props.accessToken)
@@ -578,6 +617,7 @@ export function registerRadarTools(agent: RadarMCP) {
578617
dateRange,
579618
dateStart,
580619
dateEnd,
620+
normalization,
581621
})
582622

583623
return {
@@ -614,8 +654,18 @@ export function registerRadarTools(agent: RadarMCP) {
614654
continent: ContinentArrayParam,
615655
location: LocationArrayParam,
616656
dimension: L3AttackDimensionParam,
657+
normalization: AttackNormalizationParam,
617658
},
618-
async ({ dateStart, dateEnd, dateRange, asn, location, continent, dimension }) => {
659+
async ({
660+
dateStart,
661+
dateEnd,
662+
dateRange,
663+
asn,
664+
location,
665+
continent,
666+
dimension,
667+
normalization,
668+
}) => {
619669
try {
620670
const props = getProps(agent)
621671
const client = getCloudflareClient(props.accessToken)
@@ -626,6 +676,7 @@ export function registerRadarTools(agent: RadarMCP) {
626676
dateRange,
627677
dateStart,
628678
dateEnd,
679+
normalization,
629680
})
630681

631682
return {
@@ -846,8 +897,18 @@ export function registerRadarTools(agent: RadarMCP) {
846897
continent: ContinentArrayParam,
847898
location: LocationArrayParam,
848899
dimension: AiDimensionParam,
900+
normalization: AiNormalizationParam,
849901
},
850-
async ({ dateRange, dateStart, dateEnd, asn, location, continent, dimension }) => {
902+
async ({
903+
dateRange,
904+
dateStart,
905+
dateEnd,
906+
asn,
907+
location,
908+
continent,
909+
dimension,
910+
normalization,
911+
}) => {
851912
try {
852913
const props = getProps(agent)
853914

@@ -858,6 +919,7 @@ export function registerRadarTools(agent: RadarMCP) {
858919
dateRange,
859920
dateStart,
860921
dateEnd,
922+
normalization,
861923
})
862924

863925
return {
@@ -1161,6 +1223,7 @@ export function registerRadarTools(agent: RadarMCP) {
11611223
publicKeyAlgorithm: CtPublicKeyAlgorithmParam,
11621224
dimension: CtDimensionParam,
11631225
limitPerGroup: LimitPerGroupParam,
1226+
normalization: CtNormalizationParam,
11641227
},
11651228
async ({
11661229
dateRange,
@@ -1175,6 +1238,7 @@ export function registerRadarTools(agent: RadarMCP) {
11751238
publicKeyAlgorithm,
11761239
dimension,
11771240
limitPerGroup,
1241+
normalization,
11781242
}) => {
11791243
try {
11801244
const props = getProps(agent)
@@ -1191,6 +1255,7 @@ export function registerRadarTools(agent: RadarMCP) {
11911255
validationLevel,
11921256
publicKeyAlgorithm,
11931257
limitPerGroup: dimension !== 'timeseries' ? limitPerGroup : undefined,
1258+
normalization,
11941259
})
11951260

11961261
return {
@@ -1231,7 +1296,7 @@ export function registerRadarTools(agent: RadarMCP) {
12311296
location: LocationArrayParam,
12321297
geoId: GeoIdArrayParam,
12331298
product: NetflowsProductParam,
1234-
normalization: NormalizationParam,
1299+
normalization: NetflowsNormalizationParam,
12351300
dimension: NetflowsDimensionParam,
12361301
limitPerGroup: LimitPerGroupParam,
12371302
},
@@ -1457,6 +1522,7 @@ export function registerRadarTools(agent: RadarMCP) {
14571522
dimension: RobotsTxtDimensionParam,
14581523
limitPerGroup: LimitPerGroupParam,
14591524
limit: PaginationLimitParam,
1525+
normalization: RobotsTxtNormalizationParam,
14601526
},
14611527
async ({
14621528
dateRange,
@@ -1470,6 +1536,7 @@ export function registerRadarTools(agent: RadarMCP) {
14701536
dimension,
14711537
limitPerGroup,
14721538
limit,
1539+
normalization,
14731540
}) => {
14741541
try {
14751542
const props = getProps(agent)
@@ -1487,6 +1554,7 @@ export function registerRadarTools(agent: RadarMCP) {
14871554
userAgentCategory,
14881555
limitPerGroup,
14891556
limit,
1557+
normalization,
14901558
})
14911559

14921560
return {
@@ -1679,6 +1747,8 @@ export function registerRadarTools(agent: RadarMCP) {
16791747

16801748
// ============================================================
16811749
// Leaked Credential Checks Tools
1750+
// TODO: Add normalization (PERCENTAGE_CHANGE, MIN0_MAX) once the radar API
1751+
// supports it on leaked_credential_checks v2 timeseries_groups.
16821752
// ============================================================
16831753

16841754
agent.server.tool(

apps/radar/src/types/radar.ts

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -581,10 +581,63 @@ export const NetflowsProductParam = z
581581
.optional()
582582
.describe('Filter results by network traffic product type.')
583583

584-
export const NormalizationParam = z
585-
.enum(['RAW_VALUES', 'PERCENTAGE'])
586-
.optional()
587-
.describe('Normalization method applied to results.')
584+
/**
585+
* Factory for creating tool-specific normalization parameter schemas.
586+
* Derives both the Zod enum values and the description from a single
587+
* `dimensionRules` mapping of dimension pattern → accepted values.
588+
*/
589+
function normalizationParam(dimensionRules: Record<string, string[]>) {
590+
const allValues = [...new Set(Object.values(dimensionRules).flat())] as [string, ...string[]]
591+
592+
const rulesDesc = Object.entries(dimensionRules)
593+
.map(([dim, values]) => `${dim} accepts ${values.join(' or ')}`)
594+
.join('; ')
595+
596+
return z
597+
.enum(allValues)
598+
.optional()
599+
.describe(
600+
`Normalization method applied to results. ${rulesDesc}. ` +
601+
'See https://developers.cloudflare.com/radar/concepts/normalization/'
602+
)
603+
}
604+
605+
export const HttpNormalizationParam = normalizationParam({
606+
timeseries: ['PERCENTAGE_CHANGE', 'MIN0_MAX'],
607+
timeseries_groups: ['PERCENTAGE', 'MIN0_MAX'],
608+
})
609+
610+
// TODO: Add RANK once the radar API supports it on DNS timeseries_groups.
611+
export const DnsNormalizationParam = normalizationParam({
612+
timeseries_groups: ['PERCENTAGE', 'MIN0_MAX'],
613+
})
614+
615+
export const AttackNormalizationParam = normalizationParam({
616+
timeseries: ['PERCENTAGE_CHANGE', 'MIN0_MAX'],
617+
timeseriesGroups: ['PERCENTAGE', 'MIN0_MAX'],
618+
'top/attacks': ['PERCENTAGE', 'MIN_MAX'],
619+
})
620+
621+
// TODO: Add 'inference/timeseries_groups': ['PERCENTAGE', 'MIN0_MAX'] once the
622+
// radar API supports normalization on AI inference timeseries_groups.
623+
export const AiNormalizationParam = normalizationParam({
624+
'bots/timeseries_groups': ['PERCENTAGE', 'MIN0_MAX'],
625+
})
626+
627+
export const CtNormalizationParam = normalizationParam({
628+
timeseries_groups: ['RAW_VALUES', 'PERCENTAGE'],
629+
summary: ['RAW_VALUES', 'PERCENTAGE'],
630+
})
631+
632+
// TODO: Add MIN0_MAX once the radar API supports it on robots_txt timeseries_groups.
633+
export const RobotsTxtNormalizationParam = normalizationParam({
634+
timeseries_groups: ['PERCENTAGE'],
635+
})
636+
637+
export const NetflowsNormalizationParam = normalizationParam({
638+
timeseries: ['PERCENTAGE_CHANGE', 'MIN0_MAX'],
639+
timeseries_groups: ['PERCENTAGE', 'MIN0_MAX'],
640+
})
588641

589642
export const LimitPerGroupParam = z
590643
.number()
@@ -652,10 +705,9 @@ export const OriginRegionParam = z
652705
'Example regions: us-east-1, eu-west-1, ap-southeast-1.'
653706
)
654707

655-
export const OriginNormalizationParam = z
656-
.enum(['PERCENTAGE', 'MIN0_MAX'])
657-
.optional()
658-
.describe('Normalization method for results.')
708+
export const OriginNormalizationParam = normalizationParam({
709+
timeseries_groups: ['PERCENTAGE', 'MIN0_MAX'],
710+
})
659711

660712
// ============================================================
661713
// Robots.txt Parameters

0 commit comments

Comments
 (0)