@cliwant/mcp-sam-gov 1.1.0 → 1.2.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 · FEMA ほか **31 のキーレス連邦データソース、111 ツール。**
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 · FEMA ほか **34 の連邦データソース、116 ツール。** キーレス優先 — Census business-patterns と FRED のみ無料キーが必要で、残り 32 ソースはキー不要。
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)
@@ -19,13 +19,13 @@
19
19
  | 🕵️ **パートナー・企業の検証** | "この企業をスクリーニング: OFAC 制裁・SAM 除外・単一監査指摘・銀行健全性・EPA 遵守" | OFAC, SAM, FAC, FDIC, EPA ECHO |
20
20
  | 📈 **財務開示 (SEC)** | "この上場企業の売上推移 + 最新 10-K" | SEC EDGAR |
21
21
  | ⚖️ **規制・立法** | "今四半期の VA サイバーセキュリティ規則? 進行中の Regulations.gov docket?" | Federal Register, Regulations.gov, eCFR, FAR/DFARS, Congress.gov, GovInfo |
22
- | 💲 **価格・労務・財政** | "GSA CALC 労務単価バンド、この郡の SCA 賃金決定、CPI エスカレーション" | GSA CALC, SAM WD, BLS, US Treasury |
22
+ | 💲 **価格・労務・財政** | "GSA CALC 労務単価バンド、この郡の SCA 賃金決定、CPI エスカレーション、出張 per-diem 上限" | GSA CALC, SAM WD, BLS, US Census CBP, FRED, US Treasury, GSA per-diem |
23
23
  | 🏥 **医療・研究資金** | "このテーマの NIH/NSF grant、募集中の臨床試験、この医師への業界支払" | NIH RePORTER, NSF, ClinicalTrials, CMS, NPPES |
24
24
  | 🛡 **サイバー遵守** | "この CVE は CISA KEV 必須パッチ一覧にあるか?" | NVD, CISA KEV |
25
25
  | 🌐 **貿易・地理・災害** | "この品目の HTS 関税、この住所の Census tract、この州の FEMA 宣言" | USITC HTS, Census, FEMA, Socrata, CKAN |
26
26
  | 🎓 **grant・データセット** | "過去 30 日のサイバーセキュリティ grant、連邦オープンデータセット発見" | Grants.gov, data.gov |
27
27
 
28
- **31 のキーレス連邦データソース、合計 111 ツール。API キー 0。** (初期の 52 ツール版でおおよそ p50 ~0.25s / p95 ~0.8s を計測 — ソースや上流負荷で変動する近似値であり保証値ではありません。)
28
+ **34 の連邦データソース、合計 116 ツール キーレス優先: Census business-patterns と FRED のみ無料キーが必要で、残り 32 ソースはキー不要。** (初期の 52 ツール版でおおよそ p50 ~0.25s / p95 ~0.8s を計測 — ソースや上流負荷で変動する近似値であり保証値ではありません。)
29
29
 
30
30
  ---
31
31
 
@@ -52,7 +52,7 @@ Claude Code (CLI) を既に使用している場合:
52
52
  /plugin install cliwant/mcp-sam-gov
53
53
  ```
54
54
 
55
- MCP サーバー + Claude が 111 ツールをいつ・どう呼ぶかを教える [SKILL.md ワークフローガイド](./skills/sam-gov/SKILL.md) を同時登録。
55
+ MCP サーバー + Claude が 116 ツールをいつ・どう呼ぶかを教える [SKILL.md ワークフローガイド](./skills/sam-gov/SKILL.md) を同時登録。
56
56
 
57
57
  ### 🔵 パス 3 — Codex / Cursor / Continue / Gemini 等の手動インストール
58
58
 
@@ -164,22 +164,23 @@ npm install --omit=dev
164
164
 
165
165
  ---
166
166
 
167
- ## ツールカタログ (111)
167
+ ## ツールカタログ (116)
168
168
 
169
- ワークフロー別グループ。全ツールは既定でキーレス。全 per-tool 一覧と入力 schema・誠実性 caveat の原文は [英語 README のカタログセクション](./README.md#tool-catalog-111-tools) を正とします。
169
+ ワークフロー別グループ。キーレス優先 — 大半はキー不要、Census business-patterns と FRED は無料キーが必要。全 per-tool 一覧と入力 schema・誠実性 caveat の原文は [英語 README のカタログセクション](./README.md#tool-catalog-116-tools) を正とします。
170
170
 
171
171
  - **案件 + ソリシテーション — 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`
172
172
  - **spending・受注・競合 — USAspending + FPDS + GAO (29)**: `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` `fpds_search_awards` `gao_protest_lookup`
173
173
  - **パートナー・企業の検証 — OFAC · SAM · FAC · FDIC · EPA (14)**: `ofac_screen_entity` `sam_check_exclusions` `sam_integrity_lookup` `fac_search_audits` `fac_get_findings` `fdic_search_institutions` `fdic_institution_financials` `fdic_risk_ratios` `fdic_institution_history` `fdic_branch_deposits` `fdic_bank_failures` `fdic_industry_summary` `echo_search_facilities` `echo_facility_report`
174
174
  - **財務開示 — SEC EDGAR (8)**: `edgar_lookup_cik` `edgar_company_filings` `edgar_company_facts` `edgar_company_concept` `edgar_xbrl_frames` `edgar_full_text_search` `edgar_filing_index` `edgar_daily_filing_index`
175
175
  - **規制・立法 — Federal Register · Regulations.gov · eCFR · FAR · Congress · GovInfo (18)**: `fed_register_search_documents` `fed_register_get_document` `fed_register_public_inspection` `fed_register_list_agencies` `regulations_search_dockets` `regulations_search_documents` `regulations_search_comments` `regulations_get_docket` `ecfr_search` `ecfr_list_titles` `far_clause_lookup` `far_search` `far_compliance_matrix` `congress_search_bills` `congress_get_bill` `govinfo_search_packages` `govinfo_get_package` `govinfo_list_collections`
176
- - **価格・労務・財政 — GSA CALC · SAM WD · BLS · Treasury (10)**: `gsa_benchmark_labor_rates` `sam_search_wage_determinations` `sam_get_wage_rates` `bls_timeseries` `bls_oews_wages` `bls_qcew` `treasury_debt_to_penny` `treasury_avg_interest_rates` `treasury_monthly_statement` `treasury_query_dataset`
176
+ - **価格・労務・財政 — GSA CALC · SAM WD · BLS · Census CBP · FRED · Treasury · GSA per-diem (14)**: `gsa_benchmark_labor_rates` `sam_search_wage_determinations` `sam_get_wage_rates` `bls_timeseries` `bls_oews_wages` `bls_qcew` `treasury_debt_to_penny` `treasury_avg_interest_rates` `treasury_monthly_statement` `treasury_query_dataset` `census_business_patterns`(無料 CENSUS_API_KEY 必要) `fred_search_series`(無料 FRED_API_KEY 必要) `fred_series_observations`(無料 FRED_API_KEY 必要) `gsa_perdiem_rates`(DEMO_KEY キーレス)
177
177
  - **医療・研究資金 — NIH · NSF · ClinicalTrials · CMS · NPPES (9)**: `nih_reporter_search_projects` `nsf_search_awards` `nsf_get_award` `clinicaltrials_search_studies` `clinicaltrials_get_study` `clinicaltrials_facet_counts` `cms_search_datasets` `cms_query_dataset` `nppes_lookup_provider`
178
178
  - **サイバー遵守 — NVD + CISA KEV (2)**: `cve_lookup` `cisa_kev_lookup`
179
179
  - **貿易・関税 — USITC (1)**: `hts_lookup`
180
180
  - **地理・災害・州/市オープンデータ — Census · FEMA · Socrata · CKAN (8)**: `census_geocode_address` `census_geographies_by_coordinates` `fema_disaster_declarations` `fema_search_public_assistance` `socrata_discover_datasets` `socrata_query` `ckan_discover_datasets` `ckan_query`
181
181
  - **データセット発見 — data.gov (1)**: `datagov_search_datasets`
182
182
  - **中小企業 — SBA (1)**: `sba_size_standard`
183
+ - **サーバーユーティリティ — キー探索 (1)**: `api_key_status`(各ソースに必要なキー・required/optional・登録 URL・現在の設定有無を列挙; 値は表示しない)
183
184
 
184
185
  ---
185
186
 
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 · FEMA 외 **31keyless 연방 데이터 소스, 111개 도구.**
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 · FEMA 외 **34개 연방 데이터 소스, 116개 도구.** keyless 우선 — Census business-patterns 와 FRED 만 무료 키가 필요하고 나머지 32개 소스는 키가 필요 없습니다.
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)
@@ -19,13 +19,13 @@
19
19
  | 🕵️ **파트너·기업 검증** | "이 업체 스크리닝: OFAC 제재·SAM 배제·단일감사 지적·은행 건전성·EPA 준수" | OFAC, SAM, FAC, FDIC, EPA ECHO |
20
20
  | 📈 **재무 공시 (SEC)** | "이 상장사 매출 추이 + 최신 10-K" | SEC EDGAR |
21
21
  | ⚖️ **규정·입법** | "이번 분기 VA 사이버보안 규정? 열려있는 Regulations.gov docket?" | Federal Register, Regulations.gov, eCFR, FAR/DFARS, Congress.gov, GovInfo |
22
- | 💲 **가격·노무·재정** | "GSA CALC 노무 단가 밴드, 이 카운티 SCA 임금결정, CPI 에스컬레이션" | GSA CALC, SAM WD, BLS, US Treasury |
22
+ | 💲 **가격·노무·재정** | "GSA CALC 노무 단가 밴드, 이 카운티 SCA 임금결정, CPI 에스컬레이션, 출장 per-diem 상한" | GSA CALC, SAM WD, BLS, US Census CBP, FRED, US Treasury, GSA per-diem |
23
23
  | 🏥 **보건·연구 자금** | "이 주제 NIH/NSF grant, 모집 중 임상시험, 이 의사에 대한 산업계 지급" | NIH RePORTER, NSF, ClinicalTrials, CMS, NPPES |
24
24
  | 🛡 **사이버 준수** | "이 CVE 가 CISA KEV 필수 패치 목록에 있나?" | NVD, CISA KEV |
25
25
  | 🌐 **무역·지리·재난** | "이 품목 HTS 관세, 이 주소 Census tract, 이 주의 FEMA 선포" | USITC HTS, Census, FEMA, Socrata, CKAN |
26
26
  | 🎓 **grant·데이터셋** | "최근 30일 사이버보안 grant, 연방 오픈 데이터셋 발굴" | Grants.gov, data.gov |
27
27
 
28
- **31keyless 연방 데이터 소스, 총 111도구. API 0개.** (초기 52-도구 빌드 기준 대략 p50 ~0.25s / p95 ~0.8s 측정 — 소스·업스트림 부하에 따라 변동하는 근사치이며 보장값이 아님.)
28
+ **34개 연방 데이터 소스, 총 116도구 keyless 우선: Census business-patterns 와 FRED 만 무료 키가 필요하고 나머지 32개 소스는 키가 필요 없습니다.** (초기 52-도구 빌드 기준 대략 p50 ~0.25s / p95 ~0.8s 측정 — 소스·업스트림 부하에 따라 변동하는 근사치이며 보장값이 아님.)
29
29
 
30
30
  ---
31
31
 
@@ -52,7 +52,7 @@ PowerShell, npm 등 필요 없음.
52
52
  /plugin install cliwant/mcp-sam-gov
53
53
  ```
54
54
 
55
- MCP 서버 + Claude 가 111개 도구를 언제 / 어떻게 호출할지 가르치는 [SKILL.md 워크플로 가이드](./skills/sam-gov/SKILL.md) 동시 등록.
55
+ MCP 서버 + Claude 가 116개 도구를 언제 / 어떻게 호출할지 가르치는 [SKILL.md 워크플로 가이드](./skills/sam-gov/SKILL.md) 동시 등록.
56
56
 
57
57
  ### 🔵 경로 3 — Codex / Cursor / Continue / Gemini 등 수동 설치
58
58
 
@@ -164,22 +164,23 @@ npm install --omit=dev
164
164
 
165
165
  ---
166
166
 
167
- ## 도구 카탈로그 (111개)
167
+ ## 도구 카탈로그 (116개)
168
168
 
169
- 워크플로별 그룹. 모든 도구는 기본 keyless. 전체 per-tool 목록과 입력 schema·정직성 caveat 원문은 [영문 README 의 카탈로그 섹션](./README.md#tool-catalog-111-tools)을 기준으로 하세요.
169
+ 워크플로별 그룹. keyless 우선 대부분 키가 필요 없고, Census business-patterns 와 FRED 는 무료 키가 필요합니다. 전체 per-tool 목록과 입력 schema·정직성 caveat 원문은 [영문 README 의 카탈로그 섹션](./README.md#tool-catalog-116-tools)을 기준으로 하세요.
170
170
 
171
171
  - **입찰 + 솔리시테이션 — 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`
172
172
  - **spending·수주·경쟁 — USAspending + FPDS + GAO (29)**: `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` `fpds_search_awards` `gao_protest_lookup`
173
173
  - **파트너·기업 검증 — OFAC · SAM · FAC · FDIC · EPA (14)**: `ofac_screen_entity` `sam_check_exclusions` `sam_integrity_lookup` `fac_search_audits` `fac_get_findings` `fdic_search_institutions` `fdic_institution_financials` `fdic_risk_ratios` `fdic_institution_history` `fdic_branch_deposits` `fdic_bank_failures` `fdic_industry_summary` `echo_search_facilities` `echo_facility_report`
174
174
  - **재무 공시 — SEC EDGAR (8)**: `edgar_lookup_cik` `edgar_company_filings` `edgar_company_facts` `edgar_company_concept` `edgar_xbrl_frames` `edgar_full_text_search` `edgar_filing_index` `edgar_daily_filing_index`
175
175
  - **규정·입법 — Federal Register · Regulations.gov · eCFR · FAR · Congress · GovInfo (18)**: `fed_register_search_documents` `fed_register_get_document` `fed_register_public_inspection` `fed_register_list_agencies` `regulations_search_dockets` `regulations_search_documents` `regulations_search_comments` `regulations_get_docket` `ecfr_search` `ecfr_list_titles` `far_clause_lookup` `far_search` `far_compliance_matrix` `congress_search_bills` `congress_get_bill` `govinfo_search_packages` `govinfo_get_package` `govinfo_list_collections`
176
- - **가격·노무·재정 — GSA CALC · SAM WD · BLS · Treasury (10)**: `gsa_benchmark_labor_rates` `sam_search_wage_determinations` `sam_get_wage_rates` `bls_timeseries` `bls_oews_wages` `bls_qcew` `treasury_debt_to_penny` `treasury_avg_interest_rates` `treasury_monthly_statement` `treasury_query_dataset`
176
+ - **가격·노무·재정 — GSA CALC · SAM WD · BLS · Census CBP · FRED · Treasury · GSA per-diem (14)**: `gsa_benchmark_labor_rates` `sam_search_wage_determinations` `sam_get_wage_rates` `bls_timeseries` `bls_oews_wages` `bls_qcew` `treasury_debt_to_penny` `treasury_avg_interest_rates` `treasury_monthly_statement` `treasury_query_dataset` `census_business_patterns`(무료 CENSUS_API_KEY 필요) `fred_search_series`(무료 FRED_API_KEY 필요) `fred_series_observations`(무료 FRED_API_KEY 필요) `gsa_perdiem_rates`(DEMO_KEY keyless)
177
177
  - **보건·연구 자금 — NIH · NSF · ClinicalTrials · CMS · NPPES (9)**: `nih_reporter_search_projects` `nsf_search_awards` `nsf_get_award` `clinicaltrials_search_studies` `clinicaltrials_get_study` `clinicaltrials_facet_counts` `cms_search_datasets` `cms_query_dataset` `nppes_lookup_provider`
178
178
  - **사이버 준수 — NVD + CISA KEV (2)**: `cve_lookup` `cisa_kev_lookup`
179
179
  - **무역·관세 — USITC (1)**: `hts_lookup`
180
180
  - **지리·재난·주/시 오픈데이터 — Census · FEMA · Socrata · CKAN (8)**: `census_geocode_address` `census_geographies_by_coordinates` `fema_disaster_declarations` `fema_search_public_assistance` `socrata_discover_datasets` `socrata_query` `ckan_discover_datasets` `ckan_query`
181
181
  - **데이터셋 발굴 — data.gov (1)**: `datagov_search_datasets`
182
182
  - **소상공인 — SBA (1)**: `sba_size_standard`
183
+ - **서버 유틸리티 — 키 발견 (1)**: `api_key_status`(각 소스에 필요한 키·required/optional·발급 URL·현재 설정 여부 나열; 값은 노출 안 함)
183
184
 
184
185
  ---
185
186
 
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** MCP server for US federal contracting + spending + regulation + partner vetting. **111 tools across 31 keyless federal data sources** that work today, in any AI agent.
7
+ The most comprehensive **keyless-first** MCP server for US federal contracting + spending + regulation + partner vetting. **116 tools across 34 federal data sources** keyless-first: only Census business-patterns and FRED need a free key; the other 32 sources 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** MCP server for US federal contracting + spend
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, 111 tools across 31 sources |
53
+ | 5+ separate vendor APIs / scrapers | 1 unified surface, 116 tools across 34 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 |
@@ -68,13 +68,13 @@ The federal data this wraps is **public domain**. There is no good reason it sho
68
68
  | 🕵️ **Entity & partner vetting** | "Screen this firm: OFAC sanctions, SAM exclusions, single-audit findings, bank health, EPA compliance" | OFAC, SAM, FAC, FDIC, EPA ECHO |
69
69
  | 📈 **Financial disclosure (SEC)** | "Pull this public company's revenue trend and latest 10-K filings" | SEC EDGAR |
70
70
  | ⚖️ **Regulatory & legislative** | "What VA cybersecurity rules were published this quarter? Any open Regulations.gov dockets?" | Federal Register, Regulations.gov, eCFR, FAR/DFARS, Congress.gov, GovInfo |
71
- | 💲 **Pricing, labor & fiscal** | "GSA CALC labor-rate band for a systems analyst; SCA wage determination for this county; CPI escalation" | GSA CALC, SAM WDs, BLS, US Treasury |
71
+ | 💲 **Pricing, labor & fiscal** | "GSA CALC labor-rate band for a systems analyst; SCA wage determination for this county; CPI escalation; per-diem cap for a trip" | GSA CALC, SAM WDs, BLS, US Census CBP, FRED, US Treasury, GSA per-diem |
72
72
  | 🏥 **Health & research funding** | "NIH/NSF grants on this topic; recruiting clinical trials; industry payments to this physician" | NIH RePORTER, NSF, ClinicalTrials.gov, CMS Open Payments, NPPES |
73
73
  | 🛡 **Cyber compliance** | "Is this CVE on the CISA KEV must-patch list?" | NVD, CISA KEV |
74
74
  | 🌐 **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 |
75
75
  | 🎓 **Grants & datasets** | "Cybersecurity grants posted in the last 30 days; discover federal open datasets" | Grants.gov, data.gov |
76
76
 
77
- **111 tools across 31 keyless federal data sources. Zero API keys.** (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.)
77
+ **116 tools across 34 federal data sources keyless-first: only Census business-patterns and FRED need a free key; the other 32 sources 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.)
78
78
 
79
79
  ---
80
80
 
@@ -101,7 +101,7 @@ If you already use Claude Code (the CLI):
101
101
  /plugin install cliwant/mcp-sam-gov
102
102
  ```
103
103
 
104
- 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 111 tools.
104
+ 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 116 tools.
105
105
 
106
106
  ### 🔵 Path 3 — Manual install for any MCP host (Codex, Cursor, Continue, Gemini)
107
107
 
@@ -169,7 +169,7 @@ Then point your host config at the absolute path:
169
169
 
170
170
  (Or skip this entirely — use Path 1's `.mcpb` and it auto-configures.)
171
171
 
172
- Restart Claude Desktop fully (system tray quit on Windows / Quit menu on macOS), then look for the 🔨 icon. You should see "sam-gov (111 tools)".
172
+ Restart Claude Desktop fully (system tray quit on Windows / Quit menu on macOS), then look for the 🔨 icon. You should see "sam-gov (116 tools)".
173
173
 
174
174
  ### Claude Code
175
175
 
@@ -370,9 +370,9 @@ Get one free (instant, no wait) at [api.data.gov/signup](https://api.data.gov/si
370
370
 
371
371
  ---
372
372
 
373
- ## Tool catalog (111 tools)
373
+ ## Tool catalog (116 tools)
374
374
 
375
- Grouped by workflow. Every tool is keyless by default; a handful 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.
375
+ Grouped by workflow. Keyless-first most tools need no key; Census business-patterns and FRED 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.
376
376
 
377
377
  <details>
378
378
  <summary><b>Opportunities & solicitations — SAM.gov + Grants.gov (10 tools)</b></summary>
@@ -479,7 +479,7 @@ Grouped by workflow. Every tool is keyless by default; a handful gain higher lim
479
479
  </details>
480
480
 
481
481
  <details>
482
- <summary><b>Pricing, labor & fiscal — GSA CALC · SAM WDs · BLS · US Treasury (10 tools)</b></summary>
482
+ <summary><b>Pricing, labor & fiscal — GSA CALC · SAM WDs · BLS · Census CBP · FRED · US Treasury · GSA per-diem (14 tools)</b></summary>
483
483
 
484
484
  - `gsa_benchmark_labor_rates` — GSA CALC awarded ceiling-rate market band for a labor category (a distribution, not one price)
485
485
  - `sam_search_wage_determinations` — find SCA / Davis-Bacon wage determinations for a locality
@@ -491,6 +491,10 @@ Grouped by workflow. Every tool is keyless by default; a handful gain higher lim
491
491
  - `treasury_avg_interest_rates` — average interest rate the Treasury pays by security type
492
492
  - `treasury_monthly_statement` — Monthly Treasury Statement: receipts, outlays, deficit/surplus by month
493
493
  - `treasury_query_dataset` — escape-hatch query over 5 confirmed Treasury Fiscal Data datasets
494
+ - `census_business_patterns` — establishments / employment / annual payroll by NAICS × geography (Census CBP; requires free CENSUS_API_KEY)
495
+ - `fred_search_series` — search the FRED macroeconomic series catalog (GDP/CPI/rates/unemployment; requires free FRED_API_KEY)
496
+ - `fred_series_observations` — time-series observations for a FRED series (missing values → null; requires free FRED_API_KEY)
497
+ - `gsa_perdiem_rates` — federal travel per-diem: monthly lodging + M&IE meals caps by city+state or zip (keyless via DEMO_KEY)
494
498
  </details>
495
499
 
496
500
  <details>
@@ -545,6 +549,12 @@ Grouped by workflow. Every tool is keyless by default; a handful gain higher lim
545
549
  - `sba_size_standard` — SBA small-business size standard for a 6-digit NAICS (set-aside eligibility gate)
546
550
  </details>
547
551
 
552
+ <details>
553
+ <summary><b>Server utilities — key discovery (1 tool)</b></summary>
554
+
555
+ - `api_key_status` — list which API keys the server can use, required vs optional, signup URLs, and whether each is currently set (value never shown)
556
+ </details>
557
+
548
558
  ---
549
559
 
550
560
  ## Reliability & offline snapshots
@@ -0,0 +1,74 @@
1
+ /**
2
+ * gsa-perdiem.ts — GSA Federal Travel Per-Diem lookup (`api.gsa.gov`, base
3
+ * `/travel/perdiem/v2`) — ADR-0050. The lodging + M&IE rate ceilings the federal
4
+ * government reimburses for official travel, by city/state or ZIP for a given year.
5
+ *
6
+ * WHAT IT ADDS: a NEW travel-cost lane (the per-diem authority) on the SAME
7
+ * `api.gsa.gov` host as datagov-catalog.ts, so it REUSES the audited `datagovKey.ts`
8
+ * key seam VERBATIM (keyHeader / keyModeLabel / pushKeyNote) — keyless by default via
9
+ * the shared DEMO_KEY, upgraded by DATA_GOV_API_KEY. The key rides ONLY in the
10
+ * X-Api-Key header — NEVER the URL / label / _meta / a log (the K-test). This module
11
+ * writes ZERO fetch/coercion/error/meta code: it REUSES `getJson` (redirect:"error",
12
+ * the X-Api-Key header) / `driftError` / `num`·`str` (coerce.ts) / `withMeta`·
13
+ * `buildMeta`, and MIRRORS the datagov-catalog schema_drift catch-ladder verbatim.
14
+ *
15
+ * ★ SSRF: the host is a compile-time literal (`GSA_PERDIEM_HOST`). The two lookup
16
+ * modes ride FIXED path templates; every caller value (city/state/zip/year) is
17
+ * charclass-validated THEN `encodeURIComponent`-escaped into a single path segment
18
+ * (no raw passthrough, no query steer). A post-construction hostname/protocol
19
+ * assertion + `redirect:"error"` lock it (fail closed on any off-host 3xx — a 3xx
20
+ * off api.gsa.gov could carry the X-Api-Key header away).
21
+ *
22
+ * ★ HONESTY (ADR-0050 P1–P5, live-verified 2026-07-15):
23
+ * [INPUT] EITHER (city + state) OR zip — supplying BOTH, or NEITHER, ⇒ invalid_input
24
+ * with 0 fetch (an ambiguous/empty lookup is a caller error, never a guess).
25
+ * [P1] the API returns the COMPLETE rate set for the lookup (no pagination) ⇒
26
+ * totalAvailable = the flattened row count, complete:true. NEVER fabricated.
27
+ * [P2] `errors` non-null ⇒ invalid_input surfacing the message (never a fake
28
+ * empty); a genuine no-match (rates:[] / rate:[]) ⇒ honest empty (returned:0,
29
+ * complete:true); a 429 (DEMO_KEY ~10/hr) ⇒ rate_limited THROW honoring
30
+ * Retry-After; a 5xx ⇒ upstream_unavailable THROW; a 200 non-JSON ⇒
31
+ * schema_drift. A DOWN service is NEVER a returned:0.
32
+ * [P3] `value` (monthly max lodging $) / `meals` (M&IE cap $) via `num` (null-
33
+ * never-0 — a genuine 0 stays 0). `standardRate` / `isOconus` are STRING
34
+ * booleans "true"/"false" ⇒ coerced to a real boolean (an unrecognized value
35
+ * ⇒ null, never a fabricated false). The months array is preserved AS-IS
36
+ * (never padded/fabricated to 12).
37
+ * [P4] `rates` / a group's `rate` / `months.month` absent or non-array ⇒
38
+ * driftError (never a fabricated empty).
39
+ */
40
+ import { type MetaBundle } from "./meta.js";
41
+ export declare const GSA_PERDIEM_HOST = "api.gsa.gov";
42
+ export declare const DEFAULT_PERDIEM_YEAR = "2025";
43
+ export type PerdiemMonth = {
44
+ month: number | null;
45
+ monthName: string | null;
46
+ lodgingUsd: number | null;
47
+ };
48
+ export type PerdiemRate = {
49
+ city: string | null;
50
+ county: string | null;
51
+ state: string | null;
52
+ zip: string | null;
53
+ year: number | null;
54
+ isOconus: boolean | null;
55
+ standardRate: boolean | null;
56
+ mealsUsd: number | null;
57
+ monthlyLodgingUsd: PerdiemMonth[];
58
+ };
59
+ export type GsaPerdiemRatesArgs = {
60
+ city?: string;
61
+ state?: string;
62
+ zip?: string;
63
+ year?: string;
64
+ };
65
+ /**
66
+ * Look up GSA Federal Travel per-diem rates by EITHER (city + state) OR zip, for a
67
+ * given `year` (default 2025). Returns flattened rate rows (each outer state/year
68
+ * group × inner city/rate) + honest `_meta`: totalAvailable = the row count (no
69
+ * pagination — P1), lodging/meals as null-never-0 dollars (P3), standardRate/isOconus
70
+ * as real booleans, the months array preserved as-is. The DEMO_KEY rate disclosure
71
+ * rides in the notes.
72
+ */
73
+ export declare function perdiemRates(args: GsaPerdiemRatesArgs): Promise<MetaBundle>;
74
+ //# sourceMappingURL=gsa-perdiem.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gsa-perdiem.d.ts","sourceRoot":"","sources":["../src/gsa-perdiem.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAKH,OAAO,EAAY,KAAK,UAAU,EAAqB,MAAM,WAAW,CAAC;AAOzE,eAAO,MAAM,gBAAgB,gBAAgB,CAAC;AAU9C,eAAO,MAAM,oBAAoB,SAAS,CAAC;AA+B3C,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,iBAAiB,EAAE,YAAY,EAAE,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AA4CF;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,UAAU,CAAC,CAgMrB"}
@@ -0,0 +1,296 @@
1
+ /**
2
+ * gsa-perdiem.ts — GSA Federal Travel Per-Diem lookup (`api.gsa.gov`, base
3
+ * `/travel/perdiem/v2`) — ADR-0050. The lodging + M&IE rate ceilings the federal
4
+ * government reimburses for official travel, by city/state or ZIP for a given year.
5
+ *
6
+ * WHAT IT ADDS: a NEW travel-cost lane (the per-diem authority) on the SAME
7
+ * `api.gsa.gov` host as datagov-catalog.ts, so it REUSES the audited `datagovKey.ts`
8
+ * key seam VERBATIM (keyHeader / keyModeLabel / pushKeyNote) — keyless by default via
9
+ * the shared DEMO_KEY, upgraded by DATA_GOV_API_KEY. The key rides ONLY in the
10
+ * X-Api-Key header — NEVER the URL / label / _meta / a log (the K-test). This module
11
+ * writes ZERO fetch/coercion/error/meta code: it REUSES `getJson` (redirect:"error",
12
+ * the X-Api-Key header) / `driftError` / `num`·`str` (coerce.ts) / `withMeta`·
13
+ * `buildMeta`, and MIRRORS the datagov-catalog schema_drift catch-ladder verbatim.
14
+ *
15
+ * ★ SSRF: the host is a compile-time literal (`GSA_PERDIEM_HOST`). The two lookup
16
+ * modes ride FIXED path templates; every caller value (city/state/zip/year) is
17
+ * charclass-validated THEN `encodeURIComponent`-escaped into a single path segment
18
+ * (no raw passthrough, no query steer). A post-construction hostname/protocol
19
+ * assertion + `redirect:"error"` lock it (fail closed on any off-host 3xx — a 3xx
20
+ * off api.gsa.gov could carry the X-Api-Key header away).
21
+ *
22
+ * ★ HONESTY (ADR-0050 P1–P5, live-verified 2026-07-15):
23
+ * [INPUT] EITHER (city + state) OR zip — supplying BOTH, or NEITHER, ⇒ invalid_input
24
+ * with 0 fetch (an ambiguous/empty lookup is a caller error, never a guess).
25
+ * [P1] the API returns the COMPLETE rate set for the lookup (no pagination) ⇒
26
+ * totalAvailable = the flattened row count, complete:true. NEVER fabricated.
27
+ * [P2] `errors` non-null ⇒ invalid_input surfacing the message (never a fake
28
+ * empty); a genuine no-match (rates:[] / rate:[]) ⇒ honest empty (returned:0,
29
+ * complete:true); a 429 (DEMO_KEY ~10/hr) ⇒ rate_limited THROW honoring
30
+ * Retry-After; a 5xx ⇒ upstream_unavailable THROW; a 200 non-JSON ⇒
31
+ * schema_drift. A DOWN service is NEVER a returned:0.
32
+ * [P3] `value` (monthly max lodging $) / `meals` (M&IE cap $) via `num` (null-
33
+ * never-0 — a genuine 0 stays 0). `standardRate` / `isOconus` are STRING
34
+ * booleans "true"/"false" ⇒ coerced to a real boolean (an unrecognized value
35
+ * ⇒ null, never a fabricated false). The months array is preserved AS-IS
36
+ * (never padded/fabricated to 12).
37
+ * [P4] `rates` / a group's `rate` / `months.month` absent or non-array ⇒
38
+ * driftError (never a fabricated empty).
39
+ */
40
+ import { ToolErrorCarrier } from "./errors.js";
41
+ import { getJson, driftError } from "./datasource.js";
42
+ import { num, str } from "./coerce.js";
43
+ import { withMeta } from "./meta.js";
44
+ // The SHARED api.data.gov key seam (ADR-0010 §2). api.gsa.gov accepts the SAME
45
+ // DATA_GOV_API_KEY / DEMO_KEY via the X-Api-Key header — this is another consumer
46
+ // of the audited key discipline (a key-leak regression now fails this suite too).
47
+ import { keyHeader, keyModeLabel, pushKeyNote } from "./datagovKey.js";
48
+ // ─── Fixed endpoint (SSRF core — compile-time CONSTANTS) ──────────
49
+ export const GSA_PERDIEM_HOST = "api.gsa.gov";
50
+ const GSA_PERDIEM_BASE = "/travel/perdiem/v2";
51
+ // HOST+path label — surfaces in ToolError.upstreamEndpoint; the key rides ONLY in
52
+ // the X-Api-Key header, so no token can ever appear here.
53
+ const GSA_PERDIEM_LABEL = "gsa-perdiem:/travel/perdiem/v2/rates";
54
+ const GSA_PERDIEM_SOURCE = (mode) => `${GSA_PERDIEM_HOST} via GSA Federal Travel Per-Diem API (${mode})`;
55
+ // The default per-diem fiscal year (ADR-0050 — the current confirmed vintage).
56
+ export const DEFAULT_PERDIEM_YEAR = "2025";
57
+ // ─── Validation charclasses (SSRF + "verify the input" honesty) ───
58
+ // Each rides in a single PATH segment (encodeURIComponent-escaped), so these are
59
+ // belt-and-suspenders against a Zod-bypassing direct handler call.
60
+ const CITY_RE = /^[A-Za-z .'\-]{1,60}$/;
61
+ const STATE_RE = /^[A-Za-z]{2}$/;
62
+ const ZIP_RE = /^\d{5}$/;
63
+ const YEAR_RE = /^\d{4}$/;
64
+ // ─── Honesty notes (ADR-0050 required set) ────────────────────────
65
+ const RATE_MEANING_NOTE = "lodgingUsd (from the API's monthly `value`) is the MAX nightly lodging reimbursement ceiling for that month — it VARIES SEASONALLY, hence a per-month array; mealsUsd (from `meals`) is the daily Meals & Incidental Expenses (M&IE) ceiling. Both are integer US dollars. A withheld/absent figure is null, NEVER 0 (a genuine 0 is preserved).";
66
+ const STANDARD_RATE_NOTE = "standardRate:true means this location falls under the CONUS STANDARD rate (not an individually-set non-standard rate). standardRate/isOconus are booleans coerced from the API's string 'true'/'false'.";
67
+ const NO_PAGINATION_NOTE = "The per-diem API returns the COMPLETE rate set for the lookup (no pagination); totalAvailable equals the number of rows returned.";
68
+ // ─── STRING-boolean coercion (null-never-fabricate) ───────────────
69
+ /** Coerce the API's string 'true'/'false' → a real boolean; anything else ⇒ null. */
70
+ function strBool(v) {
71
+ if (typeof v === "boolean")
72
+ return v;
73
+ if (typeof v === "string") {
74
+ const s = v.trim().toLowerCase();
75
+ if (s === "true")
76
+ return true;
77
+ if (s === "false")
78
+ return false;
79
+ }
80
+ return null;
81
+ }
82
+ /**
83
+ * Map one `months.month[]` entry → the curated per-month shape. `value` and the
84
+ * month `number` via `num` (null-never-0); `long` (month name) via `str`.
85
+ */
86
+ function mapMonth(m) {
87
+ const it = (m ?? {});
88
+ return {
89
+ month: num(it.number),
90
+ monthName: str(it.long),
91
+ lodgingUsd: num(it.value),
92
+ };
93
+ }
94
+ // ─── SSRF-guarded fetch (fixed host + hostname assertion + redirect) ──
95
+ /**
96
+ * GET one GSA per-diem JSON resource. `path` is a fully-assembled, pre-escaped
97
+ * path (NO query params — the key rides in the X-Api-Key header only). Builds
98
+ * `https://${GSA_PERDIEM_HOST}${path}` on the FIXED host, asserts the CONSTRUCTED
99
+ * URL's hostname === the host over https (belt-and-suspenders), sets
100
+ * `redirect:"error"` (an off-host 3xx must NOT be followed — it could carry the
101
+ * X-Api-Key header to a foreign host), and attaches the key ONLY in the header.
102
+ */
103
+ async function getGsaPerdiem(path) {
104
+ const url = `https://${GSA_PERDIEM_HOST}${path}`;
105
+ const built = new URL(url);
106
+ if (built.hostname !== GSA_PERDIEM_HOST || built.protocol !== "https:") {
107
+ throw new ToolErrorCarrier({
108
+ kind: "invalid_input",
109
+ message: `Constructed GSA per-diem URL host ${JSON.stringify(built.hostname)} (${built.protocol}) does not match the fixed host ${JSON.stringify(GSA_PERDIEM_HOST)} over https — refusing to fetch (SSRF safety).`,
110
+ retryable: false,
111
+ upstreamEndpoint: GSA_PERDIEM_LABEL,
112
+ });
113
+ }
114
+ // The key rides in the X-Api-Key header ONLY (never the URL/label/_meta);
115
+ // redirect:"error" (fail closed on any off-host 3xx).
116
+ return getJson(url, {
117
+ label: GSA_PERDIEM_LABEL,
118
+ headers: keyHeader(),
119
+ redirect: "error",
120
+ });
121
+ }
122
+ /**
123
+ * Look up GSA Federal Travel per-diem rates by EITHER (city + state) OR zip, for a
124
+ * given `year` (default 2025). Returns flattened rate rows (each outer state/year
125
+ * group × inner city/rate) + honest `_meta`: totalAvailable = the row count (no
126
+ * pagination — P1), lodging/meals as null-never-0 dollars (P3), standardRate/isOconus
127
+ * as real booleans, the months array preserved as-is. The DEMO_KEY rate disclosure
128
+ * rides in the notes.
129
+ */
130
+ export async function perdiemRates(args) {
131
+ const label = GSA_PERDIEM_LABEL;
132
+ const year = args.year ?? DEFAULT_PERDIEM_YEAR;
133
+ // ── [INPUT] EITHER (city + state) OR zip — never both, never neither. This is a
134
+ // caller-shape check (0 fetch): an ambiguous or empty lookup is invalid_input,
135
+ // never a silent guess. ──
136
+ const hasCityState = args.city !== undefined || args.state !== undefined;
137
+ const hasZip = args.zip !== undefined;
138
+ if (hasCityState && hasZip) {
139
+ throw new ToolErrorCarrier({
140
+ kind: "invalid_input",
141
+ retryable: false,
142
+ message: "Provide EITHER (city + state) OR zip — not both. City/state and ZIP are two distinct lookup modes; supplying both is ambiguous.",
143
+ upstreamEndpoint: label,
144
+ });
145
+ }
146
+ if (!hasCityState && !hasZip) {
147
+ throw new ToolErrorCarrier({
148
+ kind: "invalid_input",
149
+ retryable: false,
150
+ message: "Provide a lookup key: EITHER (city + state, e.g. city:'Washington', state:'DC') OR zip (e.g. zip:'20001').",
151
+ upstreamEndpoint: label,
152
+ });
153
+ }
154
+ // ── Validate + default the inputs (belt-and-suspenders behind the server Zod;
155
+ // a DIRECT handler call bypasses Zod). year rides in the PATH regardless. ──
156
+ if (!YEAR_RE.test(year)) {
157
+ throw new ToolErrorCarrier({
158
+ kind: "invalid_input",
159
+ retryable: false,
160
+ message: `Invalid year ${JSON.stringify(year)} — expected a 4-digit year (^\\d{4}$), e.g. "2025". (year rides in the request PATH; it is strictly validated.)`,
161
+ upstreamEndpoint: label,
162
+ });
163
+ }
164
+ let path;
165
+ const filtersApplied = [`year:${year}`];
166
+ let lookupMode;
167
+ if (hasZip) {
168
+ const zip = args.zip;
169
+ if (!ZIP_RE.test(zip)) {
170
+ throw new ToolErrorCarrier({
171
+ kind: "invalid_input",
172
+ retryable: false,
173
+ message: `Invalid zip ${JSON.stringify(zip)} — expected a 5-digit ZIP code (^\\d{5}$), e.g. "20001".`,
174
+ upstreamEndpoint: label,
175
+ });
176
+ }
177
+ lookupMode = `zip:${zip}`;
178
+ filtersApplied.push(lookupMode);
179
+ // Fixed template; each segment encodeURIComponent-escaped (belt-and-suspenders
180
+ // behind the charclass — no path injection, no query steer).
181
+ path = `${GSA_PERDIEM_BASE}/rates/zip/${encodeURIComponent(zip)}/year/${encodeURIComponent(year)}`;
182
+ }
183
+ else {
184
+ // city + state — BOTH are required together for this mode.
185
+ if (args.city === undefined || args.state === undefined) {
186
+ throw new ToolErrorCarrier({
187
+ kind: "invalid_input",
188
+ retryable: false,
189
+ message: "The city lookup mode requires BOTH city AND state (e.g. city:'Washington', state:'DC'). Provide both, or use zip instead.",
190
+ upstreamEndpoint: label,
191
+ });
192
+ }
193
+ const city = args.city;
194
+ const state = args.state;
195
+ if (!CITY_RE.test(city)) {
196
+ throw new ToolErrorCarrier({
197
+ kind: "invalid_input",
198
+ retryable: false,
199
+ message: `Invalid city ${JSON.stringify(city)} — expected 1–60 letters/spaces/.'- (^[A-Za-z .'\\-]{1,60}$), e.g. "Washington".`,
200
+ upstreamEndpoint: label,
201
+ });
202
+ }
203
+ if (!STATE_RE.test(state)) {
204
+ throw new ToolErrorCarrier({
205
+ kind: "invalid_input",
206
+ retryable: false,
207
+ message: `Invalid state ${JSON.stringify(state)} — expected a 2-letter state code (^[A-Za-z]{2}$), e.g. "DC", "CA".`,
208
+ upstreamEndpoint: label,
209
+ });
210
+ }
211
+ lookupMode = `city:${city}, state:${state}`;
212
+ filtersApplied.push(`city:${city}`, `state:${state}`);
213
+ path = `${GSA_PERDIEM_BASE}/rates/city/${encodeURIComponent(city)}/state/${encodeURIComponent(state)}/year/${encodeURIComponent(year)}`;
214
+ }
215
+ // ── The typed catch-ladder (datagov-catalog searchDatasets shape, VERBATIM).
216
+ // Preserve the 429/404/5xx/400/timeout ToolErrorCarrier taxonomy FIRST
217
+ // (LOAD-BEARING: the DEMO_KEY-~10/hr 429→rate_limited frontier would regress to
218
+ // schema_drift under a broader catch); reclassify a 200 non-JSON `.json()`
219
+ // SyntaxError to schema_drift SECOND; bare-rethrow LAST. The host-assert
220
+ // ToolErrorCarrier is also rethrown first. ──
221
+ let body;
222
+ try {
223
+ body = await getGsaPerdiem(path);
224
+ }
225
+ catch (e) {
226
+ if (e instanceof ToolErrorCarrier)
227
+ throw e;
228
+ if (e instanceof SyntaxError)
229
+ throw driftError(label, "GSA per-diem returned a non-JSON body at HTTP 200 — schema drift.");
230
+ throw e;
231
+ }
232
+ const b = (body ?? {});
233
+ // ── [P2] `errors` non-null ⇒ a lookup problem ⇒ invalid_input surfacing the
234
+ // message (NEVER a fake empty — swallowing this as empty ⇒ RED). ──
235
+ if (b.errors !== null && b.errors !== undefined) {
236
+ const msg = typeof b.errors === "string" ? b.errors : JSON.stringify(b.errors);
237
+ throw new ToolErrorCarrier({
238
+ kind: "invalid_input",
239
+ retryable: false,
240
+ message: `GSA per-diem reported a lookup error for ${lookupMode} (year ${year}): ${msg}`,
241
+ upstreamEndpoint: label,
242
+ });
243
+ }
244
+ // ── [P4] `rates` MUST be an array (a missing/object/null rates is drift, never a
245
+ // fabricated empty — a TypeError must never mask drift as upstream_unavailable). ──
246
+ if (!Array.isArray(b.rates)) {
247
+ throw driftError(label, "GSA per-diem shape drift — response.rates must be an array.");
248
+ }
249
+ // ── Flatten: each outer state/year group × its inner rate[]. ──
250
+ const rows = [];
251
+ for (const group of b.rates) {
252
+ const g = (group ?? {});
253
+ // [P4] a group's `rate` MUST be an array (never a fabricated empty).
254
+ if (!Array.isArray(g.rate)) {
255
+ throw driftError(label, "GSA per-diem shape drift — a rates[].rate must be an array.");
256
+ }
257
+ const gState = str(g.state);
258
+ const gYear = num(g.year);
259
+ const gOconus = strBool(g.isOconus);
260
+ for (const rate of g.rate) {
261
+ const r = (rate ?? {});
262
+ const monthsObj = (r.months ?? {});
263
+ // [P4] months.month MUST be an array (never padded/fabricated to 12).
264
+ if (!Array.isArray(monthsObj.month)) {
265
+ throw driftError(label, "GSA per-diem shape drift — a rate's months.month must be an array.");
266
+ }
267
+ rows.push({
268
+ city: str(r.city),
269
+ county: str(r.county),
270
+ state: gState,
271
+ zip: str(r.zip),
272
+ year: gYear,
273
+ isOconus: gOconus,
274
+ standardRate: strBool(r.standardRate),
275
+ mealsUsd: num(r.meals),
276
+ monthlyLodgingUsd: monthsObj.month.map(mapMonth),
277
+ });
278
+ }
279
+ }
280
+ const returned = rows.length;
281
+ const notes = [RATE_MEANING_NOTE, STANDARD_RATE_NOTE, NO_PAGINATION_NOTE];
282
+ pushKeyNote(notes);
283
+ return withMeta({ rates: rows }, {
284
+ source: GSA_PERDIEM_SOURCE(keyModeLabel()),
285
+ keylessMode: false, // keyed via the api.data.gov X-Api-Key (DEMO_KEY default)
286
+ returned,
287
+ // [P1] the COMPLETE set for the lookup (no pagination) ⇒ totalAvailable = the
288
+ // row count; complete is DERIVED true by buildMeta (returned === total).
289
+ totalAvailable: returned,
290
+ filtersApplied,
291
+ filtersDropped: [],
292
+ fieldsUnavailable: [],
293
+ notes,
294
+ });
295
+ }
296
+ //# sourceMappingURL=gsa-perdiem.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gsa-perdiem.js","sourceRoot":"","sources":["../src/gsa-perdiem.ts"],"names":[],"mappings":"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"}
package/dist/keys.js CHANGED
@@ -35,7 +35,7 @@ export const KEY_REGISTRY = [
35
35
  {
36
36
  envVar: "DATA_GOV_API_KEY",
37
37
  sources: [
38
- "api.data.gov keyed sources: Regulations.gov, Congress.gov, GovInfo, Federal Audit Clearinghouse (FAC), data.gov catalog",
38
+ "api.data.gov keyed sources: Regulations.gov, Congress.gov, GovInfo, Federal Audit Clearinghouse (FAC), data.gov catalog, GSA per-diem",
39
39
  ],
40
40
  required: false,
41
41
  signupUrl: "https://api.data.gov/signup/",