@proximap/mcp 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ameya Borkar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # @proximap/mcp
2
+
3
+ A [Model Context Protocol](https://modelcontextprotocol.io) server that exposes
4
+ [proximap](https://github.com/AmeyaBorkar/proximap) as tools for AI agents
5
+ (e.g. Claude). Powered by OpenStreetMap; no API keys required.
6
+
7
+ ## Tools
8
+
9
+ - **`find_nearby_amenities`** — `{ query, radiusMeters?, categories?, filters?, accessible?,
10
+ open?, rankBy?, mode?, explain?, concise?, limit?, language? }` → amenities near a place/`lat,lng`,
11
+ ranked by distance or road-network travel time. Supports facet filters (diet/payment/wifi/wheelchair),
12
+ accessibility-first ranking, open-now/open-at, a one-line `summary` per result, and a token-economy
13
+ `concise` mode.
14
+ - **`geocode`** — `{ query, limit?, language? }` → the best guess plus ranked candidates and an
15
+ `ambiguous` flag when several distinct places share the name (the many "Springfield"s) — present the
16
+ candidates instead of guessing.
17
+ - **`list_categories`** — `{}` → the category terms proximap understands (for `find_nearby_amenities`).
18
+ - **`detect_amenity_gaps`** — `{ query, categories?, searchRadiusMeters?, thresholdMeters?, language? }`
19
+ → which everyday amenities are missing or far, framed as "not found in OSM," never asserted as truth.
20
+ - **`walkability_score`** — `{ query, idealMeters?, maxMeters?, language? }` → a 0–100 score with a
21
+ transparent per-category breakdown, the missing categories, and a data-confidence note.
22
+ - **`compare_locations`** — `{ locations[2+], weights?, idealMeters?, maxMeters?, language? }` → a ranked
23
+ scorecard of candidate locations with a per-dimension winner.
24
+ - **`reachable_amenities`** — `{ query, within, mode?, categories?, language? }` → the amenities reachable
25
+ within a time budget (real isochrone where available), plus the polygon.
26
+ - **`plan_errands`** — `{ query, categories[1+], mode?, end?, candidatesPerCategory?, language? }` → the
27
+ shortest trip that visits one of each category (Generalized TSP), with the chosen places and totals.
28
+
29
+ All tools are powered by OpenStreetMap (+ the key-free Valhalla engine for travel time/isochrones) and
30
+ return compact JSON. Travel times and routing degrade gracefully to straight-line estimates if the
31
+ routing engine is unavailable.
32
+
33
+ ## Install & run
34
+
35
+ ```bash
36
+ npm install @proximap/mcp
37
+ proximap-mcp # starts a stdio MCP server
38
+ ```
39
+
40
+ ## Register with an MCP client
41
+
42
+ ```json
43
+ {
44
+ "mcpServers": {
45
+ "proximap": {
46
+ "command": "proximap-mcp"
47
+ }
48
+ }
49
+ }
50
+ ```
51
+
52
+ For Claude Code, add the snippet above to your `.mcp.json` (or use
53
+ `claude mcp add proximap proximap-mcp`).
54
+
55
+ ## License
56
+
57
+ MIT
package/dist/index.js ADDED
@@ -0,0 +1,454 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import {
7
+ categoryVocabulary,
8
+ compareLocations,
9
+ detectGaps,
10
+ disambiguateLocation,
11
+ findNearbyAmenities,
12
+ planErrands,
13
+ reachableAmenities,
14
+ ValhallaRoutingProvider,
15
+ walkabilityScore
16
+ } from "@proximap/core";
17
+ import { z } from "zod";
18
+
19
+ // src/payload.ts
20
+ function toNearbyPayload(result, options = {}) {
21
+ const { origin, results, total, routing } = result;
22
+ const head = {
23
+ origin: {
24
+ name: origin.name,
25
+ displayName: origin.displayName,
26
+ lat: origin.location.lat,
27
+ lng: origin.location.lng
28
+ },
29
+ total,
30
+ count: results.length,
31
+ routing: routing ?? null
32
+ };
33
+ if (options.concise) {
34
+ return {
35
+ ...head,
36
+ results: results.map((poi) => ({
37
+ rank: poi.rank,
38
+ name: poi.name ?? null,
39
+ category: poi.category,
40
+ ...poi.travelSeconds !== void 0 ? { travelSeconds: poi.travelSeconds } : { distanceMeters: Math.round(poi.distanceMeters) },
41
+ lat: poi.location.lat,
42
+ lng: poi.location.lng,
43
+ ...poi.rankingReason ? { summary: poi.rankingReason } : {}
44
+ }))
45
+ };
46
+ }
47
+ return {
48
+ ...head,
49
+ results: results.map((poi) => ({
50
+ rank: poi.rank,
51
+ name: poi.name ?? null,
52
+ category: poi.category,
53
+ kind: poi.kind ?? null,
54
+ distanceMeters: Math.round(poi.distanceMeters),
55
+ travelSeconds: poi.travelSeconds ?? null,
56
+ travelMeters: poi.travelMeters ?? null,
57
+ lat: poi.location.lat,
58
+ lng: poi.location.lng,
59
+ osmId: poi.id,
60
+ completeness: poi.completeness ?? null,
61
+ lastVerified: poi.lastVerified ?? null,
62
+ wheelchair: poi.tags.wheelchair ?? null,
63
+ wheelchairDescription: poi.tags["wheelchair:description"] ?? null,
64
+ openState: poi.openState ?? null,
65
+ nextChange: poi.nextChange ?? null,
66
+ rankingReason: poi.rankingReason ?? null
67
+ }))
68
+ };
69
+ }
70
+ var compactPlace = (place) => ({
71
+ name: place.name,
72
+ displayName: place.displayName,
73
+ lat: place.location.lat,
74
+ lng: place.location.lng,
75
+ kind: place.kind ?? null
76
+ });
77
+ function toDisambiguationPayload(result) {
78
+ return {
79
+ query: result.query,
80
+ ambiguous: result.ambiguous,
81
+ best: result.best ? compactPlace(result.best) : null,
82
+ candidates: result.candidates.map(compactPlace)
83
+ };
84
+ }
85
+ function toErrandsPayload(plan) {
86
+ return {
87
+ origin: {
88
+ name: plan.origin.name,
89
+ displayName: plan.origin.displayName,
90
+ lat: plan.origin.location.lat,
91
+ lng: plan.origin.location.lng
92
+ },
93
+ end: plan.end ? {
94
+ displayName: plan.end.displayName,
95
+ lat: plan.end.location.lat,
96
+ lng: plan.end.location.lng
97
+ } : null,
98
+ mode: plan.mode,
99
+ totalSeconds: plan.totalSeconds,
100
+ totalMeters: plan.totalMeters,
101
+ missing: plan.missing,
102
+ stops: plan.stops.map((stop) => ({
103
+ category: stop.category,
104
+ name: stop.poi.name ?? null,
105
+ kind: stop.poi.kind ?? null,
106
+ osmId: stop.poi.id,
107
+ lat: stop.poi.location.lat,
108
+ lng: stop.poi.location.lng,
109
+ legSeconds: stop.legSeconds,
110
+ legMeters: stop.legMeters
111
+ }))
112
+ };
113
+ }
114
+ function toReachablePayload(result) {
115
+ return {
116
+ origin: {
117
+ name: result.origin.name,
118
+ displayName: result.origin.displayName,
119
+ lat: result.origin.location.lat,
120
+ lng: result.origin.location.lng
121
+ },
122
+ withinMinutes: result.withinMinutes,
123
+ mode: result.mode,
124
+ isochrone: result.isochrone,
125
+ // [lng, lat] ring, or null
126
+ count: result.count,
127
+ results: result.results.map((poi) => ({
128
+ rank: poi.rank,
129
+ name: poi.name ?? null,
130
+ category: poi.category,
131
+ kind: poi.kind ?? null,
132
+ travelSeconds: poi.travelSeconds ?? null,
133
+ travelMeters: poi.travelMeters ?? null,
134
+ distanceMeters: Math.round(poi.distanceMeters),
135
+ lat: poi.location.lat,
136
+ lng: poi.location.lng,
137
+ osmId: poi.id
138
+ }))
139
+ };
140
+ }
141
+ function toGapsPayload(report) {
142
+ return {
143
+ origin: {
144
+ name: report.origin.name,
145
+ displayName: report.origin.displayName,
146
+ lat: report.origin.location.lat,
147
+ lng: report.origin.location.lng
148
+ },
149
+ searchRadiusMeters: report.searchRadiusMeters,
150
+ thresholdMeters: report.thresholdMeters,
151
+ gaps: report.gaps,
152
+ missing: report.missing
153
+ };
154
+ }
155
+ function toScorePayload(report) {
156
+ return {
157
+ origin: {
158
+ name: report.origin.name,
159
+ displayName: report.origin.displayName,
160
+ lat: report.origin.location.lat,
161
+ lng: report.origin.location.lng
162
+ },
163
+ score: report.score,
164
+ confidence: report.confidence,
165
+ decay: report.decay,
166
+ breakdown: report.breakdown,
167
+ missing: report.missing
168
+ };
169
+ }
170
+ function toComparePayload(report) {
171
+ return {
172
+ ranked: report.ranked.map((entry) => ({
173
+ index: entry.index,
174
+ displayName: entry.origin.displayName,
175
+ score: entry.score
176
+ })),
177
+ best: report.best ? {
178
+ index: report.best.index,
179
+ displayName: report.best.origin.displayName,
180
+ score: report.best.score
181
+ } : null,
182
+ weights: report.weights,
183
+ dimensions: report.dimensions,
184
+ locations: report.locations.map((location) => ({
185
+ name: location.origin.name,
186
+ displayName: location.origin.displayName,
187
+ lat: location.origin.location.lat,
188
+ lng: location.origin.location.lng,
189
+ score: location.score,
190
+ confidence: location.confidence,
191
+ missing: location.missing,
192
+ breakdown: location.breakdown
193
+ }))
194
+ };
195
+ }
196
+
197
+ // src/index.ts
198
+ var VERSION = "1.0.0";
199
+ function jsonResult(data) {
200
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
201
+ }
202
+ function errorResult(error) {
203
+ const message = error instanceof Error ? error.message : String(error);
204
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
205
+ }
206
+ var server = new McpServer({ name: "proximap", version: VERSION });
207
+ server.registerTool(
208
+ "find_nearby_amenities",
209
+ {
210
+ title: "Find nearby amenities",
211
+ description: 'Find amenities, utilities, and points of interest near a place, ranked by distance. Accepts a place name, an address, or a "lat,lng" string. Powered by OpenStreetMap.',
212
+ inputSchema: {
213
+ query: z.string().describe('Place name, address, or "lat,lng" coordinates'),
214
+ radiusMeters: z.number().positive().optional().describe("Search radius in metres (default 1000)"),
215
+ categories: z.array(z.string()).optional().describe("Restrict to categories or terms, e.g. coffee, pharmacy, petrol"),
216
+ filters: z.object({
217
+ diet: z.union([z.string(), z.array(z.string())]).optional(),
218
+ cuisine: z.union([z.string(), z.array(z.string())]).optional(),
219
+ payment: z.union([z.string(), z.array(z.string())]).optional(),
220
+ internetAccess: z.boolean().optional(),
221
+ outdoorSeating: z.boolean().optional(),
222
+ takeaway: z.boolean().optional(),
223
+ delivery: z.boolean().optional(),
224
+ wheelchair: z.union([z.string(), z.array(z.string())]).optional()
225
+ }).optional().describe("Facet filters: diet/cuisine/payment, wifi, takeaway, wheelchair, etc."),
226
+ accessible: z.boolean().optional().describe("Rank step-free / wheelchair-accessible places first"),
227
+ open: z.string().optional().describe(
228
+ 'Keep only places open at this time: "now", or an ISO datetime like "2026-06-20T21:00". Unknown-hours places are kept and labelled.'
229
+ ),
230
+ rankBy: z.enum(["distance", "travelTime"]).optional().describe("Order by straight-line distance (default) or road-network travel time"),
231
+ mode: z.enum(["walk", "bike", "drive"]).optional().describe("Travel mode for rankBy=travelTime (default walk)"),
232
+ explain: z.boolean().optional().describe("Add a short ranking reason / summary to each result"),
233
+ concise: z.boolean().optional().describe("Return a slim, high-signal payload (with a one-line summary) for token economy"),
234
+ limit: z.number().int().positive().optional().describe("Maximum number of results (default 30)"),
235
+ language: z.string().optional().describe('Preferred language for names, e.g. "en"')
236
+ }
237
+ },
238
+ async ({
239
+ query,
240
+ radiusMeters,
241
+ categories,
242
+ filters,
243
+ accessible,
244
+ open,
245
+ rankBy,
246
+ mode,
247
+ explain,
248
+ concise,
249
+ limit,
250
+ language
251
+ }) => {
252
+ try {
253
+ const openOption = open === "now" ? "now" : open ? { at: open } : void 0;
254
+ const explainOn = Boolean(explain || concise);
255
+ const result = await findNearbyAmenities(query, {
256
+ ...radiusMeters ? { radiusMeters } : {},
257
+ ...categories ? { categories } : {},
258
+ ...filters ? { filters } : {},
259
+ ...accessible ? { accessible } : {},
260
+ ...openOption ? { open: openOption } : {},
261
+ // Travel-time ranking uses the key-free public Valhalla engine.
262
+ ...rankBy === "travelTime" ? {
263
+ rankBy: "travelTime",
264
+ routing: new ValhallaRoutingProvider(),
265
+ ...mode ? { mode } : {}
266
+ } : {},
267
+ ...explainOn ? { explain: true } : {},
268
+ ...limit ? { limit } : {},
269
+ ...language ? { language } : {}
270
+ });
271
+ return jsonResult(toNearbyPayload(result, { concise: Boolean(concise) }));
272
+ } catch (error) {
273
+ return errorResult(error);
274
+ }
275
+ }
276
+ );
277
+ server.registerTool(
278
+ "geocode",
279
+ {
280
+ title: "Geocode a place",
281
+ description: 'Resolve a place name or address to coordinates using OpenStreetMap. Returns the best guess plus ranked candidates, and an `ambiguous` flag when several distinct places share the name (e.g. the many "Springfield"s) \u2014 present the candidates instead of guessing.',
282
+ inputSchema: {
283
+ query: z.string().describe("Place name or address to look up"),
284
+ limit: z.number().int().positive().optional().describe("Maximum candidates (default 5)"),
285
+ language: z.string().optional().describe('Preferred language, e.g. "en"')
286
+ }
287
+ },
288
+ async ({ query, limit, language }) => {
289
+ try {
290
+ const result = await disambiguateLocation(query, {
291
+ limit: limit ?? 5,
292
+ ...language ? { language } : {}
293
+ });
294
+ return jsonResult(toDisambiguationPayload(result));
295
+ } catch (error) {
296
+ return errorResult(error);
297
+ }
298
+ }
299
+ );
300
+ server.registerTool(
301
+ "list_categories",
302
+ {
303
+ title: "List categories",
304
+ description: "List the category terms proximap understands, for use with find_nearby_amenities. Returns each canonical term and its top-level category.",
305
+ inputSchema: {}
306
+ },
307
+ async () => jsonResult(categoryVocabulary())
308
+ );
309
+ server.registerTool(
310
+ "detect_amenity_gaps",
311
+ {
312
+ title: "Detect amenity gaps",
313
+ description: 'Report which everyday amenities are missing or far from a place \u2014 a "what is absent" check. Absence is framed as "not found in OSM within the threshold", not asserted as ground truth.',
314
+ inputSchema: {
315
+ query: z.string().describe('Place name, address, or "lat,lng" coordinates'),
316
+ categories: z.array(z.string()).optional().describe("Category terms to check (defaults to everyday needs)"),
317
+ searchRadiusMeters: z.number().positive().optional().describe("How far to search for the nearest match (default 5000)"),
318
+ thresholdMeters: z.number().positive().optional().describe("Distance beyond which a category is a gap (default 1500)"),
319
+ language: z.string().optional().describe("Preferred language for names")
320
+ }
321
+ },
322
+ async ({ query, categories, searchRadiusMeters, thresholdMeters, language }) => {
323
+ try {
324
+ const report = await detectGaps(query, {
325
+ ...categories ? { categories } : {},
326
+ ...searchRadiusMeters ? { searchRadiusMeters } : {},
327
+ ...thresholdMeters ? { thresholdMeters } : {},
328
+ ...language ? { language } : {}
329
+ });
330
+ return jsonResult(toGapsPayload(report));
331
+ } catch (error) {
332
+ return errorResult(error);
333
+ }
334
+ }
335
+ );
336
+ server.registerTool(
337
+ "walkability_score",
338
+ {
339
+ title: "Walkability score",
340
+ description: "Rate how walkable / well-served a location is on a 0-100 scale, with a transparent per-category breakdown (nearest distance + sub-score), the categories missing nearby, and a confidence note reflecting OSM data density. An open, tunable alternative to proprietary walkability indices.",
341
+ inputSchema: {
342
+ query: z.string().describe('Place name, address, or "lat,lng" coordinates'),
343
+ idealMeters: z.number().positive().optional().describe("Distance still scoring full marks, \u22485-min walk (default 400)"),
344
+ maxMeters: z.number().positive().optional().describe("Distance scoring zero, \u224830-min walk (default 2400)"),
345
+ language: z.string().optional().describe("Preferred language for names")
346
+ }
347
+ },
348
+ async ({ query, idealMeters, maxMeters, language }) => {
349
+ try {
350
+ const decay = {};
351
+ if (idealMeters) decay.idealMeters = idealMeters;
352
+ if (maxMeters) decay.maxMeters = maxMeters;
353
+ const report = await walkabilityScore(query, {
354
+ ...Object.keys(decay).length > 0 ? { decay } : {},
355
+ ...language ? { language } : {}
356
+ });
357
+ return jsonResult(toScorePayload(report));
358
+ } catch (error) {
359
+ return errorResult(error);
360
+ }
361
+ }
362
+ );
363
+ server.registerTool(
364
+ "compare_locations",
365
+ {
366
+ title: "Compare locations",
367
+ description: "Compare two or more candidate locations (e.g. places to live) by access to weighted daily-need categories, returning a ranked scorecard with a per-dimension winner and a confidence note. Key-free and OSM-native. Out of scope (not in OSM): transit frequency, school quality, crime, prices.",
368
+ inputSchema: {
369
+ locations: z.array(z.string()).min(2).describe('Two or more place names, addresses, or "lat,lng" strings'),
370
+ weights: z.record(z.string(), z.number().positive()).optional().describe('Category term to weight, e.g. { "food": 2, "transport": 3 }'),
371
+ idealMeters: z.number().positive().optional().describe("Full-marks distance (default 400)"),
372
+ maxMeters: z.number().positive().optional().describe("Zero-score distance (default 2400)"),
373
+ language: z.string().optional().describe("Preferred language for names")
374
+ }
375
+ },
376
+ async ({ locations, weights, idealMeters, maxMeters, language }) => {
377
+ try {
378
+ const categories = weights ? Object.entries(weights).map(([term, weight]) => ({ term, weight })) : void 0;
379
+ const decay = {};
380
+ if (idealMeters) decay.idealMeters = idealMeters;
381
+ if (maxMeters) decay.maxMeters = maxMeters;
382
+ const report = await compareLocations(locations, {
383
+ ...categories ? { categories } : {},
384
+ ...Object.keys(decay).length > 0 ? { decay } : {},
385
+ ...language ? { language } : {}
386
+ });
387
+ return jsonResult(toComparePayload(report));
388
+ } catch (error) {
389
+ return errorResult(error);
390
+ }
391
+ }
392
+ );
393
+ server.registerTool(
394
+ "reachable_amenities",
395
+ {
396
+ title: "Reachable amenities",
397
+ description: "List the amenities reachable within a time budget (e.g. a 15-minute walk) using a real isochrone where available, falling back to a travel-time threshold. Returns the filtered, time-sorted amenity list (the answer), plus the isochrone polygon. Powered by OpenStreetMap + the key-free Valhalla engine.",
398
+ inputSchema: {
399
+ query: z.string().describe('Place name, address, or "lat,lng" coordinates'),
400
+ within: z.number().positive().describe("Time budget in minutes (e.g. 15)"),
401
+ mode: z.enum(["walk", "bike", "drive"]).optional().describe("Travel mode (default walk)"),
402
+ categories: z.array(z.string()).optional().describe("Restrict to categories or terms, e.g. grocery, pharmacy"),
403
+ language: z.string().optional().describe("Preferred language for names")
404
+ }
405
+ },
406
+ async ({ query, within, mode, categories, language }) => {
407
+ try {
408
+ const result = await reachableAmenities(query, {
409
+ within,
410
+ routing: new ValhallaRoutingProvider(),
411
+ ...mode ? { mode } : {},
412
+ ...categories ? { categories } : {},
413
+ ...language ? { language } : {}
414
+ });
415
+ return jsonResult(toReachablePayload(result));
416
+ } catch (error) {
417
+ return errorResult(error);
418
+ }
419
+ }
420
+ );
421
+ server.registerTool(
422
+ "plan_errands",
423
+ {
424
+ title: "Plan errands",
425
+ description: 'Plan the shortest trip from an origin that visits one of each requested category (e.g. a pharmacy AND an ATM AND a grocery) \u2014 the Generalized-TSP "pick one per set, then optimize" workflow. Returns the chosen places in visit order with per-leg and total travel. Categories with no candidate nearby are reported as missing, not faked.',
426
+ inputSchema: {
427
+ query: z.string().describe('Starting place name, address, or "lat,lng" coordinates'),
428
+ categories: z.array(z.string()).min(1).describe('Categories to hit one of each, e.g. ["pharmacy", "atm", "grocery"]'),
429
+ mode: z.enum(["walk", "bike", "drive"]).optional().describe("Travel mode (default walk)"),
430
+ end: z.string().optional().describe('Optional fixed end point (place or "lat,lng")'),
431
+ candidatesPerCategory: z.number().int().positive().optional().describe("Nearest candidates considered per category (default 5)"),
432
+ language: z.string().optional().describe("Preferred language for names")
433
+ }
434
+ },
435
+ async ({ query, categories, mode, end, candidatesPerCategory, language }) => {
436
+ try {
437
+ const plan = await planErrands(query, {
438
+ categories,
439
+ ...mode ? { mode } : {},
440
+ ...end ? { end } : {},
441
+ ...candidatesPerCategory ? { candidatesPerCategory } : {},
442
+ ...language ? { language } : {}
443
+ });
444
+ return jsonResult(toErrandsPayload(plan));
445
+ } catch (error) {
446
+ return errorResult(error);
447
+ }
448
+ }
449
+ );
450
+ var transport = new StdioServerTransport();
451
+ await server.connect(transport);
452
+ process.stderr.write(`proximap MCP server ${VERSION} ready (stdio)
453
+ `);
454
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/payload.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n categoryVocabulary,\n compareLocations,\n detectGaps,\n disambiguateLocation,\n findNearbyAmenities,\n planErrands,\n reachableAmenities,\n ValhallaRoutingProvider,\n walkabilityScore,\n} from '@proximap/core';\nimport { z } from 'zod';\nimport {\n toComparePayload,\n toDisambiguationPayload,\n toErrandsPayload,\n toGapsPayload,\n toNearbyPayload,\n toReachablePayload,\n toScorePayload,\n} from './payload';\n\nconst VERSION = '1.0.0';\n\nfunction jsonResult(data: unknown) {\n return { content: [{ type: 'text' as const, text: JSON.stringify(data, null, 2) }] };\n}\n\nfunction errorResult(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n return { content: [{ type: 'text' as const, text: `Error: ${message}` }], isError: true };\n}\n\nconst server = new McpServer({ name: 'proximap', version: VERSION });\n\nserver.registerTool(\n 'find_nearby_amenities',\n {\n title: 'Find nearby amenities',\n description:\n 'Find amenities, utilities, and points of interest near a place, ranked by distance. ' +\n 'Accepts a place name, an address, or a \"lat,lng\" string. Powered by OpenStreetMap.',\n inputSchema: {\n query: z.string().describe('Place name, address, or \"lat,lng\" coordinates'),\n radiusMeters: z\n .number()\n .positive()\n .optional()\n .describe('Search radius in metres (default 1000)'),\n categories: z\n .array(z.string())\n .optional()\n .describe('Restrict to categories or terms, e.g. coffee, pharmacy, petrol'),\n filters: z\n .object({\n diet: z.union([z.string(), z.array(z.string())]).optional(),\n cuisine: z.union([z.string(), z.array(z.string())]).optional(),\n payment: z.union([z.string(), z.array(z.string())]).optional(),\n internetAccess: z.boolean().optional(),\n outdoorSeating: z.boolean().optional(),\n takeaway: z.boolean().optional(),\n delivery: z.boolean().optional(),\n wheelchair: z.union([z.string(), z.array(z.string())]).optional(),\n })\n .optional()\n .describe('Facet filters: diet/cuisine/payment, wifi, takeaway, wheelchair, etc.'),\n accessible: z\n .boolean()\n .optional()\n .describe('Rank step-free / wheelchair-accessible places first'),\n open: z\n .string()\n .optional()\n .describe(\n 'Keep only places open at this time: \"now\", or an ISO datetime like ' +\n '\"2026-06-20T21:00\". Unknown-hours places are kept and labelled.',\n ),\n rankBy: z\n .enum(['distance', 'travelTime'])\n .optional()\n .describe('Order by straight-line distance (default) or road-network travel time'),\n mode: z\n .enum(['walk', 'bike', 'drive'])\n .optional()\n .describe('Travel mode for rankBy=travelTime (default walk)'),\n explain: z\n .boolean()\n .optional()\n .describe('Add a short ranking reason / summary to each result'),\n concise: z\n .boolean()\n .optional()\n .describe('Return a slim, high-signal payload (with a one-line summary) for token economy'),\n limit: z\n .number()\n .int()\n .positive()\n .optional()\n .describe('Maximum number of results (default 30)'),\n language: z.string().optional().describe('Preferred language for names, e.g. \"en\"'),\n },\n },\n async ({\n query,\n radiusMeters,\n categories,\n filters,\n accessible,\n open,\n rankBy,\n mode,\n explain,\n concise,\n limit,\n language,\n }) => {\n try {\n const openOption = open === 'now' ? 'now' : open ? { at: open } : undefined;\n // Concise mode needs the per-result summary, so it implies explain.\n const explainOn = Boolean(explain || concise);\n const result = await findNearbyAmenities(query, {\n ...(radiusMeters ? { radiusMeters } : {}),\n ...(categories ? { categories } : {}),\n ...(filters ? { filters } : {}),\n ...(accessible ? { accessible } : {}),\n ...(openOption ? { open: openOption } : {}),\n // Travel-time ranking uses the key-free public Valhalla engine.\n ...(rankBy === 'travelTime'\n ? {\n rankBy: 'travelTime' as const,\n routing: new ValhallaRoutingProvider(),\n ...(mode ? { mode } : {}),\n }\n : {}),\n ...(explainOn ? { explain: true } : {}),\n ...(limit ? { limit } : {}),\n ...(language ? { language } : {}),\n });\n return jsonResult(toNearbyPayload(result, { concise: Boolean(concise) }));\n } catch (error) {\n return errorResult(error);\n }\n },\n);\n\nserver.registerTool(\n 'geocode',\n {\n title: 'Geocode a place',\n description:\n 'Resolve a place name or address to coordinates using OpenStreetMap. Returns the best ' +\n 'guess plus ranked candidates, and an `ambiguous` flag when several distinct places share ' +\n 'the name (e.g. the many \"Springfield\"s) — present the candidates instead of guessing.',\n inputSchema: {\n query: z.string().describe('Place name or address to look up'),\n limit: z.number().int().positive().optional().describe('Maximum candidates (default 5)'),\n language: z.string().optional().describe('Preferred language, e.g. \"en\"'),\n },\n },\n async ({ query, limit, language }) => {\n try {\n const result = await disambiguateLocation(query, {\n limit: limit ?? 5,\n ...(language ? { language } : {}),\n });\n return jsonResult(toDisambiguationPayload(result));\n } catch (error) {\n return errorResult(error);\n }\n },\n);\n\nserver.registerTool(\n 'list_categories',\n {\n title: 'List categories',\n description:\n 'List the category terms proximap understands, for use with find_nearby_amenities. ' +\n 'Returns each canonical term and its top-level category.',\n inputSchema: {},\n },\n async () => jsonResult(categoryVocabulary()),\n);\n\nserver.registerTool(\n 'detect_amenity_gaps',\n {\n title: 'Detect amenity gaps',\n description:\n 'Report which everyday amenities are missing or far from a place — a \"what is absent\" check. ' +\n 'Absence is framed as \"not found in OSM within the threshold\", not asserted as ground truth.',\n inputSchema: {\n query: z.string().describe('Place name, address, or \"lat,lng\" coordinates'),\n categories: z\n .array(z.string())\n .optional()\n .describe('Category terms to check (defaults to everyday needs)'),\n searchRadiusMeters: z\n .number()\n .positive()\n .optional()\n .describe('How far to search for the nearest match (default 5000)'),\n thresholdMeters: z\n .number()\n .positive()\n .optional()\n .describe('Distance beyond which a category is a gap (default 1500)'),\n language: z.string().optional().describe('Preferred language for names'),\n },\n },\n async ({ query, categories, searchRadiusMeters, thresholdMeters, language }) => {\n try {\n const report = await detectGaps(query, {\n ...(categories ? { categories } : {}),\n ...(searchRadiusMeters ? { searchRadiusMeters } : {}),\n ...(thresholdMeters ? { thresholdMeters } : {}),\n ...(language ? { language } : {}),\n });\n return jsonResult(toGapsPayload(report));\n } catch (error) {\n return errorResult(error);\n }\n },\n);\n\nserver.registerTool(\n 'walkability_score',\n {\n title: 'Walkability score',\n description:\n 'Rate how walkable / well-served a location is on a 0-100 scale, with a transparent ' +\n 'per-category breakdown (nearest distance + sub-score), the categories missing nearby, ' +\n 'and a confidence note reflecting OSM data density. An open, tunable alternative to ' +\n 'proprietary walkability indices.',\n inputSchema: {\n query: z.string().describe('Place name, address, or \"lat,lng\" coordinates'),\n idealMeters: z\n .number()\n .positive()\n .optional()\n .describe('Distance still scoring full marks, ≈5-min walk (default 400)'),\n maxMeters: z\n .number()\n .positive()\n .optional()\n .describe('Distance scoring zero, ≈30-min walk (default 2400)'),\n language: z.string().optional().describe('Preferred language for names'),\n },\n },\n async ({ query, idealMeters, maxMeters, language }) => {\n try {\n const decay: { idealMeters?: number; maxMeters?: number } = {};\n if (idealMeters) decay.idealMeters = idealMeters;\n if (maxMeters) decay.maxMeters = maxMeters;\n const report = await walkabilityScore(query, {\n ...(Object.keys(decay).length > 0 ? { decay } : {}),\n ...(language ? { language } : {}),\n });\n return jsonResult(toScorePayload(report));\n } catch (error) {\n return errorResult(error);\n }\n },\n);\n\nserver.registerTool(\n 'compare_locations',\n {\n title: 'Compare locations',\n description:\n 'Compare two or more candidate locations (e.g. places to live) by access to weighted ' +\n 'daily-need categories, returning a ranked scorecard with a per-dimension winner and a ' +\n 'confidence note. Key-free and OSM-native. Out of scope (not in OSM): transit frequency, ' +\n 'school quality, crime, prices.',\n inputSchema: {\n locations: z\n .array(z.string())\n .min(2)\n .describe('Two or more place names, addresses, or \"lat,lng\" strings'),\n weights: z\n .record(z.string(), z.number().positive())\n .optional()\n .describe('Category term to weight, e.g. { \"food\": 2, \"transport\": 3 }'),\n idealMeters: z.number().positive().optional().describe('Full-marks distance (default 400)'),\n maxMeters: z.number().positive().optional().describe('Zero-score distance (default 2400)'),\n language: z.string().optional().describe('Preferred language for names'),\n },\n },\n async ({ locations, weights, idealMeters, maxMeters, language }) => {\n try {\n const categories = weights\n ? Object.entries(weights).map(([term, weight]) => ({ term, weight }))\n : undefined;\n const decay: { idealMeters?: number; maxMeters?: number } = {};\n if (idealMeters) decay.idealMeters = idealMeters;\n if (maxMeters) decay.maxMeters = maxMeters;\n const report = await compareLocations(locations, {\n ...(categories ? { categories } : {}),\n ...(Object.keys(decay).length > 0 ? { decay } : {}),\n ...(language ? { language } : {}),\n });\n return jsonResult(toComparePayload(report));\n } catch (error) {\n return errorResult(error);\n }\n },\n);\n\nserver.registerTool(\n 'reachable_amenities',\n {\n title: 'Reachable amenities',\n description:\n 'List the amenities reachable within a time budget (e.g. a 15-minute walk) using a real ' +\n 'isochrone where available, falling back to a travel-time threshold. Returns the filtered, ' +\n 'time-sorted amenity list (the answer), plus the isochrone polygon. Powered by OpenStreetMap ' +\n '+ the key-free Valhalla engine.',\n inputSchema: {\n query: z.string().describe('Place name, address, or \"lat,lng\" coordinates'),\n within: z.number().positive().describe('Time budget in minutes (e.g. 15)'),\n mode: z.enum(['walk', 'bike', 'drive']).optional().describe('Travel mode (default walk)'),\n categories: z\n .array(z.string())\n .optional()\n .describe('Restrict to categories or terms, e.g. grocery, pharmacy'),\n language: z.string().optional().describe('Preferred language for names'),\n },\n },\n async ({ query, within, mode, categories, language }) => {\n try {\n const result = await reachableAmenities(query, {\n within,\n routing: new ValhallaRoutingProvider(),\n ...(mode ? { mode } : {}),\n ...(categories ? { categories } : {}),\n ...(language ? { language } : {}),\n });\n return jsonResult(toReachablePayload(result));\n } catch (error) {\n return errorResult(error);\n }\n },\n);\n\nserver.registerTool(\n 'plan_errands',\n {\n title: 'Plan errands',\n description:\n 'Plan the shortest trip from an origin that visits one of each requested category ' +\n '(e.g. a pharmacy AND an ATM AND a grocery) — the Generalized-TSP \"pick one per set, then ' +\n 'optimize\" workflow. Returns the chosen places in visit order with per-leg and total travel. ' +\n 'Categories with no candidate nearby are reported as missing, not faked.',\n inputSchema: {\n query: z.string().describe('Starting place name, address, or \"lat,lng\" coordinates'),\n categories: z\n .array(z.string())\n .min(1)\n .describe('Categories to hit one of each, e.g. [\"pharmacy\", \"atm\", \"grocery\"]'),\n mode: z.enum(['walk', 'bike', 'drive']).optional().describe('Travel mode (default walk)'),\n end: z.string().optional().describe('Optional fixed end point (place or \"lat,lng\")'),\n candidatesPerCategory: z\n .number()\n .int()\n .positive()\n .optional()\n .describe('Nearest candidates considered per category (default 5)'),\n language: z.string().optional().describe('Preferred language for names'),\n },\n },\n async ({ query, categories, mode, end, candidatesPerCategory, language }) => {\n try {\n const plan = await planErrands(query, {\n categories,\n ...(mode ? { mode } : {}),\n ...(end ? { end } : {}),\n ...(candidatesPerCategory ? { candidatesPerCategory } : {}),\n ...(language ? { language } : {}),\n });\n return jsonResult(toErrandsPayload(plan));\n } catch (error) {\n return errorResult(error);\n }\n },\n);\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\nprocess.stderr.write(`proximap MCP server ${VERSION} ready (stdio)\\n`);\n","import type {\n ComparisonReport,\n Disambiguation,\n ErrandPlan,\n GapReport,\n NearbyResult,\n Place,\n ReachableResult,\n WalkabilityReport,\n} from '@proximap/core';\n\n/**\n * Flatten a nearby-search result for agents. `concise` returns a slim, high-signal\n * payload (rank/name/category/metric/coords + a one-line summary) — materially\n * smaller than the detailed shape, for token economy.\n */\nexport function toNearbyPayload(result: NearbyResult, options: { concise?: boolean } = {}) {\n const { origin, results, total, routing } = result;\n const head = {\n origin: {\n name: origin.name,\n displayName: origin.displayName,\n lat: origin.location.lat,\n lng: origin.location.lng,\n },\n total,\n count: results.length,\n routing: routing ?? null,\n };\n\n if (options.concise) {\n return {\n ...head,\n results: results.map((poi) => ({\n rank: poi.rank,\n name: poi.name ?? null,\n category: poi.category,\n ...(poi.travelSeconds !== undefined\n ? { travelSeconds: poi.travelSeconds }\n : { distanceMeters: Math.round(poi.distanceMeters) }),\n lat: poi.location.lat,\n lng: poi.location.lng,\n ...(poi.rankingReason ? { summary: poi.rankingReason } : {}),\n })),\n };\n }\n\n return {\n ...head,\n results: results.map((poi) => ({\n rank: poi.rank,\n name: poi.name ?? null,\n category: poi.category,\n kind: poi.kind ?? null,\n distanceMeters: Math.round(poi.distanceMeters),\n travelSeconds: poi.travelSeconds ?? null,\n travelMeters: poi.travelMeters ?? null,\n lat: poi.location.lat,\n lng: poi.location.lng,\n osmId: poi.id,\n completeness: poi.completeness ?? null,\n lastVerified: poi.lastVerified ?? null,\n wheelchair: poi.tags.wheelchair ?? null,\n wheelchairDescription: poi.tags['wheelchair:description'] ?? null,\n openState: poi.openState ?? null,\n nextChange: poi.nextChange ?? null,\n rankingReason: poi.rankingReason ?? null,\n })),\n };\n}\n\nconst compactPlace = (place: Place) => ({\n name: place.name,\n displayName: place.displayName,\n lat: place.location.lat,\n lng: place.location.lng,\n kind: place.kind ?? null,\n});\n\n/** Flatten a geocoding disambiguation: best guess + ranked candidates + an ambiguity flag. */\nexport function toDisambiguationPayload(result: Disambiguation) {\n return {\n query: result.query,\n ambiguous: result.ambiguous,\n best: result.best ? compactPlace(result.best) : null,\n candidates: result.candidates.map(compactPlace),\n };\n}\n\n/** Flatten an errand plan for agent consumption. */\nexport function toErrandsPayload(plan: ErrandPlan) {\n return {\n origin: {\n name: plan.origin.name,\n displayName: plan.origin.displayName,\n lat: plan.origin.location.lat,\n lng: plan.origin.location.lng,\n },\n end: plan.end\n ? {\n displayName: plan.end.displayName,\n lat: plan.end.location.lat,\n lng: plan.end.location.lng,\n }\n : null,\n mode: plan.mode,\n totalSeconds: plan.totalSeconds,\n totalMeters: plan.totalMeters,\n missing: plan.missing,\n stops: plan.stops.map((stop) => ({\n category: stop.category,\n name: stop.poi.name ?? null,\n kind: stop.poi.kind ?? null,\n osmId: stop.poi.id,\n lat: stop.poi.location.lat,\n lng: stop.poi.location.lng,\n legSeconds: stop.legSeconds,\n legMeters: stop.legMeters,\n })),\n };\n}\n\n/** Flatten an isochrone-reachability result for agent consumption. */\nexport function toReachablePayload(result: ReachableResult) {\n return {\n origin: {\n name: result.origin.name,\n displayName: result.origin.displayName,\n lat: result.origin.location.lat,\n lng: result.origin.location.lng,\n },\n withinMinutes: result.withinMinutes,\n mode: result.mode,\n isochrone: result.isochrone, // [lng, lat] ring, or null\n count: result.count,\n results: result.results.map((poi) => ({\n rank: poi.rank,\n name: poi.name ?? null,\n category: poi.category,\n kind: poi.kind ?? null,\n travelSeconds: poi.travelSeconds ?? null,\n travelMeters: poi.travelMeters ?? null,\n distanceMeters: Math.round(poi.distanceMeters),\n lat: poi.location.lat,\n lng: poi.location.lng,\n osmId: poi.id,\n })),\n };\n}\n\n/** Map geocoding candidates to compact records. */\nexport function toGeocodePayload(places: Place[]) {\n return places.map((place) => ({\n name: place.name,\n displayName: place.displayName,\n lat: place.location.lat,\n lng: place.location.lng,\n kind: place.kind ?? null,\n }));\n}\n\n/** Flatten an amenity-gap report for agent consumption. */\nexport function toGapsPayload(report: GapReport) {\n return {\n origin: {\n name: report.origin.name,\n displayName: report.origin.displayName,\n lat: report.origin.location.lat,\n lng: report.origin.location.lng,\n },\n searchRadiusMeters: report.searchRadiusMeters,\n thresholdMeters: report.thresholdMeters,\n gaps: report.gaps,\n missing: report.missing,\n };\n}\n\n/** Flatten a walkability report for agent consumption. */\nexport function toScorePayload(report: WalkabilityReport) {\n return {\n origin: {\n name: report.origin.name,\n displayName: report.origin.displayName,\n lat: report.origin.location.lat,\n lng: report.origin.location.lng,\n },\n score: report.score,\n confidence: report.confidence,\n decay: report.decay,\n breakdown: report.breakdown,\n missing: report.missing,\n };\n}\n\n/** Flatten a location-comparison report for agent consumption. */\nexport function toComparePayload(report: ComparisonReport) {\n return {\n ranked: report.ranked.map((entry) => ({\n index: entry.index,\n displayName: entry.origin.displayName,\n score: entry.score,\n })),\n best: report.best\n ? {\n index: report.best.index,\n displayName: report.best.origin.displayName,\n score: report.best.score,\n }\n : null,\n weights: report.weights,\n dimensions: report.dimensions,\n locations: report.locations.map((location) => ({\n name: location.origin.name,\n displayName: location.origin.displayName,\n lat: location.origin.location.lat,\n lng: location.origin.location.lng,\n score: location.score,\n confidence: location.confidence,\n missing: location.missing,\n breakdown: location.breakdown,\n })),\n };\n}\n"],"mappings":";;;AAAA,SAAS,iBAAiB;AAC1B,SAAS,4BAA4B;AACrC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,SAAS;;;ACGX,SAAS,gBAAgB,QAAsB,UAAiC,CAAC,GAAG;AACzF,QAAM,EAAE,QAAQ,SAAS,OAAO,QAAQ,IAAI;AAC5C,QAAM,OAAO;AAAA,IACX,QAAQ;AAAA,MACN,MAAM,OAAO;AAAA,MACb,aAAa,OAAO;AAAA,MACpB,KAAK,OAAO,SAAS;AAAA,MACrB,KAAK,OAAO,SAAS;AAAA,IACvB;AAAA,IACA;AAAA,IACA,OAAO,QAAQ;AAAA,IACf,SAAS,WAAW;AAAA,EACtB;AAEA,MAAI,QAAQ,SAAS;AACnB,WAAO;AAAA,MACL,GAAG;AAAA,MACH,SAAS,QAAQ,IAAI,CAAC,SAAS;AAAA,QAC7B,MAAM,IAAI;AAAA,QACV,MAAM,IAAI,QAAQ;AAAA,QAClB,UAAU,IAAI;AAAA,QACd,GAAI,IAAI,kBAAkB,SACtB,EAAE,eAAe,IAAI,cAAc,IACnC,EAAE,gBAAgB,KAAK,MAAM,IAAI,cAAc,EAAE;AAAA,QACrD,KAAK,IAAI,SAAS;AAAA,QAClB,KAAK,IAAI,SAAS;AAAA,QAClB,GAAI,IAAI,gBAAgB,EAAE,SAAS,IAAI,cAAc,IAAI,CAAC;AAAA,MAC5D,EAAE;AAAA,IACJ;AAAA,EACF;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,SAAS,QAAQ,IAAI,CAAC,SAAS;AAAA,MAC7B,MAAM,IAAI;AAAA,MACV,MAAM,IAAI,QAAQ;AAAA,MAClB,UAAU,IAAI;AAAA,MACd,MAAM,IAAI,QAAQ;AAAA,MAClB,gBAAgB,KAAK,MAAM,IAAI,cAAc;AAAA,MAC7C,eAAe,IAAI,iBAAiB;AAAA,MACpC,cAAc,IAAI,gBAAgB;AAAA,MAClC,KAAK,IAAI,SAAS;AAAA,MAClB,KAAK,IAAI,SAAS;AAAA,MAClB,OAAO,IAAI;AAAA,MACX,cAAc,IAAI,gBAAgB;AAAA,MAClC,cAAc,IAAI,gBAAgB;AAAA,MAClC,YAAY,IAAI,KAAK,cAAc;AAAA,MACnC,uBAAuB,IAAI,KAAK,wBAAwB,KAAK;AAAA,MAC7D,WAAW,IAAI,aAAa;AAAA,MAC5B,YAAY,IAAI,cAAc;AAAA,MAC9B,eAAe,IAAI,iBAAiB;AAAA,IACtC,EAAE;AAAA,EACJ;AACF;AAEA,IAAM,eAAe,CAAC,WAAkB;AAAA,EACtC,MAAM,MAAM;AAAA,EACZ,aAAa,MAAM;AAAA,EACnB,KAAK,MAAM,SAAS;AAAA,EACpB,KAAK,MAAM,SAAS;AAAA,EACpB,MAAM,MAAM,QAAQ;AACtB;AAGO,SAAS,wBAAwB,QAAwB;AAC9D,SAAO;AAAA,IACL,OAAO,OAAO;AAAA,IACd,WAAW,OAAO;AAAA,IAClB,MAAM,OAAO,OAAO,aAAa,OAAO,IAAI,IAAI;AAAA,IAChD,YAAY,OAAO,WAAW,IAAI,YAAY;AAAA,EAChD;AACF;AAGO,SAAS,iBAAiB,MAAkB;AACjD,SAAO;AAAA,IACL,QAAQ;AAAA,MACN,MAAM,KAAK,OAAO;AAAA,MAClB,aAAa,KAAK,OAAO;AAAA,MACzB,KAAK,KAAK,OAAO,SAAS;AAAA,MAC1B,KAAK,KAAK,OAAO,SAAS;AAAA,IAC5B;AAAA,IACA,KAAK,KAAK,MACN;AAAA,MACE,aAAa,KAAK,IAAI;AAAA,MACtB,KAAK,KAAK,IAAI,SAAS;AAAA,MACvB,KAAK,KAAK,IAAI,SAAS;AAAA,IACzB,IACA;AAAA,IACJ,MAAM,KAAK;AAAA,IACX,cAAc,KAAK;AAAA,IACnB,aAAa,KAAK;AAAA,IAClB,SAAS,KAAK;AAAA,IACd,OAAO,KAAK,MAAM,IAAI,CAAC,UAAU;AAAA,MAC/B,UAAU,KAAK;AAAA,MACf,MAAM,KAAK,IAAI,QAAQ;AAAA,MACvB,MAAM,KAAK,IAAI,QAAQ;AAAA,MACvB,OAAO,KAAK,IAAI;AAAA,MAChB,KAAK,KAAK,IAAI,SAAS;AAAA,MACvB,KAAK,KAAK,IAAI,SAAS;AAAA,MACvB,YAAY,KAAK;AAAA,MACjB,WAAW,KAAK;AAAA,IAClB,EAAE;AAAA,EACJ;AACF;AAGO,SAAS,mBAAmB,QAAyB;AAC1D,SAAO;AAAA,IACL,QAAQ;AAAA,MACN,MAAM,OAAO,OAAO;AAAA,MACpB,aAAa,OAAO,OAAO;AAAA,MAC3B,KAAK,OAAO,OAAO,SAAS;AAAA,MAC5B,KAAK,OAAO,OAAO,SAAS;AAAA,IAC9B;AAAA,IACA,eAAe,OAAO;AAAA,IACtB,MAAM,OAAO;AAAA,IACb,WAAW,OAAO;AAAA;AAAA,IAClB,OAAO,OAAO;AAAA,IACd,SAAS,OAAO,QAAQ,IAAI,CAAC,SAAS;AAAA,MACpC,MAAM,IAAI;AAAA,MACV,MAAM,IAAI,QAAQ;AAAA,MAClB,UAAU,IAAI;AAAA,MACd,MAAM,IAAI,QAAQ;AAAA,MAClB,eAAe,IAAI,iBAAiB;AAAA,MACpC,cAAc,IAAI,gBAAgB;AAAA,MAClC,gBAAgB,KAAK,MAAM,IAAI,cAAc;AAAA,MAC7C,KAAK,IAAI,SAAS;AAAA,MAClB,KAAK,IAAI,SAAS;AAAA,MAClB,OAAO,IAAI;AAAA,IACb,EAAE;AAAA,EACJ;AACF;AAcO,SAAS,cAAc,QAAmB;AAC/C,SAAO;AAAA,IACL,QAAQ;AAAA,MACN,MAAM,OAAO,OAAO;AAAA,MACpB,aAAa,OAAO,OAAO;AAAA,MAC3B,KAAK,OAAO,OAAO,SAAS;AAAA,MAC5B,KAAK,OAAO,OAAO,SAAS;AAAA,IAC9B;AAAA,IACA,oBAAoB,OAAO;AAAA,IAC3B,iBAAiB,OAAO;AAAA,IACxB,MAAM,OAAO;AAAA,IACb,SAAS,OAAO;AAAA,EAClB;AACF;AAGO,SAAS,eAAe,QAA2B;AACxD,SAAO;AAAA,IACL,QAAQ;AAAA,MACN,MAAM,OAAO,OAAO;AAAA,MACpB,aAAa,OAAO,OAAO;AAAA,MAC3B,KAAK,OAAO,OAAO,SAAS;AAAA,MAC5B,KAAK,OAAO,OAAO,SAAS;AAAA,IAC9B;AAAA,IACA,OAAO,OAAO;AAAA,IACd,YAAY,OAAO;AAAA,IACnB,OAAO,OAAO;AAAA,IACd,WAAW,OAAO;AAAA,IAClB,SAAS,OAAO;AAAA,EAClB;AACF;AAGO,SAAS,iBAAiB,QAA0B;AACzD,SAAO;AAAA,IACL,QAAQ,OAAO,OAAO,IAAI,CAAC,WAAW;AAAA,MACpC,OAAO,MAAM;AAAA,MACb,aAAa,MAAM,OAAO;AAAA,MAC1B,OAAO,MAAM;AAAA,IACf,EAAE;AAAA,IACF,MAAM,OAAO,OACT;AAAA,MACE,OAAO,OAAO,KAAK;AAAA,MACnB,aAAa,OAAO,KAAK,OAAO;AAAA,MAChC,OAAO,OAAO,KAAK;AAAA,IACrB,IACA;AAAA,IACJ,SAAS,OAAO;AAAA,IAChB,YAAY,OAAO;AAAA,IACnB,WAAW,OAAO,UAAU,IAAI,CAAC,cAAc;AAAA,MAC7C,MAAM,SAAS,OAAO;AAAA,MACtB,aAAa,SAAS,OAAO;AAAA,MAC7B,KAAK,SAAS,OAAO,SAAS;AAAA,MAC9B,KAAK,SAAS,OAAO,SAAS;AAAA,MAC9B,OAAO,SAAS;AAAA,MAChB,YAAY,SAAS;AAAA,MACrB,SAAS,SAAS;AAAA,MAClB,WAAW,SAAS;AAAA,IACtB,EAAE;AAAA,EACJ;AACF;;;ADtMA,IAAM,UAAU;AAEhB,SAAS,WAAW,MAAe;AACjC,SAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,KAAK,UAAU,MAAM,MAAM,CAAC,EAAE,CAAC,EAAE;AACrF;AAEA,SAAS,YAAY,OAAgB;AACnC,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,SAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,UAAU,OAAO,GAAG,CAAC,GAAG,SAAS,KAAK;AAC1F;AAEA,IAAM,SAAS,IAAI,UAAU,EAAE,MAAM,YAAY,SAAS,QAAQ,CAAC;AAEnE,OAAO;AAAA,EACL;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,aACE;AAAA,IAEF,aAAa;AAAA,MACX,OAAO,EAAE,OAAO,EAAE,SAAS,+CAA+C;AAAA,MAC1E,cAAc,EACX,OAAO,EACP,SAAS,EACT,SAAS,EACT,SAAS,wCAAwC;AAAA,MACpD,YAAY,EACT,MAAM,EAAE,OAAO,CAAC,EAChB,SAAS,EACT,SAAS,gEAAgE;AAAA,MAC5E,SAAS,EACN,OAAO;AAAA,QACN,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS;AAAA,QAC1D,SAAS,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS;AAAA,QAC7D,SAAS,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS;AAAA,QAC7D,gBAAgB,EAAE,QAAQ,EAAE,SAAS;AAAA,QACrC,gBAAgB,EAAE,QAAQ,EAAE,SAAS;AAAA,QACrC,UAAU,EAAE,QAAQ,EAAE,SAAS;AAAA,QAC/B,UAAU,EAAE,QAAQ,EAAE,SAAS;AAAA,QAC/B,YAAY,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS;AAAA,MAClE,CAAC,EACA,SAAS,EACT,SAAS,uEAAuE;AAAA,MACnF,YAAY,EACT,QAAQ,EACR,SAAS,EACT,SAAS,qDAAqD;AAAA,MACjE,MAAM,EACH,OAAO,EACP,SAAS,EACT;AAAA,QACC;AAAA,MAEF;AAAA,MACF,QAAQ,EACL,KAAK,CAAC,YAAY,YAAY,CAAC,EAC/B,SAAS,EACT,SAAS,uEAAuE;AAAA,MACnF,MAAM,EACH,KAAK,CAAC,QAAQ,QAAQ,OAAO,CAAC,EAC9B,SAAS,EACT,SAAS,kDAAkD;AAAA,MAC9D,SAAS,EACN,QAAQ,EACR,SAAS,EACT,SAAS,qDAAqD;AAAA,MACjE,SAAS,EACN,QAAQ,EACR,SAAS,EACT,SAAS,gFAAgF;AAAA,MAC5F,OAAO,EACJ,OAAO,EACP,IAAI,EACJ,SAAS,EACT,SAAS,EACT,SAAS,wCAAwC;AAAA,MACpD,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yCAAyC;AAAA,IACpF;AAAA,EACF;AAAA,EACA,OAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,MAAM;AACJ,QAAI;AACF,YAAM,aAAa,SAAS,QAAQ,QAAQ,OAAO,EAAE,IAAI,KAAK,IAAI;AAElE,YAAM,YAAY,QAAQ,WAAW,OAAO;AAC5C,YAAM,SAAS,MAAM,oBAAoB,OAAO;AAAA,QAC9C,GAAI,eAAe,EAAE,aAAa,IAAI,CAAC;AAAA,QACvC,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,QACnC,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAAA,QAC7B,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,QACnC,GAAI,aAAa,EAAE,MAAM,WAAW,IAAI,CAAC;AAAA;AAAA,QAEzC,GAAI,WAAW,eACX;AAAA,UACE,QAAQ;AAAA,UACR,SAAS,IAAI,wBAAwB;AAAA,UACrC,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,QACzB,IACA,CAAC;AAAA,QACL,GAAI,YAAY,EAAE,SAAS,KAAK,IAAI,CAAC;AAAA,QACrC,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,QACzB,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,MACjC,CAAC;AACD,aAAO,WAAW,gBAAgB,QAAQ,EAAE,SAAS,QAAQ,OAAO,EAAE,CAAC,CAAC;AAAA,IAC1E,SAAS,OAAO;AACd,aAAO,YAAY,KAAK;AAAA,IAC1B;AAAA,EACF;AACF;AAEA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,aACE;AAAA,IAGF,aAAa;AAAA,MACX,OAAO,EAAE,OAAO,EAAE,SAAS,kCAAkC;AAAA,MAC7D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,gCAAgC;AAAA,MACvF,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,+BAA+B;AAAA,IAC1E;AAAA,EACF;AAAA,EACA,OAAO,EAAE,OAAO,OAAO,SAAS,MAAM;AACpC,QAAI;AACF,YAAM,SAAS,MAAM,qBAAqB,OAAO;AAAA,QAC/C,OAAO,SAAS;AAAA,QAChB,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,MACjC,CAAC;AACD,aAAO,WAAW,wBAAwB,MAAM,CAAC;AAAA,IACnD,SAAS,OAAO;AACd,aAAO,YAAY,KAAK;AAAA,IAC1B;AAAA,EACF;AACF;AAEA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,aACE;AAAA,IAEF,aAAa,CAAC;AAAA,EAChB;AAAA,EACA,YAAY,WAAW,mBAAmB,CAAC;AAC7C;AAEA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,aACE;AAAA,IAEF,aAAa;AAAA,MACX,OAAO,EAAE,OAAO,EAAE,SAAS,+CAA+C;AAAA,MAC1E,YAAY,EACT,MAAM,EAAE,OAAO,CAAC,EAChB,SAAS,EACT,SAAS,sDAAsD;AAAA,MAClE,oBAAoB,EACjB,OAAO,EACP,SAAS,EACT,SAAS,EACT,SAAS,wDAAwD;AAAA,MACpE,iBAAiB,EACd,OAAO,EACP,SAAS,EACT,SAAS,EACT,SAAS,0DAA0D;AAAA,MACtE,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,8BAA8B;AAAA,IACzE;AAAA,EACF;AAAA,EACA,OAAO,EAAE,OAAO,YAAY,oBAAoB,iBAAiB,SAAS,MAAM;AAC9E,QAAI;AACF,YAAM,SAAS,MAAM,WAAW,OAAO;AAAA,QACrC,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,QACnC,GAAI,qBAAqB,EAAE,mBAAmB,IAAI,CAAC;AAAA,QACnD,GAAI,kBAAkB,EAAE,gBAAgB,IAAI,CAAC;AAAA,QAC7C,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,MACjC,CAAC;AACD,aAAO,WAAW,cAAc,MAAM,CAAC;AAAA,IACzC,SAAS,OAAO;AACd,aAAO,YAAY,KAAK;AAAA,IAC1B;AAAA,EACF;AACF;AAEA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,aACE;AAAA,IAIF,aAAa;AAAA,MACX,OAAO,EAAE,OAAO,EAAE,SAAS,+CAA+C;AAAA,MAC1E,aAAa,EACV,OAAO,EACP,SAAS,EACT,SAAS,EACT,SAAS,mEAA8D;AAAA,MAC1E,WAAW,EACR,OAAO,EACP,SAAS,EACT,SAAS,EACT,SAAS,yDAAoD;AAAA,MAChE,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,8BAA8B;AAAA,IACzE;AAAA,EACF;AAAA,EACA,OAAO,EAAE,OAAO,aAAa,WAAW,SAAS,MAAM;AACrD,QAAI;AACF,YAAM,QAAsD,CAAC;AAC7D,UAAI,YAAa,OAAM,cAAc;AACrC,UAAI,UAAW,OAAM,YAAY;AACjC,YAAM,SAAS,MAAM,iBAAiB,OAAO;AAAA,QAC3C,GAAI,OAAO,KAAK,KAAK,EAAE,SAAS,IAAI,EAAE,MAAM,IAAI,CAAC;AAAA,QACjD,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,MACjC,CAAC;AACD,aAAO,WAAW,eAAe,MAAM,CAAC;AAAA,IAC1C,SAAS,OAAO;AACd,aAAO,YAAY,KAAK;AAAA,IAC1B;AAAA,EACF;AACF;AAEA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,aACE;AAAA,IAIF,aAAa;AAAA,MACX,WAAW,EACR,MAAM,EAAE,OAAO,CAAC,EAChB,IAAI,CAAC,EACL,SAAS,0DAA0D;AAAA,MACtE,SAAS,EACN,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,EAAE,SAAS,CAAC,EACxC,SAAS,EACT,SAAS,6DAA6D;AAAA,MACzE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,mCAAmC;AAAA,MAC1F,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,oCAAoC;AAAA,MACzF,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,8BAA8B;AAAA,IACzE;AAAA,EACF;AAAA,EACA,OAAO,EAAE,WAAW,SAAS,aAAa,WAAW,SAAS,MAAM;AAClE,QAAI;AACF,YAAM,aAAa,UACf,OAAO,QAAQ,OAAO,EAAE,IAAI,CAAC,CAAC,MAAM,MAAM,OAAO,EAAE,MAAM,OAAO,EAAE,IAClE;AACJ,YAAM,QAAsD,CAAC;AAC7D,UAAI,YAAa,OAAM,cAAc;AACrC,UAAI,UAAW,OAAM,YAAY;AACjC,YAAM,SAAS,MAAM,iBAAiB,WAAW;AAAA,QAC/C,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,QACnC,GAAI,OAAO,KAAK,KAAK,EAAE,SAAS,IAAI,EAAE,MAAM,IAAI,CAAC;AAAA,QACjD,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,MACjC,CAAC;AACD,aAAO,WAAW,iBAAiB,MAAM,CAAC;AAAA,IAC5C,SAAS,OAAO;AACd,aAAO,YAAY,KAAK;AAAA,IAC1B;AAAA,EACF;AACF;AAEA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,aACE;AAAA,IAIF,aAAa;AAAA,MACX,OAAO,EAAE,OAAO,EAAE,SAAS,+CAA+C;AAAA,MAC1E,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,kCAAkC;AAAA,MACzE,MAAM,EAAE,KAAK,CAAC,QAAQ,QAAQ,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,4BAA4B;AAAA,MACxF,YAAY,EACT,MAAM,EAAE,OAAO,CAAC,EAChB,SAAS,EACT,SAAS,yDAAyD;AAAA,MACrE,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,8BAA8B;AAAA,IACzE;AAAA,EACF;AAAA,EACA,OAAO,EAAE,OAAO,QAAQ,MAAM,YAAY,SAAS,MAAM;AACvD,QAAI;AACF,YAAM,SAAS,MAAM,mBAAmB,OAAO;AAAA,QAC7C;AAAA,QACA,SAAS,IAAI,wBAAwB;AAAA,QACrC,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,QACvB,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,QACnC,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,MACjC,CAAC;AACD,aAAO,WAAW,mBAAmB,MAAM,CAAC;AAAA,IAC9C,SAAS,OAAO;AACd,aAAO,YAAY,KAAK;AAAA,IAC1B;AAAA,EACF;AACF;AAEA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,IACE,OAAO;AAAA,IACP,aACE;AAAA,IAIF,aAAa;AAAA,MACX,OAAO,EAAE,OAAO,EAAE,SAAS,wDAAwD;AAAA,MACnF,YAAY,EACT,MAAM,EAAE,OAAO,CAAC,EAChB,IAAI,CAAC,EACL,SAAS,oEAAoE;AAAA,MAChF,MAAM,EAAE,KAAK,CAAC,QAAQ,QAAQ,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,4BAA4B;AAAA,MACxF,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,+CAA+C;AAAA,MACnF,uBAAuB,EACpB,OAAO,EACP,IAAI,EACJ,SAAS,EACT,SAAS,EACT,SAAS,wDAAwD;AAAA,MACpE,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,8BAA8B;AAAA,IACzE;AAAA,EACF;AAAA,EACA,OAAO,EAAE,OAAO,YAAY,MAAM,KAAK,uBAAuB,SAAS,MAAM;AAC3E,QAAI;AACF,YAAM,OAAO,MAAM,YAAY,OAAO;AAAA,QACpC;AAAA,QACA,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,QACvB,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;AAAA,QACrB,GAAI,wBAAwB,EAAE,sBAAsB,IAAI,CAAC;AAAA,QACzD,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,MACjC,CAAC;AACD,aAAO,WAAW,iBAAiB,IAAI,CAAC;AAAA,IAC1C,SAAS,OAAO;AACd,aAAO,YAAY,KAAK;AAAA,IAC1B;AAAA,EACF;AACF;AAEA,IAAM,YAAY,IAAI,qBAAqB;AAC3C,MAAM,OAAO,QAAQ,SAAS;AAC9B,QAAQ,OAAO,MAAM,uBAAuB,OAAO;AAAA,CAAkB;","names":[]}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@proximap/mcp",
3
+ "version": "1.0.0",
4
+ "description": "Model Context Protocol server for proximap — geospatial tools for AI agents and Claude.",
5
+ "keywords": [
6
+ "mcp",
7
+ "model-context-protocol",
8
+ "claude",
9
+ "ai",
10
+ "maps",
11
+ "geocoding",
12
+ "amenities"
13
+ ],
14
+ "license": "MIT",
15
+ "author": "Ameya Borkar <ameyaborkar17@gmail.com>",
16
+ "homepage": "https://github.com/AmeyaBorkar/proximap#readme",
17
+ "bugs": "https://github.com/AmeyaBorkar/proximap/issues",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/AmeyaBorkar/proximap.git",
21
+ "directory": "packages/mcp"
22
+ },
23
+ "type": "module",
24
+ "bin": {
25
+ "proximap-mcp": "./dist/index.js"
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "README.md",
30
+ "LICENSE"
31
+ ],
32
+ "engines": {
33
+ "node": ">=20"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "scripts": {
39
+ "build": "tsup",
40
+ "typecheck": "tsc --noEmit",
41
+ "prepublishOnly": "npm run build"
42
+ },
43
+ "dependencies": {
44
+ "@modelcontextprotocol/sdk": "^1.29.0",
45
+ "@proximap/core": "^1.0.0",
46
+ "zod": "^4.4.3"
47
+ }
48
+ }