/** * @fileoverview HTTP client for the NIST NVD API 2.0 with rate-limit pacing. * Manages API key injection, queue-based pacing of every request attempt, retry with backoff, * and Retry-After header parsing. * @module services/nvd-http/nvd-http-client */ import type { Context } from '@cyanheads/mcp-ts-core'; import { McpError } from '@cyanheads/mcp-ts-core/errors'; /** * The error thrown when NVD answers HTTP 404 because it rejected the request parameters * (malformed CVE ID, unusable match string) rather than reporting an absent record. * Deterministic, so it carries `retryable: false`: the framework's opt-out stops `withRetry` * from re-sending a request that can never succeed. * * `detail` is NVD's own diagnosis from the `message` header — it names the offending parameter, * which the generic text cannot. * * Exported so callers translate it to a domain error and tests reproduce it exactly. */ export declare function nvdRequestRejected(endpoint: string, detail?: string): McpError; /** * True when `err` is the {@link nvdRequestRejected} throw. Narrows to {@link McpError} so a * caller translating the rejection can read NVD's own diagnosis off `message` and chain it as * `cause` without casting back off `unknown`. */ export declare function isNvdRequestRejected(err: unknown): err is McpError; /** * Per-call overrides of the client's default effort. A secondary lookup the caller can proceed * without must not spend the same retry and timeout budget as the record it decorates: on the * default budget an unreachable endpoint stretches the calling tool by the full product of * attempts and timeout, and holds the shared pacing queue for the same span. */ export interface NvdRequestBudget { /** Retry attempts beyond the first. Defaults to the key-dependent budget. */ maxRetries?: number; /** Per-attempt timeout in milliseconds. Defaults to the configured request timeout. */ timeoutMs?: number; } export declare class NvdHttpClient { private readonly apiKey; private readonly timeoutMs; private lastRequestAt; /** Epoch ms before which no request may be sent — set from a 403's Retry-After. */ private backoffUntil; private queue; private draining; constructor(apiKey: string | undefined, timeoutMs: number); /** * Fetch from the NVD API. * * Retry wraps the pacing queue rather than sitting inside it, so every attempt — the first * try and each retry — waits its turn and counts against the rate budget. */ get(endpoint: string, params: Record, ctx: Context, budget?: NvdRequestBudget): Promise; /** Enqueue one request attempt, paced behind the inter-request gap and any active 403 backoff. */ private schedule; private drainNext; private fetchOnce; /** * Classify NVD's HTTP 404, which covers two unrelated faults separated only by the `message` * header: a rejected request parameter, or an unusable API key. Reporting a malformed CVE ID * when the real fault is `NVD_API_KEY` sends the caller to fix the wrong thing — no correction * to the ID can succeed while the key is refused. Both are deterministic, so neither retries. */ private rejectionFor; /** * Build the 403 error and hold the queue until NVD's window resets, so every pending request — * not just this call's retry — stops firing into a limit that is already exceeded. */ private rateLimitError; private buildUrl; private buildHeaders; } export declare function initNvdHttpClient(apiKey: string | undefined, timeoutMs: number): void; export declare function getNvdHttpClient(): NvdHttpClient; //# sourceMappingURL=nvd-http-client.d.ts.map