Skip to main content
Glama
briancasteel

Charity MCP Server

by briancasteel

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
NameRequiredDescriptionDefault
queryNoSearch term for organization name or keywords
cityNoFilter by city name (optional)
stateNoFilter by state using 2-letter abbreviation like 'CA' or 'NY' (optional)

Implementation Reference

  • 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, }; } }
  • 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)"), });
  • 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: [], }, };
  • 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}`); } });
  • 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; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/briancasteel/charity-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server