@cliwant/mcp-sam-gov 1.8.0 → 1.10.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  # @cliwant/mcp-sam-gov
2
2
 
3
3
  > **最も包括的なキーレス連邦データ MCP サーバー。**
4
- > SAM.gov · USAspending · SEC EDGAR · OFAC · FDIC · Federal Register · Regulations.gov · eCFR · FAR/DFARS · BLS · Treasury · NIH · NSF · ClinicalTrials · CMS · NVD/CISA · USITC · Census · FRED · BEA · DOL · FEMA · openFDA · NHTSA · CPSC · EPA Envirofacts · CourtListener · IRS-990(ProPublica) ほか **48 の連邦データソース、143 ツール。** キーレス優先 — Census business-patterns · FRED · BEA · DOL データエンドポイントの 4 ソースのみ無料キーが必要で、残り 44 ソースはキー不要。
4
+ > SAM.gov · USAspending · SEC EDGAR · OFAC · FDIC · Federal Register · Regulations.gov · eCFR · FAR/DFARS · BLS · Treasury · NIH · NSF · ClinicalTrials · CMS · NVD/CISA · USITC · Census · FRED · BEA · DOL · FEMA · openFDA · NHTSA · CPSC · EPA Envirofacts · CourtListener · IRS-990(ProPublica) ほか **48 の連邦データソース、144 ツール。** キーレス優先 — Census business-patterns · FRED · BEA · DOL データエンドポイントの 4 ソースのみ無料キーが必要で、残り 44 ソースはキー不要。
5
5
  > API キー不要、登録不要、サインアップ不要。Claude Desktop, Claude Code, Codex CLI, Cursor, Continue, Gemini CLI、すべての MCP ホスト対応。
6
6
 
7
7
  [English README](./README.md) · [한국어 README](./README.ko.md)
@@ -30,7 +30,7 @@
30
30
  | 🌐 **貿易・地理・災害** | "この品目の HTS 関税、この住所の Census tract、この州の FEMA 宣言" | USITC HTS, Census, FEMA, Socrata, CKAN |
31
31
  | 🎓 **grant・データセット** | "過去 30 日のサイバーセキュリティ grant、連邦オープンデータセット発見" | Grants.gov, data.gov |
32
32
 
33
- **48 の連邦データソース、合計 143 ツール — キーレス優先: Census business-patterns · FRED · BEA · DOL データエンドポイントの 4 ソースのみ無料キーが必要で、残り 44 ソースはキー不要。** (初期の 52 ツール版でおおよそ p50 ~0.25s / p95 ~0.8s を計測 — ソースや上流負荷で変動する近似値であり保証値ではありません。)
33
+ **48 の連邦データソース、合計 144 ツール — キーレス優先: Census business-patterns · FRED · BEA · DOL データエンドポイントの 4 ソースのみ無料キーが必要で、残り 44 ソースはキー不要。** (初期の 52 ツール版でおおよそ p50 ~0.25s / p95 ~0.8s を計測 — ソースや上流負荷で変動する近似値であり保証値ではありません。)
34
34
 
35
35
  ---
36
36
 
@@ -57,7 +57,7 @@ Claude Code (CLI) を既に使用している場合:
57
57
  /plugin install cliwant/mcp-sam-gov
58
58
  ```
59
59
 
60
- MCP サーバー + Claude が 143 ツールをいつ・どう呼ぶかを教える [SKILL.md ワークフローガイド](./skills/sam-gov/SKILL.md) を同時登録。
60
+ MCP サーバー + Claude が 144 ツールをいつ・どう呼ぶかを教える [SKILL.md ワークフローガイド](./skills/sam-gov/SKILL.md) を同時登録。
61
61
 
62
62
  ### 🔵 パス 3 — Codex / Cursor / Continue / Gemini 等の手動インストール
63
63
 
@@ -188,9 +188,9 @@ npm はインストール済みユーザーに新バージョンを通知しな
188
188
 
189
189
  **自動投稿は一切ありません。** サーバーにトークンもアカウントもなく、リンクを作るだけで、開いて送信するのは**あなた**です。事前入力リンクにはツール名・エラー種別・サーバーバージョンのみが含まれ、クエリ値は含まれません。公開リポジトリなので、機微な内容は削除してから送信してください。直接開く: [**New issue**](https://github.com/cliwant/mcp-sam-gov/issues/new/choose)。
190
190
 
191
- ## ツールカタログ (143)
191
+ ## ツールカタログ (144)
192
192
 
193
- ワークフロー別グループ。キーレス優先 — 大半はキー不要、Census business-patterns · FRED · BEA · DOL データエンドポイントは無料キーが必要。全 per-tool 一覧と入力 schema・誠実性 caveat の原文は [英語 README のカタログセクション](./README.md#tool-catalog-143-tools) を正とします。
193
+ ワークフロー別グループ。キーレス優先 — 大半はキー不要、Census business-patterns · FRED · BEA · DOL データエンドポイントは無料キーが必要。全 per-tool 一覧と入力 schema・誠実性 caveat の原文は [英語 README のカタログセクション](./README.md#tool-catalog-144-tools) を正とします。
194
194
 
195
195
  - **案件 + ソリシテーション — SAM.gov + Grants.gov (10)**: `sam_search_opportunities` `sam_search_shaping` `sam_get_opportunity` `sam_fetch_description` `sam_fetch_attachment_text` `sam_attachment_url` `sam_lookup_organization` `sam_lookup_notice_fields` `grants_search` `grants_get_opportunity`
196
196
  - **spending・受注・競合 — USAspending + FPDS + GAO (31)**: `usas_search_awards` `usas_search_individual_awards` `usas_get_award_detail` `usas_search_awards_by_recipient` `usas_search_subawards` `usas_search_recompetes` `usas_search_expiring_contracts`(deprecated) `usas_analyze_incumbent` `usas_search_teaming_partners` `usas_spending_over_time` `usas_search_agency_spending` `usas_search_subagency_spending` `usas_search_psc_spending` `usas_search_cfda_spending` `usas_search_state_spending` `usas_search_federal_account_spending` `usas_search_recipients` `usas_get_recipient_profile` `usas_get_agency_profile` `usas_get_agency_awards_summary` `usas_get_agency_budget_function` `usas_list_toptier_agencies` `usas_lookup_agency` `usas_autocomplete_naics` `usas_autocomplete_recipient` `usas_naics_hierarchy` `usas_glossary` `usas_list_disaster_codes`(災害緊急基金コード(DEFC)一覧 — COVID-19·IIJA/インフラ等の緊急歳出タグ) `usas_disaster_spending`(指定 DEFC の災害/緊急基金支出の地域別内訳 — どの州/郡/選挙区が COVID/IIJA 救済金を受領) `fpds_search_awards` `gao_protest_lookup`
package/README.ko.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @cliwant/mcp-sam-gov
2
2
 
3
3
  > **가장 포괄적인 keyless 연방 데이터 MCP 서버.**
4
- > SAM.gov · USAspending · SEC EDGAR · OFAC · FDIC · Federal Register · Regulations.gov · eCFR · FAR/DFARS · BLS · Treasury · NIH · NSF · ClinicalTrials · CMS · NVD/CISA · USITC · Census · FRED · BEA · DOL · FEMA · openFDA · NHTSA · CPSC · EPA Envirofacts · CourtListener · IRS-990(ProPublica) 외 **48개 연방 데이터 소스, 143개 도구.** keyless 우선 — Census business-patterns · FRED · BEA · DOL 데이터 엔드포인트 4개 소스만 무료 키가 필요하고 나머지 44개 소스는 키가 필요 없습니다.
4
+ > SAM.gov · USAspending · SEC EDGAR · OFAC · FDIC · Federal Register · Regulations.gov · eCFR · FAR/DFARS · BLS · Treasury · NIH · NSF · ClinicalTrials · CMS · NVD/CISA · USITC · Census · FRED · BEA · DOL · FEMA · openFDA · NHTSA · CPSC · EPA Envirofacts · CourtListener · IRS-990(ProPublica) 외 **48개 연방 데이터 소스, 144개 도구.** keyless 우선 — Census business-patterns · FRED · BEA · DOL 데이터 엔드포인트 4개 소스만 무료 키가 필요하고 나머지 44개 소스는 키가 필요 없습니다.
5
5
  > API 키 / 등록 / 가입 불필요. Claude Desktop, Claude Code, Codex CLI, Cursor, Continue, Gemini CLI, 모든 MCP 호스트 호환.
6
6
 
7
7
  [English README](./README.md) · [日本語 README](./README.ja.md)
@@ -30,7 +30,7 @@
30
30
  | 🌐 **무역·지리·재난** | "이 품목 HTS 관세, 이 주소 Census tract, 이 주의 FEMA 선포" | USITC HTS, Census, FEMA, Socrata, CKAN |
31
31
  | 🎓 **grant·데이터셋** | "최근 30일 사이버보안 grant, 연방 오픈 데이터셋 발굴" | Grants.gov, data.gov |
32
32
 
33
- **48개 연방 데이터 소스, 총 143개 도구 — keyless 우선: Census business-patterns · FRED · BEA · DOL 데이터 엔드포인트 4개 소스만 무료 키가 필요하고 나머지 44개 소스는 키가 필요 없습니다.** (초기 52-도구 빌드 기준 대략 p50 ~0.25s / p95 ~0.8s 측정 — 소스·업스트림 부하에 따라 변동하는 근사치이며 보장값이 아님.)
33
+ **48개 연방 데이터 소스, 총 144개 도구 — keyless 우선: Census business-patterns · FRED · BEA · DOL 데이터 엔드포인트 4개 소스만 무료 키가 필요하고 나머지 44개 소스는 키가 필요 없습니다.** (초기 52-도구 빌드 기준 대략 p50 ~0.25s / p95 ~0.8s 측정 — 소스·업스트림 부하에 따라 변동하는 근사치이며 보장값이 아님.)
34
34
 
35
35
  ---
36
36
 
@@ -57,7 +57,7 @@ PowerShell, npm 등 필요 없음.
57
57
  /plugin install cliwant/mcp-sam-gov
58
58
  ```
59
59
 
60
- MCP 서버 + Claude 가 143개 도구를 언제 / 어떻게 호출할지 가르치는 [SKILL.md 워크플로 가이드](./skills/sam-gov/SKILL.md) 동시 등록.
60
+ MCP 서버 + Claude 가 144개 도구를 언제 / 어떻게 호출할지 가르치는 [SKILL.md 워크플로 가이드](./skills/sam-gov/SKILL.md) 동시 등록.
61
61
 
62
62
  ### 🔵 경로 3 — Codex / Cursor / Continue / Gemini 등 수동 설치
63
63
 
@@ -188,9 +188,9 @@ npm은 설치한 사용자에게 새 버전을 알려주지 않으므로, 서버
188
188
 
189
189
  **자동 제출은 절대 없습니다.** 서버엔 토큰·계정이 없고 링크만 만들며, 열어서 제출하는 건 **사용자**입니다. 프리필 링크에는 툴명·에러종류·서버버전만 담기고 쿼리값은 담기지 않습니다. 공개 레포이니 민감한 내용은 지우고 제출하세요. 직접 열기: [**New issue**](https://github.com/cliwant/mcp-sam-gov/issues/new/choose).
190
190
 
191
- ## 도구 카탈로그 (143개)
191
+ ## 도구 카탈로그 (144개)
192
192
 
193
- 워크플로별 그룹. keyless 우선 — 대부분 키가 필요 없고, Census business-patterns · FRED · BEA · DOL 데이터 엔드포인트는 무료 키가 필요합니다. 전체 per-tool 목록과 입력 schema·정직성 caveat 원문은 [영문 README 의 카탈로그 섹션](./README.md#tool-catalog-143-tools)을 기준으로 하세요.
193
+ 워크플로별 그룹. keyless 우선 — 대부분 키가 필요 없고, Census business-patterns · FRED · BEA · DOL 데이터 엔드포인트는 무료 키가 필요합니다. 전체 per-tool 목록과 입력 schema·정직성 caveat 원문은 [영문 README 의 카탈로그 섹션](./README.md#tool-catalog-144-tools)을 기준으로 하세요.
194
194
 
195
195
  - **입찰 + 솔리시테이션 — SAM.gov + Grants.gov (10)**: `sam_search_opportunities` `sam_search_shaping` `sam_get_opportunity` `sam_fetch_description` `sam_fetch_attachment_text` `sam_attachment_url` `sam_lookup_organization` `sam_lookup_notice_fields` `grants_search` `grants_get_opportunity`
196
196
  - **spending·수주·경쟁 — USAspending + FPDS + GAO (31)**: `usas_search_awards` `usas_search_individual_awards` `usas_get_award_detail` `usas_search_awards_by_recipient` `usas_search_subawards` `usas_search_recompetes` `usas_search_expiring_contracts`(deprecated) `usas_analyze_incumbent` `usas_search_teaming_partners` `usas_spending_over_time` `usas_search_agency_spending` `usas_search_subagency_spending` `usas_search_psc_spending` `usas_search_cfda_spending` `usas_search_state_spending` `usas_search_federal_account_spending` `usas_search_recipients` `usas_get_recipient_profile` `usas_get_agency_profile` `usas_get_agency_awards_summary` `usas_get_agency_budget_function` `usas_list_toptier_agencies` `usas_lookup_agency` `usas_autocomplete_naics` `usas_autocomplete_recipient` `usas_naics_hierarchy` `usas_glossary` `usas_list_disaster_codes`(재난긴급자금코드(DEFC) 목록 — COVID-19·IIJA/인프라 등 긴급세출 태그) `usas_disaster_spending`(지정 DEFC의 재난/긴급자금 지출 지역별 분해 — 어느 주/카운티/선거구가 COVID/IIJA 구호금 수령) `fpds_search_awards` `gao_protest_lookup`
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ### **$4 trillion of public federal data, one `npm install` away.**
6
6
 
7
- The most comprehensive **keyless-first** MCP server for US federal contracting + spending + regulation + partner vetting. **143 tools across 48 federal data sources** — keyless-first: only 4 sources (Census business-patterns, FRED, BEA, and DOL's data endpoint) need a free key; the other 44 need none. Works today, in any AI agent.
7
+ The most comprehensive **keyless-first** MCP server for US federal contracting + spending + regulation + partner vetting. **144 tools across 48 federal data sources** — keyless-first: only 4 sources (Census business-patterns, FRED, BEA, and DOL's data endpoint) need a free key; the other 44 need none. Works today, in any AI agent.
8
8
 
9
9
  [![npm](https://img.shields.io/npm/v/@cliwant/mcp-sam-gov?color=cb3837&label=%40cliwant%2Fmcp-sam-gov&logo=npm)](https://www.npmjs.com/package/@cliwant/mcp-sam-gov)
10
10
  [![mcp-registry](https://img.shields.io/badge/MCP%20Registry-active-2ea44f?logo=anthropic)](https://registry.modelcontextprotocol.io/v0/servers?search=cliwant)
@@ -50,7 +50,7 @@ The most comprehensive **keyless-first** MCP server for US federal contracting +
50
50
  |---|---|
51
51
  | GovWin: $30K-$100K/yr per seat | Free, MIT license |
52
52
  | API key registration → wait 24h → quota tier shopping | `npm install` → working in 60s |
53
- | 5+ separate vendor APIs / scrapers | 1 unified surface, 143 tools across 48 sources |
53
+ | 5+ separate vendor APIs / scrapers | 1 unified surface, 144 tools across 48 sources |
54
54
  | LLMs hallucinate NAICS codes / agency names | Anti-hallucination autocomplete guards built-in |
55
55
  | Brittle scraping breaks weekly | Daily live smoke test ([badge above](#)) |
56
56
  | Procurement officer → IT ticket → 3-week wait | Claude Desktop double-click install |
@@ -79,7 +79,7 @@ The federal data this wraps is **public domain**. There is no good reason it sho
79
79
  | 🌐 **Trade, geo & disaster** | "HTS tariff for this product; Census tract for this address; FEMA declarations in this state" | USITC HTS, US Census, FEMA, Socrata, CKAN |
80
80
  | 🎓 **Grants & datasets** | "Cybersecurity grants posted in the last 30 days; discover federal open datasets" | Grants.gov, data.gov |
81
81
 
82
- **143 tools across 48 federal data sources — keyless-first: only 4 sources (Census business-patterns, FRED, BEA, and DOL's data endpoint) need a free key; the other 44 need none.** (An earlier 52-tool build measured roughly p50 ~0.25s / p95 ~0.8s against production federal APIs; latency varies by source and upstream load — treat it as fast, not a benchmarked guarantee.)
82
+ **144 tools across 48 federal data sources — keyless-first: only 4 sources (Census business-patterns, FRED, BEA, and DOL's data endpoint) need a free key; the other 44 need none.** (An earlier 52-tool build measured roughly p50 ~0.25s / p95 ~0.8s against production federal APIs; latency varies by source and upstream load — treat it as fast, not a benchmarked guarantee.)
83
83
 
84
84
  ---
85
85
 
@@ -106,7 +106,7 @@ If you already use Claude Code (the CLI):
106
106
  /plugin install cliwant/mcp-sam-gov
107
107
  ```
108
108
 
109
- This installs the MCP server **plus** a [SKILL.md](./skills/sam-gov/SKILL.md) workflow guide that teaches Claude when + how to use each of the 143 tools.
109
+ This installs the MCP server **plus** a [SKILL.md](./skills/sam-gov/SKILL.md) workflow guide that teaches Claude when + how to use each of the 144 tools.
110
110
 
111
111
  ### 🔵 Path 3 — Manual install for any MCP host (Codex, Cursor, Continue, Gemini)
112
112
 
@@ -174,7 +174,7 @@ Then point your host config at the absolute path:
174
174
 
175
175
  (Or skip this entirely — use Path 1's `.mcpb` and it auto-configures.)
176
176
 
177
- Restart Claude Desktop fully (system tray quit on Windows / Quit menu on macOS), then look for the 🔨 icon. You should see "sam-gov (143 tools)".
177
+ Restart Claude Desktop fully (system tray quit on Windows / Quit menu on macOS), then look for the 🔨 icon. You should see "sam-gov (144 tools)".
178
178
 
179
179
  ### Claude Code
180
180
 
@@ -399,7 +399,7 @@ This server is built to **improve from real usage**. Because an AI agent (not a
399
399
 
400
400
  You can also open one directly: [**New issue**](https://github.com/cliwant/mcp-sam-gov/issues/new/choose) — bug / feature / wrong-output templates.
401
401
 
402
- ## Tool catalog (143 tools)
402
+ ## Tool catalog (144 tools)
403
403
 
404
404
  Grouped by workflow. Keyless-first — most tools need no key; Census business-patterns, FRED, BEA, and DOL's data endpoint require a free key, and a handful of others gain higher limits from an optional free key (noted above). Descriptions are condensed — each tool's own `inputSchema` carries the full contract and honesty caveats.
405
405
 
@@ -522,7 +522,8 @@ Grouped by workflow. Keyless-first — most tools need no key; Census business-p
522
522
  - `regulations_search_documents` — search Regulations.gov rulemaking documents (rules, proposed rules, notices)
523
523
  - `regulations_search_comments` — search public comments on rulemakings
524
524
  - `regulations_get_docket` — fetch one Regulations.gov docket by id
525
- - `ecfr_search` — full-text search across the entire CFR (titleNumber=48 for FAR)
525
+ - `ecfr_search` — full-text search across the entire CFR (titleNumber=48 for FAR); returns ranked snippets + ecfrUrl
526
+ - `ecfr_get_section` — the FULL text of one CFR section by citation (any title; for FAR/DFARS prefer `far_clause_lookup`)
526
527
  - `ecfr_list_titles` — all 50 CFR titles + last-amended dates
527
528
  - `far_clause_lookup` — authoritative FAR/DFARS clause text + its prescription (exact clause number)
528
529
  - `far_search` — FAR/DFARS-scoped search (excludes GSAM, collapses to current in-force version)
@@ -0,0 +1,68 @@
1
+ /**
2
+ * arcgis-feature.ts — generic ArcGIS REST FeatureServer/MapServer layer query
3
+ * over a CURATED service allowlist (loop — SLED bid campaign, 2026-07-19).
4
+ *
5
+ * WHAT IT ADDS: an enormous amount of US government data (esp. SLED procurement /
6
+ * GIS / infrastructure) is published as ArcGIS REST feature layers. This is the
7
+ * QUERY companion to `arcgis_hub_discover_datasets` (which DISCOVERS Hub datasets):
8
+ * it queries the ROWS of a curated set of high-value ArcGIS layers. First payload:
9
+ * the DC OCP "PASS" procurement layers (solicitations / contracts / purchase
10
+ * orders / payments) — DC's live open-solicitation feed with 46 fields, keyless.
11
+ *
12
+ * The module REUSES `getJson` (redirect:"error") / `driftError` / `num`·`str` /
13
+ * `withMeta`·`buildMeta`, mirroring socrata.ts (curated allowlist) + datagov-
14
+ * catalog.ts. KEYLESS.
15
+ *
16
+ * ★ SSRF: the caller supplies a `service` ENUM key (never a free host/URL); each
17
+ * allowlist entry is a FIXED, live-verified ArcGIS layer base URL. The query
18
+ * URL is built on that fixed base, and a post-construction assertion requires
19
+ * the built hostname === the allowlist entry's host (over https) BEFORE the
20
+ * fetch; `redirect:"error"`. `where`/`outFields`/`orderByFields` ride in
21
+ * URLSearchParams (encoded) — they filter/project the read-only layer and
22
+ * CANNOT alter the host (a malformed `where` ⇒ upstream 400 ⇒ invalid_input,
23
+ * surfaced, never silent). Adding a service later = an allowlist SOURCE edit +
24
+ * a live count/`/query` verification — NEVER a free runtime param.
25
+ *
26
+ * ★ HONESTY PILLARS (captured live 2026-07-19):
27
+ * P1: totalAvailable = the layer's EXACT match count (a `returnCountOnly=true`
28
+ * companion query), NEVER the page length. Best-effort: a count failure ⇒
29
+ * totalAvailable:null + a note (the rows are still returned) — never a fake
30
+ * total. `exceededTransferLimit` also forces hasMore.
31
+ * P2: getJson THROWS on 429/5xx/timeout; an ArcGIS `{error:{code,message}}`
32
+ * body (HTTP 200) is classified (400 ⇒ invalid_input, else upstream) and
33
+ * THROWN — never a fake empty. A genuine no-match (features:[], count:0) ⇒
34
+ * honest empty.
35
+ * P3: attributes pass through VERBATIM (they are the layer's own record). NOTE
36
+ * (disclosed every response): ArcGIS date fields are epoch MILLISECONDS and a
37
+ * negative/sentinel value (e.g. -2209093200000 ≈ year 1900) is a placeholder,
38
+ * not a real date — surfaced verbatim, never coerced.
39
+ * P4: `body.features` absent/non-array (and no error) ⇒ driftError; a 200
40
+ * non-JSON body ⇒ schema_drift via the catch-ladder.
41
+ */
42
+ import { num } from "./coerce.js";
43
+ import { type MetaBundle } from "./meta.js";
44
+ export { num };
45
+ export type ArcgisService = {
46
+ key: string;
47
+ base: string;
48
+ label: string;
49
+ note: string;
50
+ };
51
+ export declare const ARCGIS_SERVICES: readonly ArcgisService[];
52
+ export type ArcgisFeatureQueryArgs = {
53
+ service: string;
54
+ where?: string;
55
+ outFields?: string;
56
+ orderByFields?: string;
57
+ limit?: number;
58
+ offset?: number;
59
+ };
60
+ /**
61
+ * Query rows from a curated ArcGIS REST feature layer. `service` is an allowlist
62
+ * enum; `where` (SQL-ish filter, default 1=1), `outFields` (default *),
63
+ * `orderByFields`, `limit`/`offset`. Returns curated record attributes + honest
64
+ * `_meta` (totalAvailable = the layer's exact match count via a returnCountOnly
65
+ * companion; epoch-ms date disclosure).
66
+ */
67
+ export declare function featureQuery(args: ArcgisFeatureQueryArgs): Promise<MetaBundle>;
68
+ //# sourceMappingURL=arcgis-feature.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arcgis-feature.d.ts","sourceRoot":"","sources":["../src/arcgis-feature.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAIH,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EAAY,KAAK,UAAU,EAAqB,MAAM,WAAW,CAAC;AAEzE,OAAO,EAAE,GAAG,EAAE,CAAC;AAMf,MAAM,MAAM,aAAa,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AACvF,eAAO,MAAM,eAAe,EAAE,SAAS,aAAa,EA2B1C,CAAC;AAmDX,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,UAAU,CAAC,CAmFpF"}
@@ -0,0 +1,206 @@
1
+ /**
2
+ * arcgis-feature.ts — generic ArcGIS REST FeatureServer/MapServer layer query
3
+ * over a CURATED service allowlist (loop — SLED bid campaign, 2026-07-19).
4
+ *
5
+ * WHAT IT ADDS: an enormous amount of US government data (esp. SLED procurement /
6
+ * GIS / infrastructure) is published as ArcGIS REST feature layers. This is the
7
+ * QUERY companion to `arcgis_hub_discover_datasets` (which DISCOVERS Hub datasets):
8
+ * it queries the ROWS of a curated set of high-value ArcGIS layers. First payload:
9
+ * the DC OCP "PASS" procurement layers (solicitations / contracts / purchase
10
+ * orders / payments) — DC's live open-solicitation feed with 46 fields, keyless.
11
+ *
12
+ * The module REUSES `getJson` (redirect:"error") / `driftError` / `num`·`str` /
13
+ * `withMeta`·`buildMeta`, mirroring socrata.ts (curated allowlist) + datagov-
14
+ * catalog.ts. KEYLESS.
15
+ *
16
+ * ★ SSRF: the caller supplies a `service` ENUM key (never a free host/URL); each
17
+ * allowlist entry is a FIXED, live-verified ArcGIS layer base URL. The query
18
+ * URL is built on that fixed base, and a post-construction assertion requires
19
+ * the built hostname === the allowlist entry's host (over https) BEFORE the
20
+ * fetch; `redirect:"error"`. `where`/`outFields`/`orderByFields` ride in
21
+ * URLSearchParams (encoded) — they filter/project the read-only layer and
22
+ * CANNOT alter the host (a malformed `where` ⇒ upstream 400 ⇒ invalid_input,
23
+ * surfaced, never silent). Adding a service later = an allowlist SOURCE edit +
24
+ * a live count/`/query` verification — NEVER a free runtime param.
25
+ *
26
+ * ★ HONESTY PILLARS (captured live 2026-07-19):
27
+ * P1: totalAvailable = the layer's EXACT match count (a `returnCountOnly=true`
28
+ * companion query), NEVER the page length. Best-effort: a count failure ⇒
29
+ * totalAvailable:null + a note (the rows are still returned) — never a fake
30
+ * total. `exceededTransferLimit` also forces hasMore.
31
+ * P2: getJson THROWS on 429/5xx/timeout; an ArcGIS `{error:{code,message}}`
32
+ * body (HTTP 200) is classified (400 ⇒ invalid_input, else upstream) and
33
+ * THROWN — never a fake empty. A genuine no-match (features:[], count:0) ⇒
34
+ * honest empty.
35
+ * P3: attributes pass through VERBATIM (they are the layer's own record). NOTE
36
+ * (disclosed every response): ArcGIS date fields are epoch MILLISECONDS and a
37
+ * negative/sentinel value (e.g. -2209093200000 ≈ year 1900) is a placeholder,
38
+ * not a real date — surfaced verbatim, never coerced.
39
+ * P4: `body.features` absent/non-array (and no error) ⇒ driftError; a 200
40
+ * non-JSON body ⇒ schema_drift via the catch-ladder.
41
+ */
42
+ import { ToolErrorCarrier } from "./errors.js";
43
+ import { getJson, driftError } from "./datasource.js";
44
+ import { num } from "./coerce.js";
45
+ import { withMeta } from "./meta.js";
46
+ export { num };
47
+ export const ARCGIS_SERVICES = [
48
+ { key: "dc_pass_solicitations", base: "https://maps2.dcgis.dc.gov/dcgis/rest/services/DCGIS_DATA/Government_Operations/MapServer/19", label: "DC OCP PASS — Solicitations", note: "DC Office of Contracting & Procurement live solicitations (SOLICITATIONNUMBER, SOLICITATIONTITLE, DUE_DATE, OPENDATE, CLOSEDATE, NIGPCODE, CONTRACTINGOFFICER, AWARD_TO…). ~25k." },
49
+ { key: "dc_pass_contracts", base: "https://maps2.dcgis.dc.gov/dcgis/rest/services/DCGIS_DATA/Government_Operations/MapServer/37", label: "DC OCP PASS — Contracts", note: "DC awarded contracts. ~50k." },
50
+ { key: "dc_pass_purchase_orders", base: "https://maps2.dcgis.dc.gov/dcgis/rest/services/DCGIS_DATA/Government_Operations/MapServer/16", label: "DC OCP PASS — Purchase Orders", note: "DC purchase orders. ~275k." },
51
+ { key: "dc_pass_payments", base: "https://maps2.dcgis.dc.gov/dcgis/rest/services/DCGIS_DATA/Government_Operations/MapServer/17", label: "DC OCP PASS — Payments", note: "DC vendor payments. ~1.55M." },
52
+ // ── Additional US local-gov procurement layers (loop, 2026-07-19) — found via
53
+ // arcgis_hub_discover_datasets, each on a reachable Esri-hosted services*.
54
+ // arcgis.com endpoint (NOT WAF-gated) + live-verified returnCountOnly. ──
55
+ { key: "asheville_purchase_orders", base: "https://services.arcgis.com/aJ16ENn1AaqdFlqx/ArcGIS/rest/services/Financials/FeatureServer/9", label: "Asheville NC — Purchase Order Line Items", note: "City of Asheville NC purchase-order line items. ~62.8k." },
56
+ { key: "asheville_po_summary", base: "https://services.arcgis.com/aJ16ENn1AaqdFlqx/ArcGIS/rest/services/Financials/FeatureServer/8", label: "Asheville NC — Purchase Order Summary", note: "City of Asheville NC purchase-order summary. ~18.7k." },
57
+ { key: "bellevue_vendor_payments", base: "https://services1.arcgis.com/EYzEZbDhXZjURPbP/arcgis/rest/services/YTD_Vendor_Payments/FeatureServer/0", label: "Bellevue WA — YTD Vendor Payments", note: "City of Bellevue WA year-to-date vendor payments. ~15.8k." },
58
+ { key: "bellevue_awarded_contracts", base: "https://services1.arcgis.com/EYzEZbDhXZjURPbP/arcgis/rest/services/Annual_Awarded_Contracts/FeatureServer/0", label: "Bellevue WA — Annual Awarded Contracts", note: "City of Bellevue WA annual awarded contracts. ~2.0k." },
59
+ { key: "miamidade_purchase_orders_2017", base: "https://services.arcgis.com/8Pc9XBTAsYuxx9Ny/arcgis/rest/services/miamidade_procurement_data_2017/FeatureServer/0", label: "Miami-Dade County FL — Purchase Orders (ADPICS) 2017", note: "Miami-Dade County FL purchase orders — a 2017 snapshot (historical, disclosed). ~94.8k." },
60
+ { key: "miamidade_purchase_orders_2025", base: "https://services.arcgis.com/8Pc9XBTAsYuxx9Ny/arcgis/rest/services/miamidade_procurement_data_informs_2025/FeatureServer/0", label: "Miami-Dade County FL — Purchase Orders (INFORMS) 2025", note: "Miami-Dade County FL purchase orders — CURRENT (2025 INFORMS). ~25k." },
61
+ { key: "suffolk_county_ny_contracts_2018", base: "https://services.arcgis.com/JsDD4qdG5r2a7hR5/arcgis/rest/services/County_Contracts_2018/FeatureServer/0", label: "Suffolk County NY — County Contracts 2018", note: "Suffolk County NY contracts — a 2018 snapshot (historical, disclosed). ~1.4k." },
62
+ { key: "matsu_borough_ak_checkbook", base: "https://services.arcgis.com/fX5IGselyy1TirdY/arcgis/rest/services/Logos_Checkbook_Data/FeatureServer/0", label: "Matanuska-Susitna Borough AK — Checkbook", note: "Matanuska-Susitna Borough AK checkbook (vendor spend). ~1.2k." },
63
+ { key: "lasvegas_checkbook", base: "https://services1.arcgis.com/F1v0ufATbBQScMtY/arcgis/rest/services/Processed_Checkbook_Table_View/FeatureServer/0", label: "City of Las Vegas NV — Open Checkbook", note: "City of Las Vegas NV checkbook (DEPARTMENT/VENDOR/TRANSACTION_AMOUNT/TRANSACTION_DATE). ~373k." },
64
+ { key: "baltimore_checkbook", base: "https://services1.arcgis.com/UWYHeuuJISiGmgXx/arcgis/rest/services/OpenCheckbookFY2022_Through_Present/FeatureServer/0", label: "Baltimore City MD — Open Checkbook (FY2022–present)", note: "Baltimore City MD checkbook, FY2022 through present (Supplier_Name/Payment_Amount/Supplier_Contract_Number/Agency). ~367k." },
65
+ { key: "naperville_vendor_payments", base: "https://services1.arcgis.com/rXJ6QApc2sOtl1Pd/arcgis/rest/services/Expenditures_2018_to_Current_Year/FeatureServer/0", label: "City of Naperville IL — Vendor Payments (2018–current)", note: "City of Naperville IL vendor payments, 2018 to current year (Vendor_Name/Payment_Amount/Purch_Order_No/Check_Date). ~127k." },
66
+ { key: "worcester_ma_checkbook_fy25", base: "https://services1.arcgis.com/j8dqo2DJE7mVUBU1/arcgis/rest/services/Fiscal_Year_2025_Open_Checkbook/FeatureServer/0", label: "City of Worcester MA — Open Checkbook FY2025", note: "City of Worcester MA open checkbook, FY2025 (Payee/Pmt_Amount/Departments/Spend_Categories/Pmt_Date). ~38k." },
67
+ { key: "lasvegas_purchasing_contracts", base: "https://services1.arcgis.com/F1v0ufATbBQScMtY/arcgis/rest/services/Purchasing_Contracts__view/FeatureServer/0", label: "City of Las Vegas NV — Purchasing Contracts", note: "City of Las Vegas NV purchasing contract register (Project_Number/Project_Name/Supplier_Name/Award_Date/Termination_Date). ~3.4k, all with a supplier. Complements lasvegas_checkbook (payments)." },
68
+ { key: "txdot_construction_projects", base: "https://services.arcgis.com/KTcxiTD9dsQw4r7Z/arcgis/rest/services/TxDOT_Projects_Info_All/FeatureServer/0", label: "Texas DOT — Projects (with awarded construction company)", note: "Texas Department of Transportation highway projects (CNSTR_CMPNY_NM awarded construction company/CNSTR_NTPD_DT notice-to-proceed/TYPE_OF_WORK/HWY_NBR). ~85k rows, ~14.1k with an awarded contractor (filter CNSTR_CMPNY_NM IS NOT NULL for awards)." },
69
+ { key: "akdot_construction_awards", base: "https://services.arcgis.com/r4A0V7UzH9fcLVvv/arcgis/rest/services/AWARDS_DASHBOARD_AWARDS_TABLE_POINTS/FeatureServer/0", label: "Alaska DOT&PF — Construction Bid Awards", note: "Alaska Department of Transportation & Public Facilities construction bid awards (ContractID/Contractor/Bids_Received/Engineer_Est/Award_Amount/Advertised_Date/Bid_Opening_Date/Reg_Award_Date/DOT_Region). ~1.1k." },
70
+ { key: "akdot_aashtoware_proposals", base: "https://services.arcgis.com/r4A0V7UzH9fcLVvv/arcgis/rest/services/AASHTOWARE_Proposals_Points/FeatureServer/0", label: "Alaska DOT&PF — AASHTOWARE Proposals/Lettings", note: "Alaska DOT&PF AASHTOWARE proposals & lettings with awarded vendor (ProposalId/LettingId/StateProjectNumber/RefVendor_LongName/RefVendor_DBECertStatus/AwardedAmount/Contract_ID/FundType). ~1k, ~1,035 with an AwardedAmount>0. Complements akdot_construction_awards." },
71
+ { key: "iowadot_public_bid_awards", base: "https://services.arcgis.com/8lRhdTsQyJpO52F1/arcgis/rest/services/Project_Scheduling_Public_Bid_Point_View/FeatureServer/0", label: "Iowa DOT — Public Bid / Project Scheduling", note: "Iowa Department of Transportation public bid & project scheduling (PROJECT_NUMBER/WORK_DESC/LETTING_FISCAL_YEAR/PROGRAM_ESTIMATE/CONTRACT_AWARDED/AWARDED/FINAL_CONTRACT/CONTRACTOR/CONTRACT_ID/STATUS). ~360." },
72
+ { key: "okdot_cirb_contract_status", base: "https://services6.arcgis.com/RBtoEUQ2lmN0K3GY/arcgis/rest/services/CIRB_Project_Status/FeatureServer/0", label: "Oklahoma DOT — CIRB Contract Status", note: "Oklahoma Department of Transportation County Improvements for Roads & Bridges (CIRB) contract status (T_PROJECT/JP_DESCRIPTION/A_CONTRACT_AMOUNT/A_AMOUNT_EARNED/A_AMOUNT_PAID/D_LET_DATE/D_CONTRACT_AWARD_DATE/D_WORK_ORDER_DATE). ~780." },
73
+ { key: "topeka_checkbook_aggregate", base: "https://services1.arcgis.com/EvtgI3PZ9PyCGJZS/arcgis/rest/services/Checkbook_Aggregate/FeatureServer/0", label: "City of Topeka KS — Open Checkbook (aggregate FY2015–2023)", note: "City of Topeka KS open checkbook, all years aggregated FY2015–2023 (fiscal_year/fiscal_period/department/program/fund/vendor_name/description/dollars). ~332k rows, ~323.6k with dollars>0." },
74
+ ];
75
+ const SERVICE_BY_KEY = new Map(ARCGIS_SERVICES.map((s) => [s.key, s]));
76
+ const DATE_NOTE = "ArcGIS date fields are epoch MILLISECONDS (e.g. DUE_DATE, OPENDATE, CLOSEDATE); a negative/sentinel value (≈ year 1900) is a placeholder, not a real date — surfaced verbatim, parse client-side.";
77
+ // ─── SSRF-guarded fetch (fixed allowlist base + hostname assertion) ──
78
+ async function getArcgisQuery(svc, params) {
79
+ const url = `${svc.base}/query?${params.toString()}`;
80
+ const allowedHost = new URL(svc.base).hostname;
81
+ const built = new URL(url);
82
+ if (built.hostname !== allowedHost || built.protocol !== "https:") {
83
+ throw new ToolErrorCarrier({
84
+ kind: "invalid_input",
85
+ message: `Constructed ArcGIS URL host ${JSON.stringify(built.hostname)} (${built.protocol}) does not match the allowlisted service host ${JSON.stringify(allowedHost)} over https — refusing to fetch (SSRF safety).`,
86
+ retryable: false,
87
+ upstreamEndpoint: `arcgis:${svc.key}`,
88
+ });
89
+ }
90
+ return getJson(url, { label: `arcgis:${svc.key}`, redirect: "error" });
91
+ }
92
+ // ArcGIS returns HTTP 200 + `{error:{code,message}}` for BOTH a bad query AND a
93
+ // transient server hiccup, and the messages OVERLAP (live-verified 2026-07-19):
94
+ // - "Failed to execute query." ⇒ a bad column/field/where (the caller's query).
95
+ // - "Unable to complete operation." ⇒ AMBIGUOUS — a bad SQL syntax OR a transient
96
+ // server blip (observed intermittently on a valid query).
97
+ // So classify a clearly-input message as invalid_input (non-retryable), but treat
98
+ // the ambiguous/generic one as upstream_unavailable (RETRYABLE) — misclassifying a
99
+ // server blip as invalid_input would falsely blame the caller for an outage.
100
+ const ARCGIS_INPUT_ERROR_RE = /failed to execute query|invalid|parameter|field|syntax|parse|not supported|does not exist|cannot find|out of range/i;
101
+ /** An ArcGIS `{error:{code,message}}` body (HTTP 200) ⇒ throw the right kind. */
102
+ function throwIfArcgisError(svc, body) {
103
+ const e = body?.error;
104
+ if (e && typeof e === "object") {
105
+ const code = num(e.code);
106
+ const msg = String(e.message ?? "ArcGIS query error");
107
+ const inputish = code === 400 && ARCGIS_INPUT_ERROR_RE.test(msg);
108
+ throw new ToolErrorCarrier({
109
+ kind: inputish ? "invalid_input" : "upstream_unavailable",
110
+ message: `ArcGIS ${svc.key} error${code !== null ? ` (${code})` : ""}: ${msg}`.slice(0, 300),
111
+ retryable: !inputish,
112
+ upstreamEndpoint: `arcgis:${svc.key}`,
113
+ });
114
+ }
115
+ }
116
+ /**
117
+ * Query rows from a curated ArcGIS REST feature layer. `service` is an allowlist
118
+ * enum; `where` (SQL-ish filter, default 1=1), `outFields` (default *),
119
+ * `orderByFields`, `limit`/`offset`. Returns curated record attributes + honest
120
+ * `_meta` (totalAvailable = the layer's exact match count via a returnCountOnly
121
+ * companion; epoch-ms date disclosure).
122
+ */
123
+ export async function featureQuery(args) {
124
+ const svc = SERVICE_BY_KEY.get(args.service);
125
+ if (!svc) {
126
+ throw new ToolErrorCarrier({
127
+ kind: "invalid_input",
128
+ message: `Unknown ArcGIS service ${JSON.stringify(args.service)}. Allowed: ${ARCGIS_SERVICES.map((s) => s.key).join(", ")}.`,
129
+ retryable: false,
130
+ });
131
+ }
132
+ const limit = args.limit ?? 50;
133
+ const offset = args.offset ?? 0;
134
+ const where = args.where && args.where.trim() ? args.where.trim() : "1=1";
135
+ const outFields = args.outFields && args.outFields.trim() ? args.outFields.trim() : "*";
136
+ const filtersApplied = ["service"];
137
+ if (args.where && args.where.trim())
138
+ filtersApplied.push("where");
139
+ // ── Primary rows query. ──
140
+ const rowParams = new URLSearchParams();
141
+ rowParams.set("where", where);
142
+ rowParams.set("outFields", outFields);
143
+ rowParams.set("returnGeometry", "false");
144
+ rowParams.set("resultOffset", String(offset));
145
+ rowParams.set("resultRecordCount", String(limit));
146
+ if (args.orderByFields && args.orderByFields.trim())
147
+ rowParams.set("orderByFields", args.orderByFields.trim());
148
+ rowParams.set("f", "json");
149
+ let body;
150
+ try {
151
+ body = await getArcgisQuery(svc, rowParams);
152
+ }
153
+ catch (e) {
154
+ if (e instanceof ToolErrorCarrier)
155
+ throw e;
156
+ if (e instanceof SyntaxError)
157
+ throw driftError(`arcgis:${svc.key}`, "ArcGIS returned a non-JSON body at HTTP 200 — schema drift.");
158
+ throw e;
159
+ }
160
+ throwIfArcgisError(svc, body); // P2: a 200 {error} body ⇒ throw (never a fake empty)
161
+ const b = (body ?? {});
162
+ // P4: features MUST be an array (a missing/non-array, with no error, is drift).
163
+ if (!Array.isArray(b.features)) {
164
+ throw driftError(`arcgis:${svc.key}`, "ArcGIS shape drift — response.features must be an array.");
165
+ }
166
+ const records = b.features.map((f) => f?.attributes ?? {});
167
+ const returned = records.length;
168
+ // ── P1: totalAvailable = the layer's exact match count (returnCountOnly
169
+ // companion). BEST-EFFORT: any count failure ⇒ null + note (rows kept). ──
170
+ let totalAvailable = null;
171
+ let countNote = null;
172
+ try {
173
+ const countParams = new URLSearchParams();
174
+ countParams.set("where", where);
175
+ countParams.set("returnCountOnly", "true");
176
+ countParams.set("f", "json");
177
+ const cbody = await getArcgisQuery(svc, countParams);
178
+ throwIfArcgisError(svc, cbody);
179
+ const c = num(cbody?.count);
180
+ if (c !== null)
181
+ totalAvailable = c;
182
+ else
183
+ countNote = "The returnCountOnly companion returned an unexpected shape — totalAvailable is unknown (null); the rows are still complete.";
184
+ }
185
+ catch {
186
+ countNote = "The returnCountOnly companion failed (transient) — totalAvailable is unknown (null); the rows are still returned.";
187
+ }
188
+ const exceeded = b.exceededTransferLimit === true;
189
+ const hasMore = totalAvailable !== null ? offset + returned < totalAvailable : (exceeded || returned >= limit);
190
+ const nextOffset = hasMore ? offset + returned : null;
191
+ const notes = [`Source: ${svc.label} (ArcGIS REST feature layer, keyless). ${svc.note}`, DATE_NOTE];
192
+ if (countNote)
193
+ notes.push(countNote);
194
+ return withMeta({ service: svc.key, records }, {
195
+ source: `${new URL(svc.base).hostname} via ArcGIS REST (keyless)`,
196
+ keylessMode: true,
197
+ returned,
198
+ totalAvailable,
199
+ filtersApplied,
200
+ filtersDropped: [],
201
+ fieldsUnavailable: [],
202
+ pagination: { offset, limit, hasMore, nextOffset },
203
+ notes,
204
+ });
205
+ }
206
+ //# sourceMappingURL=arcgis-feature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arcgis-feature.js","sourceRoot":"","sources":["../src/arcgis-feature.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * arcgis-hub.ts — ArcGIS Hub dataset DISCOVERY (`hub.arcgis.com/api/v3/datasets`),
3
+ * a NEW keyless-first SLED-discovery platform (loop cycle 9).
4
+ *
5
+ * WHAT IT ADDS: a large fraction of US state/local/regional/tribal (SLED) open
6
+ * data — especially GIS / infrastructure / permits / boundaries / procurement —
7
+ * is published on ArcGIS Hub, NOT on Socrata or CKAN. This tool DISCOVERS those
8
+ * datasets (keyword search over the whole Hub) so a B2G researcher can find what
9
+ * fragmented local data exists. It is the ArcGIS sibling of
10
+ * `socrata_discover_datasets` / `datagov_search_datasets`.
11
+ *
12
+ * The module writes ZERO fetch/coercion/error/meta code — it REUSES `getJson`
13
+ * (redirect:"error") / `driftError` / `num`·`str` (coerce.ts, null-never-0 /
14
+ * null-never-empty-string) / `withMeta`·`buildMeta`, mirroring datagov-catalog.ts.
15
+ * KEYLESS: no key seam (no token is ever read/sent).
16
+ *
17
+ * ★ SSRF: the host is a compile-time literal (`ARCGIS_HUB_HOST`). Every input
18
+ * (`q` / `filter[openData]` / `page[size]` / `page[start]`) rides in a
19
+ * MODULE-BUILT `URLSearchParams` assembled key-by-key from validated typed args
20
+ * — NO raw-query / raw-host passthrough (this is a FIXED-host catalog search, so
21
+ * there is no per-jurisdiction host vector like socrata_query has). A
22
+ * post-construction hostname/protocol assertion + `redirect:"error"` lock it.
23
+ *
24
+ * ★ PROVENANCE (the crux — different trust posture from our other sources): ArcGIS
25
+ * Hub is a GLOBAL, OPEN publishing platform. Anyone — a US city, a foreign
26
+ * government, an NGO, a company — can publish, so results are NOT all US and NOT
27
+ * all governmental. This tool is therefore explicitly a DISCOVERY aid, NOT a
28
+ * curated official-source allowlist (unlike `socrata_query`, whose hosts are a
29
+ * vetted allowlist). Each result surfaces `owner`/`orgName`/`source`/`region`
30
+ * VERBATIM so the consumer can VET the publisher, and every response carries the
31
+ * global-platform disclosure. `openDataOnly` (default true) biases toward items
32
+ * the publisher designated as open data. DISCOVERY ONLY — to read rows, follow
33
+ * the dataset on its own ArcGIS endpoint (a guarded row-query tool is a planned
34
+ * addition; ArcGIS feature services live on arbitrary hosts, which needs its own
35
+ * SSRF design).
36
+ *
37
+ * ★ HONESTY PILLARS (captured live 2026-07-19):
38
+ * P1 (real total): the v3 response carries `meta.total` (== `meta.stats.totalCount`)
39
+ * — the EXACT match count across the Hub. `totalAvailable = num(meta.total)`
40
+ * (NEVER data.length); a page is `truncated` when `offset + returned < total`.
41
+ * Pagination is a 1-based record offset (`page[start] = offset + 1`).
42
+ * P2: getJson→fetchWithRetry THROWS on 429 (rate_limited) / 5xx
43
+ * (upstream_unavailable) / timeout — NEVER a fake empty. A genuine no-match
44
+ * (`data:[]`, total 0) ⇒ honest empty (datasets:[], returned:0, total:0,
45
+ * complete:true).
46
+ * P3: every scalar via `str` (null-never-empty) / booleans preserved / `num`
47
+ * for counts (null-never-0). A missing owner/source/region is null, never "".
48
+ * P4: `body.data` absent/non-array ⇒ driftError; a 200 non-JSON body ⇒
49
+ * schema_drift via the catch-ladder (ToolErrorCarrier rethrow FIRST so a
50
+ * 429/5xx keeps its taxonomy → SyntaxError→driftError → bare rethrow).
51
+ */
52
+ import { num } from "./coerce.js";
53
+ import { type MetaBundle } from "./meta.js";
54
+ export { num };
55
+ export declare const ARCGIS_HUB_HOST = "hub.arcgis.com";
56
+ export type HubDataset = {
57
+ id: string | null;
58
+ name: string | null;
59
+ description: string | null;
60
+ owner: string | null;
61
+ orgName: string | null;
62
+ source: string | null;
63
+ region: string | null;
64
+ type: string | null;
65
+ sector: string | null;
66
+ keywords: string[];
67
+ downloadable: boolean | null;
68
+ hasApi: boolean | null;
69
+ created: string | null;
70
+ modified: string | null;
71
+ landingPage: string | null;
72
+ itemId: string | null;
73
+ };
74
+ export type ArcgisHubDiscoverArgs = {
75
+ query: string;
76
+ openDataOnly?: boolean;
77
+ limit?: number;
78
+ offset?: number;
79
+ };
80
+ /**
81
+ * Search ArcGIS Hub datasets by keyword. Filters: `query` (→q, REQUIRED, ≥2
82
+ * non-whitespace chars), `openDataOnly` (default true → filter[openData]=true,
83
+ * biases toward publisher-designated open data), `limit` (→page[size]), `offset`
84
+ * (→page[start]=offset+1, 1-based). The query is MODULE-BUILT from validated typed
85
+ * args (NO raw passthrough). Returns curated dataset rows + honest `_meta`:
86
+ * totalAvailable = the EXACT Hub match count (meta.total, P1), the global-platform
87
+ * provenance disclosure, and per-row publisher fields for vetting.
88
+ */
89
+ export declare function discoverDatasets(args: ArcgisHubDiscoverArgs): Promise<MetaBundle>;
90
+ //# sourceMappingURL=arcgis-hub.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arcgis-hub.d.ts","sourceRoot":"","sources":["../src/arcgis-hub.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AAIH,OAAO,EAAE,GAAG,EAAO,MAAM,aAAa,CAAC;AACvC,OAAO,EAAY,KAAK,UAAU,EAAqB,MAAM,WAAW,CAAC;AAIzE,OAAO,EAAE,GAAG,EAAE,CAAC;AAGf,eAAO,MAAM,eAAe,mBAAmB,CAAC;AAahD,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAkEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,UAAU,CAAC,CA6FrB"}