Full-Text Search Across Endpoints
procore_search_endpointsSearch across all Procore API endpoints using terms like 'RFI' or 'budget'. Returns ranked matches to quickly locate the desired endpoint.
Instructions
Full-text search across every Procore API endpoint summary, tag, and path. Use to quickly locate the right endpoint when you know roughly what you're looking for — e.g. 'RFI', 'budget', 'punch list', 'submittal'. Returns a JSON array of matches ranked by relevance.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Search term, e.g. 'RFI', 'budget', 'punch list', 'submittal' |
Implementation Reference
- Handler function that calls the catalog searchEndpoints() service and formats results into a human-readable string response.
export async function handleSearchEndpoints(args: { query: string; }): Promise<string> { const results = searchEndpoints(args.query); if (results.length === 0) { return `No endpoints found matching "${args.query}". Try different search terms or use procore_discover_categories to browse.`; } const lines: string[] = [ `Found ${results.length} endpoints matching "${args.query}":\n`, ]; for (const e of results) { lines.push( `- **${e.method}** \`${e.path}\` — ${e.summary}` ); lines.push( ` Category: ${e.category} / ${e.module} | operationId: \`${e.operationId}\`` ); } lines.push( "\nUse procore_get_endpoint_details with an operationId for full parameter info." ); return lines.join("\n"); } - src/catalog/service.ts:51-79 (helper)Core search logic: scores catalog entries by matching query terms against summary, tag, and path, then returns top 50 results ranked by relevance.
export function searchEndpoints(query: string): CatalogEntry[] { const catalog = loadCatalog(); const terms = query.toLowerCase().split(/\s+/); // Score each entry const scored = catalog.map((entry) => { const summaryLower = entry.summary.toLowerCase(); const tagLower = entry.tag.toLowerCase(); const pathLower = entry.path.toLowerCase(); let score = 0; for (const term of terms) { if (summaryLower.includes(term)) score += 10; if (tagLower.includes(term)) score += 5; if (pathLower.includes(term)) score += 3; } // Boost exact matches if (summaryLower === query.toLowerCase()) score += 50; return { entry, score }; }); return scored .filter((s) => s.score > 0) .sort((a, b) => b.score - a.score) .slice(0, 50) .map((s) => s.entry); } - src/tools/registry.ts:178-184 (schema)Input schema for the tool: a single required 'query' string parameter validated via Zod.
inputSchema: { query: z .string() .describe( "Search term, e.g. 'RFI', 'budget', 'punch list', 'submittal'" ), }, - src/tools/registry.ts:169-191 (registration)Registration of the 'procore_search_endpoints' tool on the MCP server, wiring the handler and schema together.
server.registerTool( "procore_search_endpoints", { title: "Full-Text Search Across Endpoints", description: "Full-text search across every Procore API endpoint summary, tag, and path. " + "Use to quickly locate the right endpoint when you know roughly what you're " + "looking for — e.g. 'RFI', 'budget', 'punch list', 'submittal'. Returns " + "a JSON array of matches ranked by relevance.", inputSchema: { query: z .string() .describe( "Search term, e.g. 'RFI', 'budget', 'punch list', 'submittal'" ), }, annotations: { title: "Search Endpoints", ...READ_ONLY }, }, async (args) => { const text = await handleSearchEndpoints(args); return { content: [{ type: "text" as const, text }] }; } ); - src/catalog/types.ts:1-14 (schema)Type definition for CatalogEntry, the data structure returned by the search.
export interface CatalogEntry { operationId: string; method: string; path: string; summary: string; tag: string; category: string; module: string; version: string; pathParams: string[]; requiredParams: string[]; hasRequestBody: boolean; contentType: string | null; }