{
  "openapi": "3.1.0",
  "info": {
    "title": "Compare Power — Public MCP Tools",
    "version": "0.0.0",
    "description": "Machine-readable description of the public MCP tools exposed by Compare Power.\n\nFor MCP-native clients, connect directly to servers[0].url + \"/mcp\". Each tool's \nreal JSON Schema input is under components/schemas/<ToolName>Input and mirrored \nin the x-mcp-tools extension for consumers that don't parse OpenAPI refs.",
    "contact": {
      "name": "Compare Power AI",
      "email": "ai@comparepower.com"
    },
    "x-manifest-version": "0.0.0"
  },
  "servers": [
    {
      "url": "https://mcp.comparepower.com",
      "description": "Public MCP (unauthenticated, read-only)"
    }
  ],
  "paths": {
    "/mcp": {
      "post": {
        "summary": "MCP JSON-RPC endpoint (all tools invoked via this single path)",
        "description": "Send a JSON-RPC body with method=\"tools/call\" and params.name set to one of: cp_analyze_plan, cp_calculate_bill, cp_compare_plans, cp_estimate_usage, cp_find_plans, cp_get_enrollment_url, cp_get_ercot_data, cp_get_market_briefing, cp_get_price_curve, cp_get_provider_profile, cp_get_state_rates, cp_recommend_plans, cp_search_content, cp_search_toolsTool-specific input shapes live under components/schemas/<ToolName>Input.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/McpJsonRpcRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC response (stream or single message)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "403": {
            "description": "Tool not available in public mode"
          },
          "429": {
            "description": "Rate limit — includes Retry-After header"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "McpJsonRpcRequest": {
        "type": "object",
        "required": [
          "jsonrpc",
          "method"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "enum": [
              "2.0"
            ]
          },
          "id": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "method": {
            "type": "string",
            "enum": [
              "tools/list",
              "tools/call",
              "resources/list",
              "resources/read",
              "initialize"
            ]
          },
          "params": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ToolCallParams"
              },
              {
                "type": "object"
              }
            ]
          }
        }
      },
      "ToolCallParams": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "enum": [
              "cp_analyze_plan",
              "cp_calculate_bill",
              "cp_compare_plans",
              "cp_estimate_usage",
              "cp_find_plans",
              "cp_get_enrollment_url",
              "cp_get_ercot_data",
              "cp_get_market_briefing",
              "cp_get_price_curve",
              "cp_get_provider_profile",
              "cp_get_state_rates",
              "cp_recommend_plans",
              "cp_search_content",
              "cp_search_tools"
            ]
          },
          "arguments": {
            "type": "object"
          }
        }
      },
      "CpAnalyzePlanInput": {
        "type": "object",
        "properties": {
          "planId": {
            "type": "string",
            "minLength": 1,
            "description": "Plan ID from cp_search_plans results"
          },
          "usage": {
            "type": "integer",
            "minimum": 100,
            "maximum": 10000,
            "default": 1000,
            "description": "Usage level in kWh for primary pricing (default 1000)"
          }
        },
        "required": [
          "planId"
        ],
        "additionalProperties": false,
        "$schema": "http://json-schema.org/draft-07/schema#"
      },
      "CpCalculateBillInput": {
        "type": "object",
        "properties": {
          "planIds": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1
            },
            "minItems": 1,
            "maxItems": 10,
            "description": "One or more plan IDs"
          },
          "usage": {
            "type": "integer",
            "minimum": 0,
            "maximum": 50000,
            "description": "Monthly usage in kWh"
          }
        },
        "required": [
          "planIds",
          "usage"
        ],
        "additionalProperties": false,
        "$schema": "http://json-schema.org/draft-07/schema#"
      },
      "CpComparePlansInput": {
        "type": "object",
        "properties": {
          "planIds": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1
            },
            "minItems": 2,
            "maxItems": 4,
            "description": "Array of 2-4 plan IDs from cp_search_plans results"
          },
          "usage": {
            "type": "integer",
            "minimum": 100,
            "maximum": 10000,
            "default": 1000,
            "description": "Primary usage level in kWh for comparison (default 1000). Average TX home uses ~1,000 kWh/month."
          }
        },
        "required": [
          "planIds"
        ],
        "additionalProperties": false,
        "$schema": "http://json-schema.org/draft-07/schema#"
      },
      "CpEstimateUsageInput": {
        "type": "object",
        "properties": {
          "squareFeet": {
            "type": "integer",
            "minimum": 100,
            "maximum": 20000,
            "description": "Home size in square feet"
          },
          "residents": {
            "type": "integer",
            "minimum": 1,
            "maximum": 20,
            "description": "Number of people living in the home"
          },
          "homeAge": {
            "type": "integer",
            "minimum": 0,
            "maximum": 200,
            "description": "Age of the home in years (newer homes are more efficient)"
          },
          "heatingType": {
            "type": "string",
            "enum": [
              "electric",
              "gas",
              "heat-pump",
              "none"
            ],
            "default": "electric",
            "description": "Type of heating system"
          },
          "hasPool": {
            "type": "boolean",
            "default": false,
            "description": "Does the home have a swimming pool?"
          },
          "hasEV": {
            "type": "boolean",
            "default": false,
            "description": "Does the household charge an electric vehicle at home?"
          }
        },
        "required": [
          "squareFeet",
          "residents"
        ],
        "additionalProperties": false,
        "$schema": "http://json-schema.org/draft-07/schema#"
      },
      "CpFindPlansInput": {
        "type": "object",
        "properties": {
          "zip": {
            "type": "string",
            "pattern": "^\\d{5}$",
            "description": "5-digit Texas ZIP code"
          },
          "term": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "description": "Filter by contract term in months"
          },
          "rateType": {
            "type": "string",
            "enum": [
              "fixed",
              "variable",
              "indexed"
            ],
            "description": "Filter by rate type"
          },
          "minGreen": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "Minimum renewable energy %"
          },
          "brand": {
            "type": "string",
            "maxLength": 100,
            "description": "Filter by provider name"
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50,
            "default": 20,
            "description": "Maximum plans to return (default 20)"
          }
        },
        "required": [
          "zip"
        ],
        "additionalProperties": false,
        "$schema": "http://json-schema.org/draft-07/schema#"
      },
      "CpGetEnrollmentUrlInput": {
        "type": "object",
        "properties": {
          "planId": {
            "type": "string",
            "minLength": 1,
            "description": "Plan ID to generate enrollment URL for (use the exact id from a prior tool call)"
          },
          "utilitySlug": {
            "type": "string",
            "description": "Utility slug override (usually auto-detected)"
          },
          "zip": {
            "type": "string",
            "description": "Customer ZIP code — preserved for fallback + attribution"
          },
          "estimatedUsage": {
            "type": "number",
            "description": "Estimated monthly kWh — preserved for pre-fill"
          }
        },
        "required": [
          "planId"
        ],
        "additionalProperties": false,
        "$schema": "http://json-schema.org/draft-07/schema#"
      },
      "CpGetErcotDataInput": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string",
            "enum": [
              "utilities",
              "cities",
              "zips"
            ],
            "description": "Type of data to query"
          },
          "filter": {
            "type": "string",
            "description": "Filter value: utility slug for cities/zips, or search string"
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 500,
            "default": 50,
            "description": "Max results (default 50)"
          }
        },
        "required": [
          "query"
        ],
        "additionalProperties": false,
        "$schema": "http://json-schema.org/draft-07/schema#"
      },
      "CpGetMarketBriefingInput": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
            "description": "Date (YYYY-MM-DD), omit for today"
          }
        },
        "additionalProperties": false,
        "$schema": "http://json-schema.org/draft-07/schema#"
      },
      "CpGetPriceCurveInput": {
        "type": "object",
        "properties": {
          "planIds": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1
            },
            "minItems": 1,
            "maxItems": 10,
            "description": "Array of plan IDs to fetch price curves for (max 10)"
          }
        },
        "required": [
          "planIds"
        ],
        "additionalProperties": false,
        "$schema": "http://json-schema.org/draft-07/schema#"
      },
      "CpGetProviderProfileInput": {
        "type": "object",
        "properties": {
          "duns": {
            "type": "string",
            "pattern": "^\\d{9,13}$",
            "description": "Utility DUNS number"
          },
          "brandName": {
            "type": "string",
            "maxLength": 100,
            "description": "Filter to specific provider name"
          }
        },
        "required": [
          "duns"
        ],
        "additionalProperties": false,
        "$schema": "http://json-schema.org/draft-07/schema#"
      },
      "CpGetStateRatesInput": {
        "type": "object",
        "properties": {
          "state": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "description": "2-letter state code (e.g. TX, CA). Omit for all states."
          },
          "sector": {
            "type": "string",
            "enum": [
              "residential",
              "commercial",
              "industrial",
              "all"
            ],
            "default": "residential",
            "description": "Rate sector to return (default residential)"
          }
        },
        "additionalProperties": false,
        "$schema": "http://json-schema.org/draft-07/schema#"
      },
      "CpRecommendPlansInput": {
        "type": "object",
        "properties": {
          "zip": {
            "type": "string",
            "pattern": "^\\d{5}$",
            "description": "Texas ZIP code"
          },
          "usage": {
            "type": "integer",
            "minimum": 100,
            "maximum": 10000,
            "default": 1000,
            "description": "Monthly kWh usage (default 1000)"
          },
          "journeyId": {
            "type": "string",
            "enum": [
              "saver",
              "mover-tx",
              "mover-new",
              "expiring",
              "disconnected"
            ],
            "default": "saver",
            "description": "Customer's situation"
          },
          "priority": {
            "type": "string",
            "enum": [
              "price",
              "stability",
              "green",
              "shortTerm"
            ],
            "description": "Override the default scoring priority"
          },
          "termPreference": {
            "type": "string",
            "enum": [
              "short",
              "medium",
              "long",
              "any"
            ],
            "description": "Preferred contract length range"
          },
          "greenPriority": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "Importance of renewable energy (0-100)"
          }
        },
        "required": [
          "zip"
        ],
        "additionalProperties": false,
        "$schema": "http://json-schema.org/draft-07/schema#"
      },
      "CpSearchContentInput": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string",
            "minLength": 2,
            "maxLength": 200,
            "description": "Search query — the question or topic to search for"
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 20,
            "default": 5,
            "description": "Maximum results to return (default 5)"
          },
          "collections": {
            "type": "string",
            "description": "Comma-separated collection filter (e.g., \"posts,faqs\"). Omit for all collections."
          }
        },
        "required": [
          "query"
        ],
        "additionalProperties": false,
        "$schema": "http://json-schema.org/draft-07/schema#"
      },
      "CpSearchToolsInput": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string",
            "minLength": 2,
            "maxLength": 200,
            "description": "What the user wants to accomplish (e.g. \"find my meter ID\", \"estimate my bill\")."
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 20,
            "description": "Max results (default 5)."
          }
        },
        "required": [
          "query"
        ],
        "additionalProperties": false,
        "$schema": "http://json-schema.org/draft-07/schema#"
      }
    }
  },
  "x-mcp-tools": [
    {
      "name": "cp_analyze_plan",
      "description": "Deep-dive on a single Texas electricity plan — pricing, fees, credits, EFL/TOS links, and sibling plans at different terms.\n\nWHEN TO USE:\n- User chose a specific plan and wants full details before enrolling.\n- User asks \"tell me more about this one\" referring to a plan from cp_find_plans or cp_recommend_plans.\n- You need the exact EFL (Electricity Facts Label) URL to link the user to.\n\nWHEN NOT TO USE:\n- User hasn't chosen a specific plan yet — use cp_recommend_plans for ranked options.\n- User wants to compare 2+ plans side-by-side — use cp_compare_plans instead (does the cost interpolation + crossover analysis automatically).\n- You just want a bill estimate — use cp_calculate_bill (lighter call, supports batch).\n\nTYPICAL FLOW:\n- Call this AFTER cp_find_plans or cp_recommend_plans has given you a planId.\n- Follow up with cp_get_enrollment_url to generate the signup link once the user decides.\n\nPARAMETERS:\n- planId (required): opaque id from a prior tool result.\n- usage (default 1000): the kWh usage level to highlight in the pricing summary. Added to the standard 500/1000/2000 tier sweep.\n\nRESPONSE:\n- name, brand, term, rateType, percentGreen: headline plan metadata.\n- pricing: requestedUsage, rate_cents at that usage, totalMonthly, annualEstimate, byUsageLevel[] across 500/1000/2000/yourUsage, curvePoints (46 typical).\n- cancellationFee: { raw, summary } — \"summary\" is a human-readable one-liner.\n- billCredits[], features[]: plan-specific extras.\n- documents: { efl, tos, yrac } URLs — surface the efl link when users ask about the terms.\n- lifecycle, utility, productGroup, siblings: siblings are the same plan family at different term lengths (12-mo, 24-mo, 36-mo).\n- nextSteps: suggests cp_compare_plans or cp_get_enrollment_url.\n\nERRORS:\n- \"Plan not found\" when the planId doesn't resolve: verify it came from a recent tool result, or re-run cp_find_plans.\n\nDO NOT:\n- Restate pricing you didn't see in the response. If rate_cents is null at the user's usage, say so honestly.\n- Invent EFL URLs — only share documents.efl when non-null.\n- Modify or re-encode planId for follow-up calls.",
      "input_schema_ref": "#/components/schemas/CpAnalyzePlanInput",
      "rubric_compliant": true
    },
    {
      "name": "cp_calculate_bill",
      "description": "Compute an exact Texas electricity bill for 1–10 plans at a specified monthly kWh, with the full charge breakdown (energy, base/minimum, TDSP delivery, TDU surcharge, bill credits).\n\nWHEN TO USE:\n- User asks \"how much would I pay on plan X at Y kWh\".\n- User gave specific kWh and wants the real bill, not a headline rate.\n- You want to batch-compare the bill at one usage level across a handful of plans without the full crossover analysis.\n\nWHEN NOT TO USE:\n- You want crossover analysis or multi-tier comparison — use cp_compare_plans (curve interpolation).\n- You want the full 46-point price curve for research — use cp_get_price_curve.\n- User hasn't picked any plans yet — use cp_find_plans or cp_recommend_plans first.\n\nTYPICAL FLOW:\n- Call this AFTER the user has at least one planId and a kWh number.\n- Pair with cp_analyze_plan when the user wants full context; use cp_calculate_bill standalone when they just want \"$X/mo\".\n\nPARAMETERS:\n- planIds (required): 1–10 opaque plan ids.\n- usage (required): monthly kWh. 0–50000; use 1000 as a reasonable default if the user doesn't know.\n\nRESPONSE:\n- plans[]: each { planId, usage_kWh, totalMonthly (\"$X.XX\"), avgCentsPerKwh, annualEstimate, chargeBreakdown[] with {name, amount, type} }. chargeBreakdown shows exactly where the money goes — share it when users ask \"why is it this much\".\n- comparison (only when >1 plan and >=2 succeeded): { cheapest, mostExpensive, monthlySavings, annualSavings }.\n- Per-plan error field present when that specific plan failed — partial success is OK.\n\nERRORS:\n- \"Bill calculation failed for all N plan(s)\" — pricing engine unreachable. Retry shortly.\n\nDO NOT:\n- Quote a totalMonthly without the chargeBreakdown when the user asks \"why\" — the breakdown is the answer.\n- Show avgCentsPerKwh as \"the plan's rate\" — it's the effective rate AT THIS usage. Different usage = different effective rate.\n- Combine numbers from different usage levels across multiple calls — re-run with the actual usage you want.",
      "input_schema_ref": "#/components/schemas/CpCalculateBillInput",
      "rubric_compliant": true
    },
    {
      "name": "cp_compare_plans",
      "description": "Compare 2–4 Texas electricity plans side-by-side with real price-curve interpolation. Surfaces the winner at each usage tier, crossover points where rankings flip, and annual-savings deltas.\n\nWHEN TO USE:\n- User has 2–4 planIds and wants a head-to-head comparison.\n- User is close to choosing but torn between options and wants \"which is actually cheapest for me\".\n- You need to explain WHY one plan wins over another — crossover points are the honest answer.\n\nWHEN NOT TO USE:\n- Only one plan of interest — use cp_analyze_plan (richer plan-specific detail).\n- More than 4 plans — narrow down with cp_recommend_plans first (rank the candidates, compare the top 4).\n- You don't need cost comparison, you need ranking — use cp_recommend_plans.\n\nTYPICAL FLOW:\n- Call this AFTER cp_find_plans or cp_recommend_plans has given you 2–4 planIds.\n- Follow up with cp_get_enrollment_url for the plan the user picks.\n\nPARAMETERS:\n- planIds (required): array of 2–4 opaque plan ids.\n- usage (default 1000): the user's expected monthly kWh. Crossover + savings numbers are computed around this point.\n\nRESPONSE:\n- plans[]: each planId with costsAtUsage[] (total + avgCents at 500 / 1000 / 2000 / yourUsage) and annualEstimate.\n- cheapestByUsage[]: at each tier, which plan wins.\n- crossoverPoints[]: usage kWh where one plan becomes cheaper than another — {planA, planB, kWh, cheaperBelow, cheaperAbove}. \"none\" when curves don't cross.\n- annualSavingsVsNext: dollars the cheapest plan saves over the runner-up at the user's usage level.\n- recommendation: one-sentence summary — either \"X wins at every tier\" or \"X wins at your usage, crossover at Y kWh\".\n- missingCurves (only present when some planIds had no price-curve data): advise the user those plans may be inactive.\n\nERRORS:\n- \"Price curve data unavailable for all requested plans\" — every plan id returned no curve. Suggest different ids from cp_find_plans.\n\nDO NOT:\n- Smooth over crossover points — if one plan wins below 1200 kWh and the other wins above, say that. It's the USP of this tool.\n- Imagine savings numbers — annualSavingsVsNext is the only savings figure you should quote.\n- Include planIds the user never gave you.",
      "input_schema_ref": "#/components/schemas/CpComparePlansInput",
      "rubric_compliant": true
    },
    {
      "name": "cp_estimate_usage",
      "description": "Estimate monthly electricity usage (kWh) from home characteristics using Texas climate patterns (hot summers, mild winters).\n\nWHEN TO USE:\n- User doesn't know their kWh usage and can't look at their bill.\n- You need a reasonable estimate before calling cp_recommend_plans or cp_calculate_bill.\n- User asks \"how much electricity does my home use\".\n\nWHEN NOT TO USE:\n- User already has a kWh number from their bill — use it directly in cp_recommend_plans or cp_calculate_bill.\n- User wants bill dollars, not kWh — cp_calculate_bill needs usage, estimatedMonthlyBill here is a rough sanity check only.\n- Non-Texas users — the climate model is TX-tuned; estimates will mislead.\n\nTYPICAL FLOW:\n- Call this BEFORE cp_recommend_plans when usage is unknown — feed averageMonthlyUsage in as \"usage\".\n- Share the suggestedPricingMode hint with the user (\"flat\" vs \"seasonal\") to frame plan choice.\n\nPARAMETERS:\n- squareFeet (required): 100–20000. Home size.\n- residents (required): 1–20. Household size.\n- homeAge (optional): years. Newer homes are more efficient.\n- heatingType (default \"electric\"): electric | gas | heat-pump | none. Huge input for winter usage.\n- hasPool (default false): pools are a major kWh driver (pumps, heaters).\n- hasEV (default false): typical EV adds ~250–400 kWh/mo for a home charger.\n\nRESPONSE:\n- averageMonthlyUsage (\"X kWh\"), annualUsage, peakMonth, peakUsage: the headline numbers.\n- estimatedMonthlyBill: rough dollar estimate — sanity check only, NOT a bill calc.\n- confidence: \"low\" | \"medium\" | \"high\" based on how many inputs were provided.\n- factors: human-readable list of what drove the estimate.\n- usageVariancePercent: (max-min)/avg across 12 months; high variance suggests a plan with bill credits may save money.\n- suggestedPricingMode: text hint — \"flat\" for <=40% variance, \"seasonal\" for >40%.\n- monthlyBreakdown[]: 12-entry array with {month, kWh}.\n- nextStep: suggests cp_recommend_plans / cp_calculate_bill with the estimated usage.\n\nERRORS:\n- \"Estimation error: X\" — usually invalid inputs. Re-ask the user for clarification.\n\nDO NOT:\n- Present estimates as actual bills — this is a model, not a meter reading.\n- Skip mentioning confidence when presenting the number — users should know if it was \"medium\" not \"high\".\n- Use estimatedMonthlyBill for pricing comparison — feed the kWh into cp_calculate_bill for real numbers.\n\nQUICK REFERENCE (for your mental sanity check):\n  small apartment     ~500  kWh/mo\n  average Texas home  ~1000 kWh/mo\n  large home          ~1800 kWh/mo\n  large + pool + EV   ~2300 kWh/mo",
      "input_schema_ref": "#/components/schemas/CpEstimateUsageInput",
      "rubric_compliant": true
    },
    {
      "name": "cp_find_plans",
      "description": "Find Texas electricity plans available at a given ZIP code, returned sorted by price per kWh at 1,000 kWh usage.\n\nWHEN TO USE:\n- User mentions any Texas ZIP code, address, or city and electricity shopping.\n- User says \"I want to switch providers\" or \"find me a plan\" without specifying a plan by id.\n- First call in the canonical workflow — everything else needs the utility + plan list this returns.\n\nWHEN NOT TO USE:\n- User already gave you a planId — skip ahead to cp_analyze_plan or cp_compare_plans.\n- User wants personalized ranking, not a catalog — use cp_recommend_plans after this.\n- User is outside Texas or outside an ERCOT deregulated area — the tool will return error=\"no_utility\" and explain.\n\nTYPICAL FLOW:\n- Call this FIRST with the user's ZIP.\n- Then call cp_recommend_plans with the same ZIP + usage for scored ranking,\n  OR cp_analyze_plan with a planId from these results,\n  OR cp_compare_plans with 2–4 planIds.\n\nPARAMETERS:\n- zip (required): exactly 5 digits, Texas ZIPs only.\n- term, rateType, minGreen, brand (optional): filters applied after the catalog is fetched.\n- limit (default 20, max 50): how many plans to return.\n\nRESPONSE:\n- utility: { name, duns, slug } — preserve duns verbatim for cp_get_provider_profile follow-ups.\n- plans[]: each has planId, name, provider, pricePerKwh (cents at 1000 kWh), termMonths, rateType, greenPercent, cancellationFee. Preserve planId verbatim for any follow-up tool call.\n- totalFound vs showing: how many matched the filters vs how many are in the response.\n- nextSteps: advisory list of follow-up tools.\n\nERRORS:\n- error=\"no_utility\" with zip outside ERCOT (municipal utilities like Austin Energy, CPS Energy, co-ops): tell the user their area isn't deregulated and you can't help them switch providers there.\n- error=\"no_plans\" with a valid utility: unusual — suggest they try again shortly.\n\nDO NOT:\n- Invent planIds, provider names, rates, or utility DUNS numbers — only use values returned here.\n- Reformat planIds. They are opaque strings; pass them verbatim to follow-up tools.\n- Assume a ZIP is deregulated without calling this — non-ERCOT ZIPs (El Paso, Lubbock parts, East Texas co-op areas) return no_utility.",
      "input_schema_ref": "#/components/schemas/CpFindPlansInput",
      "rubric_compliant": true
    },
    {
      "name": "cp_get_enrollment_url",
      "description": "Generate the final handoff URL a user clicks to enroll in a specific plan. Preferentially returns a short cp.link URL with attribution UTMs + cp_ref token; long URL as fallback.\n\nWHEN TO USE:\n- User has chosen a plan (you have a concrete planId from cp_find_plans / cp_recommend_plans / cp_analyze_plan / cp_compare_plans) and is ready to sign up.\n- The LAST step in the canonical workflow — everything prior is research and decision support.\n\nWHEN NOT TO USE:\n- User is still deciding — stay in cp_analyze_plan / cp_compare_plans.\n- User wants to browse — cp_find_plans / cp_recommend_plans.\n- User asked about pricing, fees, or terms — cp_analyze_plan is the right tool.\n\nTYPICAL FLOW:\n- Call this AFTER the user has picked a plan.\n- Pass planId + (if available) zip + estimatedUsage so the enrollment page starts pre-filled at the customer-info / payment step.\n- Share the returned short_url with the user — that's the attribution-enriched deep link.\n- If short_url is absent (short-link service unreachable), the tool falls back to long_url + a human-readable attribution note — still usable, just without closed-loop attribution.\n\nPARAMETERS:\n- planId (required): opaque id from a prior tool call. Do not invent.\n- utilitySlug (optional): override utility mapping; usually auto-detected.\n- zip (optional): customer ZIP. Preserved for fallback + pre-fill.\n- estimatedUsage (optional): monthly kWh for pre-fill.\n\nRESPONSE:\n- planId: the id you passed in, echoed for verification.\n- short_url: cp.link/e/TOKEN — the primary URL to share with the user. Carries utm_source (inferred AI agent), utm_medium=ai-agent, utm_campaign, and cp_ref (attribution token).\n- long_url: full orders.comparepower.com URL. Use only if short_url is absent or your UI can't render short URLs.\n- expires_at: ms timestamp when the short-link expires (default 30 days).\n- attribution: one-sentence human-readable description of what's being tracked.\n- brandName, groupName, utilityName, termMonths: plan metadata for context.\n- note: reminder that this is the final handoff step.\n\nERRORS:\n- \"Could not generate enrollment URL for plan X\": the plan may no longer be active, or the enrollment API is down. Suggest the user try a different plan.\n\nDO NOT:\n- Invent planIds — ALWAYS use a real id from a prior tool call.\n- Strip the cp_ref / utm parameters — they close the attribution funnel when the user enrolls.\n- Share the long_url when short_url is available — users prefer short links and we lose attribution granularity otherwise.\n- Describe this URL as a completed enrollment — the user still has to click through and finish signup on the orders site.",
      "input_schema_ref": "#/components/schemas/CpGetEnrollmentUrlInput",
      "rubric_compliant": true
    },
    {
      "name": "cp_get_ercot_data",
      "description": "Query ERCOT (Electric Reliability Council of Texas) reference data — the list of TX utilities (TDSPs), cities in deregulated zones, and ZIP→utility mapping.\n\nWHEN TO USE:\n- User asks \"which utility serves [city/ZIP]\" and you don't already have a ZIP to call cp_find_plans with.\n- User asks \"is [city] in a deregulated area\".\n- User wants the list of TX deregulated utilities (Oncor, CenterPoint, AEP, TNMP).\n\nWHEN NOT TO USE:\n- User has a ZIP and wants plans — call cp_find_plans directly (it resolves the utility for you).\n- User wants plan catalog filtered by utility — use cp_find_plans with the ZIP, not this tool.\n- User wants plan detail — wrong tool entirely.\n\nTYPICAL FLOW:\n- Standalone, typically for answering geography questions before the user has a specific ZIP.\n- Chain: query=\"utilities\" to get DUNS → cp_get_provider_profile with that DUNS to drill into providers.\n\nPARAMETERS:\n- query (required): \"utilities\" | \"cities\" | \"zips\".\n- filter (optional): search string for utilities (name/slug/DUNS), or utility slug for cities/zips.\n- limit (default 50, max 500): pagination cap.\n\nRESPONSE:\nShape depends on the query field.\n- query=\"utilities\": utilities[] with { duns, name, shortName, slug }. Preserve duns verbatim for cp_get_provider_profile follow-ups.\n- query=\"cities\": cities[] with { city, county, duns, utilityName }.\n- query=\"zips\": zips[] with { zip, city, duns, utilityName }. Limit is important — TX has tens of thousands of ZIPs.\n\nERRORS:\n- \"No X data available\" — typically means the ERCOT dataset endpoint is unreachable. Retry shortly.\n\nDO NOT:\n- Invent utility DUNS or slugs — only use values from a response.\n- Present the 4 main utilities as the only ones — there are a handful of smaller ERCOT TDSPs; the full list is what's returned.\n- Conflate \"ERCOT\" (grid operator, ~85% of TX) with \"Texas\" (the whole state). El Paso, Lubbock parts, and East Texas co-ops are NOT in ERCOT and NOT deregulated.",
      "input_schema_ref": "#/components/schemas/CpGetErcotDataInput",
      "rubric_compliant": true
    },
    {
      "name": "cp_get_market_briefing",
      "description": "Return today's Compare Power editorial market briefing — national rate overview, state highlights, Texas deep-dive, sector analysis, and things-to-watch. Human-written, data-sourced.\n\nWHEN TO USE:\n- User asks \"what's happening with electricity rates right now\", \"are rates going up\", \"is now a good time to switch\".\n- User wants context or commentary, not a plan list.\n- You want to back a recommendation with current market trend language.\n\nWHEN NOT TO USE:\n- User wants plan pricing for their ZIP — use cp_find_plans or cp_recommend_plans (plan pricing is live; this briefing has a one-day lag at worst).\n- User wants EIA state-level numbers — use cp_get_state_rates (focused dataset).\n- User wants data about a specific provider — use cp_get_provider_profile.\n\nTYPICAL FLOW:\n- Call this standalone when the user asks about market conditions.\n- Combine with cp_get_state_rates for TX-specific trend context when relevant.\n\nPARAMETERS:\n- date (optional): YYYY-MM-DD. Omit for today's briefing. Historical dates return the briefing published that day (if any).\n\nRESPONSE:\n- reportTime, author: when + by whom.\n- nationalOverview: markdown paragraphs on the current US electricity market.\n- stateHighlights[]: state-by-state callouts.\n- texasDeepDive: Texas-specific analysis (most relevant for our users).\n- sectorAnalysis: residential vs commercial vs industrial breakdowns.\n- thingsToWatch[]: forward-looking watchpoints.\n- freshnessNote / note: set when today's briefing hasn't been generated yet and the cached prior-day content is being returned.\n\nERRORS:\n- \"Market briefing not available\" — the editorial pipeline hasn't produced content for that date. Fall back to cp_get_state_rates for raw numbers.\n\nDO NOT:\n- Present briefing content as live quotes — this is editorial, with a lag.\n- Combine the briefing narrative with invented numbers; cite only the figures included.\n- Quote \"thingsToWatch\" as if they are confirmed future events — they are Compare Power's perspective on what to monitor.",
      "input_schema_ref": "#/components/schemas/CpGetMarketBriefingInput",
      "rubric_compliant": true
    },
    {
      "name": "cp_get_price_curve",
      "description": "Return the raw 46-point price curve for 1–10 plans — price at every usage level from 100 to 4,600 kWh in 100 kWh increments.\n\nWHEN TO USE:\n- User has highly variable monthly usage and wants to see the curve shape, not a single price.\n- You want to identify bill-credit thresholds (dramatic rate dips at 1000 or 2000 kWh).\n- Research / debugging — you want the underlying data behind cp_compare_plans.\n\nWHEN NOT TO USE:\n- User wants a single bill at a known usage — use cp_calculate_bill.\n- User wants plan recommendations — use cp_recommend_plans.\n- User wants a head-to-head comparison — use cp_compare_plans (which calls this tool internally and adds crossover analysis).\n\nTYPICAL FLOW:\n- Call this as a diagnostic when a user asks \"is there a usage sweet spot for this plan\".\n- Pair with cp_analyze_plan for plan context and cp_compare_plans for ranking.\n\nPARAMETERS:\n- planIds (required): 1–10 opaque plan ids.\n\nRESPONSE:\n- curves: map keyed by planId. Each value is either\n  - { available: true, pointCount, summary: { cheapestPoint, mostExpensivePoint, rateAt1000kWh }, points[] with {usage, rate, total} }, or\n  - { available: false, note } when the curve is missing for that plan.\n- tip: a reminder to look for dramatic rate dips.\n\nERRORS:\n- \"No price curve data returned for the requested plans\" — all plans missing. Verify planIds are current.\n\nDO NOT:\n- Present a plan's rateAt1000kWh as \"THE rate\" — it's the effective rate at one usage level; the curve shape matters.\n- Average the 46 points — the average is meaningless; users pay the rate at THEIR usage.\n- Invent curve shapes; only quote points that are in the response.",
      "input_schema_ref": "#/components/schemas/CpGetPriceCurveInput",
      "rubric_compliant": true
    },
    {
      "name": "cp_get_provider_profile",
      "description": "Profile every Retail Electric Provider (REP) offering plans under a given utility DUNS — plan count, price range, term options, green mix, same-day signup cutoff times.\n\nWHEN TO USE:\n- User asks \"tell me about [provider]\" (TXU, Reliant, Direct Energy, Green Mountain, etc.).\n- User wants to compare providers, not plans — \"which provider has the most green plans\" or \"who has the cheapest average rate\".\n- User asks about same-day signup — the cutoffTimes block has this.\n\nWHEN NOT TO USE:\n- User wants specific plan detail — use cp_analyze_plan.\n- User wants a catalog sorted by price — use cp_find_plans (provider-level aggregation is too coarse).\n- User gave a ZIP but no DUNS — call cp_find_plans first to get the DUNS from its response.\n\nTYPICAL FLOW:\n- Call this AFTER cp_find_plans (which returns utility.duns) when the user wants provider-level insight.\n- Follow up with cp_find_plans filtered by brand, or cp_analyze_plan on a specific plan from this provider.\n\nPARAMETERS:\n- duns (required): 9–13 digit utility DUNS from a prior tool result. Opaque number — do not assume format.\n- brandName (optional): filter to a single provider by name or slug (case-insensitive substring match).\n\nRESPONSE:\n- utilityName, totalProviders, totalPlans: top-level counts.\n- providers[], each: brandName, planCount, priceRange { min, max } in cents per kWh at 1000 kWh, averagePrice, termOptions[] (sorted month values), greenPlanCount, cutoffTimes { holidayCutoff, weekdayCutoff, weekendCutoff } for same-day signup.\n- When brandName filter is used but matches nothing: response includes \"availableProviders\" list so you can suggest correct spellings.\n\nERRORS:\n- \"No providers found for utility DUNS X\" — DUNS is wrong, or utility isn't in a deregulated area. Re-run cp_find_plans with a ZIP.\n\nDO NOT:\n- Invent price averages or plan counts — only quote what's returned.\n- Treat priceRange.min as the \"real\" price — it's the cheapest at 1,000 kWh; actual cost varies with usage. Use cp_analyze_plan or cp_calculate_bill for that.\n- Share cutoffTimes as universally valid — they're today's cutoffs, and agents should warn users to call the provider to confirm when near the cutoff.",
      "input_schema_ref": "#/components/schemas/CpGetProviderProfileInput",
      "rubric_compliant": true
    },
    {
      "name": "cp_get_state_rates",
      "description": "Return EIA (US Energy Information Administration) electricity rate averages — per-state, by sector, with month-over-month and year-over-year deltas. Strictly historical; has a 60–90 day publication lag.\n\nWHEN TO USE:\n- User asks \"what's the average rate in [state]\" or \"is Texas cheaper than California\".\n- User wants trend language (MoM or YoY) to contextualize a plan recommendation.\n- User asks a macro question about US or regional electricity markets.\n\nWHEN NOT TO USE:\n- User wants TODAY's pricing for their ZIP — use cp_find_plans or cp_recommend_plans. EIA data is lagged.\n- User wants editorial narrative instead of numbers — use cp_get_market_briefing.\n- User wants plan-level detail (fees, EFL) — use cp_analyze_plan.\n\nTYPICAL FLOW:\n- Call this standalone when the user asks about state averages or rate trends.\n- Pair with cp_get_market_briefing when you need commentary to go with the numbers.\n\nPARAMETERS:\n- state (optional): 2-letter code (TX, CA, NY). Omit to see all states.\n- sector (default \"residential\"): residential | commercial | industrial | all.\n\nRESPONSE:\n- dataSource: always \"U.S. Energy Information Administration (EIA)\".\n- lagNote: reminder that EIA publishes 60–90 days after the reference period.\n- reportTime: when the data was last refreshed from EIA.\n- nationalAverage: top-level US number(s) for the requested sector.\n- states[]: { stateId, stateName, residential/commercial/industrial rate, momChange, yoyChange }.\n- commentary / highlight (only when state is TX-or-similar and editorial coverage exists): short interpretive text.\n- tip (only when state=TX): reminds you to switch to cp_find_plans for live TX pricing.\n\nERRORS:\n- warning: \"No rate data found for state X\" — unexpected except for US territories or typos.\n\nDO NOT:\n- Present EIA rates as \"current pricing\" — always mention the lag when a user could plausibly try to act on them.\n- Compare EIA state averages directly to a cp_find_plans result — they measure different things (historical average vs live plan list).\n- Skip the dataSource / lagNote when relaying numbers.",
      "input_schema_ref": "#/components/schemas/CpGetStateRatesInput",
      "rubric_compliant": true
    },
    {
      "name": "cp_recommend_plans",
      "description": "Rank plans at a Texas ZIP for a specific usage profile and journey, with transparent scoring you can show the user.\n\nWHEN TO USE:\n- User has a ZIP + usage (or is willing to let you default to 1,000 kWh) and wants a ranked list with reasons.\n- User asks \"which plan is best for me\", \"cheapest in my area\", \"most stable\", \"most green\", \"shortest term\".\n- After cp_find_plans when the raw catalog is too long to present and you need to pick 3–5 to show.\n\nWHEN NOT TO USE:\n- User hasn't given a ZIP yet — call cp_find_plans first (it surfaces the utility / ZIP errors).\n- User already picked a plan and wants a deep-dive — use cp_analyze_plan.\n- User wants raw catalog without scoring — use cp_find_plans directly.\n\nTYPICAL FLOW:\n- Call this AFTER cp_find_plans has resolved the ZIP (or independently when you already know the ZIP).\n- If the user doesn't know their usage, call cp_estimate_usage first.\n- Follow up with cp_analyze_plan for plan details or cp_compare_plans for side-by-side on the top results.\n\nPARAMETERS:\n- zip (required): 5-digit Texas ZIP.\n- usage (default 1000): monthly kWh, 100–10000.\n- journeyId (default \"saver\"): user's situation — saver, mover-tx, mover-new, expiring, disconnected.\n- priority (optional): override scoring priority — price | stability | green | shortTerm.\n- termPreference (optional): short | medium | long | any.\n- greenPriority (optional): 0–100 weight for renewable content.\n\nRESPONSE:\n- top5[]: ranked plans with planId (preserve verbatim), planName, provider, totalScore, termMonths, greenPercent, and scoringFactors[] with the full breakdown (factor name, score, weight, contribution, \"why\" text). Share the \"why\" text with the user — it's the transparency value prop.\n- plansLoaded / plansScored / timingMs: diagnostic metadata.\n- nextSteps: follow-up tool hints.\n\nERRORS:\n- Empty receipts array with \"No recommendations available\" message: ZIP may not be deregulated, or all plans were filtered out. Suggest the user try a different ZIP or drop filters.\n\nDO NOT:\n- Invent scores, rankings, or rationale. Only report what the tool returned.\n- Drop the scoringFactors breakdown — it's the whole point. Users trust the ranking because they can see the math.\n- Preserve planId verbatim for any follow-up call.",
      "input_schema_ref": "#/components/schemas/CpRecommendPlansInput",
      "rubric_compliant": true
    },
    {
      "name": "cp_search_content",
      "description": "Semantic search (Workers AI + Cloudflare Vectorize) over Compare Power's content — FAQs, blog posts, electricity-rates pages, provider reviews, utility pages. Returns snippets, not full pages.\n\nWHEN TO USE:\n- User asks an informational question Compare Power has likely written about (\"what's the difference between fixed and variable rates\", \"how do bill credits work\", \"is TXU reliable\").\n- You want the grounded source text before answering — this tool is your documentation lookup.\n- User asks about Texas electricity topics that aren't plan-catalog questions.\n\nWHEN NOT TO USE:\n- User wants plans, prices, or bill math — this tool returns editorial content, not pricing data. Use cp_find_plans / cp_recommend_plans / cp_calculate_bill.\n- User wants tools/calculators (\"find my ESI-ID\", \"help me switch\") — use cp_search_tools.\n- Query is one-word or very short — supply more context (min length 2 chars, but work well with 3+).\n\nTYPICAL FLOW:\n- Call this standalone for knowledge questions.\n- When the user asks \"how does X work\" AND then \"what plan should I get\", chain cp_search_content → cp_find_plans.\n\nPARAMETERS:\n- query (required): natural-language question or topic. 2–200 chars.\n- limit (default 5, max 20): how many snippets to return.\n- collections (optional): comma-separated filter (e.g. \"posts,faqs\"). Omit to search everything.\n\nRESPONSE:\n- found: number of results.\n- results[]: { collection, title, content (excerpt), slug, score }. Cite collection + title when you use an excerpt.\n- note: reminder to answer using ONLY returned content.\n\nERRORS:\n- found=0 with \"Do NOT search again. Tell user you do not have this information.\" — the index genuinely lacks content on that topic. Tell the user; do not retry the same query.\n\nDO NOT:\n- Add external knowledge on top of the returned snippets. If you answer beyond what's in content[i].content, you are no longer grounded.\n- Invent slugs or citation URLs — use returned slug verbatim.\n- Rephrase excerpts as quotes — they may already be partial sentences; paraphrase accurately instead.",
      "input_schema_ref": "#/components/schemas/CpSearchContentInput",
      "rubric_compliant": true
    },
    {
      "name": "cp_search_tools",
      "description": "Search Compare Power's tools catalog — interactive widgets and calculators (ESI-ID lookup, bill analyzer, usage estimator wizard, etc.) with their canonical paths for linking.\n\nWHEN TO USE:\n- User wants to DO something in a UI (\"find my meter ID\", \"analyze my PDF bill\", \"help me estimate usage\").\n- User asks \"is there a tool for X\" or \"how do I X on Compare Power\".\n- You want to hand the user a direct link instead of answering through more tool calls.\n\nWHEN NOT TO USE:\n- User wants content/explanation — use cp_search_content.\n- User wants plan search/comparison — these are OTHER MCP tools already, not UI tools. Use cp_find_plans / cp_compare_plans directly.\n- Pure knowledge question — cp_search_content is more targeted.\n\nTYPICAL FLOW:\n- Call this when you've decided the best answer is \"go use this tool on the website\" rather than continuing a tool-call chain.\n\nPARAMETERS:\n- query (required): 2–200 chars — what the user wants to accomplish (\"find my meter ID\", \"estimate my bill\").\n- limit (default 5, max 20): max results.\n\nRESPONSE:\n- found: number of matching tools.\n- results[]: { id, title, description, path (e.g. \"/tools/esiid-lookup\"), category, score }. Use path VERBATIM when linking.\n- note: reminder not to reword the path.\n\nERRORS:\n- found=0 with \"Suggest the user browse /tools instead\" — no tool matched. Do NOT try the same query again.\n\nDO NOT:\n- Reword the path. \"/tools/esiid-lookup\" stays \"/tools/esiid-lookup\" — not \"/tools/esi-id-lookup\" or \"the ESI-ID lookup tool\".\n- Describe a tool the search didn't return — stick to results[].\n- Use this for CMS content search — that's cp_search_content.",
      "input_schema_ref": "#/components/schemas/CpSearchToolsInput",
      "rubric_compliant": true
    }
  ]
}