Demo script — Council District [NN] ([MEMBER NAME])

TEMPLATE. Copy this file to district-[NN].md and fill every [BRACKETED] placeholder. A run-it-live script for showing BetaNYC’s grounded AI + NYC/NYS open data MCPs to Council Member [MEMBER NAME] and staff. District [NN] covers [NEIGHBORHOODS].

Companion to the general ../user-journeys.md catalog, framed for a Council audience: their district’s data, their own legislation, their discretionary funding, their oversight.


Before you start (2 min)

Opening line:

“I’m going to ask an AI the questions your office fields every week — but this one is wired into the actual city and state record. Watch where the numbers come from.”


Act 1 — Your district, right now (3 min)

Prompt:

“What were the top 311 complaint types in Council District [NN] over the last 30 days?”

What to say: “Live 311 data through BetaNYC’s 311 MCP — the same complaints your constituents file every day, summarized in one question.”


Act 2 — Grounded vs. ungrounded (2 min)

Prompt:

“What legislation has Council Member [MEMBER NAME] introduced in the last year, and where is each bill in the process?”

What to say: “An ordinary chatbot will invent bill numbers or give you last term’s list. This one reads the Council’s own Legistar system — current sponsors, current status, real Intro numbers you can cite.”


Act 3 — Your legislation meets your funding (4 min)

The act a Council audience cares about most. Make it about them.

Prompt (step one — the allocation):

“Show the FY[YYYY] NYC Council discretionary (Schedule C) awards sponsored by council member [SURNAME].”

Prompt (step two — the check):

“Now check NYC Checkbook for FY[YYYY] spending paid to [LARGEST RECIPIENT ORG].”

What to say: “It just moved from where the money is allocated — the discretionary budget — to where it’s actually spent, in Checkbook. That’s the reconciliation your office does by hand, in one question.”

Look for a Tax Levy Elected Officials budget code in the Checkbook results. That is discretionary money appearing as an actual disbursement, and calling it out by name is the strongest moment available in this act.

⚠️ Ask by sponsoring member surname, never by district number. The Schedule C data keys on the sponsoring member, and the budget tool has no district filter. A district-shaped question does not fail loudly — it silently returns citywide awards that an agent will summarize as the district’s funding. See the parameter table in the presenter-notes section below.


Act 4 — An issue you’re working on, sourced (4 min)

Ask the office for a live priority, or pre-fill one here.

Prompt:

“For Council District [NN] and the issue of [ISSUE]: pull recent 311 data that quantifies it, find any related NYC Council legislation and upcoming hearings, and check NYS Open Legislation for a state bill on the same topic. Give me a short, sourced brief.”

What to say: “Every line traces to an official source — city data, the Council’s own system, and Albany’s. If staff challenges a number, the agent shows where it came from. Grounded, not improvised.”


Closing (1 min)

“BetaNYC built these connectors and guides in the open. Any office can point its own AI at the real city and state record instead of trusting it to remember. If there’s a question your team asks constantly, that’s exactly the kind of user journey we want to add.”

Leave-behind links:


Presenter notes (verified [YYYY-MM-DD])

Dry-run every prompt above against the live MCPs before the meeting, and record what actually happened here. A script without verified notes is a draft, not a demo. Delete this instruction line once you’ve filled the section in.

✅ Fixed 2026-07-21 — the BetaNYC MCPs now reject unknown parameters

This used to be the most dangerous thing in this document. All seven servers accepted parameters that were not in their schema, silently dropped them, and returned unfiltered results with no error — real, correctly formatted data answering a different question. A guessed parameter name produced plausible garbage and nothing in the response revealed it.

Now shipped. As of 2026-07-21 every BetaNYC server rejects an undeclared parameter, and six of the seven also name the parameters they do accept. (nyc-checkbook-mcp is the exception: outside search_contracts it returns zod’s bare “Unrecognized key(s)” JSON with no accepted-parameter list. If you demo a refusal deliberately, use the budget or charter server.) Minimum versions carrying the fix:

nyc-budget-mcp 1.3.0 · nyc-council-mcp 2.5.0 · nyc-checkbook-mcp 1.4.0 · nyc-record-mcp 1.1.0 · nyc-311-mcp 1.1.0 · nyc-charter-laws-rules 0.2.0 · nys-openlegislation-mcp 2.3.0

Verified live against the published packages. A wrong parameter name now produces, e.g.:

search does not accept 'council_district'. Accepted parameters: query, corpus, limit.
Unrecognized parameters are rejected rather than ignored: dropping one would return
real, correctly formatted results for a different question.

Confirm your versions before presenting — run /mcp-update, and remember these are consumed via npx -y, so a stale npx cache can still serve an old build. If you are on an older version, the silent-drop behavior above is still live and the table below is a safety requirement rather than a convenience.

This is worth demoing deliberately. Asking for something the tool cannot do, and having it refuse by name, is a stronger trust argument than any successful query. Socrata is a third-party server and is NOT covered — its rows below still drop silently.

Verified-correct parameter names (re-verified 2026-07-21):

Task Correct parameter Do not use If you get it wrong
Schedule C awards by member council_member="[SURNAME]" (surname substring) council_district, sponsor rejected by name (fixed)
Checkbook payments to an org payee_name="[ORG NAME]" vendor rejected by name (fixed)
Socrata aggregate query separate select / where / group / order / limit a single soql blob ⚠️ still silently ignored — third-party
311 by council district council_district='04'zero-padded string '4' ⚠️ still returns zero rows silently — this is a data-value trap, not a parameter one, so the fix does not catch it

⚠️ limit truncates the TOTAL, not just the list — verified the hard way 2026-07-21

This produced two wrong dollar figures that reached finished demo scripts. search_awards reports a summary total computed over the rows it returned. Cap the query and you cap the sum, with nothing in the output marking it partial.

The rule: pass limit: 200 or higher on any member-year query, and treat returned count == limit as truncated until you re-run higher. A returned count that equals the limit is not a result, it is a warning. The same applies to search_transparency_resolutions, where the default 50 silently truncated a rescission table mid-alphabet.

Note the strict-parameter fix does not help here. limit is a valid parameter receiving a valid value; there is nothing for a schema to reject. Schema strictness catches wrong names, never wrong numbers.

⚠️ 311 counts are a moving 30-day window

Any “N complaints in the last 30 days” figure is perishable by construction and will not reproduce on a later date. That is not an error — it is the query. Re-run it the morning of the meeting and read the fresh number, rather than reciting a figure verified weeks earlier.

⚠️ Two more silent traps, both verified 2026-07-21

Zero-padding. In the 311 dataset (erm2-nwe9), council districts are zero-padded strings. council_district='4' returns zero rows with no error; '04' returns thousands. This affects districts 1–9 only — a two-digit district will never reveal the bug, so a script that worked for District 10 will fail silently for District 4.

Surname substring collisions — and the fiscal year decides how bad it is. council_member matches as a substring, so a surname contained in another member’s returns both, summed into one total. council_member="Powers", fiscal_year=2026 returns 156 awards, $3,405,000 — Selvena Brooks-Powers (D31) and Keith Powers (D4) combined. The same query at fiscal_year=2027 returns only Brooks-Powers, because Keith Powers no longer serves.

A merged two-member total looks entirely reasonable, and the sponsor column is the only tell. Check your member’s surname for collisions before the meeting, state the fiscal year, and read the sponsor column before quoting any figure. Unfixed — New-York-City-Budget#38.

Also verified — do not misread these as findings

Carried-forward gotchas — confirm these still hold on your dry run

Figures and their provenance

Record every number you plan to say out loud, and how you got it:

Claim How verified
[figure] [tool + parameters]

Also record what you did not verify. If you are unsure of the district’s neighborhood composition, say so here rather than reciting a list to a room that knows the district better than you do.


Backup prompts (if one falls flat)

If asked “can it be wrong?” — Yes, and that’s the honest answer: the agent can misread, or a dataset can be stale. The safeguard is that every answer is traceable to a source you can check, which an ungrounded chatbot can’t offer. Grounding doesn’t make it infallible; it makes it accountable.