Skip to content

Commit cfb9a5c

Browse files
brendanbabbclaude
andcommitted
Harden runtime for Lambda: cache catalog, redact key, pg keepAlive
Three small production-hardening tweaks driven by running on Lambda and RDS: - list-datasets: cache the ~2MB Census data.json catalog in module-level memory with a 1h TTL. Persists across warm Lambda invocations so repeated calls don't refetch the catalog every time. - fetch-aggregate-data: redact the Census API key from the URL before logging it. Matters in Lambda because DEBUG_LOGS=true ships these lines to persistent CloudWatch. - seed-runner: enable keepAlive on the pg Client to stop RDS from silently dropping idle connections mid-seed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 8e06faf commit cfb9a5c

3 files changed

Lines changed: 25 additions & 10 deletions

File tree

mcp-db/src/seeds/scripts/seed-runner.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,11 @@ export class SeedRunner {
4141
dataPath?: string,
4242
rateLimitConfig?: Partial<RateLimitConfig>,
4343
) {
44-
this.client = new Client({ connectionString: dbUrl })
44+
this.client = new Client({
45+
connectionString: dbUrl,
46+
keepAlive: true,
47+
keepAliveInitialDelayMillis: 10000,
48+
})
4549
// Only use __dirname as fallback if no dataPath provided
4650
const defaultPath = path.join(__dirname, '../../../data')
4751
this.dataPath = dataPath || defaultPath

mcp-server/src/tools/fetch-aggregate-data.tool.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ export class FetchAggregateDataTool extends BaseTool<TableArgs> {
143143
const fetch = (await import('node-fetch')).default
144144
const res = await fetch(url)
145145

146-
console.log(`URL Attempted: ${url}`)
146+
console.log(`URL Attempted: ${url.replace(/key=[^&]*/g, 'key=REDACTED')}`)
147147

148148
if (!res.ok) {
149149
return this.createErrorResponse(

mcp-server/src/tools/list-datasets.tool.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ import { ToolContent } from '../types/base.types.js'
1616

1717
export const toolDescription = `Call this FIRST when the user asks for Census data but has not named a specific dataset; do not guess the dataset_id. Returns the full Census Bureau catalog of dataset IDs, titles, and available vintages so you can pick the right one. Workflow: list-datasets -> search-data-tables -> fetch-dataset-geography -> resolve-geography-fips -> fetch-aggregate-data.`
1818

19+
// Module-level cache — persists across warm Lambda invocations so repeated
20+
// calls don't refetch Census's ~2MB data.json catalog every time.
21+
const CATALOG_TTL_MS = 60 * 60 * 1000
22+
let catalogCache: { json: string; expiresAt: number } | null = null
23+
1924
export class ListDatasetsTool extends BaseTool<object> {
2025
name = 'list-datasets'
2126
description = toolDescription
@@ -133,6 +138,13 @@ export class ListDatasetsTool extends BaseTool<object> {
133138
args: object,
134139
apiKey: string,
135140
): Promise<{ content: ToolContent[] }> {
141+
const now = Date.now()
142+
if (catalogCache && catalogCache.expiresAt > now) {
143+
return {
144+
content: [{ type: 'text', text: catalogCache.json }],
145+
}
146+
}
147+
136148
try {
137149
const fetch = (await import('node-fetch')).default
138150
const catalogUrl = `https://api.census.gov/data.json?key=${apiKey}`
@@ -162,15 +174,14 @@ export class ListDatasetsTool extends BaseTool<object> {
162174

163175
const aggregated = this.aggregateDatasets(simplified)
164176

177+
const json = JSON.stringify(aggregated, (key, value) => {
178+
return value === null ? undefined : value
179+
})
180+
181+
catalogCache = { json, expiresAt: now + CATALOG_TTL_MS }
182+
165183
return {
166-
content: [
167-
{
168-
type: 'text',
169-
text: JSON.stringify(aggregated, (key, value) => {
170-
return value === null ? undefined : value
171-
}),
172-
},
173-
],
184+
content: [{ type: 'text', text: json }],
174185
}
175186
} catch (error) {
176187
const errorMessage =

0 commit comments

Comments
 (0)