@bbradar/mcp 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,156 @@
1
+ # @bbradar/mcp
2
+
3
+ Local STDIO MCP server for [bbradar.io](https://bbradar.io) Pro subscribers. It lets MCP clients query [bbradar.io](https://bbradar.io) Pro program, target, opportunity, and recent-change data through a local npm package.
4
+
5
+ ## Requirements
6
+
7
+ - Node.js 20 or newer.
8
+ - A [bbradar.io](https://bbradar.io) Pro API key.
9
+ - An MCP client that supports local STDIO servers.
10
+
11
+ ## Install
12
+
13
+ Most MCP clients can run the package directly with `npx`:
14
+
15
+ ```bash
16
+ npx -y @bbradar/mcp
17
+ ```
18
+
19
+ For a global install:
20
+
21
+ ```bash
22
+ npm install -g @bbradar/mcp
23
+ bbradar-mcp
24
+ ```
25
+
26
+ For a project-local install:
27
+
28
+ ```bash
29
+ npm install @bbradar/mcp
30
+ npx bbradar-mcp
31
+ ```
32
+
33
+ The server needs your [bbradar.io](https://bbradar.io) Pro key. In the MCP client config, add it as `BBRADAR_API_KEY`.
34
+
35
+ ## Client Configuration
36
+
37
+ ### OpenCode
38
+
39
+ ```json
40
+ {
41
+ "$schema": "https://opencode.ai/config.json",
42
+ "mcp": {
43
+ "bbradar.io": {
44
+ "type": "local",
45
+ "command": ["npx", "-y", "@bbradar/mcp"],
46
+ "enabled": true,
47
+ "timeout": 30000,
48
+ "environment": {
49
+ "BBRADAR_API_KEY": "your_pro_mcp_api_key"
50
+ }
51
+ }
52
+ }
53
+ }
54
+ ```
55
+
56
+ ### Codex
57
+
58
+ Add with the Codex CLI:
59
+
60
+ ```bash
61
+ codex mcp add bbradar.io --env BBRADAR_API_KEY="your_pro_mcp_api_key" -- npx -y @bbradar/mcp
62
+ ```
63
+
64
+ Or add it to `~/.codex/config.toml`:
65
+
66
+ ```toml
67
+ [mcp_servers."bbradar.io"]
68
+ command = "npx"
69
+ args = ["-y", "@bbradar/mcp"]
70
+
71
+ [mcp_servers."bbradar.io".env]
72
+ BBRADAR_API_KEY = "your_pro_mcp_api_key"
73
+ ```
74
+
75
+ ### Claude Code
76
+
77
+ Add a private user-level server:
78
+
79
+ ```bash
80
+ claude mcp add bbradar.io --scope user --env BBRADAR_API_KEY="your_pro_mcp_api_key" -- npx -y @bbradar/mcp
81
+ ```
82
+
83
+ On native Windows, wrap `npx` with `cmd /c`:
84
+
85
+ ```bash
86
+ claude mcp add bbradar.io --scope user --env BBRADAR_API_KEY="your_pro_mcp_api_key" -- cmd /c npx -y @bbradar/mcp
87
+ ```
88
+
89
+ ### Generic MCP JSON
90
+
91
+ ```json
92
+ {
93
+ "mcpServers": {
94
+ "bbradar.io": {
95
+ "command": "npx",
96
+ "args": ["-y", "@bbradar/mcp"],
97
+ "env": {
98
+ "BBRADAR_API_KEY": "your_pro_mcp_api_key"
99
+ }
100
+ }
101
+ }
102
+ }
103
+ ```
104
+
105
+ ## Verify
106
+
107
+ After adding the MCP config, restart your client and call `get_mcp_status`. A working setup returns server status, timing, and rate-limit metadata.
108
+
109
+ ## Tools
110
+
111
+ - `search_programs`
112
+ - `get_program`
113
+ - `resolve_program`
114
+ - `run_program_name_action`
115
+ - `get_program_targets`
116
+ - `get_recent_changes`
117
+ - `get_opportunities`
118
+ - `find_programs`
119
+ - `find_wildcard_programs`
120
+ - `find_web3_programs`
121
+ - `find_reward_programs`
122
+ - `find_web3_contests`
123
+ - `find_language_programs`
124
+ - `find_target_type_programs`
125
+ - `find_vdp_programs`
126
+ - `find_paid_programs`
127
+ - `find_stack_matches`
128
+ - `find_low_noise_programs`
129
+ - `find_recent_by_type`
130
+ - `find_programs_by_target`
131
+ - `find_program_candidates`
132
+ - `find_hunt_candidates`
133
+ - `find_fresh_hunt_candidates`
134
+ - `list_filters`
135
+ - `get_mcp_status`
136
+ - `compare_programs`
137
+ - `compare_programs_compact`
138
+ - `summarize_program_activity`
139
+ - `get_program_brief`
140
+ - `find_recently_added_wildcards`
141
+ - `find_low_competition_high_reward`
142
+ - `get_program_delta`
143
+ - `get_latest_added_targets`
144
+ - `get_program_scope_summary`
145
+ - `get_program_target_breakdown`
146
+ - `get_program_scope_delta`
147
+ - `get_recent_target_activity`
148
+ - `check_program_new_targets`
149
+ - `check_watchlist_new_targets`
150
+ - `export_targets`
151
+
152
+ ## Prompts
153
+
154
+ - `find_best_programs_to_hunt`
155
+ - `summarize_program_scope`
156
+ - `prepare_recon_plan`
@@ -0,0 +1,61 @@
1
+ import { type CacheStats } from "./cache.js";
2
+ import type { ServerConfig } from "./config.js";
3
+ export type QueryValue = string | number | boolean | null | undefined | readonly string[] | readonly number[] | readonly boolean[];
4
+ export type ApiRequestOptions = {
5
+ method?: "GET" | "POST";
6
+ path: string;
7
+ query?: Record<string, QueryValue>;
8
+ body?: unknown;
9
+ cache?: boolean;
10
+ coalesce?: boolean;
11
+ };
12
+ export type ApiResult<T> = {
13
+ data: T;
14
+ requestId: string;
15
+ upstreamRequestId?: string | undefined;
16
+ cached: boolean;
17
+ coalesced?: boolean | undefined;
18
+ status: number;
19
+ fetchedAt: string;
20
+ cacheExpiresAt?: string | undefined;
21
+ };
22
+ export declare class BBRadarApiError extends Error {
23
+ readonly requestId: string;
24
+ readonly upstreamRequestId: string | undefined;
25
+ readonly status: number | undefined;
26
+ readonly detail?: unknown;
27
+ readonly errors?: unknown;
28
+ constructor(params: {
29
+ message: string;
30
+ requestId: string;
31
+ upstreamRequestId?: string | undefined;
32
+ status?: number | undefined;
33
+ detail?: unknown;
34
+ errors?: unknown;
35
+ cause?: unknown;
36
+ });
37
+ }
38
+ export declare class BBRadarClient {
39
+ private readonly config;
40
+ private readonly cache;
41
+ private readonly inFlight;
42
+ constructor(config: ServerConfig);
43
+ diagnostics(): {
44
+ response_cache: CacheStats & {
45
+ enabled: boolean;
46
+ };
47
+ in_flight_requests: number;
48
+ };
49
+ validateKey(): Promise<void>;
50
+ listPrograms(query: Record<string, QueryValue>): Promise<ApiResult<unknown>>;
51
+ getProgram(programId: string): Promise<ApiResult<unknown>>;
52
+ getProgramTargets(programId: string): Promise<ApiResult<unknown>>;
53
+ getRecentChanges(query: Record<string, QueryValue>): Promise<ApiResult<unknown>>;
54
+ searchTargets(query: Record<string, QueryValue>): Promise<ApiResult<unknown>>;
55
+ getOpportunities(level: string, query: Record<string, QueryValue>): Promise<ApiResult<unknown>>;
56
+ exportTargets(body: Record<string, unknown>): Promise<ApiResult<unknown>>;
57
+ request<T = unknown>(options: ApiRequestOptions): Promise<ApiResult<T>>;
58
+ private fetchFromApi;
59
+ private buildUrl;
60
+ private headers;
61
+ }
@@ -0,0 +1,307 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { TtlCache } from "./cache.js";
3
+ import { stableStringify } from "./json.js";
4
+ import { encodeProgramIdPath } from "./url.js";
5
+ export class BBRadarApiError extends Error {
6
+ requestId;
7
+ upstreamRequestId;
8
+ status;
9
+ detail;
10
+ errors;
11
+ constructor(params) {
12
+ super(params.message, { cause: params.cause });
13
+ this.name = "BBRadarApiError";
14
+ this.requestId = params.requestId;
15
+ this.upstreamRequestId = params.upstreamRequestId;
16
+ this.status = params.status;
17
+ this.detail = params.detail;
18
+ this.errors = params.errors;
19
+ }
20
+ }
21
+ export class BBRadarClient {
22
+ config;
23
+ cache;
24
+ inFlight = new Map();
25
+ constructor(config) {
26
+ this.config = config;
27
+ this.cache = new TtlCache(config.cacheMaxEntries, config.cacheTtlMs);
28
+ }
29
+ diagnostics() {
30
+ return {
31
+ response_cache: {
32
+ enabled: this.config.cacheTtlMs > 0 && this.config.cacheMaxEntries > 0,
33
+ ...this.cache.stats()
34
+ },
35
+ in_flight_requests: this.inFlight.size
36
+ };
37
+ }
38
+ async validateKey() {
39
+ try {
40
+ await this.request({
41
+ path: "/pro/programs",
42
+ query: { page: 1, page_size: 1 },
43
+ cache: false,
44
+ coalesce: false
45
+ });
46
+ }
47
+ catch (error) {
48
+ if (!(error instanceof BBRadarApiError)) {
49
+ throw error;
50
+ }
51
+ if (error.status === 401) {
52
+ throw new Error("BBRADAR_API_KEY was rejected by the BBRadar Pro API.");
53
+ }
54
+ if (error.status === 402) {
55
+ throw new Error("The BBRadar API key does not have an active Pro subscription entitlement.");
56
+ }
57
+ if (error.status === 403) {
58
+ throw new Error("The BBRadar API key is not allowed to use MCP or lacks the required read-only scope.");
59
+ }
60
+ if (error.status === 423) {
61
+ throw new Error("The BBRadar Pro account or API key is locked.");
62
+ }
63
+ throw error;
64
+ }
65
+ }
66
+ listPrograms(query) {
67
+ return this.request({
68
+ path: "/pro/programs",
69
+ query
70
+ });
71
+ }
72
+ getProgram(programId) {
73
+ return this.request({
74
+ path: `/pro/programs/${encodeProgramIdPath(programId)}`
75
+ });
76
+ }
77
+ getProgramTargets(programId) {
78
+ return this.request({
79
+ path: `/pro/programs/${encodeProgramIdPath(programId)}/targets`
80
+ });
81
+ }
82
+ getRecentChanges(query) {
83
+ return this.request({
84
+ path: "/pro/targets/changes",
85
+ query
86
+ });
87
+ }
88
+ searchTargets(query) {
89
+ return this.request({
90
+ path: "/pro/targets/search",
91
+ query
92
+ });
93
+ }
94
+ getOpportunities(level, query) {
95
+ return this.request({
96
+ path: `/pro/opportunities/${encodeURIComponent(level)}`,
97
+ query
98
+ });
99
+ }
100
+ exportTargets(body) {
101
+ return this.request({
102
+ method: "POST",
103
+ path: "/pro/targets/export",
104
+ body,
105
+ cache: false,
106
+ coalesce: false
107
+ });
108
+ }
109
+ async request(options) {
110
+ const method = options.method ?? "GET";
111
+ const requestId = randomUUID();
112
+ const url = this.buildUrl(options.path, options.query);
113
+ const requestKey = stableStringify({
114
+ method,
115
+ url: url.toString(),
116
+ body: options.body ?? null
117
+ });
118
+ const cacheable = method === "GET" && (options.cache ?? true) && this.config.cacheTtlMs > 0 && this.config.cacheMaxEntries > 0;
119
+ const coalesceable = method === "GET" && (options.coalesce ?? true);
120
+ if (cacheable) {
121
+ const cached = this.cache.get(requestKey);
122
+ if (cached) {
123
+ return {
124
+ ...cached.value,
125
+ requestId,
126
+ cached: true,
127
+ cacheExpiresAt: new Date(cached.expiresAt).toISOString()
128
+ };
129
+ }
130
+ }
131
+ if (coalesceable) {
132
+ const pending = this.inFlight.get(requestKey);
133
+ if (pending) {
134
+ try {
135
+ const result = (await pending);
136
+ return {
137
+ ...result,
138
+ requestId,
139
+ cached: false,
140
+ coalesced: true
141
+ };
142
+ }
143
+ catch (error) {
144
+ throw cloneApiError(error, requestId);
145
+ }
146
+ }
147
+ }
148
+ const request = this.fetchFromApi(options, method, url, requestId);
149
+ if (coalesceable) {
150
+ this.inFlight.set(requestKey, request);
151
+ }
152
+ try {
153
+ const result = await request;
154
+ if (cacheable) {
155
+ this.cache.set(requestKey, result);
156
+ }
157
+ return {
158
+ ...result,
159
+ requestId,
160
+ cached: false
161
+ };
162
+ }
163
+ finally {
164
+ if (coalesceable) {
165
+ this.inFlight.delete(requestKey);
166
+ }
167
+ }
168
+ }
169
+ async fetchFromApi(options, method, url, requestId) {
170
+ const controller = new AbortController();
171
+ const timeout = setTimeout(() => controller.abort(), this.config.requestTimeoutMs);
172
+ try {
173
+ const init = {
174
+ method,
175
+ headers: this.headers(options.body),
176
+ cache: "no-store",
177
+ signal: controller.signal
178
+ };
179
+ if (options.body !== undefined) {
180
+ init.body = JSON.stringify(options.body);
181
+ }
182
+ const response = await fetch(url, init);
183
+ const upstreamRequestId = extractRequestId(response.headers);
184
+ const data = await parseResponseBody(response);
185
+ if (!response.ok) {
186
+ const params = {
187
+ message: formatApiErrorMessage(response.status, data),
188
+ requestId,
189
+ status: response.status,
190
+ detail: extractObjectField(data, "detail"),
191
+ errors: extractObjectField(data, "errors")
192
+ };
193
+ if (upstreamRequestId) {
194
+ params.upstreamRequestId = upstreamRequestId;
195
+ }
196
+ throw new BBRadarApiError(params);
197
+ }
198
+ const result = {
199
+ data: data,
200
+ status: response.status,
201
+ fetchedAt: new Date().toISOString()
202
+ };
203
+ if (upstreamRequestId) {
204
+ result.upstreamRequestId = upstreamRequestId;
205
+ }
206
+ return result;
207
+ }
208
+ catch (error) {
209
+ if (error instanceof BBRadarApiError) {
210
+ throw error;
211
+ }
212
+ throw new BBRadarApiError({
213
+ message: error instanceof Error && error.name === "AbortError" ? "BBRadar API request timed out." : "BBRadar API request failed.",
214
+ requestId,
215
+ cause: error
216
+ });
217
+ }
218
+ finally {
219
+ clearTimeout(timeout);
220
+ }
221
+ }
222
+ buildUrl(path, query) {
223
+ const url = new URL(`${this.config.apiBaseUrl}${path}`);
224
+ for (const [key, value] of Object.entries(query ?? {})) {
225
+ if (value === undefined || value === null) {
226
+ continue;
227
+ }
228
+ if (Array.isArray(value)) {
229
+ if (value.length > 0) {
230
+ url.searchParams.set(key, value.join(","));
231
+ }
232
+ continue;
233
+ }
234
+ url.searchParams.set(key, String(value));
235
+ }
236
+ return url;
237
+ }
238
+ headers(body) {
239
+ const headers = {
240
+ authorization: `Bearer ${this.config.apiKey}`,
241
+ accept: "application/json",
242
+ "cache-control": "no-cache, no-store, max-age=0",
243
+ pragma: "no-cache",
244
+ "user-agent": "@bbradar/mcp"
245
+ };
246
+ if (body !== undefined) {
247
+ headers["content-type"] = "application/json";
248
+ }
249
+ return headers;
250
+ }
251
+ }
252
+ async function parseResponseBody(response) {
253
+ if (response.status === 204) {
254
+ return null;
255
+ }
256
+ const contentType = response.headers.get("content-type") ?? "";
257
+ const text = await response.text();
258
+ if (text.length === 0) {
259
+ return null;
260
+ }
261
+ if (contentType.includes("application/json") || contentType.includes("+json")) {
262
+ try {
263
+ return JSON.parse(text);
264
+ }
265
+ catch {
266
+ return text;
267
+ }
268
+ }
269
+ return text;
270
+ }
271
+ function extractRequestId(headers) {
272
+ return (headers.get("x-request-id") ??
273
+ headers.get("x-correlation-id") ??
274
+ headers.get("request-id") ??
275
+ undefined);
276
+ }
277
+ function extractObjectField(value, field) {
278
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
279
+ return undefined;
280
+ }
281
+ return value[field];
282
+ }
283
+ function formatApiErrorMessage(status, data) {
284
+ const detail = extractObjectField(data, "detail");
285
+ const message = extractObjectField(data, "message");
286
+ const suffix = typeof detail === "string"
287
+ ? detail
288
+ : typeof message === "string"
289
+ ? message
290
+ : "Request was rejected.";
291
+ return `BBRadar API returned ${status}: ${suffix}`;
292
+ }
293
+ function cloneApiError(error, requestId) {
294
+ if (!(error instanceof BBRadarApiError)) {
295
+ return error;
296
+ }
297
+ return new BBRadarApiError({
298
+ message: error.message,
299
+ requestId,
300
+ upstreamRequestId: error.upstreamRequestId,
301
+ status: error.status,
302
+ detail: error.detail,
303
+ errors: error.errors,
304
+ cause: error
305
+ });
306
+ }
307
+ //# sourceMappingURL=bbradarClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bbradarClient.js","sourceRoot":"","sources":["../src/bbradarClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ export type CacheEntry<T> = {
2
+ value: T;
3
+ expiresAt: number;
4
+ };
5
+ export type CacheStats = {
6
+ entries: number;
7
+ max_entries: number;
8
+ ttl_ms: number;
9
+ hits: number;
10
+ misses: number;
11
+ sets: number;
12
+ evictions: number;
13
+ };
14
+ export declare class TtlCache<T> {
15
+ private readonly maxEntries;
16
+ private readonly ttlMs;
17
+ private readonly entries;
18
+ private hits;
19
+ private misses;
20
+ private sets;
21
+ private evictions;
22
+ constructor(maxEntries?: number, ttlMs?: number);
23
+ get(key: string): CacheEntry<T> | undefined;
24
+ set(key: string, value: T): void;
25
+ stats(): CacheStats;
26
+ }
package/dist/cache.js ADDED
@@ -0,0 +1,60 @@
1
+ export class TtlCache {
2
+ maxEntries;
3
+ ttlMs;
4
+ entries = new Map();
5
+ hits = 0;
6
+ misses = 0;
7
+ sets = 0;
8
+ evictions = 0;
9
+ constructor(maxEntries = 500, ttlMs = 30_000) {
10
+ this.maxEntries = maxEntries;
11
+ this.ttlMs = ttlMs;
12
+ }
13
+ get(key) {
14
+ const entry = this.entries.get(key);
15
+ if (!entry) {
16
+ this.misses += 1;
17
+ return undefined;
18
+ }
19
+ if (entry.expiresAt <= Date.now()) {
20
+ this.entries.delete(key);
21
+ this.misses += 1;
22
+ return undefined;
23
+ }
24
+ this.hits += 1;
25
+ return entry;
26
+ }
27
+ set(key, value) {
28
+ if (this.ttlMs <= 0 || this.maxEntries <= 0) {
29
+ return;
30
+ }
31
+ if (this.entries.has(key)) {
32
+ this.entries.delete(key);
33
+ }
34
+ while (this.entries.size >= this.maxEntries) {
35
+ const oldestKey = this.entries.keys().next().value;
36
+ if (oldestKey === undefined) {
37
+ break;
38
+ }
39
+ this.entries.delete(oldestKey);
40
+ this.evictions += 1;
41
+ }
42
+ this.entries.set(key, {
43
+ value,
44
+ expiresAt: Date.now() + this.ttlMs
45
+ });
46
+ this.sets += 1;
47
+ }
48
+ stats() {
49
+ return {
50
+ entries: this.entries.size,
51
+ max_entries: this.maxEntries,
52
+ ttl_ms: this.ttlMs,
53
+ hits: this.hits,
54
+ misses: this.misses,
55
+ sets: this.sets,
56
+ evictions: this.evictions
57
+ };
58
+ }
59
+ }
60
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAeA,MAAM,OAAO,QAAQ;IAQA;IACA;IARF,OAAO,GAAG,IAAI,GAAG,EAAyB,CAAC;IACpD,IAAI,GAAG,CAAC,CAAC;IACT,MAAM,GAAG,CAAC,CAAC;IACX,IAAI,GAAG,CAAC,CAAC;IACT,SAAS,GAAG,CAAC,CAAC;IAEtB,YACmB,aAAa,GAAG,EAChB,QAAQ,MAAM;QADd,eAAU,GAAV,UAAU,CAAM;QAChB,UAAK,GAAL,KAAK,CAAS;IAC9B,CAAC;IAEJ,GAAG,CAAC,GAAW;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEpC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;YACjB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,IAAI,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAClC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACzB,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;YACjB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC;QACf,OAAO,KAAK,CAAC;IACf,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,KAAQ;QACvB,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,IAAI,CAAC,EAAE,CAAC;YAC5C,OAAO;QACT,CAAC;QAED,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;QAED,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAC5C,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;YAEnD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC5B,MAAM;YACR,CAAC;YAED,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC/B,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC;QACtB,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE;YACpB,KAAK;YACL,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK;SACnC,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC;IACjB,CAAC;IAED,KAAK;QACH,OAAO;YACL,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI;YAC1B,WAAW,EAAE,IAAI,CAAC,UAAU;YAC5B,MAAM,EAAE,IAAI,CAAC,KAAK;YAClB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,SAAS,EAAE,IAAI,CAAC,SAAS;SAC1B,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,12 @@
1
+ export type ServerConfig = {
2
+ apiKey: string;
3
+ apiBaseUrl: string;
4
+ webBaseUrl: string;
5
+ cacheTtlMs: number;
6
+ cacheMaxEntries: number;
7
+ defaultRateLimitPerMinute: number;
8
+ exportRateLimitPerMinute: number;
9
+ requestTimeoutMs: number;
10
+ validateOnStartup: boolean;
11
+ };
12
+ export declare function loadConfig(env?: NodeJS.ProcessEnv): ServerConfig;