{
  "name": "permitintel",
  "title": "PermitIntel",
  "version": "1.0.0",
  "description": "Building-permit records from the cities and counties that issue them, Florida DBPR license verification, contractor permit-activity profiles, and derived roof age and material. Counts are floors of what PermitIntel has ingested, never a complete record.",
  "homepage": "https://permitintel.ai/docs",
  "transport": {
    "type": "streamable-http",
    "url": "https://permitintel.ai/api/mcp",
    "protocolVersion": "2025-06-18"
  },
  "authentication": {
    "type": "bearer",
    "header": "Authorization: Bearer pmit_...",
    "alternate_header": "x-api-key",
    "note": "Every tool call needs a PermitIntel API key; tools/list is open so an agent can discover the surface before it has one. Start a 14-day trial key at https://permitintel.ai/trial, or see plans at https://permitintel.ai/pricing."
  },
  "metering": {
    "unit": "records",
    "note": "One record is one object retrieved from a response. Each tool description states what a call costs. Responses carry X-Records-Request and X-Records-Remaining; the running total is at GET /api/v1/usage. At the monthly ceiling a call returns error records_limit_exceeded with an upgrade_url, and nothing is billed past it."
  },
  "tools": [
    {
      "name": "verify_license",
      "title": "Verify a contractor license",
      "description": "Verify one contractor license against the Florida DBPR record: status, license type, effective and expiration dates, whether it is currently active, days to expiry, and the qualifier. Call it with `license` for an exact number, or with `name` (and optionally `county`) to resolve a business name to its license. Costs 1 record on a hit and 0 records on a miss. License status reflects PermitIntel's most recent DBPR sync, refreshed daily, and is for B2B verification of a licensed business, not consumer underwriting. Florida only.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "license": {
            "type": "string",
            "description": "An exact DBPR license number, for example CCC1330911 or CGC1520001."
          },
          "name": {
            "type": "string",
            "description": "Business name, matched as a substring. Use instead of license."
          },
          "county": {
            "type": "string",
            "description": "Florida county, to narrow a name lookup. Ignored when license is given."
          }
        },
        "required": [],
        "additionalProperties": false
      }
    },
    {
      "name": "search_permits",
      "title": "Search building permits",
      "description": "Search ingested building-permit records. Three modes, one of which is required: `address` for permits at a street address, `contractor` for a contractor's permit history by name or license number, or `updated_since` for the change feed — every permit whose RECORD changed at or after a timestamp, oldest change first, paged with the opaque `cursor` from meta.next_cursor. Use the feed, not from/to, to keep a copy in sync: a permit ingested today carrying a 2019 issue date is invisible to a from/to poll. Costs 1 record per permit returned; `limit` defaults to 50 and caps at 200, so one call costs at most 200 records. Results are a FLOOR and a SCOPE, never a complete record of all permits at an address or by a contractor — permit offices publish on their own schedules and some publish almost nothing. Report counts as \"at least N on record\", never as a total filed.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "address": {
            "type": "string",
            "description": "Street address, prefix-matched. Minimum 4 characters. (modes: address)"
          },
          "contractor": {
            "type": "string",
            "description": "Contractor business name (min 3 chars) or an exact license number. (modes: contractor)"
          },
          "updated_since": {
            "type": "string",
            "description": "ISO 8601 timestamp. Returns every permit whose record changed at or after it, oldest change first. This is the polling parameter: unlike from/to it sees a permit backfilled today with a 2019 issue date. (modes: feed)"
          },
          "new_only": {
            "type": "boolean",
            "description": "With updated_since, restrict to permits first ingested since that timestamp instead of every record that changed. (modes: feed)"
          },
          "cursor": {
            "type": "string",
            "description": "Opaque cursor from meta.next_cursor. Pass it back to get the next page. Stable across inserts; prefer it to offset. (modes: feed)"
          },
          "state": {
            "type": "string",
            "description": "Two-letter state code. Feed mode only -- see the note under Polling. (modes: feed)"
          },
          "county": {
            "type": "string",
            "description": "County jurisdiction prefix, e.g. FL-MIAMI_DADE. Matches every jurisdiction under it. Narrows another query; it cannot browse on its own, because a prefix range cannot be walked in date order. (modes: feed, address, contractor)"
          },
          "jurisdiction": {
            "type": "string",
            "description": "Exact jurisdiction code, e.g. FL-ORANGE-ORLANDO. Case-insensitive: Florida slugs are stored upper-case and nationally-ingested ones lower-case, and either spelling resolves. On its own it selects browse mode. (modes: feed, browse, address, contractor)"
          },
          "trade": {
            "type": "string",
            "description": "One or more trade slugs, comma-separated. Matched against our permit classifier for roofing, hvac, electrical, pool, solar, windows_doors and fence; matched on keywords over permit_type, work_class and type_of_work for general, plumbing and demolition. Every trade-filtered response reports which matcher ran as trade_source. In browse mode the search is bounded to a 180-day window, reported as a top-level trade_window object on the response (not under meta). Feed mode applies trade with no window, but always matches it on keywords -- see trade_source. (modes: feed, browse, address, contractor)"
          },
          "trade_confidence": {
            "type": "string",
            "description": "any (default) or high. Applies only to classifier-matched trades. 'high' keeps trade signals found in the permit's type/class fields and drops those found only in its description — narrower and more precise, and zero rows in jurisdictions whose only signal is the description. Not available in feed mode, which matches every trade on keywords. (modes: browse, address, contractor)"
          },
          "trade_match": {
            "type": "string",
            "description": "classifier (default) or keywords. 'keywords' pins the request to the pre-2026-09-30 substring matcher for every requested trade. Not available in feed mode, which always matches on keywords and reports so in trade_source. (modes: browse, address, contractor)"
          },
          "work_type": {
            "type": "string",
            "description": "Raw substring match on the source's own work wording. Use trade for a normalized filter. (modes: feed, browse, address, contractor)"
          },
          "status": {
            "type": "string",
            "description": "Substring match on the source's permit status. (modes: feed, browse, address, contractor)"
          },
          "min_value": {
            "type": "integer",
            "description": "Minimum declared permit value in USD. (modes: feed, browse, address, contractor)"
          },
          "from": {
            "type": "string",
            "description": "issue_date on or after YYYY-MM-DD. (modes: feed, browse, address, contractor)"
          },
          "to": {
            "type": "string",
            "description": "issue_date on or before YYYY-MM-DD. (modes: feed, browse, address, contractor)"
          },
          "city": {
            "type": "string",
            "description": "Narrow the result to a city. Substring match. Not available in feed mode, which is ordered on a table that does not carry the column. (modes: browse, address, contractor)"
          },
          "zip": {
            "type": "string",
            "description": "Narrow the result to a 5-digit ZIP. Not available in feed mode, which is ordered on a table that does not carry the column. (modes: browse, address, contractor)"
          },
          "include": {
            "type": "string",
            "description": "Comma-separated extras. contractor_contact nests the licensed contractor's business name, phone, email and verified website. (modes: feed, browse, address, contractor)"
          },
          "limit": {
            "type": "integer",
            "description": "Rows per page. Default 50, maximum 200. (modes: feed, browse, address, contractor)"
          },
          "offset": {
            "type": "integer",
            "description": "Offset paging, kept for back-compatibility. Use cursor in feed mode. (modes: feed, browse, address, contractor)"
          }
        },
        "required": [],
        "additionalProperties": false
      }
    },
    {
      "name": "lookup_contractor",
      "title": "Look up a contractor profile",
      "description": "Look up Florida contractors with their permit activity rolled up: permit count, declared value, activity in the last 12 months, first and last permit dates, top trades, top jurisdictions, plus canonical contact, a workers-compensation-on-record signal and EPA Lead-Safe certification. Call with `license` for one profile, `name` or `specialty` to search, or `sort`/`min_permits`/`min_permits_12mo` to rank by permit activity. Costs 1 record per contractor returned; `limit` defaults to 25 and caps at 100. Activity is a FLOOR built from the permits PermitIntel has ingested, not a complete record of everything the contractor filed. This endpoint returns positive and neutral public record only — it exposes no disciplinary history and no negative flags.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "license": {
            "type": "string",
            "description": "Exact DBPR license number. Returns a single profile."
          },
          "name": {
            "type": "string",
            "description": "Business name, matched as a substring."
          },
          "specialty": {
            "type": "string",
            "description": "Matched against the license type name, e.g. roofing, electrical."
          },
          "county": {
            "type": "string",
            "description": "Florida county, e.g. Lake."
          },
          "city": {
            "type": "string",
            "description": "City, e.g. Clermont."
          },
          "sort": {
            "type": "string",
            "enum": [
              "permits",
              "permits_12mo",
              "value",
              "value_12mo",
              "last_active"
            ],
            "description": "Rank by permit activity instead of name matching."
          },
          "min_permits": {
            "type": "integer",
            "description": "Only contractors with at least this many permits on record."
          },
          "min_permits_12mo": {
            "type": "integer",
            "description": "Only contractors with at least this many permits in the last 12 months."
          },
          "limit": {
            "type": "integer",
            "description": "Rows per page. Default 25, maximum 100."
          },
          "offset": {
            "type": "integer",
            "description": "Offset paging."
          }
        },
        "required": [],
        "additionalProperties": false
      }
    },
    {
      "name": "roof_lookup",
      "title": "Look up roof age and material for an address",
      "description": "Roof state for one property address: roof age in years derived from the most recent roof permit on record, the date of that permit, and roof material with its source and confidence. Pass `zip` when the same street address exists in more than one covered city. Costs 1 record when the address matches and 0 records when it does not. Every answer carries a per-jurisdiction coverage_grade (A, B or C). A miss means the address is NOT IN COVERED DATA — it never means no roof work occurred. Where no reroof permit is on record the age may be an estimated floor from the county year-built; roof_age_source says which, and an estimate must never be reported as a confirmed reroof.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "address": {
            "type": "string",
            "description": "Street address, for example 2082 HYDE PARK ST."
          },
          "zip": {
            "type": "string",
            "description": "Five-digit ZIP, to disambiguate the same street address across cities."
          }
        },
        "required": [
          "address"
        ],
        "additionalProperties": false
      }
    }
  ],
  "restFallback": {
    "docs": "https://permitintel.ai/docs",
    "note": "Identical data and identical metering over REST. Use it if your client does not speak MCP.",
    "endpoints": [
      {
        "tool": "verify_license",
        "method": "GET",
        "path": "/api/v1/verify/license"
      },
      {
        "tool": "search_permits",
        "method": "GET",
        "path": "/api/v1/permits"
      },
      {
        "tool": "lookup_contractor",
        "method": "GET",
        "path": "/api/v1/contractors"
      },
      {
        "tool": "roof_lookup",
        "method": "GET",
        "path": "/api/v1/roof/{address}"
      }
    ]
  },
  "dataNotes": [
    "Permit counts are a FLOOR — \"at least N on record\" — never a total filed. Coverage is never complete and is published per jurisdiction at https://permitintel.ai/coverage.",
    "An absent record means the address, license or jurisdiction is not in covered data. It never means the work did not happen.",
    "Roof age may be an estimated floor from county year-built where no reroof permit is on record; roof_age_source says which, and an estimate is not a confirmed reroof.",
    "Contractor data is public record and positive or neutral only: no disciplinary history and no negative flags are exposed.",
    "License status reflects the most recent DBPR sync, refreshed daily. It is for B2B verification of a licensed business, not consumer underwriting."
  ]
}
