plan_stack
Get infrastructure recommendations, cost estimates, or full audits for your project. Describe your use case for free-tier stack suggestions, or analyze existing services to identify costs and risks.
Instructions
Get stack recommendations, cost estimates, or a full infrastructure audit. Describe what you're building to get a free-tier stack, or pass your current services to estimate costs and find risks.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| mode | Yes | recommend: free-tier stack for a use case. estimate: cost analysis at scale. audit: risk + cost + gap analysis. | |
| use_case | No | What you're building (for recommend mode, e.g. 'Next.js SaaS app') | |
| services | No | Current vendor names (for estimate/audit mode, e.g. ['Vercel', 'Supabase']) | |
| scale | No | Scale for cost estimation (default: hobby) | |
| requirements | No | Specific infra needs for recommend mode (e.g. ['database', 'auth', 'email']) |
Implementation Reference
- src/server.ts:105-176 (handler)The 'plan_stack' tool is registered and implemented in 'src/server.ts'. It handles three modes ('recommend', 'estimate', 'audit') and calls corresponding helper functions ('getStackRecommendation', 'estimateCosts', 'auditStack').
server.registerTool( "plan_stack", { description: "Get stack recommendations, cost estimates, or a full infrastructure audit. Describe what you're building to get a free-tier stack, or pass your current services to estimate costs and find risks.", inputSchema: { mode: z.enum(["recommend", "estimate", "audit"]).describe("recommend: free-tier stack for a use case. estimate: cost analysis at scale. audit: risk + cost + gap analysis."), use_case: z.string().optional().describe("What you're building (for recommend mode, e.g. 'Next.js SaaS app')"), services: z.array(z.string()).optional().describe("Current vendor names (for estimate/audit mode, e.g. ['Vercel', 'Supabase'])"), scale: z.enum(["hobby", "startup", "growth"]).optional().describe("Scale for cost estimation (default: hobby)"), requirements: z.array(z.string()).optional().describe("Specific infra needs for recommend mode (e.g. ['database', 'auth', 'email'])"), }, }, async ({ mode, use_case, services, scale, requirements }) => { try { recordToolCall("plan_stack"); if (mode === "recommend") { if (!use_case) { return { isError: true, content: [{ type: "text" as const, text: "use_case is required for recommend mode" }], }; } const result = getStackRecommendation(use_case, requirements); logRequest({ ts: new Date().toISOString(), type: "mcp", endpoint: "plan_stack", params: { mode, use_case, requirements }, result_count: result.stack.length, session_id: getSessionId?.() }); return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }], }; } if (mode === "estimate") { if (!services || services.length === 0) { return { isError: true, content: [{ type: "text" as const, text: "services is required for estimate mode" }], }; } const result = estimateCosts(services, scale ?? "hobby"); logRequest({ ts: new Date().toISOString(), type: "mcp", endpoint: "plan_stack", params: { mode, services, scale: scale ?? "hobby" }, result_count: result.services.length, session_id: getSessionId?.() }); return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }], }; } if (mode === "audit") { if (!services || services.length === 0) { return { isError: true, content: [{ type: "text" as const, text: "services is required for audit mode" }], }; } const result = auditStack(services); logRequest({ ts: new Date().toISOString(), type: "mcp", endpoint: "plan_stack", params: { mode, services }, result_count: result.services_analyzed, session_id: getSessionId?.() }); return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }], }; } return { isError: true, content: [{ type: "text" as const, text: `Unknown mode: ${mode}` }], }; } catch (err) { console.error("plan_stack error:", err); return { isError: true, content: [{ type: "text" as const, text: `Error: ${err instanceof Error ? err.message : String(err)}` }], }; } } );