Skip to main content
Glama
agilesix
by agilesix
analyze-competition.ts4.45 kB
/** * Analyze Competition Tool * Analyze the competitive landscape for similar awards */ import { z } from 'zod'; import type { USASpendingClient } from '../clients/usaspending'; import { buildAwardFilters } from '../builders/filter-builder'; import { COMPETITION_FIELDS, transformCompetitionRecipient } from '../builders/field-mapper'; import type { AnalyzeCompetitionParams } from '../types/tool-params'; export function registerAnalyzeCompetitionTool(server: any, client: USASpendingClient) { server.tool( "analyze_competition", "Analyze the competitive landscape based on transaction activity. Shows who's winning awards/modifications, market shares, and spending patterns. NOTE: Analyzes transaction activity, which includes both new awards and modifications to existing awards.", { keywords: z.array(z.string()).optional().describe("Keywords describing the type of work"), agencyName: z.string().optional().describe("Agency to analyze"), naicsCodes: z.array(z.string()).optional().describe("NAICS codes for the industry"), pscCodes: z.array(z.string()).optional().describe("Product Service Codes"), activityStartDate: z.string().optional().describe("Start date in YYYY-MM-DD format for transaction activity (defaults to 1 year ago if not provided)"), activityEndDate: z.string().optional().describe("End date in YYYY-MM-DD format for transaction activity (defaults to today if not provided)"), minAmount: z.number().optional().describe("Minimum award size to analyze"), limit: z.number().min(1).max(100).optional().describe("Number of top recipients to show (default: 20)"), }, async (params: AnalyzeCompetitionParams) => { try { // Build filters using centralized filter builder // Always apply date filter for competition analysis (defaults to last year) const activityStartDate = params.activityStartDate || (() => { const date = new Date(); date.setFullYear(date.getFullYear() - 1); return date.toISOString().split('T')[0]; })(); const activityEndDate = params.activityEndDate || new Date().toISOString().split('T')[0]; const filters = buildAwardFilters({ keywords: params.keywords, agencyName: params.agencyName, naicsCodes: params.naicsCodes, pscCodes: params.pscCodes, activityStartDate, activityEndDate, minAmount: params.minAmount, }); // Get awards and aggregate by recipient const searchParams: any = { filters, fields: COMPETITION_FIELDS, limit: 100, // Get more results for better analysis page: 1, sort: "Award Amount", order: "desc" as const, }; const result = await client.searchAwards(searchParams); // Aggregate by recipient const recipientMap = new Map<string, { name: string, uei: string, totalAmount: number, awardCount: number, awards: string[], }>(); for (const award of result.results || []) { const name = String(award["Recipient Name"] || "Unknown"); const uei = String(award["Recipient UEI"] || ""); const amount = Number(award["Award Amount"]) || 0; const id = String(award["Award ID"] || ""); if (!recipientMap.has(name)) { recipientMap.set(name, { name, uei, totalAmount: 0, awardCount: 0, awards: [], }); } const recipient = recipientMap.get(name)!; recipient.totalAmount += amount; recipient.awardCount += 1; recipient.awards.push(id); } // Convert to array and sort by total amount const topRecipients = Array.from(recipientMap.values()) .sort((a, b) => b.totalAmount - a.totalAmount) .slice(0, params.limit || 20); const totalMarketSize = topRecipients.reduce((sum, r) => sum + r.totalAmount, 0); return { content: [ { type: "text", text: JSON.stringify( { summary: `Competitive analysis showing top ${topRecipients.length} recipients`, total_awards_analyzed: result.page_metadata?.total || 0, total_market_size: totalMarketSize, top_recipients: topRecipients.map(r => transformCompetitionRecipient(r, totalMarketSize)), }, null, 2 ), }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error: ${error instanceof Error ? error.message : "Unknown error"}`, }, ], }; } } ); }

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/agilesix/usaspending-mcp-nextjs'

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