* Helper function to make authenticated requests to the Radar API * Used for endpoints not yet available in the Cloudflare SDK
(
accessToken: string,
endpoint: string,
params: Record<string, unknown> = {}
)
| 137 | * Used for endpoints not yet available in the Cloudflare SDK |
| 138 | */ |
| 139 | async function fetchRadarApi( |
| 140 | accessToken: string, |
| 141 | endpoint: string, |
| 142 | params: Record<string, unknown> = {} |
| 143 | ): Promise<unknown> { |
| 144 | const url = new URL(`${RADAR_API_BASE}${endpoint}`) |
| 145 | |
| 146 | // Defense-in-depth: Ensure the resolved path stays within Radar API scope |
| 147 | // The URL constructor normalizes the path (resolves '..' and decodes percent-encoding), |
| 148 | // so we check the final pathname to prevent path traversal attacks |
| 149 | if (!url.pathname.startsWith('/client/v4/radar/')) { |
| 150 | throw new Error('Invalid endpoint path: must be within the Radar API scope') |
| 151 | } |
| 152 | |
| 153 | for (const [key, value] of Object.entries(params)) { |
| 154 | if (value === undefined || value === null) continue |
| 155 | |
| 156 | if (Array.isArray(value)) { |
| 157 | for (const item of value) { |
| 158 | url.searchParams.append(key, String(item)) |
| 159 | } |
| 160 | } else { |
| 161 | url.searchParams.set(key, String(value)) |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | const response = await fetch(url.toString(), { |
| 166 | method: 'GET', |
| 167 | headers: { |
| 168 | Authorization: `Bearer ${accessToken}`, |
| 169 | 'Content-Type': 'application/json', |
| 170 | }, |
| 171 | }) |
| 172 | |
| 173 | if (!response.ok) { |
| 174 | const errorBody = await response.text() |
| 175 | throw new Error(`API request failed (${response.status}): ${errorBody}`) |
| 176 | } |
| 177 | |
| 178 | const data = (await response.json()) as { success: boolean; result: unknown; errors?: unknown[] } |
| 179 | |
| 180 | if (!data.success) { |
| 181 | throw new Error(`API returned error: ${JSON.stringify(data.errors)}`) |
| 182 | } |
| 183 | |
| 184 | return data.result |
| 185 | } |
| 186 | |
| 187 | export function registerRadarTools(agent: RadarMCP) { |
| 188 | agent.server.registerTool( |