OpenCityRecord
Server Details
City council meetings, transcripts, votes, and documents for U.S. cities, with citations.
- Status
- Healthy
- Uptime
- 49.9% over 21 days
- Last Tested
- Transport
- Streamable HTTP · MCP 2025-11-25
- URL
TDQS
Scored across 12 tools
The set has several search-adjacent tools (search, search_meetings, search_documents, search_by_official, search_decisions), but each targets a distinct source or scope and the WHEN/WHEN NOT guidance sharply separates them. fetch vs get_transcript_window and whats_changed vs search_decisions are the closest overlaps, yet their descriptions clarify when to prefer each.
All names use snake_case, and families like search_* and *_timeline are predictable. Minor deviations exist: fetch is a bare verb, application_timeline/issue_timeline are noun phrases, and whats_changed lacks the apostrophe, but overall consistency is high.
12 tools is well within the 3–15 sweet spot and each tool maps to a distinct civic-record query intent (search, timelines, officials, votes, decisions, summaries, retrieval). No tool appears redundant enough to remove.
The surface covers core retrieval workflows: search across transcripts/documents/decisions, official lookup, voting records, issue/application timelines, change summaries, full fetch, and transcript windows. Minor gaps remain, such as direct list/browse operations for meetings or issues without a keyword, but most gaps have workarounds through search or timelines.
Available Tools
12 toolsapplication_timelineDevelopment application timelineARead-onlyIdempotentInspect
How a development project moved through the city — a rezone, subdivision, site plan, conditional-use permit, or apartment/commercial build. Returns the application's lifecycle across meetings (pending → under-review → hearing → approved/denied), oldest first, with the status recorded at each step.
WHEN: 'What's the status of the Silo Park development?', 'How did rezone X move through the council?'. WHEN NOT: For issues/policy (not a specific project) use issue_timeline; for quotes use search_meetings.
EXAMPLES: { "q": "Silo Park", "city": "lehi-ut" } { "application_id": "" }
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | Resolve by a project-name / address / slug substring when you don't have the id (e.g. 'Silo Park'). Ignored if application_id is set. | |
| city | No | City slug to scope resolution (e.g. 'lehi-ut'). Required with q when you have access to more than one city. | |
| limit | No | Max timeline entries. Default 100. | |
| application_id | No | UUID of the development application. Preferred when known. |
Output Schema
| Name | Required | Description |
|---|---|---|
| timeline | Yes | Every meeting where the application came up, oldest first — read status_at_mention down the list to see the lifecycle. |
| application | Yes | The resolved application, or null when nothing matched the query/scope. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive, and open-world, so the safety profile is covered. The description adds genuine behavioral context beyond that: entries are returned oldest-first, each step carries the status recorded at that step, and the lifecycle state machine (pending → under-review → hearing → approved/denied) is disclosed. It stops short of discussing resolution failure behavior or pagination beyond the limit param.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded purpose sentence, then clearly delimited WHEN / WHEN NOT / EXAMPLES blocks. Every line earns its place and nothing is repeated from structured fields unnecessarily.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be re-explained, and annotations carry the safety profile. The description still supplies the ordering guarantee, the lifecycle vocabulary, and the sibling routing, leaving nothing an agent needs to call this correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so q, city, limit, and application_id are all documented in the schema itself, including the q-ignored-when-id-set rule and the UUID format. The description adds examples showing q+city together but no semantics beyond what the schema already states, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource: it returns how a development project moved through meetings, with named application types (rezone, subdivision, site plan, CUP, build). It also implicitly distinguishes itself from the policy-oriented sibling issue_timeline. An agent can tell this apart from search_meetings or issue_timeline without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit WHEN examples ('What's the status of the Silo Park development?') and WHEN NOT routing to issue_timeline for policy issues and search_meetings for quotes. The alternative selection conditions are spelled out rather than inferred.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fetchFetch a full transcript or documentARead-onlyIdempotentInspect
Retrieve the full record behind a search result: the complete meeting transcript (for meeting:… ids) or the full document text (for doc:… ids). Long records are truncated at a safe length with a marker.
WHEN: After the search tool, to read a result in full before answering or citing. WHEN NOT: Ids are opaque — never construct one by hand; only use ids returned by search.
EXAMPLES: { "id": "meeting:lehi-ut:1f0c…" } { "id": "doc:lehi-ut:1f0c…:42" }
A not-found error means that specific id is stale or unindexed — re-run search for fresh ids; it is not an access restriction.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | An id returned by the search tool. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | |
| url | Yes | Canonical citation URL. |
| text | Yes | Full text of the record (transcript or document), possibly truncated. |
| title | Yes | |
| metadata | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, so the safety profile is covered; the description goes further by disclosing truncation of long records with a marker and by clarifying that a not-found error means a stale id rather than an access restriction. That error-semantics detail is context annotations cannot express.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core action, then uses short labeled sections (WHEN, WHEN NOT, EXAMPLES, error note) with zero filler. Every sentence conveys a constraint or a failure mode the agent needs.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return-value documentation is not required, yet the description still covers truncation, id provenance, and error interpretation. For a single-parameter read tool this is fully sufficient to call correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and there is only one parameter, so the baseline is 3. The description adds real meaning beyond the schema's terse 'An id returned by the search tool': the meeting:/doc: prefixes, the opacity of ids, and the prohibition on hand-constructing them.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (retrieve) and resource (full record) and explicitly maps the two id namespaces to two outcomes: 'complete meeting transcript (for meeting:… ids) or the full document text (for doc:… ids)'. This lets an agent distinguish it from search and search_meetings without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit WHEN ('after the search tool, to read a result in full before answering or citing') and WHEN NOT ('never construct one by hand; only use ids returned by search'), plus the remedy for the most likely failure mode. Nothing about selection is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_transcript_windowRead the transcript around a momentARead-onlyIdempotentInspect
The passage of a meeting transcript surrounding a given moment — what was said before and after, with speakers and timestamps. Pass the meeting_id and start_seconds from a search_meetings hit.
WHEN: ALWAYS, before quoting or reasoning from a search_meetings hit. Search returns individual segments, and a segment is often a fragment — one real hit was the entirety of "does not have a contract with FLOC". The answer is in the passage around it, not in the segment. WHEN NOT: To find something, use search_meetings first — this tool has no query and only reads a meeting you already located. For a whole document, use fetch.
Prefer this over fetch for transcripts: fetch truncates at 40,000 characters from the START of a meeting, so on a three-hour council meeting anything after roughly the first hour is missing entirely — including most late-agenda votes.
EXAMPLES: { "meeting_id": "977675c0-...", "around_seconds": 5113 } { "meeting_id": "977675c0-...", "around_seconds": 5113, "before_seconds": 300, "after_seconds": 300 }
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max segments returned. Default 80. | |
| meeting_id | Yes | Meeting UUID, taken from a search_meetings or search_documents hit. | |
| after_seconds | No | Seconds of transcript to include after the centre. Default 180 — a little longer than `before`, because the answer to 'what happened here' usually follows the mention. | |
| around_seconds | Yes | Elapsed seconds into the meeting to centre the window on — pass the `start_seconds` of the hit you want context for. | |
| before_seconds | No | Seconds of transcript to include before the centre. Default 120, max 900. |
Output Schema
| Name | Required | Description |
|---|---|---|
| window | Yes | The time range actually covered, after clamping to the meeting. |
| segments | Yes | |
| city_slug | Yes | |
| truncated | Yes | True when `limit` cut the window short — widen nothing, ask for a narrower range. |
| video_url | Yes | Jumps to the start of this window. Use verbatim — do not invent URLs. |
| meeting_id | Yes | |
| transcript | Yes | The window as one readable passage, speaker-prefixed. Quote from this verbatim — do not paraphrase and present it as a quote. |
| meeting_url | Yes | Use this verbatim when citing — do not invent URLs. |
| meeting_date | Yes | Unix seconds, UTC midnight. |
| meeting_title | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, openWorld, non-destructive, so the safety profile is covered. The description adds real behavioral value annotations cannot carry: search returns fragmentary segments, and fetch truncates at 40,000 characters from the START of a meeting, silently losing late-agenda content. It stops short of describing output shape or any rate limits, but the truncation caveat is exactly the kind of context that prevents a wrong tool choice.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the one-line definition, then WHEN / WHEN NOT / EXAMPLES in scannable order. The anecdote about the FLOC fragment and the fetch truncation detail both earn their length by justifying the routing rule.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be explained. Combined with annotations, a 100%-covered schema, and explicit routing guidance, everything an agent needs to invoke this correctly is present.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description adds the non-obvious mapping that the caller should pass start_seconds from a search_meetings hit as around_seconds, and provides two concrete invocation examples including before/after overrides. That is genuine meaning beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource — return the transcript passage surrounding a given moment, with speakers and timestamps — and immediately frames it against siblings (search_meetings hits, fetch). An agent can distinguish it from every other sibling without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit WHEN (always, before quoting or reasoning from a search hit) and WHEN NOT (to find something, use search_meetings; for a whole document, use fetch), plus a stated preference rule over fetch with the reason. Alternatives and selection conditions are fully specified.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
issue_timelineIssue lifecycle timelineARead-onlyIdempotentInspect
How a local issue developed over time — a zoning change, a housing or density fight, a tax or fee, a road, park, or police matter. Returns the chronological lifecycle of the issue across meetings (raised → discussed → voted), oldest first, each entry linking to the meeting.
WHEN: 'How has the short-term-rental issue progressed?', 'When was X first discussed and what happened since?'. WHEN NOT: For what a specific person said, use search_by_official; for how they voted, use track_position. For a one-off keyword search, use search_meetings.
Pass city whenever you pass q. The same phrase names a different fight in every city, and without a city the match is ambiguous — you will get issue: null plus a candidates list to choose from rather than a confident answer about the wrong city. Check candidates even on a resolved issue: 'short-term rentals' matches both a citywide ordinance and one subdivision's area-plan amendment.
EXAMPLES: { "q": "short-term rentals", "city": "lehi-ut" } — ALWAYS pass city with q { "issue_id": "" }
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | Resolve the issue by a title/slug substring when you don't have the id (e.g. 'short-term rentals'). Ignored if issue_id is set. | |
| city | No | City slug to scope resolution (e.g. 'lehi-ut'). Required with q when you have access to more than one city. | |
| limit | No | Max timeline entries. Default 100. | |
| issue_id | No | UUID of the issue. Preferred when known. |
Output Schema
| Name | Required | Description |
|---|---|---|
| issue | Yes | The resolved issue. Null when nothing matched, OR when `q` matched issues in more than one city and no `city` was given — check `candidates` in that case and re-call with a city or an issue_id. |
| timeline | Yes | Every meeting where the issue was discussed, oldest first. |
| candidates | Yes | Other issues matching `q`. Populated (with issue null) when the match was ambiguous ACROSS cities — pick one rather than assuming. Also lists same-city alternates alongside a resolved issue, since one phrase often matches both a citywide ordinance and a single development's version of it. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, non-destructive), and the description adds non-obvious runtime behavior: oldest-first ordering, per-entry meeting links, and the fallback where an ambiguous match yields issue: null plus a candidates list. That is real behavioral disclosure beyond structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose, then WHEN/WHEN NOT, then examples; nearly every sentence earns its place. Slight redundancy in the two sentences about candidates, but no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return values need not be described, and the description still covers invocation guidance, resolution ambiguity behavior, and required pairings. Nothing an agent needs to call this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% so the baseline is 3, but the description adds resolution semantics the schema does not convey — the city-scoping rationale (same phrase means a different fight per city) and the candidates list even on a resolved issue. limit/issue_id precedence is left to the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific resource (an issue's chronological lifecycle) with concrete domain examples (zoning, housing, tax, road, park) and the shape of the return (raised → discussed → voted, oldest first, each entry linking to the meeting). It is clearly distinguishable from siblings like search_meetings and track_position.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit WHEN examples, explicit WHEN NOT with named alternatives and the condition that selects each (person's statements → search_by_official, votes → track_position, one-off keyword → search_meetings). Also prescribes passing city with q.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_officialsList elected officialsARead-onlyIdempotentInspect
Who serves on a city council — mayors and council members — looked up by city, state, or name.
Returns the CURRENT ELECTED roster by default: people in office right now, no appointed board members, no staff. Answer 'who represents me' straight from this — no need to hedge about whether someone still holds the seat. Each row carries role, status, and term dates.
WHEN: First stop for 'who represents…' questions, and the prerequisite for any search_by_official / track_position call (both need a UUID this tool returns). WHEN NOT: For voting records use track_position; for quotes use search_by_official, with an id from here.
Set include_former for historical questions ('who was mayor in 2024?') or when reading an older meeting whose speakers have since left office — an official_id you got elsewhere may belong to someone no longer seated. Set include_appointed for planning commissioners and city staff.
EXAMPLES: { "city": "lehi-ut" } — the sitting Lehi council { "city": "lehi-ut", "include_former": true } — plus everyone who has left office { "state": "UT", "name_contains": "binns" } — find 'Paul Binns' across Utah { "city": "lehi-ut" } then pivot to track_position with one of the returned ids
| Name | Required | Description | Default |
|---|---|---|---|
| city | No | City slug (e.g. 'lehi-ut'). Strongly recommended — narrows the response to one city's roster. Without it, returns officials across every city you have access to (can be hundreds of rows). | |
| limit | No | Max rows returned. Default 50. | |
| state | No | Two-letter US state abbreviation (e.g. 'UT', 'CA'). Use when you want every city in a state. Case-insensitive. | |
| name_contains | No | Case-insensitive substring match on the official's name (e.g. 'binns' matches 'Paul Binns'). Combine with `city` or `state` to look up someone specific. | |
| include_former | No | Include officials who have left office. Default false — 'who represents me' means the sitting roster. Turn on for historical questions ('who was mayor in 2024?') or when reading an old meeting where the speakers are no longer seated. | |
| include_appointed | No | Include appointed board members (planning commissioners and the like) and city staff (administrator, attorney, recorder). Default false — they are not elected and do not vote on the council. |
Output Schema
| Name | Required | Description |
|---|---|---|
| total | Yes | Number of officials returned in this response. |
| officials | Yes | Officials matching the filters — by default the CURRENT ELECTED roster, so this answers 'who serves on the council' directly. Public-record data from city government websites. |
| citiesQueried | Yes | Number of cities scanned. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only/idempotent/non-destructive semantics, and the description adds real behavior beyond them: the default roster excludes appointed members and staff, former officials require an explicit flag, and a stale official_id may point to someone no longer seated. The mention of role/status/term-date columns is mildly redundant given the output schema exists, keeping this just short of top marks.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Well front-loaded with the most decision-relevant fact first (current roster by default), then WHEN/WHEN NOT, then examples. It is longer than strictly necessary because the include_former and include_appointed rationales are largely duplicated in the schema descriptions.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a six-parameter, annotation-rich tool with an output schema, the description covers purpose, default filtering behavior, flag semantics, prerequisites, and sibling routing without needing to explain return values. Nothing an agent needs in order to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds intent-level meaning: include_former is framed as the historical/old-meeting switch, include_appointed as the planning-commissioner/staff switch. The EXAMPLES block demonstrates realistic parameter combinations (city+include_former, state+name_contains) that the schema alone does not convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource — looking up who serves on a city council — and pins the default scope precisely (current elected officials, no appointed members, no staff). An agent can distinguish it from track_position and search_by_official without reading a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit WHEN and WHEN NOT sections name the conditions and the alternatives: track_position for voting records, search_by_official for quotes. It also declares a prerequisite relationship (both siblings need a UUID this tool returns), which is exactly the routing information an agent needs.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
searchSearch city meetings and documentsARead-onlyIdempotentInspect
Anything about a city council, mayor, ordinance, zoning case, development permit, meeting agenda, or municipal budget. Combined keyword search across public city-government meeting transcripts AND meeting documents (agendas, minutes, packets, ordinances). Returns results with opaque ids — pass an id to the fetch tool for the full transcript or document text.
OpenCity URLs in the query are understood as scope, not text: a city site (taylorsville.openutah.org) narrows to that city, a state site (utahrecord.org, myopentexas.org) narrows to that state, and a full meeting URL resolves to that exact meeting. A bare URL with no other words returns that scope's most recent activity.
WHEN: General civic questions ('what has the city said about X?') from clients that auto-route through search/fetch (e.g. ChatGPT connectors), and whenever the user pastes an opencity/openutah/opentexas-style URL. WHEN NOT: If search_meetings / search_documents / list_officials are available to you, prefer them — they take filters (city, meeting type, doc type) and return structured fields this tool flattens away.
EXAMPLES: { "query": "short-term rental ordinance" } { "query": "parking taylorsville.openutah.org" } — 'parking', scoped to Taylorsville { "query": "utahrecord.org" } — recent activity across Utah cities
Empty results mean no records matched the query — never interpret them as missing access or permissions; retry with broader keywords instead.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Free-text search query. |
Output Schema
| Name | Required | Description |
|---|---|---|
| notice | No | Present when results may mislead — e.g. a URL in the query resolved to cities outside this account's scope. Relay this to the user. |
| results | Yes | Matches across meeting transcripts and documents, newest first. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/openWorld/non-destructive, so the safely-read profile is covered. The description goes well beyond that: it explains the opaque-id → fetch handoff, the URL-as-scope (not text) resolution rules, and that empty results mean no matches rather than a permissions failure with a retry-broader-keywords remedy — genuinely useful operational context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Long but organized into purpose, URL semantics, WHEN/WHEN NOT, and worked examples, with the core purpose front-loaded. The opening nominal phrase ('Anything about a city council, mayor, ...') is slightly list-heavy, but every section carries routing or interpretation value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return-value details are not required, yet the description still explains the id→fetch workflow that makes the output usable. Combined with the URL-scoping rules and empty-result interpretation, an agent has everything needed to call and consume this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% for the single query parameter, so baseline is 3. The description adds real meaning beyond 'Free-text search query' — how embedded OpenCity/state URLs are parsed as scope, and that a bare URL returns that scope's most recent activity — which materially changes how the agent should construct the value.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Combined keyword search across public city-government meeting transcripts AND meeting documents') and enumerates the covered content types (ordinances, zoning, agendas, budgets). It explicitly distinguishes itself from search_meetings, search_documents, and list_officials, so an agent can separate it from siblings without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Dedicated WHEN and WHEN NOT sections: prefers this tool for auto-routing clients and pasted OpenCity URLs, and explicitly routes to filtered siblings when they are available. Both the trigger conditions and the alternatives are named, leaving little to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_by_officialSearch what an official said (paid)ARead-onlyIdempotentInspect
Retrieve every transcript segment a specific elected official has spoken. Optional q narrows within their segments. Returns their speaking history, newest first — what they argued, asked, and moved, in their own words.
WHEN: 'What has Councilmember X said about Y', their reasoning on an issue, or a verbatim quote with a jump-to timestamp. WHEN NOT: For how they voted, use track_position — that reads the recorded roll call (yes/no/abstain per decision). Transcripts capture discussion, not the tally, so compiling a voting record from these segments systematically under-reports and will miss votes entirely. If you don't yet have their UUID, call list_officials first. For non-elected speakers (staff, public commenters), use search_meetings.
EXAMPLES: { "official_id": "", "q": "housing" } { "official_id": "" } — full speaking history newest-first
REQUIRES: a paid plan (Resident and above). Free callers get an upgrade hint, no data.
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | Optional free-text query to narrow within this official's segments (e.g. 'budget', 'zoning'). | |
| limit | No | Max hits returned. Default 10. | |
| cities | No | Optional city-slug allow-list. Officials belong to one city already, so this is rarely needed; a list that excludes the official's city returns no hits. | |
| official_id | Yes | UUID of the elected official. Get this from list_officials or from the official_id field on a search_meetings hit. |
Output Schema
| Name | Required | Description |
|---|---|---|
| hits | Yes | Transcript segments where this official spoke, newest first. |
| total | Yes | Total estimated matches for this official. |
| citiesQueried | Yes | Number of cities actually searched. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, openWorld), and the description adds context they cannot: a paid-plan gate (Resident and above), the concrete free-tier behavior ('upgrade hint, no data'), and the newest-first ordering of results. These are exactly the behavioral traits an agent needs before invoking.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core behavior, then cleanly sectioned into WHEN / WHEN NOT / EXAMPLES / REQUIRES. Every line carries routing or behavioral information; nothing is filler despite the length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return-value explanation is unnecessary, and the description covers everything else an agent needs: prerequisites (UUID from list_officials), paywall gating, alternative tools, and example calls. No material gap remains.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds invocation-shape value the schema doesn't: it clarifies that omitting q returns full speaking history and shows two worked examples. It slightly duplicates the schema's official_id sourcing note, keeping it from a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource — retrieving every transcript segment a specific elected official has spoken — and scopes it ('spoken', newest-first). It explicitly distinguishes itself from siblings by naming track_position and search_meetings, so an agent can route correctly without opening other schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit WHEN ('what has X said about Y', reasoning, verbatim quote with timestamp) and WHEN NOT sections name the exact alternatives: track_position for votes, list_officials for the UUID prerequisite, search_meetings for non-elected speakers. It even explains *why* the alternative is correct (transcripts under-report voting records), which is unusually strong routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_decisionsSearch recorded motions, votes and amountsARead-onlyIdempotentInspect
What a body actually DECIDED, from the approved minutes — the motion as recorded, who moved it, the roll-call tally, and any dollar figure attached. This is the structured record, not people talking: use it whenever the question is what was approved, denied, tabled, or how much something cost.
WHEN: 'Which cities approved Flock Safety cameras?', 'Show me every contract over $100k in Texas this year', 'Where did accessory-dwelling ordinances stall?', 'What did Lehi vote on last month?'. WHEN NOT: For what someone SAID, use search_meetings. For one official's record across items, use track_position. For a project's lifecycle, use application_timeline.
TWO THINGS THAT WILL MISLEAD YOU IF IGNORED:
• Report outcome, not result. A passed motion TO TABLE means the item did NOT advance; 161 published motions are exactly that. outcome already combines motion_kind with result.
• amount_deferred: true means the money was named inside a motion to delay or table. Never describe it as spending.
• EMPTY IS NOT "NOTHING WAS DECIDED". Motions come only from minutes we have extracted, which lag the meeting and are missing for many cities. Read coverage / coverage_note before answering, and fall back to search_documents for the minutes or agenda.
• unofficial_decisions are decisions heard in the RECORDING of a meeting where no official motion covers that item — usually because its minutes are not published yet. They are NOT the official record: say "according to the meeting recording" and repeat the row's record_note (it says whether the minutes are unpublished or simply have no matching motion), never present their vote_count as an official roll call, and never count them into total. Verify a specific one with get_transcript_window at its timestamp_seconds.
q matches whole words, so 'Flock' will not return a person named Flocke. Every row carries a citation_url pointing at that exact motion.
EXAMPLES: { "q": "Flock Safety" } { "q": "accessory dwelling", "outcome": "tabled" } { "min_amount_usd": 100000, "basis": ["award"], "since": "2026-01-01" }
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | Term matched as a WHOLE WORD or phrase against the motion text — a vendor ('Flock Safety'), an instrument ('Ordinance 26-08'), a subject ('accessory dwelling'). Whole-word matching means 'Flock' will not match a person named Flocke. Omit to browse by city, date or amount alone. | |
| basis | No | Restrict to how the money is being used. 'award' is a contract/grant/purchase; 'appropriation' is budget/bond/levy; 'fee' is a fee, fine or waiver. Rates and third-party figures are never returned. | |
| limit | No | Max motions returned, newest first. Default 40. | |
| match | No | How `q` is matched. 'phrase' (default): the whole of `q` as one whole-word phrase — for an instrument or vendor ('Ordinance 26-08', 'Flock Safety'). 'any': each word of `q` as a whole word, in any order, a plural matching its singular; a motion matching more of the words ranks first. Use 'any' for a subject described in a resident's words ('sycamore trees', 'budget adopted'). | phrase |
| since | No | Meetings on or after this date (YYYY-MM-DD). | |
| until | No | Meetings on or before this date (YYYY-MM-DD). | |
| cities | No | City-slug allow-list (e.g. ['lehi-ut']). Omit to search every city the plan allows. | |
| outcome | No | Filter by what happened to the ITEM, not to the motion. 'tabled' means a motion to table passed, so the item did not advance. Use 'denied' or 'tabled' to find where something stalled — for policy-diffusion questions that is the more informative half. | any |
| min_amount_usd | No | Only motions carrying a parsed dollar figure at or above this many whole dollars. Use 0 for every motion that carries any dollar figure. Excludes tax rates, third-party figures and deferred amounts automatically. |
Output Schema
| Name | Required | Description |
|---|---|---|
| total | Yes | Number of OFFICIAL motions returned, from published minutes (not an estimate — this is the row count). Does not include unofficial_decisions; see `returned`. |
| blocked | Yes | Tabled, denied, continued or withdrawn — the item did not advance. |
| summary | Yes | One sentence saying what came back, official and recording-reported separately. Read this first: `total` counts OFFICIAL motions only, so a response can carry decisions while total is 0. |
| approved | Yes | |
| coverage | Yes | Minutes coverage for each queried city that returned NO decisions, so an empty result can be told apart from a gap in our record. Omitted cities had hits. Empty when every city had hits, or when more than 8 cities came back empty (see coverage_note). |
| returned | Yes | Everything returned: `total` (official motions) + `unofficial_total` (recording-reported decisions). |
| decisions | Yes | |
| citiesQueried | Yes | |
| coverage_note | Yes | Plain-language caveat to relay when results are thin because minutes have not been extracted. Null when every queried city returned decisions. |
| citiesWithHits | Yes | How many distinct cities appear in the results. The useful denominator for 'how many cities have done this'. |
| totalAmountUsd | Yes | Sum of the returned motions' amounts, in whole dollars, excluding rates, third-party figures and deferred amounts. NULL when none carried a figure. Treat as a floor, not a budget: budgets, financings, awards and amendments legitimately overlap, so this can double-count the same dollars across instruments. |
| unofficial_total | Yes | Number of unofficial_decisions returned. NOT included in `total`, `approved` or `blocked`, which count official motions only. |
| unofficial_decisions | Yes | Decisions heard in the RECORDING of a meeting where no official motion covers that item: usually the approved minutes have not been published or extracted yet, and sometimes the published minutes have no motion for the item. NOT the official record: report them as 'according to the meeting recording' and repeat each row's record_note, which says which of the two cases applies. Never merge them into official results or present vote_count as an official roll call. A row is dropped when a published motion covers the same agenda item, so these do not restate `decisions`. Omitted when a dollar filter is set (transcript decisions carry no parsed amounts). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover the safety profile (readOnly, idempotent, non-destructive, open world), so the bar is lower — yet the description adds substantial non-obvious behavior: outcome vs. result semantics, amount_deferred meaning, that empty results reflect extraction lag rather than absence of decisions, that unofficial_decisions must not be counted in total or presented as official roll calls, and that every row carries a citation_url. This is exactly the kind of context annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Well front-loaded, with WHAT, WHEN, WHEN NOT and pitfalls in scannable blocks despite the length — justified by a 9-parameter tool with subtle semantics. Minor duplication: the 'Flock'/'Flocke' whole-word illustration restates the q schema description verbatim and could be dropped.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be explained; the description instead covers the interpretive layer the schema cannot — coverage/coverage_note caveats, how to handle unofficial_decisions, and where to verify a specific one (get_transcript_window). Complete for the tool's complexity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3 and the schema documents every parameter thoroughly. The description adds value through worked example invocations that demonstrate non-obvious combinations (outcome:'tabled' with a free-text subject; min_amount_usd with basis:['award'] and since), plus the outcome-not-result guidance. It repeats the whole-word matching rule already in the schema, so it is not a clean 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('search recorded motions, votes and amounts') and immediately scopes it: the structured record of what a body decided, drawn from approved minutes. It also names the sibling it is not ('not people talking... use search_meetings'), so an agent can route without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit WHEN section with natural-language queries, an explicit WHEN NOT section naming three alternatives (search_meetings, track_position, application_timeline) with the condition selecting each, and a documented fallback (search_documents) when coverage is empty. Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_documentsSearch meeting documentsARead-onlyIdempotentInspect
What the city put in writing — agendas, minutes, staff reports, meeting packets, attachments, ordinances, and resolutions. Keyword search across extracted document text. Use this when the value is in the written record, not the spoken transcript.
WHEN: Looking for ordinance language, budget tables, agenda text, staff reports, attached PDFs.
WHEN NOT: For what was said in the meeting, use search_meetings. For metadata-only queries ('what meetings happened last week?'), search_meetings with q on a placeholder works better.
EXAMPLES: { "q": "ADU ordinance", "cities": ["lehi-ut"] } { "q": "FY2027 budget", "cities": ["lehi-ut"] } { "q": "developer presentation Main Street" }
hits: [] means no document text matched — simplify the query and retry; never report it as missing access to a city.
| Name | Required | Description | Default |
|---|---|---|---|
| q | Yes | Free-text query matched against extracted text from agendas, minutes, packets, and attachments. | |
| limit | No | Max hits returned. Default 25, up to 150. Results are spread across cities and meetings so one verbose meeting cannot fill the response. For statewide or multi-city questions pass a high limit and filter the results yourself — that is cheaper and more complete than many narrow queries. | |
| cities | No | City-slug allow-list. Strongly recommended — searches every accessible city otherwise. | |
| doc_types | No | Filter by doc_type values, e.g. ['agenda', 'minutes', 'packet', 'attachment']. |
Output Schema
| Name | Required | Description |
|---|---|---|
| hits | Yes | Document matches, newest first. |
| total | Yes | Rough upper bound on candidate documents, NOT a count containing your full query — matching relaxes trailing words to keep recall up. Never report this as 'N mentions'; count `hits` instead. |
| citiesQueried | Yes | Number of cities actually searched. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover readOnly/idempotent/openWorld, but the description adds real behavioral context: the meaning of an empty `hits: []` (no text match, not missing access), the retry guidance, and the note that `limit` results are spread across cities and meetings. It stops short of documenting result shape or any auth/rate constraints.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded and scannable with labeled WHEN / WHEN NOT / EXAMPLES sections, and every block earns its place. The opening clause is stylistic rather than the operational verb, which pushes the actual function into the second sentence.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Output schema exists so return values need not be spelled out, yet the description still explains the empty-result case that an agent would otherwise misreport. With 4 well-documented parameters and full sibling routing, nothing needed to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the description goes beyond it with three worked examples that demonstrate realistic `q` + `cities` combinations and the newline `q` placeholder pattern for the sibling tool. It does not add format or enum detail the schema lacks, but the examples are genuinely additive.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource (keyword search across extracted document text) and enumerates the covered artifact types (agendas, minutes, staff reports, packets, attachments, ordinances, resolutions). It explicitly distinguishes itself from the sibling search_meetings by contrasting the written record with the spoken transcript.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit WHEN and WHEN NOT blocks naming the alternative tool (search_meetings) and the conditions that select it, including the metadata-only case with the `q` placeholder trick. Nothing about tool selection is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_meetingsSearch meeting transcriptsARead-onlyIdempotentInspect
What was actually said out loud at a city council meeting, planning-commission hearing, or any other public city-government meeting — about zoning, housing, the city budget, taxes, permits, ordinances, roads, parks, police, or public comment. Keyword search across transcript segments; each hit includes speaker, snippet, timestamp, and — when the speaker matches an elected official — an official_id UUID.
WHEN: 'What did people say about X?', 'When was Y last discussed?', or any query where the value is what was spoken in a meeting. WHEN NOT: For one person's complete speaking history, use search_by_official (better signal, no false positives from name collisions). For ordinance text, packets, or attached PDFs, use search_documents.
For 'recent' or 'this year' questions pass since — the corpus runs back years, and without a window old material competes with new. For 'what did OFFICIALS say' pass officials_only — it keeps segments whose speaker matched the city roster (elected, appointed or senior staff), because everyone else is unnamed by design and their quotes cannot be cited to a person.
EXAMPLES: { "q": "short-term rentals", "cities": ["lehi-ut"] } { "q": "housing affordability", "cities": ["lehi-ut"], "since": "2026-01-01", "officials_only": true } { "q": "downtown parking", "cities": ["lehi-ut"], "meeting_types": ["city_council"] } { "q": "developer Main Street" } — last meetings across allowed cities, newest first
hits: [] means no transcript segments matched — simplify the query and retry; never report it as missing access to a city.
| Name | Required | Description | Default |
|---|---|---|---|
| q | Yes | Free-text query matched against transcript text + speaker names + agenda items. | |
| limit | No | Max hits returned. Default 25, up to 150. Results are spread across cities and meetings so one verbose meeting cannot fill the response. For statewide or multi-city questions pass a high limit and filter the results yourself — that is cheaper and more complete than many narrow queries. | |
| since | No | Only search meetings on or after this date (YYYY-MM-DD). Use for 'recent' / 'this year' questions — without it the corpus goes back years and old material outranks new. | |
| until | No | Only search meetings on or before this date (YYYY-MM-DD). | |
| cities | No | City-slug allow-list (e.g. ['lehi-ut']). Strongly recommended — searches every accessible city otherwise. | |
| meeting_types | No | Filter by meeting_type values (e.g. ['city_council', 'planning_commission']). | |
| officials_only | No | Return only segments whose speaker was matched to the city's official roster — elected members, appointed board members, AND senior staff (city administrator, attorney, recorder). NOT elected-only: filtering is on whether the speaker is identified at all. Use for 'what did OFFICIALS say about X'; without it, most hits are applicants and residents at the comment mic, whose names are withheld by design, so the quotes come back unattributed and cannot be cited to a person. To keep only elected speakers, cross-reference the returned official_id against list_officials. |
Output Schema
| Name | Required | Description |
|---|---|---|
| hits | Yes | Transcript segments matching the query, newest first. |
| total | Yes | NOT a count of segments containing your full query, and never safe to report to a user as 'N mentions' — count `hits`, or say the record contains 'many'. With hybrid ranking on it is the number of hits returned; otherwise it is a rough upper bound on candidates, and because matching relaxes trailing words to keep recall up, a two-word query can report tens of thousands while only dozens contain both words. |
| citiesQueried | Yes | Number of cities actually searched. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, so the added value here is real behavioral context: result spread across cities and meetings, the `official_id` field appearing only when a speaker matches an elected official, attribution limits for unnamed residents, and an explicit instruction that `hits: []` means no match rather than missing access. The last point prevents a concrete failure mode the annotations never mention.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with what is searched, then WHEN/WHEN NOT, then examples, then the empty-result rule — a sensible priority order. It runs long and duplicates the `since` and `limit` rationale already present in the schema descriptions, which is the main waste.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return shape needn't be explained, and the description covers everything else an agent needs: scope, filters, routing to sibling tools, the meaning of an empty hit list, and the citation caveat. Nothing required to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all seven parameters and the baseline would be 3. The description still adds value through worked examples that show parameters being combined (cities + since + officials_only, meeting_types, and a bare-query case), plus a cross-reference pointing elected-only filtering to list_officials. Some of the `since` rationale restates the schema text verbatim, which caps this below a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (keyword search) and resource (meeting transcript segments) and immediately scopes what counts: city council, planning-commission, and other public city meetings. It names the differentiating siblings — search_by_official for a person's speaking history and search_documents for ordinance text and PDFs — so an agent can route correctly without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit WHEN block gives the triggering question shapes ('What did people say about X?'), and the WHEN NOT block names the two alternatives with the condition that selects each. Conditional guidance on `since` for 'recent' questions and `officials_only` for 'what did OFFICIALS say' closes the remaining ambiguity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
track_positionTrack an official's position over timeARead-onlyIdempotentInspect
The voting record of one elected official. Their actual recorded roll-call votes (yes/no/abstain, per decision, with the decision title) plus expressed stances, in chronological order, with cited quotes, jump-to timestamps, and detected position reversals (flips).
This is the ONLY tool that returns how someone voted. topic and the since/until date window are all optional — omit them all for a complete record.
WHEN: any question about how a named official voted — 'How has Councilmember X voted over the last couple months?' (pass since/until, no topic), 'How has X voted on housing?' (pass topic), 'Has the mayor changed position on short-term rentals?'. WHEN NOT: For what they said rather than how they voted, use search_by_official. For a topic across everyone (not one person) use whats_changed or search_meetings; to find the official_id first, use list_officials.
Do NOT try to reconstruct a voting record from transcript segments — transcripts capture discussion, not the tally, so that reliably under-reports. Votes live here.
PAID plan required (attributes positions to a named official).
EXAMPLES: { "official_id": "", "since": "2026-06-01" } — recent voting record, all topics { "official_id": "", "topic": "housing" } { "official_id": "" } — the complete record
| Name | Required | Description | Default |
|---|---|---|---|
| city | No | City slug to scope to (e.g. 'lehi-ut'). Defaults to the official's city. | |
| limit | No | Max trajectory entries. Default 100. | |
| since | No | Only include meetings on or after this date (YYYY-MM-DD). Use with `until` for 'the last couple of months'. | |
| topic | No | Optional topic / issue to narrow to (e.g. 'housing', 'short-term rentals'). Omit to get the official's complete voting and stance record — that is the right call for 'how has X voted recently', which no topic reliably captures. | |
| until | No | Only include meetings on or before this date (YYYY-MM-DD). | |
| official_id | Yes | UUID of the official. Get it from list_officials or a search_meetings hit's official_id. |
Output Schema
| Name | Required | Description |
|---|---|---|
| topic | Yes | Echo of the topic filter, or null when the full record was requested. |
| summary | Yes | Roll-up of the trajectory, including detected position reversals. |
| official | Yes | |
| trajectory | Yes | Chronological (oldest first) positions this official took — narrowed to `topic` and the date window when given. When truncated by `limit`, the OLDEST entries are dropped, so the most recent record is always present. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only/idempotent, but the description adds non-obvious context: a paid-plan requirement, a warning not to reconstruct voting records from transcript segments (they under-report because they capture discussion not tally), and the claim that this is the only tool returning votes. These are genuinely valuable behavioral facts beyond the structured data.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded headline plus tightly organized WHEN/WHEN NOT/EXAMPLES sections; sentences are dense but each carries routing or caveat information. Slightly long, with some repetition between prose and examples, keeping it short of a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, annotations covering the safety profile, 100% schema coverage, and explicit cost/limitation caveats, an agent has everything needed to select and invoke it correctly. The dependency on list_officials for the UUID is called out.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the schema for topic/since/until already states optionality and the 'omit for complete record' guidance — so the description largely restates rather than extends it. The example payloads add marginal value by showing valid argument combinations, but the baseline of 3 applies when the schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a concrete verb+resource ('The voting record of one elected official') and immediately scopes the return ('roll-call votes ... plus expressed stances, chronological'). It explicitly distinguishes itself from siblings — search_by_official for statements, whats_changed/search_meetings for topic-wide queries, list_officials for the ID.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Has explicit WHEN / WHEN NOT blocks with literal example questions mapped to argument patterns ('How has X voted on housing?' → pass topic) and named alternatives for each adjacent case. No inference is required to route correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
whats_changedWhat changed since a dateARead-onlyIdempotentInspect
What the city council actually decided recently — the changeset of council activity in a city since a date: decisions taken (with tallies where a roll call was recorded), issues discussed, development-application status changes, and — on paid plans — per-member votes and stance reversals. Free plans get the aggregated changeset without member attribution (redacted: true). Most decisions are unanimous voice votes with roll_call: false and no tally — they are still decisions; report them.
WHEN: 'What did the council do in the last two weeks?', 'Any movement on development since June?'. WHEN NOT: For one person or one issue over time, use track_position / issue_timeline.
EXAMPLES: { "city": "lehi-ut", "since": "2026-06-21" }
| Name | Required | Description | Default |
|---|---|---|---|
| city | Yes | City slug (e.g. 'lehi-ut'). | |
| limit | No | Max decisions/issues/applications each. Default 50. | |
| since | Yes | ISO date (YYYY-MM-DD). Only activity on/after this date is returned. |
Output Schema
| Name | Required | Description |
|---|---|---|
| period | Yes | |
| redacted | Yes | True when member-level attribution (vote-by-member, named stance flips) was withheld because the plan does not identify officials. Tallies and decision titles are complete either way. |
| decisions | Yes | Decisions taken in the window — approved, denied, tabled or continued — whether or not a roll call was recorded (see `roll_call`). Items only discussed are not decisions and appear under issues_active instead. |
| truncated | Yes | True when the window held more activity than one call reads, so the OLDEST part of it is missing. Narrow `since` and call again rather than reporting the result as complete. |
| stance_flips | Yes | Members who reversed position within the window. Empty when nobody flipped, or when `redacted` is true. |
| issues_active | Yes | Issues discussed in the window. |
| meeting_count | Yes | |
| applications_active | Yes | Development applications that came up, with the statuses seen in the window. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the read-only/idempotent annotations by disclosing plan-gated behavior (free plans return `redacted: true` with no member attribution) and a real interpretive trap: most decisions are unanimous voice votes with `roll_call: false` and no tally, yet must still be reported. This is exactly the kind of behavioral context an agent cannot derive from annotations or schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded, followed by WHEN / WHEN NOT and a single concrete example, with zero filler. Every sentence carries a distinct load: scope, plan behavior, edge case, routing, and a usable call snippet.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a three-parameter, output-schema-backed read tool, the description covers everything an agent needs: scope, selection criteria, sibling routing, plan gating, and a data-interpretation warning. Return values are already covered by the output schema, so nothing material is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so city, since, and limit are already fully documented in the schema; the description adds no syntax, format, or edge-case meaning for them. The worked example demonstrates a valid call but does not extend parameter semantics, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource — the 'changeset of council activity in a city since a date' — and enumerates exactly what it contains (decisions with tallies, issues discussed, development-application status changes, paid-plan member votes). It further distinguishes itself from siblings by naming track_position and issue_timeline as the wrong tools for single-entity queries.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit WHEN section gives sample user questions ('What did the council do in the last two weeks?') and an explicit WHEN NOT that routes single-person or single-issue longitudinal queries to track_position / issue_timeline. The alternative and the condition selecting it are both stated.
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.
12 tool updates
- First observed
application_timeline - First observed
fetch - First observed
get_transcript_window - First observed
issue_timeline - First observed
list_officials - First observed
search - First observed
search_by_official - First observed
search_decisions - First observed
search_documents - First observed
search_meetings - First observed
track_position - First observed
whats_changed
Publisher details
- Operator
- OpenCityRecord · Publisher source
- Operator website
- https://opencityrecord.org
- Vendor relationship
- First-party
- Documentation
- https://opencityrecord.org/mcp
- Trust center
- Not available
- Restrictions
- Requires an OpenCityRecord account; the free account covers one city.
Related MCP Connectors
Search US local-government meetings: AI summaries, transcripts, and adopted plan documents.
The public record of local government: what your officials actually say and do, word for word.
Local government intelligence for AI agents.
Public municipal notices with source links. x402 $0.005 USDC, Base. As is, no warranty.
11
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceOpen & historical US government bid solicitations from city/county portals — updated daily.11 npm1MIT
- AlicenseNot gradedqualityDmaintenanceProvides programmatic access to municipal codes, ordinances, and zoning regulations from thousands of US cities and counties through the Municode digital library, enabling search, navigation, and retrieval of local government legal documents.6MIT
- FlicenseNot gradedqualityDmaintenanceA read-only MCP server for querying Los Angeles City legislative data - Council Files, votes, member activity, and Neighborhood Council engagement - through parameterized tools without raw SQL.1-
- AlicenseAqualityDmaintenanceSearch bills and speaker-attributed hearing transcripts across all 50 US state legislatures.71MIT
Glama MCP Gateway
Add one secure layer between your agents and this server.