@forgemeshlabs/gov-transparency-mcp 0.1.5 → 0.1.7

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/index.js CHANGED
@@ -98,7 +98,7 @@ const TOOLS = [
98
98
  openWorldHint: true,
99
99
  },
100
100
  description:
101
- "PAID ($0.02) — Congressional stock trades parsed from official STOCK Act disclosures: US House member trades with ticker, asset, buy/sell, transaction date, and disclosed amount range. Filter by member name, ticker, trade type (purchase/sale/sale_partial/exchange), state, or date window. Disclosures lag trades by up to 45 days by law. Requires WALLET_PRIVATE_KEY.",
101
+ "PAID ($0.02) — Congress stock trades from official STOCK Act disclosures: US House member trades with ticker, asset, buy/sell, transaction date, and disclosed amount range. Filter by member name, ticker, trade type (purchase/sale/sale_partial/exchange), state, or date window. House only, no Senate; disclosures lag trades by up to 45 days by law. Requires WALLET_PRIVATE_KEY.",
102
102
  inputSchema: {
103
103
  type: "object",
104
104
  properties: {
@@ -142,7 +142,7 @@ const TOOLS = [
142
142
  openWorldHint: true,
143
143
  },
144
144
  description:
145
- "PAID ($0.01) — Federal contract awards won by any company: award id, dollar amount, awarding agency, dates, description, and record link, sorted largest first. Optional agency and date-window filters. Requires WALLET_PRIVATE_KEY.",
145
+ "PAID ($0.01) — Federal contracts search: US government contract awards won by a company: award id, dollar amount, awarding agency, dates, description, and record link, sorted largest first. Optional agency and date-window filters. Requires WALLET_PRIVATE_KEY.",
146
146
  inputSchema: {
147
147
  type: "object",
148
148
  properties: {
@@ -220,7 +220,7 @@ const TOOLS = [
220
220
  openWorldHint: true,
221
221
  },
222
222
  description:
223
- "PAID ($0.01) — US federal lobbying filings by client company or lobbying firm: reported income, issue areas, specific-issue text, and named lobbyists per filing. Optional year filter. Requires WALLET_PRIVATE_KEY.",
223
+ "PAID ($0.01) — Lobbying filings: US federal lobbying disclosures by client company or lobbying firm: reported income, issue areas, specific-issue text, and named lobbyists per filing. Optional year filter. Requires WALLET_PRIVATE_KEY.",
224
224
  inputSchema: {
225
225
  type: "object",
226
226
  properties: {
@@ -348,6 +348,10 @@ async function main() {
348
348
  }
349
349
  return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
350
350
  } catch (e) {
351
+ if (e && e.status !== undefined) {
352
+ // Service refusal: structured data, the upstream body is untrusted and never interpolated into prose.
353
+ return { content: [{ type: "text", text: JSON.stringify({ error: e.message, status: e.status, charged: e.charged, upstream: e.upstream, untrusted: true }, null, 2) }], isError: true };
354
+ }
351
355
  return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
352
356
  }
353
357
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgemeshlabs/gov-transparency-mcp",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Watch the watchers: congressional stock trades, federal contracts, campaign finance, lobbying, and new regulations as nine MCP tools. All official US government data, paid per call with x402 USDC on Base — no account or API key.",
5
5
  "main": "index.js",
6
6
  "bin": {
package/x402-guard.js CHANGED
@@ -99,6 +99,24 @@ function createGuard(opts) {
99
99
  }
100
100
  }
101
101
 
102
+ // Keep the service's refusal as data, never as prose: the message is fixed (status plus a short error
103
+ // code if the service sent one); the parsed body is carried in err.upstream, capped at 1500 chars.
104
+ function httpError(res, prefix, charged) {
105
+ let upstream;
106
+ try { upstream = JSON.parse(res.text); } catch { upstream = res.text.slice(0, 1500); }
107
+ if (upstream && typeof upstream === "object") {
108
+ const json = JSON.stringify(upstream);
109
+ if (json.length > 1500) upstream = json.slice(0, 1500);
110
+ }
111
+ const code = upstream && typeof upstream === "object" && typeof upstream.error === "string" && /^[A-Za-z0-9_.-]{1,64}$/.test(upstream.error) ? upstream.error : null;
112
+ const err = new Error(`${prefix}HTTP ${res.status}${code ? ` (${code})` : ""}`);
113
+ err.status = res.status;
114
+ err.upstream = upstream;
115
+ if (charged !== undefined) err.charged = charged;
116
+ else if (upstream && typeof upstream === "object" && typeof upstream.charged === "boolean") err.charged = upstream.charged;
117
+ return err;
118
+ }
119
+
102
120
  function parseJson(res) {
103
121
  try { return JSON.parse(res.text); } catch { throw new Error(`HTTP ${res.status}: non-JSON response`); }
104
122
  }
@@ -111,7 +129,7 @@ function createGuard(opts) {
111
129
  const init = { method, headers: { ...headers, ...(body ? { "content-type": "application/json" } : {}) }, ...(body ? { body: JSON.stringify(body) } : {}) };
112
130
  const res = await fetchBounded(url, init);
113
131
  if (res.status !== 402) {
114
- if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.text.slice(0, 200)}`);
132
+ if (!res.ok) throw httpError(res, "", false); // nothing was signed, so nothing was charged
115
133
  return parseJson(res);
116
134
  }
117
135
  let challenge;
@@ -119,7 +137,7 @@ function createGuard(opts) {
119
137
  const paymentRequired = httpClient.getPaymentRequiredResponse((n) => res.headers.get(n), challenge);
120
138
  const payload = await httpClient.createPaymentPayload(paymentRequired); // policy runs (and reserves budget) inside
121
139
  const paid = await fetchBounded(url, { ...init, headers: { ...init.headers, ...httpClient.encodePaymentSignatureHeader(payload) } });
122
- if (!paid.ok) throw new Error(`Payment failed — HTTP ${paid.status}: ${paid.text.slice(0, 200)}`);
140
+ if (!paid.ok) throw httpError(paid, "Payment failed — ");
123
141
  let settle = null;
124
142
  try { settle = httpClient.getPaymentSettleResponse((n) => paid.headers.get(n)) || null; } catch { settle = null; }
125
143
  const contentType = paid.headers.get("content-type") || "";