Kukun MCP Quickstart
The Kukun MCP server lets AI assistants such as Claude, Cursor, and GitHub Copilot query Kukun data directly through the Model Context Protocol. Ask questions in plain language and the assistant calls Kukun tools for you: search 800M+ U.S. building permits, find addresses with or without specific permit activity, and pull full property records.
The MCP server serves the same data as the REST API and draws from the same plan quota, so you can use both side by side.
Before You Start
You need two things:
- A plan that includes MCP Access. Every Permits and Property Join plan includes MCP Access. Which tools you can call depends on the products in your plan (see Available Tools).
- An API key. Sign in and copy your key from My API Keys.
Each API key belongs to one subscription, and an MCP connection uses one key. If you have Permits and Property Join as separate subscriptions, add the Kukun server twice in your client (one entry per key), or contact sales about a combined plan.
Server Details
| Setting | Value |
|---|---|
| Server URL | https://api.mykukun.com/mcp |
| Transport | Streamable HTTP |
| Authentication | Authorization: Bearer YOUR_API_KEY |
Replace YOUR_API_KEY with your key in every example below. Treat the key like a password: don't commit it to source
control or share it in screenshots.
Install
Pick your client. After adding the server, restart or reload the client so it picks up the new configuration.
Claude Code
Run this in your terminal:
Code
Run claude mcp list to confirm the server shows as connected. Add --scope user to make it available in all your
projects.
Claude Desktop
Claude Desktop starts MCP servers as local processes, so it connects to remote servers through the
mcp-remote bridge. This requires Node.js 18 or later.
- Open Settings → Developer → Edit Config. This opens
claude_desktop_config.json:- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
- macOS:
- Add the Kukun server:
Code
- Save the file and restart Claude Desktop. The Kukun tools appear under the tools menu in the chat input.
The key goes in env rather than directly in args because some systems split arguments that contain spaces.
Cursor
Open Settings → MCP → Add new global MCP server, or edit ~/.cursor/mcp.json directly (use .cursor/mcp.json in
a project to scope it to that project):
Code
VS Code (GitHub Copilot)
Create .vscode/mcp.json in your workspace. This setup asks for your key the first time the server starts and stores
it securely, so the key never ends up in the file:
Code
Click Start above the server entry, then open Copilot Chat in Agent mode to use the Kukun tools.
Check the connection
Ask your assistant:
Code
ping checks that the server, transport, and your key work end to end. It's free and never counts against your quota.
Available Tools
| Tool | Requires | What it does | Usage counted |
|---|---|---|---|
ping | Any plan with MCP Access | Connectivity check | Never |
permits_search | Permits plan | Search permits with filters, sorting, and pagination | 1 per permit returned |
permits_area_search | Permits plan | Find addresses that have, or are missing, a permit type within a date window | 1 per address returned |
permits_property_search | Permits plan | All permits on file for one property | 1 per permit returned |
property_join | Property Join plan | Full profile for one property in one call: record, value, condition, outlook, component lifecycle, and permits | 1 per successful lookup |
Your client may also list property_record, property_avm, property_condition_score_pico,
property_investment_outlook_kio, property_community_facts, and property_component_lifecycle. These aren't
available over MCP. Use property_join instead, which returns that data as sections of one response.
Keyword search
permits_search, permits_area_search, and permits_property_search accept an optional keyword that matches
words in the permit description:
- It isn't case-sensitive, and the words must appear together and in the same order.
water heatermatches "Replace water heater" but not "heater for water". - Only whole words match:
heatdoesn't match "heater". Hyphens split words, sore-roofmatches "re roof". - 100 characters at most, with at least one letter or digit.
- Permits without a description never match.
keyword narrows results. It doesn't replace label: use label for the permit type and keyword for specific
wording, for example label: "plumbing" with keyword: "water heater".
permits_search
Mirrors GET /v1/permits/search. All parameters are optional, but send at least one filter. A call with no
parameters is rejected.
| Parameter | Description |
|---|---|
address, city, county, state, zip-code | Location filters. state accepts FL or Florida; county uses the full name, for example Hillsborough |
jurisdiction | Issuing authority, for example Los Angeles |
status | applied, issued, in-process, denied, closed, review, expired, inspection, violation |
label | Permit type, comma-separated. No prefix matches any of them; prefix with + (or !) to require a type or - to exclude it, for example kitchen-remodel,-bathroom-remodel |
keyword | Words in the permit description, for example water heater (see Keyword search) |
date-from, date-to | Status date range, ISO 8601 (2024-01-01). Send both |
min-cost, max-cost, has-cost | Permit cost filters |
latitude, longitude, distance | Radius search. distance is in miles and defaults to 5 |
sort-by, sort-order | Sort by city, address, jurisdiction, county, state, or date; asc or desc |
limit | Page size, 1 to 1000. Default 10 |
next-page-id | Cursor from the previous page. Keep the other parameters the same |
details | 0 returns only the total count (not counted against your quota); 1 returns full records |
permits_area_search
Mirrors GET /v1/permits/area-search. Returns deduplicated addresses. Send at least one area filter: city,
county, state, zip-code, or latitude and longitude.
| Parameter | Description |
|---|---|
label (required) | One permit type to check, for example roof. No prefixes |
label-condition (required) | has for addresses with a matching permit, missing for addresses without one |
date-from, date-to (required) | Date window, ISO 8601 |
keyword | Words in the permit description. The same permit must also match label and the date window (see Keyword search) |
city, county, state, zip-code | Area filters. state accepts FL or Florida |
latitude, longitude, distance | Radius search. distance is in miles and defaults to 5 |
limit, next-page-id, details | Same as permits_search. details defaults to 1 |
With label-condition: "missing", an address is returned when none of its permits matches the label, the date window,
and the keyword (if you send one).
permits_property_search
Mirrors GET /v1/permits/property-search. Returns the permit history for a single address. For geographic queries
and filters, use permits_search or permits_area_search.
| Parameter | Description |
|---|---|
kukun-id | Kukun property identifier. Takes precedence over the address parameters |
address, city, state, zip-code | Full address. All four are required when you don't send kukun-id; address must start with a street number |
keyword | Only return the property's permits whose description matches (see Keyword search) |
limit | Maximum permits to return, 1 to 100. Default 10. There's no pagination cursor |
The response includes the resolved address and a permits block:
total- all permits on file for the property (only the matching ones when you sendkeyword)returned- permits included in this response (this is what counts against your quota)truncated-truewhentotalis larger thanlimititems- the permits, newest first
property_join
Mirrors GET /v1/property/join. Resolves the property once and returns everything Kukun has on it, one section per
data set.
| Parameter | Description |
|---|---|
kukun-id | Kukun property identifier. Takes precedence over the address parameters |
address, city, state, zip-code | Full address. state accepts FL or Florida; zip-code accepts 5 digits or ZIP+4 |
include | Comma-separated sections to return. Default: all six (see below) |
permit-limit | Maximum permits in the permits section, 1 to 100. Default 10. Ignored when include leaves out permits |
property_join doesn't accept keyword or other permit filters. The permits section always holds the property's
newest permits, up to permit-limit. To filter a property's permits by description, call permits_property_search
with keyword.
Sections:
| Section | Contents |
|---|---|
property_record | Location and parcel data, structure characteristics, tax, assessment, and legal records |
avm | Automated valuation estimate with low/high range |
condition_score_pico | Property and neighborhood condition scores |
investment_outlook_kio | 5-year value forecast |
component_lifecycle | Age and remaining life of major home components |
permits | Permit history for the property |
kukun_id and address appear once at the top level. The property record is required: if it can't be resolved, the
call fails. If any other section is unavailable, the call still succeeds, that section comes back null, and it's
listed in unavailable_sections (see Response Format).
Each lookup counts as 1, whichever sections you request and even when some are unavailable. Use include to keep
responses small, for example include: "avm,condition_score_pico".
Permit labels
basement-remodel, bathroom-remodel, comm-conversion, deck, demolition, drywall, electrical, fence,
floor-task, foundation, general-construction, general-remodel, hvac, kitchen-remodel, landscape,
new-addition, new-construct, new-construct-adu, plumbing, pool, roof, sidewalk, siding, solar,
whole-house-remodel, window
Example prompts
You don't need to name tools or parameters. Describe what you want and the assistant picks the tool:
- "How many roof permits were issued in Tampa, Florida in 2025?"
- "List the 20 most recent kitchen remodel permits in ZIP 94025, newest first."
- "Find solar permits within 10 miles of 28.0614, -82.1506 filed since January 2024."
- "Find permits that mention 'water heater' in Tampa, Florida since January 2024."
- "Which addresses in Hillsborough County had no roof permit between 2015 and 2025?"
- "Show me every permit on file for 4521 Oak Ave, Tampa, FL 33611."
- "Does 4521 Oak Ave, Tampa, FL 33611 have any permits that mention 're-roof'?"
- "Get the full property profile, including value, condition score, and permits, for 123 Main St, Kansas City, MO 64113."
Tip: ask for a count first ("how many…"). Count-only searches don't use your quota, and they help you size a full pull.
Usage and Billing
- Shared quota. MCP and REST draw from the same monthly quota for each product. A permit returned through
permits_searchcounts the same as one returned byGET /v1/permits/search. - Only successful calls count. Failed calls,
ping, count-only searches (details: 0), and protocol messages such as listing tools are never counted. - Requests can't exceed your remaining quota. If a permits search asks for more records than you have left, it's
rejected. Lower
limitor upgrade your plan. - Quotas reset at the start of each billing cycle.
- Upgrades apply automatically within about a minute. You don't need a new API key or a client restart.
Check your usage and plan on My API Keys.
Response Format
Your AI assistant reads tool results for you, so you only need this section if you build your own integration or want to inspect raw results.
Each tool returns its data as JSON text in a content block. MCP results don't use the REST
{ success, message, errors, metadata } envelope: the text holds the data itself, plus a response_id. The same
identifier, the timestamp, and the API version are also in _meta.
Example result:
Code
Include the response_id when you contact support about a specific result.
Partial results
When some of the data couldn't be retrieved, the result is marked partial and lists what's missing. These fields
appear both in the JSON text and in _meta. property_join also reports which sections were delivered and which ones
you left out with include.
Example property_join fields when the investment outlook was unavailable and permits weren't requested:
Code
| Reason | Meaning |
|---|---|
no_data | Kukun has no data for this property in that section |
upstream_unavailable | A data source was temporarily unavailable; try again later |
timeout | The data source didn't respond in time; try again later |
not_requested | You left the section out with include |
Error results
When a tool call fails, the result has isError: true and the text holds a human-readable message. Field-level
details, when there are any, are in _meta.details:
Code
An invalid keyword returns one of these messages: keyword must not be empty.,
keyword must be 100 characters or fewer., or keyword must contain at least one letter or digit.
Failed tool calls don't count against your quota. For gateway errors such as an invalid key or exhausted quota, see Errors.
Custom Integrations
If you're building your own MCP client, send JSON-RPC 2.0 messages with POST to the server URL. Include these headers
on every request:
Code
Start a session:
Code
If the response includes an Mcp-Session-Id header, send it back on every following request.
List the tools:
Code
Call a tool:
Code
The tool result is returned as JSON text in result.content[0].text (see Response Format). The
response may arrive as a single JSON body or as a text/event-stream; read the last event in the stream.
Limitations:
- Send one JSON-RPC message per request. Batch requests (a JSON array) are rejected.
GETrequests (SSE stream resumption) aren't supported and return405.
Errors
Authentication errors return a regular HTTP status. Most other MCP errors return HTTP 200 with the error inside the
JSON-RPC body, so check for an error field (or result.isError) in every response.
| Code | Meaning | What to do |
|---|---|---|
| HTTP 401 | Missing or invalid API key | Check the Authorization: Bearer header and the key itself |
| HTTP 405 | GET isn't supported | Use POST |
-32001 | Your plan doesn't include MCP Access | Subscribe to a plan with MCP Access in the portal |
-32003 | The tool isn't in your plan (for example, property_join on a Permits-only key) or isn't offered over MCP | Use a key for the right product, or upgrade your plan |
-32004 | Your monthly quota for that product is used up, or the request asks for more records than you have left | Lower limit, wait for the next billing cycle, or upgrade |
-32600 | Batch requests aren't supported | Send one message per request |
-32700 | The request body isn't valid JSON | Fix the request body |
Tool-level errors, such as an invalid parameter or a property that can't be found, come back as a tool result with
isError: true and a message explaining the problem (see Error results). These calls don't count
against your quota.
Support
Email [email protected] for connection, authentication, or quota issues. Standard response time is one business day. For plans and pricing, use [email protected].
For the REST API, see the Kukun API Quickstart.