@cliwant/mcp-sam-gov 1.14.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +3 -2
- package/README.ko.md +3 -2
- package/README.md +7 -5
- package/dist/arcgis-feature.d.ts.map +1 -1
- package/dist/arcgis-feature.js +8 -0
- package/dist/arcgis-feature.js.map +1 -1
- package/dist/bonfire.d.ts +1 -1
- package/dist/bonfire.d.ts.map +1 -1
- package/dist/bonfire.js +19 -17
- package/dist/bonfire.js.map +1 -1
- package/dist/ckan.d.ts +6 -4
- package/dist/ckan.d.ts.map +1 -1
- package/dist/ckan.js +17 -4
- package/dist/ckan.js.map +1 -1
- package/dist/data-map.d.ts +43 -0
- package/dist/data-map.d.ts.map +1 -0
- package/dist/data-map.js +344 -0
- package/dist/data-map.js.map +1 -0
- package/dist/fpds.d.ts.map +1 -1
- package/dist/fpds.js +3 -2
- package/dist/fpds.js.map +1 -1
- package/dist/gao.d.ts.map +1 -1
- package/dist/gao.js +17 -6
- package/dist/gao.js.map +1 -1
- package/dist/gsa-perdiem.d.ts +5 -0
- package/dist/gsa-perdiem.d.ts.map +1 -1
- package/dist/gsa-perdiem.js +6 -1
- package/dist/gsa-perdiem.js.map +1 -1
- package/dist/open-checkbook.d.ts +8 -0
- package/dist/open-checkbook.d.ts.map +1 -1
- package/dist/open-checkbook.js +9 -1
- package/dist/open-checkbook.js.map +1 -1
- package/dist/pricing.d.ts +1 -0
- package/dist/pricing.d.ts.map +1 -1
- package/dist/pricing.js +135 -50
- package/dist/pricing.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +76 -24
- package/dist/server.js.map +1 -1
- package/dist/socrata.d.ts +16 -1
- package/dist/socrata.d.ts.map +1 -1
- package/dist/socrata.js +76 -1
- package/dist/socrata.js.map +1 -1
- package/package.json +1 -1
- package/src/arcgis-feature.ts +8 -0
- package/src/bonfire.ts +19 -17
- package/src/ckan.ts +17 -4
- package/src/data-map.ts +386 -0
- package/src/fpds.ts +3 -2
- package/src/gao.ts +20 -6
- package/src/gsa-perdiem.ts +12 -1
- package/src/open-checkbook.ts +26 -3
- package/src/pricing.ts +142 -48
- package/src/server.ts +88 -23
- package/src/socrata.ts +83 -1
package/dist/server.js
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
20
20
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
21
|
-
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
21
|
+
import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
22
22
|
import { z } from "zod";
|
|
23
23
|
import { SamGovClient, daysUntilResponse, applyResponseDeadlineWindow, } from "./sam-gov/index.js";
|
|
24
24
|
import * as usas from "./usaspending.js";
|
|
@@ -86,14 +86,23 @@ import * as keys from "./keys.js";
|
|
|
86
86
|
import { toToolError, ToolErrorCarrier, errorFromResponse } from "./errors.js";
|
|
87
87
|
import * as feedback from "./feedback.js";
|
|
88
88
|
import { checkForUpdate } from "./update-check.js";
|
|
89
|
+
import { renderDataMapMarkdown } from "./data-map.js";
|
|
89
90
|
import { buildMeta, isMetaBundle, withMeta, } from "./meta.js";
|
|
90
91
|
import { pathToFileURL, fileURLToPath } from "node:url";
|
|
91
92
|
import { realpathSync } from "node:fs";
|
|
92
93
|
import { resolveToolsets, ALL_TOOLSET_NAMES, TOOLSET_HINTS, filterToolsFor, toolNotLoadedEnvelope, } from "./toolsets.js";
|
|
94
|
+
// Where a state-level bid feed lives when it is NOT on OpenGov or Bonfire. Measured,
|
|
95
|
+
// not cosmetic: asked for "Illinois state solicitations", an eval agent searched
|
|
96
|
+
// opengov_list_governments then bonfire_list_organizations, found no state-level IL
|
|
97
|
+
// portal in either, and never reached data.illinois.gov 6rb8-ntpm (in one run it answered
|
|
98
|
+
// with generic portal advice after a single call; in another it ran out of budget). These are
|
|
99
|
+
// the tools an agent reaches for when it thinks "solicitations", so the pointer goes
|
|
100
|
+
// here rather than only on socrata_query, which it never opened.
|
|
101
|
+
const STATE_BID_FEEDS_POINTER = "STATE-LEVEL bid feeds on open-data portals are NOT in this directory: TX TxDOT lettings, advertised and taking bids = socrata_query data.texas.gov qh8x-rm8r; IL CDB capital bids that are ANTICIPATED and NOT YET POSTED = socrata_query data.illinois.gov 6rb8-ntpm (~48 rows, not IL's full register). Map: resource samgov://data-map/state-local.";
|
|
93
102
|
const SERVER_NAME = "mcp-sam-gov";
|
|
94
103
|
// Kept in lockstep with package.json / manifest.json / server.json.
|
|
95
104
|
// Keep in sync with package.json "version" (asserted at release; see CHANGELOG).
|
|
96
|
-
const SERVER_VERSION = "1.
|
|
105
|
+
const SERVER_VERSION = "1.16.0";
|
|
97
106
|
// ─── Tool input schemas (Zod) ────────────────────────────────────
|
|
98
107
|
const SamSearchInput = z.object({
|
|
99
108
|
query: z.string().optional().describe("Free-text title query"),
|
|
@@ -584,11 +593,15 @@ const WageSearchInput = z.object({
|
|
|
584
593
|
state: z
|
|
585
594
|
.string()
|
|
586
595
|
.optional()
|
|
587
|
-
.describe("2-letter USPS state code (e.g. '
|
|
596
|
+
.describe("2-letter USPS state code (e.g. 'IL'), applied SERVER-SIDE. A full name is applied client-side instead."),
|
|
588
597
|
county: z
|
|
589
598
|
.string()
|
|
590
599
|
.optional()
|
|
591
|
-
.describe("County name (substring match),
|
|
600
|
+
.describe("County name (substring match, e.g. 'Cook'). When given, ALL pages for the state are scanned before filtering so no WDs are missed."),
|
|
601
|
+
constructionType: z
|
|
602
|
+
.enum(["Building", "Residential", "Heavy", "Highway"])
|
|
603
|
+
.optional()
|
|
604
|
+
.describe("DBA construction type (Building | Residential | Heavy | Highway). This is the PRIMARY key for a Davis-Bacon lookup: pass state + county + constructionType to pinpoint the correct WD. E.g. Building = federal buildings, schools; Heavy = bridges, utilities; Highway = roads."),
|
|
592
605
|
query: z
|
|
593
606
|
.string()
|
|
594
607
|
.optional()
|
|
@@ -598,8 +611,8 @@ const WageSearchInput = z.object({
|
|
|
598
611
|
.boolean()
|
|
599
612
|
.optional()
|
|
600
613
|
.describe("Only standard (non-non-standard) WDs (default true)."),
|
|
601
|
-
limit: z.number().min(1).max(50).optional().describe("Page size (default 20, max 50)."),
|
|
602
|
-
page: z.number().min(0).optional().describe("0-based page index (default 0)."),
|
|
614
|
+
limit: z.number().min(1).max(50).optional().describe("Page size (default 20, max 50). Ignored when county or constructionType is given — a full-state scan is performed instead."),
|
|
615
|
+
page: z.number().min(0).optional().describe("0-based page index (default 0). Ignored when county or constructionType is given — a full-state scan is performed instead."),
|
|
603
616
|
});
|
|
604
617
|
const WageRatesInput = z.object({
|
|
605
618
|
reference: z
|
|
@@ -1568,7 +1581,8 @@ const EdgarCompanyConceptInput = z.object({
|
|
|
1568
1581
|
// validated strings (a bad column ⇒ upstream 400 ⇒ invalid_input, surfaced).
|
|
1569
1582
|
const SocrataDomainEnum = z
|
|
1570
1583
|
.enum(socrata.SOCRATA_DOMAINS)
|
|
1571
|
-
.describe("Which allowlisted Socrata portal to query (
|
|
1584
|
+
.describe("Which allowlisted Socrata portal to query (the SSRF host allowlist — no free host). " +
|
|
1585
|
+
"Jurisdiction of non-obvious hosts: cthru.data.socrata.com=MASSACHUSETTS statewide (CTHRU); atlanta.data.socrata.com=Atlanta GA; controllerdata.lacity.org+data.lacity.org=Los Angeles; www.dallasopendata.com=Dallas TX; data.brla.gov=Baton Rouge LA; data.kcmo.org=Kansas City MO; data.cstx.gov=College Station TX; data.weho.org=West Hollywood CA; opendata.usac.org+datahub.usac.org=federal USAC E-rate. data.colorado.gov's procurement data is CITY OF DENVER, not CO state.");
|
|
1572
1586
|
const SocrataQueryInput = z.object({
|
|
1573
1587
|
domain: SocrataDomainEnum,
|
|
1574
1588
|
datasetId: z
|
|
@@ -1585,7 +1599,12 @@ const SocrataQueryInput = z.object({
|
|
|
1585
1599
|
select: z
|
|
1586
1600
|
.string()
|
|
1587
1601
|
.optional()
|
|
1588
|
-
.describe("Optional SoQL $select (column projection
|
|
1602
|
+
.describe("Optional SoQL $select (column projection or aggregate). " +
|
|
1603
|
+
"For a TOTAL: 'sum(amount)' (add a where for vendor/fiscal-year filter). " +
|
|
1604
|
+
"For a TOP-N ranking: 'vendor_name, sum(amount) as total' — pair with order='total DESC'. " +
|
|
1605
|
+
"Any SoQL function call (sum/count/avg/min/max) or 'distinct' activates aggregate mode: " +
|
|
1606
|
+
"totalAvailable becomes null (no raw-row total for aggregates) and the count(*) companion is skipped. " +
|
|
1607
|
+
"NEVER sum rows from one page to get a total — always use an aggregate select."),
|
|
1589
1608
|
where: z
|
|
1590
1609
|
.string()
|
|
1591
1610
|
.optional()
|
|
@@ -1620,8 +1639,9 @@ const SocrataDiscoverDatasetsInput = z.object({
|
|
|
1620
1639
|
q: z
|
|
1621
1640
|
.string()
|
|
1622
1641
|
.min(1)
|
|
1623
|
-
.describe("
|
|
1624
|
-
domain: SocrataDomainEnum.optional().describe("Optional: scope discovery to ONE
|
|
1642
|
+
.describe("TOPIC words only, e.g. 'solicitations', 'contract', 'vendor payments', 'checkbook'. Put the JURISDICTION in `domain`, never in q: every q term must match and dataset titles rarely repeat their place name, so q='Illinois solicitations' returns 0 while domain=data.illinois.gov q='solicitations' finds the dataset."),
|
|
1643
|
+
domain: SocrataDomainEnum.optional().describe("Optional: scope discovery to ONE portal; omit to search all. The catalog does not index every host (USAC returns 0); those stay queryable via socrata_query with a known 4x4. " +
|
|
1644
|
+
"Jurisdiction of non-obvious hosts: cthru.data.socrata.com=MASSACHUSETTS statewide (CTHRU); atlanta.data.socrata.com=Atlanta GA; controllerdata.lacity.org+data.lacity.org=Los Angeles; www.dallasopendata.com=Dallas TX; data.brla.gov=Baton Rouge LA; data.kcmo.org=Kansas City MO; data.cstx.gov=College Station TX; data.weho.org=West Hollywood CA; opendata.usac.org+datahub.usac.org=federal USAC E-rate. data.colorado.gov's procurement data is CITY OF DENVER, not CO state."),
|
|
1625
1645
|
limit: z
|
|
1626
1646
|
.number()
|
|
1627
1647
|
.int()
|
|
@@ -2598,7 +2618,7 @@ const BonfireSearchOpportunitiesInput = z.object({
|
|
|
2598
2618
|
const OpenCheckbookSearchInput = z.object({
|
|
2599
2619
|
portal: z
|
|
2600
2620
|
.enum(openCheckbook.OPEN_CHECKBOOK_PORTALS.map((p) => p.key))
|
|
2601
|
-
.describe("The curated Open-Checkbook portal (SSRF allowlist enum). 'sd' = State of South Dakota Open Checkbook (~740,980 vendor payments, ~$8.41B, ~3 most-recent fiscal years)."),
|
|
2621
|
+
.describe("The curated Open-Checkbook portal (SSRF allowlist enum). 'sd' = State of South Dakota Open Checkbook (~740,980 vendor payments, ~$8.41B, ~3 most-recent fiscal years). 'ak' = State of Alaska Open Checkbook (41,751 payments, $1.18B — FY2026 ONLY; FY2019–2025 return count:0 meaning not published, NOT zero spend)."),
|
|
2602
2622
|
year: z.string().min(1).max(40).optional().describe("Fiscal-year filter (EXACT match), e.g. '2025'. Default 'All Years' = the exposed ~3-year window (NOT full history)."),
|
|
2603
2623
|
vendor: z.string().min(1).max(200).optional().describe("Vendor name filter (EXACT match, e.g. 'US BANK NA' → 917). A partial/misspelled value returns an honest count:0."),
|
|
2604
2624
|
org: z.string().min(1).max(200).optional().describe("Department filter (org1, EXACT match, e.g. 'TRANSPORTATION' → 109,887)."),
|
|
@@ -2625,7 +2645,7 @@ const TableauViewCsvInput = z.object({
|
|
|
2625
2645
|
const ArcgisFeatureQueryInput = z.object({
|
|
2626
2646
|
service: z
|
|
2627
2647
|
.enum(arcgisFeature.ARCGIS_SERVICES.map((s) => s.key))
|
|
2628
|
-
.describe("Service key (SSRF allowlist;
|
|
2648
|
+
.describe("Service key (SSRF allowlist; 29 services). DC OCP PASS (solicitations/contracts/purchase_orders/payments). US local govs: Asheville NC, Bellevue WA, Miami-Dade FL×2, Suffolk County NY, Mat-Su AK, Las Vegas NV×2, Baltimore MD, Naperville IL, Worcester MA, Topeka KS (FY2015–23), Hennepin County MN (CIP pipeline), Charlotte-Mecklenburg NC (CIP pipeline); TX/AK/IA/OK DOT bid/award registers. ND DOT flex-funding to local agencies (nddot_flex×4 — NOT vendor contracts)."),
|
|
2629
2649
|
where: z
|
|
2630
2650
|
.string()
|
|
2631
2651
|
.min(1)
|
|
@@ -3717,7 +3737,7 @@ const GsaPerdiemRatesInput = z
|
|
|
3717
3737
|
.string()
|
|
3718
3738
|
.regex(/^\d{4}$/)
|
|
3719
3739
|
.optional()
|
|
3720
|
-
.describe("
|
|
3740
|
+
.describe("U.S. federal fiscal year number (Oct 1–Sep 30). Default: current FY at call time. IMPORTANT: October 2026 = FY2027; September 2026 = FY2026. To get October 2026 rates, pass year='2027'. Validated ^\d{4}$ (it rides in the request path)."),
|
|
3721
3741
|
})
|
|
3722
3742
|
.describe("Look up GSA per-diem rates by EITHER (city + state) OR zip. Supplying both, or neither, ⇒ invalid_input.");
|
|
3723
3743
|
// ─── US DOL Data API v4 (apiprod.dol.gov) — the labor-enforcement lane ──
|
|
@@ -4729,7 +4749,7 @@ export const TOOLS = [
|
|
|
4729
4749
|
// ━━━ Pricing / Wage (3) ━━━
|
|
4730
4750
|
defineTool({
|
|
4731
4751
|
name: "sam_search_wage_determinations",
|
|
4732
|
-
description: "Find the Service Contract Act (SCA) or Davis-Bacon (DBA) wage determination(s)
|
|
4752
|
+
description: "Find the Service Contract Act (SCA) or Davis-Bacon Act (DBA) wage determination(s) for a locality (keyless SAM SGS). For a Davis-Bacon lookup pass state + county + constructionType (e.g. 'IL', 'Cook', 'Building') — the tool scans ALL pages for the state so no WDs are missed, ranks single-county WDs first (the most specific match), and reports real match counts. Then call sam_get_wage_rates on the top result to read the rate table. SCA: pass state + county. constructionType is DBA-only: Building (federal buildings/schools), Residential, Heavy (bridges/utilities), Highway (roads). NOTE: `query` matches WD number/title only, NOT occupation.",
|
|
4733
4753
|
inputSchema: WageSearchInput,
|
|
4734
4754
|
handler: (input) => pricing.searchWageDeterminations(input),
|
|
4735
4755
|
}),
|
|
@@ -4913,7 +4933,7 @@ export const TOOLS = [
|
|
|
4913
4933
|
// ━━━ Socrata / SODA — keyless SLED + E-rate open data (2) ━━━ ADR-0004
|
|
4914
4934
|
defineTool({
|
|
4915
4935
|
name: "socrata_query",
|
|
4916
|
-
description: "Query rows from an allowlisted Socrata/SODA open-data portal (keyless; ~a dozen US state portals + USAC E-rate on one identical API — state spend/checkbook/contract/vendor-payment datasets). Input `domain` (curated allowlist enum — the SSRF host guard), `datasetId` (4x4, from socrata_discover_datasets), optional SoQL `select`/`where`/`order`/`q`, `limit` (≤1000, def 100), `offset`, `withTotal` (def true). HONESTY: SODA's row response has no total, so a count(*) companion supplies an exact totalAvailable; if it fails the rows still return with totalAvailable:null + a note (hasMore is then inferred from page-fill, never a false complete). Genuine-empty ⇒ complete:true/total:0; an outage/400/404 THROWS (never a fake empty). Value fields are strings.",
|
|
4936
|
+
description: "Query rows from an allowlisted Socrata/SODA open-data portal (keyless; ~a dozen US state portals + USAC E-rate on one identical API — state spend/checkbook/contract/vendor-payment datasets). State procurement mirrors: NY ehig-g5x3, NJ ubnu-tqu7, WA s8d5-pj78, MA cthru.data.socrata.com pegc-naaa (~49M payment rows). Full map: read resource samgov://data-map/state-local. Input `domain` (curated allowlist enum — the SSRF host guard), `datasetId` (4x4, from socrata_discover_datasets), optional SoQL `select`/`where`/`order`/`q`, `limit` (≤1000, def 100), `offset`, `withTotal` (def true). AGGREGATES: for a grand total pass select='sum(amount)' with a where filter; for top-N vendors pass select='vendor_name, sum(amount) as total' with order='total DESC' — these return the final answer directly, NOT a page to manually sum. HONESTY: SODA's row response has no total, so a count(*) companion supplies an exact totalAvailable; if it fails the rows still return with totalAvailable:null + a note (hasMore is then inferred from page-fill, never a false complete). Genuine-empty ⇒ complete:true/total:0; an outage/400/404 THROWS (never a fake empty). Value fields are strings.",
|
|
4917
4937
|
inputSchema: SocrataQueryInput,
|
|
4918
4938
|
handler: (input) => socrata.query(input),
|
|
4919
4939
|
}),
|
|
@@ -4926,7 +4946,7 @@ export const TOOLS = [
|
|
|
4926
4946
|
// ━━━ CKAN datastore_search — keyless SLED open data (2) ━━━ ADR-0006
|
|
4927
4947
|
defineTool({
|
|
4928
4948
|
name: "ckan_query",
|
|
4929
|
-
description: "Query rows from an allowlisted CKAN datastore resource (keyless;
|
|
4949
|
+
description: "Query rows from an allowlisted CKAN datastore resource (keyless; state/city spend/checkbook/procurement/vendor tables on the CKAN Action API). VA eVA PO lines: host=data.virginia.gov resourceId=3c7f1bde-35b0-4fbf-b89c-978a19124d53. Full map: read resource samgov://data-map/state-local. Input `host` (curated allowlist enum — the SSRF host guard: data.ca.gov, data.virginia.gov, data.boston.gov), `resourceId` (36-char lowercase UUID, from ckan_discover_datasets), optional `q` (full-text), `filters` (constrained object {field:value} we JSON.stringify), `sort`, `limit` (≤1000, def 100), `offset`. HONESTY: CKAN's envelope carries a real result.total — the DEFAULT is an EXACT total (exact totalAvailable + hasMore); the rare estimated total (total_was_estimated:true) is disclosed via totalIsEstimated + a note and does NOT drive pagination (it can be above OR below the truth). Genuine-empty ⇒ complete:true/total:0; an outage/404/409 or success:false THROWS (never a fake empty). Values are typed per result.fields[].type.",
|
|
4930
4950
|
inputSchema: CkanQueryInput,
|
|
4931
4951
|
handler: (input) => ckan.query(input),
|
|
4932
4952
|
}),
|
|
@@ -5248,30 +5268,30 @@ export const TOOLS = [
|
|
|
5248
5268
|
// (one keyless GET returns all ~560 orgs) + per-org solicitations.
|
|
5249
5269
|
defineTool({
|
|
5250
5270
|
name: "opengov_list_governments",
|
|
5251
|
-
description: "List the government portals on OpenGov Procurement (formerly ProcureNow) — the directory for opengov_search_solicitations (keyless; api.procurement.opengov.com). OpenGov Procurement hosts the live open-solicitation portals of 525+ US state/local governments (cities, counties, school & special districts across 42 states + DC). The WHOLE directory arrives in ONE keyless GET and is filtered client-side: `state` (2-letter), `query` (case-insensitive name substring); `limit`(1..200)/`offset`. Only ACTIVE, non-internal portals are returned. Output: { governments:[{ code, name, city, state, website }] } + honest _meta. Feed a result's `code` to opengov_search_solicitations. HONESTY: this consumes ONLY the anonymous endpoints the public portal itself calls (the official key-gated api-key API is NOT used) — genuinely keyless; totalAvailable is the EXACT filtered portal count (never the page length); a 429/5xx/timeout THROWS (never a fake empty); a non-array body ⇒ schema_drift.",
|
|
5271
|
+
description: "List the government portals on OpenGov Procurement (formerly ProcureNow) — the directory for opengov_search_solicitations (keyless; api.procurement.opengov.com). OpenGov Procurement hosts the live open-solicitation portals of 525+ US state/local governments (cities, counties, school & special districts across 42 states + DC). The WHOLE directory arrives in ONE keyless GET and is filtered client-side: `state` (2-letter), `query` (case-insensitive name substring); `limit`(1..200)/`offset`. Only ACTIVE, non-internal portals are returned. Output: { governments:[{ code, name, city, state, website }] } + honest _meta. Feed a result's `code` to opengov_search_solicitations. HONESTY: this consumes ONLY the anonymous endpoints the public portal itself calls (the official key-gated api-key API is NOT used) — genuinely keyless; totalAvailable is the EXACT filtered portal count (never the page length); a 429/5xx/timeout THROWS (never a fake empty); a non-array body ⇒ schema_drift." + " " + STATE_BID_FEEDS_POINTER,
|
|
5252
5272
|
inputSchema: OpengovListGovernmentsInput,
|
|
5253
5273
|
handler: (input) => opengov.listGovernments(input),
|
|
5254
5274
|
}),
|
|
5255
5275
|
defineTool({
|
|
5256
5276
|
name: "opengov_search_solicitations",
|
|
5257
|
-
description: "List a government's public solicitations on OpenGov Procurement (keyless; api.procurement.opengov.com, POST /project/list with the required publicView gate). Input `governmentCode` (the portal slug from opengov_list_governments, e.g. 'santacruzca', 'orlando', 'u-46'; REQUIRED), `limit`(1..100)/`offset`. Returns { governmentCode, solicitations:[{ id, title, solicitationNumber, status, type, department, releaseDate, proposalDeadline, contactName, link }] } + honest _meta. ★STATUS: `status` is surfaced VERBATIM — **open = currently ACCEPTING responses**; pending/evaluation/closed are ALSO returned (publicView shows all public projects), so filter status==='open' for live bids. `link` is the public portal page. HONESTY: totalAvailable = the API's `count` = the org's TOTAL public-project count (all statuses), NEVER the page length and NOT an open-only count (a note discloses this); pagination is the API's fixed page (offset is snapped to the page boundary, disclosed); a genuine no-match ⇒ complete:true/returned:0; a 429/5xx/timeout THROWS (never a fake empty); a non-array `projects` ⇒ schema_drift; a bad `governmentCode` ⇒ invalid_input pre-fetch. Genuinely keyless (the key-gated official API is NOT used).",
|
|
5277
|
+
description: "List a government's public solicitations on OpenGov Procurement (keyless; api.procurement.opengov.com, POST /project/list with the required publicView gate). Input `governmentCode` (the portal slug from opengov_list_governments, e.g. 'santacruzca', 'orlando', 'u-46'; REQUIRED), `limit`(1..100)/`offset`. Returns { governmentCode, solicitations:[{ id, title, solicitationNumber, status, type, department, releaseDate, proposalDeadline, contactName, link }] } + honest _meta. ★STATUS: `status` is surfaced VERBATIM — **open = currently ACCEPTING responses**; pending/evaluation/closed are ALSO returned (publicView shows all public projects), so filter status==='open' for live bids. `link` is the public portal page. HONESTY: totalAvailable = the API's `count` = the org's TOTAL public-project count (all statuses), NEVER the page length and NOT an open-only count (a note discloses this); pagination is the API's fixed page (offset is snapped to the page boundary, disclosed); a genuine no-match ⇒ complete:true/returned:0; a 429/5xx/timeout THROWS (never a fake empty); a non-array `projects` ⇒ schema_drift; a bad `governmentCode` ⇒ invalid_input pre-fetch. Genuinely keyless (the key-gated official API is NOT used)." + " " + STATE_BID_FEEDS_POINTER,
|
|
5258
5278
|
inputSchema: OpengovSearchSolicitationsInput,
|
|
5259
5279
|
handler: (input) => opengov.searchSolicitations(input),
|
|
5260
5280
|
}),
|
|
5261
5281
|
// ━━━ Bonfire (Euna) — keyless per-org open-opportunity RSS (SLED bids) ━━━
|
|
5262
5282
|
// SLED bid campaign. Thousands of US state/local govs on Bonfire expose a keyless
|
|
5263
|
-
// RSS of open opportunities. Ships a curated
|
|
5283
|
+
// RSS of open opportunities. Ships a curated 187-org live-verified seed directory
|
|
5264
5284
|
// (Bonfire's authoritative org API is auth-gated → out of bounds). Fixed-suffix
|
|
5265
5285
|
// SSRF (.bonfirehub.com). RSS = the complete open set (totalAvailable honest).
|
|
5266
5286
|
defineTool({
|
|
5267
5287
|
name: "bonfire_list_organizations",
|
|
5268
|
-
description: "List US governments on the Bonfire (Euna) eProcurement platform — the directory for bonfire_search_opportunities (keyless). Bonfire hosts thousands of US state/local governments' open-bid portals, each with a keyless RSS feed. Filter the curated seed by `state` (2-letter) / `query` (case-insensitive name substring); `limit`(1..200)/`offset`. Output: { organizations:[{ org, name, state }] }. Feed a result's `org` to bonfire_search_opportunities. ★HONESTY: this is a CURATED, live-verified SEED of
|
|
5288
|
+
description: "List US governments on the Bonfire (Euna) eProcurement platform — the directory for bonfire_search_opportunities (keyless). Bonfire hosts thousands of US state/local governments' open-bid portals, each with a keyless RSS feed. Filter the curated seed by `state` (2-letter) / `query` (case-insensitive name substring); `limit`(1..200)/`offset`. Output: { organizations:[{ org, name, state }] }. Feed a result's `org` to bonfire_search_opportunities. ★HONESTY: this is a CURATED, live-verified SEED of 187 US orgs — Bonfire has NO keyless org-list API (its authoritative directory is auth-gated, out of bounds), and Euna markets up to ~900 US orgs, so the seed is PARTIAL (disclosed in _meta); probe `{slug}.bonfirehub.com/opportunities/rss` to extend. totalAvailable = the exact filtered seed count." + " " + STATE_BID_FEEDS_POINTER,
|
|
5269
5289
|
inputSchema: BonfireListOrganizationsInput,
|
|
5270
5290
|
handler: (input) => bonfire.listOrganizations(input),
|
|
5271
5291
|
}),
|
|
5272
5292
|
defineTool({
|
|
5273
5293
|
name: "bonfire_search_opportunities",
|
|
5274
|
-
description: "List a government's currently-OPEN solicitations on Bonfire (keyless; {org}.bonfirehub.com/opportunities/rss, RSS 2.0). Input `org` (the subdomain slug from bonfire_list_organizations, e.g. 'harriscountytx', 'broward', 'u-46'; REQUIRED), `limit`(1..200)/`offset`. Returns { org, opportunities:[{ referenceNumber, name, description, closeDate, link, pubDate }] } + honest _meta. HONESTY: the RSS is the COMPLETE set of the org's currently-open opportunities (no server pagination), so totalAvailable = the exact open-opportunity count (never a page length) and this tool pages over it client-side; an empty feed (returned 0) means no open opportunities right now (honest empty, complete:true); `closeDate` is parsed best-effort from the description; a 429/5xx/404/timeout THROWS (never a fake empty); a 200 non-RSS body ⇒ schema_drift; a bad `org` ⇒ invalid_input pre-fetch. Fixed-suffix SSRF (.bonfirehub.com) + redirect:error. Keyless (Bonfire's auth-gated directory API is NOT used).",
|
|
5294
|
+
description: "List a government's currently-OPEN solicitations on Bonfire (keyless; {org}.bonfirehub.com/opportunities/rss, RSS 2.0). Input `org` (the subdomain slug from bonfire_list_organizations, e.g. 'harriscountytx', 'broward', 'u-46'; REQUIRED), `limit`(1..200)/`offset`. Returns { org, opportunities:[{ referenceNumber, name, description, closeDate, link, pubDate }] } + honest _meta. HONESTY: the RSS is the COMPLETE set of the org's currently-open opportunities (no server pagination), so totalAvailable = the exact open-opportunity count (never a page length) and this tool pages over it client-side; an empty feed (returned 0) means no open opportunities right now (honest empty, complete:true); `closeDate` is parsed best-effort from the description; a 429/5xx/404/timeout THROWS (never a fake empty); a 200 non-RSS body ⇒ schema_drift; a bad `org` ⇒ invalid_input pre-fetch. Fixed-suffix SSRF (.bonfirehub.com) + redirect:error. Keyless (Bonfire's auth-gated directory API is NOT used)." + " " + STATE_BID_FEEDS_POINTER,
|
|
5275
5295
|
inputSchema: BonfireSearchOpportunitiesInput,
|
|
5276
5296
|
handler: (input) => bonfire.searchOpportunities(input),
|
|
5277
5297
|
}),
|
|
@@ -5550,7 +5570,7 @@ export const TOOLS = [
|
|
|
5550
5570
|
// never-0; standardRate/isOconus are STRING booleans coerced to real booleans.
|
|
5551
5571
|
defineTool({
|
|
5552
5572
|
name: "gsa_perdiem_rates",
|
|
5553
|
-
description: "Look up GSA Federal Travel PER-DIEM rates — the max lodging + Meals & Incidental Expenses (M&IE) reimbursement ceilings for official U.S. government travel (api.gsa.gov /travel/perdiem/v2, keyed — DATA_GOV_API_KEY or the shared DEMO_KEY). Input: EITHER `city` + `state` (2-letter) OR `zip` (5-digit) — supplying BOTH or NEITHER → invalid_input with 0 fetch; optional `year` (default: current federal fiscal year). Returns { rates:[{ city, county, state, zip, year, isOconus, standardRate, mealsUsd, monthlyLodgingUsd:[{ month (1-12), monthName, lodgingUsd }] }] } + honest _meta. HONESTY: lodgingUsd is the MAX nightly lodging ceiling for that month — VARIES SEASONALLY (hence a per-month array); mealsUsd is the daily M&IE ceiling; both are integer US dollars, null-when-withheld (NEVER 0 — genuine 0 preserved). standardRate/isOconus are booleans coerced from the API's string 'true'/'false' (unrecognized → null, never fabricated false); months array preserved AS-IS (never padded to 12). API returns COMPLETE rate set (no pagination) → totalAvailable = row count, complete:true. Genuine no-match → honest empty; `errors` field non-null → invalid_input; 429 (DEMO_KEY ~10 req/hr) → rate_limited THROWS; set DATA_GOV_API_KEY (free, api.data.gov/signup) for 1000/hr. 5xx/timeout → upstream_unavailable THROWS; 200 non-JSON → schema_drift. Key rides ONLY in the X-Api-Key header.",
|
|
5573
|
+
description: "Look up GSA Federal Travel PER-DIEM rates — the max lodging + Meals & Incidental Expenses (M&IE) reimbursement ceilings for official U.S. government travel (api.gsa.gov /travel/perdiem/v2, keyed — DATA_GOV_API_KEY or the shared DEMO_KEY). Input: EITHER `city` + `state` (2-letter) OR `zip` (5-digit) — supplying BOTH or NEITHER → invalid_input with 0 fetch; optional `year` (default: current federal fiscal year). Returns { rates:[{ city, county, state, zip, year, fiscalYear (= year, the U.S. FY: Oct 2026 = FY2027), isOconus, standardRate, mealsUsd, monthlyLodgingUsd:[{ month (1-12), monthName, lodgingUsd }] }] } + honest _meta. HONESTY: lodgingUsd is the MAX nightly lodging ceiling for that month — VARIES SEASONALLY (hence a per-month array); mealsUsd is the daily M&IE ceiling; both are integer US dollars, null-when-withheld (NEVER 0 — genuine 0 preserved). standardRate/isOconus are booleans coerced from the API's string 'true'/'false' (unrecognized → null, never fabricated false); months array preserved AS-IS (never padded to 12). API returns COMPLETE rate set (no pagination) → totalAvailable = row count, complete:true. Genuine no-match → honest empty; `errors` field non-null → invalid_input; 429 (DEMO_KEY ~10 req/hr) → rate_limited THROWS; set DATA_GOV_API_KEY (free, api.data.gov/signup) for 1000/hr. 5xx/timeout → upstream_unavailable THROWS; 200 non-JSON → schema_drift. Key rides ONLY in the X-Api-Key header.",
|
|
5554
5574
|
inputSchema: GsaPerdiemRatesInput,
|
|
5555
5575
|
handler: (input) => gsaPerdiem.perdiemRates(input),
|
|
5556
5576
|
}),
|
|
@@ -5679,7 +5699,7 @@ async function main() {
|
|
|
5679
5699
|
// not using the default (all). The default instructions stay byte-identical.
|
|
5680
5700
|
// B2: unknown toolset names are reported here (not only stderr) because
|
|
5681
5701
|
// stderr is invisible in Claude Desktop.
|
|
5682
|
-
const BASE_INSTRUCTIONS = "This server wraps US government open data (keyless-first). If a tool result looks wrong, a tool stays broken, or the user wants a capability this server lacks, help improve it: call the `feedback` tool — or use the `report` URL present on schema_drift / upstream_unavailable errors — to get a PREFILLED GitHub issue link, and offer it to the user to open and submit. Nothing is posted automatically; the user submits. Never include secrets or personal data in a report (the repo is public).";
|
|
5702
|
+
const BASE_INSTRUCTIONS = "This server wraps US government open data (keyless-first). State/local dataset IDs (Socrata, CKAN, etc.) are listed in the MCP resource samgov://data-map/state-local. If a tool result looks wrong, a tool stays broken, or the user wants a capability this server lacks, help improve it: call the `feedback` tool — or use the `report` URL present on schema_drift / upstream_unavailable errors — to get a PREFILLED GitHub issue link, and offer it to the user to open and submit. Nothing is posted automatically; the user submits. Never include secrets or personal data in a report (the repo is public).";
|
|
5683
5703
|
let serverInstructions;
|
|
5684
5704
|
if (isAllTools && tsResult.unknown.length === 0) {
|
|
5685
5705
|
// Default: byte-identical to main.
|
|
@@ -5708,7 +5728,7 @@ async function main() {
|
|
|
5708
5728
|
serverInstructions = `${BASE_INSTRUCTIONS} ${parts.join(" ")}`;
|
|
5709
5729
|
}
|
|
5710
5730
|
const server = new Server({ name: SERVER_NAME, version: SERVER_VERSION }, {
|
|
5711
|
-
capabilities: { tools: {} },
|
|
5731
|
+
capabilities: { tools: {}, resources: {} },
|
|
5712
5732
|
// Surfaced to the agent at initialize. Tells it how to route real-usage
|
|
5713
5733
|
// friction back to the project WITHOUT the server ever posting anything.
|
|
5714
5734
|
instructions: serverInstructions,
|
|
@@ -5723,6 +5743,38 @@ async function main() {
|
|
|
5723
5743
|
})),
|
|
5724
5744
|
};
|
|
5725
5745
|
});
|
|
5746
|
+
// ── MCP Resources: state & local data map ──────────────────────────────
|
|
5747
|
+
const DATA_MAP_URI = "samgov://data-map/state-local";
|
|
5748
|
+
const DATA_MAP_NAME = "State & local data map";
|
|
5749
|
+
const DATA_MAP_DESCRIPTION = "Jurisdiction → verified tool call → row count for every allowlisted state/local Socrata, CKAN, Tableau and Open Checkbook dataset.";
|
|
5750
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
5751
|
+
return {
|
|
5752
|
+
resources: [
|
|
5753
|
+
{
|
|
5754
|
+
uri: DATA_MAP_URI,
|
|
5755
|
+
name: DATA_MAP_NAME,
|
|
5756
|
+
description: DATA_MAP_DESCRIPTION,
|
|
5757
|
+
mimeType: "text/markdown",
|
|
5758
|
+
},
|
|
5759
|
+
],
|
|
5760
|
+
};
|
|
5761
|
+
});
|
|
5762
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (req) => {
|
|
5763
|
+
const { uri } = req.params;
|
|
5764
|
+
if (uri !== DATA_MAP_URI) {
|
|
5765
|
+
throw new Error(`Unknown resource: ${uri}`);
|
|
5766
|
+
}
|
|
5767
|
+
return {
|
|
5768
|
+
contents: [
|
|
5769
|
+
{
|
|
5770
|
+
uri: DATA_MAP_URI,
|
|
5771
|
+
mimeType: "text/markdown",
|
|
5772
|
+
text: renderDataMapMarkdown(),
|
|
5773
|
+
},
|
|
5774
|
+
],
|
|
5775
|
+
};
|
|
5776
|
+
});
|
|
5777
|
+
// ── end MCP Resources ──────────────────────────────────────────────────
|
|
5726
5778
|
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
5727
5779
|
const { name, arguments: args } = req.params;
|
|
5728
5780
|
// Check if the tool exists but is not loaded in the current toolset profile.
|