whodoicallfor-mcp

whodoicallfor-mcp

MCP server that helps users identify which trade to call during home emergencies by analyzing natural-language problem descriptions and providing ranked matches with costs, sources, and dispatch lines.

Category
Visit Server

README

whodoicallfor-mcp

An MCP server that answers the question people actually have during a home emergency: which trade do I call?

Not "find me a plumber." The prior question — is this even a plumber?

The problem

I called a plumber, who told me to call an appliance tech. I called an appliance tech, who told me to call a plumber.

That is the normal experience of a household emergency, and it is expensive. Two trip charges, half a day gone, and the water is still coming through the ceiling. The trades are specialized and their boundaries are invisible from the outside: a stain on your ceiling is a roofer if it tracks the weather, an HVAC tech if it tracks the air conditioning, and a plumber if it tracks the shower upstairs. Water damage after the leak stops is a fourth trade — a mitigation crew — and a plumber will tell you so after you have paid the callout.

This server gives an assistant the routing table. Describe the problem in plain words; get back who to call, in what order, what not to call them for, what it typically costs with a dated source, and — where one exists — a 24/7 dispatch line.

Content comes from the public JSON API at whodoicallfor.com. No auth, no API key, no configuration.

Install

Claude Code

claude mcp add whodoicallfor -- npx -y whodoicallfor-mcp

Cursor — add to ~/.cursor/mcp.json (or .cursor/mcp.json in a project):

{
  "mcpServers": {
    "whodoicallfor": {
      "command": "npx",
      "args": ["-y", "whodoicallfor-mcp"]
    }
  }
}

Same shape works for Claude Desktop (claude_desktop_config.json), Windsurf, Zed, and anything else that speaks stdio MCP.

Or don't run anything. A hosted instance of this server is live:

https://whodoicallfor.com/api/mcp/

It speaks streamable HTTP, is stateless, and needs no credentials. The trailing slash is required — the slashless URL answers with a 308 redirect, and several MCP clients will not follow a redirect on a POST.

claude mcp add --transport http whodoicallfor https://whodoicallfor.com/api/mcp/

Running it directly

npx whodoicallfor-mcp                # stdio (default)
npx whodoicallfor-mcp --http         # streamable HTTP on http://localhost:3000/mcp
npx whodoicallfor-mcp --http 8080    # ...on a port you pick

Requires Node 20.19 or newer.

Tools

All three are read-only, idempotent, and touch nothing outside the public API.

Tool Arguments Returns
route_emergency problem (required), state (optional) Up to 3 ranked situations, best first
get_scenario category, slug One situation in full
list_scenarios — Every published situation with canonical URLs

route_emergency

Plain-language problem in, ranked situations out. Pass a US state code or name ("FL", "Florida") to filter dispatch numbers down to lines that actually cover that state.

Input

{ "problem": "water coming through my ceiling" }

Output (abridged — the real response carries three matches with full costs, faq, and sources arrays)

{
  "matches": [
    {
      "title": "Water Leaking From the Ceiling — Who Do I Call?",
      "category": "water",
      "vertical": "water-damage",
      "tldr": "What's directly above matters less than timing. Appears or worsens when it rains → roofer. Appears when the AC runs, or only in cooling season → HVAC condensate line. Neither, and it tracks water use upstairs → plumber. If water is still coming, shut the main valve, keep it clear of lights and outlets, and photograph everything before any repair. Renting? The landlord's emergency line comes before any of this.",
      "who_to_call": [
        {
          "who": "Emergency plumber",
          "when": "Water is actively dripping or flowing and you can't stop it at the source",
          "not_for": "The leak has stopped — a plumber fixes pipes, not soaked drywall"
        },
        {
          "who": "Roofer",
          "when": "The stain appears or worsens when it rains and stays quiet in dry weather — usually failed flashing",
          "not_for": "The stain tracks showers, washes, or flushes rather than the weather"
        }
      ],
      "costs": [
        {
          "item": "Water-damaged ceiling repair",
          "range": "$200–$1,500",
          "source": "Fixr",
          "source_url": "https://www.fixr.com/costs/ceiling-repair"
        }
      ],
      "updated": "2026-08-12",
      "url": "https://whodoicallfor.com/water/ceiling-leaking/",
      "recommended_call": null,
      "match_score": 12
    }
  ],
  "state": null
}

The not_for field is the part that saves the wasted trip charge.

When nothing scores high enough, you get an empty matches array plus a note, the category index, and a link to guided triage — rather than a confidently wrong answer.

get_scenario

Fetch one situation in full using category and slug from route_emergency or list_scenarios.

{ "category": "water", "slug": "ceiling-leaking" }

Returns the same structured object, plus body_markdown (the complete step-by-step guide) when the upstream API supplies it.

list_scenarios

No arguments. Returns { site, count, scenarios[] } — every published situation with its title, description, category, slug, updated date, canonical URL, and JSON URL.

How matching works

route_emergency ranks by weighted token overlap between your problem and four fields of each situation: title ×3, description ×2, tldr ×1, and the who/when of each who_to_call step ×1. Tokens are lowercased, stripped of punctuation, filtered against a stopword list, and suffix-folded, so "leaking" matches "leak" and "pipes" matches "pipe". Anything scoring below 3 is discarded; at most three matches come back.

tldr and who_to_call live on the per-page documents rather than the index, so the first route_emergency call warms a full in-memory cache (bounded concurrency, ~2s cold). Subsequent calls are single-digit milliseconds. The index is cached for 10 minutes and page bodies for an hour, so the server never hammers the site.

There is no fuzzy matching, no embedding model, and no network call to anything but whodoicallfor.com. It is a small deterministic function you can read in one sitting: src/match.ts.

Disclosure

Two things worth being plain about.

recommended_call entries are sponsored. They are paid pay-per-call lines, and each one is labeled "sponsored": true in the payload. When a situation carries one, that is the commercial engine behind the site. Treat it as an ad with a phone number, and surface it to users as such — the flag is in the data precisely so you can.

Life-safety numbers are never sponsored. 911, poison control, and gas utility emergency lines carry no recommended_call at all — there is a hard server-side guard that strips it for gas scenarios and for any situation without a commercial vertical. If someone smells gas, the answer is "get out and call the utility from outside," and nobody has paid for that placement. That guard is not a policy preference; it is code, upstream, and this client cannot override it.

Cost figures are cited to a named source with an access date. Verify anything you are about to act on.

Development

npm install
npm run typecheck
npm run build
npm start           # stdio
npm run start:http  # http on :3000
src/index.ts    entry point: tool definitions, stdio + streamable HTTP transports
src/client.ts   cached fetches against the public JSON endpoints
src/match.ts    token-overlap ranking

Coverage

US-focused. Around 40 situations across water and flooding, gas and carbon monoxide, power and electrical, heating and cooling, appliances, pests and wildlife, lockouts, car trouble, and disputes with landlords and contractors. list_scenarios always reflects what is actually published.

License

MIT © Daniel Widmonte

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured