Skip to main content
Glama
briancasteel

Charity MCP Server

by briancasteel

charity-lookup

Retrieve detailed IRS nonprofit information including tax status, organization classification, and current standing using an EIN (Employer Identification Number).

Instructions

Look up detailed information about a charity or nonprofit organization using their EIN (Employer Identification Number). This tool retrieves comprehensive information from the IRS database including: - Official organization name and location - Tax deductibility status and codes - Organization classification and activity codes - Current status with the IRS - Foundation type and ruling information

Use this tool when you need complete details about a specific charity.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
einYesThe charity's EIN (Tax ID) in format XX-XXXXXXX or XXXXXXXXX (e.g., '13-1837418' or '131837418')

Implementation Reference

  • The primary handler function for the 'charity-lookup' tool. It handles input sanitization, validation, rate limiting, API calls to retrieve charity data by EIN, response validation and transformation, formatting, and comprehensive error handling.
    export async function handleCharityLookup(args: unknown): Promise<CallToolResult> {
      const startTime = Date.now();
      
      try {
        logger.debug("Charity lookup requested", { args });
    
        // Extract and sanitize input
        const rawInput = args as any;
        const sanitizedEIN = InputSanitizer.sanitizeEIN(rawInput?.ein || '');
        
        if (!sanitizedEIN) {
          throw new ValidationError("EIN is required", "ein");
        }
    
        // Validate EIN format and business rules
        const validatedEIN = EINValidator.validate(sanitizedEIN);
        
        logger.debug("Input validated", { originalEIN: rawInput?.ein, validatedEIN });
    
        // Check rate limit
        if (!(await rateLimiter.checkRateLimit('charity_lookup'))) {
          const resetTime = rateLimiter.getResetTime('charity_lookup');
          const resetDate = new Date(resetTime).toISOString();
          
          return {
            content: [
              {
                type: "text",
                text: `⏱️ Rate limit exceeded for charity lookup. Please try again after ${resetDate}.`,
              } as TextContent,
            ],
            isError: true,
          };
        }
    
        // Make API call
        logger.info("Looking up charity", { ein: validatedEIN });
        const apiResponse = await charityAPIClient.getOrganizationByEIN(validatedEIN);
        
        // Validate API response
        const validatedResponse = ResponseValidator.validateGetOrgResponse(apiResponse);
        
        // Transform to standardized format
        const transformedCharity = CharityTransformer.transformCharityLookup(validatedResponse, validatedEIN);
        
        // Format for display
        const formattedText = ResponseFormatter.formatCharityLookup(transformedCharity);
        
        const processingTime = Date.now() - startTime;
        logger.info("Charity lookup completed successfully", { 
          ein: validatedEIN, 
          name: transformedCharity.name,
          processingTime 
        });
    
        return {
          content: [
            {
              type: "text",
              text: formattedText,
            } as TextContent,
          ],
        };
    
      } catch (error) {
        const processingTime = Date.now() - startTime;
        logger.error("Charity lookup failed", { args, error, processingTime });
        
        let formattedError;
        let errorText: string;
    
        if (error instanceof ValidationError) {
          formattedError = ErrorFormatter.formatValidationError(error);
          errorText = ErrorFormatter.toUserMessage(formattedError);
        } else {
          const mcpError = handleMCPError(error);
          errorText = mcpError.message;
        }
    
        return {
          content: [
            {
              type: "text",
              text: errorText,
            } as TextContent,
          ],
          isError: true,
        };
      }
    }
  • The tool definition object including name, description, and input schema for validating the EIN parameter.
    export const CHARITY_LOOKUP_TOOL = {
      name: "charity-lookup",
      description: `
        Look up detailed information about a charity or nonprofit organization using their EIN (Employer Identification Number).
        This tool retrieves comprehensive information from the IRS database including:
        - Official organization name and location
        - Tax deductibility status and codes
        - Organization classification and activity codes
        - Current status with the IRS
        - Foundation type and ruling information
        
        Use this tool when you need complete details about a specific charity.
      `.trim(),
      inputSchema: {
        type: "object" as const,
        properties: {
          ein: {
            type: "string",
            description: "The charity's EIN (Tax ID) in format XX-XXXXXXX or XXXXXXXXX (e.g., '13-1837418' or '131837418')",
            pattern: "^\\d{2}-?\\d{7}$",
          },
        },
        required: ["ein"],
      },
    };
  • Registration of the 'charity-lookup' tool in the MCP list tools handler, making it discoverable.
    server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        CHARITY_LOOKUP_TOOL,
        PUBLIC_CHARITY_CHECK_TOOL,
        CHARITY_SEARCH_TOOL,
        LIST_ORGANIZATIONS_TOOL,
      ],
    }));
  • Registration of the 'charity-lookup' tool handler in the MCP call tool request switch statement.
    case CHARITY_LOOKUP_TOOL.name:
      return await handleCharityLookup(args);
  • The registerAllTools function that sets up both list and call handlers for all tools including charity-lookup.
    export function registerAllTools(server: Server) {
      try {
        // Register tools list handler
        server.setRequestHandler(ListToolsRequestSchema, async () => ({
          tools: [
            CHARITY_LOOKUP_TOOL,
            PUBLIC_CHARITY_CHECK_TOOL,
            CHARITY_SEARCH_TOOL,
            LIST_ORGANIZATIONS_TOOL,
          ],
        }));
    
        // Register tool call handler
        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}`);
          }
        });
    
        logger.info("All charity tools registered successfully", {
          tools: [
            CHARITY_LOOKUP_TOOL.name,
            PUBLIC_CHARITY_CHECK_TOOL.name,
            CHARITY_SEARCH_TOOL.name,
            LIST_ORGANIZATIONS_TOOL.name,
          ],
        });
      } catch (error) {
        logger.error("Failed to register tools", { error });
        throw error;
      }
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It discloses the data source (IRS database) and lists the types of information retrieved, which adds useful context. However, it does not mention behavioral aspects like rate limits, authentication needs, error handling, or whether the lookup is cached, leaving gaps in transparency for a tool with no annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized and front-loaded: the first sentence states the core purpose, followed by a bulleted list of retrieved information for clarity, and ends with usage guidance. Every sentence earns its place without redundancy, making it efficient and well-structured.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (single parameter, no output schema, no annotations), the description is moderately complete. It explains what the tool does and what information it retrieves, but lacks details on output format, error cases, or integration with sibling tools. This is adequate but has clear gaps for a tool without annotations or output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has 100% description coverage, clearly documenting the 'ein' parameter with format and examples. The description adds no additional parameter semantics beyond what the schema provides, such as explaining EIN significance or validation rules. This meets the baseline of 3 since the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the specific action ('Look up detailed information') and resource ('charity or nonprofit organization'), distinguishing it from siblings like 'charity-search' (likely broader search) and 'list-organizations' (likely listing multiple). It specifies using EIN as the identifier, making the purpose explicit and differentiated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear context for when to use it ('when you need complete details about a specific charity'), but does not explicitly state when not to use it or name alternatives among the sibling tools. This gives adequate guidance but lacks explicit exclusions or comparisons to other tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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