Skip to main content
Glama
pgang002

nfip-mcp-server

by pgang002

nfip-mcp-server

CI License: MIT Python 3.10+

An MCP (Model Context Protocol) server exposing real US flood insurance claims data — sourced live from FEMA's public OpenFEMA API — as tools an AI agent can call: claim lookup, filtered search, aggregate stats, and flood event summaries.

Why this exists

Built as a hands-on project to learn MCP server design against a real, non-trivial dataset rather than mock data — the same pattern used to expose proprietary data (claims, policies, internal knowledge bases) to enterprise AI agents.

  • Real data — pulled directly from FEMA's OpenFEMA API, no key required, redacted for privacy, no PII.

  • Tested — unit tests against the query layer, integration tests against the real server subprocess, CI running both on every push.

  • Deployable — pip-installable package, Dockerfile included.

Related MCP server: fema-nfhl-mcp

Available Tools

Tool

Description

get_claim

Look up a single flood claim by its unique ID

search_claims

Filtered search by state, flood event, minimum building payment, and/or year

claims_summary

Aggregate stats (count, total/average payments, year range), optionally by state

list_flood_events

Distinct named flood events in the data with claim counts

Architecture

nfip-mcp-server/
├── src/nfip_mcp/
│   ├── db.py          ← pure query functions (no MCP dependency, easy to unit test)
│   └── server.py       ← FastMCP tool wrappers around db.py, entry point
├── data/
│   ├── nfip_raw.json   ← real claims data pulled from the OpenFEMA API
│   └── claims.db        ← SQLite database built from the raw data
├── scripts/
│   ├── build_db.py      ← loads nfip_raw.json into claims.db
│   ├── fetch_more_data.py ← pulls a larger, filtered dataset from the live API
│   └── demo_client.py    ← manual walkthrough of every tool
├── tests/
│   ├── test_db.py        ← unit tests against the query layer
│   └── test_integration.py ← spins up the real server and calls it over stdio
└── .github/workflows/ci.yml

The query logic in db.py is deliberately free of any MCP-specific code — it's plain functions taking a sqlite3.Connection and returning dicts, so it's testable without spinning up a server or client. server.py just wires thin @mcp.tool() wrappers around it.

Setup

git clone https://github.com/YOUR-USERNAME/nfip-mcp-server
cd nfip-mcp-server
pip install -e ".[dev]"
python scripts/build_db.py

Running the tests

pytest tests/                # everything
pytest tests/test_db.py      # fast unit tests only
pytest -m integration        # slower, spins up the real server subprocess

Running the server

nfip-mcp-server               # after pip install, runs over stdio
# or
python -m nfip_mcp.server

To see it working interactively without a full MCP host installed:

python scripts/demo_client.py

Connecting to Claude Desktop (or another MCP host)

Add to your MCP host's config (e.g. claude_desktop_config.json):

{
  "mcpServers": {
    "nfip-claims": {
      "command": "nfip-mcp-server"
    }
  }
}

Running with Docker

docker build -t nfip-mcp-server .
docker run -i nfip-mcp-server

Point your MCP host's command at docker run -i nfip-mcp-server to use the containerized version instead.

Scaling up the data

The bundled data/claims.db has a small (~24 record) real sample, enough to prove the pipeline end to end. To pull a much larger, properly filtered slice of the real data (this defaults to flood-prone Northeast states):

python scripts/fetch_more_data.py
python scripts/build_db.py

License

MIT — see LICENSE.

Available Tools

4 tools
claims_summaryA

Get aggregate statistics across claims, optionally filtered to one state.

Args: state: Two-letter US state code to filter by. Omit for all states.

Returns: Claim count, total and average building payments, and the earliest/latest loss years in scope.

ParametersJSON Schema
NameRequiredDescriptionDefault
stateNo

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full disclosure burden, and it delivers: the Returns section specifies exactly what the agent will receive — claim count, total and average building payments, and loss year range — and the read-only aggregation nature is unmistakable. It stops short of describing edge cases such as empty scopes or invalid state codes, but the core observable behavior is well disclosed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description opens with a one-sentence summary and then uses compact Args/Returns sections, with every line earning its place. The Returns section is justified because there is no output schema to carry that information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter aggregation tool with no output schema and no annotations, the description covers purpose, parameter semantics, and return content — the essentials an agent needs to select and invoke it correctly. The only minor gaps are unspecified edge-case behavior (empty results, invalid state input) and exact field names in the return payload.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, yet the description fully compensates by documenting the state parameter's format (two-letter US state code), filtering semantics, and the default behavior when omitted (all states). For the tool's sole parameter, this is complete semantic coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening line, 'Get aggregate statistics across claims, optionally filtered to one state,' names a specific verb, resource, and scope. The phrase 'aggregate statistics' clearly distinguishes this tool from siblings like get_claim and search_claims, which operate on individual claim records.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage — an agent can infer it should pick this tool when summary numbers across claims are needed rather than individual records. However, it never explicitly names an alternative or states when not to use it, leaving routing to inference from the word 'aggregate.'

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_claimA

Look up a single NFIP flood claim by its unique claim ID.

Args: claim_id: The claim's unique identifier (UUID string).

Returns: The full claim record, or an error message if not found.

ParametersJSON Schema
NameRequiredDescriptionDefault
claim_idYes

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the behavioral burden. It signals a read-only operation via 'Look up' and discloses both success behavior ('Returns the full claim record') and failure behavior ('error message if not found'). This is adequate for a simple get operation, though it does not discuss auth or other edge cases.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and well-structured: a one-sentence purpose, then Args, then Returns. Every line adds necessary information, and there is no filler or repetition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter lookup with no annotations and no output schema, the description is sufficiently complete. It covers what the tool does, what the parameter means, what is returned, and what happens if the claim is not found.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% and the schema only describes claim_id as a string. The description fully compensates by explaining that claim_id is the claim's unique identifier and a UUID string, adding meaningful format and semantic detail beyond the raw schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses the specific verb 'Look up' and identifies the resource as a 'single NFIP flood claim' identified by its unique claim ID. This clearly distinguishes it from sibling tools like search_claims, which implies searching rather than exact-ID lookup.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description clearly establishes the intended use case: call this when you have a specific claim ID and want the full record for that single claim. It does not explicitly mention alternatives like using search_claims when the ID is unknown, so it stops short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_flood_eventsA

List the distinct named flood events present in the claims data (e.g. 'Hurricane Katrina', 'Tropical Storm Allison'), with claim counts.

Returns: A dict mapping each flood event name to its claim count.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.1/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It usefully discloses that results are grouped by distinct event names and returns a dict mapping names to claim counts. It does not mention ordering, potential size of the result, or that the operation is read-only, though 'List' implies it.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact, front-loaded with the core purpose, and the Returns line adds useful output structure without redundancy. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter listing tool with no output schema, the description is sufficient: it states what the tool lists, gives concrete examples, and specifies the exact return shape. An agent can call it correctly without additional information.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so there is no parameter burden for the description to carry. The baseline of 4 applies because no additional parameter explanation is needed.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('List') and a precise resource ('distinct named flood events') while adding that claim counts are included. It clearly distinguishes this aggregation tool from the sibling tools that fetch individual claims or summaries.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The intended use is implied: call this when you need the distinct named flood events and their claim counts. However, there is no explicit guidance about when to prefer this over search_claims or claims_summary, nor any exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_claimsA

Search NFIP flood claims with optional filters. All filters are combined with AND; omit any filter you don't need.

Args: state: Two-letter US state code, e.g. 'FL', 'NJ'. flood_event: Substring match against the named flood event, e.g. 'Katrina' matches 'Hurricane Katrina'. min_building_payment: Only return claims where the net building payment was at least this amount. year: Year the loss occurred. limit: Max number of results to return (default 10).

Returns: A dict with the matching claims and how many were found.

ParametersJSON Schema
NameRequiredDescriptionDefault
yearNo
limitNo
stateNo
flood_eventNo
min_building_paymentNo

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the behavioral burden and does it well: it explains AND-combination, substring matching for flood_event, threshold filtering for min_building_payment, the default limit, and that a dict with matching claims and a count is returned. Minor gaps remain around pagination, sorting, and exact response keys.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with a clear opening sentence, an Args block, and a Returns line. It is concise and informative, though the default-limit value is repeated from the schema and the wording 'optional filters' appears twice in slightly different forms.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a search tool with five optional parameters and no output schema, the description provides enough context to call it correctly: all parameters are documented, filter behavior is clear, and the return type is summarized. The exact structure of the returned dict is not specified, but this is a minor gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, and the description fully compensates by explaining every parameter: state format, substring matching for flood_event, min_building_payment threshold, year meaning, and limit default. It also adds relational semantics by stating filters are combined with AND.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool searches NFIP flood claims with optional filters, using a specific verb and resource. It does not explicitly differentiate from sibling tools like get_claim or claims_summary, but the meaning is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives useful filter-combination guidance (AND logic, omit unneeded filters) and makes clear the tool is for searching claims. However, it never states when to prefer search_claims over siblings such as get_claim or claims_summary, leaving tool selection mostly implied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 4 tool updatesv0.1.0
    • First observedclaims_summary
    • First observedget_claim
    • First observedlist_flood_events
    • First observedsearch_claims

TDQS

A4.3/5.0

Scored across 4 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: listing flood events, retrieving a single claim, searching claims by filters, and producing aggregate statistics. There is minimal overlap in functionality, and descriptions make the boundaries obvious.

Naming Consistency4/5

Three tools follow a consistent verb_noun pattern: list_flood_events, get_claim, and search_claims. claims_summary breaks the pattern by using a noun phrase, though it is still clear and readable.

Tool Count5/5

Four tools is a well-scoped size for a read-only NFIP claims data server. Each tool covers a distinct query need without redundancy or bloat.

Completeness4/5

The surface covers the core read-only workflows: enumeration, single-record lookup, filtered search, and aggregate statistics. Minor gaps exist, such as summary filtering by flood event or year, but these do not create dead ends for common use cases.

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    An MCP server that gives AI assistants direct access to the FEMA National Flood Hazard Layer (NFHL) for flood zone lookups, FIRM panel information, and flood map data in the United States.
    6
    5 npm
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    An MCP server that gives AI agents grounded access to US natural-hazard data — weather alerts, forecasts, earthquakes, and FEMA flood zones — from free, keyless US government APIs.
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Remote MCP server exposing US Census (ACS 5-year) and FEMA flood data. Works as a connector in both Claude and ChatGPT.
    -