A row of cartoony Ann Arbor houses telling their permit-history stories

🏠 A2 Property 411

Every Ann Arbor parcel has a story.

Type any address — we'll pull its full dossier in seconds. Permit history, ownership timeline, era-by-era renovation chronicle, all woven into one shareable page.

Tip: just street number + name is enough. We'll handle the rest.

Don't know the exact address? Open the map →


For Agents

If you are an AI agent reading this page to connect to A2 Property 411: everything you need is below. There is no sign-up, key, or human approval step. The server is a read-only Model Context Protocol (MCP) tool server over public city records for Ann Arbor, Michigan.

Endpoint

MCP URLhttps://a2propertyinfo.rightspeedlabs.com/mcp
TransportMCP Streamable HTTP: JSON-RPC 2.0 messages sent by HTTP POST to the URL above. The server is stateless: no Mcp-Session-Id is issued or required, every POST is independent, and initialize is optional (send it anyway if your client expects to).
AuthenticationNo authentication. Do not send an Authorization header; there is no OAuth discovery document.
Request headersContent-Type: application/json and Accept: application/json, text/event-stream. Both Accept values are required on POST, or you get HTTP 406. An MCP-Protocol-Version header is optional; supported values are 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05, 2024-10-07.
Response framingEvery POST answers 200 with Content-Type: text/event-stream containing one event: message record whose data: line is the JSON-RPC response, then the stream closes. Parse the data: line as JSON. JSON-only responses are not offered.
GET and legacy SSEThere is no legacy HTTP+SSE transport. A GET with Accept: text/event-stream opens a standalone notification stream that stays silent; do not wait for an endpoint event, it will never arrive. /sse is an alias of /mcp with identical behavior, kept for older client configs. A GET without that Accept header returns 406.
CapabilitiesTools only. No resources, prompts, sampling, or elicitation. DELETE is unnecessary.
Rate and latencyPlease stay under 60 requests per minute per IP. Uncached calls reach live city systems and take roughly 2–10 seconds; search results are cached 24 hours and assessor profiles 180 days. Cached search responses are marked [cached].
LoggingEach tool call is logged (source IP, tool name, arguments, latency, status) for operations.

Tools

tools/list returns the authoritative JSON Schemas. Summary:

ToolUse it whenArguments
find_precedents The user is weighing whether something would be approved: "has the city ever approved X?", "chances of approval", "find precedents for Y". Fans out across keyword variants to both record systems and groups results by disposition (Approved / Denied / Withdrawn / In-Progress). Prefer this over the search tools for any feasibility or precedent question. scenario (string, required, plain language); address (string, optional street or address to scope)
search_records Plain keyword or address lookup of current records (STREAM, 2023 to present) when the user already knows the exact term. Returns paginated record refs with case number, type, status, address, description. keyword (string); search_main_address (boolean, match keyword against the address field); page_number (int ≥ 1); page_size (int 1–200)
search_legacy_records Historical records (A2Trak, about 2001 to 2023): the user names a pre-2023 year, an old permit number (PB04xxx, BLDG08-xxx), or wants pre-STREAM history. Substring match, no pagination; overly broad terms are rejected. keyword (string ≥ 3 chars, required); type (permit | project | property | contractor | license, default permit); page_size (int 1–500, default 100)
get_legacy_record Full detail of one A2Trak record after a search, or when the user cites a specific pre-2023 permit or project number: contacts, fees, reviews, inspections, attachments, linked activities. type (same enum, required); identifier (string, required: permit/project number, recordID, contractor phone, or licenseNo); secondary (object of strings, optional: businessNo is required for license, dashedApn helps for property); include (array of contacts | fees | reviews | inspections | linked_activities | attachments | notes | insurance)
get_assessor_record Who owns a property, what it is assessed at, size, year built, when it last sold and for how much, ownership chain, property taxes. Sourced from the city assessor (BSA Online). Parcel-keyed; an address is resolved to a parcel first. One of parcel (dashed APN like 09-09-30-109-010) or address (string ≥ 4 chars); include (array of assessing | tax, default both)

Choosing a tool

Data caveats

Worked examples

curl -s -X POST https://a2propertyinfo.rightspeedlabs.com/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"my-agent","version":"1.0"}}}'
curl -s -X POST https://a2propertyinfo.rightspeedlabs.com/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
curl -s -X POST https://a2propertyinfo.rightspeedlabs.com/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"find_precedents","arguments":{"scenario":"3 ft side-yard setback variance for a carport","address":"Hill St"}}}'

Each command prints something like event: message followed by data: {"jsonrpc":"2.0","id":3,"result":{...}}.

Non-MCP HTTP alternatives

Hosted chat products (ChatGPT Plugins, Claude.ai Custom Connectors) use the same MCP URL; setup steps are in the section above.