charity-search
Search the IRS database for nonprofit organizations by name, location, or keywords to find tax-deductible charities and verify their status.
Instructions
Search for charities and nonprofit organizations in the IRS database. You can search by organization name, location, or combine multiple criteria.
Search parameters:
- query: Organization name or keywords
- city: Filter by city name
- state: Filter by state (2-letter code like 'CA', 'NY')
Returns a list of matching organizations with basic information including:
- Organization name and EIN
- Location (city, state)
- Deductibility status
Use this tool to find organizations when you don't have their exact EIN.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | Search term for organization name or keywords | |
| city | No | Filter by city name (optional) | |
| state | No | Filter by state using 2-letter abbreviation like 'CA' or 'NY' (optional) |
Implementation Reference
- src/tools/charity-search.ts:48-144 (handler)The handler function that implements the core logic of the 'charity-search' tool: input validation, rate limiting, API call to charityAPIClient.searchCharities, response formatting, and error handling.export async function handleCharitySearch(args: unknown): Promise<CallToolResult> { try { logger.debug("Charity search requested", { args }); // Validate input const input = CharitySearchInputSchema.parse(args); logger.debug("Input validated", { input }); // Ensure at least one search parameter is provided if (!input.query && !input.city && !input.state) { return { content: [ { type: "text", text: "At least one search parameter must be provided.", } as TextContent, ], isError: true, }; } // Check rate limit if (!(await rateLimiter.checkRateLimit('charity_search'))) { const resetTime = rateLimiter.getResetTime('charity_search'); const resetDate = new Date(resetTime).toISOString(); return { content: [ { type: "text", text: `Rate limit exceeded for charity search. Please try again after ${resetDate}.`, } as TextContent, ], isError: true, }; } // Prepare API parameters const apiParams = { q: input.query, city: input.city, state: input.state, }; // Make API call logger.info("Searching charities", { params: apiParams }); const response = await charityAPIClient.searchCharities(apiParams); if (!response.data) { throw new CharityAPIError("No data returned from CharityAPI", 404); } // Format response const results = Array.isArray(response.data) ? response.data : []; const output: CharitySearchOutput = { results: results.map(charity => ({ ein: charity.ein, name: charity.name, city: charity.city, state: charity.state, deductibilityCode: charity.deductibilityCode, })), }; // Create formatted text response const formattedText = formatCharitySearchResponse(output, input); logger.info("Charity search completed successfully", { resultCount: output.results.length, searchParams: apiParams }); return { content: [ { type: "text", text: formattedText, } as TextContent, ], }; } catch (error) { logger.error("Charity search failed", { args, error }); const mcpError = handleMCPError(error); return { content: [ { type: "text", text: mcpError.message, } as TextContent, ], isError: true, }; } }
- src/schemas/charity-schemas.ts:29-46 (schema)Zod schema used for runtime validation of charity-search tool inputs in the handler.export const CharitySearchInputSchema = z.object({ query: z.string() .min(3, "Search query cannot be empty") .max(200, "Search query cannot exceed 200 characters") .optional(), city: z.string() .optional() .transform((val) => val === "" ? undefined : val) .refine((val) => val === undefined || (val.length <= 100), "City name cannot exceed 100 characters"), state: z.string() .optional() .transform((val) => val === "" ? undefined : val) .transform((val) => val?.toUpperCase()) .refine((val) => val === undefined || (val.length === 2 && /^[A-Za-z]{2}$/.test(val)), "State must be a 2-letter abbreviation (e.g., CA, ca, NY, ny)"), });
- src/tools/charity-search.ts:7-46 (registration)Tool definition object including name, description, and input schema, exported and used for registration in tools/index.ts.export const CHARITY_SEARCH_TOOL = { name: "charity-search", description: ` Search for charities and nonprofit organizations in the IRS database. You can search by organization name, location, or combine multiple criteria. Search parameters: - query: Organization name or keywords - city: Filter by city name - state: Filter by state (2-letter code like 'CA', 'NY') Returns a list of matching organizations with basic information including: - Organization name and EIN - Location (city, state) - Deductibility status Use this tool to find organizations when you don't have their exact EIN. `.trim(), inputSchema: { type: "object" as const, properties: { query: { type: "string", description: "Search term for organization name or keywords", maxLength: 200, }, city: { type: "string", description: "Filter by city name (optional)", maxLength: 100, }, state: { type: "string", description: "Filter by state using 2-letter abbreviation like 'CA' or 'NY' (optional)", pattern: "^[A-Z]{2}$", }, }, required: [], }, };
- src/tools/index.ts:22-41 (registration)Registration of tool call handler in registerAllTools function, dispatching 'charity-search' calls to handleCharitySearch.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; switch (name) { case CHARITY_LOOKUP_TOOL.name: return await handleCharityLookup(args); case PUBLIC_CHARITY_CHECK_TOOL.name: return await handlePublicCharityCheck(args); case CHARITY_SEARCH_TOOL.name: return await handleCharitySearch(args); case LIST_ORGANIZATIONS_TOOL.name: return await handleListOrganizations(args); default: throw new Error(`Unknown tool: ${name}`); } });
- src/tools/charity-search.ts:146-185 (helper)Helper function to format the charity search results into a markdown response string.function formatCharitySearchResponse(output: CharitySearchOutput, input: CharitySearchInput): string { const { results } = output; let response = `# Charity Search Results\n\n`; // Search summary const searchCriteria = []; if (input.query) searchCriteria.push(`Query: "${input.query}"`); if (input.city) searchCriteria.push(`City: ${input.city}`); if (input.state) searchCriteria.push(`State: ${input.state}`); response += `**Search Criteria:** ${searchCriteria.join(', ')}\n`; response += `**Results:** ${results.length} organizations found\n\n`; if (results.length === 0) { response += `No organizations found matching your search criteria. Try:\n`; response += `- Broadening your search terms\n`; response += `- Checking spelling of organization name or location\n`; response += `- Using fewer filters\n`; return response; } // Results listing results.forEach((charity, index) => { response += `## ${index + 1}. ${charity.name}\n`; response += `**EIN:** ${charity.ein}\n`; if (charity.city || charity.state) { response += `**Location:** ${[charity.city, charity.state].filter(Boolean).join(', ')}\n`; } if (charity.deductibilityCode) { response += `**Deductibility Code:** ${charity.deductibilityCode}\n`; } response += `\n`; }); return response; }