@cliwant/mcp-sam-gov 1.13.2 → 1.14.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.
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Toolset profiles for MCP_SAM_GOV_TOOLSETS.
3
+ *
4
+ * Each of the 152 registered tools belongs to exactly one named toolset.
5
+ * A user may load a subset by setting the env var (comma- or space-separated,
6
+ * case-insensitive). Unset or "all" → all 152 tools loaded (default; byte-
7
+ * identical to today's behaviour). Unknown names → stderr warning AND a note
8
+ * in the server instructions; if no valid name remains → fall back to all,
9
+ * with a stderr warning and an instructions note.
10
+ *
11
+ * `feedback` and `api_key_status` are always loaded in every profile.
12
+ * Their toolset mapping is "core" (so a one-set-per-tool mapping is maintained),
13
+ * but `resolveToolsets` always injects them into any profile's loaded set.
14
+ * They are listed in the README table as "always loaded".
15
+ *
16
+ * Available toolsets:
17
+ * core — SAM.gov discovery/awards/wage determinations/exclusions/integrity,
18
+ * Grants.gov, all USAspending, FPDS, GAO, FAR/eCFR/Federal Register,
19
+ * SBA, OFAC, GSA labor-rate benchmarks; api_key_status + feedback
20
+ * (always loaded in every profile)
21
+ * sled — State/local (SLED): OpenGov, Bonfire, ArcGIS, Socrata,
22
+ * data.gov/CKAN, Tableau, Open Checkbook, search.gov domains
23
+ * vetting — Partner due-diligence: FAC, FDIC, EPA ECHO/TRI,
24
+ * CourtListener, nonprofit (IRS 990), Senate LDA lobbying
25
+ * disclosure — SEC EDGAR financial filings and XBRL frames
26
+ * regulatory — Regulations.gov, Congress.gov, GovInfo
27
+ * pricing — GSA per-diem, BLS, Treasury, BEA, Census business-patterns,
28
+ * FRED, DOL, USITC HTS
29
+ * health — CMS, NPPES, NIH, NSF, ClinicalTrials.gov, openFDA
30
+ * safety — NHTSA vehicle recalls, CPSC consumer-product recalls
31
+ * geo — Census geocode, FEMA disasters, NWS alerts, CBP border wait
32
+ * times, data.gov catalog
33
+ * cyber — NVD CVE, CISA KEV, NIST SP 800-53
34
+ */
35
+ /** Every valid toolset name. */
36
+ export declare const ALL_TOOLSET_NAMES: readonly ["core", "sled", "vetting", "disclosure", "regulatory", "pricing", "health", "safety", "geo", "cyber"];
37
+ export type ToolsetName = typeof ALL_TOOLSET_NAMES[number];
38
+ /**
39
+ * Short 2–4 word description for each toolset, shown in the server instructions
40
+ * when a profile is active so agents know what other sets exist.
41
+ */
42
+ export declare const TOOLSET_HINTS: Readonly<Record<ToolsetName, string>>;
43
+ /**
44
+ * Tools that are always loaded regardless of the active profile.
45
+ * Their toolset mapping is "core" (one-set-per-tool), but resolveToolsets
46
+ * injects them into every profile's loaded set.
47
+ */
48
+ export declare const ALWAYS_LOADED_TOOLS: ReadonlySet<string>;
49
+ /**
50
+ * Canonical mapping: tool name → toolset.
51
+ * Every registered tool must appear exactly once.
52
+ * Tests enforce: union === all 152 tools, no entry for an unregistered name.
53
+ *
54
+ * After moves (review 2026-09-18):
55
+ * ofac_screen_entity vetting → core (govcon users screen SAM exclusions + OFAC together)
56
+ * gsa_benchmark_labor_rates pricing → core (proposal pricing; needed in core govcon workflows)
57
+ * hts_lookup geo → pricing (tariff/duty data; groups with cost/rate tools)
58
+ *
59
+ * Resulting counts: core=60, sled=13, vetting=16, disclosure=8, regulatory=9,
60
+ * pricing=15, health=17, safety=3, geo=8, cyber=3. Total=152.
61
+ */
62
+ export declare const TOOL_TOOLSET_MAP: Readonly<Record<string, ToolsetName>>;
63
+ export type ResolveResult = {
64
+ /** Tools that are loaded (by name). */
65
+ loaded: Set<string>;
66
+ /** Valid toolset names that were requested. */
67
+ sets: string[];
68
+ /** Names that were not recognised as valid toolset names. */
69
+ unknown: string[];
70
+ /** True if we fell back to all because no valid name survived. */
71
+ fellBack: boolean;
72
+ };
73
+ /**
74
+ * Parse MCP_SAM_GOV_TOOLSETS and return the set of tool names to expose.
75
+ *
76
+ * Pure: takes the env value and the full tool-name list as inputs, reads
77
+ * nothing from `process.env`, and has no side-effects. Call sites (server.ts)
78
+ * are responsible for logging any warnings to stderr.
79
+ *
80
+ * `feedback` and `api_key_status` are always injected into `loaded` regardless
81
+ * of the requested profile (ALWAYS_LOADED_TOOLS invariant).
82
+ *
83
+ * @param envValue The raw value of MCP_SAM_GOV_TOOLSETS (undefined / empty = all).
84
+ * @param toolNames The complete list of registered tool names (from TOOLS).
85
+ */
86
+ export declare function resolveToolsets(envValue: string | undefined, toolNames: readonly string[]): ResolveResult;
87
+ /**
88
+ * Filter a tool list to only those in the loaded set.
89
+ * Exported so server.ts can call this and tests can import and verify the logic
90
+ * without mutating dist/server.js.
91
+ */
92
+ export declare function filterToolsFor<T extends {
93
+ name: string;
94
+ }>(tools: readonly T[], loaded: ReadonlySet<string>): T[];
95
+ /**
96
+ * Build the structured tool_not_loaded error envelope.
97
+ * Exported so server.ts can call this and tests can import and verify the logic
98
+ * without mutating dist/server.js.
99
+ *
100
+ * @param toolName The name of the tool that was called but not loaded.
101
+ * @param loadedSets The currently loaded toolset names (from ResolveResult.sets).
102
+ */
103
+ export declare function toolNotLoadedEnvelope(toolName: string, loadedSets: readonly string[]): {
104
+ ok: false;
105
+ error: {
106
+ kind: "tool_not_loaded";
107
+ message: string;
108
+ retryable: false;
109
+ };
110
+ };
111
+ //# sourceMappingURL=toolsets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolsets.d.ts","sourceRoot":"","sources":["../src/toolsets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,gCAAgC;AAChC,eAAO,MAAM,iBAAiB,iHAWpB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,OAAO,iBAAiB,CAAC,MAAM,CAAC,CAAC;AAE3D;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAW/D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,WAAW,CAAC,MAAM,CAGlD,CAAC;AAEH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAmMlE,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,uCAAuC;IACvC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACpB,+CAA+C;IAC/C,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,6DAA6D;IAC7D,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,kEAAkE;IAClE,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,SAAS,EAAE,SAAS,MAAM,EAAE,GAC3B,aAAa,CAkEf;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EACvD,KAAK,EAAE,SAAS,CAAC,EAAE,EACnB,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,GAC1B,CAAC,EAAE,CAEL;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,SAAS,MAAM,EAAE,GAC5B;IACD,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE;QAAE,IAAI,EAAE,iBAAiB,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,KAAK,CAAA;KAAE,CAAC;CACvE,CAyBA"}
@@ -0,0 +1,390 @@
1
+ /**
2
+ * Toolset profiles for MCP_SAM_GOV_TOOLSETS.
3
+ *
4
+ * Each of the 152 registered tools belongs to exactly one named toolset.
5
+ * A user may load a subset by setting the env var (comma- or space-separated,
6
+ * case-insensitive). Unset or "all" → all 152 tools loaded (default; byte-
7
+ * identical to today's behaviour). Unknown names → stderr warning AND a note
8
+ * in the server instructions; if no valid name remains → fall back to all,
9
+ * with a stderr warning and an instructions note.
10
+ *
11
+ * `feedback` and `api_key_status` are always loaded in every profile.
12
+ * Their toolset mapping is "core" (so a one-set-per-tool mapping is maintained),
13
+ * but `resolveToolsets` always injects them into any profile's loaded set.
14
+ * They are listed in the README table as "always loaded".
15
+ *
16
+ * Available toolsets:
17
+ * core — SAM.gov discovery/awards/wage determinations/exclusions/integrity,
18
+ * Grants.gov, all USAspending, FPDS, GAO, FAR/eCFR/Federal Register,
19
+ * SBA, OFAC, GSA labor-rate benchmarks; api_key_status + feedback
20
+ * (always loaded in every profile)
21
+ * sled — State/local (SLED): OpenGov, Bonfire, ArcGIS, Socrata,
22
+ * data.gov/CKAN, Tableau, Open Checkbook, search.gov domains
23
+ * vetting — Partner due-diligence: FAC, FDIC, EPA ECHO/TRI,
24
+ * CourtListener, nonprofit (IRS 990), Senate LDA lobbying
25
+ * disclosure — SEC EDGAR financial filings and XBRL frames
26
+ * regulatory — Regulations.gov, Congress.gov, GovInfo
27
+ * pricing — GSA per-diem, BLS, Treasury, BEA, Census business-patterns,
28
+ * FRED, DOL, USITC HTS
29
+ * health — CMS, NPPES, NIH, NSF, ClinicalTrials.gov, openFDA
30
+ * safety — NHTSA vehicle recalls, CPSC consumer-product recalls
31
+ * geo — Census geocode, FEMA disasters, NWS alerts, CBP border wait
32
+ * times, data.gov catalog
33
+ * cyber — NVD CVE, CISA KEV, NIST SP 800-53
34
+ */
35
+ /** Every valid toolset name. */
36
+ export const ALL_TOOLSET_NAMES = [
37
+ "core",
38
+ "sled",
39
+ "vetting",
40
+ "disclosure",
41
+ "regulatory",
42
+ "pricing",
43
+ "health",
44
+ "safety",
45
+ "geo",
46
+ "cyber",
47
+ ];
48
+ /**
49
+ * Short 2–4 word description for each toolset, shown in the server instructions
50
+ * when a profile is active so agents know what other sets exist.
51
+ */
52
+ export const TOOLSET_HINTS = {
53
+ core: "SAM, USAspending, FAR",
54
+ sled: "state/local procurement",
55
+ vetting: "FAC, FDIC, EPA, OFAC",
56
+ disclosure: "SEC EDGAR filings",
57
+ regulatory: "Regs.gov, Congress, GovInfo",
58
+ pricing: "GSA, BLS, Treasury, DOL",
59
+ health: "CMS, NIH, openFDA",
60
+ safety: "NHTSA, CPSC recalls",
61
+ geo: "Census geocode, FEMA, NWS",
62
+ cyber: "CVE, CISA KEV, NIST",
63
+ };
64
+ /**
65
+ * Tools that are always loaded regardless of the active profile.
66
+ * Their toolset mapping is "core" (one-set-per-tool), but resolveToolsets
67
+ * injects them into every profile's loaded set.
68
+ */
69
+ export const ALWAYS_LOADED_TOOLS = new Set([
70
+ "feedback",
71
+ "api_key_status",
72
+ ]);
73
+ /**
74
+ * Canonical mapping: tool name → toolset.
75
+ * Every registered tool must appear exactly once.
76
+ * Tests enforce: union === all 152 tools, no entry for an unregistered name.
77
+ *
78
+ * After moves (review 2026-09-18):
79
+ * ofac_screen_entity vetting → core (govcon users screen SAM exclusions + OFAC together)
80
+ * gsa_benchmark_labor_rates pricing → core (proposal pricing; needed in core govcon workflows)
81
+ * hts_lookup geo → pricing (tariff/duty data; groups with cost/rate tools)
82
+ *
83
+ * Resulting counts: core=60, sled=13, vetting=16, disclosure=8, regulatory=9,
84
+ * pricing=15, health=17, safety=3, geo=8, cyber=3. Total=152.
85
+ */
86
+ export const TOOL_TOOLSET_MAP = {
87
+ // ── core (60) ──────────────────────────────────────────────────────────────
88
+ // SAM.gov: discovery, attachments, wage-determinations, exclusions, integrity
89
+ sam_search_opportunities: "core",
90
+ sam_search_shaping: "core",
91
+ sam_get_opportunity: "core",
92
+ sam_fetch_description: "core",
93
+ sam_attachment_url: "core",
94
+ sam_fetch_attachment_text: "core",
95
+ sam_lookup_organization: "core",
96
+ sam_lookup_notice_fields: "core",
97
+ sam_check_exclusions: "core",
98
+ sam_get_wage_rates: "core",
99
+ sam_search_wage_determinations: "core",
100
+ sam_integrity_lookup: "core",
101
+ // Grants.gov
102
+ grants_search: "core",
103
+ grants_get_opportunity: "core",
104
+ // USAspending (29)
105
+ usas_search_awards: "core",
106
+ usas_search_individual_awards: "core",
107
+ usas_search_subagency_spending: "core",
108
+ usas_lookup_agency: "core",
109
+ usas_search_awards_by_recipient: "core",
110
+ usas_search_subawards: "core",
111
+ usas_search_recompetes: "core",
112
+ usas_search_expiring_contracts: "core",
113
+ usas_get_award_detail: "core",
114
+ usas_analyze_incumbent: "core",
115
+ usas_spending_over_time: "core",
116
+ usas_search_psc_spending: "core",
117
+ usas_search_state_spending: "core",
118
+ usas_search_cfda_spending: "core",
119
+ usas_search_federal_account_spending: "core",
120
+ usas_search_agency_spending: "core",
121
+ usas_get_agency_profile: "core",
122
+ usas_get_agency_awards_summary: "core",
123
+ usas_get_agency_budget_function: "core",
124
+ usas_search_recipients: "core",
125
+ usas_get_recipient_profile: "core",
126
+ usas_autocomplete_naics: "core",
127
+ usas_autocomplete_recipient: "core",
128
+ usas_disaster_spending: "core",
129
+ usas_glossary: "core",
130
+ usas_list_disaster_codes: "core",
131
+ usas_list_toptier_agencies: "core",
132
+ usas_naics_hierarchy: "core",
133
+ usas_search_teaming_partners: "core",
134
+ // FPDS
135
+ fpds_search_awards: "core",
136
+ // GAO
137
+ gao_protest_lookup: "core",
138
+ // FAR / DFARS
139
+ far_search: "core",
140
+ far_clause_lookup: "core",
141
+ far_compliance_matrix: "core",
142
+ // eCFR
143
+ ecfr_search: "core",
144
+ ecfr_list_titles: "core",
145
+ ecfr_get_section: "core",
146
+ // Federal Register
147
+ fed_register_search_documents: "core",
148
+ fed_register_get_document: "core",
149
+ fed_register_list_agencies: "core",
150
+ fed_register_public_inspection: "core",
151
+ // SBA
152
+ sba_size_standard: "core",
153
+ // OFAC (moved from vetting — govcon users screen SAM exclusions + OFAC together)
154
+ ofac_screen_entity: "core",
155
+ // GSA labor-rate benchmarks (moved from pricing — needed in core proposal-pricing)
156
+ gsa_benchmark_labor_rates: "core",
157
+ // Always-loaded utilities (every profile; mapped to core for 1-set invariant)
158
+ api_key_status: "core",
159
+ feedback: "core",
160
+ // ── sled (13) ──────────────────────────────────────────────────────────────
161
+ // State/local procurement + spend
162
+ opengov_list_governments: "sled",
163
+ opengov_search_solicitations: "sled",
164
+ bonfire_list_organizations: "sled",
165
+ bonfire_search_opportunities: "sled",
166
+ arcgis_hub_discover_datasets: "sled",
167
+ arcgis_feature_query: "sled",
168
+ socrata_discover_datasets: "sled",
169
+ socrata_query: "sled",
170
+ ckan_discover_datasets: "sled",
171
+ ckan_query: "sled",
172
+ tableau_view_csv: "sled",
173
+ open_checkbook_search: "sled",
174
+ search_gov_domains: "sled",
175
+ // ── vetting (16) ───────────────────────────────────────────────────────────
176
+ // Partner due-diligence (ofac_screen_entity moved to core)
177
+ fac_search_audits: "vetting",
178
+ fac_get_findings: "vetting",
179
+ fdic_search_institutions: "vetting",
180
+ fdic_institution_financials: "vetting",
181
+ fdic_institution_history: "vetting",
182
+ fdic_bank_failures: "vetting",
183
+ fdic_branch_deposits: "vetting",
184
+ fdic_industry_summary: "vetting",
185
+ fdic_risk_ratios: "vetting",
186
+ echo_search_facilities: "vetting",
187
+ echo_facility_report: "vetting",
188
+ epa_tri_facilities: "vetting",
189
+ courtlistener_search_opinions: "vetting",
190
+ nonprofit_search: "vetting",
191
+ nonprofit_financials: "vetting",
192
+ lda_search_filings: "vetting",
193
+ // ── disclosure (8) ─────────────────────────────────────────────────────────
194
+ // SEC EDGAR
195
+ edgar_company_concept: "disclosure",
196
+ edgar_company_facts: "disclosure",
197
+ edgar_company_filings: "disclosure",
198
+ edgar_daily_filing_index: "disclosure",
199
+ edgar_filing_index: "disclosure",
200
+ edgar_full_text_search: "disclosure",
201
+ edgar_lookup_cik: "disclosure",
202
+ edgar_xbrl_frames: "disclosure",
203
+ // ── regulatory (9) ─────────────────────────────────────────────────────────
204
+ // Regulations.gov, Congress.gov, GovInfo
205
+ regulations_search_documents: "regulatory",
206
+ regulations_search_comments: "regulatory",
207
+ regulations_search_dockets: "regulatory",
208
+ regulations_get_docket: "regulatory",
209
+ congress_search_bills: "regulatory",
210
+ congress_get_bill: "regulatory",
211
+ govinfo_list_collections: "regulatory",
212
+ govinfo_search_packages: "regulatory",
213
+ govinfo_get_package: "regulatory",
214
+ // ── pricing (15) ───────────────────────────────────────────────────────────
215
+ // GSA per-diem, BLS, Treasury, BEA, Census business-patterns, FRED, DOL, USITC HTS
216
+ // (gsa_benchmark_labor_rates moved to core; hts_lookup moved in from geo)
217
+ gsa_perdiem_rates: "pricing",
218
+ bls_oews_wages: "pricing",
219
+ bls_qcew: "pricing",
220
+ bls_timeseries: "pricing",
221
+ treasury_avg_interest_rates: "pricing",
222
+ treasury_debt_to_penny: "pricing",
223
+ treasury_monthly_statement: "pricing",
224
+ treasury_query_dataset: "pricing",
225
+ bea_regional_data: "pricing",
226
+ census_business_patterns: "pricing",
227
+ fred_search_series: "pricing",
228
+ fred_series_observations: "pricing",
229
+ dol_list_datasets: "pricing",
230
+ dol_get_dataset: "pricing",
231
+ hts_lookup: "pricing",
232
+ // ── health (17) ────────────────────────────────────────────────────────────
233
+ // CMS, NPPES, NIH, NSF, ClinicalTrials.gov, openFDA
234
+ cms_medicare_provider_services: "health",
235
+ cms_hospital_compare: "health",
236
+ cms_facility_directory: "health",
237
+ cms_dmepos_suppliers: "health",
238
+ cms_revoked_providers: "health",
239
+ cms_query_dataset: "health",
240
+ cms_search_datasets: "health",
241
+ nppes_lookup_provider: "health",
242
+ nih_reporter_search_projects: "health",
243
+ nsf_search_awards: "health",
244
+ nsf_get_award: "health",
245
+ clinicaltrials_search_studies: "health",
246
+ clinicaltrials_get_study: "health",
247
+ clinicaltrials_facet_counts: "health",
248
+ openfda_enforcement: "health",
249
+ openfda_device_clearances: "health",
250
+ openfda_drug_approvals: "health",
251
+ // ── safety (3) ─────────────────────────────────────────────────────────────
252
+ // NHTSA, CPSC
253
+ nhtsa_recalls: "safety",
254
+ nhtsa_complaints: "safety",
255
+ cpsc_recalls: "safety",
256
+ // ── geo (8) ────────────────────────────────────────────────────────────────
257
+ // Census geocode, FEMA, NWS, CBP, data.gov catalog
258
+ // (hts_lookup moved to pricing)
259
+ census_geocode_address: "geo",
260
+ census_geographies_by_coordinates: "geo",
261
+ fema_disaster_declarations: "geo",
262
+ fema_search_hazard_mitigation: "geo",
263
+ fema_search_public_assistance: "geo",
264
+ nws_active_alerts: "geo",
265
+ cbp_border_wait_times: "geo",
266
+ datagov_search_datasets: "geo",
267
+ // ── cyber (3) ──────────────────────────────────────────────────────────────
268
+ // NVD CVE, CISA KEV, NIST 800-53
269
+ cve_lookup: "cyber",
270
+ cisa_kev_lookup: "cyber",
271
+ nist_800_53_controls: "cyber",
272
+ };
273
+ /**
274
+ * Parse MCP_SAM_GOV_TOOLSETS and return the set of tool names to expose.
275
+ *
276
+ * Pure: takes the env value and the full tool-name list as inputs, reads
277
+ * nothing from `process.env`, and has no side-effects. Call sites (server.ts)
278
+ * are responsible for logging any warnings to stderr.
279
+ *
280
+ * `feedback` and `api_key_status` are always injected into `loaded` regardless
281
+ * of the requested profile (ALWAYS_LOADED_TOOLS invariant).
282
+ *
283
+ * @param envValue The raw value of MCP_SAM_GOV_TOOLSETS (undefined / empty = all).
284
+ * @param toolNames The complete list of registered tool names (from TOOLS).
285
+ */
286
+ export function resolveToolsets(envValue, toolNames) {
287
+ const allLoaded = new Set(toolNames);
288
+ // Unset or empty → all.
289
+ if (!envValue || !envValue.trim()) {
290
+ return { loaded: allLoaded, sets: ["all"], unknown: [], fellBack: false };
291
+ }
292
+ // Split on commas and/or whitespace, normalise to lower-case.
293
+ const tokens = envValue
294
+ .split(/[\s,]+/)
295
+ .map((t) => t.toLowerCase().trim())
296
+ .filter(Boolean);
297
+ // "all" anywhere → all.
298
+ if (tokens.includes("all")) {
299
+ return { loaded: allLoaded, sets: ["all"], unknown: [], fellBack: false };
300
+ }
301
+ const validSet = new Set(ALL_TOOLSET_NAMES);
302
+ const validNames = [];
303
+ const unknownNames = [];
304
+ for (const token of tokens) {
305
+ if (validSet.has(token)) {
306
+ if (!validNames.includes(token))
307
+ validNames.push(token);
308
+ }
309
+ else {
310
+ unknownNames.push(token);
311
+ }
312
+ }
313
+ // No valid names → fall back to all.
314
+ if (validNames.length === 0) {
315
+ return {
316
+ loaded: allLoaded,
317
+ sets: ["all"],
318
+ unknown: unknownNames,
319
+ fellBack: true,
320
+ };
321
+ }
322
+ // Build the loaded set: union of all tools in the requested toolsets.
323
+ const requestedSets = new Set(validNames);
324
+ const loaded = new Set();
325
+ for (const toolName of toolNames) {
326
+ const ts = TOOL_TOOLSET_MAP[toolName];
327
+ if (ts && requestedSets.has(ts)) {
328
+ loaded.add(toolName);
329
+ }
330
+ }
331
+ // Always inject always-loaded tools (feedback, api_key_status) regardless
332
+ // of the profile. They are already in core's mapping but must appear in every
333
+ // profile's loaded set.
334
+ for (const toolName of toolNames) {
335
+ if (ALWAYS_LOADED_TOOLS.has(toolName)) {
336
+ loaded.add(toolName);
337
+ }
338
+ }
339
+ return {
340
+ loaded,
341
+ sets: validNames,
342
+ unknown: unknownNames,
343
+ fellBack: false,
344
+ };
345
+ }
346
+ /**
347
+ * Filter a tool list to only those in the loaded set.
348
+ * Exported so server.ts can call this and tests can import and verify the logic
349
+ * without mutating dist/server.js.
350
+ */
351
+ export function filterToolsFor(tools, loaded) {
352
+ return tools.filter((t) => loaded.has(t.name));
353
+ }
354
+ /**
355
+ * Build the structured tool_not_loaded error envelope.
356
+ * Exported so server.ts can call this and tests can import and verify the logic
357
+ * without mutating dist/server.js.
358
+ *
359
+ * @param toolName The name of the tool that was called but not loaded.
360
+ * @param loadedSets The currently loaded toolset names (from ResolveResult.sets).
361
+ */
362
+ export function toolNotLoadedEnvelope(toolName, loadedSets) {
363
+ const toolset = TOOL_TOOLSET_MAP[toolName] ?? "unknown";
364
+ // Suggest the union of currently loaded sets + the needed set (not just the
365
+ // needed set alone, which would silently drop the user's current profile).
366
+ const isAll = loadedSets.length === 1 && loadedSets[0] === "all";
367
+ let suggestion;
368
+ if (isAll) {
369
+ // All tools are already loaded — the tool simply doesn't exist.
370
+ suggestion = `MCP_SAM_GOV_TOOLSETS=all`;
371
+ }
372
+ else if (toolset === "unknown") {
373
+ suggestion = `MCP_SAM_GOV_TOOLSETS=all`;
374
+ }
375
+ else {
376
+ // Build union: current loaded sets + the required set, deduped.
377
+ const unionSets = [...loadedSets];
378
+ if (!unionSets.includes(toolset))
379
+ unionSets.push(toolset);
380
+ suggestion = `MCP_SAM_GOV_TOOLSETS=${unionSets.join(",")}`;
381
+ }
382
+ const error = {
383
+ kind: "tool_not_loaded",
384
+ message: `Tool '${toolName}' belongs to the '${toolset}' toolset, which is not loaded. ` +
385
+ `Set ${suggestion} to enable it.`,
386
+ retryable: false,
387
+ };
388
+ return { ok: false, error };
389
+ }
390
+ //# sourceMappingURL=toolsets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolsets.js","sourceRoot":"","sources":["../src/toolsets.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cliwant/mcp-sam-gov",
3
- "version": "1.13.2",
3
+ "version": "1.14.0",
4
4
  "mcpName": "io.github.cliwant/mcp-sam-gov",
5
5
  "description": "Most comprehensive keyless-first MCP server for US federal + state/local (SLED) contracting + spending + regulation: SAM.gov, USAspending, Federal Register, eCFR, Grants.gov, plus SLED procurement bids (OpenGov, Bonfire, ArcGIS, Socrata 53 hosts). 152 tools (147 need no API key), plug into Claude Desktop / Claude Code / Codex CLI / Cursor / Continue / Gemini CLI.",
6
6
  "keywords": [
package/src/bls.ts CHANGED
@@ -149,10 +149,11 @@ const BLS_ID_TO_KEY: ReadonlyMap<string, BlsSeriesKey> = new Map(
149
149
  );
150
150
 
151
151
  // ─── Raw-seriesId charclass (SSRF + "verify the ID" honesty) ──────
152
- // `^[A-Z0-9]{1,20}$` — uppercase alnum, explicit length bound. In JS (no `m`
152
+ // `^[A-Z0-9]{1,25}$` — uppercase alnum, explicit length bound. In JS (no `m`
153
153
  // flag) `$` matches only end-of-input, and `[A-Z0-9]` cannot include `\n`, so a
154
154
  // trailing newline is rejected. Rejects `../`, encoded traversal, `@host`, `;`.
155
- const SERIES_ID_RE = /^[A-Z0-9]{1,20}$/;
155
+ // 25 chars covers OEWS series IDs (e.g. OEUN000000000000015125201, 25 chars).
156
+ const SERIES_ID_RE = /^[A-Z0-9]{1,25}$/;
156
157
 
157
158
  // ─── Year bounds ──────────────────────────────────────────────────
158
159
  const YEAR_MIN = 1900;
@@ -417,7 +418,7 @@ export async function timeseries(args: BlsTimeseriesArgs): Promise<MetaBundle> {
417
418
  if (typeof raw !== "string" || !SERIES_ID_RE.test(raw)) {
418
419
  throw new ToolErrorCarrier({
419
420
  kind: "invalid_input",
420
- message: `Invalid seriesId ${JSON.stringify(raw)} — expected 1..20 uppercase alphanumeric characters (^[A-Z0-9]{1,20}$). A BLS series ID like 'CUUR0000SA0'; punctuation/whitespace/lowercase are rejected (SSRF + "verify the ID" honesty).`,
421
+ message: `Invalid seriesId ${JSON.stringify(raw)} — expected 1..25 uppercase alphanumeric characters (^[A-Z0-9]{1,25}$). A BLS series ID like 'CUUR0000SA0' or OEWS 'OEUN000000000000015125201' (25 chars); punctuation/whitespace/lowercase are rejected (SSRF + "verify the ID" honesty).`,
421
422
  retryable: false,
422
423
  upstreamEndpoint: label,
423
424
  });
package/src/errors.ts CHANGED
@@ -32,7 +32,12 @@ export type ErrorKind =
32
32
  /** API returned 200 but we couldn't parse / shape doesn't match. */
33
33
  | "schema_drift"
34
34
  /** Anything else. Don't retry. */
35
- | "unknown";
35
+ | "unknown"
36
+ /**
37
+ * The tool exists in the registry but is not loaded in the current toolset
38
+ * profile. Set MCP_SAM_GOV_TOOLSETS to enable the required toolset.
39
+ */
40
+ | "tool_not_loaded";
36
41
 
37
42
  export type ToolError = {
38
43
  kind: ErrorKind;