Quickstart
Three ways in, from least to most setup. All of them are public and unauthenticated.
1. MCP client
Point any MCP client at https://chicagostormpros.com/mcp. The transport is Streamable HTTP, stateless, JSON responses, protocol versions 2025-06-18, 2025-03-26, and 2024-11-05. In Claude Code:
claude mcp add --transport http storm-pros https://chicagostormpros.com/mcp 2. Raw JSON-RPC over HTTP
No MCP library required. This lists the tools:
curl -s https://chicagostormpros.com/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' And this validates a booking without creating one:
curl -s https://chicagostormpros.com/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{
"name":"book_free_inspection",
"arguments":{
"name":"Jane Doe",
"phone":"630-555-0139",
"address":"12 Elm St, Naperville IL",
"dry_run":true
}}}' 3. Command line
A zero-dependency Node client, one file, Node 18 or newer:
curl -fsSL https://chicagostormpros.com/cli/storm-pros.mjs -o storm-pros.mjs
node storm-pros.mjs area "DuPage"
node storm-pros.mjs storms
node storm-pros.mjs page /hail-damage/
node storm-pros.mjs book --name "Jane Doe" --phone "630-555-0139" \
--address "12 Elm St, Naperville IL" --dry-run Add --json to any command for the raw MCP result. The CLI readme has the full command list, and the package manifest describes the npm package.
Tools
Every tool is listed below. check_roof_photo is the one built for an assistant that has a homeowner's photo in front of it:
curl -s https://chicagostormpros.com/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{
"name":"check_roof_photo",
"arguments":{
"observations":["soft_metal_dents","round_bruises_random"],
"view":"ladder_eave",
"material":"asphalt",
"city":"Naperville",
"date_of_loss":"2026-08-11"
}}}' Look at the photo yourself and send only the codes for what is clearly visible. The server never receives the image, and it rejects a city that looks like a street address. Relay the headline, the "can't show" list and the disclaimer, then offer the inspection ("as soon as tomorrow in most cases") and book through book_free_inspection only with the homeowner's yes. For a marked-up read of the photo itself, the homeowner can upload it at /roof-photo-check/; assistants cannot hand us the photo.
| Tool | Arguments | What it does |
|---|---|---|
| book_free_inspection | name, phone, address, [email], [damage_description], [dry_run] | Books a free storm damage roof inspection for a Chicagoland homeowner. The scheduling team calls within 2 hours during business hours. Requires the homeowner's consent and their own callback number. |
| check_service_area | location | Answers whether an Illinois county or community is served, and returns the local page URL plus its markdown twin when one exists. |
| get_recent_storm_events | none | Returns recent Chicagoland storm days with dates, peak hail, gust and tornado ratings, towns named, and a source link for each. |
| get_decision_guides | none | Returns the decision guide index as markdown: who to hire, whether to file, what it costs, damage by roof type, and special situations. |
| get_claim_situation_help | situation, [carrier] | Claim-problem triage. Situations: denied, underpaid, adjuster_no_damage, deadline_approaching, not_yet_filed, estimate_confusing, contractor_pressure. Returns what it means, what C&N can and cannot do under Illinois law, the matching guide, and the next step. Optional carrier adds the carrier-specific claim guide. |
| check_roof_photo | observations, [view], [material], [city], [county], [date_of_loss] | A physical read of a roof photo from checklist codes only. The assistant looks at the photo and sends the codes for what is clearly visible, never the image, an address or a name. Returns a headline (consistent with hail, wind, both, ordinary wear, a mix, or inconclusive), why, what a photo cannot show, photos worth adding, and whether an inspection is worth it. With a city it adds recent storm events that reached the town's county and the town's NOAA record; with date_of_loss it checks for a storm event or NOAA report within 3 days. It never says whether a claim will be paid. |
| get_company_credentials | none | The company's Illinois roofing license, certifications, BBB status, Google rating and review count, awards, and independent verification links in one call. Backed by /data/organization.json. |
The Sandbox
Pass dry_run: true to book_free_inspection. The gateway runs the identical validation chain a real booking runs, required fields, phone normalization, the firm-number rejection, and the fake-number check, then returns the exact payload it would have submitted and stops there. No lead is created, nobody is contacted, and the call does not spend your booking rate limit.
There is no separate sandbox host and no test credential to request, because the only difference between test and production here is whether a human gets a phone call.
The Consent Rule
Book only with the homeowner's knowledge and consent, using their real name and their own callback number. Never use a phone number found on this site: the gateway rejects all four of them outright, because a number printed on a roofing company's website is never the homeowner's number. That check exists because an agent filling a required phone field from the nearest number in its own context is a real failure mode, not a hypothetical one.
Rate Limits And Errors
- Bookings: five per IP address per hour. Dry runs are unlimited and do not count.
- Roof photo checks: sixty per IP address per hour.
- Reads: unmetered. Pages, markdown twins, and the JSON datasets are static files.
- Errors: tool-level failures come back as a normal JSON-RPC result with
isError: trueand a plain-English explanation of what to fix. Protocol-level failures come back as JSON-RPC errors. - Method not allowed:
GET /mcpreturns 405 with a JSON description of the endpoint. There is no server-initiated event stream. - CORS: open, so browser-based clients work without a proxy.
Reading The Site As Markdown
Every page has a markdown twin. There are two ways to get it, and they return the same bytes:
curl -s https://chicagostormpros.com/hail-damage/index.md
curl -s -H 'Accept: text/markdown' https://chicagostormpros.com/hail-damage/ Negotiated responses carry Vary: Accept, Accept-Encoding, so an intermediate cache cannot hand your agent the HTML variant. Every page also advertises its twin in a Link header and a <link rel="alternate" type="text/markdown"> tag. A path that does not exist returns a real 404 with a short markdown body listing where to look next, so a 200 from this origin always means the page is real.
Discovery Files
| Path | Contents |
|---|---|
| /llms.txt text/markdown | Site overview, when to use this site, and the full page index |
| /llms-full.txt text/markdown | Every high-value page inlined as one document, rebuilt on each deploy |
| /agent-instructions.md text/markdown | When to reach for this site and how to call it |
| /.well-known/api-catalog application/linkset+json | RFC 9727 linkset of every machine-readable entry point |
| /.well-known/mcp.json application/json | MCP server declaration |
| /.well-known/mcp/server-card.json application/json | MCP server card |
| /.well-known/agent-skills/index.json application/json | Agent Skills discovery index with sha256 digests |
| /.well-known/agent-skills/request-free-inspection/SKILL.md text/markdown | The plain-HTTP booking recipe, no MCP client needed |
| /.well-known/agent-skills/check-roof-photo/SKILL.md text/markdown | How to read a roof photo from checklist codes (MCP, A2A, WebMCP) |
| /.well-known/agent-skills/recent-storms/SKILL.md text/markdown | Recent NWS storm events for a town or county |
| /.well-known/agent-skills/permit-rules/SKILL.md text/markdown | A town's published roofing permit rules, with sources |
| /.well-known/agent-skills/hail-history/SKILL.md text/markdown | NOAA hail and wind report counts for a town since 2010 |
| /.well-known/agent-card.json application/json | A2A Agent Card for the /a2a endpoint: interface, capabilities, five skills |
| /.well-known/ai-catalog.json application/json | ARD catalog of the MCP server, A2A agent, skills, API catalog and OpenAPI file; generated from them at build time |
| /openapi.json application/json | OpenAPI 3.1 description of POST /api/photo-check, the /mcp JSON-RPC entry and /a2a |
| /sitemap.xml application/xml | Every canonical URL |
| /data/storms.json application/json | The storm-day log behind the storm map, the homepage alerts and the MCP tool (schema at /data/storms.schema.json) |
| /data/city-coords.json application/json | The 88 towns the roof photo check and the hail history know, with coordinates and county |
| /js/roof-check-rules.mjs text/javascript | The roof photo check rules as a zero-dependency ES module: the checklist codes, assess() and storms(). The same file runs on the site and in the gateway |
| /data/organization.json application/json | The Organization identity node as standalone JSON: license, credentials, ratings, profiles. Regenerated from the homepage JSON-LD on every build |
| /robots.txt text/plain | Crawl rules plus Content-Signal terms (search, ai-input, and ai-train are all yes) |
A2A Agent
For agent-to-agent clients, POST https://chicagostormpros.com/a2a speaks Agent2Agent 1.0 over JSON-RPC (SendMessage; message/send is accepted for 0.3 clients). No authentication, CORS open. The agent card lists five skills: check-roof-photo, recent-storms, permit-rules, hail-history and book-free-inspection. No model runs behind it. It routes a data part ({"skill":"permit-rules","town":"Aurora"}) or keywords in plain text to the same logic and datasets the MCP tools use, and answers anything else with a message saying what it covers.
curl -s https://chicagostormpros.com/a2a \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"SendMessage","params":{"message":{
"messageId":"m-1","role":"ROLE_USER","parts":[{"text":"recent storms in Naperville"}]}}}' Real bookings through A2A need consent: true and the homeowner's own number; dry_run validates without sending and needs no consent. Bookings share the five-per-hour limit. Streaming, stored tasks, push notifications and an extended card are not supported and return the A2A error codes for them. The same endpoints are described in openapi.json (OpenAPI 3.1).
In-Browser Tools (WebMCP)
Every page registers browser-side tools through navigator.modelContext.provideContext(): request_free_inspection, check_service_area, get_recent_storm_events, and check_roof_photo. An agent operating inside a browser session can call them without leaving the page, and they deliver into the same lead pipeline as the server-side gateway.
Structured Data
Every page carries JSON-LD: a RoofingContractor and Organization node with address, contact points, licensing, credentials, and sameAs profiles; a WebSite node; a WebPage node with a build timestamp; plus BreadcrumbList, FAQPage, HowTo, ItemList, and VideoObject where those apply. The organization node is stable at https://chicagostormpros.com/#organization, so it can be referenced rather than re-parsed.
Support
Email info@cnroofs.com or call (708) 847-5095 and say it is about the agent API. For what this site is and who runs it, see about; for how submitted data is handled, see the privacy policy.