fema-mcp-server
An open-source MCP server that allows AI agents to query FEMA disaster declarations, public assistance grants, housing aid, and NFIP flood insurance claims. It supports searching by state, incident type, and date range, retrieving specific disaster details, and running SQL queries on flood claim data via DataCanvas.
Endpoint and access
No sign-in to connect and list tools. Individual tools may still need a vendor account, credentials, or permissions; check the documented prerequisites.
https://fema.caseyjhand.com/mcpWhat its tools do
8 tools, grouped by the hints the server advertises. Hints are self-declared, not verified behavior.
Checks and signals
Each line is separate evidence, not a trust score.
- Passed Endpoint answered the live check ·
- Availability 1 of 1 checks passed in 30 days
What you can do with it
- Search federal disaster declarations by state, incident type, or date range
- Retrieve public assistance funded projects for a specific disaster
- Get individual assistance housing data broken down by county or ZIP
- Run SQL queries against NFIP flood insurance claim datasets
Source and maintenance
- Maintenance checked
Protocol and capabilities
- Server
- fema-mcp-server 0.5.1
- MCP protocol negotiated in check
- 2025-11-25
- Tools
- 8 advertised
- Resources
- 1 advertised
- Resource templates
- 2 advertised
- Prompts
- 0 advertised
{
"tools": {
"listChanged": true
},
"logging": {},
"prompts": {
"listChanged": true
},
"resources": {
"subscribe": true,
"listChanged": true
},
"extensions": {
"io.modelcontextprotocol/skills": {
"directoryRead": true
}
}
}Server-provided instructions
Untrusted server output shown for inspection. It is not Koskamo guidance and is excluded from knowledge search.
Tools
8 advertised
fema_search_disasters — Search FEMA Disaster Declarations
Search federal disaster declarations by title, state, incident type, declaration type, date range, and county. Returns deduplicated declaration-level summaries — each disaster number appears once with a designated_area_count showing how many counties/municipalities were designated. The disaster number is the chain key for fema_get_disaster, fema_get_public_assistance, and fema_get_housing_assistance. Use declaration_type to filter: DR (major disaster, most common), EM (emergency), FM (fire management). Date filters apply to the declaration date. Use fema_get_disaster to retrieve all designated-area rows for a specific declaration. county selects the declarations that designated a matching area; their area counts and program flags still describe the whole declaration, and matched_areas lists the matching areas with their own program flags. A search reads the most recent 10,000 designated-area rows; when more match, it returns the declarations dated after the day that window ends, sets truncated, and its notice names the date_to that continues with older declarations. A page holds at most 100,000 characters of declarations; when declarations remain past it, next_offset is the offset to continue from.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"limit": {
"type": "integer",
"default": 50,
"maximum": 1000,
"minimum": 1,
"description": "Maximum number of unique disaster declarations to return (1–1000, default 50). A page also stops at 100,000 characters of declarations (roughly 290 declarations, about 190 with county); when either bound leaves matching declarations out, next_offset names the offset to continue from."
},
"state": {
"type": "string",
"maxLength": 2,
"minLength": 2,
"description": "Two-letter US state/territory code (e.g., TX, CA, FL, PR). Filters by state."
},
"title": {
"type": "string",
"description": "Case-insensitive substring of the declaration title (e.g., harvey). A substring can also match unrelated names such as HARVEYTOWN FIRE; it does not identify a unique storm."
},
"county": {
"type": "string",
"description": "Filter by designated area / county name (e.g., Harris, Harris County, Harris (County)). Case-insensitive substring match: bare harris also matches Harrison. Recognized terminal County, Parish, Borough, and Census Area forms match with or without parentheses. Selects declarations by matching areas; declaration counts and flags still describe the whole declaration, and matched_areas lists the matching areas."
},
"offset": {
"type": "integer",
"default": 0,
"maximum": 9007199254740991,
"minimum": 0,
"description": "Pagination offset in declarations (default 0). Pass the previous page’s next_offset to continue."
},
"date_to": {
"anyOf": [
{
"type": "string",
"const": ""
},
{
"type": "string",
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$",
"description": "Calendar date, YYYY-MM-DD."
}
],
"description": "End of the declaration date range as a calendar date in YYYY-MM-DD format (e.g., 2024-12-31). Inclusive; equal to date_from for a single day."
},
"date_from": {
"anyOf": [
{
"type": "string",
"const": ""
},
{
"type": "string",
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$",
"description": "Calendar date, YYYY-MM-DD."
}
],
"description": "Start of the declaration date range as a calendar date in YYYY-MM-DD format (e.g., 2024-01-01). Inclusive; not after date_to."
},
"incident_type": {
"type": "string",
"description": "Incident type filter (e.g., Flood, Hurricane, Tornado, Fire, Earthquake, Severe Storm). Case-insensitive substring match: storm also matches Snowstorm and Winter Storm."
},
"declaration_type": {
"enum": [
"DR",
"EM",
"FM"
],
"type": "string",
"description": "Declaration type: DR (major disaster declaration), EM (emergency declaration), FM (fire management assistance declaration)."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"declarations",
"total_declarations",
"total_area_rows",
"returned_count"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"invalid_state",
"invalid_range",
"request_too_long"
],
"description": "Machine-readable failure mode. Declared by this tool: `invalid_state`: The state parameter is not a valid 2-letter US state/territory code. `invalid_range`: date_from is later than date_to, so no declaration could match. `request_too_long`: The title, incident_type, or county value makes the request URL longer than the roughly 8,900 characters OpenFEMA accepts. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"notice": {
"type": "string",
"description": "Zero-match guidance for supplied filters; paging guidance with continuation or the last valid offset for nonzero totals; when truncated is true, lower-bound totals and the date_to (YYYY-MM-DD) to search next; when area_totals_unavailable is true, why full area totals could not be read."
},
"truncated": {
"type": "boolean",
"description": "True when more than 10,000 designated-area rows match: the search covers only the declarations dated after the day its 10,000-row window ends. Each returned declaration is complete, the totals are lower bounds, and the notice names the date_to that continues with older declarations. Absent otherwise."
},
"totalCount": {
"type": "number",
"description": "Unique disaster declarations matching the query — the unit declaration-level pagination pages over. Exceeds returned_count when the matches span more than one page. A lower bound when truncated is true."
},
"next_offset": {
"type": "number",
"description": "Offset of the next page: offset plus returned_count. Present only while matching declarations remain past this page — when truncated is true, only within the declarations dated after the day the window ends. Pass it as offset to continue."
},
"declarations": {
"type": "array",
"items": {
"type": "object",
"required": [
"disaster_number",
"title",
"state",
"incident_type",
"declaration_type",
"declaration_date",
"ia_declared",
"pa_declared",
"hm_declared",
"designated_area_count"
],
"properties": {
"state": {
"type": "string",
"description": "Two-letter state/territory code."
},
"title": {
"type": "string",
"description": "Official declaration title (e.g., \"SEVERE STORMS AND FLOODING\")."
},
"hm_declared": {
"type": "boolean",
"description": "True when Hazard Mitigation grants were declared for any area of the declaration, also when county is set."
},
"ia_declared": {
"type": "boolean",
"description": "True when legacy IA or the Individuals and Households Program (IHP) was declared for any area. Does not guarantee housing records. Describes the whole declaration also when county is set; matched_areas carries each matched area’s own flag."
},
"pa_declared": {
"type": "boolean",
"description": "True when Public Assistance (infrastructure recovery grants) was declared for any area of the declaration, also when county is set."
},
"incident_type": {
"type": "string",
"description": "Type of incident (e.g., Flood, Hurricane, Tornado, Severe Storm)."
},
"matched_areas": {
"type": "array",
"items": {
"type": "object",
"required": [
"area",
"ia_declared",
"pa_declared",
"hm_declared"
],
"properties": {
"area": {
"type": "string",
"description": "Name of the designated area, as stored (e.g., \"Bexar (County)\")."
},
"hm_declared": {
"type": "boolean",
"description": "True when Hazard Mitigation was declared for this area."
},
"ia_declared": {
"type": "boolean",
"description": "True when legacy IA or IHP was declared for this area."
},
"pa_declared": {
"type": "boolean",
"description": "True when Public Assistance was declared for this area."
},
"fips_state_code": {
"type": "string",
"description": "2-digit state FIPS code. Absent when not recorded."
},
"fips_county_code": {
"type": "string",
"description": "3-digit county FIPS code. Absent when not recorded."
}
},
"description": "One designated area that county matched, with its own program flags.",
"additionalProperties": false
},
"description": "The designated areas of this declaration that county matched, each with the programs declared for that area — which can differ from the declaration: an area designated for PA and HM only has ia_declared false. Present only when county is set."
},
"disaster_number": {
"type": "number",
"description": "Unique FEMA disaster number — pass to fema_get_disaster, fema_get_public_assistance, or fema_get_housing_assistance."
},
"declaration_date": {
"type": "string",
"description": "ISO 8601 date the declaration was signed."
},
"declaration_type": {
"type": "string",
"description": "Declaration type: DR (major disaster), EM (emergency), or FM (fire management)."
},
"incident_end_date": {
"type": "string",
"description": "ISO 8601 incident end date. Absent for ongoing or unrecorded incidents."
},
"incident_begin_date": {
"type": "string",
"description": "ISO 8601 incident start date. Absent when not recorded."
},
"designated_area_count": {
"type": "number",
"description": "Number of counties/municipalities the whole declaration designated, also when county is set — use fema_get_disaster for the full area list."
}
},
"description": "Deduplicated summary for one disaster declaration.",
"additionalProperties": false
},
"description": "Disaster declarations matching the search from offset onward, one entry per unique disaster number, bounded by limit and by 100,000 characters of declarations."
},
"returned_count": {
"type": "number",
"description": "Number of unique deduplicated declarations in this response."
},
"total_area_rows": {
"type": "number",
"description": "Total matching designated-area rows from the API (raw row count, not declaration count) — with county set, the rows of the matching areas only. DisasterDeclarationsSummaries returns one row per designated area per disaster, so this is always ≥ the number of unique declarations. When it exceeds 10,000, the search reads the most recent 10,000 rows and drops the declarations dated on the oldest day among them, so every returned declaration is complete; the notice names the date_to that continues with older declarations."
},
"total_declarations": {
"type": "number",
"description": "Unique disaster declarations matching the query, before offset/limit paging — the bound for declaration-level pagination (limit/offset apply to declarations, not area rows). A lower bound when truncated is true: it counts only the declarations dated after the day the 10,000-row window ends."
},
"area_totals_unavailable": {
"type": "boolean",
"description": "True when county is set and reading the returned declarations’ full designated-area rows failed: designated_area_count and the ia/pa/hm flags then count only the areas county matched, and the notice names the cause. Absent otherwise."
}
},
"additionalProperties": false
}{
"readOnlyHint": true,
"openWorldHint": true
}fema_get_disaster — Get FEMA Disaster Details
Fetch up to 1,000 designated-area rows for a specific FEMA disaster by disaster number (e.g., 4781). Discloses partial area and program coverage when more rows exist. Returns counties/municipalities along with programs observed, incident period, and state info. Use fema_search_disasters to find disaster numbers. The returned disaster_number chains to fema_get_public_assistance and fema_get_housing_assistance.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"disaster_number"
],
"properties": {
"disaster_number": {
"type": "integer",
"maximum": 9007199254740991,
"description": "FEMA disaster number (e.g., 4781). Obtain from fema_search_disasters.",
"exclusiveMinimum": 0
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"disaster_number",
"title",
"state",
"incident_type",
"declaration_type",
"declaration_date",
"ia_declared",
"pa_declared",
"hm_declared",
"designated_areas",
"designated_area_count"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"not_found"
],
"description": "Machine-readable failure mode. Declared by this tool: `not_found`: No declaration found with the given disaster number. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"state": {
"type": "string",
"description": "Two-letter state/territory code."
},
"title": {
"type": "string",
"description": "Official declaration title (e.g., \"SEVERE STORMS AND FLOODING\")."
},
"notice": {
"type": "string",
"description": "Returned/total area counts and partial program evidence, with the dataset tool for full rows."
},
"hm_declared": {
"type": "boolean",
"description": "True when HM was declared in a fetched row. When areas_truncated is true, false does not exclude HM in unfetched rows."
},
"ia_declared": {
"type": "boolean",
"description": "True when legacy IA or IHP was declared in a fetched row. Does not guarantee housing records. When areas_truncated is true, false does not exclude IA in unfetched rows."
},
"pa_declared": {
"type": "boolean",
"description": "True when PA was declared in a fetched row. When areas_truncated is true, false does not exclude PA in unfetched rows."
},
"incident_type": {
"type": "string",
"description": "Type of incident (e.g., Flood, Hurricane, Severe Storm)."
},
"areas_truncated": {
"type": "boolean",
"description": "True when designated-area rows remain unfetched; program flags cover only fetched rows."
},
"disaster_number": {
"type": "number",
"description": "FEMA disaster number — use as the chain key for PA and housing assistance tools."
},
"declaration_date": {
"type": "string",
"description": "ISO 8601 date the declaration was signed."
},
"declaration_type": {
"type": "string",
"description": "Declaration type: DR (major disaster), EM (emergency), or FM (fire management)."
},
"designated_areas": {
"type": "array",
"items": {
"type": "object",
"required": [
"area",
"ia_declared",
"pa_declared",
"hm_declared"
],
"properties": {
"area": {
"type": "string",
"description": "Name of the designated county or municipality."
},
"hm_declared": {
"type": "boolean",
"description": "True when HM was declared for this area."
},
"ia_declared": {
"type": "boolean",
"description": "True when legacy IA or IHP was declared for this area."
},
"pa_declared": {
"type": "boolean",
"description": "True when PA was declared for this area."
},
"fips_state_code": {
"type": "string",
"description": "2-digit state FIPS code. Absent when not returned."
},
"fips_county_code": {
"type": "string",
"description": "3-digit county FIPS code. Absent when not returned."
}
},
"description": "A single designated area entry.",
"additionalProperties": false
},
"description": "Fetched counties/municipalities designated for assistance, up to 1,000 rows."
},
"incident_end_date": {
"type": "string",
"description": "ISO 8601 end date of the incident. Absent for ongoing or unrecorded incidents."
},
"incident_begin_date": {
"type": "string",
"description": "ISO 8601 start date of the incident. Absent when not recorded."
},
"designated_area_count": {
"type": "number",
"description": "Number of designated-area rows fetched for this declaration."
},
"total_designated_area_count": {
"type": "number",
"description": "Upstream total designated-area rows, present when areas_truncated is true."
}
},
"additionalProperties": false
}{
"readOnlyHint": true,
"openWorldHint": true
}fema_get_public_assistance — Get FEMA Public Assistance Projects
Retrieve Public Assistance (PA) funded project records for a disaster or state — shows where federal recovery money was obligated. Returns applicant, damage category, project size and status, federal share obligated, and total obligated amounts. Either disaster_number or state must be provided. Use disaster_number (from fema_search_disasters) to scope to a single declaration, or state to browse all PA projects for a state. PA projects are created only when the PA program is declared (pa_declared: true on the disaster). A page holds at most 100,000 characters of projects; when projects remain past it, next_offset is the offset to continue from.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"limit": {
"type": "integer",
"default": 100,
"maximum": 10000,
"minimum": 1,
"description": "Maximum number of projects to return (1–10000, default 100). A page also stops at 100,000 characters of projects (roughly 220 projects); when either bound leaves matching projects out, next_offset names the offset to continue from."
},
"state": {
"type": "string",
"maxLength": 2,
"minLength": 2,
"description": "Two-letter state code to browse all PA projects for a state."
},
"county": {
"type": "string",
"description": "Filter by county name (e.g., Harris, dekalb). Case-insensitive substring match: harris also matches Harrison. Recognized terminal County, Parish, Borough, and Census Area names accept plain or parenthesized types; unknown names retain original case fallbacks."
},
"offset": {
"type": "integer",
"default": 0,
"maximum": 9007199254740991,
"minimum": 0,
"description": "Pagination offset (default 0). Pass the previous page’s next_offset to continue."
},
"disaster_number": {
"type": "integer",
"maximum": 9007199254740991,
"description": "FEMA disaster number to scope results to a single declaration. Numbers above 32767 match no declaration.",
"exclusiveMinimum": 0
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"projects",
"total_count",
"returned_count"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"invalid_state",
"missing_filter",
"not_found",
"request_too_long"
],
"description": "Machine-readable failure mode. Declared by this tool: `invalid_state`: The state parameter is not a valid 2-letter US state/territory code. `missing_filter`: Neither disaster_number nor state was provided. `not_found`: The disaster number is above 32767, the highest number OpenFEMA holds, so no declaration exists. `request_too_long`: The county value makes the request URL longer than the roughly 8,900 characters OpenFEMA accepts. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"notice": {
"type": "string",
"description": "Zero-match guidance for supplied filters and declared programs, or paging guidance: the continuation offset and whether limit or the response budget ended the page, or the last valid offset for a nonzero total."
},
"projects": {
"type": "array",
"items": {
"type": "object",
"required": [
"disaster_number"
],
"properties": {
"state": {
"type": "string",
"description": "Two-letter state abbreviation. Absent when not recorded."
},
"county": {
"type": "string",
"description": "County where the project is located. Absent when not recorded."
},
"pw_number": {
"type": "number",
"description": "Project Worksheet number assigned by FEMA. Absent when not yet assigned."
},
"applicant_id": {
"type": "string",
"description": "FEMA applicant identifier for the recipient organization. Absent when not recorded."
},
"project_size": {
"type": "string",
"description": "Large or Small, set by comparing the eligible amount from the damage survey with the large-project threshold FEMA sets for each fiscal year. Absent when unclassified."
},
"project_amount": {
"type": "number",
"description": "Estimated total eligible cost of the project in USD from FEMA's damage survey, without administrative costs. Absent when not yet determined."
},
"project_status": {
"type": "string",
"description": "Eligibility status of the project: Eligible (nearly all projects), Pending Eligibility Review, Ineligible, or Withdrawn. Absent when not set."
},
"disaster_number": {
"type": "number",
"description": "FEMA disaster number this project belongs to."
},
"total_obligated": {
"type": "number",
"description": "Federal share of the eligible project amount plus State (recipient) and applicant (subrecipient) administrative costs, in USD; equals federal_share_obligated when no administrative costs are recorded. Holds no non-federal share. 0 when nothing is obligated."
},
"application_title": {
"type": "string",
"description": "Name of the applicant organization or project. Absent when not recorded."
},
"damage_category_code": {
"type": "string",
"description": "FEMA damage category code (e.g., A=Debris Removal, B=Emergency Protective Measures, C=Roads and Bridges). Absent when unclassified."
},
"first_obligation_date": {
"type": "string",
"description": "ISO 8601 date the first federal obligation was recorded. Absent before obligation."
},
"federal_share_obligated": {
"type": "number",
"description": "Federal dollars obligated for this project in USD; 0 when nothing is obligated."
},
"damage_category_description": {
"type": "string",
"description": "Human-readable name for the damage category code. Absent when unclassified."
}
},
"description": "A single Public Assistance funded project record.",
"additionalProperties": false
},
"description": "Public assistance funded project records from offset onward, bounded by limit and by 100,000 characters of projects."
},
"totalCount": {
"type": "number",
"description": "Total matching projects before pagination — exceeds returned_count when results were capped at the limit."
},
"next_offset": {
"type": "number",
"description": "Offset of the next page: offset plus returned_count. Present only while matching projects remain past this page; pass it as offset to continue."
},
"total_count": {
"type": "number",
"description": "Total matching projects before pagination."
},
"returned_count": {
"type": "number",
"description": "Number of projects in this response."
}
},
"additionalProperties": false
}{
"readOnlyHint": true,
"openWorldHint": true
}fema_get_housing_assistance — Get FEMA Housing Assistance Data
Retrieve Individual Assistance (IA) housing data for a disaster by disaster number. Returns owner and/or renter records aggregated by county, city, and ZIP code — valid registrations, total approved IHP amounts, repair/rental/other-needs amounts, and inspection counts (owners also carry inspected damage in USD). Narrow with state, county, or zip_code; owners_count, renters_count, and offset then count the filtered records. Use type to select owners, renters, or both (default). A page holds at most 100,000 characters of records, owners and renters cut at the same row count so one offset continues both; when records remain past it, next_offset is the offset to continue from. Use disaster_number from fema_search_disasters.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"disaster_number"
],
"properties": {
"type": {
"enum": [
"owners",
"renters",
"both"
],
"type": "string",
"default": "both",
"description": "Which housing assistance dataset to query: owners, renters, or both (default)."
},
"limit": {
"type": "integer",
"default": 100,
"maximum": 10000,
"minimum": 1,
"description": "Maximum records per dataset to return (1–10000, default 100). A page also stops at 100,000 characters of owner and renter records together, cutting both datasets at the same row count; when either bound leaves matching records out, next_offset names the offset to continue from."
},
"state": {
"type": "string",
"maxLength": 2,
"minLength": 2,
"description": "Two-letter state code to narrow results when a disaster spans multiple states."
},
"county": {
"type": "string",
"description": "Filter by county name (e.g., Harris, Harris County, Harris (County)). Case-insensitive substring match: bare harris also matches Harrison. Recognized terminal County, Parish, Borough, and Census Area forms match with or without parentheses, retaining the county name and type."
},
"offset": {
"type": "integer",
"default": 0,
"maximum": 9007199254740991,
"minimum": 0,
"description": "Pagination offset into each queried dataset (default 0). Pass the previous page’s next_offset to continue both."
},
"zip_code": {
"anyOf": [
{
"type": "string",
"const": ""
},
{
"type": "string",
"pattern": "^\\d{5}$",
"description": "5-digit ZIP code."
}
],
"description": "Filter by 5-digit ZIP code, as a string with leading zeros kept (e.g., 77084, 00609). ZIP+4 is not accepted. One ZIP can return several records, one per county and city spelling."
},
"disaster_number": {
"type": "integer",
"maximum": 9007199254740991,
"description": "FEMA disaster number. Obtain from fema_search_disasters. Numbers above 32767 match no declaration.",
"exclusiveMinimum": 0
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"owners",
"renters",
"owners_count",
"renters_count"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"invalid_state",
"not_found",
"request_too_long"
],
"description": "Machine-readable failure mode. Declared by this tool: `invalid_state`: The state parameter is not a valid 2-letter US state/territory code. `not_found`: The disaster number is above 32767, the highest number OpenFEMA holds, so no declaration exists. `request_too_long`: The county value makes the request URL longer than the roughly 8,900 characters OpenFEMA accepts. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"notice": {
"type": "string",
"description": "Zero-match guidance for supplied filters and declared programs, or paging guidance: the continuation offset and whether limit or the response budget ended the page, and each queried nonzero dataset’s total and last valid offset when past its end."
},
"owners": {
"type": "array",
"items": {
"type": "object",
"required": [
"disaster_number"
],
"properties": {
"city": {
"type": "string",
"description": "City name. Absent when not recorded."
},
"state": {
"type": "string",
"description": "Two-letter state code. Absent when not recorded."
},
"county": {
"type": "string",
"description": "County name. Absent when not recorded."
},
"zip_code": {
"type": "string",
"description": "5-digit ZIP code; 00000 when OpenFEMA files the records under no ZIP code. Absent when not recorded."
},
"total_damage": {
"type": "number",
"description": "Total damage in USD recorded at the latest inspections: FEMA Verified Loss for real property (the home) and personal property, excluding transportation; 0 when none was recorded or no one was inspected (see total_inspected). Absent when not reported."
},
"rental_amount": {
"type": "number",
"description": "Rental assistance granted in USD; 0 when none was. Absent when not reported."
},
"disaster_number": {
"type": "number",
"description": "FEMA disaster number this record belongs to."
},
"total_inspected": {
"type": "number",
"description": "Number of homeowner applicants who received a FEMA inspection. 0 means no one in this area was inspected, so a 0 in average_fema_inspected_damage or total_damage then means not inspected, not $0 of damage. Absent when not reported."
},
"other_needs_amount": {
"type": "number",
"description": "Other needs assistance (personal property, transportation, etc.) in USD; 0 when none was approved. Absent when not reported."
},
"valid_registrations": {
"type": "number",
"description": "Number of valid IA registrations from homeowners in this area. Absent when not reported."
},
"repair_replace_amount": {
"type": "number",
"description": "Home repair and replacement assistance in USD; 0 when none was approved. Absent when not reported."
},
"no_fema_inspected_damage": {
"type": "number",
"description": "Number of inspected homeowner applicants whose inspector recorded no damage; 0 when there were none. Absent when not reported."
},
"total_approved_ihp_amount": {
"type": "number",
"description": "Total Individuals and Households Program (IHP) assistance approved in USD; 0 when none was. Absent when not reported."
},
"approved_for_fema_assistance": {
"type": "number",
"description": "Number of homeowner registrations approved for any FEMA assistance; 0 when none were. Absent when not reported."
},
"average_fema_inspected_damage": {
"type": "number",
"description": "Average damage in USD, under FEMA's inspection guidelines, of the homeowners with a completed inspection; 0 when no damage was recorded or no one was inspected (see total_inspected). Measured separately from total_damage, so it is not total_damage divided by a count. Absent when not reported."
}
},
"description": "Homeowner housing assistance aggregated by county and ZIP for one disaster.",
"additionalProperties": false
},
"description": "Homeowner (HousingAssistanceOwners) records. Empty when type is \"renters\" or no owner data exists."
},
"renters": {
"type": "array",
"items": {
"type": "object",
"required": [
"disaster_number"
],
"properties": {
"city": {
"type": "string",
"description": "City name. Absent when not recorded."
},
"state": {
"type": "string",
"description": "Two-letter state code. Absent when not recorded."
},
"county": {
"type": "string",
"description": "County name. Absent when not recorded."
},
"zip_code": {
"type": "string",
"description": "5-digit ZIP code; 00000 when OpenFEMA files the records under no ZIP code. Absent when not recorded."
},
"rental_amount": {
"type": "number",
"description": "Rental assistance granted in USD; 0 when none was. Absent when not reported."
},
"disaster_number": {
"type": "number",
"description": "FEMA disaster number this record belongs to."
},
"total_inspected": {
"type": "number",
"description": "Number of renter applicants who received a FEMA inspection; 0 when none did. Absent when not reported."
},
"other_needs_amount": {
"type": "number",
"description": "Other needs assistance in USD; 0 when none was approved. Absent when not reported."
},
"valid_registrations": {
"type": "number",
"description": "Number of valid IA registrations from renters in this area. Absent when not reported."
},
"repair_replace_amount": {
"type": "number",
"description": "Home repair and replacement assistance in USD as OpenFEMA records it for renters; 0 when none was approved, as in nearly every renter area, since FEMA's data dictionary notes renters are not eligible for it. Absent when not reported."
},
"total_with_major_damage": {
"type": "number",
"description": "Number of valid renter registrations assigned a major degree of damage, counted like total_inspected_with_no_damage; 0 when there were none. Absent when not reported."
},
"total_approved_ihp_amount": {
"type": "number",
"description": "Total IHP assistance approved for renters in USD; 0 when none was. Absent when not reported."
},
"total_with_moderate_damage": {
"type": "number",
"description": "Number of valid renter registrations assigned a moderate degree of damage, counted like total_inspected_with_no_damage; 0 when there were none. Absent when not reported."
},
"approved_for_fema_assistance": {
"type": "number",
"description": "Number of renter registrations approved for any FEMA assistance; 0 when none were. Absent when not reported."
},
"total_with_substantial_damage": {
"type": "number",
"description": "Number of valid renter registrations assigned a substantial degree of damage, counted like total_inspected_with_no_damage; 0 when there were none. Absent when not reported."
},
"total_inspected_with_no_damage": {
"type": "number",
"description": "Number of valid renter registrations assigned no damage. Renters get a damage degree rather than a full home inspection, so this and the other degree counts count registrations and can exceed total_inspected; 0 when there were none. Absent when not reported."
}
},
"description": "Renter housing assistance aggregated by county and ZIP for one disaster. Renter records carry no dollar damage figures.",
"additionalProperties": false
},
"description": "Renter (HousingAssistanceRenters) records. Empty when type is \"owners\" or no renter data exists."
},
"totalCount": {
"type": "number",
"description": "Total owner + renter records available before the per-dataset limit — exceeds the returned count when either dataset was capped."
},
"next_offset": {
"type": "number",
"description": "Offset of the next page, for both datasets: offset plus the larger of the owner and renter record counts returned. Present only while records remain in a queried dataset; pass it as offset to continue."
},
"owners_count": {
"type": "number",
"description": "Total owner records matching the disaster and any state, county, or zip_code filter, before the per-dataset limit."
},
"county_filter": {
"type": "string",
"description": "The county name both datasets were filtered by, trimmed, matched as a case-insensitive substring. Absent when county was not set."
},
"renters_count": {
"type": "number",
"description": "Total renter records matching the disaster and any state, county, or zip_code filter, before the per-dataset limit."
},
"zip_code_filter": {
"type": "string",
"description": "The 5-digit ZIP code both datasets were filtered by. Absent when zip_code was not set."
}
},
"additionalProperties": false
}{
"readOnlyHint": true,
"openWorldHint": true
}fema_search_nfip — Search NFIP Flood Insurance Claims
Search National Flood Insurance Program (NFIP) claims data by state, county, ZIP code, and year range. Returns the matching claim count and claim records — amounts paid on building and contents, damage estimates, flood zones, cause and occupancy codes, and loss dates — newest loss first. state is required — the full NFIP dataset is 2.7 million rows; unfiltered access is prohibited. Page the inline claims with limit and offset; a page holds at most 100,000 characters of claims, and while claims remain past it, next_offset is the offset to continue from. When DataCanvas is enabled and the match exceeds that inline budget, a call at offset 0 stages the match (up to 50,000 claims) on a canvas: inspect the staged table with fema_dataframe_describe, then aggregate it with SQL via fema_dataframe_query.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"state"
],
"properties": {
"limit": {
"type": "integer",
"default": 1000,
"maximum": 10000,
"minimum": 1,
"description": "Maximum claims to return inline (1–10000, default 1000). A page also stops at 100,000 characters of claims (roughly 330 claims); when either bound leaves matching claims out, next_offset names the offset to continue from. When DataCanvas is enabled and the match exceeds that character budget, a call at offset 0 stages the match on a canvas regardless of this value."
},
"state": {
"type": "string",
"maxLength": 2,
"minLength": 2,
"description": "Two-letter US state code (required). NFIP dataset is 2.7M rows — state filter is mandatory."
},
"offset": {
"type": "integer",
"default": 0,
"maximum": 9007199254740991,
"minimum": 0,
"description": "Pagination offset into the matching claims, ordered newest date of loss first (default 0). Pass the previous page’s next_offset to continue. Only a call at offset 0 stages a match on a canvas; a later offset returns just its inline page. After a spill (spilled=true), read rows past the inline claims from the canvas table with fema_dataframe_query."
},
"year_to": {
"type": "integer",
"maximum": 2100,
"minimum": 1970,
"description": "End year of loss, inclusive (e.g., 2023). Equal to year_from for a single year."
},
"zip_code": {
"type": "string",
"pattern": "^\\d{5}$",
"description": "ZIP code to narrow results to a specific area (5-digit, e.g., 77002)."
},
"canvas_id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{10}$",
"description": "Optional canvas ID from a prior call, to stage this match as another table on that canvas. Used only when the match is staged; omit it to stage on a fresh canvas. Pass the returned canvas_id to fema_dataframe_describe for the table columns, then to fema_dataframe_query for SQL."
},
"year_from": {
"type": "integer",
"maximum": 2100,
"minimum": 1970,
"description": "Start year of loss, inclusive (e.g., 2020). Not after year_to."
},
"county_code": {
"type": "string",
"description": "County FIPS code to narrow results within the state: the full 5-digit state+county code (e.g., 48201 for Harris County TX), or the 3-digit county part (e.g., 201), to which the server prepends the state FIPS. A shorter county part is zero-padded to 3 digits (1 → 001), and a 4-digit code is zero-padded to 5 (1001 → 01001), the form a numeric 01001 arrives in. Any other value is rejected before the search runs."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"claims",
"total_count",
"returned_count",
"spilled"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"invalid_state",
"invalid_county_code",
"invalid_range"
],
"description": "Machine-readable failure mode. Declared by this tool: `invalid_state`: The state parameter is not a valid 2-letter US state/territory code. `invalid_county_code`: county_code is not 1–5 digits, or is a 1–3-digit county part for a state with no FIPS code (FM, MH, PW). `invalid_range`: year_from is later than year_to, so no claim could match. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"claims": {
"type": "array",
"items": {
"type": "object",
"properties": {
"state": {
"type": "string",
"description": "Two-letter state code. Absent when not in the record."
},
"zip_code": {
"type": "string",
"description": "5-digit ZIP code of the insured property. Absent when not recorded."
},
"county_code": {
"type": "string",
"description": "5-digit state+county FIPS code (e.g., 48201 for Harris County TX). Absent when not recorded."
},
"date_of_loss": {
"type": "string",
"description": "ISO 8601 date the flood loss occurred. Absent when not recorded."
},
"year_of_loss": {
"type": "number",
"description": "Calendar year the flood loss occurred. Absent when not recorded."
},
"occupancy_type": {
"type": "number",
"description": "NFIP occupancy type code, as published. Legacy codes: 1 single-family residence; 2 residential building with 2 to 4 units; 3 residential building with more than 4 units; 4 non-residential building; 6 non-residential business. Risk Rating 2.0 codes: 11 single-family residential building (except a mobile home or a single unit within a multi-unit building); 12 residential non-condo building with 2 to 4 units, insuring all units; 13 residential non-condo building with 5 or more units, insuring all units; 14 residential mobile or manufactured home; 15 residential condo association building; 16 single residential unit within a multi-unit building; 17 non-residential mobile or manufactured home; 18 non-residential building; 19 non-residential unit within a multi-unit building. Other values pass through unchanged. Absent when not recorded."
},
"cause_of_damage": {
"type": "string",
"description": "OpenFEMA cause-of-damage code, as published: 0 other causes; 1 tidal water overflow; 2 stream, river, or lake overflow; 3 alluvial fan overflow; 4 accumulation of rainfall or snowmelt; 7 erosion-demolition; 8 erosion-removal; 9 earth movement, landslide, land subsidence, sinkholes, etc.; A closed basin lake; B expedited claim handling without site inspection; C expedited claim handling follow-up site inspection; D expedited claim handling by the Adjusting Process Pilot Program (remote adjustment). Other values pass through unchanged. Absent when not recorded."
},
"rated_flood_zone": {
"type": "string",
"description": "FEMA flood zone designation at the property (e.g., AE, X, VE). Absent when not recorded."
},
"amount_paid_building": {
"type": "number",
"description": "NFIP claim payment for the building structure in USD; 0 when the claim paid nothing on the building. Absent when not recorded."
},
"amount_paid_contents": {
"type": "number",
"description": "NFIP claim payment for contents (personal property) in USD; 0 when the claim paid nothing on contents. Absent when not recorded."
},
"building_damage_amount": {
"type": "number",
"description": "Estimated total building damage in USD (may exceed paid amount). Absent when not assessed."
},
"contents_damage_amount": {
"type": "number",
"description": "Estimated total contents damage in USD (may exceed paid amount). Absent when not assessed."
}
},
"description": "A single NFIP flood insurance claim record.",
"additionalProperties": false
},
"description": "Claims from offset onward, newest date of loss first, bounded by limit and 100,000 characters of claims. When spilled=true, canvas_table holds the staged match."
},
"notice": {
"type": "string",
"description": "Zero-match, paging, and canvas guidance, composed with county-coverage disclosure: a state/year count of claims without county codes when already available, or an unchecked-coverage notice; this count does not identify any county or ZIP."
},
"spilled": {
"type": "boolean",
"description": "True when the match exceeded the inline budget and was staged on DataCanvas: call fema_dataframe_describe, then fema_dataframe_query, with canvas_id. False when nothing was staged."
},
"canvas_id": {
"type": "string",
"description": "Canvas ID for the staged match. Pass to fema_dataframe_describe and fema_dataframe_query. Present only when spilled=true."
},
"truncated": {
"type": "boolean",
"description": "True when the 50,000-row canvas cap was reached: canvas_table holds staged_count of the total_count matching claims. Apply tighter filters (county_code, zip_code, year range) to stage the complete set."
},
"next_offset": {
"type": "number",
"description": "Offset of the next inline page: offset plus returned_count. Present only while matching claims remain past this page, after a spill too; pass it as offset to continue."
},
"total_count": {
"type": "number",
"description": "Claims matching the filters, as counted by OpenFEMA — before offset, limit, and the canvas row cap."
},
"canvas_table": {
"type": "string",
"description": "DuckDB table on the canvas holding the staged claims. List its columns with fema_dataframe_describe, then reference it in fema_dataframe_query FROM clauses. Present only when spilled=true."
},
"staged_count": {
"type": "number",
"description": "Claims staged on canvas_table: the whole match, or its first 50,000 when truncated=true. Present only when spilled=true."
},
"returned_count": {
"type": "number",
"description": "Number of claim records in claims."
}
},
"additionalProperties": false
}{
"readOnlyHint": true,
"openWorldHint": true
}fema_dataframe_query — Query FEMA DataCanvas Table
Run a read-only SQL SELECT against a DataCanvas table staged by fema_search_nfip. Enables aggregation, GROUP BY, SUM/COUNT, time-series, and filtered analysis over the full NFIP claims result without re-fetching from the API. Call fema_dataframe_describe first to get the exact table name and column names needed for valid SQL. Only SELECT statements are allowed — DDL, DML, COPY, and file-reading functions are blocked. A response holds at most 100,000 characters of rows; when it holds fewer rows than the query returned, truncated and shown are set and the notice says how to page with OFFSET.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"canvas_id",
"query"
],
"properties": {
"query": {
"type": "string",
"description": "SQL SELECT statement to run against the staged table. Use the table name from fema_dataframe_describe. Results are capped at the canvas row limit and at 100,000 characters of rows; append LIMIT/OFFSET, under an ORDER BY, to page through additional rows, or aggregate (GROUP BY, COUNT, SUM) to summarize the full set in one query. Example: \"SELECT year_of_loss, COUNT(*) AS claims, SUM(amount_paid_building) AS total_building_paid FROM df_nfip_abc123 GROUP BY year_of_loss ORDER BY year_of_loss\""
},
"canvas_id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{10}$",
"description": "Canvas ID from the fema_search_nfip response (the canvas_id field)."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"rows",
"row_count",
"canvas_id"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"cap": {
"type": "number",
"description": "The canvas row limit, present only when it capped the result. Absent when only the response budget cut it."
},
"rows": {
"type": "array",
"items": {
"type": "object",
"description": "A single result row — keys are column names from the SELECT, values are the computed data.",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
"description": "Result rows from the query, bounded by the canvas row limit and by 100,000 characters of rows — when the truncated enrichment field is set, page the remainder with OFFSET (see the notice guidance)."
},
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"canvas_unavailable",
"canvas_not_found",
"missing_table",
"invalid_sql",
"non_select_statement",
"multi_statement",
"denied_function",
"plan_operator_not_allowed",
"sql_read_only",
"sql_execution_error"
],
"description": "Machine-readable failure mode. Declared by this tool: `canvas_unavailable`: DataCanvas is disabled on this deployment (CANVAS_PROVIDER_TYPE is not set to duckdb). `canvas_not_found`: The canvas_id does not correspond to an active canvas session. `missing_table`: The SELECT names a table that is not staged on this canvas — misspelled, dropped, or expired. `invalid_sql`: A SELECT fails to prepare — an unknown column, function, or type, or an invalid expression. `non_select_statement`: The statement is not a SELECT (DDL, DML, PRAGMA, EXPLAIN, ATTACH) or cannot be parsed. `multi_statement`: The query holds more than one SQL statement. `denied_function`: The SELECT calls a blocked function — a file reader, an engine-setting reader, or query()/query_table() over anything but one literal SELECT. `plan_operator_not_allowed`: The query plan uses an operator outside the read-only allowlist, such as a table function like range() or a system catalog like information_schema. `sql_read_only`: The statement writes or names a file, such as COPY … TO or a quoted file path in FROM. `sql_execution_error`: A SELECT fails while converting or calculating values in the staged data. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"shown": {
"type": "number",
"description": "Number of rows in this truncated response — the amount to advance OFFSET by, and to lower any LIMIT by, for the next page."
},
"notice": {
"type": "string",
"description": "Continuation guidance when truncated — how far to advance OFFSET, and lower any LIMIT, under an ORDER BY, to page the remainder."
},
"canvas_id": {
"type": "string",
"description": "Canvas ID that was queried — reuse for follow-up queries."
},
"row_count": {
"type": "number",
"description": "Number of rows in this response — fewer than the query returned when truncated is set."
},
"truncated": {
"type": "boolean",
"description": "True when more rows match than this response holds: the result was capped at the canvas row limit, or cut at the 100,000-character response budget."
}
},
"additionalProperties": false
}{
"readOnlyHint": true,
"openWorldHint": false,
"idempotentHint": true
}fema_dataframe_describe — Describe FEMA DataCanvas Tables
List tables and column schemas on a DataCanvas staged by fema_search_nfip. Call this before fema_dataframe_query to discover the exact table name, column names, and DuckDB data types needed to write valid SQL. Row count reflects what was actually staged — check truncated in the fema_search_nfip response to know whether the canvas holds the full matching set.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"canvas_id"
],
"properties": {
"canvas_id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{10}$",
"description": "Canvas ID from the fema_search_nfip response."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"tables",
"canvas_id"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"canvas_not_found",
"canvas_unavailable"
],
"description": "Machine-readable failure mode. Declared by this tool: `canvas_not_found`: The canvas_id does not correspond to an active canvas session. `canvas_unavailable`: DataCanvas is disabled on this deployment (CANVAS_PROVIDER_TYPE is not set to duckdb). Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"tables": {
"type": "array",
"items": {
"type": "object",
"required": [
"name",
"kind",
"row_count",
"columns"
],
"properties": {
"kind": {
"type": "string",
"description": "Object type: \"table\" for a registered data table, \"view\" for a derived view."
},
"name": {
"type": "string",
"description": "Table or view name — use this exact string in SQL FROM clauses."
},
"columns": {
"type": "array",
"items": {
"type": "object",
"required": [
"name",
"type"
],
"properties": {
"name": {
"type": "string",
"description": "Column name — use in SELECT, WHERE, GROUP BY, and ORDER BY clauses."
},
"type": {
"type": "string",
"description": "DuckDB SQL data type (e.g., VARCHAR, DOUBLE, INTEGER, BOOLEAN)."
},
"nullable": {
"type": "boolean",
"description": "True when the column can contain NULL values. Absent when nullability is unknown."
}
},
"description": "A single column in the table.",
"additionalProperties": false
},
"description": "All columns in this table, in schema order."
},
"row_count": {
"type": "number",
"description": "Total rows in this table — the full staged result set, not the inline preview count."
}
},
"description": "A single registered table or view on the canvas.",
"additionalProperties": false
},
"description": "All tables and views available on this canvas."
},
"canvas_id": {
"type": "string",
"description": "Canvas ID that was described — pass to fema_dataframe_query."
}
},
"additionalProperties": false
}{
"readOnlyHint": true,
"openWorldHint": false,
"idempotentHint": true
}fema_query_dataset — Query Any OpenFEMA Dataset
Generic OData query against any dataset in the OpenFEMA dataset catalog — the escape hatch for datasets the convenience tools do not cover (e.g., NfipPolicies, IndividualAssistanceHousingRegistrantsLargeDisasters, FemaWebDeclarationAreas, PublicAssistanceApplicants). Accepts raw OData filter, select, orderby, and pagination parameters. For NfipPolicies, use propertyState (not state) for the state and reportedZipCode for the ZIP code — it has no countyCode; always include a ZIP filter to avoid timeout. The dataset name must match the exact OpenFEMA entity name (case-sensitive, e.g., NfipClaims). Names missing from the catalog return an unknown_dataset error; a listed dataset OpenFEMA does not serve through the API, or a retired version, returns dataset_not_served. A page holds at most 100,000 characters of records; when records remain past it, next_offset is the offset to continue from.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"dataset"
],
"properties": {
"limit": {
"type": "integer",
"default": 100,
"maximum": 10000,
"minimum": 1,
"description": "Maximum records to return (1–10000, default 100). A page also stops at 100,000 characters of records; when either bound leaves matching records out, next_offset names the offset to continue from."
},
"filter": {
"type": "string",
"description": "OData $filter expression (e.g., \"state eq 'TX' and declarationDate ge '2024-01-01T00:00:00.000Z'\"). String values in single quotes. For a list of values use field in ('a','b'): OpenFEMA rejects filters nested more than 100 levels deep (about 90 chained and/or comparisons) and request URLs past about 8,900 characters."
},
"offset": {
"type": "integer",
"default": 0,
"maximum": 9007199254740991,
"minimum": 0,
"description": "Pagination offset (default 0). Pass the previous page’s next_offset to continue."
},
"select": {
"type": "string",
"description": "Comma-separated field names to return (e.g., \"disasterNumber,state,declarationDate\")."
},
"dataset": {
"type": "string",
"minLength": 1,
"description": "OpenFEMA dataset entity name (case-sensitive, e.g., NfipPolicies, FemaWebDeclarationAreas, PublicAssistanceApplicants)."
},
"orderby": {
"type": "string",
"description": "OData $orderby expression (e.g., \"declarationDate desc\")."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"dataset",
"rows",
"total_count",
"returned_count"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"rows": {
"type": "array",
"items": {
"type": "object",
"properties": {},
"description": "A single record from the queried dataset. Field names and types vary by dataset.",
"additionalProperties": {}
},
"description": "Records from offset onward, bounded by limit and by 100,000 characters of records. Field names depend on the queried dataset."
},
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"unknown_dataset",
"dataset_not_served",
"catalog_unavailable",
"invalid_filter",
"invalid_select",
"invalid_orderby",
"invalid_odata_syntax",
"request_too_long",
"filter_too_complex"
],
"description": "Machine-readable failure mode. Declared by this tool: `unknown_dataset`: The dataset name is not in the OpenFEMA dataset catalog. `dataset_not_served`: The OpenFEMA dataset catalog lists the dataset, but OpenFEMA answers its API endpoint with a 404 page, or the version is past its deprecation date and returns no records. `catalog_unavailable`: The OpenFEMA dataset catalog could not be read, so the dataset API version is unknown. `invalid_filter`: OpenFEMA rejected the filter expression — an unknown field, a value of the wrong type or range, or unparseable syntax. `invalid_select`: OpenFEMA rejected the select list — an unknown field or unparseable syntax. `invalid_orderby`: OpenFEMA rejected the orderby expression — an unknown field or unparseable syntax. `invalid_odata_syntax`: OpenFEMA could not parse the request and did not say whether filter, select, or orderby was at fault. `request_too_long`: The request URL, mostly the filter after percent-encoding, is longer than the roughly 8,900 characters OpenFEMA accepts. `filter_too_complex`: The filter nests more than 100 levels deep, the most OpenFEMA allows; every and/or adds a level. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"notice": {
"type": "string",
"description": "Paging guidance — the offset to continue from and whether limit or the response budget ended the page, filter advice when nothing matches, or the total and last valid offset when offset is at or past the end — plus OpenFEMA’s notice when the dataset version is deprecated."
},
"dataset": {
"type": "string",
"description": "The dataset that was queried."
},
"totalCount": {
"type": "number",
"description": "Total matching records before pagination — exceeds returned_count when results were capped at the limit."
},
"next_offset": {
"type": "number",
"description": "Offset of the next page: offset plus returned_count. Present only while matching records remain past this page; pass it as offset to continue."
},
"total_count": {
"type": "number",
"description": "Total matching records before pagination."
},
"returned_count": {
"type": "number",
"description": "Number of records in this response."
}
},
"additionalProperties": false
}{
"readOnlyHint": true,
"openWorldHint": true
}Prompts
0 advertised
Resources
1 advertised
fema-guide
Gates, identifier sources, and OData syntax for the fema_* tools over OpenFEMA. Use when answering a US federal disaster question (declarations, Public Assistance or housing aid totals, NFIP flood-insurance claims), naming a storm or county across FEMA datasets, or writing a fema_query_dataset filter for a dataset the other tools don't cover.
skill://fema-guide/SKILL.mdFEMA Disaster Declaration — FEMA Disaster Declaration
Summary for a specific FEMA disaster declaration — title, state, incident type, programs observed (IA/PA/HM), incident period, and fetched designated area count (up to 500 rows), with disclosure when coverage is partial. Read-once context injection for agents already holding a disaster number.
fema://disaster/{disasterNumber}io.modelcontextprotocol/skills
skill://{+path}90-day uptime
A lightweight connection check about every six hours, sampled on hosts with many listings. It shows the server answered, not that its tools work.
- 2026-11-07: 1 of 1 checks answered
30-day availability
Aggregated from read-only remote checks; this is not a tool-execution or security guarantee.
- Successful checks
- 1 of 1 (100%)
- Average latency
- 601 ms
- 95th percentile latency
- 601 ms
Maintenance history
Public check and source-refresh events. Editorial and ownership records remain private.
- · probe · passed · 601 ms · metadata changed · 8 tool(s), 1 resource(s), 2 resource template(s), 0 prompt(s)
Connect from your client
Each snippet registers the same endpoint; pick the client you use.
Claude Code
claude mcp add --transport http fema-mcp-server-caseyjhand https://fema.caseyjhand.com/mcpCodex
codex mcp add fema-mcp-server-caseyjhand --url https://fema.caseyjhand.com/mcpCursor
{
"mcpServers": {
"fema-mcp-server-caseyjhand": {
"url": "https://fema.caseyjhand.com/mcp"
}
}
}Cline
{
"mcpServers": {
"fema-mcp-server-caseyjhand": {
"type": "streamableHttp",
"url": "https://fema.caseyjhand.com/mcp",
"disabled": false
}
}
}VS Code
{
"servers": {
"fema-mcp-server-caseyjhand": {
"type": "http",
"url": "https://fema.caseyjhand.com/mcp"
}
}
}Gemini CLI
gemini mcp add --transport http fema-mcp-server-caseyjhand https://fema.caseyjhand.com/mcpClaude Desktop
https://fema.caseyjhand.com/mcpWindsurf
{
"mcpServers": {
"fema-mcp-server-caseyjhand": {
"serverUrl": "https://fema.caseyjhand.com/mcp"
}
}
}Let your agent find its tools
Find servers like fema-mcp-server from your agent. Add Koskamo once to search the index for endpoints, access requirements, and client setup. Public discovery needs no key.