recommend
Get an opinionated API recommendation for any development task. Input your requirements like task, volume, budget, and constraints to receive the best API pick with pricing, working code, and a runner-up alternative.
Instructions
Get an opinionated API recommendation for a task. Returns ONE best pick with pricing, working quickstart code, setup time, and a runner-up. Better than guessing - uses verified pricing and setup data that's more current than your training data.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| task | Yes | What you need, e.g. 'send transactional emails', 'process payments' | |
| volume | No | Monthly volume (default 10000) | |
| budget | No | Max monthly USD (0 = free tier only, omit for no limit) | |
| priority | No | What matters most (default: simplicity) | |
| constraints | No | Constraints: 'no-credit-card', 'no-domain-verification' |
Implementation Reference
- src/index.ts:611-688 (registration)Registration of the 'recommend' tool on the MCP server with its description and schema, plus the handler function that calls the API and formats the result.
// Tool 7: recommend server.tool( "recommend", "Get an opinionated API recommendation for a task. Returns ONE best pick with pricing, working quickstart code, setup time, and a runner-up. Better than guessing - uses verified pricing and setup data that's more current than your training data.", { task: z.string().describe("What you need, e.g. 'send transactional emails', 'process payments'"), volume: z.number().optional().describe("Monthly volume (default 10000)"), budget: z.number().optional().describe("Max monthly USD (0 = free tier only, omit for no limit)"), priority: z.enum(["cost", "simplicity", "deliverability", "scale"]).optional().describe("What matters most (default: simplicity)"), constraints: z.array(z.string()).optional().describe("Constraints: 'no-credit-card', 'no-domain-verification'"), }, async ({ task, volume, budget, priority, constraints }) => { try { const params: Record<string, string> = { task }; if (volume !== undefined) params.volume = String(volume); if (budget !== undefined) params.budget = String(budget); if (priority) params.priority = priority; if (constraints?.length) params.constraints = constraints.join(","); const data = await apiGet<RecommendResponse>("/recommend", params); if (!data.recommendation) { return textResult( `No APIs match your constraints for "${task}".\n` + (data.message || "Try relaxing budget or removing constraints.") ); } const rec = data.recommendation; const lines = [ `## Recommendation: ${rec.name}`, "", `**Why:** ${rec.reasoning.join(". ")}`, "", `### Pricing`, `Monthly cost at ${(data.volume).toLocaleString()} emails: **$${rec.monthlyCost.toFixed(2)}**`, `Free tier: ${rec.pricing.freeRequestsPerMonth.toLocaleString()} emails/mo`, `At scale: $${rec.pricing.costAt10k} (10K) | $${rec.pricing.costAt50k} (50K) | $${rec.pricing.costAt100k} (100K)`, "", `### Setup`, `Time to first request: ${rec.setup.timeToFirstRequest} min`, `Lines of code: ${rec.setup.linesOfCode}`, `Domain verification: ${rec.setup.requiresDomainVerification ? "Required" : "Not required"}`, `Credit card: ${rec.setup.requiresCreditCard ? "Required" : "Not required"}`, "", `### Quickstart (${rec.quickstart.language})`, "```" + rec.quickstart.language, rec.quickstart.code, "```", "", `Best for: ${rec.bestFor.join(", ")}`, `Not great for: ${rec.notGreatFor.join(", ")}`, "", `Features: inbound ${rec.features.supportsInbound ? "yes" : "no"} | templates ${rec.features.hasTemplateEngine ? "yes" : "no"} | webhooks ${rec.features.webhookSupport ? "yes" : "no"}`, `Last verified: ${rec.lastVerified}`, `Details: ${rec.detailUrl}`, ]; if (data.runnerUp) { lines.push( "", `## Runner-up: ${data.runnerUp.name}`, `Score: ${data.runnerUp.score} | Cost: $${data.runnerUp.monthlyCost.toFixed(2)}/mo`, `Why: ${data.runnerUp.reasoning.join(". ")}`, `Details: ${data.runnerUp.detailUrl}`, ); } if (data.comparison) { lines.push("", "### Full Comparison", "```", data.comparison, "```"); } return textResult(lines.join("\n")); } catch (err) { return errorResult(err instanceof Error ? err.message : String(err)); } } ); - src/index.ts:156-195 (schema)TypeScript interface for the API response from the /recommend endpoint, defining the shape of the recommendation data returned to the tool handler.
interface RecommendResponse { task: string; volume: number; budget: number | null; priority: string; constraints: string[]; recommendation: { name: string; slug: string; url: string; score: number; reasoning: string[]; monthlyCost: number; bestFor: string[]; notGreatFor: string[]; setup: { timeToFirstRequest: number; linesOfCode: number; requiresDomainVerification: boolean; requiresCreditCard: boolean; }; pricing: { freeRequestsPerMonth: number; costAt10k: number; costAt50k: number; costAt100k: number; tiers: Array<{ name: string; monthlyEmails: number; pricePerMonth: number }>; }; features: { supportsInbound: boolean; hasTemplateEngine: boolean; webhookSupport: boolean; }; quickstart: { language: string; code: string; }; lastVerified: string; detailUrl: string; } | null; - src/index.ts:622-687 (handler)The actual handler function for the 'recommend' tool. It builds query params, calls the API, and formats the recommendation response with pricing, setup, quickstart code, runner-up, and comparison data.
async ({ task, volume, budget, priority, constraints }) => { try { const params: Record<string, string> = { task }; if (volume !== undefined) params.volume = String(volume); if (budget !== undefined) params.budget = String(budget); if (priority) params.priority = priority; if (constraints?.length) params.constraints = constraints.join(","); const data = await apiGet<RecommendResponse>("/recommend", params); if (!data.recommendation) { return textResult( `No APIs match your constraints for "${task}".\n` + (data.message || "Try relaxing budget or removing constraints.") ); } const rec = data.recommendation; const lines = [ `## Recommendation: ${rec.name}`, "", `**Why:** ${rec.reasoning.join(". ")}`, "", `### Pricing`, `Monthly cost at ${(data.volume).toLocaleString()} emails: **$${rec.monthlyCost.toFixed(2)}**`, `Free tier: ${rec.pricing.freeRequestsPerMonth.toLocaleString()} emails/mo`, `At scale: $${rec.pricing.costAt10k} (10K) | $${rec.pricing.costAt50k} (50K) | $${rec.pricing.costAt100k} (100K)`, "", `### Setup`, `Time to first request: ${rec.setup.timeToFirstRequest} min`, `Lines of code: ${rec.setup.linesOfCode}`, `Domain verification: ${rec.setup.requiresDomainVerification ? "Required" : "Not required"}`, `Credit card: ${rec.setup.requiresCreditCard ? "Required" : "Not required"}`, "", `### Quickstart (${rec.quickstart.language})`, "```" + rec.quickstart.language, rec.quickstart.code, "```", "", `Best for: ${rec.bestFor.join(", ")}`, `Not great for: ${rec.notGreatFor.join(", ")}`, "", `Features: inbound ${rec.features.supportsInbound ? "yes" : "no"} | templates ${rec.features.hasTemplateEngine ? "yes" : "no"} | webhooks ${rec.features.webhookSupport ? "yes" : "no"}`, `Last verified: ${rec.lastVerified}`, `Details: ${rec.detailUrl}`, ]; if (data.runnerUp) { lines.push( "", `## Runner-up: ${data.runnerUp.name}`, `Score: ${data.runnerUp.score} | Cost: $${data.runnerUp.monthlyCost.toFixed(2)}/mo`, `Why: ${data.runnerUp.reasoning.join(". ")}`, `Details: ${data.runnerUp.detailUrl}`, ); } if (data.comparison) { lines.push("", "### Full Comparison", "```", data.comparison, "```"); } return textResult(lines.join("\n")); } catch (err) { return errorResult(err instanceof Error ? err.message : String(err)); } } - src/index.ts:854-854 (registration)HTTP endpoint list-of-tools registration includes the 'recommend' tool with its input schema for discovery purposes.
{ name: "recommend", description: "Get an opinionated API recommendation with pricing and working quickstart code", inputSchema: { type: "object", properties: { task: { type: "string" }, volume: { type: "number" }, budget: { type: "number" }, priority: { type: "string", enum: ["cost", "simplicity", "deliverability", "scale"] }, constraints: { type: "array", items: { type: "string" } } }, required: ["task"] } },