Skip to main content
Glama
Tar-ive

Grants Search MCP Server

search-grants

Search for government grants using keywords to find funding opportunities, eligibility details, and deadlines.

Instructions

Search for government grants based on keywords

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYesSearch query for grants (e.g., 'Artificial intelligence', 'Climate change')
pageNoPage number for pagination (default: 1)
grantsPerPageNoNumber of grants per page (default: 3)

Implementation Reference

  • Executes the 'search-grants' tool: parses arguments, queries the grants.gov API via POST request with filters and pagination, handles responses, errors, and formats output using helper functions.
    if (request.params.name === "search-grants") {
      try {
        const args = request.params.arguments as { query?: string; page?: number; grantsPerPage?: number } | undefined;
        const searchQuery = args?.query ? String(args.query).trim() : "Artificial intelligence";
        const page = args?.page || 1;
        const grantsPerPage = args?.grantsPerPage || 3;
        
        console.error(`Debug: Starting search with query: ${searchQuery}, page: ${page}, grantsPerPage: ${grantsPerPage}`);
    
        const url = 'https://api.simpler.grants.gov/v1/opportunities/search';
        const searchData = {
          filters: {
            opportunity_status: {
              one_of: ["forecasted", "posted"]
            }
          },
          pagination: {
            order_by: "opportunity_id",
            page_offset: page,
            page_size: grantsPerPage,
            sort_direction: "descending"
          },
          query: searchQuery
        };
    
        const response = await axios.post<GrantsAPIResponse>(url, searchData, {
          headers: {
            'accept': 'application/json',
            'X-Auth': API_KEY,
            'Content-Type': 'application/json'
          }
        });
    
        if (!response.data?.data) {
          return {
            content: [{
              type: "text",
              text: "No results found or invalid response format"
            }]
          };
        }
    
        const grants = response.data.data;
        if (grants.length === 0) {
          return {
            content: [{
              type: "text",
              text: `No grants found matching "${searchQuery}"`
            }]
          };
        }
    
        const summaryText = createSummary(grants, searchQuery, page, grantsPerPage);
    
        return {
          content: [{
            type: "text",
            text: summaryText
          }]
        };
    
      } catch (error) {
        console.error('Debug: Error occurred:', error);
        if (axios.isAxiosError(error)) {
          console.error('Debug: Axios error response:', error.response?.data);
          return {
            content: [{
              type: "text",
              text: `API Error: ${error.response?.data?.message || error.message}`
            }]
          };
        }
        return {
          content: [{
            type: "text",
            text: `Error: ${error instanceof Error ? error.message : 'Unknown error occurred'}`
          }]
        };
      }
    }
  • Input schema definition for the 'search-grants' tool, specifying query (required), page, and grantsPerPage parameters.
    inputSchema: {
      type: "object",
      properties: {
        query: {
          type: "string",
          description: "Search query for grants (e.g., 'Artificial intelligence', 'Climate change')"
        },
        page: {
          type: "number",
          description: "Page number for pagination (default: 1)"
        },
        grantsPerPage: {
          type: "number",
          description: "Number of grants per page (default: 3)"
        }
      },
      required: ["query"]
    }
  • src/index.ts:121-146 (registration)
    Registers the 'search-grants' tool in the MCP server's listTools handler, including its name, description, and input schema.
    server.setRequestHandler(ListToolsRequestSchema, async () => {
      return {
        tools: [{
          name: "search-grants",
          description: "Search for government grants based on keywords",
          inputSchema: {
            type: "object",
            properties: {
              query: {
                type: "string",
                description: "Search query for grants (e.g., 'Artificial intelligence', 'Climate change')"
              },
              page: {
                type: "number",
                description: "Page number for pagination (default: 1)"
              },
              grantsPerPage: {
                type: "number",
                description: "Number of grants per page (default: 3)"
              }
            },
            required: ["query"]
          }
        }]
      };
    });
  • Helper function to format detailed information for a single grant into a structured text block.
    const formatGrantDetails = (grant: Grant) => {
      return `
    OPPORTUNITY DETAILS
    ------------------
    Title: ${grant.opportunity_title}
    Opportunity Number: ${grant.opportunity_number}
    Agency: ${grant.agency_name} (${grant.agency_code})
    Status: ${grant.opportunity_status}
    
    FUNDING INFORMATION
    ------------------
    Award Floor: ${grant.summary.award_floor ? `$${grant.summary.award_floor.toLocaleString()}` : 'Not specified'}
    Award Ceiling: ${grant.summary.award_ceiling ? `$${grant.summary.award_ceiling.toLocaleString()}` : 'Not specified'}
    Category: ${grant.category}
    
    DATES AND DEADLINES
    ------------------
    Posted Date: ${grant.summary.post_date || 'N/A'}
    Close Date: ${grant.summary.close_date || 'N/A'}
    
    CONTACT INFORMATION
    ------------------
    Agency Contact: ${grant.summary.agency_contact_description || 'Not provided'}
    Email: ${grant.summary.agency_email_address || 'Not provided'}
    Phone: ${grant.summary.agency_phone_number || 'Not provided'}
    
    ELIGIBILITY
    ------------------
    ${grant.summary.applicant_eligibility_description ? 
      grant.summary.applicant_eligibility_description.replace(/<[^>]*>/g, '').trim() : 
      'Eligibility information not provided'}
    
    ADDITIONAL INFORMATION
    ------------------
    More Details URL: ${grant.summary.additional_info_url || 'Not available'}
    
    Description:
    ${grant.summary.summary_description ? 
      grant.summary.summary_description.replace(/<[^>]*>/g, '').trim() : 
      'No description available'}
    
    ==========================================================================
    `;
    };
  • Helper function to create a paginated summary of grant search results, using formatGrantDetails for each grant.
    const createSummary = (grants: Grant[], searchQuery: string, page: number = 1, grantsPerPage: number = 3) => {
      const startIdx = (page - 1) * grantsPerPage;
      const endIdx = startIdx + grantsPerPage;
      const displayedGrants = grants.slice(startIdx, endIdx);
      const totalPages = Math.ceil(grants.length / grantsPerPage);
    
      return `Search Results for "${searchQuery}":
    
    OVERVIEW
    --------
    Total Grants Found: ${grants.length}
    Showing grants ${startIdx + 1} to ${Math.min(endIdx, grants.length)} of ${grants.length}
    Page ${page} of ${totalPages}
    
    DETAILED GRANT LISTINGS
    ----------------------
    ${displayedGrants.map(formatGrantDetails).join("\n")}
    
    Note: Showing ${grantsPerPage} grants per page. Total grants available: ${grants.length}
    `;
    };
Behavior2/5

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

No annotations are provided, so the description carries the full burden of behavioral disclosure. It mentions searching but doesn't describe behavioral traits such as rate limits, authentication needs, response format, error handling, or whether it's read-only or has side effects. For a search tool with zero annotation coverage, this leaves significant gaps in understanding how it behaves.

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 a single, efficient sentence that directly states the tool's function without unnecessary words. It is appropriately sized and front-loaded, making it easy to understand quickly. Every part of the sentence contributes to clarifying the purpose.

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

Completeness2/5

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

Given the complexity of a search tool with no annotations and no output schema, the description is incomplete. It lacks information on behavioral aspects, usage context, and what to expect in return. While the schema covers parameters well, the overall context for effective tool use is insufficient, especially for an agent needing to understand results and limitations.

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 description adds minimal semantic context beyond the input schema, which has 100% coverage with clear descriptions for all parameters. It implies keyword-based searching but doesn't provide additional details like search scope, result types, or parameter interactions. With high schema coverage, the baseline is 3, as the schema does most of the work.

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

Purpose4/5

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

The description clearly states the tool's purpose as 'Search for government grants based on keywords', which includes a specific verb ('Search'), resource ('government grants'), and mechanism ('based on keywords'). It distinguishes the tool's function well, though without sibling tools, differentiation isn't applicable. The purpose is specific and actionable.

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

Usage Guidelines2/5

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

The description provides no guidance on when to use this tool versus alternatives, prerequisites, or limitations. It simply states what the tool does without context about appropriate scenarios or constraints. Since there are no sibling tools, this is less critical, but general usage context is still missing.

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/Tar-ive/grants-mcp'

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