nextbillion-mcp 0.1.11 → 0.2.1
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/dist/index.js +470 -134
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2561,6 +2561,112 @@ function handleIntersectionResults(result, left, right) {
|
|
|
2561
2561
|
result.value = merged.data;
|
|
2562
2562
|
return result;
|
|
2563
2563
|
}
|
|
2564
|
+
var $ZodTuple = /* @__PURE__ */ $constructor("$ZodTuple", (inst, def) => {
|
|
2565
|
+
$ZodType.init(inst, def);
|
|
2566
|
+
const items = def.items;
|
|
2567
|
+
inst._zod.parse = (payload, ctx) => {
|
|
2568
|
+
const input = payload.value;
|
|
2569
|
+
if (!Array.isArray(input)) {
|
|
2570
|
+
payload.issues.push({
|
|
2571
|
+
input,
|
|
2572
|
+
inst,
|
|
2573
|
+
expected: "tuple",
|
|
2574
|
+
code: "invalid_type"
|
|
2575
|
+
});
|
|
2576
|
+
return payload;
|
|
2577
|
+
}
|
|
2578
|
+
payload.value = [];
|
|
2579
|
+
const proms = [];
|
|
2580
|
+
const optinStart = getTupleOptStart(items, "optin");
|
|
2581
|
+
const optoutStart = getTupleOptStart(items, "optout");
|
|
2582
|
+
if (!def.rest) {
|
|
2583
|
+
if (input.length < optinStart) {
|
|
2584
|
+
payload.issues.push({
|
|
2585
|
+
code: "too_small",
|
|
2586
|
+
minimum: optinStart,
|
|
2587
|
+
inclusive: true,
|
|
2588
|
+
input,
|
|
2589
|
+
inst,
|
|
2590
|
+
origin: "array"
|
|
2591
|
+
});
|
|
2592
|
+
return payload;
|
|
2593
|
+
}
|
|
2594
|
+
if (input.length > items.length) {
|
|
2595
|
+
payload.issues.push({
|
|
2596
|
+
code: "too_big",
|
|
2597
|
+
maximum: items.length,
|
|
2598
|
+
inclusive: true,
|
|
2599
|
+
input,
|
|
2600
|
+
inst,
|
|
2601
|
+
origin: "array"
|
|
2602
|
+
});
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
const itemResults = new Array(items.length);
|
|
2606
|
+
for (let i = 0; i < items.length; i++) {
|
|
2607
|
+
const r = items[i]._zod.run({ value: input[i], issues: [] }, ctx);
|
|
2608
|
+
if (r instanceof Promise) {
|
|
2609
|
+
proms.push(r.then((rr) => {
|
|
2610
|
+
itemResults[i] = rr;
|
|
2611
|
+
}));
|
|
2612
|
+
} else {
|
|
2613
|
+
itemResults[i] = r;
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
if (def.rest) {
|
|
2617
|
+
let i = items.length - 1;
|
|
2618
|
+
const rest = input.slice(items.length);
|
|
2619
|
+
for (const el of rest) {
|
|
2620
|
+
i++;
|
|
2621
|
+
const result = def.rest._zod.run({ value: el, issues: [] }, ctx);
|
|
2622
|
+
if (result instanceof Promise) {
|
|
2623
|
+
proms.push(result.then((r) => handleTupleResult(r, payload, i)));
|
|
2624
|
+
} else {
|
|
2625
|
+
handleTupleResult(result, payload, i);
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2629
|
+
if (proms.length) {
|
|
2630
|
+
return Promise.all(proms).then(() => handleTupleResults(itemResults, payload, items, input, optoutStart));
|
|
2631
|
+
}
|
|
2632
|
+
return handleTupleResults(itemResults, payload, items, input, optoutStart);
|
|
2633
|
+
};
|
|
2634
|
+
});
|
|
2635
|
+
function getTupleOptStart(items, key) {
|
|
2636
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
2637
|
+
if (items[i]._zod[key] !== "optional")
|
|
2638
|
+
return i + 1;
|
|
2639
|
+
}
|
|
2640
|
+
return 0;
|
|
2641
|
+
}
|
|
2642
|
+
function handleTupleResult(result, final, index) {
|
|
2643
|
+
if (result.issues.length) {
|
|
2644
|
+
final.issues.push(...prefixIssues(index, result.issues));
|
|
2645
|
+
}
|
|
2646
|
+
final.value[index] = result.value;
|
|
2647
|
+
}
|
|
2648
|
+
function handleTupleResults(itemResults, final, items, input, optoutStart) {
|
|
2649
|
+
for (let i = 0; i < items.length; i++) {
|
|
2650
|
+
const r = itemResults[i];
|
|
2651
|
+
const isPresent = i < input.length;
|
|
2652
|
+
if (r.issues.length) {
|
|
2653
|
+
if (!isPresent && i >= optoutStart) {
|
|
2654
|
+
final.value.length = i;
|
|
2655
|
+
break;
|
|
2656
|
+
}
|
|
2657
|
+
final.issues.push(...prefixIssues(i, r.issues));
|
|
2658
|
+
}
|
|
2659
|
+
final.value[i] = r.value;
|
|
2660
|
+
}
|
|
2661
|
+
for (let i = final.value.length - 1; i >= input.length; i--) {
|
|
2662
|
+
if (items[i]._zod.optout === "optional" && final.value[i] === void 0) {
|
|
2663
|
+
final.value.length = i;
|
|
2664
|
+
} else {
|
|
2665
|
+
break;
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
return final;
|
|
2669
|
+
}
|
|
2564
2670
|
var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => {
|
|
2565
2671
|
$ZodType.init(inst, def);
|
|
2566
2672
|
inst._zod.parse = (payload, ctx) => {
|
|
@@ -5358,6 +5464,26 @@ function intersection(left, right) {
|
|
|
5358
5464
|
right
|
|
5359
5465
|
});
|
|
5360
5466
|
}
|
|
5467
|
+
var ZodTuple = /* @__PURE__ */ $constructor("ZodTuple", (inst, def) => {
|
|
5468
|
+
$ZodTuple.init(inst, def);
|
|
5469
|
+
ZodType.init(inst, def);
|
|
5470
|
+
inst._zod.processJSONSchema = (ctx, json, params) => tupleProcessor(inst, ctx, json, params);
|
|
5471
|
+
inst.rest = (rest) => inst.clone({
|
|
5472
|
+
...inst._zod.def,
|
|
5473
|
+
rest
|
|
5474
|
+
});
|
|
5475
|
+
});
|
|
5476
|
+
function tuple(items, _paramsOrRest, _params) {
|
|
5477
|
+
const hasRest = _paramsOrRest instanceof $ZodType;
|
|
5478
|
+
const params = hasRest ? _params : _paramsOrRest;
|
|
5479
|
+
const rest = hasRest ? _paramsOrRest : null;
|
|
5480
|
+
return new ZodTuple({
|
|
5481
|
+
type: "tuple",
|
|
5482
|
+
items,
|
|
5483
|
+
rest,
|
|
5484
|
+
...util_exports.normalizeParams(params)
|
|
5485
|
+
});
|
|
5486
|
+
}
|
|
5361
5487
|
var ZodRecord = /* @__PURE__ */ $constructor("ZodRecord", (inst, def) => {
|
|
5362
5488
|
$ZodRecord.init(inst, def);
|
|
5363
5489
|
ZodType.init(inst, def);
|
|
@@ -20277,9 +20403,20 @@ function toError(value) {
|
|
|
20277
20403
|
|
|
20278
20404
|
// src/config.ts
|
|
20279
20405
|
import { tmpdir } from "node:os";
|
|
20280
|
-
import { join } from "node:path";
|
|
20406
|
+
import { isAbsolute, join } from "node:path";
|
|
20407
|
+
function resolveImageDir(env = process.env) {
|
|
20408
|
+
const raw = env.NBAI_IMAGE_DIR?.trim();
|
|
20409
|
+
if (!raw) return {};
|
|
20410
|
+
if (raw.toLowerCase() === "tmp") return { dir: join(tmpdir(), "nextbillion-mcp") };
|
|
20411
|
+
if (!isAbsolute(raw)) {
|
|
20412
|
+
return {
|
|
20413
|
+
warning: `NBAI_IMAGE_DIR must be an absolute path or "tmp", got "${raw}"; map images will not be saved`
|
|
20414
|
+
};
|
|
20415
|
+
}
|
|
20416
|
+
return { dir: raw };
|
|
20417
|
+
}
|
|
20281
20418
|
function imageOutputDir(env = process.env) {
|
|
20282
|
-
return env.
|
|
20419
|
+
return resolveImageDir(env).dir;
|
|
20283
20420
|
}
|
|
20284
20421
|
var DEFAULT_BASE_URL = "https://api.nextbillion.io";
|
|
20285
20422
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
@@ -20432,6 +20569,34 @@ function summarizePlaces(response, noun = "result") {
|
|
|
20432
20569
|
return `${items.length} ${noun}${items.length === 1 ? "" : "s"} found:
|
|
20433
20570
|
${lines.join("\n")}${more}`;
|
|
20434
20571
|
}
|
|
20572
|
+
var HazmatSchema = array(
|
|
20573
|
+
_enum([
|
|
20574
|
+
"explosives",
|
|
20575
|
+
"gas",
|
|
20576
|
+
"flammable_liquid",
|
|
20577
|
+
"flammable_gas",
|
|
20578
|
+
"organic",
|
|
20579
|
+
"toxic",
|
|
20580
|
+
"radioactive",
|
|
20581
|
+
"corrosive",
|
|
20582
|
+
"other"
|
|
20583
|
+
])
|
|
20584
|
+
).min(1).describe("Hazardous cargo classes carried (truck mode, flexible service only)");
|
|
20585
|
+
var EmissionClassSchema = _enum(["euro0", "euro1", "euro2", "euro3", "euro4", "euro5", "euro6", "euro7", "euro8", "euro9"]).describe("Engine emission class (EU regions, truck mode, flexible service only)");
|
|
20586
|
+
var ExcludeSchema = array(
|
|
20587
|
+
_enum([
|
|
20588
|
+
"toll",
|
|
20589
|
+
"ferry",
|
|
20590
|
+
"highway",
|
|
20591
|
+
"service_road",
|
|
20592
|
+
"uturn",
|
|
20593
|
+
"sharp_turn",
|
|
20594
|
+
"left_turn",
|
|
20595
|
+
"right_turn"
|
|
20596
|
+
])
|
|
20597
|
+
).min(1).describe(
|
|
20598
|
+
"Strict filter: only routes that completely avoid these features are returned (error if none exists). Use `avoid` for a soft preference instead. Flexible service only"
|
|
20599
|
+
);
|
|
20435
20600
|
|
|
20436
20601
|
// src/tools/places/text-search.ts
|
|
20437
20602
|
var TextSearchSchema = strictObject({
|
|
@@ -20617,8 +20782,30 @@ var geocodeStructured = {
|
|
|
20617
20782
|
}
|
|
20618
20783
|
};
|
|
20619
20784
|
|
|
20620
|
-
// src/tools/places/place-
|
|
20785
|
+
// src/tools/places/place-browse.ts
|
|
20621
20786
|
var Schema4 = strictObject({
|
|
20787
|
+
categories: array(string2().min(1)).min(1).describe(
|
|
20788
|
+
'Category names or ids to browse; a place matching any of them is returned (e.g. ["restaurant"], ["schools"], ["7376"])'
|
|
20789
|
+
),
|
|
20790
|
+
...PlacesFilterShape
|
|
20791
|
+
});
|
|
20792
|
+
var placeBrowse = {
|
|
20793
|
+
name: "place_browse",
|
|
20794
|
+
title: "Browse Places by Category",
|
|
20795
|
+
description: 'List places of given categories around a location, ranked by distance, without a text query - e.g. all restaurants or schools near a point. Use place_search when the user describes what they want in words; use place_lookup to expand a result by id. Parameters: categories (required array of category names or ids); near {latitude, longitude} (recommended), radius_m (with near), country_codes, bounding_box {west, south, east, north}, limit, language, view. Example: {"categories": ["restaurant"], "near": {"latitude": 1.2839, "longitude": 103.8607}, "radius_m": 800, "limit": 10}',
|
|
20796
|
+
inputSchema: Schema4,
|
|
20797
|
+
annotations: READ_ONLY,
|
|
20798
|
+
async run(args, nb) {
|
|
20799
|
+
const response = await nb.getJson("/browse", {
|
|
20800
|
+
categories: args.categories.join(","),
|
|
20801
|
+
...placesFilterQuery(args)
|
|
20802
|
+
});
|
|
20803
|
+
return textResult(summarizePlaces(response, "place"), response);
|
|
20804
|
+
}
|
|
20805
|
+
};
|
|
20806
|
+
|
|
20807
|
+
// src/tools/places/place-lookup.ts
|
|
20808
|
+
var Schema5 = strictObject({
|
|
20622
20809
|
id: string2().min(1).describe("Unique place id, as returned by the other place/geocoding tools"),
|
|
20623
20810
|
view: ViewSchema.optional()
|
|
20624
20811
|
});
|
|
@@ -20626,7 +20813,7 @@ var placeLookup = {
|
|
|
20626
20813
|
name: "place_lookup",
|
|
20627
20814
|
title: "Place Lookup",
|
|
20628
20815
|
description: 'Fetch the full details of a place (address, position, access points, categories, contacts) by its unique id, as returned by place_search, geocode_forward, autosuggest, autocomplete or search_along_route. Parameters: id (required); optional view. Example: {"id": "2EmBgAmFpR9dg0D89EBzNA"}',
|
|
20629
|
-
inputSchema:
|
|
20816
|
+
inputSchema: Schema5,
|
|
20630
20817
|
annotations: READ_ONLY,
|
|
20631
20818
|
async run(args, nb) {
|
|
20632
20819
|
const response = await nb.getJson("/lookup", {
|
|
@@ -20638,7 +20825,7 @@ var placeLookup = {
|
|
|
20638
20825
|
};
|
|
20639
20826
|
|
|
20640
20827
|
// src/tools/places/postcode-lookup.ts
|
|
20641
|
-
var
|
|
20828
|
+
var Schema6 = strictObject({
|
|
20642
20829
|
postal_code: string2().optional().describe("Postal/ZIP code to look up (requires `country`; do not combine with `coordinate`)"),
|
|
20643
20830
|
country: string2().optional().describe(
|
|
20644
20831
|
"Country of the postal code \u2014 name, alpha-2, or alpha-3 ISO code. Required with `postal_code`"
|
|
@@ -20654,7 +20841,7 @@ var postcodeLookup = {
|
|
|
20654
20841
|
name: "postcode_lookup",
|
|
20655
20842
|
title: "Postcode Lookup",
|
|
20656
20843
|
description: 'Get the centroid and boundary polygon of a postal/ZIP code, or find the postal code that contains a coordinate; one lookup per call. Supported countries: USA, India, UK, Netherlands, Austria, Germany, Indonesia, France, Singapore, Philippines, Canada, Australia, New Zealand, Italy, Brazil, Mexico, Spain. Parameters: either postal_code plus country (name, alpha-2 or alpha-3 code), or coordinate {latitude, longitude}; optional geojson_boundary (true for a GeoJSON boundary). Example: {"postal_code": "90011", "country": "USA"}',
|
|
20657
|
-
inputSchema:
|
|
20844
|
+
inputSchema: Schema6,
|
|
20658
20845
|
annotations: READ_ONLY,
|
|
20659
20846
|
async run(args, nb) {
|
|
20660
20847
|
if (args.postal_code && args.coordinate) {
|
|
@@ -20697,7 +20884,7 @@ var AVOID_VALUES = [
|
|
|
20697
20884
|
"tunnel",
|
|
20698
20885
|
"none"
|
|
20699
20886
|
];
|
|
20700
|
-
var
|
|
20887
|
+
var Schema7 = strictObject({
|
|
20701
20888
|
origin: CoordinateSchema.describe("Route start (must be a routable land location)"),
|
|
20702
20889
|
destination: CoordinateSchema.describe("Route end (must be a routable land location)"),
|
|
20703
20890
|
waypoints: array(CoordinateSchema).max(200).optional().describe("Intermediate stops visited in order (max 200)"),
|
|
@@ -20712,6 +20899,26 @@ var Schema6 = strictObject({
|
|
|
20712
20899
|
avoid: array(_enum(AVOID_VALUES)).optional().describe(
|
|
20713
20900
|
"Road features to avoid when alternatives exist. Fast service supports only toll/ferry/highway"
|
|
20714
20901
|
),
|
|
20902
|
+
exclude: ExcludeSchema.optional(),
|
|
20903
|
+
road_info: array(
|
|
20904
|
+
_enum([
|
|
20905
|
+
"max_speed",
|
|
20906
|
+
"toll_distance",
|
|
20907
|
+
"toll_cost",
|
|
20908
|
+
"toll_info",
|
|
20909
|
+
"truck_route",
|
|
20910
|
+
"stop_sign",
|
|
20911
|
+
"traffic_light"
|
|
20912
|
+
])
|
|
20913
|
+
).min(1).optional().describe(
|
|
20914
|
+
"Extra per-segment information to return: max_speed, toll_distance, toll_cost, toll_info, truck_route, stop_sign, traffic_light (flexible service only)"
|
|
20915
|
+
),
|
|
20916
|
+
hazmat_type: HazmatSchema.optional(),
|
|
20917
|
+
truck_axle_load: number2().positive().optional().describe("Load per axle in tonnes (truck mode, flexible service only)"),
|
|
20918
|
+
emission_class: EmissionClassSchema.optional(),
|
|
20919
|
+
cross_border: boolean2().optional().describe(
|
|
20920
|
+
"Allow the route to cross international borders (region-dependent, flexible service only)"
|
|
20921
|
+
),
|
|
20715
20922
|
honor_restrictions: boolean2().optional().describe(
|
|
20716
20923
|
"When true, enforce restricted-area rules: no route is generated if origin, destination, or a waypoint is inside a restricted area. Default false: route anyway with a warning (flexible service only)"
|
|
20717
20924
|
),
|
|
@@ -20726,8 +20933,8 @@ var Schema6 = strictObject({
|
|
|
20726
20933
|
var directions = {
|
|
20727
20934
|
name: "directions",
|
|
20728
20935
|
title: "Directions",
|
|
20729
|
-
description: 'Calculate a route between an origin and a destination with optional waypoints; returns distance (meters), duration (seconds) and an encoded polyline per route - pass the polyline to static_route_map to draw it. Use distance_matrix for many origin/destination pairs. Parameters: origin, destination {latitude, longitude} (required); optional waypoints (array
|
|
20730
|
-
inputSchema:
|
|
20936
|
+
description: 'Calculate a route between an origin and a destination with optional waypoints; returns distance (meters), duration (seconds) and an encoded polyline per route - pass the polyline to static_route_map to draw it. Use distance_matrix for many origin/destination pairs. Parameters: origin, destination {latitude, longitude} (required); optional waypoints (array, max 200), mode (car | truck | motorcycle | bike | walk), service ("flexible" default: all modes and options; "fast": car/truck only, lower latency), route_type, departure_time (UNIX seconds), avoid (soft filter), exclude (strict filter), road_info (max_speed, toll_distance, toll_cost, toll_info, truck_route, stop_sign, traffic_light), honor_restrictions, alternatives, steps (fast only), geometry (polyline | polyline6), truck options: truck_size_cm {height, width, length}, truck_weight_kg, truck_axle_load (tonnes), hazmat_type, emission_class (euro0-euro9), cross_border. Example: {"origin": {"latitude": 37.7749, "longitude": -122.4194}, "destination": {"latitude": 34.0522, "longitude": -118.2437}, "mode": "truck", "truck_weight_kg": 18000, "hazmat_type": ["flammable_liquid"]}',
|
|
20937
|
+
inputSchema: Schema7,
|
|
20731
20938
|
annotations: READ_ONLY,
|
|
20732
20939
|
async run(args, nb) {
|
|
20733
20940
|
const service = args.service ?? "flexible";
|
|
@@ -20745,6 +20952,12 @@ var directions = {
|
|
|
20745
20952
|
route_type: args.route_type,
|
|
20746
20953
|
departure_time: args.departure_time,
|
|
20747
20954
|
avoid: args.avoid?.length ? args.avoid.join("|") : void 0,
|
|
20955
|
+
exclude: args.exclude?.length ? args.exclude.join("|") : void 0,
|
|
20956
|
+
road_info: args.road_info?.length ? args.road_info.join("|") : void 0,
|
|
20957
|
+
hazmat_type: args.hazmat_type?.length ? args.hazmat_type.join("|") : void 0,
|
|
20958
|
+
truck_axle_load: args.truck_axle_load,
|
|
20959
|
+
emission_class: args.emission_class,
|
|
20960
|
+
cross_border: args.cross_border,
|
|
20748
20961
|
honor_restrictions: args.honor_restrictions,
|
|
20749
20962
|
alternatives: args.alternatives,
|
|
20750
20963
|
altcount: args.alternatives ? 3 : void 0,
|
|
@@ -20769,7 +20982,7 @@ Geometry (encoded polyline) is in structured content.` : "No route found.",
|
|
|
20769
20982
|
};
|
|
20770
20983
|
|
|
20771
20984
|
// src/tools/routing/distance-matrix.ts
|
|
20772
|
-
var
|
|
20985
|
+
var Schema8 = strictObject({
|
|
20773
20986
|
origins: array(CoordinateSchema).min(1).max(1e3).describe("Start points (matrix rows)"),
|
|
20774
20987
|
destinations: array(CoordinateSchema).min(1).max(1e3).describe("End points (matrix columns)"),
|
|
20775
20988
|
mode: _enum(["car", "truck", "motorcycle", "bike", "walk"]).optional().describe("Travel mode (default car). Modes other than car/truck require service=flexible"),
|
|
@@ -20781,6 +20994,16 @@ var Schema7 = strictObject({
|
|
|
20781
20994
|
"Departure as a UNIX timestamp in seconds, for typical-traffic results (flexible only)"
|
|
20782
20995
|
),
|
|
20783
20996
|
avoid: array(_enum(["toll", "highway", "ferry", "none"])).optional().describe("Road features to avoid"),
|
|
20997
|
+
exclude: ExcludeSchema.optional(),
|
|
20998
|
+
hazmat_type: HazmatSchema.optional(),
|
|
20999
|
+
truck_axle_load: number2().positive().optional().describe("Load per axle in tonnes (truck mode, flexible service only)"),
|
|
21000
|
+
emission_class: EmissionClassSchema.optional(),
|
|
21001
|
+
cross_border: boolean2().optional().describe(
|
|
21002
|
+
"Allow routes to cross international borders (region-dependent, flexible service only)"
|
|
21003
|
+
),
|
|
21004
|
+
route_failed_prompt: boolean2().optional().describe(
|
|
21005
|
+
"When true, unroutable origin/destination pairs return -1 instead of 0 so they can be told apart from zero-distance pairs"
|
|
21006
|
+
),
|
|
20784
21007
|
honor_restrictions: boolean2().optional().describe(
|
|
20785
21008
|
"When true, enforce restricted-area rules for origin/destination points instead of routing through with a warning (flexible service only)"
|
|
20786
21009
|
),
|
|
@@ -20790,8 +21013,8 @@ var Schema7 = strictObject({
|
|
|
20790
21013
|
var distanceMatrix = {
|
|
20791
21014
|
name: "distance_matrix",
|
|
20792
21015
|
title: "Distance Matrix",
|
|
20793
|
-
description: 'Compute travel distance (meters) and duration (seconds) for every origin-to-destination pair in one call; one row per origin with one element per destination, in input order.
|
|
20794
|
-
inputSchema:
|
|
21016
|
+
description: 'Compute travel distance (meters) and duration (seconds) for every origin-to-destination pair in one call; one row per origin with one element per destination, in input order. Use it instead of repeated directions calls when several pairs are needed. Parameters: origins, destinations (arrays of {latitude, longitude}, required); optional mode, service ("fast" default: up to 1000x1000 points; "flexible": the options below, max 50x50), route_type, departure_time, avoid (soft), exclude (strict), honor_restrictions, route_failed_prompt (unroutable pairs return -1 instead of 0), truck options: truck_size_cm {height, width, length}, truck_weight_kg, truck_axle_load, hazmat_type, emission_class, cross_border. Example: {"origins": [{"latitude": 1.29, "longitude": 103.85}], "destinations": [{"latitude": 1.35, "longitude": 103.99}, {"latitude": 1.3, "longitude": 103.77}], "route_failed_prompt": true}',
|
|
21017
|
+
inputSchema: Schema8,
|
|
20795
21018
|
annotations: READ_ONLY,
|
|
20796
21019
|
async run(args, nb) {
|
|
20797
21020
|
const service = args.service ?? "fast";
|
|
@@ -20813,6 +21036,12 @@ var distanceMatrix = {
|
|
|
20813
21036
|
route_type: args.route_type,
|
|
20814
21037
|
departure_time: args.departure_time,
|
|
20815
21038
|
avoid: args.avoid?.length ? args.avoid.join("|") : void 0,
|
|
21039
|
+
exclude: args.exclude?.length ? args.exclude.join("|") : void 0,
|
|
21040
|
+
hazmat_type: args.hazmat_type?.length ? args.hazmat_type.join("|") : void 0,
|
|
21041
|
+
truck_axle_load: args.truck_axle_load,
|
|
21042
|
+
emission_class: args.emission_class,
|
|
21043
|
+
cross_border: args.cross_border,
|
|
21044
|
+
route_failed_prompt: args.route_failed_prompt,
|
|
20816
21045
|
honor_restrictions: args.honor_restrictions,
|
|
20817
21046
|
truck_size: args.truck_size_cm ? `${args.truck_size_cm.height},${args.truck_size_cm.width},${args.truck_size_cm.length}` : void 0,
|
|
20818
21047
|
truck_weight: args.truck_weight_kg
|
|
@@ -20827,7 +21056,7 @@ var distanceMatrix = {
|
|
|
20827
21056
|
};
|
|
20828
21057
|
|
|
20829
21058
|
// src/tools/routing/isochrone.ts
|
|
20830
|
-
var
|
|
21059
|
+
var Schema9 = strictObject({
|
|
20831
21060
|
origin: CoordinateSchema.describe("Starting point of the reachability analysis"),
|
|
20832
21061
|
contours_minutes: array(number2().int().min(1).max(40)).max(4).optional().describe("Travel times in minutes, one contour each (max 4 values, increasing, max 40)"),
|
|
20833
21062
|
contours_meters: array(number2().int().min(1).max(6e4)).max(4).optional().describe("Travel distances in meters, one contour each (max 4 values, increasing, max 60000)"),
|
|
@@ -20836,13 +21065,15 @@ var Schema8 = strictObject({
|
|
|
20836
21065
|
denoise: number2().min(0).max(1).optional().describe(
|
|
20837
21066
|
"Remove contours smaller than this fraction of the largest (default 1: largest only)"
|
|
20838
21067
|
),
|
|
20839
|
-
departure_time: number2().int().optional().describe("Departure as a UNIX timestamp in seconds, for typical-traffic analysis")
|
|
21068
|
+
departure_time: number2().int().optional().describe("Departure as a UNIX timestamp in seconds, for typical-traffic analysis"),
|
|
21069
|
+
contours_colors: array(string2().regex(/^[0-9a-fA-F]{6}$/, "hex color without #, e.g. ff0000")).max(4).optional().describe('One hex color (without #) per contour, e.g. ["ff0000", "00ff00"]'),
|
|
21070
|
+
generalize: number2().positive().optional().describe("Simplification tolerance in meters (Douglas-Peucker); omit for automatic")
|
|
20840
21071
|
});
|
|
20841
21072
|
var isochrone = {
|
|
20842
21073
|
name: "isochrone",
|
|
20843
21074
|
title: "Isochrone",
|
|
20844
|
-
description: 'Calculate the area reachable from a point within given travel times or distances; returns a GeoJSON FeatureCollection of contours (geometry coordinates are
|
|
20845
|
-
inputSchema:
|
|
21075
|
+
description: 'Calculate the area reachable from a point within given travel times or distances; returns a GeoJSON FeatureCollection of contours (geometry coordinates are [longitude, latitude]). To draw the result, pass each contour ring to static_route_map as paths[].geojson_coordinates with a fill_color. Parameters: origin {latitude, longitude} (required); exactly one of contours_minutes (array, up to 4 values, max 40) or contours_meters (array, up to 4 values, max 60000); optional mode, polygons (true for Polygon geometry), denoise, generalize (meters), contours_colors (hex without #, one per contour), departure_time. Example: {"origin": {"latitude": 37.7749, "longitude": -122.4194}, "contours_minutes": [5, 10], "polygons": true}',
|
|
21076
|
+
inputSchema: Schema9,
|
|
20846
21077
|
annotations: READ_ONLY,
|
|
20847
21078
|
async run(args, nb) {
|
|
20848
21079
|
if (!args.contours_minutes?.length && !args.contours_meters?.length) {
|
|
@@ -20858,7 +21089,9 @@ var isochrone = {
|
|
|
20858
21089
|
mode: args.mode,
|
|
20859
21090
|
polygons: args.polygons,
|
|
20860
21091
|
denoise: args.denoise,
|
|
20861
|
-
departure_time: args.departure_time
|
|
21092
|
+
departure_time: args.departure_time,
|
|
21093
|
+
contours_colors: args.contours_colors?.join(","),
|
|
21094
|
+
generalize: args.generalize
|
|
20862
21095
|
});
|
|
20863
21096
|
const features = response.features ?? [];
|
|
20864
21097
|
const metric = args.contours_minutes?.length ? "minutes" : "meters";
|
|
@@ -20871,18 +21104,19 @@ var isochrone = {
|
|
|
20871
21104
|
};
|
|
20872
21105
|
|
|
20873
21106
|
// src/tools/routing/search-along-route.ts
|
|
20874
|
-
var
|
|
21107
|
+
var Schema10 = strictObject({
|
|
20875
21108
|
route_points: array(CoordinateSchema).min(2).describe("Waypoints defining the route to search along, in travel order"),
|
|
20876
21109
|
query: string2().min(1).describe('What to find along the route, e.g. "gas station", "coffee"'),
|
|
20877
21110
|
max_detour_seconds: number2().int().min(1).max(3600).optional().describe("Max driving time to reach a result after leaving the route (default 900 = 15 min)"),
|
|
20878
21111
|
sort_by: _enum(["detour_time", "detour_offset"]).optional().describe("Order by detour driving time, or by distance of the detour point from route start"),
|
|
20879
|
-
limit: number2().int().min(1).max(20).optional().describe("Maximum results (default 10, max 20)")
|
|
21112
|
+
limit: number2().int().min(1).max(20).optional().describe("Maximum results (default 10, max 20)"),
|
|
21113
|
+
view: _enum(["Unified", "AR", "CN", "IN", "PK", "MA", "RU", "RS", "TW", "TR"]).optional().describe("Geopolitical view for disputed territories (default: requester region or Unified)")
|
|
20880
21114
|
});
|
|
20881
21115
|
var searchAlongRoute = {
|
|
20882
21116
|
name: "search_along_route",
|
|
20883
21117
|
title: "Search Along Route",
|
|
20884
|
-
description: 'Find places matching a text query along a driving route, with the detour time and distance each stop adds - for "find X on the way" questions. Pass the route as an ordered list of waypoints, e.g. the origin, waypoints and destination used with directions. Parameters: route_points (array of {latitude, longitude}, at least 2, required), query (required); optional max_detour_seconds (default 900, max 3600), sort_by (detour_time | detour_offset), limit (max 20). Example: {"route_points": [{"latitude": 34.0493, "longitude": -118.2557}, {"latitude": 34.0415, "longitude": -118.231}], "query": "gas station", "max_detour_seconds": 600}',
|
|
20885
|
-
inputSchema:
|
|
21118
|
+
description: 'Find places matching a text query along a driving route, with the detour time and distance each stop adds - for "find X on the way" questions. Pass the route as an ordered list of waypoints, e.g. the origin, waypoints and destination used with directions. Parameters: route_points (array of {latitude, longitude}, at least 2, required), query (required); optional max_detour_seconds (default 900, max 3600), sort_by (detour_time | detour_offset), limit (max 20), view. Example: {"route_points": [{"latitude": 34.0493, "longitude": -118.2557}, {"latitude": 34.0415, "longitude": -118.231}], "query": "gas station", "max_detour_seconds": 600}',
|
|
21119
|
+
inputSchema: Schema10,
|
|
20886
21120
|
annotations: READ_ONLY,
|
|
20887
21121
|
async run(args, nb) {
|
|
20888
21122
|
const response = await nb.postJson(
|
|
@@ -20893,7 +21127,8 @@ var searchAlongRoute = {
|
|
|
20893
21127
|
q: args.query,
|
|
20894
21128
|
max_detour_time: args.max_detour_seconds,
|
|
20895
21129
|
sort_by: args.sort_by,
|
|
20896
|
-
limit: args.limit
|
|
21130
|
+
limit: args.limit,
|
|
21131
|
+
view: args.view
|
|
20897
21132
|
}
|
|
20898
21133
|
);
|
|
20899
21134
|
const items = response.items ?? [];
|
|
@@ -20926,84 +21161,6 @@ function logInfo(message) {
|
|
|
20926
21161
|
console.error(`[nextbillion-mcp] ${message}`);
|
|
20927
21162
|
}
|
|
20928
21163
|
|
|
20929
|
-
// src/tools/maps/static-shared.ts
|
|
20930
|
-
var StaticImageShape = {
|
|
20931
|
-
width: number2().int().min(16).max(2048).optional().describe("Image width in pixels (default 512)"),
|
|
20932
|
-
height: number2().int().min(16).max(2048).optional().describe("Image height in pixels (default 512)"),
|
|
20933
|
-
style: string2().optional().describe('Map style id: "streets", "light", "dark", or "hybrid" (default streets)'),
|
|
20934
|
-
format: _enum(["png", "jpg", "webp"]).optional().describe("Image format (default png)"),
|
|
20935
|
-
retina: boolean2().optional().describe("Render at @2x resolution for high-DPI displays")
|
|
20936
|
-
};
|
|
20937
|
-
var MIME_TYPES = {
|
|
20938
|
-
png: "image/png",
|
|
20939
|
-
jpg: "image/jpeg",
|
|
20940
|
-
webp: "image/webp"
|
|
20941
|
-
};
|
|
20942
|
-
function staticImagePath(positionSegment, args) {
|
|
20943
|
-
const style = args.style ?? "streets";
|
|
20944
|
-
const size = `${args.width ?? 512}x${args.height ?? 512}${args.retina ? "@2x" : ""}`;
|
|
20945
|
-
const format = args.format ?? "png";
|
|
20946
|
-
return `/maps/${encodeURIComponent(style)}/static/${positionSegment}/${size}.${format}`;
|
|
20947
|
-
}
|
|
20948
|
-
function markerParam(markers, order) {
|
|
20949
|
-
return markers.map((m) => {
|
|
20950
|
-
const pair = order === "lng-first" ? `${m.longitude},${m.latitude}` : `${m.latitude},${m.longitude}`;
|
|
20951
|
-
return `${pair}${m.color ? `,${m.color}` : ""}`;
|
|
20952
|
-
}).join("|");
|
|
20953
|
-
}
|
|
20954
|
-
async function fetchImageResult(nb, path, query, caption, args, filePrefix = "map") {
|
|
20955
|
-
const image = await nb.getBinary(path, query);
|
|
20956
|
-
const mimeType = image.contentType.startsWith("image/") ? image.contentType : MIME_TYPES[args.format ?? "png"];
|
|
20957
|
-
const savedPath = await saveImage(image.data, filePrefix, args.format ?? "png");
|
|
20958
|
-
const location = savedPath ? ` Saved to ${savedPath} (for clients that cannot display images inline).` : "";
|
|
20959
|
-
return {
|
|
20960
|
-
content: [
|
|
20961
|
-
{ type: "image", data: Buffer.from(image.data).toString("base64"), mimeType },
|
|
20962
|
-
{ type: "text", text: caption + location }
|
|
20963
|
-
]
|
|
20964
|
-
};
|
|
20965
|
-
}
|
|
20966
|
-
async function saveImage(data, prefix, ext) {
|
|
20967
|
-
try {
|
|
20968
|
-
const dir = imageOutputDir();
|
|
20969
|
-
await mkdir(dir, { recursive: true });
|
|
20970
|
-
const hash = createHash("sha1").update(data).digest("hex").slice(0, 8);
|
|
20971
|
-
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\..+/, "");
|
|
20972
|
-
const filePath = join2(dir, `${prefix}-${stamp}-${hash}.${ext === "jpg" ? "jpg" : ext}`);
|
|
20973
|
-
await writeFile(filePath, data);
|
|
20974
|
-
return filePath;
|
|
20975
|
-
} catch (error2) {
|
|
20976
|
-
logError("Could not save rendered image to disk", error2);
|
|
20977
|
-
return void 0;
|
|
20978
|
-
}
|
|
20979
|
-
}
|
|
20980
|
-
|
|
20981
|
-
// src/tools/maps/static-map-image.ts
|
|
20982
|
-
var Schema10 = strictObject({
|
|
20983
|
-
center: CoordinateSchema.describe("Center of the map view"),
|
|
20984
|
-
zoom: number2().min(0).max(22).describe("Zoom level (0 = world, ~10 = city, ~15 = streets; fractional values allowed)"),
|
|
20985
|
-
markers: array(CoordinateSchema.extend({ color: string2().optional() })).optional().describe('Markers to draw, each with optional color (e.g. "red", "#0000ff")'),
|
|
20986
|
-
...StaticImageShape
|
|
20987
|
-
});
|
|
20988
|
-
var staticMapImage = {
|
|
20989
|
-
name: "static_map_image",
|
|
20990
|
-
title: "Static Map Image",
|
|
20991
|
-
description: 'Render a static map image centered on a location, optionally with markers; returns the image inline and also saves it to a local file (path in the result text) for clients that cannot display images. To draw a route, use static_route_map. Parameters: center {latitude, longitude} and zoom (0-22) (required); optional markers (array of {latitude, longitude, color}), width, height (default 512), style (streets | light | dark | hybrid), format (png | jpg | webp), retina. Example: {"center": {"latitude": 48.8566, "longitude": 2.3522}, "zoom": 14, "style": "dark", "markers": [{"latitude": 48.8584, "longitude": 2.2945, "color": "red"}]}',
|
|
20992
|
-
inputSchema: Schema10,
|
|
20993
|
-
annotations: READ_ONLY,
|
|
20994
|
-
async run(args, nb) {
|
|
20995
|
-
const position = `${args.center.latitude},${args.center.longitude},${args.zoom}`;
|
|
20996
|
-
return fetchImageResult(
|
|
20997
|
-
nb,
|
|
20998
|
-
staticImagePath(position, args),
|
|
20999
|
-
{ markers: args.markers?.length ? markerParam(args.markers, "lng-first") : void 0 },
|
|
21000
|
-
`Map centered at (${args.center.latitude}, ${args.center.longitude}), zoom ${args.zoom}` + (args.markers?.length ? `, ${args.markers.length} marker(s).` : "."),
|
|
21001
|
-
args,
|
|
21002
|
-
"map"
|
|
21003
|
-
);
|
|
21004
|
-
}
|
|
21005
|
-
};
|
|
21006
|
-
|
|
21007
21164
|
// src/tools/maps/polyline.ts
|
|
21008
21165
|
var PRECISION = 1e5;
|
|
21009
21166
|
function decodePolyline(encoded) {
|
|
@@ -21108,64 +21265,238 @@ function fitPolylineToBudget(points, maxEncodedChars) {
|
|
|
21108
21265
|
};
|
|
21109
21266
|
}
|
|
21110
21267
|
|
|
21111
|
-
// src/tools/maps/static-
|
|
21268
|
+
// src/tools/maps/static-shared.ts
|
|
21269
|
+
var StaticImageShape = {
|
|
21270
|
+
width: number2().int().min(16).max(2048).optional().describe("Image width in pixels (default 512)"),
|
|
21271
|
+
height: number2().int().min(16).max(2048).optional().describe("Image height in pixels (default 512)"),
|
|
21272
|
+
style: string2().optional().describe('Map style id: "streets", "light", "dark", or "hybrid" (default streets)'),
|
|
21273
|
+
format: _enum(["png", "jpg", "webp"]).optional().describe("Image format (default png)"),
|
|
21274
|
+
retina: boolean2().optional().describe("Render at @2x resolution for high-DPI displays")
|
|
21275
|
+
};
|
|
21276
|
+
var MarkerSchema = strictObject({
|
|
21277
|
+
latitude: number2().min(-90).max(90),
|
|
21278
|
+
longitude: number2().min(-180).max(180),
|
|
21279
|
+
color: string2().optional().describe('Marker color, e.g. "red" or "#0000ff" (ignored when icon_url is set)'),
|
|
21280
|
+
icon_url: string2().url().optional().describe("URL of a custom marker image (max 64 kB / 4096 px, e.g. 64x64 PNG)"),
|
|
21281
|
+
anchor: _enum([
|
|
21282
|
+
"top",
|
|
21283
|
+
"left",
|
|
21284
|
+
"bottom",
|
|
21285
|
+
"right",
|
|
21286
|
+
"center",
|
|
21287
|
+
"topleft",
|
|
21288
|
+
"bottomleft",
|
|
21289
|
+
"topright",
|
|
21290
|
+
"bottomright"
|
|
21291
|
+
]).optional().describe("Anchor point of a custom icon (default bottom)"),
|
|
21292
|
+
scale: number2().positive().optional().describe("Custom icon scale factor (default 1)")
|
|
21293
|
+
});
|
|
21294
|
+
var PathSchema = strictObject({
|
|
21295
|
+
points: array(CoordinateSchema).min(2).optional().describe("Vertices as {latitude, longitude} objects"),
|
|
21296
|
+
geojson_coordinates: array(tuple([number2(), number2()])).min(2).optional().describe(
|
|
21297
|
+
"Vertices as GeoJSON [longitude, latitude] pairs - pass an isochrone or GeoJSON ring here unchanged"
|
|
21298
|
+
),
|
|
21299
|
+
stroke_color: string2().optional().describe("Line color (default blue)"),
|
|
21300
|
+
stroke_width: number2().int().min(1).max(20).optional().describe("Line width in px (default 3)"),
|
|
21301
|
+
fill_color: string2().optional().describe(
|
|
21302
|
+
'Fill color for a closed shape, e.g. "rgba(255,0,0,0.3)" or "#ff000055"; omit for a line'
|
|
21303
|
+
)
|
|
21304
|
+
}).describe("A line or filled polygon overlay");
|
|
21305
|
+
var PathsShape = {
|
|
21306
|
+
paths: array(PathSchema).max(10).optional().describe("Extra lines/polygons to draw (e.g. isochrone contours as filled polygons)")
|
|
21307
|
+
};
|
|
21308
|
+
var MIME_TYPES = {
|
|
21309
|
+
png: "image/png",
|
|
21310
|
+
jpg: "image/jpeg",
|
|
21311
|
+
webp: "image/webp"
|
|
21312
|
+
};
|
|
21313
|
+
function staticImagePath(positionSegment, args) {
|
|
21314
|
+
const style = args.style ?? "streets";
|
|
21315
|
+
const size = `${args.width ?? 512}x${args.height ?? 512}${args.retina ? "@2x" : ""}`;
|
|
21316
|
+
const format = args.format ?? "png";
|
|
21317
|
+
return `/maps/${encodeURIComponent(style)}/static/${positionSegment}/${size}.${format}`;
|
|
21318
|
+
}
|
|
21319
|
+
function markerParam(markers, order) {
|
|
21320
|
+
return markers.map((m) => {
|
|
21321
|
+
const pair = order === "lng-first" ? `${m.longitude},${m.latitude}` : `${m.latitude},${m.longitude}`;
|
|
21322
|
+
const commands = [];
|
|
21323
|
+
if (m.icon_url) commands.push(`icon:${m.icon_url}`);
|
|
21324
|
+
if (m.anchor) commands.push(`anchor:${m.anchor}`);
|
|
21325
|
+
if (m.scale !== void 0) commands.push(`scale:${m.scale}`);
|
|
21326
|
+
const color = m.color && !m.icon_url ? `,${m.color}` : "";
|
|
21327
|
+
return `${commands.length ? commands.join("|") + "|" : ""}${pair}${color}`;
|
|
21328
|
+
}).join("|");
|
|
21329
|
+
}
|
|
21330
|
+
async function fetchImageResult(nb, path, query, caption, args, filePrefix = "map") {
|
|
21331
|
+
const image = await nb.getBinary(path, query);
|
|
21332
|
+
const mimeType = image.contentType.startsWith("image/") ? image.contentType : MIME_TYPES[args.format ?? "png"];
|
|
21333
|
+
const savedPath = await saveImage(image.data, filePrefix, args.format ?? "png");
|
|
21334
|
+
const location = savedPath ? ` Saved to ${savedPath} (for clients that cannot display images inline).` : "";
|
|
21335
|
+
return {
|
|
21336
|
+
content: [
|
|
21337
|
+
{ type: "image", data: Buffer.from(image.data).toString("base64"), mimeType },
|
|
21338
|
+
{ type: "text", text: caption + location }
|
|
21339
|
+
]
|
|
21340
|
+
};
|
|
21341
|
+
}
|
|
21342
|
+
async function saveImage(data, prefix, ext) {
|
|
21343
|
+
const dir = imageOutputDir();
|
|
21344
|
+
if (!dir) return void 0;
|
|
21345
|
+
try {
|
|
21346
|
+
await mkdir(dir, { recursive: true });
|
|
21347
|
+
const hash = createHash("sha1").update(data).digest("hex").slice(0, 8);
|
|
21348
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\..+/, "");
|
|
21349
|
+
const filePath = join2(dir, `${prefix}-${stamp}-${hash}.${ext === "jpg" ? "jpg" : ext}`);
|
|
21350
|
+
try {
|
|
21351
|
+
await writeFile(filePath, data, { flag: "wx" });
|
|
21352
|
+
} catch (error2) {
|
|
21353
|
+
if (error2.code !== "EEXIST") throw error2;
|
|
21354
|
+
}
|
|
21355
|
+
return filePath;
|
|
21356
|
+
} catch (error2) {
|
|
21357
|
+
logError("Could not save rendered image to disk", error2);
|
|
21358
|
+
return void 0;
|
|
21359
|
+
}
|
|
21360
|
+
}
|
|
21361
|
+
var URL_BYTE_BUDGET = 8e3;
|
|
21362
|
+
function pathVertices(path) {
|
|
21363
|
+
const vertices = path.points?.length ? path.points : (path.geojson_coordinates ?? []).map(([longitude, latitude]) => ({ latitude, longitude }));
|
|
21364
|
+
if (path.fill_color && vertices.length >= 3) {
|
|
21365
|
+
const first = vertices[0];
|
|
21366
|
+
const last = vertices[vertices.length - 1];
|
|
21367
|
+
if (first.latitude !== last.latitude || first.longitude !== last.longitude) {
|
|
21368
|
+
return [...vertices, first];
|
|
21369
|
+
}
|
|
21370
|
+
}
|
|
21371
|
+
return vertices;
|
|
21372
|
+
}
|
|
21373
|
+
function pathParams(paths, maxEncodedChars = Number.POSITIVE_INFINITY) {
|
|
21374
|
+
let simplified = false;
|
|
21375
|
+
const values = paths.map((path) => {
|
|
21376
|
+
const style = [
|
|
21377
|
+
`stroke:${path.stroke_color ?? "blue"}`,
|
|
21378
|
+
`width:${path.stroke_width ?? 3}`,
|
|
21379
|
+
`fill:${path.fill_color ?? "none"}`
|
|
21380
|
+
];
|
|
21381
|
+
const fitted = fitPolylineToBudget(pathVertices(path), maxEncodedChars);
|
|
21382
|
+
simplified ||= fitted.simplified;
|
|
21383
|
+
return `${style.join("|")}|enc:${fitted.encoded}`;
|
|
21384
|
+
});
|
|
21385
|
+
return { values, simplified };
|
|
21386
|
+
}
|
|
21387
|
+
|
|
21388
|
+
// src/tools/maps/static-map-image.ts
|
|
21112
21389
|
var Schema11 = strictObject({
|
|
21390
|
+
center: CoordinateSchema.describe("Center of the map view"),
|
|
21391
|
+
zoom: number2().min(0).max(22).describe("Zoom level (0 = world, ~10 = city, ~15 = streets; fractional values allowed)"),
|
|
21392
|
+
markers: array(MarkerSchema).optional().describe("Markers to draw, each with optional color or custom icon"),
|
|
21393
|
+
...PathsShape,
|
|
21394
|
+
...StaticImageShape
|
|
21395
|
+
});
|
|
21396
|
+
var staticMapImage = {
|
|
21397
|
+
name: "static_map_image",
|
|
21398
|
+
title: "Static Map Image",
|
|
21399
|
+
description: 'Render a static map image centered on a location, with optional markers and line/polygon overlays; returns the image inline and, if NBAI_IMAGE_DIR is set, also saves it as a file (path in the result text). For a map auto-fitted to a route or to overlays, use static_route_map. Parameters: center {latitude, longitude} and zoom (0-22) (required); optional markers (array of {latitude, longitude, color, icon_url, anchor, scale}), paths (array of {points [{latitude, longitude}] OR geojson_coordinates [[longitude, latitude], ...], stroke_color, stroke_width, fill_color for a filled polygon}), width, height (default 512), style (streets | light | dark | hybrid), format (png | jpg | webp), retina. Example: {"center": {"latitude": 48.8566, "longitude": 2.3522}, "zoom": 14, "markers": [{"latitude": 48.8584, "longitude": 2.2945, "color": "red"}], "paths": [{"points": [{"latitude": 48.85, "longitude": 2.29}, {"latitude": 48.86, "longitude": 2.30}], "stroke_color": "green"}]}',
|
|
21400
|
+
inputSchema: Schema11,
|
|
21401
|
+
annotations: READ_ONLY,
|
|
21402
|
+
async run(args, nb) {
|
|
21403
|
+
const position = `${args.center.latitude},${args.center.longitude},${args.zoom}`;
|
|
21404
|
+
const path = staticImagePath(position, args);
|
|
21405
|
+
const markers = args.markers?.length ? markerParam(args.markers, "lng-first") : void 0;
|
|
21406
|
+
let overlay = args.paths?.length ? pathParams(args.paths) : void 0;
|
|
21407
|
+
for (const budget of [4e3, 2e3, 1e3, 500, 200]) {
|
|
21408
|
+
if (!overlay || nb.buildUrl(path, { markers, path: overlay.values }).length <= URL_BYTE_BUDGET)
|
|
21409
|
+
break;
|
|
21410
|
+
overlay = pathParams(args.paths, budget);
|
|
21411
|
+
}
|
|
21412
|
+
const parts = [
|
|
21413
|
+
`Map centered at (${args.center.latitude}, ${args.center.longitude}), zoom ${args.zoom}`
|
|
21414
|
+
];
|
|
21415
|
+
if (args.markers?.length) parts.push(`${args.markers.length} marker(s)`);
|
|
21416
|
+
if (args.paths?.length)
|
|
21417
|
+
parts.push(
|
|
21418
|
+
`${args.paths.length} overlay path(s)${overlay?.simplified ? " (simplified to fit the URL limit)" : ""}`
|
|
21419
|
+
);
|
|
21420
|
+
return fetchImageResult(
|
|
21421
|
+
nb,
|
|
21422
|
+
path,
|
|
21423
|
+
{ markers, path: overlay?.values },
|
|
21424
|
+
`${parts.join(", ")}.`,
|
|
21425
|
+
args,
|
|
21426
|
+
"map"
|
|
21427
|
+
);
|
|
21428
|
+
}
|
|
21429
|
+
};
|
|
21430
|
+
|
|
21431
|
+
// src/tools/maps/static-route-map.ts
|
|
21432
|
+
var Schema12 = strictObject({
|
|
21113
21433
|
encoded_polyline: string2().optional().describe(
|
|
21114
21434
|
"Route geometry as a Google encoded polyline (precision 5) \u2014 exactly what the directions tool returns with its default geometry setting"
|
|
21115
21435
|
),
|
|
21116
21436
|
route_points: array(CoordinateSchema).min(2).optional().describe("Alternative to encoded_polyline: the route as an ordered list of coordinates"),
|
|
21117
|
-
markers: array(
|
|
21437
|
+
markers: array(MarkerSchema).optional().describe("Extra markers, e.g. origin and destination, with optional color or custom icon"),
|
|
21438
|
+
...PathsShape,
|
|
21118
21439
|
stroke_color: string2().optional().describe('Route line color (e.g. "blue", "#ff0000"; default blue)'),
|
|
21119
21440
|
stroke_width: number2().int().min(1).max(20).optional().describe("Route line width in pixels (default 4)"),
|
|
21120
21441
|
padding: number2().min(0).max(1).optional().describe("Margin around the route as a fraction of image size (default 0.1)"),
|
|
21121
21442
|
...StaticImageShape
|
|
21122
21443
|
});
|
|
21123
|
-
var URL_BYTE_BUDGET = 8e3;
|
|
21124
21444
|
var ENCODED_CHAR_BUDGETS = [4e3, 3e3, 2e3, 1200, 600, 200];
|
|
21125
21445
|
var staticRouteMap = {
|
|
21126
21446
|
name: "static_route_map",
|
|
21127
21447
|
title: "Static Route Map",
|
|
21128
|
-
description: 'Render a static map
|
|
21129
|
-
inputSchema:
|
|
21448
|
+
description: 'Render a static map auto-fitted to a route and/or overlays: a route from directions, and/or lines and filled polygons such as isochrone contours; very long geometry is simplified automatically to fit the map API URL limit (distances unaffected). Returns the image inline and, if NBAI_IMAGE_DIR is set, also saves it as a file (path in the result text). Parameters: at least one of encoded_polyline (the geometry string from directions, preferred), route_points (array of {latitude, longitude}) or paths (array of {points OR geojson_coordinates [[longitude, latitude], ...], stroke_color, stroke_width, fill_color}); optional markers (array of {latitude, longitude, color, icon_url, anchor, scale}), stroke_color, stroke_width (route line), padding, width, height, style, format, retina. Example (isochrone contours) - Example: {"paths": [{"geojson_coordinates": [[-122.42, 37.77], [-122.40, 37.78], [-122.41, 37.79], [-122.42, 37.77]], "fill_color": "rgba(29,78,216,0.35)", "stroke_color": "#1d4ed8"}], "markers": [{"latitude": 37.7749, "longitude": -122.4194, "color": "red"}]}',
|
|
21449
|
+
inputSchema: Schema12,
|
|
21130
21450
|
annotations: READ_ONLY,
|
|
21131
21451
|
async run(args, nb) {
|
|
21132
|
-
if (!args.encoded_polyline && !args.route_points) {
|
|
21133
|
-
throw new ToolInputError("Provide `encoded_polyline` (preferred) or `route_points`.");
|
|
21134
|
-
}
|
|
21135
21452
|
if (args.encoded_polyline && args.route_points) {
|
|
21136
21453
|
throw new ToolInputError("Provide either `encoded_polyline` or `route_points`, not both.");
|
|
21137
21454
|
}
|
|
21138
|
-
|
|
21139
|
-
|
|
21140
|
-
|
|
21141
|
-
|
|
21142
|
-
|
|
21455
|
+
if (!args.encoded_polyline && !args.route_points && !args.paths?.length) {
|
|
21456
|
+
throw new ToolInputError(
|
|
21457
|
+
"Provide a route (`encoded_polyline` or `route_points`) and/or overlay `paths`."
|
|
21458
|
+
);
|
|
21459
|
+
}
|
|
21143
21460
|
const markers = args.markers?.length ? markerParam(args.markers, "lat-first") : void 0;
|
|
21144
21461
|
const padding = args.padding !== void 0 ? String(args.padding) : void 0;
|
|
21145
21462
|
const path = staticImagePath("auto", args);
|
|
21146
|
-
const
|
|
21147
|
-
|
|
21148
|
-
|
|
21149
|
-
|
|
21150
|
-
|
|
21151
|
-
|
|
21152
|
-
|
|
21153
|
-
|
|
21154
|
-
|
|
21155
|
-
|
|
21156
|
-
const fitted = fitPolylineToBudget(
|
|
21157
|
-
|
|
21158
|
-
|
|
21159
|
-
|
|
21160
|
-
|
|
21161
|
-
|
|
21162
|
-
|
|
21463
|
+
const routeStyle = [
|
|
21464
|
+
`stroke:${args.stroke_color ?? "blue"}`,
|
|
21465
|
+
`width:${args.stroke_width ?? 4}`,
|
|
21466
|
+
"fill:none"
|
|
21467
|
+
].join("|");
|
|
21468
|
+
const routePoints = args.encoded_polyline ? decodePolyline(args.encoded_polyline) : args.route_points ?? [];
|
|
21469
|
+
const build2 = (budget) => {
|
|
21470
|
+
const values = [];
|
|
21471
|
+
let simplified = false;
|
|
21472
|
+
if (routePoints.length) {
|
|
21473
|
+
const fitted = fitPolylineToBudget(routePoints, budget);
|
|
21474
|
+
simplified ||= fitted.simplified;
|
|
21475
|
+
values.push(`${routeStyle}|enc:${fitted.encoded}`);
|
|
21476
|
+
}
|
|
21477
|
+
if (args.paths?.length) {
|
|
21478
|
+
const overlay = pathParams(args.paths, budget);
|
|
21479
|
+
simplified ||= overlay.simplified;
|
|
21480
|
+
values.push(...overlay.values);
|
|
21481
|
+
}
|
|
21482
|
+
return { values, simplified };
|
|
21483
|
+
};
|
|
21484
|
+
let built = build2(Number.POSITIVE_INFINITY);
|
|
21485
|
+
for (const budget of ENCODED_CHAR_BUDGETS) {
|
|
21486
|
+
if (nb.buildUrl(path, { path: built.values, markers, padding }).length <= URL_BYTE_BUDGET)
|
|
21487
|
+
break;
|
|
21488
|
+
built = build2(budget);
|
|
21163
21489
|
}
|
|
21490
|
+
const parts = ["Map rendered"];
|
|
21491
|
+
if (routePoints.length) parts.push("with the route");
|
|
21492
|
+
if (args.paths?.length) parts.push(`${args.paths.length} overlay path(s)`);
|
|
21493
|
+
if (args.markers?.length) parts.push(`${args.markers.length} marker(s)`);
|
|
21494
|
+
const note = built.simplified ? " Display geometry was simplified to fit the map URL limit; distances are unaffected." : "";
|
|
21164
21495
|
return fetchImageResult(
|
|
21165
21496
|
nb,
|
|
21166
21497
|
path,
|
|
21167
|
-
|
|
21168
|
-
|
|
21498
|
+
{ path: built.values, markers, padding },
|
|
21499
|
+
`${parts.join(", ")}.${note}`,
|
|
21169
21500
|
args,
|
|
21170
21501
|
"route-map"
|
|
21171
21502
|
);
|
|
@@ -21183,6 +21514,7 @@ var ALL_TOOLS = [
|
|
|
21183
21514
|
geocodeReverse,
|
|
21184
21515
|
geocodeStructured,
|
|
21185
21516
|
isochrone,
|
|
21517
|
+
placeBrowse,
|
|
21186
21518
|
placeLookup,
|
|
21187
21519
|
placeSearch,
|
|
21188
21520
|
postcodeLookup,
|
|
@@ -21353,7 +21685,7 @@ function extractApiMessage(bodyText) {
|
|
|
21353
21685
|
}
|
|
21354
21686
|
|
|
21355
21687
|
// src/index.ts
|
|
21356
|
-
var pkg = true ? { version: "0.1
|
|
21688
|
+
var pkg = true ? { version: "0.2.1" } : createRequire(import.meta.url)("../package.json");
|
|
21357
21689
|
function main() {
|
|
21358
21690
|
const args = process.argv.slice(2);
|
|
21359
21691
|
if (args.includes("--version") || args.includes("-v")) {
|
|
@@ -21370,7 +21702,8 @@ Environment variables:
|
|
|
21370
21702
|
NBAI_API_KEY (required) NextBillion.ai API key
|
|
21371
21703
|
NBAI_BASE_URL (optional) API base URL, default https://api.nextbillion.io
|
|
21372
21704
|
NBAI_TIMEOUT_MS (optional) per-request timeout, default 30000
|
|
21373
|
-
NBAI_IMAGE_DIR (optional)
|
|
21705
|
+
NBAI_IMAGE_DIR (optional) also save rendered maps to this absolute directory, or
|
|
21706
|
+
"tmp" for the OS temp dir; unset = nothing is written to disk
|
|
21374
21707
|
`
|
|
21375
21708
|
);
|
|
21376
21709
|
return;
|
|
@@ -21391,6 +21724,9 @@ Environment variables:
|
|
|
21391
21724
|
baseUrl: config2.baseUrl,
|
|
21392
21725
|
timeoutMs: config2.timeoutMs
|
|
21393
21726
|
});
|
|
21727
|
+
const imageDir = resolveImageDir();
|
|
21728
|
+
if (imageDir.warning) logError(imageDir.warning);
|
|
21729
|
+
else if (imageDir.dir) logInfo(`rendered map images will also be saved to ${imageDir.dir}`);
|
|
21394
21730
|
const handle = serveStdio(() => buildServer(nb, pkg.version));
|
|
21395
21731
|
logInfo(`${SERVER_NAME} ${pkg.version} listening on stdio`);
|
|
21396
21732
|
const shutdown = () => {
|
package/package.json
CHANGED