grants_search
Search federal grant opportunities on Grants.gov. Filter by keyword, CFDA number, agency code, or opportunity number. Includes forecasted and posted opportunities by default.
Instructions
Search Grants.gov federal grant opportunities (financial assistance, distinct from contracts on SAM.gov). Filter by keyword / CFDA / agency / opportunity number. Default status = forecasted + posted.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| keyword | No | ||
| cfda | No | CFDA program number, e.g. '10.500' | |
| agency | No | Grants.gov agency code, e.g. 'DHS-FEMA' | |
| oppNum | No | Specific opportunity number | |
| oppStatuses | No | Defaults to forecasted+posted | |
| rows | No |
Implementation Reference
- src/grants.ts:35-89 (handler)The searchGrants function that executes the "grants_search" tool logic. It calls the Grants.gov /v1/api/search2 endpoint with keyword, CFDA, agency, oppNum, oppStatuses, and rows parameters, then returns filtered grant results.
export async function searchGrants(args: { keyword?: string; cfda?: string; // CFDA program number, e.g. "10.500" agency?: string; // agency code, e.g. "DHS-FEMA" oppNum?: string; // opportunity number oppStatuses?: GrantStatus[]; rows?: number; }) { const body: Record<string, unknown> = { rows: args.rows ?? 10, keyword: args.keyword ?? "", cfda: args.cfda ?? "", agencies: args.agency ?? "", oppNum: args.oppNum ?? "", oppStatuses: (args.oppStatuses ?? ["forecasted", "posted"]).join("|"), }; type Resp = { errorcode?: number; msg?: string; data?: { hitCount?: number; oppHits?: { id?: string; number?: string; title?: string; agencyCode?: string; agencyName?: string; openDate?: string; closeDate?: string; oppStatus?: string; docType?: string; cfdaList?: string; }[]; }; }; const json = await postJson<Resp>("search2", body); if (json.errorcode && json.errorcode !== 0) { throw new Error(`Grants.gov error: ${json.msg ?? "unknown"}`); } return { totalRecords: json.data?.hitCount ?? 0, grants: (json.data?.oppHits ?? []).map((g) => ({ id: g.id ?? "", opportunityNumber: g.number ?? "", title: g.title ?? "", agencyCode: g.agencyCode ?? "", agencyName: g.agencyName ?? "", openDate: g.openDate, closeDate: g.closeDate, status: g.oppStatus, docType: g.docType, cfdaList: g.cfdaList, })), }; } - src/server.ts:253-266 (schema)Zod schema for the grants_search tool input: keyword (optional string), cfda (optional string), agency (optional string), oppNum (optional string), oppStatuses (optional array of forecasted/posted/closed/archived), rows (optional number 1-50).
const GrantsSearchInput = z.object({ keyword: z.string().optional(), cfda: z.string().optional().describe("CFDA program number, e.g. '10.500'"), agency: z .string() .optional() .describe("Grants.gov agency code, e.g. 'DHS-FEMA'"), oppNum: z.string().optional().describe("Specific opportunity number"), oppStatuses: z .array(z.enum(["forecasted", "posted", "closed", "archived"])) .optional() .describe("Defaults to forecasted+posted"), rows: z.number().min(1).max(50).optional(), }); - src/server.ts:506-512 (registration)Tool registration entry for "grants_search" in the tools array, with description and inputSchema reference to GrantsSearchInput.
// ━━━ Grants.gov (2) ━━━ { name: "grants_search", description: "Search Grants.gov federal grant opportunities (financial assistance, distinct from contracts on SAM.gov). Filter by keyword / CFDA / agency / opportunity number. Default status = forecasted + posted.", inputSchema: GrantsSearchInput, }, - src/grants.ts:17-31 (helper)The postJson helper function used by searchGrants to POST JSON requests to Grants.gov API with a 15-second timeout.
async function postJson<T>( endpoint: string, body: Record<string, unknown>, ): Promise<T> { const r = await fetch(`${GRANTS}/${endpoint}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), signal: AbortSignal.timeout(15_000), }); if (!r.ok) { throw new Error(`Grants.gov ${endpoint} returned ${r.status}`); } return (await r.json()) as T; } - src/server.ts:784-786 (handler)The call handler dispatch case for "grants_search" which parses args with GrantsSearchInput and calls grants.searchGrants().
// Grants.gov case "grants_search": return await grants.searchGrants(GrantsSearchInput.parse(args));