@mailwoman/mcp 8.3.0 → 8.5.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/cli.ts +120 -5
- package/layer-guards.ts +101 -0
- package/out/cli.d.ts +24 -2
- package/out/cli.d.ts.map +1 -1
- package/out/cli.js +95 -4
- package/out/cli.js.map +1 -1
- package/out/layer-guards.d.ts +64 -0
- package/out/layer-guards.d.ts.map +1 -0
- package/out/layer-guards.js +89 -0
- package/out/layer-guards.js.map +1 -0
- package/out/server.d.ts +3 -3
- package/out/server.js +3 -3
- package/out/tools.d.ts +48 -1
- package/out/tools.d.ts.map +1 -1
- package/out/tools.js +189 -1
- package/out/tools.js.map +1 -1
- package/package.json +10 -8
- package/server.ts +3 -3
- package/tools.ts +251 -1
package/tools.ts
CHANGED
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
* file (the actual product surface) is testable without any MCP plumbing — `tools.test.ts` calls
|
|
9
9
|
* `buildToolTable` directly with stub deps.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
11
|
+
* One tool per capability the exotic-POI/BDC arcs' other packages expose to a human/CLI caller — see
|
|
12
|
+
* `buildToolTable`'s return value for the authoritative, current list (this comment intentionally states no count,
|
|
13
|
+
* so it can't go stale as tools are added):
|
|
12
14
|
*
|
|
13
15
|
* - `mailwoman_parse` — the runtime pipeline's parse (optionally POI-aware).
|
|
14
16
|
* - `mailwoman_geocode` — the street-level geocode cascade (`mailwoman/geocode-core`).
|
|
@@ -17,6 +19,21 @@
|
|
|
17
19
|
* we never run it".
|
|
18
20
|
* - `mailwoman_layer_manifest` — read a spatial-layer database's provenance manifest + coverage summary
|
|
19
21
|
* (`@mailwoman/core/layers`).
|
|
22
|
+
* - `mailwoman_bdc_filing_landscape` — read a bdc.db layer's provider/technology/speed-bucket filing census over a
|
|
23
|
+
* set of census blocks or H3 cells (`@mailwoman/bdc`'s `filingLandscape`).
|
|
24
|
+
* - `mailwoman_plausibility_check` — score one claimed broadband-service assertion against BDC filing evidence and
|
|
25
|
+
* nearby telecom infrastructure (`@mailwoman/bdc`'s `plausibilityCheck`), returning a positive-evidence-only bundle
|
|
26
|
+
* with an always-present `coverage_confidence`. A missing/absent `bdc_database_path`/`poi_database_path` degrades
|
|
27
|
+
* to a typed abstain entry in the bundle, never a throw (2b task 7, decision 6).
|
|
28
|
+
* - `mailwoman_filer_lookup` (3a task 7) — read the FCC filer identity crosswalk (`@mailwoman/filer`'s
|
|
29
|
+
* `filerLookup`) for one identifier (FRN, Form 499 ID, or BDC provider ID): every OTHER identifier it shares an
|
|
30
|
+
* authoritative edge with, its current attributes, its authoritative entity cluster, and any inferred links —
|
|
31
|
+
* reported separately, never merged into the cluster. `as_of` is always present (defaults to today).
|
|
32
|
+
* - `mailwoman_filer_family` (3b task 9) — read a corporate family's membership (`@mailwoman/filer/sdk`'s
|
|
33
|
+
* `familyRollup`) from a filer.db layer database, given a `family_id` or a `node_id`. Distinct from an entity
|
|
34
|
+
* cluster (same filer, different identifiers) — a corporate family spans several DIFFERENT filers under a
|
|
35
|
+
* holding/parent/subsidiary/management relationship. The handler passes `familyRollup`'s result through
|
|
36
|
+
* unchanged: no reshaping, filtering, or summarizing of who-owns-whom data.
|
|
20
37
|
*/
|
|
21
38
|
|
|
22
39
|
import { z } from "zod"
|
|
@@ -30,6 +47,24 @@ export interface MCPToolDeps {
|
|
|
30
47
|
poiSearch: (q: { query: string; poiDatabasePath?: string }) => Promise<unknown>
|
|
31
48
|
overpassExport: (query: string) => Promise<string>
|
|
32
49
|
layerManifest: (databasePath: string) => Promise<unknown>
|
|
50
|
+
bdcFilingLandscape: (q: { databasePath: string; geoids?: string[]; h3Cells?: number[] }) => Promise<unknown>
|
|
51
|
+
plausibilityCheck: (q: {
|
|
52
|
+
bdcDatabasePath?: string
|
|
53
|
+
poiDatabasePath?: string
|
|
54
|
+
address?: string
|
|
55
|
+
point?: { type: "Point"; coordinates: [number, number] }
|
|
56
|
+
geoid?: string
|
|
57
|
+
technologyCode: number
|
|
58
|
+
claimedDownloadMbps: number
|
|
59
|
+
}) => Promise<unknown>
|
|
60
|
+
filerLookup: (q: {
|
|
61
|
+
databasePath: string
|
|
62
|
+
frn?: string
|
|
63
|
+
form499ID?: string
|
|
64
|
+
bdcProviderID?: number
|
|
65
|
+
asOf?: string
|
|
66
|
+
}) => Promise<unknown>
|
|
67
|
+
filerFamily: (q: { databasePath: string; familyID?: string; nodeID?: string; asOf?: string }) => Promise<unknown>
|
|
33
68
|
}
|
|
34
69
|
|
|
35
70
|
/**
|
|
@@ -87,6 +122,138 @@ const LayerManifestInputSchema = z.object({
|
|
|
87
122
|
.describe("Path to a mailwoman spatial-layer database (poi.db, an address-points shard, etc.)."),
|
|
88
123
|
})
|
|
89
124
|
|
|
125
|
+
const BDCFilingLandscapeInputSchema = z.object({
|
|
126
|
+
database_path: z
|
|
127
|
+
.string()
|
|
128
|
+
.min(1)
|
|
129
|
+
.describe("Path to a bdc.db layer database (FCC Broadband Data Collection availability)."),
|
|
130
|
+
geoids: z
|
|
131
|
+
.array(z.string())
|
|
132
|
+
.min(1)
|
|
133
|
+
.optional()
|
|
134
|
+
.describe(
|
|
135
|
+
"15-character census block GEOIDs to query. Provide exactly one of `geoids` or `h3_cells` — never both, never neither, never empty."
|
|
136
|
+
),
|
|
137
|
+
h3_cells: z
|
|
138
|
+
.array(z.number())
|
|
139
|
+
.min(1)
|
|
140
|
+
.optional()
|
|
141
|
+
.describe(
|
|
142
|
+
"Resolution-9 short H3 cell integers (the bdc.db availability spine) to query directly. Provide exactly " +
|
|
143
|
+
"one of `geoids` or `h3_cells` — never both, never neither, never empty."
|
|
144
|
+
),
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
const PlausibilityCheckPointInputSchema = z.object({
|
|
148
|
+
type: z.literal("Point").describe('GeoJSON geometry type — always "Point".'),
|
|
149
|
+
coordinates: z
|
|
150
|
+
.tuple([z.number(), z.number()])
|
|
151
|
+
.describe("[longitude, latitude] pair, GeoJSON coordinate order (longitude first)."),
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
const PlausibilityCheckInputSchema = z.object({
|
|
155
|
+
bdc_database_path: z
|
|
156
|
+
.string()
|
|
157
|
+
.optional()
|
|
158
|
+
.describe(
|
|
159
|
+
"Path to a bdc.db layer database (FCC Broadband Data Collection availability). Omit — or point at a file " +
|
|
160
|
+
"that doesn't exist — to abstain on filing evidence rather than error."
|
|
161
|
+
),
|
|
162
|
+
poi_database_path: z
|
|
163
|
+
.string()
|
|
164
|
+
.optional()
|
|
165
|
+
.describe(
|
|
166
|
+
"Path to a poi.db layer database carrying the telecom-infrastructure categories (`telecom_exchange`, " +
|
|
167
|
+
"`tower_comms`, etc.). Omit — or point at a file that doesn't exist — to abstain on physical-plant " +
|
|
168
|
+
"evidence rather than error."
|
|
169
|
+
),
|
|
170
|
+
address: z
|
|
171
|
+
.string()
|
|
172
|
+
.optional()
|
|
173
|
+
.describe(
|
|
174
|
+
"The claimed service location as a free-text postal address, geocoded via the server's runtime pipeline " +
|
|
175
|
+
"when `point` isn't also given. At least one of `address`, `point`, or `geoid` is required."
|
|
176
|
+
),
|
|
177
|
+
point: PlausibilityCheckPointInputSchema.optional().describe(
|
|
178
|
+
"The claimed service location as a GeoJSON Point, bypassing geocoding. At least one of `address`, `point`, " +
|
|
179
|
+
"or `geoid` is required."
|
|
180
|
+
),
|
|
181
|
+
geoid: z
|
|
182
|
+
.string()
|
|
183
|
+
.optional()
|
|
184
|
+
.describe(
|
|
185
|
+
"15-character census block GEOID for the claimed service location — the exact, native filing-evidence " +
|
|
186
|
+
"path (no h3-cell approximation). May be supplied alongside `point`/`address` (geoid drives filing " +
|
|
187
|
+
"evidence; the point drives physical-plant evidence independently). At least one of `address`, " +
|
|
188
|
+
"`point`, or `geoid` is required."
|
|
189
|
+
),
|
|
190
|
+
technology_code: z
|
|
191
|
+
.number()
|
|
192
|
+
.describe(
|
|
193
|
+
"FCC BDC technology code for the claimed service, e.g. 50 = optical carrier fiber (BroadbandTechnologyCode)."
|
|
194
|
+
),
|
|
195
|
+
claimed_download_mbps: z.number().describe("The claimed downstream speed in Mbps."),
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
const FilerLookupInputSchema = z.object({
|
|
199
|
+
database_path: z.string().min(1).describe("Path to a filer.db layer database (FCC filer identity crosswalk)."),
|
|
200
|
+
frn: z
|
|
201
|
+
.string()
|
|
202
|
+
.optional()
|
|
203
|
+
.describe(
|
|
204
|
+
"The zero-padded 10-digit FCC Registration Number to look up, e.g. '0001753557'. Provide exactly one of " +
|
|
205
|
+
"`frn`, `form499_id`, or `bdc_provider_id` — never more than one, never none."
|
|
206
|
+
),
|
|
207
|
+
form499_id: z
|
|
208
|
+
.string()
|
|
209
|
+
.optional()
|
|
210
|
+
.describe(
|
|
211
|
+
"The FCC Form 499 filer ID to look up. Provide exactly one of `frn`, `form499_id`, or `bdc_provider_id` — " +
|
|
212
|
+
"never more than one, never none."
|
|
213
|
+
),
|
|
214
|
+
bdc_provider_id: z
|
|
215
|
+
.number()
|
|
216
|
+
.optional()
|
|
217
|
+
.describe(
|
|
218
|
+
"The FCC BDC provider_id to look up. Provide exactly one of `frn`, `form499_id`, or `bdc_provider_id` — " +
|
|
219
|
+
"never more than one, never none."
|
|
220
|
+
),
|
|
221
|
+
as_of: z
|
|
222
|
+
.string()
|
|
223
|
+
.optional()
|
|
224
|
+
.describe(
|
|
225
|
+
"ISO date (YYYY-MM-DD) to scope the lookup as-of — only relationships valid on or before this date, and not " +
|
|
226
|
+
"yet closed by it, are included. Defaults to today; the result always states the date actually used."
|
|
227
|
+
),
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
const FilerFamilyInputSchema = z.object({
|
|
231
|
+
database_path: z.string().min(1).describe("Path to a filer.db layer database (FCC filer identity crosswalk)."),
|
|
232
|
+
family_id: z
|
|
233
|
+
.string()
|
|
234
|
+
.optional()
|
|
235
|
+
.describe(
|
|
236
|
+
"The canonicalized family_id to roll up, e.g. as returned by mailwoman_filer_lookup's `families` field. " +
|
|
237
|
+
"Provide exactly one of `family_id` or `node_id` — never both, never neither."
|
|
238
|
+
),
|
|
239
|
+
node_id: z
|
|
240
|
+
.string()
|
|
241
|
+
.optional()
|
|
242
|
+
.describe(
|
|
243
|
+
"A filer-graph node_id (e.g. 'frn:0001753557') to resolve EVERY corporate family it currently belongs to " +
|
|
244
|
+
"— a node may legitimately belong to more than one (e.g. a different holding company vs. management " +
|
|
245
|
+
"company). Provide exactly one of `family_id` or `node_id` — never both, never neither."
|
|
246
|
+
),
|
|
247
|
+
as_of: z
|
|
248
|
+
.string()
|
|
249
|
+
.optional()
|
|
250
|
+
.describe(
|
|
251
|
+
"ISO date (YYYY-MM-DD) to scope the rollup as-of — only family memberships valid on or before this date, " +
|
|
252
|
+
"and not yet closed by it, are included. Defaults to today; each returned family states the date " +
|
|
253
|
+
"actually used."
|
|
254
|
+
),
|
|
255
|
+
})
|
|
256
|
+
|
|
90
257
|
/**
|
|
91
258
|
* Build the tool table for a concrete `MCPToolDeps` implementation. Pure — no transport, no I/O of its own.
|
|
92
259
|
*/
|
|
@@ -158,5 +325,88 @@ export function buildToolTable(deps: MCPToolDeps): MCPToolDef[] {
|
|
|
158
325
|
return deps.layerManifest(databasePath)
|
|
159
326
|
},
|
|
160
327
|
},
|
|
328
|
+
{
|
|
329
|
+
name: "mailwoman_bdc_filing_landscape",
|
|
330
|
+
description:
|
|
331
|
+
"Read the FCC Broadband Data Collection (BDC) provider/technology/speed-bucket filing census over a set " +
|
|
332
|
+
"of census blocks (`geoids`) or H3 cells (`h3_cells`) from a bdc.db layer database. Returns the source " +
|
|
333
|
+
"vintage, how many queried blocks were surveyed vs. unknown (never surveyed), and the filing summary. " +
|
|
334
|
+
"Provide exactly one of `geoids` or `h3_cells`.",
|
|
335
|
+
inputSchema: BDCFilingLandscapeInputSchema,
|
|
336
|
+
handler: async (args) => {
|
|
337
|
+
const { database_path, geoids, h3_cells } = BDCFilingLandscapeInputSchema.parse(args)
|
|
338
|
+
|
|
339
|
+
return deps.bdcFilingLandscape({ databasePath: database_path, geoids, h3Cells: h3_cells })
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
name: "mailwoman_plausibility_check",
|
|
344
|
+
description:
|
|
345
|
+
"Score one claimed broadband-service assertion (technology + speed at a location) against the FCC BDC " +
|
|
346
|
+
"filing census and nearby telecom infrastructure, returning an evidence bundle: filings that corroborate " +
|
|
347
|
+
"(or don't), nearby physical plant, and a coverage_confidence reflecting survey completeness. NEVER " +
|
|
348
|
+
"returns a verdict stronger than 'no supporting evidence found' — absence of evidence degrades " +
|
|
349
|
+
"confidence, it never disproves the claim. Provide at least one of `address`, `point`, or `geoid`.",
|
|
350
|
+
inputSchema: PlausibilityCheckInputSchema,
|
|
351
|
+
handler: async (args) => {
|
|
352
|
+
const { bdc_database_path, poi_database_path, address, point, geoid, technology_code, claimed_download_mbps } =
|
|
353
|
+
PlausibilityCheckInputSchema.parse(args)
|
|
354
|
+
|
|
355
|
+
return deps.plausibilityCheck({
|
|
356
|
+
bdcDatabasePath: bdc_database_path,
|
|
357
|
+
poiDatabasePath: poi_database_path,
|
|
358
|
+
address,
|
|
359
|
+
point,
|
|
360
|
+
geoid,
|
|
361
|
+
technologyCode: technology_code,
|
|
362
|
+
claimedDownloadMbps: claimed_download_mbps,
|
|
363
|
+
})
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
name: "mailwoman_filer_lookup",
|
|
368
|
+
description:
|
|
369
|
+
"Look up the FCC filer identity crosswalk for one identifier (FRN, Form 499 filer ID, or BDC provider_id) " +
|
|
370
|
+
"from a filer.db layer database: every OTHER identifier it shares an authoritative edge with (never " +
|
|
371
|
+
"collapsed — a provider_id carrying multiple FRNs reports all of them), its current attributes, its " +
|
|
372
|
+
"authoritative entity cluster, and any inferred links reported SEPARATELY with their score — never merged " +
|
|
373
|
+
"into the cluster. Scoped `as_of` a date (defaults to today, always present in the result). Provide " +
|
|
374
|
+
"exactly one of `frn`, `form499_id`, or `bdc_provider_id`.",
|
|
375
|
+
inputSchema: FilerLookupInputSchema,
|
|
376
|
+
handler: async (args) => {
|
|
377
|
+
const { database_path, frn, form499_id, bdc_provider_id, as_of } = FilerLookupInputSchema.parse(args)
|
|
378
|
+
|
|
379
|
+
return deps.filerLookup({
|
|
380
|
+
databasePath: database_path,
|
|
381
|
+
frn,
|
|
382
|
+
form499ID: form499_id,
|
|
383
|
+
bdcProviderID: bdc_provider_id,
|
|
384
|
+
asOf: as_of,
|
|
385
|
+
})
|
|
386
|
+
},
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
name: "mailwoman_filer_family",
|
|
390
|
+
description:
|
|
391
|
+
"Read a corporate family's full membership from a filer.db layer database — a holding/parent/subsidiary/" +
|
|
392
|
+
"management tree spanning several DIFFERENT filers, distinct from an entity cluster (same filer, " +
|
|
393
|
+
"different identifiers). Given `family_id`, returns that one family's members; given `node_id`, " +
|
|
394
|
+
"resolves EVERY family that node currently belongs to (a node MAY belong to more than one). Each " +
|
|
395
|
+
"returned family reports its members with relationship, source, and assertion (`authoritative` — the " +
|
|
396
|
+
"source document states the membership — vs `inferred` — a name match concluded it, with its " +
|
|
397
|
+
"match_score), a deduped distinct_member_count, and its known display names. Provide exactly one of " +
|
|
398
|
+
"`family_id` or `node_id`.",
|
|
399
|
+
inputSchema: FilerFamilyInputSchema,
|
|
400
|
+
handler: async (args) => {
|
|
401
|
+
const { database_path, family_id, node_id, as_of } = FilerFamilyInputSchema.parse(args)
|
|
402
|
+
|
|
403
|
+
return deps.filerFamily({
|
|
404
|
+
databasePath: database_path,
|
|
405
|
+
familyID: family_id,
|
|
406
|
+
nodeID: node_id,
|
|
407
|
+
asOf: as_of,
|
|
408
|
+
})
|
|
409
|
+
},
|
|
410
|
+
},
|
|
161
411
|
]
|
|
162
412
|
}
|