diff --git a/apps/radar/src/radar.app.ts b/apps/radar/src/radar.app.ts index 2f9217d7..5f629bfc 100644 --- a/apps/radar/src/radar.app.ts +++ b/apps/radar/src/radar.app.ts @@ -15,6 +15,7 @@ import { registerAccountTools } from '@repo/mcp-common/src/tools/account.tools' import { MetricsTracker } from '@repo/mcp-observability' import { BASE_INSTRUCTIONS } from './radar.context' +import { registerOriginsTools } from './tools/origins.tools' import { registerRadarTools } from './tools/radar.tools' import { registerUrlScannerTools } from './tools/url-scanner.tools' @@ -69,6 +70,7 @@ export class RadarMCP extends McpAgent { registerAccountTools(this) registerRadarTools(this) + registerOriginsTools(this) registerUrlScannerTools(this) } diff --git a/apps/radar/src/radar.context.ts b/apps/radar/src/radar.context.ts index f1bb3ede..e178f410 100644 --- a/apps/radar/src/radar.context.ts +++ b/apps/radar/src/radar.context.ts @@ -39,4 +39,26 @@ Example: To compare HTTP traffic between Portugal and Spain over the last 7 days This applies to date filters and other filters that support comparison across multiple values. If a tool does **not** support array-based filters, you can achieve the same comparison by making multiple separate calls to the tool. + +### Cloud Observatory (Origins) + +The Cloud Observatory tools (\`list_origins\`, \`get_origin_details\`, \`get_origins_timeseries\`, \`get_origins_summary\`, +\`get_origins_timeseries_groups\`) provide performance insights for major cloud providers (hyperscalers): +- **AMAZON** (AWS) +- **GOOGLE** (GCP) +- **MICROSOFT** (Azure) +- **ORACLE** (OCI) + +Available metrics include: +- \`TCP_RTT\`: TCP round-trip time (latency) +- \`TCP_HANDSHAKE_DURATION\`: Time to establish TCP connection +- \`TLS_HANDSHAKE_DURATION\`: Time to complete TLS handshake +- \`RESPONSE_HEADER_RECEIVE_DURATION\`: Time to first byte (TTFB) +- \`CONNECTION_FAILURES\`: Failed connection attempts +- \`REQUESTS\`: Total request volume + +You can filter by specific cloud regions (e.g., \`us-east-1\`, \`eu-west-1\`) and group results by: +- \`REGION\`: Compare performance across cloud regions +- \`SUCCESS_RATE\`: Analyze connection reliability +- \`PERCENTILE\`: View p50, p90, p99 latency distributions ` diff --git a/apps/radar/src/tools/origins.tools.ts b/apps/radar/src/tools/origins.tools.ts new file mode 100644 index 00000000..356fee8c --- /dev/null +++ b/apps/radar/src/tools/origins.tools.ts @@ -0,0 +1,319 @@ +/** + * Cloud Observatory / Origins Tools + * + * These tools provide insights into hyperscaler (cloud provider) performance metrics + * as observed from Cloudflare's network. The data powers the Cloud Observatory feature + * in Cloudflare Radar (https://radar.cloudflare.com/cloud-observatory). + * + * Supported cloud providers: Amazon (AWS), Google (GCP), Microsoft (Azure), Oracle (OCI) + */ +import { getProps } from '@repo/mcp-common/src/get-props' +import { + PaginationLimitParam, + PaginationOffsetParam, +} from '@repo/mcp-common/src/types/shared.types' + +import { + OriginAggIntervalParam, + OriginArrayParam, + OriginDimensionParam, + OriginLimitPerGroupParam, + OriginMetricParam, + OriginNormalizationParam, + OriginRegionParam, + OriginSlugParam, +} from '../types/origins' +import { DateEndArrayParam, DateRangeArrayParam, DateStartArrayParam } from '../types/radar' + +import type { RadarMCP } from '../radar.app' + +const RADAR_API_BASE = 'https://api.cloudflare.com/client/v4/radar' + +/** + * Helper function to make authenticated requests to the Radar Origins API + */ +async function fetchOriginsApi( + accessToken: string, + endpoint: string, + params: Record = {} +): Promise { + const url = new URL(`${RADAR_API_BASE}${endpoint}`) + + // Add query parameters, handling arrays properly + for (const [key, value] of Object.entries(params)) { + if (value === undefined || value === null) continue + + if (Array.isArray(value)) { + for (const item of value) { + url.searchParams.append(key, String(item)) + } + } else { + url.searchParams.set(key, String(value)) + } + } + + const response = await fetch(url.toString(), { + method: 'GET', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + }) + + if (!response.ok) { + const errorBody = await response.text() + throw new Error(`API request failed (${response.status}): ${errorBody}`) + } + + const data = (await response.json()) as { success: boolean; result: unknown; errors?: unknown[] } + + if (!data.success) { + throw new Error(`API returned error: ${JSON.stringify(data.errors)}`) + } + + return data.result +} + +export function registerOriginsTools(agent: RadarMCP) { + /** + * List all available cloud provider origins with their regions + */ + agent.server.tool( + 'list_origins', + 'List cloud provider origins (hyperscalers) available in Cloud Observatory. Returns Amazon (AWS), Google (GCP), Microsoft (Azure), and Oracle (OCI) with their available regions.', + { + limit: PaginationLimitParam, + offset: PaginationOffsetParam, + }, + async ({ limit, offset }) => { + try { + const props = getProps(agent) + const result = await fetchOriginsApi(props.accessToken, '/origins', { + limit, + offset, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error listing origins: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + /** + * Get details for a specific cloud provider origin + */ + agent.server.tool( + 'get_origin_details', + 'Get details for a specific cloud provider origin, including all available regions.', + { + slug: OriginSlugParam, + }, + async ({ slug }) => { + try { + const props = getProps(agent) + const result = await fetchOriginsApi(props.accessToken, `/origins/${slug}`) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting origin details: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + /** + * Get time series metrics for cloud provider origins + */ + agent.server.tool( + 'get_origins_timeseries', + 'Retrieve time series performance metrics for cloud provider origins. Use this to analyze trends in connection performance, latency, and reliability over time.', + { + origin: OriginArrayParam, + metric: OriginMetricParam, + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + region: OriginRegionParam, + aggInterval: OriginAggIntervalParam, + }, + async ({ origin, metric, dateRange, dateStart, dateEnd, region, aggInterval }) => { + try { + const props = getProps(agent) + const result = await fetchOriginsApi(props.accessToken, '/origins/timeseries', { + origin, + metric, + dateRange, + dateStart, + dateEnd, + region, + aggInterval, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting origins timeseries: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + /** + * Get summary metrics for cloud provider origins grouped by dimension + */ + agent.server.tool( + 'get_origins_summary', + 'Retrieve aggregated summary of cloud provider performance metrics grouped by dimension (region, success rate, or percentile). Useful for comparing performance across regions or understanding distribution.', + { + dimension: OriginDimensionParam, + origin: OriginArrayParam, + metric: OriginMetricParam, + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + region: OriginRegionParam, + limitPerGroup: OriginLimitPerGroupParam, + }, + async ({ dimension, origin, metric, dateRange, dateStart, dateEnd, region, limitPerGroup }) => { + try { + const props = getProps(agent) + const result = await fetchOriginsApi(props.accessToken, `/origins/summary/${dimension}`, { + origin, + metric, + dateRange, + dateStart, + dateEnd, + region, + limitPerGroup, + }) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting origins summary: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) + + /** + * Get time series metrics for cloud provider origins grouped by dimension + */ + agent.server.tool( + 'get_origins_timeseries_groups', + 'Retrieve time series of cloud provider performance metrics grouped by dimension over time. Use this to visualize how metrics like latency vary across regions or percentiles over time.', + { + dimension: OriginDimensionParam, + origin: OriginArrayParam, + metric: OriginMetricParam, + dateRange: DateRangeArrayParam.optional(), + dateStart: DateStartArrayParam.optional(), + dateEnd: DateEndArrayParam.optional(), + region: OriginRegionParam, + aggInterval: OriginAggIntervalParam, + limitPerGroup: OriginLimitPerGroupParam, + normalization: OriginNormalizationParam, + }, + async ({ + dimension, + origin, + metric, + dateRange, + dateStart, + dateEnd, + region, + aggInterval, + limitPerGroup, + normalization, + }) => { + try { + const props = getProps(agent) + const result = await fetchOriginsApi( + props.accessToken, + `/origins/timeseries_groups/${dimension}`, + { + origin, + metric, + dateRange, + dateStart, + dateEnd, + region, + aggInterval, + limitPerGroup, + normalization, + } + ) + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ result }), + }, + ], + } + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error getting origins timeseries groups: ${error instanceof Error ? error.message : String(error)}`, + }, + ], + } + } + } + ) +} diff --git a/apps/radar/src/types/origins.ts b/apps/radar/src/types/origins.ts new file mode 100644 index 00000000..75f39eab --- /dev/null +++ b/apps/radar/src/types/origins.ts @@ -0,0 +1,108 @@ +/** + * This file contains the validators for the Cloud Observatory / Origins tools. + * These tools provide insights into hyperscaler (cloud provider) performance metrics + * as observed from Cloudflare's network. + */ +import { z } from 'zod' + +/** + * Supported cloud provider origins (hyperscalers) + */ +export const OriginSlugParam = z + .enum(['AMAZON', 'GOOGLE', 'MICROSOFT', 'ORACLE']) + .describe( + 'The cloud provider origin to query. Supported values: AMAZON (AWS), GOOGLE (GCP), MICROSOFT (Azure), ORACLE (OCI).' + ) + +export const OriginArrayParam = z + .array(OriginSlugParam) + .min(1) + .describe( + 'Array of cloud provider origins to query. At least one origin must be specified. ' + + 'Supported values: AMAZON (AWS), GOOGLE (GCP), MICROSOFT (Azure), ORACLE (OCI).' + ) + +/** + * Metrics available for origin performance analysis + */ +export const OriginMetricParam = z + .enum([ + 'CONNECTION_FAILURES', + 'REQUESTS', + 'RESPONSE_HEADER_RECEIVE_DURATION', + 'TCP_HANDSHAKE_DURATION', + 'TCP_RTT', + 'TLS_HANDSHAKE_DURATION', + ]) + .describe( + 'The performance metric to retrieve. ' + + 'CONNECTION_FAILURES: Number of failed connections. ' + + 'REQUESTS: Total request count. ' + + 'RESPONSE_HEADER_RECEIVE_DURATION: Time to receive response headers (ms). ' + + 'TCP_HANDSHAKE_DURATION: TCP handshake time (ms). ' + + 'TCP_RTT: TCP round-trip time (ms). ' + + 'TLS_HANDSHAKE_DURATION: TLS handshake time (ms).' + ) + +/** + * Dimensions for grouping origin metrics + */ +export const OriginDimensionParam = z + .enum(['REGION', 'SUCCESS_RATE', 'PERCENTILE']) + .describe( + 'The dimension by which to group results. ' + + 'REGION: Group by cloud provider region (e.g., us-east-1). ' + + 'SUCCESS_RATE: Group by connection success rate. ' + + 'PERCENTILE: Group by performance percentiles (p50, p90, p99).' + ) + +/** + * Cloud provider region filter + */ +export const OriginRegionParam = z + .array(z.string().max(100)) + .optional() + .describe( + 'Filters results by cloud provider region. ' + + 'Example regions: us-east-1, eu-west-1, ap-southeast-1. ' + + 'Region names vary by provider.' + ) + +/** + * Aggregation interval for time series data + */ +export const OriginAggIntervalParam = z + .enum(['15m', '1h', '1d', '1w']) + .optional() + .describe( + 'Aggregation interval for time series results. ' + + '15m: 15 minutes (for short time ranges). ' + + '1h: 1 hour (default). ' + + '1d: 1 day. ' + + '1w: 1 week (for long time ranges).' + ) + +/** + * Normalization method for time series groups + */ +export const OriginNormalizationParam = z + .enum(['PERCENTAGE', 'MIN0_MAX']) + .optional() + .describe( + 'Normalization method for results. ' + + 'PERCENTAGE: Values as percentages (default). ' + + 'MIN0_MAX: Normalized between 0 and max value.' + ) + +/** + * Limit per group for summary/timeseries group queries + */ +export const OriginLimitPerGroupParam = z + .number() + .int() + .positive() + .optional() + .describe( + 'Limits the number of items per group. ' + + 'When item count exceeds the limit, extra items appear grouped under "other".' + )