usas_search_expiring_contracts
Search federal contracts expiring within N months by agency and NAICS. Results sorted by end date, top 10 by value. Identify recompete opportunities for upcoming contract actions.
Instructions
Find federal contracts at agency × NAICS that expire within N months. Recompete radar — end-date sorted, top 10 by value. Use for 'what VA cloud contracts are up for recompete' or 'show 541512 contracts expiring in 6 months'.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| agency | No | ||
| naics | No | ||
| fiscalYear | No | ||
| monthsUntilExpiry | No | ||
| minAwardValue | No | ||
| limit | No |
Implementation Reference
- src/usaspending.ts:337-405 (handler)Main handler function for the 'usas_search_expiring_contracts' tool. Searches USAspending for contracts expiring within N months by: (1) querying search/spending_by_award with filters, (2) filtering by minAwardValue and generated_internal_id, (3) enriching up to 8 contracts via getAwardDetail to get period_of_performance dates, (4) filtering to contracts expiring within the cutoff days, (5) sorting by daysUntilExpiry ascending.
export async function searchExpiringContracts(args: { agency?: string; naics?: string; fiscalYear?: number; monthsUntilExpiry?: number; minAwardValue?: number; limit?: number; }) { const filters = buildFilters(args); type SearchResp = { results?: { "Award ID"?: string; "Recipient Name"?: string; "Award Amount"?: number; generated_internal_id?: string; }[]; }; const search = await postUsas<SearchResp>("search/spending_by_award", { filters, fields: ["Award ID", "Recipient Name", "Award Amount"], limit: 50, page: 1, subawards: false, sort: "Award Amount", order: "desc", }); const candidates = (search.results ?? []).filter( (r) => (r["Award Amount"] ?? 0) >= (args.minAwardValue ?? 100_000) && r.generated_internal_id, ); // Enrich up to 8 in parallel — be polite to USAspending. const enrich = candidates.slice(0, 8); const details = await Promise.all( enrich.map((r) => getAwardDetail(r.generated_internal_id!)), ); const now = Date.now(); const cutoffDays = (args.monthsUntilExpiry ?? 12) * 30; const contracts = details .map((d, idx) => { if (!d || !d.periodOfPerformance.endDate) return null; const end = new Date(d.periodOfPerformance.endDate).getTime(); if (Number.isNaN(end)) return null; const days = Math.ceil((end - now) / (24 * 60 * 60 * 1000)); if (days < -30 || days > cutoffDays) return null; const orig = enrich[idx] ?? {}; return { awardId: d.awardId || orig["Award ID"] || "", recipient: d.recipient || orig["Recipient Name"] || "", amount: d.totalObligation || orig["Award Amount"] || 0, endDate: d.periodOfPerformance.endDate, potentialEndDate: d.periodOfPerformance.potentialEndDate, awardingAgency: d.awardingAgency ?? "", awardingSubAgency: d.awardingSubAgency, naicsCode: d.naicsCode, setAsideDescription: d.setAsideDescription, description: d.description, daysUntilExpiry: days, }; }) .filter((x): x is NonNullable<typeof x> => x !== null) .slice(0, args.limit ?? 10) .sort((a, b) => a.daysUntilExpiry - b.daysUntilExpiry); return { contracts, searchedCount: candidates.length }; } - src/usaspending.ts:42-67 (helper)Shared buildFilters helper that constructs USAspending API filter objects from agency, naics, fiscalYear, setAside, and pscCodes parameters. Used by searchExpiringContracts to build the query filters.
function buildFilters(args: { agency?: string; naics?: string; fiscalYear?: number; setAside?: string; pscCodes?: string[]; }): UsasFilters { const filters: UsasFilters = { award_type_codes: ["A", "B", "C", "D"] }; if (args.agency) { filters.agencies = [ { type: "awarding", tier: "toptier", name: args.agency }, ]; } if (args.naics) filters.naics_codes = [args.naics]; if (args.fiscalYear) { filters.time_period = [ { start_date: `${args.fiscalYear - 1}-10-01`, end_date: `${args.fiscalYear}-09-30`, }, ]; } if (args.setAside) filters.set_aside_type_codes = [args.setAside]; if (args.pscCodes?.length) filters.psc_codes = args.pscCodes; return filters; } - src/usaspending.ts:277-333 (helper)Helper function getAwardDetail fetches detailed award data from the USAspending awards/{id} endpoint, returning period_of_performance dates, set-aside info, competition data, and NAICS codes. Used by searchExpiringContracts to enrich expiring contract candidates with end dates.
export async function getAwardDetail(generatedInternalId: string) { try { const r = await fetch( `${USAS}/awards/${encodeURIComponent(generatedInternalId)}/`, { signal: AbortSignal.timeout(10_000) }, ); if (!r.ok) return null; type Resp = { piid?: string; description?: string; total_obligation?: number; base_and_all_options?: number; period_of_performance?: { start_date?: string; end_date?: string; potential_end_date?: string; }; latest_transaction_contract_data?: { type_set_aside?: string; type_set_aside_description?: string; extent_competed?: string; number_of_offers_received?: string; naics?: string; naics_description?: string; }; awarding_agency?: { toptier_agency?: { name?: string }; subtier_agency?: { name?: string }; }; recipient?: { recipient_name?: string }; }; const json = (await r.json()) as Resp; const ltc = json.latest_transaction_contract_data ?? {}; return { awardId: json.piid ?? "", recipient: json.recipient?.recipient_name ?? "", totalObligation: json.total_obligation ?? 0, baseAndAllOptions: json.base_and_all_options ?? 0, periodOfPerformance: { startDate: json.period_of_performance?.start_date ?? null, endDate: json.period_of_performance?.end_date ?? null, potentialEndDate: json.period_of_performance?.potential_end_date ?? null, }, description: json.description ?? "", setAsideType: ltc.type_set_aside, setAsideDescription: ltc.type_set_aside_description, competitionExtent: ltc.extent_competed, numberOfOffers: ltc.number_of_offers_received, awardingAgency: json.awarding_agency?.toptier_agency?.name, awardingSubAgency: json.awarding_agency?.subtier_agency?.name, naicsCode: ltc.naics, naicsDescription: ltc.naics_description, }; } catch { return null; } } - src/server.ts:112-119 (schema)Zod input schema (UsasExpiringInput) defining the tool's input parameters: agency, naics, fiscalYear, monthsUntilExpiry, minAwardValue, limit.
const UsasExpiringInput = z.object({ agency: z.string().optional(), naics: z.string().optional(), fiscalYear: z.number().int().min(2007).optional(), monthsUntilExpiry: z.number().min(1).max(36).optional(), minAwardValue: z.number().optional(), limit: z.number().min(1).max(20).optional(), }); - src/server.ts:355-360 (registration)Tool registration entry in the MCP server tool list, mapping the name 'usas_search_expiring_contracts' to its description and input schema. The handler dispatch at line 701-702 calls usas.searchExpiringContracts with the parsed input.
{ name: "usas_search_expiring_contracts", description: "Find federal contracts at agency × NAICS that expire within N months. Recompete radar — end-date sorted, top 10 by value. Use for 'what VA cloud contracts are up for recompete' or 'show 541512 contracts expiring in 6 months'.", inputSchema: UsasExpiringInput, },