MCP: the API as tools

Every tool runs its /api/v1 route's handler and returns { meta, data } exactly as the API does: the same population string, n, as-of, release, caption stack and minimum-sample guards, and the same refusals. 34 tools.

Endpoint and credentials

Streamable HTTP, JSON-RPC 2.0, POST only.

POST https://serffsurf.com/api/mcp
Authorization: Bearer <access code, or an API key with the mcp scope>
Content-Type: application/json
  • An access code with the ia_v3 flag lists these tools; an aggregate code lists the aggregate-safe ones.
  • An API key (ss_live_… the live release; ss_test_… fixture data) needs the mcp scope and calls these tools only: Settings > Keys.
  • Every tool description ends with the instruction: report n, population and as-of from meta; no evaluative adjectives; never aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.
  • Insights only: the tools answer with values and analytics derived from public filings and short cited excerpts (at most 280 characters, with the SERFF tracking number), never a filing's documents, page images or a letter's full text.
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "market_distribution",
    "arguments": {
      "state": "TX",
      "line": "wc",
      "pop": "current"
    }
  }
}

The meta contract

Every answer's meta, rendered verbatim by the site and returned verbatim here:

release · as_of · published_at · state · line · regime · value_kind · population · population_string · n · n_wp · weight_coverage · weighting · premium_year_min · premium_year_max · basis · year · partial · caption · notes · source · accuracy · coverage · freshness · guards · depends · tier · suppressed · licensed_to

Refusals (the answer, not a failure)

value_kind_not_applicable
the line has no such value kind (an LCM on ppa): the answer, not an error to retry
not_comparable
a cross-state or cross-regime comparison, or filing-level rows pooled: the answer
insufficient_n
below the minimum sample: meta.guards says why
not_available
not in this release, not enabled for the code, or no such entity
scope_required
the code or key lacks the tier or scope (territories:read, mcp)
row_cap
above the export cap: narrow the filters or schedule the export
bad_request
a parameter the schema refuses
timeout
the 8 s statement timeout: narrow the filters
rate_limited
120 requests a minute per code, a key's own limit, 25,000 tool rows a day per code (HTTP 429 with the reset time)
db_unavailable
retry after 5 s

Tools

states_index

The states index: one process measure per state with its n (never a price level). Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/states · aggregate-safe

  • line?: string
  • measure?: "ranked_carriers" | "ranked_premium_share" | "objection_rate" | "median_days" | "median_filed_change" | "data_through" | "pending"

market_page

The assembled market page: KPIs and every section with its own meta. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/states/{st}/{line}/market · aggregate-safe

  • line: string
  • pop?: "current" | "filed"
  • weight?: "carriers" | "premium"
  • year?: string
  • subject?: string
  • state: string

market_distribution

The in-state LCM distribution: bins, published quantiles, marks, coverage. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/states/{st}/{line}/distribution · aggregate-safe

  • line: string
  • pop?: "current" | "filed"
  • weight?: "carriers" | "premium"
  • year?: string
  • subject?: string
  • state: string

market_movers

Quality-gated year-end movers and pending positions. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/states/{st}/{line}/movers · partner

  • line: string
  • from?: string
  • to?: string
  • limit?: integer
  • subject?: string
  • state: string

market_indicated_vs_taken

Indicated vs taken for an effective year: the published summary and company rows. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/states/{st}/{line}/indicated-vs-taken · aggregate-safe

  • line: string
  • year?: string
  • limit?: integer
  • state: string

market_signal

Four early-warning signals, each with its own n; never blended. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/states/{st}/{line}/signal · aggregate-safe

  • line: string
  • state: string

market_direction

Market direction: year-end medians, matched change, median filed change. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/states/{st}/{line}/direction · aggregate-safe

  • line: string
  • state: string

market_regulator

Regulator posture: objection rate, days (censoring), dispositions, issues. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/states/{st}/{line}/regulator · aggregate-safe

  • line: string
  • year?: string
  • type?: string
  • subject?: string
  • state: string

market_cadence

Rate filings per submission month vs the prior 12 months; filing-type mix. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/states/{st}/{line}/cadence · aggregate-safe

  • line: string
  • year?: string
  • state: string

market_concentration

Observed-premium concentration among ranked groups. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/states/{st}/{line}/concentration · aggregate-safe

  • line: string
  • state: string

market_environment

Rate environment: bureau editions and basis breaks, adoption, residual market, industry. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/states/{st}/{line}/environment · aggregate-safe

  • line: string
  • state: string

structure_ladders

Tier ladders as filed; no-base ladders as ranges. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/states/{st}/{line}/structure/ladders · partner

  • line: string
  • state: string

structure_classes

Class market cells, appetite and the subject's class values. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/states/{st}/{line}/structure/classes · partner

  • line: string
  • dimension?: "class" | "federal_act" | "territory"
  • code?: string
  • subject?: string
  • include_base?: boolean | "0" | "1" | "true" | "false"
  • state: string

structure_expenses

Expense provisions by component: rungs, quantiles, the subject's values with each one's source (form, or llm = AI-read). Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/states/{st}/{line}/structure/expenses · partner

  • line: string
  • year?: string
  • subject?: string
  • dots?: boolean | "0" | "1" | "true" | "false"
  • state: string

structure_decomposition

LCM = EM x LP from the same filing, with published percentiles. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/states/{st}/{line}/structure/decomposition · partner

  • line: string
  • subject?: string
  • weight?: "carriers" | "premium"
  • state: string

movement_relative

Filed movement relative to the bureau: index % and gap pp, chains of 3+ years. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/states/{st}/{line}/movement/relative · partner

  • line: string
  • year?: string
  • subject?: string
  • state: string

movement_adoption

Adoption of bureau editions: lags, strip and ECDF over carriers writing in the state. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/states/{st}/{line}/movement/adoption · partner

  • line: string
  • edition?: string
  • state: string

movement_reaffirmations

"No change" reaffirmations with their quote and page, and refusal counts. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/states/{st}/{line}/movement/reaffirmations · partner

  • line: string
  • state: string

filing_detail

One filing: header, company rows, LCM observations, carry note, expenses, classes, facts, letters, documents. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/filings/{tracking} · partner

  • tracking: string

filing_letter

One objection or response letter as insights: type, round, dates, days, issues, the summaries, the filing's disposition, at most a 280-character quoted excerpt and the SERFF citation; never the letter's text. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/filings/{tracking}/letters/{occurrence} · partner

  • tracking: string
  • occurrence: integer
  • type?: "objection" | "response"

regulator_summary

Regulator posture: objection rate, days (censoring), dispositions, issues. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/states/{st}/{line}/regulator · aggregate-safe

  • line: string
  • year?: string
  • type?: string
  • subject?: string
  • state: string

regulator_precedents

Precedents on one issue: the regulator's ask and the carrier's response on other carriers' filings, as summaries and short quoted excerpts (<= 280 characters) with the SERFF tracking (gate: 5 from 3 carriers, the subject excluded); else the letters index. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/states/{st}/{line}/regulator/precedents · partner

  • line: string
  • issue?: string
  • disposition?: "approved" | "any" | "modified" | "disapproved" | "withdrawn" | "pending" | "filed_acknowledged" | "closed_other" | "other"
  • year?: string
  • type?: "all" | "rate" | "loss_cost" | "adoption" | "rule_form" | "other"
  • substantive?: boolean | "0" | "1" | "true" | "false"
  • q?: string
  • exclude_group?: string
  • subject?: string
  • limit?: integer
  • offset?: integer
  • state: string

issues

The objection-issue crosswalk. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/issues · aggregate-safe

no arguments

group_position

A group's positions by state (each state its own population), history. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/groups/{group}/position · partner

  • group: string
  • line?: string
  • pop?: "current" | "filed"
  • weight?: "carriers" | "premium"

group_structure

The structure page scoped to a group's entities in one state. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/groups/{group}/structure · partner

  • group: string
  • state: string
  • line?: string
  • weight?: "carriers" | "premium"

group_movement

The movement page scoped to a group's entities in one state. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/groups/{group}/movement · partner

  • group: string
  • state: string
  • line?: string

carrier_position

A carrier's position in every state it has one, with history. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/carriers/{cocode}/position · partner

  • cocode: string
  • line?: string

events

Events between two named releases, optionally for one watchlist. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/events · partner

  • watch?: string
  • scope?: "watch" | "states"
  • since?: string
  • kinds?: string
  • state?: string
  • line?: string
  • limit?: integer
  • cursor?: string

statement

The Monthly Position Statement of a watchlist: eight fixed cards, snapshot to snapshot. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/statement · partner

  • watch?: string
  • month?: string

freshness

Data through per state. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/freshness · aggregate-safe

  • state?: string

coverage

Ranked share of filed premium and the top unranked writers per state. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/coverage · aggregate-safe

  • state?: string

datasets

The published data dictionary: each dataset's columns with type, definition, unit, precision, and the filters, group-by keys and statistics allowed over them. Report n, population and as-of from meta; do not use evaluative adjectives; do not aggregate across states or regimes; a value_kind_not_applicable or not_comparable error is the answer.

GET /api/v1/datasets · aggregate-safe

no arguments