Skip to main content
Glama
carlosahumada89

govrider-mcp-server

search_opportunities

Search global government procurement opportunities for your technology, product, or service. Get sector, region, funding type, estimated value, deadline, and status for up to 10 matches. Free initial discovery without credits.

Instructions

Search government procurement opportunities worldwide. Returns sector category, region, funding type, estimated value, deadline, and status for up to 10 matches. FREE - no credits required. Always searches globally across all sources. Use this for initial discovery before committing credits to enriched search.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYesDescription of the technology, product, or service to match against government opportunities (min 10 chars). Include any country, region, or sector context directly in the description.

Implementation Reference

  • The async handler function for search_opportunities that calls the /api/v1/search endpoint, processes the response, and formats output with sector, region, funding type, estimated value, deadline, and status.
      async ({ query }) => {
        const body: Record<string, unknown> = { query }
    
        const { ok, status, data } = await apiCall('/api/v1/search', body)
    
        if (!ok) {
          return { content: [{ type: 'text' as const, text: errorText(data, status) }], isError: true }
        }
    
        const matches = data.matches as Array<Record<string, unknown>>
        const totalScanned = data.total_scanned as number
    
        if (!matches || matches.length === 0) {
          return {
            content: [{
              type: 'text' as const,
              text: `No opportunities found matching "${query.slice(0, 100)}". Try broadening your search or removing filters.`,
            }],
          }
        }
    
        const lines = matches.map((m, i) => {
          const parts = [
            `${i + 1}. **${m.sector}**`,
            `   Region: ${m.region ?? 'Unknown'}`,
            `   Type: ${m.funding_type ?? 'Unknown'}`,
            `   Value: ${m.estimated_value ?? 'Not disclosed'}`,
            `   Deadline: ${m.deadline ?? 'Unknown'}`,
            `   Status: ${m.status}`,
            `   ID: ${m.id}`,
          ]
          return parts.join('\n')
        })
    
        const header = `Found ${matches.length} opportunities (scanned ${totalScanned?.toLocaleString() ?? '?'} active listings):\n`
        const footer = '\n---\nUse **search_enriched** with the same query to get full AI analysis, match scores, titles, descriptions, and application URLs (costs 1 credit).'
    
        return {
          content: [{ type: 'text' as const, text: header + lines.join('\n\n') + footer }],
        }
      },
    )
  • The input schema definition for search_opportunities using Zod, with a 'query' string parameter (min 10, max 10000 chars) described for searching government opportunities.
    {
      description:
        'Search government procurement opportunities worldwide. Returns sector category, region, funding type, estimated value, deadline, and status for up to 10 matches. FREE - no credits required. Always searches globally across all sources. Use this for initial discovery before committing credits to enriched search.',
      inputSchema: {
        query: z
          .string()
          .min(10)
          .max(10000)
          .describe('Description of the technology, product, or service to match against government opportunities (min 10 chars). Include any country, region, or sector context directly in the description.'),
      },
    },
  • src/index.ts:97-151 (registration)
    Registration of the 'search_opportunities' tool via server.registerTool with its schema and handler.
    server.registerTool(
      'search_opportunities',
      {
        description:
          'Search government procurement opportunities worldwide. Returns sector category, region, funding type, estimated value, deadline, and status for up to 10 matches. FREE - no credits required. Always searches globally across all sources. Use this for initial discovery before committing credits to enriched search.',
        inputSchema: {
          query: z
            .string()
            .min(10)
            .max(10000)
            .describe('Description of the technology, product, or service to match against government opportunities (min 10 chars). Include any country, region, or sector context directly in the description.'),
        },
      },
      async ({ query }) => {
        const body: Record<string, unknown> = { query }
    
        const { ok, status, data } = await apiCall('/api/v1/search', body)
    
        if (!ok) {
          return { content: [{ type: 'text' as const, text: errorText(data, status) }], isError: true }
        }
    
        const matches = data.matches as Array<Record<string, unknown>>
        const totalScanned = data.total_scanned as number
    
        if (!matches || matches.length === 0) {
          return {
            content: [{
              type: 'text' as const,
              text: `No opportunities found matching "${query.slice(0, 100)}". Try broadening your search or removing filters.`,
            }],
          }
        }
    
        const lines = matches.map((m, i) => {
          const parts = [
            `${i + 1}. **${m.sector}**`,
            `   Region: ${m.region ?? 'Unknown'}`,
            `   Type: ${m.funding_type ?? 'Unknown'}`,
            `   Value: ${m.estimated_value ?? 'Not disclosed'}`,
            `   Deadline: ${m.deadline ?? 'Unknown'}`,
            `   Status: ${m.status}`,
            `   ID: ${m.id}`,
          ]
          return parts.join('\n')
        })
    
        const header = `Found ${matches.length} opportunities (scanned ${totalScanned?.toLocaleString() ?? '?'} active listings):\n`
        const footer = '\n---\nUse **search_enriched** with the same query to get full AI analysis, match scores, titles, descriptions, and application URLs (costs 1 credit).'
    
        return {
          content: [{ type: 'text' as const, text: header + lines.join('\n\n') + footer }],
        }
      },
    )
  • The apiCall helper used by the handler to make HTTP POST requests to the GovRider API.
    async function apiCall(
      path: string,
      body?: Record<string, unknown>,
      method: 'GET' | 'POST' = 'POST',
    ): Promise<{
      ok: boolean
      status: number
      data: Record<string, unknown>
    }> {
      const res = await fetch(`${API_BASE}${path}`, {
        method,
        headers: {
          'Authorization': `Bearer ${API_KEY}`,
          'Content-Type': 'application/json',
        },
        ...(body ? { body: JSON.stringify(body) } : {}),
      })
    
      let data: Record<string, unknown>
      try {
        data = (await res.json()) as Record<string, unknown>
      } catch {
        data = { error: 'invalid_response', message: `Server returned ${res.status} with non-JSON body` }
      }
    
      return { ok: res.ok, status: res.status, data }
    }
  • The errorText helper used by the handler to format error messages based on HTTP status codes.
    function errorText(data: Record<string, unknown>, status: number): string {
      const msg = (data.message as string) ?? 'Unknown error'
      if (status === 401) return `Authentication failed: ${msg}. Check your GOVRIDER_API_KEY.`
      if (status === 402) return `No credits remaining. Purchase more at https://govrider.ai/pricing`
      if (status === 429) return `Rate limit exceeded. ${msg}`
      return `Error (${status}): ${msg}`
    }
Behavior4/5

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

No annotations provided, so description carries full burden. It discloses result limit (10 matches), free cost, and global search. Does not explicitly state read-only behavior, but search operation implies no side effects.

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?

Three sentences with no wasted words. Front-loads purpose, then adds key usage guidance and differentiating details.

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

Completeness5/5

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

Single parameter, no output schema, yet description covers what it returns, limits, cost, scope, and relationship to sibling tool. Complete for the tool's complexity.

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?

Schema coverage is 100% with a detailed description. The tool description does not add significant meaning beyond the schema; it reiterates context inclusion advice already in the schema description.

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?

Explicitly states it searches government procurement opportunities worldwide, lists returned fields, and distinguishes from sibling tool search_enriched by noting free usage and global search.

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

Usage Guidelines5/5

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

Clearly advises use for initial discovery before committing credits to enriched search, implying when to switch to search_enriched. Provides context on free usage and global scope.

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/carlosahumada89/govrider-mcp-server'

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