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
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | 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. |
Implementation Reference
- src/index.ts:110-151 (handler)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 }], } }, ) - src/index.ts:99-109 (schema)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 }], } }, ) - src/index.ts:52-78 (helper)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 } } - src/index.ts:80-86 (helper)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}` }