NYC Open Data Capital Projects MCP Server
This server gives an AI assistant a read-only MCP interface to NYC capital projects data, with 18 purpose-built tools that answer schedule, budget, and lifecycle questions while baking in the dataset's domain rules.
Run read-only SQL against a local DuckDB, with typed analytics tables, raw mirrors, and CSV/XLSX export options.
Check dataset freshness, current reporting period, row counts, and caveats.
Browse agency and category dictionaries with live presence, budget counts, and totals.
Look up official field definitions and table schemas.
Resolve any project identifier (PID, FMS ID, name fragment) to matching schedules and budgets.
Get schedule detail: phase, lifecycle, signed variance, linked budgets, and forecast-past-due flags.
Get budget-line detail: total budget, spend, variance, and linked schedules.
View period-by-period history for a schedule or budget line, including original budget where recorded.
Produce schedule or budget breakdowns by agency, sponsor, borough, phase, category, and more.
Find newly completed or newly delayed projects between reporting periods.
Analyze delay-reason distributions with coverage stats.
Measure budget/spend changes between periods for an agency or a specific FMS line.
Rank projects or budget lines by budget, spend, variance, or cumulative change.
Compute duration statistics between actual milestones.
List project portfolios with filters like agency, borough, community board, category, and lifecycle status, with both per-PID and deduplicated budget totals.
Generate interactive HTML reports from a single prompt using the underlying data and domain rules.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@NYC Open Data Capital Projects MCP ServerShow me the capital projects with largest schedule delays"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
NYC Open Data — Capital Projects MCP Server
A local MCP server over the NYC Capital Projects Dashboard (CPD) datasets on NYC Open Data. It ingests four public Socrata datasets into a single local DuckDB and exposes 18 tools so an AI assistant can answer schedule, budget, and lifecycle questions about NYC capital projects — with the domain rules (PID↔FMS many-to-many, role-aware agency attribution, signed variance reporting) baked into the tools instead of left for the caller to rediscover.
Source datasets (Socrata)
ID | Dataset |
| Citywide Capital Project List Detail (the schedule↔budget edge) |
| Citywide Budget & Spend by FY |
| Citywide Budget Spend History & Variance |
| Citywide Schedule History & Variance |
Related MCP server: OfficeRnD MCP Server
What's an MCP — and why use one?
MCP (Model Context Protocol) is a standard way to give an AI assistant a new, trusted skill. Instead of pasting a spreadsheet into a chat and hoping the model reads it right, you hand it a set of well-defined tools it can call — with the domain rules already baked in. It's the difference between telling an analyst "here's a spreadsheet, good luck" and hiring one who already knows the data cold.
Why not just ChatGPT + a CSV? Public data is messy in ways a generic chatbot can't see. Ask a raw LLM "what's NYC's biggest library project?" and it'll happily double-count a budget line shared by several projects, call a long-finished branch "still under construction" because its funding line is still open, or — seeing only three reporting periods a year (Jan / May / Sep) — assume months of data have gone missing. It sounds confident — and it's wrong. This server encodes the guardrails once — the PID↔FMS many-to-many, role-aware agency attribution, the 3×-a-year reporting cadence, signed reporting — so every answer is consistent, sourced, and reproducible.
What one prompt can build
This isn't only a query tool. Point an AI agent at it and a single prompt produces a polished, self-contained interactive HTML report — with the domain rules already applied. Four real examples (one prompt → one file; click to open the live report):
1 · NYC capital projects overview — a first-timer's field guide to the dataset
Use this MCP to create a general overview of project schedule/budget and agency count for first-timer to quickly understand the coverage of the dataset, in an html file for presentation.
▶ Open the presentation — a nine-slide introduction to the dataset's schedule and budget units, reporting coverage, lifecycle mix, budget and spend, agency roles, and category coverage.
2 · Schedule ↔ Budget topology — the many-to-many anatomy of the portfolio
Analyze the schedule and budget many-to-many relationship across NYC capital projects and build a single interactive HTML report — the 1:1-vs-fan-out split, the outlier extremes, a per-agency breakdown, and budget concentration.
▶ Open the report — fan-out rings, a bipartite diagram, the "tangled few" outliers (hover to see the real schedules and budget lines), an agency scatter, and a budget concentration curve.
3 · Parks projects over $50M — every big build, and what funds it
Build an interactive one-file HTML report on NYC Parks projects over $50M. For each budget line, show every schedule associated with it, with phase and forecast completion.
▶ Open the report — 23 budget lines; hover any to reveal its linked schedules. Quietly applies the category taxonomy, so the $1.9B "Park Pedestrian Bridges" route to Bridges, not Parks.
4 · Budget & schedule change monitor — what moved this period, by agency
Build an interactive one-file HTML monitor of NYC capital projects' budget and schedule changes by managing agency, with a click-through detail view for each project's schedule and budget history.
▶ Open the report — KPIs, a trend chart, a sortable watchlist, and a per-project popup with schedule-variance bars and a stacked budget-vs-spend chart.
Each report was generated from the prompt shown, then lightly polished. The overview uses reporting period 202605; the other three use 202601. Browse all four in the report gallery.
🚀 Quick Start
Want the data without the setup? If your AI can run commands on your computer, just ask it to install everything for you.
✅ Let your AI install it (easiest)
Works with AI agents that can run terminal commands — Claude Code, Claude cowork (Claude Desktop's local-agent mode), Codex CLI, or another coding agent like Cursor.
Start your AI agent on this computer.
Paste the message below.
Approve each step (Allow, or press y) as it clones, installs, and connects the server.
Message to paste:
Install the MCP server at
https://github.com/WillHsiaoNYC/NYC-Opendata-Capital-Projects-MCP on this
machine — follow its README to clone the repo, install it with uv, run
`od-cpd init --source opendata` to download all four datasets into a local
database, and wire it into my MCP client config. If Open Data downloads
fail, explain the GitHub snapshot fallback and ask before switching to
`--source github`. Report the source, snapshot download date when applicable,
and loaded reporting periods. Then run a verification query to confirm it works.What "done" looks like: your AI reports the loaded reporting period (e.g.
202605), confirms od-cpd is connected with its 18 tools, and answers a
test question like "What's the biggest NYC capital project right now?" Takes a
few minutes, mostly the dataset download.
🖥️ Claude Desktop (chat)
Claude Desktop can use a local server but can't install one itself. Run the
Manual install, then add od-cpd to its own config (with
the absolute path to uv) and fully restart — see
Connect an MCP client.
☁️ claude.ai or ChatGPT (web)
These connect only to remote MCP servers, not a local one like this — so they
can't run od-cpd directly. Use one of the options above.
Manual install
Requires Python ≥ 3.12 and uv.
git clone https://github.com/WillHsiaoNYC/NYC-Opendata-Capital-Projects-MCP.git
cd NYC-Opendata-Capital-Projects-MCP
uv sync --locked
uv run od-cpd init # download + materialize all 4 datasets → ./var/cpd.duckdb
uv run od-cpd status # confirm the loaded reporting periodOptional: set OD_CPD_SOCRATA_APP_TOKEN to a free
Socrata app token to avoid
anonymous rate limits during ingest.
Choosing a download source
NYC Open Data is the default and supplies the current published files. The installer downloads complete CSVs through Socrata's v3 export service with bounded retries. It explicitly selects every native column as text to preserve values; ordinary website exports can format or round currency, percentages, and dates. Metadata validates the fields and maps headers automatically. Users and installing agents do not need to rename columns or transform values.
uv run od-cpd init --source opendata
uv run od-cpd init --source github # release tag data-latest
uv run od-cpd init --source github --release TAG # choose a dated snapshotGitHub snapshots are release attachments in this repository, not files in
Git history. The default data-latest tag is a maintainer-managed data snapshot;
it does not mean GitHub's latest software release. A snapshot can be older than
Open Data. Check its download date and reporting periods before choosing it.
The GitHub path needs no live Socrata access: its manifest supplies source
metadata, field mappings, row counts, byte counts, and SHA-256 checksums. Downloads
are pinned to release asset IDs in this repository. Both sources pass schema,
completeness, and database health checks before publication. An older source
revision cannot replace newer local data, even within the same reporting period;
use a separate database to explore an older snapshot.
Downloads never silently switch sources. On an Open Data failure, the CLI exits with a suggested GitHub command; it does not start an interactive prompt. An installing agent should explain that fallback and obtain the user's source choice, or honor a source choice already provided. Report the chosen source and freshness after installation. If the requested snapshot has not been published, the GitHub path fails clearly; adding this option does not itself publish assets.
Connect an MCP client
The server speaks stdio. Use the absolute path to uv (run which uv to
find it) — GUI apps like Claude Desktop don't inherit your shell PATH, so a
bare uv command fails silently.
Claude Code — from inside the repo folder:
claude mcp add od-cpd --env PYTHONPATH="$(pwd)/src" -- \
"$(which uv)" run --directory "$(pwd)" od-cpd-serverClaude Desktop — edit its config (macOS:
~/Library/Application Support/Claude/claude_desktop_config.json), add the block
below, then fully quit (⌘Q) and reopen Claude Desktop:
{
"mcpServers": {
"od-cpd": {
"command": "/absolute/path/to/uv",
"args": ["run", "--directory", "/absolute/path/to/repo", "od-cpd-server"],
"env": { "PYTHONPATH": "/absolute/path/to/repo/src" }
}
}
}(PYTHONPATH keeps the launch robust when uv's editable install is flaky — e.g.
on iCloud-synced paths.)
Keeping data fresh
The source datasets report on a Jan / May / Sep cycle, and Socrata typically publishes each period ~2.5–3 months later (so new data usually lands around April, August, and December). Source revision times and reporting periods are separate: check both before interpreting freshness.
uv run od-cpd status # local period, ingestion time and build
uv run od-cpd status --check-upstream # verify source revisions and complete periods
uv run od-cpd update # check revisions; fully reload if sources changed
uv run od-cpd update --force # fully reload even if revisions appear unchanged
uv run od-cpd update --source github # fully reload the selected GitHub snapshot
uv run od-cpd rematerialize # rebuild local raw data without downloadingEvery data refresh downloads all four datasets in full and rebuilds the
database, including historical records. There is no incremental append: Open
Data can backfill or correct older records. The default Open Data update checks
source revisions first and skips downloading when unchanged; use --force to
require a full refresh regardless. A GitHub update always downloads and rebuilds
from the selected release (--release TAG, default data-latest). Refreshing a
GitHub snapshot does not make it newer than the files its maintainer published.
Refresh and rematerialization build in an isolated shadow, validate its inputs,
then atomically replace the database. Before/after health reports remain under
var/ingest-runs/; a failed build preserves the prior database and its diagnostics.
Reconnect MCP clients after changing server code. See docs/FEATURES.md for
snapshot scopes, source-coverage reconciliation and export provenance. status
and MCP build provenance retain the download source and, for GitHub snapshots,
the release and snapshot date, including after rematerialization.
To keep it fresh automatically, schedule update (e.g. monthly via cron):
# 9am on the 1st of each month
0 9 1 * * cd /path/to/repo && uv run od-cpd updatePreparing GitHub snapshots (maintainers)
uv run od-cpd snapshot /path/to/new-snapshot-directoryThe output directory must not already exist. This command downloads and
validates all four lossless CSV exports and creates manifest.json, without
touching the live database. Assets retain the exact bytes returned by the text
projection export. The manifest marks this format as socrata-text-v1 and records
source URLs, metadata mappings, download dates, reporting periods, row counts,
byte counts, and SHA-256 checksums. Ordinary website exports are not substitutes
for these assets.
Publish those five files together as assets of a dated GitHub release in this
repository. Publishing is a separate maintainer action; the snapshot command
does not create or upload a release. Optionally promote a complete, validated
snapshot to the data-latest release. Never commit the generated CSVs to Git.
Keep dated releases available for users who pin --release TAG, and publish the
snapshot date and reporting periods in the release notes. Until a maintainer
publishes a complete snapshot, the GitHub fallback is unavailable.
What's inside
docs/FEATURES.md— the canonical inventory: all 18 tools and every domain rule the server encodes. Start here.
The headline domain rules, briefly:
"Project" is ambiguous. A PID identifies a schedule; an FMS ID identifies a budget line. They are many-to-many (~3% fan out), so the tools list all counterparts rather than silently picking one.
Agency attribution is role-aware. "Agency X's projects" means the sponsor (owner) view for normal agencies, but the managing (builder) view for the three construction-manager agencies (DDC/DCAS/EDC).
Values are reported signed and neutral ("moved 45 days later", "budget grew $2.1M") rather than only surfacing one direction.
Layout
src/od_cpd/— ingest, materialization, and the MCP server + toolsdata/— curated agency/category dictionaries (YAML, tracked)tests/— unit tests + golden evals (uv run pytest)var/,exports/— runtime DuckDB + exports (gitignored, regenerable)
Develop
uv run pytest # fallback: PYTHONPATH=src python -m pytestClassification is dictionary-driven: edit data/agencies.yaml /
data/categories.yaml (not Python) to adjust agency or category mappings,
then re-materialize. See CLAUDE.md for the atomic-swap pattern that applies
materialization changes without re-downloading.
Data caveats
This is an independent project, not affiliated with the City of New York.
Figures reflect whatever reporting period the underlying Socrata datasets
carry at ingest time; always check dataset_info for the current period and
per-dataset caveats.
Available Tools
18 toolsbudget_breakdownARead-onlyIdempotent
Total budget / spend by managing_agency or category, deduped on (fms_id,
managing_agency). Category is line-grain (additive). Optional agency scopes to one
agency; agency_role ('auto'|'sponsor'|'managing') picks owner vs builder lens. For
richer cuts use run_sql.
Interpretation rules:
[grain] Schedule questions use PID; budget questions use the BUDGET LINE key (managing_agency, fms_id). The same FMS ID under different holders is distinct lines. Schedule history is PID x reporting_period; budget history is budget line x reporting_period; fiscal-year budgets also key on fiscal_year. The schedule source has no fms_id; budget sources have no pid. The combined source repeats rows across PID-budget links and location splits: deduplicate at the requested entity grain before counting or summing. Never compare budgets using fms_id alone.
[period_basis] Reporting-period basis: state the returned period for every count, total or ranking and both periods for comparisons. Period aggregates default to the latest complete snapshot, which may precede a partially published newer period. fms_location, fms_sponsor and lifetime_budget_variance are all-history dimensions without reporting_period: use them for enrichment or lifetime figures, never as a single period's inventory.
[agency_roles] Agency attribution is role-aware: an agency's projects mean its sponsor (owner) view, except DDC/DCAS/EDC default to managing (builder). State the returned agency_scope. managing_agency is the executor on schedules and the budget-holder on budgets; a budget-only holder is not a schedule executor. list_agencies exposes is_schedule_executor. For sponsor-scoped budget totals, use a semi-join to fms_sponsor; a value-bearing join can multiply lines. Co-owner totals can overlap and must not be added together.
[categories] Classify with the curated category_dim, not project-name searches: specific ten-year labels/FMS prefixes precede sponsor routing, then generic facility keywords and Other. Categories key on (managing_agency, fms_id); institution owner rules can use all-history ownership. Schedule category_scope='current' uses each PID's current funding links; 'all_history' includes former links. A PID can count in multiple categories, so schedule category counts are non-additive; each budget line has one category.
| Name | Required | Description | Default |
|---|---|---|---|
| agency | No | ||
| metric | No | total_budget | |
| period | No | current | |
| group_by | No | managing_agency | |
| agency_role | No | auto |
Output Schema
| Name | Required | Description |
|---|---|---|
| groups | Yes | |
| metric | Yes | |
| period | Yes | |
| provenance | Yes | |
| interpretation_rules | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only/idempotent safety, and the description adds substantial behavior the annotations cannot: dedup requirements, non-additive category counts, the latest-complete-snapshot default for periods, and role-aware agency attribution. These are exactly the traits that prevent miscounting.
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?
The first sentence is tightly front-loaded and earns its place, but the 'Interpretation rules' block is a dense wall of text that folds in cross-cutting grain, schedule/PID, and category rules beyond this tool's core scope. Useful, but oversized and hard to parse at a glance.
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 formatting need not be described, and the remaining complexity (grain, dedup, roles, period basis, categories) is thoroughly addressed. An agent has enough to call and correctly interpret the result.
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?
With 0% schema coverage the description carries the burden, and it covers most params: agency ('scopes to one agency'), agency_role ('owner vs builder lens'), group_by ('by managing_agency or category'), and period via the period_basis rules. metric's two values (total_budget/spend) are only implied by the opening wording rather than defined, so full compensation is slightly incomplete.
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?
The opening sentence states a specific verb+resource ('Total budget / spend by managing_agency or category, deduped on (fms_id, managing_agency)') and even names the grouping grain, so an agent knows exactly what it returns. It routes to run_sql for richer cuts, but does not explicitly distinguish itself from the sibling schedule_breakdown, which the near-identical name makes risky.
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?
It gives explicit alternative routing ('For richer cuts use run_sql') and clear when-not guidance ('fms_location, fms_sponsor and lifetime_budget_variance... use them for enrichment or lifetime figures, never as a single period's inventory'). It also states which lens to pick via agency_role and the semi-join rule for sponsor-scoped totals, 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.
budget_changeARead-onlyIdempotent
Δ budget/spend for an agency ('agency:DEP') or FMS line ('fms:ABC') between two periods.
For an agency target, agency_role ('auto'|'sponsor'|'managing') picks the lens; sponsor
scope uses the latest-period owner set (as-of caveat in the result label). An FMS id held
by several managing agencies is several distinct budget lines: the result then lists
per-line deltas (never a cross-agency sum); pass managing_agency to scope to one line.
Interpretation rules:
[grain] Schedule questions use PID; budget questions use the BUDGET LINE key (managing_agency, fms_id). The same FMS ID under different holders is distinct lines. Schedule history is PID x reporting_period; budget history is budget line x reporting_period; fiscal-year budgets also key on fiscal_year. The schedule source has no fms_id; budget sources have no pid. The combined source repeats rows across PID-budget links and location splits: deduplicate at the requested entity grain before counting or summing. Never compare budgets using fms_id alone.
[period_basis] Reporting-period basis: state the returned period for every count, total or ranking and both periods for comparisons. Period aggregates default to the latest complete snapshot, which may precede a partially published newer period. fms_location, fms_sponsor and lifetime_budget_variance are all-history dimensions without reporting_period: use them for enrichment or lifetime figures, never as a single period's inventory.
[agency_roles] Agency attribution is role-aware: an agency's projects mean its sponsor (owner) view, except DDC/DCAS/EDC default to managing (builder). State the returned agency_scope. managing_agency is the executor on schedules and the budget-holder on budgets; a budget-only holder is not a schedule executor. list_agencies exposes is_schedule_executor. For sponsor-scoped budget totals, use a semi-join to fms_sponsor; a value-bearing join can multiply lines. Co-owner totals can overlap and must not be added together.
[budget_baseline] budget_variance is change from the previous reporting period; cumulative_budget_change is latest minus original. State which basis is used. original_budget prefers the adopted first budget, with first_snapshot as fallback: disclose original_budget_source. Adoption months are calendar months from a separate first-budget system, not reporting snapshots.
[signed_values] Report neutral, SIGNED changes: "moved 45 days later" or "budget decreased $2M". Do not echo loaded terms in the answer. "slippage" means positive schedule change and "overrun" means positive budget growth; neither includes the decreasing side. Preserve the returned direction and metric basis.
| Name | Required | Description | Default |
|---|---|---|---|
| metric | No | total_budget | |
| target | Yes | ||
| to_period | Yes | ||
| agency_role | No | auto | |
| from_period | Yes | ||
| managing_agency | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| change | Yes | |
| target | Yes | |
| provenance | Yes | |
| interpretation_rules | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/closed-world, and the description adds substantial behavioral context beyond them: as-of caveats and latest-snapshot defaults, explicit dedup warnings for the combined source, overlap warnings on co-owner totals, signed-value reporting conventions, and disclosure requirements (agency_scope, original_budget_source, period basis).
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?
The description is long, but it is front-loaded with the core operation and then organized into labeled rules, with no filler sentences. The density is justified by the domain complexity, though some interpretation rules read as output-guidance that could be trimmed.
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?
Given an output schema exists (so return formatting needn't be explained), the description covers the remaining complexity well: entity grain keying, period basis, agency-role attribution, budget baseline semantics, and signed-value direction. An agent has enough to call the tool and interpret results 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 0%, so the description must carry parameter meaning and largely does: target syntax ('agency:DEP' / 'fms:ABC'), agency_role lens values, managing_agency scoping, and metric semantics (budget vs spend). It is not fully exhaustive, though — the accepted format of from_period/to_period is never specified.
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?
The opening sentence gives a specific verb/operation ('Δ budget/spend'), names both resource types ('agency:DEP' or 'fms:ABC'), and states the temporal scope ('between two periods'). It is readily distinguishable from siblings like get_project_budget (single-project snapshot) or budget_breakdown (dimension breakdown) because it is framed strictly as a delta computation.
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?
It gives clear operating context: when to pass managing_agency to scope to one line, what agency_role does, and which grain/basis applies to budget vs schedule questions. However it never names an alternative tool (e.g., budget_breakdown or get_project_budget) or states when NOT to use this tool, so routing relies on inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
dataset_infoARead-onlyIdempotent
Per-dataset freshness, current period, row counts, and the key caveats.
Interpretation rules:
[freshness] Reporting snapshots publish three times yearly (Jan/May/Sep, YYYYMM ending 01/05/09). Reporting periods, source revisions and ingestion timestamps are different clocks. dataset_info is local-only metadata, not an upstream freshness check. Use its available periods and complete snapshot information when choosing a comparison.
[period_basis] Reporting-period basis: state the returned period for every count, total or ranking and both periods for comparisons. Period aggregates default to the latest complete snapshot, which may precede a partially published newer period. fms_location, fms_sponsor and lifetime_budget_variance are all-history dimensions without reporting_period: use them for enrichment or lifetime figures, never as a single period's inventory.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| caveats | Yes | |
| datasets | Yes | |
| provenance | Yes | |
| domain_rules | Yes | |
| interpretation_rules | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, closed-world, so the description adds substantial value by disclosing that this is local-only metadata rather than an upstream check and that the latest period may precede a partially published snapshot. That is genuine behavioral context about data staleness and scope that annotations don't cover.
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 summary line then two long bullet rules, which is reasonable, but the prose is dense and the rules are somewhat run-on, mixing several concepts per sentence. It is not excessively long but could be more scannable.
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?
Given zero parameters, a rich output schema (so return format needn't be re-explained), and annotations covering safety, the description fills the remaining gap: how to interpret freshness and period basis. It is complete for the call itself, though it doesn't tie usage to specific sibling tools.
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?
No parameters, so the schema carries nothing and the description correctly focuses on output semantics. It describes the freshness and period_basis caveats that shape how returned values should be interpreted, which is appropriate use of the space.
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 (per-dataset metadata) and enumerates what it returns (freshness, period, row counts, caveats). It's clearly distinct from sibling tools like describe_table or run_sql, though the phrasing 'Per-dataset' isn't further elaborated on what a dataset is.
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?
The interpretation rules imply when to use the returned periods ('use them for enrichment or lifetime figures, never as a single period's inventory') and warn it is local metadata, not an upstream freshness check. But it never explicitly states when to call dataset_info versus alternatives in the sibling list, nor gives a directive like 'call this before any aggregation'.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delay_reason_statsBRead-onlyIdempotent
Distribution of reason-for-delay (only populated when variance>0). Defaults to current
period; pass scope='all_history' for lifetime. Carries a coverage block (delayed_total /
with_reason / without_reason — the denominator for the distribution, counting delayed rows
by bare variance_day>0). agency_role ('auto'|'sponsor'|'managing') picks owner vs builder
lens.
Interpretation rules:
[period_basis] Reporting-period basis: state the returned period for every count, total or ranking and both periods for comparisons. Period aggregates default to the latest complete snapshot, which may precede a partially published newer period. fms_location, fms_sponsor and lifetime_budget_variance are all-history dimensions without reporting_period: use them for enrichment or lifetime figures, never as a single period's inventory.
[agency_roles] Agency attribution is role-aware: an agency's projects mean its sponsor (owner) view, except DDC/DCAS/EDC default to managing (builder). State the returned agency_scope. managing_agency is the executor on schedules and the budget-holder on budgets; a budget-only holder is not a schedule executor. list_agencies exposes is_schedule_executor. For sponsor-scoped budget totals, use a semi-join to fms_sponsor; a value-bearing join can multiply lines. Co-owner totals can overlap and must not be added together.
[schedule_coverage] Schedule totals and cumulative variance use dashboard-aligned schedule_history. source_schedule_history retains native observations absent from that population; schedule_source_coverage reconciles them. State the schedule universe and cumulative basis. Parenthesized phases are no-schedule reasons. Respect excluded variance artifacts and missing/invalid-duration counts; forward duration statistics exclude reversed dates.
[signed_values] Report neutral, SIGNED changes: "moved 45 days later" or "budget decreased $2M". Do not echo loaded terms in the answer. "slippage" means positive schedule change and "overrun" means positive budget growth; neither includes the decreasing side. Preserve the returned direction and metric basis.
| Name | Required | Description | Default |
|---|---|---|---|
| scope | No | current | |
| agency | No | ||
| period | No | current | |
| agency_role | No | auto |
Output Schema
| Name | Required | Description |
|---|---|---|
| reasons | Yes | |
| coverage | Yes | |
| provenance | Yes | |
| interpretation_rules | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/closed-world, so safety is covered. The description adds genuine behavioral detail beyond them: the coverage block contents with denominators, the variance_day>0 population rule, and the agency_role lens. It stops short of disclosing edge behavior like empty distributions or default period resolution timing, but adds meaningful 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?
The tool's own description is front-loaded well, but the appended 'Interpretation rules' block is a large generic preamble covering reporting periods, agency roles, schedule coverage, and signed values that applies broadly across the toolkit rather than this tool. It buries the specific tool semantics and hurts scannability.
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?
It provides the coverage block and enum behavior, and an output schema exists so return shape need not be explained. Yet for a tool whose core is a distribution, it omits what the aggregation is keyed on and leaves the 'period' and 'agency' parameters unexplained, so an agent still has gaps before calling 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 0% across 4 parameters, so the description must carry the burden; it explains scope ('all_history' for lifetime, default current) and agency_role enum behavior ('auto'|'sponsor'|'managing' picks owner vs builder). It says nothing about the 'period' or 'agency' parameters, so half the params remain undocumented.
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 (distribution of reason-for-delay) and scope behavior. It establishes what is being counted, but without a title and amid many aggregate stats siblings, differentiation from tools like schedule_breakdown or budget_breakdown relies on inference. Purpose is clear but sibling differentiation is thin.
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?
It states a usage condition ('only populated when variance>0') and the scope switch for all_history, which is real guidance. However, it never names when to prefer an alternative (e.g. schedule_changes) or when this distribution is not the right lens, leaving the routing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
describe_fieldARead-onlyIdempotent
Official field definitions (the NYC Open Data data dictionary): description,
allowed values, primary/foreign key, limitations, notes. Filter by field (column
name or display name) and/or dataset (RAW table name or socrata_id); omit both
for the full dictionary.
Interpretation rules:
[grain] Schedule questions use PID; budget questions use the BUDGET LINE key (managing_agency, fms_id). The same FMS ID under different holders is distinct lines. Schedule history is PID x reporting_period; budget history is budget line x reporting_period; fiscal-year budgets also key on fiscal_year. The schedule source has no fms_id; budget sources have no pid. The combined source repeats rows across PID-budget links and location splits: deduplicate at the requested entity grain before counting or summing. Never compare budgets using fms_id alone.
[lifecycle] Lifecycle: Pre-Design -> Design -> Construction Procurement -> Construction -> Close-out. Schedule progression is reported from Design through Construction. Forecasts and most actual milestones can be suppressed outside those phases; actual_construction_end is the exception and means substantial completion. NULL milestones need not mean missing data. Budget spend%=100 does not prove completion. forecast_past_due is evaluated as of the observation period, not today's date.
| Name | Required | Description | Default |
|---|---|---|---|
| field | No | ||
| dataset | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| fields | Yes | |
| provenance | Yes | |
| interpretation_rules | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and closed-world, so the safety profile is covered. The description adds output content and confirms both filters are optional, but discloses no behavioral traits an agent needs at call time (result size, pagination, truncation, whether an unknown field name errors or returns empty). The long 'Interpretation rules' block is domain guidance about the underlying data, not behavior of this tool.
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?
The opening paragraph is tight and front-loaded. But roughly 80% of the text is an 'Interpretation rules' section about data grain and project lifecycle that is tangential to invoking a two-parameter dictionary lookup and reads like reused domain context rather than tool documentation.
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 no explanation, and the description covers purpose, both filters, and the no-filter default. An agent can call this correctly from what is given; the surplus interpretation rules neither help nor hurt completeness for this specific tool.
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 0% and neither parameter is documented in the schema, so the description must carry the load — and it does: `field` accepts a column name or display name, `dataset` accepts a RAW table name or socrata_id, and both being omitted returns the full dictionary. That adds real meaning beyond the bare schema, though case-sensitivity and partial-name matching rules remain unstated.
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?
Names a specific verb+resource ('describe_field' -> official field definitions / NYC Open Data data dictionary) and enumerates exactly what is returned: description, allowed values, primary/foreign key, limitations, notes. This is clearly distinguishable from siblings like describe_table and dataset_info, which cover whole tables/datasets rather than individual columns.
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?
The description states how to scope the call ('Filter by `field` ... and/or `dataset` ... omit both for the full dictionary'), which is genuine usage guidance for the parameters. However it never says when to reach for this tool versus describe_table, dataset_info, or run_sql, so alternative 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.
describe_tableARead-onlyIdempotent
Schema catalog for every queryable DuckDB table (typed analytics tables, dims, raw mirrors): live columns + types plus curated grain and keying notes. No arg → catalog of all tables; table= (case-insensitive) → full detail. Use this instead of DESCRIBE/SHOW (blocked in run_sql). Complements describe_field (official field semantics for the 4 raw datasets).
Interpretation rules:
[grain] Schedule questions use PID; budget questions use the BUDGET LINE key (managing_agency, fms_id). The same FMS ID under different holders is distinct lines. Schedule history is PID x reporting_period; budget history is budget line x reporting_period; fiscal-year budgets also key on fiscal_year. The schedule source has no fms_id; budget sources have no pid. The combined source repeats rows across PID-budget links and location splits: deduplicate at the requested entity grain before counting or summing. Never compare budgets using fms_id alone.
[period_basis] Reporting-period basis: state the returned period for every count, total or ranking and both periods for comparisons. Period aggregates default to the latest complete snapshot, which may precede a partially published newer period. fms_location, fms_sponsor and lifetime_budget_variance are all-history dimensions without reporting_period: use them for enrichment or lifetime figures, never as a single period's inventory.
[sql_usage] Prefer purpose-built tools for supported questions. For custom SQL, use describe_table for grain/keying and describe_field for field definitions, then prefer typed tables. Raw mirrors are VARCHAR and need casts. run_sql enforces read-only access, not correct analytical grain: the caller must prevent join fan-out and choose the period/population. Disclose truncated results; use CSV/XLSX for the full result when needed.
| Name | Required | Description | Default |
|---|---|---|---|
| table | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| table | No | |
| tables | No | |
| columns | No | |
| provenance | Yes | |
| interpretation_rules | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover readOnly/idempotent, but the description adds real context beyond them: run_sql enforces read-only access but not analytical grain, the caller must prevent join fan-out, period aggregates default to a latest complete snapshot, and truncated results must be disclosed with CSV/XLSX fallback. This is genuinely non-obvious operational behavior.
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 and both calling modes are front-loaded in the first two sentences, which is exactly right. The trailing 'Interpretation rules' block is long and includes domain guidance (e.g. 'Never compare budgets using fms_id alone') that is only indirectly about describing tables, though it is clearly labeled and structured.
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 needn't be spelled out, and the description instead supplies the grain/keying semantics, period-basis rules, and SQL-usage constraints an agent needs before trusting the catalog for custom queries. Nothing essential 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 0%—the single 'table' property has no description—so the description carries the full burden and does: omitting the arg yields a catalog of all tables, and passing table=<name> (case-insensitive) yields full detail. That fully compensates for the empty 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+resource ('Schema catalog for every queryable DuckDB table') and enumerates both modes ('No arg → catalog of all tables; table=<name> → full detail'). It also names the sibling it complements (describe_field) and the tools it substitutes for (DESCRIBE/SHOW blocked in run_sql), so an agent can tell it apart 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 direction on when to use it: 'Use this instead of DESCRIBE/SHOW (blocked in run_sql)', 'Complements describe_field', and 'prefer typed tables' for custom SQL. It names alternatives and the conditions that select them (raw mirrors are VARCHAR and need casts).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_project_budgetARead-onlyIdempotent
Budget (FMS line): total, spend, variance; lists linked schedules. NB budget has no 'completed' state; spend%=100 ≠ done.
Interpretation rules:
[relationships] "Project" is ambiguous: PID identifies a SCHEDULE; FMS ID identifies a BUDGET. They are MANY-TO-MANY: a budget can fund several PIDs and a PID can have several budgets. LIST ALL linked counterparts; never silently select one. One linked counterpart does not establish a 1:1 relationship in the reverse direction. A budget without a PID is normal before Design and for lines that do not require schedules; absence alone does not establish missing data. Source-only schedules can also lack dashboard funding links.
[grain] Schedule questions use PID; budget questions use the BUDGET LINE key (managing_agency, fms_id). The same FMS ID under different holders is distinct lines. Schedule history is PID x reporting_period; budget history is budget line x reporting_period; fiscal-year budgets also key on fiscal_year. The schedule source has no fms_id; budget sources have no pid. The combined source repeats rows across PID-budget links and location splits: deduplicate at the requested entity grain before counting or summing. Never compare budgets using fms_id alone.
[population] Listings/rankings default to population_scope='latest_known' (each entity's own latest observation). Use population_scope='current' for the selected complete snapshot. State the population_scope and observation period; respect present_in_current_snapshot. Detail tools use latest-known state and each anchor's latest available link period; these links are not proof of presence in the current complete snapshot.
[budget_baseline] budget_variance is change from the previous reporting period; cumulative_budget_change is latest minus original. State which basis is used. original_budget prefers the adopted first budget, with first_snapshot as fallback: disclose original_budget_source. Adoption months are calendar months from a separate first-budget system, not reporting snapshots.
[lifecycle] Lifecycle: Pre-Design -> Design -> Construction Procurement -> Construction -> Close-out. Schedule progression is reported from Design through Construction. Forecasts and most actual milestones can be suppressed outside those phases; actual_construction_end is the exception and means substantial completion. NULL milestones need not mean missing data. Budget spend%=100 does not prove completion. forecast_past_due is evaluated as of the observation period, not today's date.
[signed_values] Report neutral, SIGNED changes: "moved 45 days later" or "budget decreased $2M". Do not echo loaded terms in the answer. "slippage" means positive schedule change and "overrun" means positive budget growth; neither includes the decreasing side. Preserve the returned direction and metric basis.
| Name | Required | Description | Default |
|---|---|---|---|
| fms_id | Yes | ||
| managing_agency | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| answer | Yes | |
| provenance | Yes | |
| linked_schedules | Yes | |
| interpretation_rules | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds far more than the readOnly/idempotent annotations: budget has no 'completed' state and spend%=100 does not prove completion, NULL milestones need not be missing, forecast_past_due is evaluated as of the observation period, and changes are strictly signed. It also warns that the combined source repeats rows and must be deduplicated before counting.
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?
The purpose sentence is well front-loaded, but the body is roughly 2,000+ characters of dense, undifferentiated prose whose numbered categories read like a data dictionary rather than tool-specific usage. Substantial material (lifecycle phases, signed-value conventions) would serve better as shared reference documentation.
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 described, and the description thoroughly covers grain, population scope, baseline basis, and lifecycle caveats. The only real gap is parameter-level detail (managing_agency optionality and identifier format) left uncovered by a 0%-documented schema.
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 0%, so the description must carry the parameter burden. It does clarify that (managing_agency, fms_id) jointly define the budget line and that the same FMS ID under different holders is a distinct line, but it never states fms_id's format or that managing_agency is optional and defaults to null.
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?
The opening clause states a specific verb and resource: 'Budget (FMS line): total, spend, variance; lists linked schedules.' The [grain] rule explicitly separates it from schedule siblings by keying budget questions on (managing_agency, fms_id) while schedule questions use PID. It is clear but the crisp purpose statement is buried under a large block of interpretation rules.
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 substantial situational guidance: default population_scope='latest_known' vs 'current', which key to use for which question grain, and instruction to list ALL linked counterparts rather than silently picking one. It never names sibling tools (budget_breakdown, budget_change) as alternatives, so it stops short of explicit routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_project_historyARead-onlyIdempotent
Period-by-period history for ONE project. Schedule lens (pid=…): each period's
phase, forecast, signed variance, delay reason + a current-state header carrying
agency_project_name, cumulative variance, and forecast_past_due (see
get_project_schedule). Budget lens (fms_id=…, case-insensitive): each period's
budget/spend/signed variance per (managing_agency, fms_id) line, the line-keyed
fms_project_name, + the adopted original budget when recorded (adoption-only lines
return header-only). Provide exactly one of pid/fms_id; managing_agency scopes a
multi-agency FMS id to one line — otherwise ALL lines are listed.
Interpretation rules:
[relationships] "Project" is ambiguous: PID identifies a SCHEDULE; FMS ID identifies a BUDGET. They are MANY-TO-MANY: a budget can fund several PIDs and a PID can have several budgets. LIST ALL linked counterparts; never silently select one. One linked counterpart does not establish a 1:1 relationship in the reverse direction. A budget without a PID is normal before Design and for lines that do not require schedules; absence alone does not establish missing data. Source-only schedules can also lack dashboard funding links.
[grain] Schedule questions use PID; budget questions use the BUDGET LINE key (managing_agency, fms_id). The same FMS ID under different holders is distinct lines. Schedule history is PID x reporting_period; budget history is budget line x reporting_period; fiscal-year budgets also key on fiscal_year. The schedule source has no fms_id; budget sources have no pid. The combined source repeats rows across PID-budget links and location splits: deduplicate at the requested entity grain before counting or summing. Never compare budgets using fms_id alone.
[population] Listings/rankings default to population_scope='latest_known' (each entity's own latest observation). Use population_scope='current' for the selected complete snapshot. State the population_scope and observation period; respect present_in_current_snapshot. Detail tools use latest-known state and each anchor's latest available link period; these links are not proof of presence in the current complete snapshot.
[budget_baseline] budget_variance is change from the previous reporting period; cumulative_budget_change is latest minus original. State which basis is used. original_budget prefers the adopted first budget, with first_snapshot as fallback: disclose original_budget_source. Adoption months are calendar months from a separate first-budget system, not reporting snapshots.
[signed_values] Report neutral, SIGNED changes: "moved 45 days later" or "budget decreased $2M". Do not echo loaded terms in the answer. "slippage" means positive schedule change and "overrun" means positive budget growth; neither includes the decreasing side. Preserve the returned direction and metric basis.
[schedule_coverage] Schedule totals and cumulative variance use dashboard-aligned schedule_history. source_schedule_history retains native observations absent from that population; schedule_source_coverage reconciles them. State the schedule universe and cumulative basis. Parenthesized phases are no-schedule reasons. Respect excluded variance artifacts and missing/invalid-duration counts; forward duration statistics exclude reversed dates.
[lifecycle] Lifecycle: Pre-Design -> Design -> Construction Procurement -> Construction -> Close-out. Schedule progression is reported from Design through Construction. Forecasts and most actual milestones can be suppressed outside those phases; actual_construction_end is the exception and means substantial completion. NULL milestones need not mean missing data. Budget spend%=100 does not prove completion. forecast_past_due is evaluated as of the observation period, not today's date.
| Name | Required | Description | Default |
|---|---|---|---|
| pid | No | ||
| fms_id | No | ||
| managing_agency | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| lines | No | |
| anchor | Yes | |
| periods | No | |
| provenance | Yes | |
| interpretation_rules | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only cover safety (readOnly, idempotent, closed-world), while the description adds substantial operational context: many-to-many PID-FMS linking that must not be collapsed to 1:1, deduplication at the requested entity grain, adoption-only lines returning header-only, original_budget_source disclosure, and that forecast_past_due is evaluated as of the observation period rather than today. These are non-obvious behaviors that materially change how results are interpreted.
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?
The opening paragraph is well front-loaded and the '[rule]' structure is legible, but the description is very large for a three-parameter tool, with much of the tail consisting of general portfolio-wide interpretation guidance (population scopes, signed-value conventions, lifecycle definitions) that is not specific to this call. Signal is present but diluted.
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, and the description nevertheless supplies everything an agent needs to query correctly: identifier selection, grain, deduplication expectations, and the two lens-specific column sets. Nothing essential is missing for a tool of this 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 description coverage is 0% and all three parameters carry only bare titles, so the description must carry the burden — and it does. It defines pid as the schedule identifier, fms_id as a case-insensitive budget line key, managing_agency as a line-scoping filter, and enforces mutual exclusivity ('exactly one of pid/fms_id').
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 ('Period-by-period history for ONE project') and immediately splits the two lenses by identifier: pid selects the schedule view, fms_id the budget view. It distinguishes itself from siblings by pointing at get_project_schedule for the linked schedule lens, so an agent can tell what it returns without opening the output 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 invocation constraints — 'Provide exactly one of pid/fms_id' — and explains that managing_agency scopes a multi-agency FMS id to a single line. It also states the grain to use per question type (schedule=PID, budget=budget line). It stops short of telling the agent when to prefer this tool over get_project_schedule/get_project_budget, so it is clear context without explicit exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_project_scheduleARead-onlyIdempotent
Schedule (PID): phase, lifecycle, signed variance, reason; lists linked budgets;
forecast_past_due flags a forecast already past as of the PID's own latest report
(never true for completed/cancelled).
Interpretation rules:
[relationships] "Project" is ambiguous: PID identifies a SCHEDULE; FMS ID identifies a BUDGET. They are MANY-TO-MANY: a budget can fund several PIDs and a PID can have several budgets. LIST ALL linked counterparts; never silently select one. One linked counterpart does not establish a 1:1 relationship in the reverse direction. A budget without a PID is normal before Design and for lines that do not require schedules; absence alone does not establish missing data. Source-only schedules can also lack dashboard funding links.
[population] Listings/rankings default to population_scope='latest_known' (each entity's own latest observation). Use population_scope='current' for the selected complete snapshot. State the population_scope and observation period; respect present_in_current_snapshot. Detail tools use latest-known state and each anchor's latest available link period; these links are not proof of presence in the current complete snapshot.
[lifecycle] Lifecycle: Pre-Design -> Design -> Construction Procurement -> Construction -> Close-out. Schedule progression is reported from Design through Construction. Forecasts and most actual milestones can be suppressed outside those phases; actual_construction_end is the exception and means substantial completion. NULL milestones need not mean missing data. Budget spend%=100 does not prove completion. forecast_past_due is evaluated as of the observation period, not today's date.
[funding_totals] attributed_budget counts a funding line fully on every PID it funds; it is not an allocated share or an independent project cost. Summing it across PIDs can double-count shared funding. For the portfolio's distinct funding total use summary.line_budget_total, which counts each (managing_agency, fms_id) once. State which budget basis is reported.
[signed_values] Report neutral, SIGNED changes: "moved 45 days later" or "budget decreased $2M". Do not echo loaded terms in the answer. "slippage" means positive schedule change and "overrun" means positive budget growth; neither includes the decreasing side. Preserve the returned direction and metric basis.
[schedule_coverage] Schedule totals and cumulative variance use dashboard-aligned schedule_history. source_schedule_history retains native observations absent from that population; schedule_source_coverage reconciles them. State the schedule universe and cumulative basis. Parenthesized phases are no-schedule reasons. Respect excluded variance artifacts and missing/invalid-duration counts; forward duration statistics exclude reversed dates.
[location] Location belongs to the BUDGET LINE, represented by fms_location. A PID inherits its funding-line boroughs: one specific borough takes precedence over Citywide, multiple specific boroughs yield 'Multiple', and only Citywide lines yield 'Citywide'. Preserve the boroughs list when several boroughs apply.
| Name | Required | Description | Default |
|---|---|---|---|
| pid | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| answer | Yes | |
| provenance | Yes | |
| linked_budgets | Yes | |
| interpretation_rules | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish readOnly/idempotent/closed-world safety, so the bar is low, yet the description adds substantive behavior: forecast_past_due is evaluated as of the observation period (never for completed/cancelled), attributed_budget double-counts shared funding, and schedule totals use dashboard-aligned schedule_history. This is meaningful context beyond the annotations, though nothing on rate limits or pagination.
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?
It is front-loaded with purpose and organized under bracketed headers ([relationships], [population], [lifecycle]), which aids scanning. However for a single-parameter read tool the volume is heavy and much of it reads as cross-cutting boilerplate likely shared across many sibling tools rather than tool-specific 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 values need not be re-explained, and the description instead supplies the interpretive domain context (lifecycle phases, funding basis, location inheritance) an agent needs to read the results correctly. It is close to complete for this tool's complexity, with the pid format being the main omission.
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?
Only one input (pid) at 0% schema coverage, so the description must compensate. It does clarify PID identity in [relationships] ('PID identifies a SCHEDULE; FMS ID identifies a BUDGET'), which partly compensates, but it never states the pid format or expected value, and the population_scope referenced in the text is not actually in this 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?
The opener 'Schedule (PID): phase, lifecycle, signed variance, reason; lists linked budgets' states the resource and the main returned fields, so an agent knows this retrieves schedule state for a PID. It does not, however, explicitly distinguish itself from close siblings like schedule_breakdown, schedule_changes, or get_project_budget, so an agent still has to infer the boundary.
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?
The interpretation rules convey how to use the output (state population_scope, report signed changes, don't trust attributed_budget as a project cost), which is real usage guidance. But there is no explicit when-to-use-this-vs-siblings statement, so tool selection among schedule_breakdown/schedule_changes/get_project_budget 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.
list_agenciesCRead-onlyIdempotent
Agency dictionary with live CPD presence + schedule-executor flag.
Interpretation rules:
[agency_roles] Agency attribution is role-aware: an agency's projects mean its sponsor (owner) view, except DDC/DCAS/EDC default to managing (builder). State the returned agency_scope. managing_agency is the executor on schedules and the budget-holder on budgets; a budget-only holder is not a schedule executor. list_agencies exposes is_schedule_executor. For sponsor-scoped budget totals, use a semi-join to fms_sponsor; a value-bearing join can multiply lines. Co-owner totals can overlap and must not be added together.
[presence] Presence in a selected reporting snapshot means reportable at that period. There is no separate active flag. Historical presence or a latest-known row does not prove presence in the current complete snapshot. A completed project can retain an open budget line for years; presence does not mean construction is in progress.
| Name | Required | Description | Default |
|---|---|---|---|
| contains | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| agencies | Yes | |
| provenance | Yes | |
| interpretation_rules | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish that this is a read-only, idempotent, closed-world tool. The description adds substantial behavioral context beyond that: how presence is determined, that there is no active flag, that historical presence is not current presence, and that role attribution and co-owner totals have non-obvious semantics.
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?
The description is front-loaded and labeled with bracketed rules, but it is dense and jargon-heavy for a one-parameter dictionary tool. Many sentences carry useful domain caveats, though some could likely live in output schema documentation instead of invocation guidance.
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 field structure need not be described. The description supplies rich domain semantics for interpreting results, but it completely omits the 'contains' parameter and offers no guidance on when to use this tool over related tools, leaving invocation context incomplete.
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?
The input schema has one parameter, 'contains', with 0% schema description coverage, and the description never mentions it. No meaning, expected format, or filtering behavior is provided for the only invocation parameter.
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?
The opening sentence identifies the resource as an 'Agency dictionary' with two specific returned fields: live CPD presence and a schedule-executor flag. That is more specific than a generic list tool, though the acronym CPD is unexplained and the description does not explicitly contrast the tool with siblings beyond domain notes.
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?
The description gives extensive interpretation rules for returned data, but it does not explicitly say when to call list_agencies versus alternatives such as run_sql, dataset_info, or list_categories. The semi-join note is about downstream SQL usage, not about when this MCP tool should be selected.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_categoriesARead-onlyIdempotent
Program/facility categories (Library, Parks & Recreation, Sewer & Water, …)
with budget-line counts and total budget. Use a category name as the category
filter on rank_projects. Categories are derived from ten_year_plan_category +
sponsor_agency + fms-id prefix — NOT managing_agency or project name.
Interpretation rules:
[categories] Classify with the curated category_dim, not project-name searches: specific ten-year labels/FMS prefixes precede sponsor routing, then generic facility keywords and Other. Categories key on (managing_agency, fms_id); institution owner rules can use all-history ownership. Schedule category_scope='current' uses each PID's current funding links; 'all_history' includes former links. A PID can count in multiple categories, so schedule category counts are non-additive; each budget line has one category.
[grain] Schedule questions use PID; budget questions use the BUDGET LINE key (managing_agency, fms_id). The same FMS ID under different holders is distinct lines. Schedule history is PID x reporting_period; budget history is budget line x reporting_period; fiscal-year budgets also key on fiscal_year. The schedule source has no fms_id; budget sources have no pid. The combined source repeats rows across PID-budget links and location splits: deduplicate at the requested entity grain before counting or summing. Never compare budgets using fms_id alone.
[period_basis] Reporting-period basis: state the returned period for every count, total or ranking and both periods for comparisons. Period aggregates default to the latest complete snapshot, which may precede a partially published newer period. fms_location, fms_sponsor and lifetime_budget_variance are all-history dimensions without reporting_period: use them for enrichment or lifetime figures, never as a single period's inventory.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| period | No | |
| categories | Yes | |
| provenance | Yes | |
| interpretation_rules | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With readOnlyHint/idempotentHint already covering the safety profile, the description still adds real behavioral context: categories are derived from specific fields, schedule counts are non-additive (a PID can appear in multiple categories), and rows must be deduplicated at the requested grain. These are non-obvious traits an agent could not infer 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 and routing are front-loaded, which is good. However, the dense interpretation-rules block runs long, mixing grain, period-basis, and category-derivation guidance into one tool entry; some of it reads as dataset-wide rules that could sit elsewhere, diluting scannability.
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, and the description compensates by covering grain, category derivation, and period-basis caveats. It is largely complete for a no-param enumerator, though the volume of interpretive rules suggests some complexity is pushed onto the reader rather than resolved.
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?
The tool takes zero parameters, so the schema carries no parameter semantics and the baseline is 4. The description instead spends its detail on grain and filtering conventions, which is appropriate for a parameterless enumerator.
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?
The opening names a specific resource (program/facility categories) with concrete examples and states the returned data (budget-line counts and total budget). The routing line 'Use a category name as the category filter on rank_projects' cleanly separates this from sibling lookup tools.
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?
It gives explicit context by naming the downstream consumer ('Use a category name as the category filter on rank_projects'), which implies when this tool is the right first step. It does not explicitly exclude alternatives like describe_table or list_agencies, so it stops short of full when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
project_duration_statsARead-onlyIdempotent
Duration distribution between two ACTUAL milestones (requires both dates). Only forward actual_design_start to actual_construction_end is supported. Negative intervals are retained in invalid_intervals and excluded from statistics; missing-date and invalid-order counts reconcile to the full latest-known population. Optional group_by returns per-group stats instead of the citywide block.
Interpretation rules:
[lifecycle] Lifecycle: Pre-Design -> Design -> Construction Procurement -> Construction -> Close-out. Schedule progression is reported from Design through Construction. Forecasts and most actual milestones can be suppressed outside those phases; actual_construction_end is the exception and means substantial completion. NULL milestones need not mean missing data. Budget spend%=100 does not prove completion. forecast_past_due is evaluated as of the observation period, not today's date.
[schedule_coverage] Schedule totals and cumulative variance use dashboard-aligned schedule_history. source_schedule_history retains native observations absent from that population; schedule_source_coverage reconciles them. State the schedule universe and cumulative basis. Parenthesized phases are no-schedule reasons. Respect excluded variance artifacts and missing/invalid-duration counts; forward duration statistics exclude reversed dates.
[population] Listings/rankings default to population_scope='latest_known' (each entity's own latest observation). Use population_scope='current' for the selected complete snapshot. State the population_scope and observation period; respect present_in_current_snapshot. Detail tools use latest-known state and each anchor's latest available link period; these links are not proof of presence in the current complete snapshot.
| Name | Required | Description | Default |
|---|---|---|---|
| group_by | No | ||
| to_milestone | No | actual_construction_end | |
| from_milestone | No | actual_design_start |
Output Schema
| Name | Required | Description |
|---|---|---|
| stats | No | |
| groups | No | |
| n_projects | Yes | |
| provenance | Yes | |
| interpretation_rules | Yes | |
| excluded_missing_dates | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover the safe-read profile, and the description exceeds that bar substantially: lifecycle phase constraints, milestone suppression behavior, NULL semantics, forecast_past_due evaluation window, invalid_intervals handling, and population_scope reconciliation. This is unusually rich behavioral disclosure for a statistics tool.
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 behavior well, but the interpretation-rules block is dense and reads like internal analyst documentation compressed into three bracketed paragraphs. Every sentence carries substance, yet the volume is heavy for a 3-parameter stats tool and the structure is more reference-note than guidance.
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?
Given an output schema exists, return values needn't be explained, and the description still manages to cover lifecycle phases, schedule coverage, and population_scope semantics – the key interpretive context an agent needs. References to schedule_source_coverage and invalid_intervals are dense but complete enough 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 0%, but the description explains the semantics of group_by (per-group stats instead of the citywide block) and the from/to milestone directionality. It doesn't explain the enum values managing_agency/borough/lifecycle_status or the observation-period interaction, so it partially compensates for the schema gap.
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: duration distribution between two actual milestones, with a named constraint that only forward actual_design_start→actual_construction_end is supported. Distinct from siblings like schedule_breakdown or get_project_schedule, though it never explicitly names an alternative.
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?
Clarifies when the tool applies (requires both dates, only forward direction supported, negative intervals retained but excluded). Gives conditions and caveats but never contrasts against sibling tools like schedule_breakdown or delay_reason_stats, so the routing guidance is implicit rather than explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
project_portfolioARead-onlyIdempotent
Cross-section listing of projects (PIDs): filter by category (see
list_categories), borough, community_board, lifecycle_status
('in_progress'|'completed'|'cancelled'), and/or agency (+agency_role lens);
rows ordered by nearest completion date (NULLs last). Each row carries schedule
state + attributed_budget; summary covers the FULL filtered set and reports
BOTH budget bases (per-PID attributed vs deduped line_budget_total). Borough
matches the PID's boroughs LIST, so multi-borough projects are found by any of
their boroughs. Rows carry forecast_past_due — a forecast already past as of
the observation period (never true for completed/cancelled). population_scope
'latest_known' (default) uses each PID's own latest row; 'current' uses values at
the selected complete snapshot. Rows expose reporting_period and current-snapshot
presence. category_scope 'current' uses current links; 'all_history' includes
former category links.
Interpretation rules:
[relationships] "Project" is ambiguous: PID identifies a SCHEDULE; FMS ID identifies a BUDGET. They are MANY-TO-MANY: a budget can fund several PIDs and a PID can have several budgets. LIST ALL linked counterparts; never silently select one. One linked counterpart does not establish a 1:1 relationship in the reverse direction. A budget without a PID is normal before Design and for lines that do not require schedules; absence alone does not establish missing data. Source-only schedules can also lack dashboard funding links.
[population] Listings/rankings default to population_scope='latest_known' (each entity's own latest observation). Use population_scope='current' for the selected complete snapshot. State the population_scope and observation period; respect present_in_current_snapshot. Detail tools use latest-known state and each anchor's latest available link period; these links are not proof of presence in the current complete snapshot.
[presence] Presence in a selected reporting snapshot means reportable at that period. There is no separate active flag. Historical presence or a latest-known row does not prove presence in the current complete snapshot. A completed project can retain an open budget line for years; presence does not mean construction is in progress.
[agency_roles] Agency attribution is role-aware: an agency's projects mean its sponsor (owner) view, except DDC/DCAS/EDC default to managing (builder). State the returned agency_scope. managing_agency is the executor on schedules and the budget-holder on budgets; a budget-only holder is not a schedule executor. list_agencies exposes is_schedule_executor. For sponsor-scoped budget totals, use a semi-join to fms_sponsor; a value-bearing join can multiply lines. Co-owner totals can overlap and must not be added together.
[categories] Classify with the curated category_dim, not project-name searches: specific ten-year labels/FMS prefixes precede sponsor routing, then generic facility keywords and Other. Categories key on (managing_agency, fms_id); institution owner rules can use all-history ownership. Schedule category_scope='current' uses each PID's current funding links; 'all_history' includes former links. A PID can count in multiple categories, so schedule category counts are non-additive; each budget line has one category.
[funding_totals] attributed_budget counts a funding line fully on every PID it funds; it is not an allocated share or an independent project cost. Summing it across PIDs can double-count shared funding. For the portfolio's distinct funding total use summary.line_budget_total, which counts each (managing_agency, fms_id) once. State which budget basis is reported.
[lifecycle] Lifecycle: Pre-Design -> Design -> Construction Procurement -> Construction -> Close-out. Schedule progression is reported from Design through Construction. Forecasts and most actual milestones can be suppressed outside those phases; actual_construction_end is the exception and means substantial completion. NULL milestones need not mean missing data. Budget spend%=100 does not prove completion. forecast_past_due is evaluated as of the observation period, not today's date.
[location] Location belongs to the BUDGET LINE, represented by fms_location. A PID inherits its funding-line boroughs: one specific borough takes precedence over Citywide, multiple specific boroughs yield 'Multiple', and only Citywide lines yield 'Citywide'. Preserve the boroughs list when several boroughs apply.
[signed_values] Report neutral, SIGNED changes: "moved 45 days later" or "budget decreased $2M". Do not echo loaded terms in the answer. "slippage" means positive schedule change and "overrun" means positive budget growth; neither includes the decreasing side. Preserve the returned direction and metric basis.
[schedule_coverage] Schedule totals and cumulative variance use dashboard-aligned schedule_history. source_schedule_history retains native observations absent from that population; schedule_source_coverage reconciles them. State the schedule universe and cumulative basis. Parenthesized phases are no-schedule reasons. Respect excluded variance artifacts and missing/invalid-duration counts; forward duration statistics exclude reversed dates.
| Name | Required | Description | Default |
|---|---|---|---|
| n | No | ||
| agency | No | ||
| borough | No | ||
| category | No | ||
| agency_role | No | auto | |
| category_scope | No | current | |
| community_board | No | ||
| lifecycle_status | No | ||
| population_scope | No | latest_known |
Output Schema
| Name | Required | Description |
|---|---|---|
| rows | Yes | |
| notes | Yes | |
| summary | Yes | |
| truncated | Yes | |
| provenance | Yes | |
| interpretation_rules | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only cover safety (readOnly, idempotent, non-open-world; no destructiveHint). The description goes far beyond that, disclosing double-counting semantics of attributed_budget, that summary covers the FULL filtered set under two budget bases, the forecast_past_due definition, multi-borough matching, signed-value reporting conventions, and suppression rules for milestones. This is unusually deep behavioral disclosure for a read-only tool.
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?
The purpose and parameter semantics are front-loaded effectively, but the body is nine dense interpretation blocks, much of which reads as dataset-wide doctrine rather than tool-specific guidance, and some of it restates itself (population_scope and presence are covered across three separate blocks). Justified by complexity, yet well past the point where every sentence clearly earns its place.
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 enumerated, and the description still identifies the key returned fields (schedule state, attributed_budget, forecast_past_due, reporting_period, current-snapshot presence). Given 9 parameters, 0% schema coverage, and a highly relational domain (PID vs FMS ID many-to-many), the interpretation rules close the gaps an agent would otherwise hit.
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 0%, so the schema contributes no meaning at all and the description must carry it. It explains borough (matches the PID's boroughs LIST, Citywide/Multiple precedence), category_scope, population_scope, agency + agency_role lens, and lifecycle_status enum values, but leaves 'community_board' as a bare filter name and says nothing about 'n' (row cap, max 500).
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?
The first clause states a specific verb+resource ('Cross-section listing of projects (PIDs)') and enumerates the exact filters the agent can apply. It explicitly routes the reader elsewhere for enumerations ('see list_categories', 'list_agencies exposes is_schedule_executor') and implicitly contrasts with 'Detail tools' used for single-anchor lookups, so it is distinguishable from siblings such as rank_projects and get_project_schedule.
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?
Rich contextual guidance: it names default population_scope, when to switch to 'current', the two category_scope modes, and how to obtain distinct totals (summary.line_budget_total vs summing attributed_budget). What it does not do is say explicitly when to choose this tool over rank_projects or schedule_breakdown, so the when-to-use-vs-sibling story is inferred rather than stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
rank_projectsARead-onlyIdempotent
Rank schedules (entity='schedule', rows=PIDs) or budgets (entity='budget', rows=FMS lines).
rank_by must be NATIVE to entity; the other domain is filter-only. Echoes ranked_entity.
Budget rank_by: total_budget | spend_to_date | spend_pct | budget_variance
(last-period delta) | cumulative_budget_change (latest - original budget).
Optional category (see list_categories) filters to one program type, e.g. 'Library'.
Optional agency scopes to one agency; agency_role ('auto'|'sponsor'|'managing') picks
the lens — 'auto' uses the owner (sponsor) view, except DDC/DCAS/EDC default to builder
(managing). Echoes agency_scope; schedule rows carry forecast_past_due — a forecast
already past as of the observation period (never true for completed/cancelled).
population_scope='latest_known' uses each entity's own latest observation;
'current' uses values at the selected complete snapshot. Rows include their
reporting_period and current-snapshot presence. category_scope='current' uses
current funding links; 'all_history' also matches removed funding links.
Interpretation rules:
[relationships] "Project" is ambiguous: PID identifies a SCHEDULE; FMS ID identifies a BUDGET. They are MANY-TO-MANY: a budget can fund several PIDs and a PID can have several budgets. LIST ALL linked counterparts; never silently select one. One linked counterpart does not establish a 1:1 relationship in the reverse direction. A budget without a PID is normal before Design and for lines that do not require schedules; absence alone does not establish missing data. Source-only schedules can also lack dashboard funding links.
[grain] Schedule questions use PID; budget questions use the BUDGET LINE key (managing_agency, fms_id). The same FMS ID under different holders is distinct lines. Schedule history is PID x reporting_period; budget history is budget line x reporting_period; fiscal-year budgets also key on fiscal_year. The schedule source has no fms_id; budget sources have no pid. The combined source repeats rows across PID-budget links and location splits: deduplicate at the requested entity grain before counting or summing. Never compare budgets using fms_id alone.
[population] Listings/rankings default to population_scope='latest_known' (each entity's own latest observation). Use population_scope='current' for the selected complete snapshot. State the population_scope and observation period; respect present_in_current_snapshot. Detail tools use latest-known state and each anchor's latest available link period; these links are not proof of presence in the current complete snapshot.
[presence] Presence in a selected reporting snapshot means reportable at that period. There is no separate active flag. Historical presence or a latest-known row does not prove presence in the current complete snapshot. A completed project can retain an open budget line for years; presence does not mean construction is in progress.
[agency_roles] Agency attribution is role-aware: an agency's projects mean its sponsor (owner) view, except DDC/DCAS/EDC default to managing (builder). State the returned agency_scope. managing_agency is the executor on schedules and the budget-holder on budgets; a budget-only holder is not a schedule executor. list_agencies exposes is_schedule_executor. For sponsor-scoped budget totals, use a semi-join to fms_sponsor; a value-bearing join can multiply lines. Co-owner totals can overlap and must not be added together.
[categories] Classify with the curated category_dim, not project-name searches: specific ten-year labels/FMS prefixes precede sponsor routing, then generic facility keywords and Other. Categories key on (managing_agency, fms_id); institution owner rules can use all-history ownership. Schedule category_scope='current' uses each PID's current funding links; 'all_history' includes former links. A PID can count in multiple categories, so schedule category counts are non-additive; each budget line has one category.
[funding_totals] attributed_budget counts a funding line fully on every PID it funds; it is not an allocated share or an independent project cost. Summing it across PIDs can double-count shared funding. For the portfolio's distinct funding total use summary.line_budget_total, which counts each (managing_agency, fms_id) once. State which budget basis is reported.
[budget_baseline] budget_variance is change from the previous reporting period; cumulative_budget_change is latest minus original. State which basis is used. original_budget prefers the adopted first budget, with first_snapshot as fallback: disclose original_budget_source. Adoption months are calendar months from a separate first-budget system, not reporting snapshots.
[signed_values] Report neutral, SIGNED changes: "moved 45 days later" or "budget decreased $2M". Do not echo loaded terms in the answer. "slippage" means positive schedule change and "overrun" means positive budget growth; neither includes the decreasing side. Preserve the returned direction and metric basis.
[schedule_coverage] Schedule totals and cumulative variance use dashboard-aligned schedule_history. source_schedule_history retains native observations absent from that population; schedule_source_coverage reconciles them. State the schedule universe and cumulative basis. Parenthesized phases are no-schedule reasons. Respect excluded variance artifacts and missing/invalid-duration counts; forward duration statistics exclude reversed dates.
[lifecycle] Lifecycle: Pre-Design -> Design -> Construction Procurement -> Construction -> Close-out. Schedule progression is reported from Design through Construction. Forecasts and most actual milestones can be suppressed outside those phases; actual_construction_end is the exception and means substantial completion. NULL milestones need not mean missing data. Budget spend%=100 does not prove completion. forecast_past_due is evaluated as of the observation period, not today's date.
| Name | Required | Description | Default |
|---|---|---|---|
| n | No | ||
| agency | No | ||
| entity | Yes | ||
| rank_by | Yes | ||
| category | No | ||
| direction | No | top | |
| agency_role | No | auto | |
| delayed_only | No | ||
| category_scope | No | current | |
| max_total_budget | No | ||
| min_total_budget | No | ||
| population_scope | No | latest_known |
Output Schema
| Name | Required | Description |
|---|---|---|
| rows | Yes | |
| label | Yes | |
| rank_by | Yes | |
| provenance | Yes | |
| ranked_entity | Yes | |
| interpretation_rules | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent, so safety is covered. The description adds rich behavioral context (echoes ranked_entity, forecast_past_due semantics, population/presence rules), but this is extended interpretation content rather than the tool's own operation-level behavior.
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?
The core operation is front-loaded but is immediately followed by a very long interpretation-rules block with many relationship/grain/presence nuances that go beyond invocation guidance. It is information-dense but not tightly scoped to the call.
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?
Given 12 params, no schema descriptions, and an output schema present, the description covers the critical rank_by and scope semantics well. Some under-documented filters and the extensive interpretation rules leave minor gaps.
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 0%, so the description must compensate and it does: it defines rank_by options per entity, category/agency/agency_role/agency_scope semantics, population_scope, and category_scope. Several filters (n, direction, delayed_only, min/max_total_budget) remain only briefly covered.
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 specific verb (rank) and resources (schedules/budgets by PID/FMS lines) and distinguishes entity modes for schedule vs budget. Sibling differentiation is implicit (e.g., vs schedule_breakdown/budget_breakdown) but not named, so it stops short of a 5.
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?
Explains when to use each entity, population_scope, and category_scope, and clarifies rank_by must be native to the entity with the other domain filter-only. No explicit alternatives/exclusions, so not a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
resolve_project_referenceARead-onlyIdempotent
Resolve any project identifier (PID, FMS ID, name, partial) → schedule+budget matches bucketed by entity. Call this first for any named-project question.
Interpretation rules:
[resolution] Call resolve_project_reference first for named-project questions. Matches identify candidate entities, not funding relationships: inspect linked_budgets/linked_schedules in the detail tools. Preserve multiple candidates rather than silently selecting one. Follow pagination next_offset when more matches are needed; disclose truncation when reporting an incomplete list.
[relationships] "Project" is ambiguous: PID identifies a SCHEDULE; FMS ID identifies a BUDGET. They are MANY-TO-MANY: a budget can fund several PIDs and a PID can have several budgets. LIST ALL linked counterparts; never silently select one. One linked counterpart does not establish a 1:1 relationship in the reverse direction. A budget without a PID is normal before Design and for lines that do not require schedules; absence alone does not establish missing data. Source-only schedules can also lack dashboard funding links.
[grain] Schedule questions use PID; budget questions use the BUDGET LINE key (managing_agency, fms_id). The same FMS ID under different holders is distinct lines. Schedule history is PID x reporting_period; budget history is budget line x reporting_period; fiscal-year budgets also key on fiscal_year. The schedule source has no fms_id; budget sources have no pid. The combined source repeats rows across PID-budget links and location splits: deduplicate at the requested entity grain before counting or summing. Never compare budgets using fms_id alone.
[presence] Presence in a selected reporting snapshot means reportable at that period. There is no separate active flag. Historical presence or a latest-known row does not prove presence in the current complete snapshot. A completed project can retain an open budget line for years; presence does not mean construction is in progress.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| query | Yes | ||
| offset | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| provenance | Yes | |
| budget_matches | Yes | |
| schedule_matches | Yes | |
| interpretation_rules | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, and closed-world behavior, but the description adds substantial domain behavior beyond that: PID vs. FMS ID ambiguity, many-to-many funding relationships, deduplication requirements, and presence semantics. This is exactly the kind of interpretation context that prevents misuse.
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?
The first sentence front-loads the purpose and scope, and the bracketed interpretation rules are structured rather than rambling. It is long for a tool definition, with some repetition of the many-to-many point, but nearly every sentence carries actionable domain guidance.
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?
Given the tool's high ambiguity (identifier resolution across schedules and budgets), the description supplies extensive interpretation rules that an agent needs to call it correctly. The existence of an output schema reduces the need to describe return values, and annotations cover the safety profile, so the remaining gaps are minor.
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 0%, so the description must compensate. It explains the 'query' parameter accepts PID, FMS ID, name, or partial, and mentions pagination via next_offset, but it never explains what 'limit' or 'offset' control in this specific tool, leaving the parameter semantics partially covered.
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?
The description states a specific verb (resolve) and resource (project identifier) and explicitly names its role: 'Call this first for any named-project question.' It distinguishes itself from sibling detail tools by clarifying that matches identify candidate entities rather than funding relationships, so an agent can tell it apart from get_project_schedule/get_project_budget 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?
It gives explicit routing guidance: call this first for named-project questions, then inspect linked_budgets/linked_schedules via detail tools. It also covers when to preserve multiple candidates, when to follow pagination, and when to disclose truncation, 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.
run_sqlA
Run a read-only SELECT against the local CPD DuckDB.
output: 'inline' (default, rows capped) | 'csv' | 'xlsx' (writes a file, returns path).
PREFER the typed tables: latest_project_state (1 row/PID, borough+boroughs,
attributed_budget), schedule_history (PID x period), budget_history (snapshot rows
at the (fms_id, managing_agency) x period grain), original_budget (adopted first
budgets), lifetime_budget_variance (per-line lifetime, original_budget_source),
schedule_budget_link (PID<->FMS edges), pid_funding (per-PID link rollup),
cumulative_schedule_variance (per-PID lifetime days), fms_sponsor (fms_id -> owner),
fms_location (line-level borough/community board/name), agency_rollup_by_period,
category_dim, agency_dim, project_budget_fy, meta, data_build,
source_schedule_history (95tx-native observations, in_dashboard flag),
schedule_source_coverage (PID-level reconciliation and both cumulative bases).
RAW mirrors (raw_project_detail, raw_budget_fy, raw_budget_history,
raw_schedule_history) are all VARCHAR. Inline results echo latest_reporting_period
and may carry period_basis_note and truncation_note.Interpretation rules:
[sql_usage] Prefer purpose-built tools for supported questions. For custom SQL, use describe_table for grain/keying and describe_field for field definitions, then prefer typed tables. Raw mirrors are VARCHAR and need casts. run_sql enforces read-only access, not correct analytical grain: the caller must prevent join fan-out and choose the period/population. Disclose truncated results; use CSV/XLSX for the full result when needed.
[relationships] "Project" is ambiguous: PID identifies a SCHEDULE; FMS ID identifies a BUDGET. They are MANY-TO-MANY: a budget can fund several PIDs and a PID can have several budgets. LIST ALL linked counterparts; never silently select one. One linked counterpart does not establish a 1:1 relationship in the reverse direction. A budget without a PID is normal before Design and for lines that do not require schedules; absence alone does not establish missing data. Source-only schedules can also lack dashboard funding links.
[grain] Schedule questions use PID; budget questions use the BUDGET LINE key (managing_agency, fms_id). The same FMS ID under different holders is distinct lines. Schedule history is PID x reporting_period; budget history is budget line x reporting_period; fiscal-year budgets also key on fiscal_year. The schedule source has no fms_id; budget sources have no pid. The combined source repeats rows across PID-budget links and location splits: deduplicate at the requested entity grain before counting or summing. Never compare budgets using fms_id alone.
[period_basis] Reporting-period basis: state the returned period for every count, total or ranking and both periods for comparisons. Period aggregates default to the latest complete snapshot, which may precede a partially published newer period. fms_location, fms_sponsor and lifetime_budget_variance are all-history dimensions without reporting_period: use them for enrichment or lifetime figures, never as a single period's inventory.
[population] Listings/rankings default to population_scope='latest_known' (each entity's own latest observation). Use population_scope='current' for the selected complete snapshot. State the population_scope and observation period; respect present_in_current_snapshot. Detail tools use latest-known state and each anchor's latest available link period; these links are not proof of presence in the current complete snapshot.
[agency_roles] Agency attribution is role-aware: an agency's projects mean its sponsor (owner) view, except DDC/DCAS/EDC default to managing (builder). State the returned agency_scope. managing_agency is the executor on schedules and the budget-holder on budgets; a budget-only holder is not a schedule executor. list_agencies exposes is_schedule_executor. For sponsor-scoped budget totals, use a semi-join to fms_sponsor; a value-bearing join can multiply lines. Co-owner totals can overlap and must not be added together.
[categories] Classify with the curated category_dim, not project-name searches: specific ten-year labels/FMS prefixes precede sponsor routing, then generic facility keywords and Other. Categories key on (managing_agency, fms_id); institution owner rules can use all-history ownership. Schedule category_scope='current' uses each PID's current funding links; 'all_history' includes former links. A PID can count in multiple categories, so schedule category counts are non-additive; each budget line has one category.
[funding_totals] attributed_budget counts a funding line fully on every PID it funds; it is not an allocated share or an independent project cost. Summing it across PIDs can double-count shared funding. For the portfolio's distinct funding total use summary.line_budget_total, which counts each (managing_agency, fms_id) once. State which budget basis is reported.
[budget_baseline] budget_variance is change from the previous reporting period; cumulative_budget_change is latest minus original. State which basis is used. original_budget prefers the adopted first budget, with first_snapshot as fallback: disclose original_budget_source. Adoption months are calendar months from a separate first-budget system, not reporting snapshots.
[schedule_coverage] Schedule totals and cumulative variance use dashboard-aligned schedule_history. source_schedule_history retains native observations absent from that population; schedule_source_coverage reconciles them. State the schedule universe and cumulative basis. Parenthesized phases are no-schedule reasons. Respect excluded variance artifacts and missing/invalid-duration counts; forward duration statistics exclude reversed dates.
[signed_values] Report neutral, SIGNED changes: "moved 45 days later" or "budget decreased $2M". Do not echo loaded terms in the answer. "slippage" means positive schedule change and "overrun" means positive budget growth; neither includes the decreasing side. Preserve the returned direction and metric basis.
[lifecycle] Lifecycle: Pre-Design -> Design -> Construction Procurement -> Construction -> Close-out. Schedule progression is reported from Design through Construction. Forecasts and most actual milestones can be suppressed outside those phases; actual_construction_end is the exception and means substantial completion. NULL milestones need not mean missing data. Budget spend%=100 does not prove completion. forecast_past_due is evaluated as of the observation period, not today's date.
[location] Location belongs to the BUDGET LINE, represented by fms_location. A PID inherits its funding-line boroughs: one specific borough takes precedence over Citywide, multiple specific boroughs yield 'Multiple', and only Citywide lines yield 'Citywide'. Preserve the boroughs list when several boroughs apply.
[presence] Presence in a selected reporting snapshot means reportable at that period. There is no separate active flag. Historical presence or a latest-known row does not prove presence in the current complete snapshot. A completed project can retain an open budget line for years; presence does not mean construction is in progress.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | ||
| output | No | inline |
Output Schema
| Name | Required | Description |
|---|---|---|
| file | No | |
| rows | No | |
| truncated | No | |
| provenance | Yes | |
| interpretation_rules | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations (readOnlyHint=false, idempotent=false, destructive=false) are consistent with the disclosed side effect that 'csv'/'xlsx' 'writes a file, returns path'; the description reconciles the 'read-only SELECT' claim with that file write rather than hiding it. It also adds behavior annotations do not carry: rows are capped inline, truncation_note is echoed, latest_reporting_period/period_basis_note may be returned, and results can be truncated so CSV/XLSX should be used for full output.
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 correctly (purpose, then output modes, then preferred tables, then rules), but the body is a very large wall of text. Long stretches on lifecycle phases, location inheritance, presence semantics and category rules are domain knowledge that applies to many sibling tools, not to invoking run_sql, and dilute the actionable SQL-authoring guidance.
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 needed, and the description instead concentrates on the things an agent cannot infer: table grains, many-to-many PID/FMS linkage, period and population bases, funding double-counting, and the requirement to disclose truncation, population_scope and period basis. For a raw-SQL escape hatch this is about as complete as it can be.
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 0%, so the description must carry both params. It fully explains the `output` enum (inline default with capped rows, csv, xlsx writes a file and returns a path), which is the one param with non-obvious behavior. The `query` param is a raw SQL string, so there is little semantic surface left, but the description compensates with grain/cast guidance ('Raw mirrors are all VARCHAR').
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 specific verb+resource+constraint: 'Run a read-only SELECT against the local CPD DuckDB.' It also distinguishes itself from the many siblings by stating 'Prefer purpose-built tools for supported questions' and naming describe_table/describe_field as the SQL-authoring helpers, so an agent can tell it apart from get_project_budget, budget_breakdown, etc.
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 routing rules: prefer purpose-built tools for supported questions, fall back to run_sql for 'custom SQL', consult describe_table for grain/keying and describe_field for definitions, and prefer typed tables over RAW VARCHAR mirrors. It even states the boundary of its own guarantee ('enforces read-only access, not correct analytical grain').
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
schedule_breakdownARead-onlyIdempotent
Counts/averages of schedule metrics by agency/sponsor/borough/phase/category.
agency scopes to one agency; agency_role ('auto'|'sponsor'|'managing') picks owner
vs builder lens (auto: sponsor, except DDC/DCAS/EDC -> managing). Category grouping
counts a PID once in EACH of its categories (non-additive). Report neutral, signed
variance.
Interpretation rules:
[grain] Schedule questions use PID; budget questions use the BUDGET LINE key (managing_agency, fms_id). The same FMS ID under different holders is distinct lines. Schedule history is PID x reporting_period; budget history is budget line x reporting_period; fiscal-year budgets also key on fiscal_year. The schedule source has no fms_id; budget sources have no pid. The combined source repeats rows across PID-budget links and location splits: deduplicate at the requested entity grain before counting or summing. Never compare budgets using fms_id alone.
[period_basis] Reporting-period basis: state the returned period for every count, total or ranking and both periods for comparisons. Period aggregates default to the latest complete snapshot, which may precede a partially published newer period. fms_location, fms_sponsor and lifetime_budget_variance are all-history dimensions without reporting_period: use them for enrichment or lifetime figures, never as a single period's inventory.
[agency_roles] Agency attribution is role-aware: an agency's projects mean its sponsor (owner) view, except DDC/DCAS/EDC default to managing (builder). State the returned agency_scope. managing_agency is the executor on schedules and the budget-holder on budgets; a budget-only holder is not a schedule executor. list_agencies exposes is_schedule_executor. For sponsor-scoped budget totals, use a semi-join to fms_sponsor; a value-bearing join can multiply lines. Co-owner totals can overlap and must not be added together.
[categories] Classify with the curated category_dim, not project-name searches: specific ten-year labels/FMS prefixes precede sponsor routing, then generic facility keywords and Other. Categories key on (managing_agency, fms_id); institution owner rules can use all-history ownership. Schedule category_scope='current' uses each PID's current funding links; 'all_history' includes former links. A PID can count in multiple categories, so schedule category counts are non-additive; each budget line has one category.
[signed_values] Report neutral, SIGNED changes: "moved 45 days later" or "budget decreased $2M". Do not echo loaded terms in the answer. "slippage" means positive schedule change and "overrun" means positive budget growth; neither includes the decreasing side. Preserve the returned direction and metric basis.
[schedule_coverage] Schedule totals and cumulative variance use dashboard-aligned schedule_history. source_schedule_history retains native observations absent from that population; schedule_source_coverage reconciles them. State the schedule universe and cumulative basis. Parenthesized phases are no-schedule reasons. Respect excluded variance artifacts and missing/invalid-duration counts; forward duration statistics exclude reversed dates.
[presence] Presence in a selected reporting snapshot means reportable at that period. There is no separate active flag. Historical presence or a latest-known row does not prove presence in the current complete snapshot. A completed project can retain an open budget line for years; presence does not mean construction is in progress.
| Name | Required | Description | Default |
|---|---|---|---|
| agency | No | ||
| metric | No | count | |
| period | No | current | |
| group_by | Yes | ||
| statistic | No | count | |
| agency_role | No | auto |
Output Schema
| Name | Required | Description |
|---|---|---|
| groups | Yes | |
| metric | Yes | |
| period | Yes | |
| provenance | Yes | |
| interpretation_rules | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Despite readOnlyHint/idempotentHint covering safety, the description adds substantial behavioral context: non-additive category counts, deduplication requirements at entity grain, signed variance direction conventions, defaulting to latest complete snapshot, and the distinction between schedule_history and source_schedule_history. This goes well beyond the annotations and is rich enough for a high score.
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?
The description is very long with a dense block of interpretation rules that resembles internal documentation rather than tool-selection guidance. Critical scoping information is buried after the opening, and the bulk of the rules concerns data-model semantics that are not strictly needed before invoking the tool.
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?
The tool has an output schema and rich annotations, but the description still supplies enough grain, period, and categorization semantics to call the tool correctly. It does not document the underlying data sources for the caller to reconcile beyond what it provides, leaving minor gaps for the six-parameter surface.
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 0% and six parameters are undocumented in the schema, so the description must compensate. It does so for several key parameters — agency_role values and auto-routing rules, period defaulting to latest snapshot, and the non-additive behavior of category grouping. It does not clarify the metric/statistic enum semantics (e.g., what 'sum' means on a variance field), leaving a gap.
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 specific verb (counts/averages) and resource (schedule metrics) plus the grouping dimensions for the scheduled breakdown. It is nearly indistinguishable in framing from the sibling budget_breakdown, but the schedule-metric focus and the explicit group_by dimensions differentiate it adequately for selection.
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?
The interpretation rules give extensive when-to-use context (segment grain, period basis, agency roles, category behavior), effectively telling the agent which analytical framing to apply. However, there is no explicit routing to siblings like budget_breakdown or rank_projects, so the agent must infer when this tool is preferable to alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
schedule_changesCRead-onlyIdempotent
Newly completed (DR1) or newly delayed projects between two periods. agency scopes
to one agency; agency_role ('auto'|'sponsor'|'managing') picks owner vs builder lens.
Interpretation rules:
[period_basis] Reporting-period basis: state the returned period for every count, total or ranking and both periods for comparisons. Period aggregates default to the latest complete snapshot, which may precede a partially published newer period. fms_location, fms_sponsor and lifetime_budget_variance are all-history dimensions without reporting_period: use them for enrichment or lifetime figures, never as a single period's inventory.
[signed_values] Report neutral, SIGNED changes: "moved 45 days later" or "budget decreased $2M". Do not echo loaded terms in the answer. "slippage" means positive schedule change and "overrun" means positive budget growth; neither includes the decreasing side. Preserve the returned direction and metric basis.
[schedule_coverage] Schedule totals and cumulative variance use dashboard-aligned schedule_history. source_schedule_history retains native observations absent from that population; schedule_source_coverage reconciles them. State the schedule universe and cumulative basis. Parenthesized phases are no-schedule reasons. Respect excluded variance artifacts and missing/invalid-duration counts; forward duration statistics exclude reversed dates.
[lifecycle] Lifecycle: Pre-Design -> Design -> Construction Procurement -> Construction -> Close-out. Schedule progression is reported from Design through Construction. Forecasts and most actual milestones can be suppressed outside those phases; actual_construction_end is the exception and means substantial completion. NULL milestones need not mean missing data. Budget spend%=100 does not prove completion. forecast_past_due is evaluated as of the observation period, not today's date.
[agency_roles] Agency attribution is role-aware: an agency's projects mean its sponsor (owner) view, except DDC/DCAS/EDC default to managing (builder). State the returned agency_scope. managing_agency is the executor on schedules and the budget-holder on budgets; a budget-only holder is not a schedule executor. list_agencies exposes is_schedule_executor. For sponsor-scoped budget totals, use a semi-join to fms_sponsor; a value-bearing join can multiply lines. Co-owner totals can overlap and must not be added together.
| Name | Required | Description | Default |
|---|---|---|---|
| agency | No | ||
| to_period | Yes | ||
| agency_role | No | auto | |
| change_type | Yes | ||
| from_period | Yes | ||
| include_cancelled | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| changes | Yes | |
| provenance | Yes | |
| interpretation_rules | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover readOnly/idempotent, so the bar is lower, and the description adds substantial non-redundant behavior: period aggregates default to the latest complete snapshot, NULL milestones are not necessarily missing, forecast_past_due is evaluated as of the observation period, and budget spend%=100 does not prove completion. These are genuinely useful semantics beyond the annotations. It does not describe the return shape, but that is expected with an output schema present.
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?
The description is very long relative to a 6-parameter tool and front-loads only the first sentence; the interpretation rules read as internal documentation rather than agent-facing guidance, with bracketed labels and dense prose that a caller cannot always act on. Several rules (semi-join to fms_sponsor, co-owner overlap) are data-model trivia rather than call-instruction.
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?
There is an output schema, so return values need not be explained, and the period/lifecycle/agency semantics are unusually thorough. However, the tool does not clarify which fields are returned for the two change_type modes or give the period string format, leaving moderate gaps for a tool that is otherwise heavily annotated with interpretation rules.
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 0%, so the description must compensate, and it does for agency ('scopes to one agency'), agency_role (defines 'auto'|'sponsor'|'managing' as owner vs builder lens and explains sponsor defaults) and change_type (completed/delayed via 'Newly completed (DR1) or newly delayed'). from_period/to_period are left without format guidance, and include_cancelled and the DR1 designation are not explained, 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?
The opening sentence does state the resource and scope: 'Newly completed (DR1) or newly delayed projects between two periods,' which maps change_type to a project set. However, the tool name is 'schedule_changes' while the description's first clause is about project completion/delay classification rather than schedule change magnitude. The bulk of the description is dense interpretation guidance that does not further clarify what the tool returns; the purpose is not clearly distinguished from siblings like schedule_breakdown, budget_change, or delay_reason_stats.
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?
There is no explicit 'use this tool when...' statement nor any mention of sibling alternatives. The interpretation rules imply analytic usage but never name an alternative or exclusion. An agent must infer when to call this versus schedule_breakdown or rank_projects.
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.
18 tool updates
v0.1.1- Changed
budget_breakdown4 fields changed- added
Input schema / properties / agency_role / enumAdded value: +[ + "auto", + "sponsor", + "managing" +] - added
Input schema / properties / group_by / enumAdded value: +[ + "managing_agency", + "category" +] - added
Input schema / properties / metric / enumAdded value: +[ + "total_budget", + "spend" +] - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$defs": { + "InterpretationRule": { + "properties": { + "id": { + "description": "Stable identifier of the domain rule.", + "title": "Id", + "type": "string" + }, + "text": { + "description": "Guidance for interpreting and reporting this tool's result.", + "title": "Text", + "type": "string" + } + }, + "required": [ + "id", + "text" + ], + "title": "InterpretationRule", + "type": "object" + } + }, + "additionalProperties": true, + "properties": { + "groups": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Groups", + "type": "array" + }, + "interpretation_rules": { + "items": { + "$ref": "#/$defs/InterpretationRule" + }, + "title": "Interpretation Rules", + "type": "array" + }, + "metric": { + "title": "Metric", + "type": "string" + }, + "period": { + "title": "Period", + "type": "string" + }, + "provenance": { + "additionalProperties": true, + "title": "Provenance", + "type": "object" + } + }, + "required": [ + "provenance", + "interpretation_rules", + "groups", + "period", + "metric" + ], + "title": "BreakdownResult", + "type": "object" +}
- Changed
budget_change4 fields changed- added
Input schema / properties / agency_role / enumAdded value: +[ + "auto", + "sponsor", + "managing" +] - added
Input schema / properties / managing_agencyAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Managing Agency" +} - added
Input schema / properties / metric / enumAdded value: +[ + "total_budget", + "spend" +] - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$defs": { + "InterpretationRule": { + "properties": { + "id": { + "description": "Stable identifier of the domain rule.", + "title": "Id", + "type": "string" + }, + "text": { + "description": "Guidance for interpreting and reporting this tool's result.", + "title": "Text", + "type": "string" + } + }, + "required": [ + "id", + "text" + ], + "title": "InterpretationRule", + "type": "object" + } + }, + "additionalProperties": true, + "properties": { + "change": { + "additionalProperties": true, + "title": "Change", + "type": "object" + }, + "interpretation_rules": { + "items": { + "$ref": "#/$defs/InterpretationRule" + }, + "title": "Interpretation Rules", + "type": "array" + }, + "provenance": { + "additionalProperties": true, + "title": "Provenance", + "type": "object" + }, + "target": { + "title": "Target", + "type": "string" + } + }, + "required": [ + "provenance", + "interpretation_rules", + "target", + "change" + ], + "title": "BudgetChangeResult", + "type": "object" +}
- Changed
dataset_info1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$defs": { + "InterpretationRule": { + "properties": { + "id": { + "description": "Stable identifier of the domain rule.", + "title": "Id", + "type": "string" + }, + "text": { + "description": "Guidance for interpreting and reporting this tool's result.", + "title": "Text", + "type": "string" + } + }, + "required": [ + "id", + "text" + ], + "title": "InterpretationRule", + "type": "object" + } + }, + "additionalProperties": true, + "properties": { + "caveats": { + "items": { + "type": "string" + }, + "title": "Caveats", + "type": "array" + }, + "datasets": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Datasets", + "type": "array" + }, + "domain_rules": { + "items": { + "type": "string" + }, + "title": "Domain Rules", + "type": "array" + }, + "interpretation_rules": { + "items": { + "$ref": "#/$defs/InterpretationRule" + }, + "title": "Interpretation Rules", + "type": "array" + }, + "provenance": { + "additionalProperties": true, + "title": "Provenance", + "type": "object" + } + }, + "required": [ + "provenance", + "interpretation_rules", + "datasets", + "domain_rules", + "caveats" + ], + "title": "DatasetInfoResult", + "type": "object" +}
- Changed
delay_reason_stats3 fields changed- added
Input schema / properties / agency_role / enumAdded value: +[ + "auto", + "sponsor", + "managing" +] - added
Input schema / properties / scope / enumAdded value: +[ + "current", + "all_history" +] - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$defs": { + "InterpretationRule": { + "properties": { + "id": { + "description": "Stable identifier of the domain rule.", + "title": "Id", + "type": "string" + }, + "text": { + "description": "Guidance for interpreting and reporting this tool's result.", + "title": "Text", + "type": "string" + } + }, + "required": [ + "id", + "text" + ], + "title": "InterpretationRule", + "type": "object" + } + }, + "additionalProperties": true, + "properties": { + "coverage": { + "additionalProperties": true, + "title": "Coverage", + "type": "object" + }, + "interpretation_rules": { + "items": { + "$ref": "#/$defs/InterpretationRule" + }, + "title": "Interpretation Rules", + "type": "array" + }, + "provenance": { + "additionalProperties": true, + "title": "Provenance", + "type": "object" + }, + "reasons": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Reasons", + "type": "array" + } + }, + "required": [ + "provenance", + "interpretation_rules", + "reasons", + "coverage" + ], + "title": "ReasonsResult", + "type": "object" +}
- Changed
describe_field1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$defs": { + "InterpretationRule": { + "properties": { + "id": { + "description": "Stable identifier of the domain rule.", + "title": "Id", + "type": "string" + }, + "text": { + "description": "Guidance for interpreting and reporting this tool's result.", + "title": "Text", + "type": "string" + } + }, + "required": [ + "id", + "text" + ], + "title": "InterpretationRule", + "type": "object" + } + }, + "additionalProperties": true, + "properties": { + "fields": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Fields", + "type": "array" + }, + "interpretation_rules": { + "items": { + "$ref": "#/$defs/InterpretationRule" + }, + "title": "Interpretation Rules", + "type": "array" + }, + "provenance": { + "additionalProperties": true, + "title": "Provenance", + "type": "object" + } + }, + "required": [ + "provenance", + "interpretation_rules", + "fields" + ], + "title": "FieldsResult", + "type": "object" +}
- Added
describe_table - Changed
get_project_budget2 fields changed- added
Input schema / properties / fms_id / minLengthAdded value: +1 - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$defs": { + "InterpretationRule": { + "properties": { + "id": { + "description": "Stable identifier of the domain rule.", + "title": "Id", + "type": "string" + }, + "text": { + "description": "Guidance for interpreting and reporting this tool's result.", + "title": "Text", + "type": "string" + } + }, + "required": [ + "id", + "text" + ], + "title": "InterpretationRule", + "type": "object" + } + }, + "additionalProperties": true, + "properties": { + "answer": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Answer", + "type": "array" + }, + "interpretation_rules": { + "items": { + "$ref": "#/$defs/InterpretationRule" + }, + "title": "Interpretation Rules", + "type": "array" + }, + "linked_schedules": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Linked Schedules", + "type": "array" + }, + "provenance": { + "additionalProperties": true, + "title": "Provenance", + "type": "object" + } + }, + "required": [ + "provenance", + "interpretation_rules", + "answer", + "linked_schedules" + ], + "title": "BudgetResult", + "type": "object" +}
- Added
get_project_history - Changed
get_project_schedule2 fields changed- added
Input schema / properties / pid / minLengthAdded value: +1 - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$defs": { + "InterpretationRule": { + "properties": { + "id": { + "description": "Stable identifier of the domain rule.", + "title": "Id", + "type": "string" + }, + "text": { + "description": "Guidance for interpreting and reporting this tool's result.", + "title": "Text", + "type": "string" + } + }, + "required": [ + "id", + "text" + ], + "title": "InterpretationRule", + "type": "object" + } + }, + "additionalProperties": true, + "properties": { + "answer": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Answer" + }, + "interpretation_rules": { + "items": { + "$ref": "#/$defs/InterpretationRule" + }, + "title": "Interpretation Rules", + "type": "array" + }, + "linked_budgets": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Linked Budgets", + "type": "array" + }, + "provenance": { + "additionalProperties": true, + "title": "Provenance", + "type": "object" + } + }, + "required": [ + "provenance", + "interpretation_rules", + "answer", + "linked_budgets" + ], + "title": "ScheduleResult", + "type": "object" +}
- Changed
list_agencies1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$defs": { + "InterpretationRule": { + "properties": { + "id": { + "description": "Stable identifier of the domain rule.", + "title": "Id", + "type": "string" + }, + "text": { + "description": "Guidance for interpreting and reporting this tool's result.", + "title": "Text", + "type": "string" + } + }, + "required": [ + "id", + "text" + ], + "title": "InterpretationRule", + "type": "object" + } + }, + "additionalProperties": true, + "properties": { + "agencies": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Agencies", + "type": "array" + }, + "interpretation_rules": { + "items": { + "$ref": "#/$defs/InterpretationRule" + }, + "title": "Interpretation Rules", + "type": "array" + }, + "provenance": { + "additionalProperties": true, + "title": "Provenance", + "type": "object" + } + }, + "required": [ + "provenance", + "interpretation_rules", + "agencies" + ], + "title": "AgenciesResult", + "type": "object" +}
- Changed
list_categories1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$defs": { + "InterpretationRule": { + "properties": { + "id": { + "description": "Stable identifier of the domain rule.", + "title": "Id", + "type": "string" + }, + "text": { + "description": "Guidance for interpreting and reporting this tool's result.", + "title": "Text", + "type": "string" + } + }, + "required": [ + "id", + "text" + ], + "title": "InterpretationRule", + "type": "object" + } + }, + "additionalProperties": true, + "properties": { + "categories": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Categories", + "type": "array" + }, + "interpretation_rules": { + "items": { + "$ref": "#/$defs/InterpretationRule" + }, + "title": "Interpretation Rules", + "type": "array" + }, + "period": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Period" + }, + "provenance": { + "additionalProperties": true, + "title": "Provenance", + "type": "object" + } + }, + "required": [ + "provenance", + "interpretation_rules", + "categories" + ], + "title": "CategoriesResult", + "type": "object" +}
- Changed
project_duration_stats4 fields changed- added
Input schema / properties / from_milestone / enumAdded value: +[ + "actual_design_start", + "actual_construction_end" +] - changed
Input schema / properties / group_by / anyOfPrevious value: -[ - { - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "managing_agency", + "borough", + "lifecycle_status" + ], + "type": "string" + }, + { + "type": "null" + } +] - added
Input schema / properties / to_milestone / enumAdded value: +[ + "actual_design_start", + "actual_construction_end" +] - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$defs": { + "InterpretationRule": { + "properties": { + "id": { + "description": "Stable identifier of the domain rule.", + "title": "Id", + "type": "string" + }, + "text": { + "description": "Guidance for interpreting and reporting this tool's result.", + "title": "Text", + "type": "string" + } + }, + "required": [ + "id", + "text" + ], + "title": "InterpretationRule", + "type": "object" + } + }, + "additionalProperties": true, + "properties": { + "excluded_missing_dates": { + "title": "Excluded Missing Dates", + "type": "integer" + }, + "groups": { + "anyOf": [ + { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Groups" + }, + "interpretation_rules": { + "items": { + "$ref": "#/$defs/InterpretationRule" + }, + "title": "Interpretation Rules", + "type": "array" + }, + "n_projects": { + "title": "N Projects", + "type": "integer" + }, + "provenance": { + "additionalProperties": true, + "title": "Provenance", + "type": "object" + }, + "stats": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Stats" + } + }, + "required": [ + "provenance", + "interpretation_rules", + "n_projects", + "excluded_missing_dates" + ], + "title": "DurationResult", + "type": "object" +}
- Changed
project_portfolio7 fields changed- added
Input schema / properties / agency_role / enumAdded value: +[ + "auto", + "sponsor", + "managing" +] - added
Input schema / properties / category_scopeAdded value: +{ + "default": "current", + "enum": [ + "current", + "all_history" + ], + "title": "Category Scope", + "type": "string" +} - changed
Input schema / properties / lifecycle_status / anyOfPrevious value: -[ - { - "type": "string" - }, - { - "type": "null" - } -]New value: +[ + { + "enum": [ + "in_progress", + "completed", + "cancelled" + ], + "type": "string" + }, + { + "type": "null" + } +] - added
Input schema / properties / n / maximumAdded value: +500 - added
Input schema / properties / n / minimumAdded value: +1 - added
Input schema / properties / population_scopeAdded value: +{ + "default": "latest_known", + "enum": [ + "latest_known", + "current" + ], + "title": "Population Scope", + "type": "string" +} - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$defs": { + "InterpretationRule": { + "properties": { + "id": { + "description": "Stable identifier of the domain rule.", + "title": "Id", + "type": "string" + }, + "text": { + "description": "Guidance for interpreting and reporting this tool's result.", + "title": "Text", + "type": "string" + } + }, + "required": [ + "id", + "text" + ], + "title": "InterpretationRule", + "type": "object" + } + }, + "additionalProperties": true, + "properties": { + "interpretation_rules": { + "items": { + "$ref": "#/$defs/InterpretationRule" + }, + "title": "Interpretation Rules", + "type": "array" + }, + "notes": { + "items": { + "type": "string" + }, + "title": "Notes", + "type": "array" + }, + "provenance": { + "additionalProperties": true, + "title": "Provenance", + "type": "object" + }, + "rows": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Rows", + "type": "array" + }, + "summary": { + "additionalProperties": true, + "title": "Summary", + "type": "object" + }, + "truncated": { + "title": "Truncated", + "type": "boolean" + } + }, + "required": [ + "provenance", + "interpretation_rules", + "rows", + "summary", + "truncated", + "notes" + ], + "title": "PortfolioResult", + "type": "object" +}
- Changed
rank_projects9 fields changed- added
Input schema / properties / agency_role / enumAdded value: +[ + "auto", + "sponsor", + "managing" +] - added
Input schema / properties / category_scopeAdded value: +{ + "default": "current", + "enum": [ + "current", + "all_history" + ], + "title": "Category Scope", + "type": "string" +} - added
Input schema / properties / direction / enumAdded value: +[ + "top", + "bottom" +] - added
Input schema / properties / entity / enumAdded value: +[ + "schedule", + "budget" +] - added
Input schema / properties / n / maximumAdded value: +500 - added
Input schema / properties / n / minimumAdded value: +1 - added
Input schema / properties / population_scopeAdded value: +{ + "default": "latest_known", + "enum": [ + "latest_known", + "current" + ], + "title": "Population Scope", + "type": "string" +} - added
Input schema / properties / rank_by / enumAdded value: +[ + "period_variance_days", + "cumulative_variance_days", + "total_budget", + "spend_to_date", + "spend_pct", + "budget_variance", + "cumulative_budget_change" +] - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$defs": { + "InterpretationRule": { + "properties": { + "id": { + "description": "Stable identifier of the domain rule.", + "title": "Id", + "type": "string" + }, + "text": { + "description": "Guidance for interpreting and reporting this tool's result.", + "title": "Text", + "type": "string" + } + }, + "required": [ + "id", + "text" + ], + "title": "InterpretationRule", + "type": "object" + } + }, + "additionalProperties": true, + "properties": { + "interpretation_rules": { + "items": { + "$ref": "#/$defs/InterpretationRule" + }, + "title": "Interpretation Rules", + "type": "array" + }, + "label": { + "title": "Label", + "type": "string" + }, + "provenance": { + "additionalProperties": true, + "title": "Provenance", + "type": "object" + }, + "rank_by": { + "enum": [ + "period_variance_days", + "cumulative_variance_days", + "total_budget", + "spend_to_date", + "spend_pct", + "budget_variance", + "cumulative_budget_change" + ], + "title": "Rank By", + "type": "string" + }, + "ranked_entity": { + "enum": [ + "schedule", + "budget" + ], + "title": "Ranked Entity", + "type": "string" + }, + "rows": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Rows", + "type": "array" + } + }, + "required": [ + "provenance", + "interpretation_rules", + "ranked_entity", + "rank_by", + "rows", + "label" + ], + "title": "RankingResult", + "type": "object" +}
- Changed
resolve_project_reference4 fields changed- added
Input schema / properties / limitAdded value: +{ + "default": 50, + "maximum": 500, + "minimum": 1, + "title": "Limit", + "type": "integer" +} - added
Input schema / properties / offsetAdded value: +{ + "default": 0, + "minimum": 0, + "title": "Offset", + "type": "integer" +} - added
Input schema / properties / query / minLengthAdded value: +1 - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$defs": { + "InterpretationRule": { + "properties": { + "id": { + "description": "Stable identifier of the domain rule.", + "title": "Id", + "type": "string" + }, + "text": { + "description": "Guidance for interpreting and reporting this tool's result.", + "title": "Text", + "type": "string" + } + }, + "required": [ + "id", + "text" + ], + "title": "InterpretationRule", + "type": "object" + } + }, + "additionalProperties": true, + "properties": { + "budget_matches": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Budget Matches", + "type": "array" + }, + "interpretation_rules": { + "items": { + "$ref": "#/$defs/InterpretationRule" + }, + "title": "Interpretation Rules", + "type": "array" + }, + "provenance": { + "additionalProperties": true, + "title": "Provenance", + "type": "object" + }, + "schedule_matches": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Schedule Matches", + "type": "array" + } + }, + "required": [ + "provenance", + "interpretation_rules", + "schedule_matches", + "budget_matches" + ], + "title": "ResolutionResult", + "type": "object" +}
- Changed
run_sql2 fields changed- added
Input schema / properties / output / enumAdded value: +[ + "inline", + "csv", + "xlsx" +] - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$defs": { + "InterpretationRule": { + "properties": { + "id": { + "description": "Stable identifier of the domain rule.", + "title": "Id", + "type": "string" + }, + "text": { + "description": "Guidance for interpreting and reporting this tool's result.", + "title": "Text", + "type": "string" + } + }, + "required": [ + "id", + "text" + ], + "title": "InterpretationRule", + "type": "object" + } + }, + "additionalProperties": true, + "properties": { + "file": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "File" + }, + "interpretation_rules": { + "items": { + "$ref": "#/$defs/InterpretationRule" + }, + "title": "Interpretation Rules", + "type": "array" + }, + "provenance": { + "additionalProperties": true, + "title": "Provenance", + "type": "object" + }, + "rows": { + "anyOf": [ + { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Rows" + }, + "truncated": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Truncated" + } + }, + "required": [ + "provenance", + "interpretation_rules" + ], + "title": "SQLResult", + "type": "object" +}
- Changed
schedule_breakdown5 fields changed- added
Input schema / properties / agency_role / enumAdded value: +[ + "auto", + "sponsor", + "managing" +] - added
Input schema / properties / group_by / enumAdded value: +[ + "managing_agency", + "sponsor_agency", + "borough", + "phase_norm", + "lifecycle_status", + "category" +] - added
Input schema / properties / metric / enumAdded value: +[ + "count", + "schedule_variance" +] - added
Input schema / properties / statistic / enumAdded value: +[ + "count", + "mean", + "median", + "sum", + "min", + "max" +] - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$defs": { + "InterpretationRule": { + "properties": { + "id": { + "description": "Stable identifier of the domain rule.", + "title": "Id", + "type": "string" + }, + "text": { + "description": "Guidance for interpreting and reporting this tool's result.", + "title": "Text", + "type": "string" + } + }, + "required": [ + "id", + "text" + ], + "title": "InterpretationRule", + "type": "object" + } + }, + "additionalProperties": true, + "properties": { + "groups": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Groups", + "type": "array" + }, + "interpretation_rules": { + "items": { + "$ref": "#/$defs/InterpretationRule" + }, + "title": "Interpretation Rules", + "type": "array" + }, + "metric": { + "title": "Metric", + "type": "string" + }, + "period": { + "title": "Period", + "type": "string" + }, + "provenance": { + "additionalProperties": true, + "title": "Provenance", + "type": "object" + } + }, + "required": [ + "provenance", + "interpretation_rules", + "groups", + "period", + "metric" + ], + "title": "BreakdownResult", + "type": "object" +}
- Changed
schedule_changes3 fields changed- added
Input schema / properties / agency_role / enumAdded value: +[ + "auto", + "sponsor", + "managing" +] - added
Input schema / properties / change_type / enumAdded value: +[ + "completed", + "delayed" +] - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$defs": { + "InterpretationRule": { + "properties": { + "id": { + "description": "Stable identifier of the domain rule.", + "title": "Id", + "type": "string" + }, + "text": { + "description": "Guidance for interpreting and reporting this tool's result.", + "title": "Text", + "type": "string" + } + }, + "required": [ + "id", + "text" + ], + "title": "InterpretationRule", + "type": "object" + } + }, + "additionalProperties": true, + "properties": { + "changes": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "title": "Changes", + "type": "array" + }, + "interpretation_rules": { + "items": { + "$ref": "#/$defs/InterpretationRule" + }, + "title": "Interpretation Rules", + "type": "array" + }, + "provenance": { + "additionalProperties": true, + "title": "Provenance", + "type": "object" + } + }, + "required": [ + "provenance", + "interpretation_rules", + "changes" + ], + "title": "ChangesResult", + "type": "object" +}
16 tool updates
v0.1.0- First observed
budget_breakdown - First observed
budget_change - First observed
dataset_info - First observed
delay_reason_stats - First observed
describe_field - First observed
get_project_budget - First observed
get_project_schedule - First observed
list_agencies - First observed
list_categories - First observed
project_duration_stats - First observed
project_portfolio - First observed
rank_projects - First observed
resolve_project_reference - First observed
run_sql - First observed
schedule_breakdown - First observed
schedule_changes
TDQS
Scored across 18 tools
Tools are cleanly split by domain (schedule vs budget) and by function (detail, history, breakdown, change, duration), so most purposes are distinct. There is mild overlap between rank_projects and project_portfolio (both listing/filtering entities) and between the breakdown/rank tools for aggregate cuts, but the descriptions disambiguate these well enough.
Names are consistently snake_case throughout. The only deviation is stylistic: some use verb_noun (get_project_schedule, list_agencies, resolve_project_reference, rank_projects) while others are noun-based (schedule_breakdown, dataset_info, project_portfolio), but this remains predictable and readable.
18 tools is slightly heavy but justified for a rich analytical domain spanning schedule and budget lenses with detail, history, change, duration, and aggregate operations. Each tool appears to earn its place, with run_sql serving as the general escape hatch.
The surface covers the full read-only analytical lifecycle: resolution, entity detail, per-period history, breakdowns, period-over-period changes, duration statistics, portfolio listing, rankings, dictionaries, schema/field introspection, and an arbitrary SQL fallback. No obvious gaps for a read-only open-data server.
Maintenance
Related MCP Connectors
US government data as clean JSON for AI agents: SAM.gov contract opportunities, USAspending awards, Grants.gov grants, House STOCK Act trades, and SEC EDGAR filings (Form 4 insider trades, 8-K events, 13F holdings, 13D/G stakes, XBRL fundamentals, 10-K/10-Q sections). 19 read-only tools. Data is as fresh as each source publishes; congressional trades lag up to 45 days and report dollar ranges (House only). Free tier, no card.
SaaS intelligence for AI agents. 5 unified tools cover 1,000+ services with 91-96% token savings.
MCP facade over the Nebelus Construction API. ~48 tools give full agent build parity: create/update/probe agents, edit graphs, attach knowledge and vector stores, wire connectors, set governance policies and locked guardrails, enable grounding-trace, and read deployment wiring. Purpose-built for regulated industries: data residency is enforced per region (EU / GCC-KSA), with PII controls and an audit trail. Agents are created as drafts — no deploy tool is exposed over MCP by design; publishing happens in the Nebelus console.
Connect AI agents to financial institution origination, analytics, and compliance workflows.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceGoverned data pipeline for NBA ticketing and attendance data, exposing typed tools for AI agents to query game performance and seat-tier sales without direct database access.-
- AlicenseNot gradedqualityAmaintenanceEnables AI clients to interact with OfficeRnD coworking space management platform via natural language, exposing 130+ tools for CRUD operations and business logic.MIT
- FlicenseNot gradedqualityDmaintenanceProvides 19 tools for discovery, data analysis, configuration, and simulation, enabling an agent to drive digital twin workflows with session persistence and knowledge graph capabilities.1-
- AlicenseAqualityAmaintenanceA MCP server exposing 95 US government open-data tools as agent-callable tools, enabling direct access to official APIs for environmental, financial, licensing, and infrastructure data with honest run-status reporting.815101 npmMIT