gov-data-mcp 1.0.4 → 1.1.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.md +4 -4
- package/package.json +1 -1
- package/src/catalog.json +1973 -1
- package/src/tools.js +58 -7
package/src/tools.js
CHANGED
|
@@ -29,10 +29,29 @@ export const FEATURED = [
|
|
|
29
29
|
'license-verifier',
|
|
30
30
|
];
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Meta tools are hyphen-case for one reason: so that EVERY tool this server
|
|
34
|
+
* exposes follows a single rule, and for catalog tools the rule is stronger
|
|
35
|
+
* still — the tool name IS the Apify slug, which is also the value you pass to
|
|
36
|
+
* describe/run and the tail of the Store URL. The old snake_case meta names
|
|
37
|
+
* split the surface arbitrarily in two, which an independent review scored 2/5,
|
|
38
|
+
* and the alternative (snake_case everywhere) would have made 12 tool names
|
|
39
|
+
* differ from the slug they resolve to, trading one inconsistency for another.
|
|
40
|
+
*
|
|
41
|
+
* Renamed in v1.1.0. The pre-1.1 names are recognised in resolveCall and
|
|
42
|
+
* answered with an explicit rename notice rather than silently aliased — a
|
|
43
|
+
* caller with a hardcoded name deserves to be told, not quietly patched.
|
|
44
|
+
*/
|
|
32
45
|
export const META_TOOLS = {
|
|
33
|
-
SEARCH: '
|
|
34
|
-
DESCRIBE: '
|
|
35
|
-
RUN: '
|
|
46
|
+
SEARCH: 'search-gov-data-tools',
|
|
47
|
+
DESCRIBE: 'describe-gov-data-tool',
|
|
48
|
+
RUN: 'run-gov-data-tool',
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const RENAMED_IN_1_1 = {
|
|
52
|
+
search_gov_data_tools: 'search-gov-data-tools',
|
|
53
|
+
describe_gov_data_tool: 'describe-gov-data-tool',
|
|
54
|
+
run_gov_data_tool: 'run-gov-data-tool',
|
|
36
55
|
};
|
|
37
56
|
|
|
38
57
|
/** MCP tool names must match ^[a-zA-Z0-9_-]{1,64}$. Slugs already do. */
|
|
@@ -78,9 +97,30 @@ export const ANNOTATIONS = {
|
|
|
78
97
|
BILLED_LIVE_READ: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
79
98
|
};
|
|
80
99
|
|
|
100
|
+
/** Format a price so an agent can compare tools without doing arithmetic. */
|
|
101
|
+
export function priceLine(pricing) {
|
|
102
|
+
if (!pricing) return 'Price unavailable for this tool — check its Store page before calling.';
|
|
103
|
+
const per1k = pricing.usdPer1000;
|
|
104
|
+
const cheapest = pricing.tierDiscountsUsdPerUnit
|
|
105
|
+
? Math.min(...Object.values(pricing.tierDiscountsUsdPerUnit))
|
|
106
|
+
: pricing.usdPerUnit;
|
|
107
|
+
const discount = cheapest < pricing.usdPerUnit
|
|
108
|
+
? ` Lower on paid Apify plans, down to $${(cheapest * 1000).toFixed(2)} per 1,000.`
|
|
109
|
+
: '';
|
|
110
|
+
return `$${pricing.usdPerUnit} per ${pricing.unit} ($${per1k} per 1,000).${discount}`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function costNote(pricing) {
|
|
114
|
+
return 'COST AND SIDE EFFECTS: read-only with respect to the government source — it never writes to any external system — '
|
|
115
|
+
+ `but each call starts a metered run on YOUR Apify account, billed ${priceLine(pricing)} `
|
|
116
|
+
+ 'Nothing is charged when a run fails.';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** The generic, price-free wording used by the run tool, which can run anything. */
|
|
81
120
|
export const COST_NOTE =
|
|
82
121
|
'COST AND SIDE EFFECTS: read-only with respect to the government source — it never writes to any external system — '
|
|
83
|
-
+ 'but each call starts a metered run on YOUR Apify account
|
|
122
|
+
+ 'but each call starts a metered run on YOUR Apify account, billed per result row at the rate this tool reports. '
|
|
123
|
+
+ 'Call describe-gov-data-tool first to see the exact price before running anything. '
|
|
84
124
|
+ 'Nothing is charged when a run fails.';
|
|
85
125
|
|
|
86
126
|
/**
|
|
@@ -117,7 +157,7 @@ export function featuredToolDefinitions(index) {
|
|
|
117
157
|
const routing = ROUTING[slug] ? ` ${ROUTING[slug]}` : '';
|
|
118
158
|
return {
|
|
119
159
|
name: toolNameFor(slug),
|
|
120
|
-
description: `${a.title}. ${truncate(a.description, 400)}${routing} Reads live from the official government source. ${
|
|
160
|
+
description: `${a.title}. ${truncate(a.description, 400)}${routing} Reads live from the official government source. ${costNote(a.pricing)} Store page: ${a.storeUrl}`,
|
|
121
161
|
inputSchema: a.inputSchema,
|
|
122
162
|
annotations: { title: a.title, ...ANNOTATIONS.BILLED_LIVE_READ },
|
|
123
163
|
};
|
|
@@ -161,7 +201,7 @@ export function metaToolDefinitions(index) {
|
|
|
161
201
|
type: 'object',
|
|
162
202
|
properties: {
|
|
163
203
|
tool: { type: 'string', description: 'The tool name to run, e.g. "usgs-seismic-design-screener".' },
|
|
164
|
-
input: { type: 'object', description:
|
|
204
|
+
input: { type: 'object', description: `Input object matching the schema returned by ${META_TOOLS.DESCRIBE}.` },
|
|
165
205
|
maxItems: { type: 'integer', description: 'Maximum rows to return. Default 200.', minimum: 1, maximum: 1000 },
|
|
166
206
|
},
|
|
167
207
|
required: ['tool', 'input'],
|
|
@@ -193,6 +233,9 @@ export function searchCatalog(index, query, limit = 10) {
|
|
|
193
233
|
title: x.actor.title,
|
|
194
234
|
description: truncate(x.actor.description, 300),
|
|
195
235
|
categories: x.actor.categories,
|
|
236
|
+
// Price rides along on every search hit so an agent can weigh cost while
|
|
237
|
+
// choosing, rather than discovering it only after it has already called.
|
|
238
|
+
usdPer1000Results: x.actor.pricing ? x.actor.pricing.usdPer1000 : null,
|
|
196
239
|
storeUrl: x.actor.storeUrl,
|
|
197
240
|
}));
|
|
198
241
|
}
|
|
@@ -212,6 +255,9 @@ export function describeTool(index, name) {
|
|
|
212
255
|
title: actor.title,
|
|
213
256
|
description: actor.description,
|
|
214
257
|
categories: actor.categories,
|
|
258
|
+
pricing: actor.pricing
|
|
259
|
+
? { ...actor.pricing, summary: priceLine(actor.pricing) }
|
|
260
|
+
: null,
|
|
215
261
|
storeUrl: actor.storeUrl,
|
|
216
262
|
inputSchema: actor.inputSchema,
|
|
217
263
|
};
|
|
@@ -229,7 +275,12 @@ export function resolveCall(index, toolName, args) {
|
|
|
229
275
|
}
|
|
230
276
|
return { ok: true, slug, input: (args && args.input) || {}, maxItems: args && args.maxItems };
|
|
231
277
|
}
|
|
232
|
-
|
|
278
|
+
// A caller with a hardcoded pre-1.1 name gets told what happened rather than
|
|
279
|
+
// silently aliased, so the hardcode gets fixed instead of quietly persisting.
|
|
280
|
+
if (RENAMED_IN_1_1[toolName]) {
|
|
281
|
+
return { ok: false, error: `"${toolName}" was renamed to "${RENAMED_IN_1_1[toolName]}" in gov-data-mcp v1.1.0, so that every tool on this server uses one naming convention. Call "${RENAMED_IN_1_1[toolName]}" instead — the arguments are unchanged.` };
|
|
282
|
+
}
|
|
283
|
+
return { ok: false, error: `Unknown tool "${toolName}". Call ${META_TOOLS.SEARCH} to list what this server offers.` };
|
|
233
284
|
}
|
|
234
285
|
|
|
235
286
|
/** Shape a run result into MCP text content. Never collapses a failure into an empty answer. */
|