nextbillion-mcp 0.1.11 → 0.2.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.
Files changed (2) hide show
  1. package/dist/index.js +447 -131
  2. 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);
@@ -20432,6 +20558,34 @@ function summarizePlaces(response, noun = "result") {
20432
20558
  return `${items.length} ${noun}${items.length === 1 ? "" : "s"} found:
20433
20559
  ${lines.join("\n")}${more}`;
20434
20560
  }
20561
+ var HazmatSchema = array(
20562
+ _enum([
20563
+ "explosives",
20564
+ "gas",
20565
+ "flammable_liquid",
20566
+ "flammable_gas",
20567
+ "organic",
20568
+ "toxic",
20569
+ "radioactive",
20570
+ "corrosive",
20571
+ "other"
20572
+ ])
20573
+ ).min(1).describe("Hazardous cargo classes carried (truck mode, flexible service only)");
20574
+ var EmissionClassSchema = _enum(["euro0", "euro1", "euro2", "euro3", "euro4", "euro5", "euro6", "euro7", "euro8", "euro9"]).describe("Engine emission class (EU regions, truck mode, flexible service only)");
20575
+ var ExcludeSchema = array(
20576
+ _enum([
20577
+ "toll",
20578
+ "ferry",
20579
+ "highway",
20580
+ "service_road",
20581
+ "uturn",
20582
+ "sharp_turn",
20583
+ "left_turn",
20584
+ "right_turn"
20585
+ ])
20586
+ ).min(1).describe(
20587
+ "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"
20588
+ );
20435
20589
 
20436
20590
  // src/tools/places/text-search.ts
20437
20591
  var TextSearchSchema = strictObject({
@@ -20617,8 +20771,30 @@ var geocodeStructured = {
20617
20771
  }
20618
20772
  };
20619
20773
 
20620
- // src/tools/places/place-lookup.ts
20774
+ // src/tools/places/place-browse.ts
20621
20775
  var Schema4 = strictObject({
20776
+ categories: array(string2().min(1)).min(1).describe(
20777
+ 'Category names or ids to browse; a place matching any of them is returned (e.g. ["restaurant"], ["schools"], ["7376"])'
20778
+ ),
20779
+ ...PlacesFilterShape
20780
+ });
20781
+ var placeBrowse = {
20782
+ name: "place_browse",
20783
+ title: "Browse Places by Category",
20784
+ 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}',
20785
+ inputSchema: Schema4,
20786
+ annotations: READ_ONLY,
20787
+ async run(args, nb) {
20788
+ const response = await nb.getJson("/browse", {
20789
+ categories: args.categories.join(","),
20790
+ ...placesFilterQuery(args)
20791
+ });
20792
+ return textResult(summarizePlaces(response, "place"), response);
20793
+ }
20794
+ };
20795
+
20796
+ // src/tools/places/place-lookup.ts
20797
+ var Schema5 = strictObject({
20622
20798
  id: string2().min(1).describe("Unique place id, as returned by the other place/geocoding tools"),
20623
20799
  view: ViewSchema.optional()
20624
20800
  });
@@ -20626,7 +20802,7 @@ var placeLookup = {
20626
20802
  name: "place_lookup",
20627
20803
  title: "Place Lookup",
20628
20804
  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: Schema4,
20805
+ inputSchema: Schema5,
20630
20806
  annotations: READ_ONLY,
20631
20807
  async run(args, nb) {
20632
20808
  const response = await nb.getJson("/lookup", {
@@ -20638,7 +20814,7 @@ var placeLookup = {
20638
20814
  };
20639
20815
 
20640
20816
  // src/tools/places/postcode-lookup.ts
20641
- var Schema5 = strictObject({
20817
+ var Schema6 = strictObject({
20642
20818
  postal_code: string2().optional().describe("Postal/ZIP code to look up (requires `country`; do not combine with `coordinate`)"),
20643
20819
  country: string2().optional().describe(
20644
20820
  "Country of the postal code \u2014 name, alpha-2, or alpha-3 ISO code. Required with `postal_code`"
@@ -20654,7 +20830,7 @@ var postcodeLookup = {
20654
20830
  name: "postcode_lookup",
20655
20831
  title: "Postcode Lookup",
20656
20832
  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: Schema5,
20833
+ inputSchema: Schema6,
20658
20834
  annotations: READ_ONLY,
20659
20835
  async run(args, nb) {
20660
20836
  if (args.postal_code && args.coordinate) {
@@ -20697,7 +20873,7 @@ var AVOID_VALUES = [
20697
20873
  "tunnel",
20698
20874
  "none"
20699
20875
  ];
20700
- var Schema6 = strictObject({
20876
+ var Schema7 = strictObject({
20701
20877
  origin: CoordinateSchema.describe("Route start (must be a routable land location)"),
20702
20878
  destination: CoordinateSchema.describe("Route end (must be a routable land location)"),
20703
20879
  waypoints: array(CoordinateSchema).max(200).optional().describe("Intermediate stops visited in order (max 200)"),
@@ -20712,6 +20888,26 @@ var Schema6 = strictObject({
20712
20888
  avoid: array(_enum(AVOID_VALUES)).optional().describe(
20713
20889
  "Road features to avoid when alternatives exist. Fast service supports only toll/ferry/highway"
20714
20890
  ),
20891
+ exclude: ExcludeSchema.optional(),
20892
+ road_info: array(
20893
+ _enum([
20894
+ "max_speed",
20895
+ "toll_distance",
20896
+ "toll_cost",
20897
+ "toll_info",
20898
+ "truck_route",
20899
+ "stop_sign",
20900
+ "traffic_light"
20901
+ ])
20902
+ ).min(1).optional().describe(
20903
+ "Extra per-segment information to return: max_speed, toll_distance, toll_cost, toll_info, truck_route, stop_sign, traffic_light (flexible service only)"
20904
+ ),
20905
+ hazmat_type: HazmatSchema.optional(),
20906
+ truck_axle_load: number2().positive().optional().describe("Load per axle in tonnes (truck mode, flexible service only)"),
20907
+ emission_class: EmissionClassSchema.optional(),
20908
+ cross_border: boolean2().optional().describe(
20909
+ "Allow the route to cross international borders (region-dependent, flexible service only)"
20910
+ ),
20715
20911
  honor_restrictions: boolean2().optional().describe(
20716
20912
  "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
20913
  ),
@@ -20726,8 +20922,8 @@ var Schema6 = strictObject({
20726
20922
  var directions = {
20727
20923
  name: "directions",
20728
20924
  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 of {latitude, longitude}, max 200), mode (car | truck | motorcycle | bike | walk), service ("flexible" default: all modes, route_type, departure_time, full avoid list and truck options; "fast": car/truck only, lower latency), route_type, departure_time (UNIX seconds), avoid (array), honor_restrictions, alternatives, steps (fast only), geometry (polyline default | polyline6), truck_size_cm {height, width, length}, truck_weight_kg. Example: {"origin": {"latitude": 37.7749, "longitude": -122.4194}, "destination": {"latitude": 34.0522, "longitude": -118.2437}, "mode": "car"}',
20730
- inputSchema: Schema6,
20925
+ 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"]}',
20926
+ inputSchema: Schema7,
20731
20927
  annotations: READ_ONLY,
20732
20928
  async run(args, nb) {
20733
20929
  const service = args.service ?? "flexible";
@@ -20745,6 +20941,12 @@ var directions = {
20745
20941
  route_type: args.route_type,
20746
20942
  departure_time: args.departure_time,
20747
20943
  avoid: args.avoid?.length ? args.avoid.join("|") : void 0,
20944
+ exclude: args.exclude?.length ? args.exclude.join("|") : void 0,
20945
+ road_info: args.road_info?.length ? args.road_info.join("|") : void 0,
20946
+ hazmat_type: args.hazmat_type?.length ? args.hazmat_type.join("|") : void 0,
20947
+ truck_axle_load: args.truck_axle_load,
20948
+ emission_class: args.emission_class,
20949
+ cross_border: args.cross_border,
20748
20950
  honor_restrictions: args.honor_restrictions,
20749
20951
  alternatives: args.alternatives,
20750
20952
  altcount: args.alternatives ? 3 : void 0,
@@ -20769,7 +20971,7 @@ Geometry (encoded polyline) is in structured content.` : "No route found.",
20769
20971
  };
20770
20972
 
20771
20973
  // src/tools/routing/distance-matrix.ts
20772
- var Schema7 = strictObject({
20974
+ var Schema8 = strictObject({
20773
20975
  origins: array(CoordinateSchema).min(1).max(1e3).describe("Start points (matrix rows)"),
20774
20976
  destinations: array(CoordinateSchema).min(1).max(1e3).describe("End points (matrix columns)"),
20775
20977
  mode: _enum(["car", "truck", "motorcycle", "bike", "walk"]).optional().describe("Travel mode (default car). Modes other than car/truck require service=flexible"),
@@ -20781,6 +20983,16 @@ var Schema7 = strictObject({
20781
20983
  "Departure as a UNIX timestamp in seconds, for typical-traffic results (flexible only)"
20782
20984
  ),
20783
20985
  avoid: array(_enum(["toll", "highway", "ferry", "none"])).optional().describe("Road features to avoid"),
20986
+ exclude: ExcludeSchema.optional(),
20987
+ hazmat_type: HazmatSchema.optional(),
20988
+ truck_axle_load: number2().positive().optional().describe("Load per axle in tonnes (truck mode, flexible service only)"),
20989
+ emission_class: EmissionClassSchema.optional(),
20990
+ cross_border: boolean2().optional().describe(
20991
+ "Allow routes to cross international borders (region-dependent, flexible service only)"
20992
+ ),
20993
+ route_failed_prompt: boolean2().optional().describe(
20994
+ "When true, unroutable origin/destination pairs return -1 instead of 0 so they can be told apart from zero-distance pairs"
20995
+ ),
20784
20996
  honor_restrictions: boolean2().optional().describe(
20785
20997
  "When true, enforce restricted-area rules for origin/destination points instead of routing through with a warning (flexible service only)"
20786
20998
  ),
@@ -20790,8 +21002,8 @@ var Schema7 = strictObject({
20790
21002
  var distanceMatrix = {
20791
21003
  name: "distance_matrix",
20792
21004
  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. Always prefer this over repeated directions calls for multiple pairs. Parameters: origins, destinations (arrays of {latitude, longitude}, required); optional mode, service ("fast" default: up to 1000x1000 points; "flexible": departure_time, route_type, truck options and more modes, max 50x50), route_type, departure_time, avoid, honor_restrictions, truck_size_cm {height, width, length}, truck_weight_kg. Example: {"origins": [{"latitude": 1.29, "longitude": 103.85}], "destinations": [{"latitude": 1.35, "longitude": 103.99}, {"latitude": 1.3, "longitude": 103.77}]}',
20794
- inputSchema: Schema7,
21005
+ 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. Always prefer this over repeated directions calls for multiple pairs. 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}',
21006
+ inputSchema: Schema8,
20795
21007
  annotations: READ_ONLY,
20796
21008
  async run(args, nb) {
20797
21009
  const service = args.service ?? "fast";
@@ -20813,6 +21025,12 @@ var distanceMatrix = {
20813
21025
  route_type: args.route_type,
20814
21026
  departure_time: args.departure_time,
20815
21027
  avoid: args.avoid?.length ? args.avoid.join("|") : void 0,
21028
+ exclude: args.exclude?.length ? args.exclude.join("|") : void 0,
21029
+ hazmat_type: args.hazmat_type?.length ? args.hazmat_type.join("|") : void 0,
21030
+ truck_axle_load: args.truck_axle_load,
21031
+ emission_class: args.emission_class,
21032
+ cross_border: args.cross_border,
21033
+ route_failed_prompt: args.route_failed_prompt,
20816
21034
  honor_restrictions: args.honor_restrictions,
20817
21035
  truck_size: args.truck_size_cm ? `${args.truck_size_cm.height},${args.truck_size_cm.width},${args.truck_size_cm.length}` : void 0,
20818
21036
  truck_weight: args.truck_weight_kg
@@ -20827,7 +21045,7 @@ var distanceMatrix = {
20827
21045
  };
20828
21046
 
20829
21047
  // src/tools/routing/isochrone.ts
20830
- var Schema8 = strictObject({
21048
+ var Schema9 = strictObject({
20831
21049
  origin: CoordinateSchema.describe("Starting point of the reachability analysis"),
20832
21050
  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
21051
  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 +21054,15 @@ var Schema8 = strictObject({
20836
21054
  denoise: number2().min(0).max(1).optional().describe(
20837
21055
  "Remove contours smaller than this fraction of the largest (default 1: largest only)"
20838
21056
  ),
20839
- departure_time: number2().int().optional().describe("Departure as a UNIX timestamp in seconds, for typical-traffic analysis")
21057
+ departure_time: number2().int().optional().describe("Departure as a UNIX timestamp in seconds, for typical-traffic analysis"),
21058
+ 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"]'),
21059
+ generalize: number2().positive().optional().describe("Simplification tolerance in meters (Douglas-Peucker); omit for automatic")
20840
21060
  });
20841
21061
  var isochrone = {
20842
21062
  name: "isochrone",
20843
21063
  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 in GeoJSON [longitude, latitude] order). 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 instead of LineString), denoise, departure_time. Example: {"origin": {"latitude": 37.7749, "longitude": -122.4194}, "contours_minutes": [5, 10], "polygons": true}',
20845
- inputSchema: Schema8,
21064
+ 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}',
21065
+ inputSchema: Schema9,
20846
21066
  annotations: READ_ONLY,
20847
21067
  async run(args, nb) {
20848
21068
  if (!args.contours_minutes?.length && !args.contours_meters?.length) {
@@ -20858,7 +21078,9 @@ var isochrone = {
20858
21078
  mode: args.mode,
20859
21079
  polygons: args.polygons,
20860
21080
  denoise: args.denoise,
20861
- departure_time: args.departure_time
21081
+ departure_time: args.departure_time,
21082
+ contours_colors: args.contours_colors?.join(","),
21083
+ generalize: args.generalize
20862
21084
  });
20863
21085
  const features = response.features ?? [];
20864
21086
  const metric = args.contours_minutes?.length ? "minutes" : "meters";
@@ -20871,18 +21093,19 @@ var isochrone = {
20871
21093
  };
20872
21094
 
20873
21095
  // src/tools/routing/search-along-route.ts
20874
- var Schema9 = strictObject({
21096
+ var Schema10 = strictObject({
20875
21097
  route_points: array(CoordinateSchema).min(2).describe("Waypoints defining the route to search along, in travel order"),
20876
21098
  query: string2().min(1).describe('What to find along the route, e.g. "gas station", "coffee"'),
20877
21099
  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
21100
  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)")
21101
+ limit: number2().int().min(1).max(20).optional().describe("Maximum results (default 10, max 20)"),
21102
+ 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
21103
  });
20881
21104
  var searchAlongRoute = {
20882
21105
  name: "search_along_route",
20883
21106
  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: Schema9,
21107
+ 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}',
21108
+ inputSchema: Schema10,
20886
21109
  annotations: READ_ONLY,
20887
21110
  async run(args, nb) {
20888
21111
  const response = await nb.postJson(
@@ -20893,7 +21116,8 @@ var searchAlongRoute = {
20893
21116
  q: args.query,
20894
21117
  max_detour_time: args.max_detour_seconds,
20895
21118
  sort_by: args.sort_by,
20896
- limit: args.limit
21119
+ limit: args.limit,
21120
+ view: args.view
20897
21121
  }
20898
21122
  );
20899
21123
  const items = response.items ?? [];
@@ -20926,84 +21150,6 @@ function logInfo(message) {
20926
21150
  console.error(`[nextbillion-mcp] ${message}`);
20927
21151
  }
20928
21152
 
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
21153
  // src/tools/maps/polyline.ts
21008
21154
  var PRECISION = 1e5;
21009
21155
  function decodePolyline(encoded) {
@@ -21108,64 +21254,233 @@ function fitPolylineToBudget(points, maxEncodedChars) {
21108
21254
  };
21109
21255
  }
21110
21256
 
21111
- // src/tools/maps/static-route-map.ts
21257
+ // src/tools/maps/static-shared.ts
21258
+ var StaticImageShape = {
21259
+ width: number2().int().min(16).max(2048).optional().describe("Image width in pixels (default 512)"),
21260
+ height: number2().int().min(16).max(2048).optional().describe("Image height in pixels (default 512)"),
21261
+ style: string2().optional().describe('Map style id: "streets", "light", "dark", or "hybrid" (default streets)'),
21262
+ format: _enum(["png", "jpg", "webp"]).optional().describe("Image format (default png)"),
21263
+ retina: boolean2().optional().describe("Render at @2x resolution for high-DPI displays")
21264
+ };
21265
+ var MarkerSchema = strictObject({
21266
+ latitude: number2().min(-90).max(90),
21267
+ longitude: number2().min(-180).max(180),
21268
+ color: string2().optional().describe('Marker color, e.g. "red" or "#0000ff" (ignored when icon_url is set)'),
21269
+ icon_url: string2().url().optional().describe("URL of a custom marker image (max 64 kB / 4096 px, e.g. 64x64 PNG)"),
21270
+ anchor: _enum([
21271
+ "top",
21272
+ "left",
21273
+ "bottom",
21274
+ "right",
21275
+ "center",
21276
+ "topleft",
21277
+ "bottomleft",
21278
+ "topright",
21279
+ "bottomright"
21280
+ ]).optional().describe("Anchor point of a custom icon (default bottom)"),
21281
+ scale: number2().positive().optional().describe("Custom icon scale factor (default 1)")
21282
+ });
21283
+ var PathSchema = strictObject({
21284
+ points: array(CoordinateSchema).min(2).optional().describe("Vertices as {latitude, longitude} objects"),
21285
+ geojson_coordinates: array(tuple([number2(), number2()])).min(2).optional().describe(
21286
+ "Vertices as GeoJSON [longitude, latitude] pairs - pass an isochrone or GeoJSON ring here unchanged"
21287
+ ),
21288
+ stroke_color: string2().optional().describe("Line color (default blue)"),
21289
+ stroke_width: number2().int().min(1).max(20).optional().describe("Line width in px (default 3)"),
21290
+ fill_color: string2().optional().describe(
21291
+ 'Fill color for a closed shape, e.g. "rgba(255,0,0,0.3)" or "#ff000055"; omit for a line'
21292
+ )
21293
+ }).describe("A line or filled polygon overlay");
21294
+ var PathsShape = {
21295
+ paths: array(PathSchema).max(10).optional().describe("Extra lines/polygons to draw (e.g. isochrone contours as filled polygons)")
21296
+ };
21297
+ var MIME_TYPES = {
21298
+ png: "image/png",
21299
+ jpg: "image/jpeg",
21300
+ webp: "image/webp"
21301
+ };
21302
+ function staticImagePath(positionSegment, args) {
21303
+ const style = args.style ?? "streets";
21304
+ const size = `${args.width ?? 512}x${args.height ?? 512}${args.retina ? "@2x" : ""}`;
21305
+ const format = args.format ?? "png";
21306
+ return `/maps/${encodeURIComponent(style)}/static/${positionSegment}/${size}.${format}`;
21307
+ }
21308
+ function markerParam(markers, order) {
21309
+ return markers.map((m) => {
21310
+ const pair = order === "lng-first" ? `${m.longitude},${m.latitude}` : `${m.latitude},${m.longitude}`;
21311
+ const commands = [];
21312
+ if (m.icon_url) commands.push(`icon:${m.icon_url}`);
21313
+ if (m.anchor) commands.push(`anchor:${m.anchor}`);
21314
+ if (m.scale !== void 0) commands.push(`scale:${m.scale}`);
21315
+ const color = m.color && !m.icon_url ? `,${m.color}` : "";
21316
+ return `${commands.length ? commands.join("|") + "|" : ""}${pair}${color}`;
21317
+ }).join("|");
21318
+ }
21319
+ async function fetchImageResult(nb, path, query, caption, args, filePrefix = "map") {
21320
+ const image = await nb.getBinary(path, query);
21321
+ const mimeType = image.contentType.startsWith("image/") ? image.contentType : MIME_TYPES[args.format ?? "png"];
21322
+ const savedPath = await saveImage(image.data, filePrefix, args.format ?? "png");
21323
+ const location = savedPath ? ` Saved to ${savedPath} (for clients that cannot display images inline).` : "";
21324
+ return {
21325
+ content: [
21326
+ { type: "image", data: Buffer.from(image.data).toString("base64"), mimeType },
21327
+ { type: "text", text: caption + location }
21328
+ ]
21329
+ };
21330
+ }
21331
+ async function saveImage(data, prefix, ext) {
21332
+ try {
21333
+ const dir = imageOutputDir();
21334
+ await mkdir(dir, { recursive: true });
21335
+ const hash = createHash("sha1").update(data).digest("hex").slice(0, 8);
21336
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\..+/, "");
21337
+ const filePath = join2(dir, `${prefix}-${stamp}-${hash}.${ext === "jpg" ? "jpg" : ext}`);
21338
+ await writeFile(filePath, data);
21339
+ return filePath;
21340
+ } catch (error2) {
21341
+ logError("Could not save rendered image to disk", error2);
21342
+ return void 0;
21343
+ }
21344
+ }
21345
+ var URL_BYTE_BUDGET = 8e3;
21346
+ function pathVertices(path) {
21347
+ const vertices = path.points?.length ? path.points : (path.geojson_coordinates ?? []).map(([longitude, latitude]) => ({ latitude, longitude }));
21348
+ if (path.fill_color && vertices.length >= 3) {
21349
+ const first = vertices[0];
21350
+ const last = vertices[vertices.length - 1];
21351
+ if (first.latitude !== last.latitude || first.longitude !== last.longitude) {
21352
+ return [...vertices, first];
21353
+ }
21354
+ }
21355
+ return vertices;
21356
+ }
21357
+ function pathParams(paths, maxEncodedChars = Number.POSITIVE_INFINITY) {
21358
+ let simplified = false;
21359
+ const values = paths.map((path) => {
21360
+ const style = [
21361
+ `stroke:${path.stroke_color ?? "blue"}`,
21362
+ `width:${path.stroke_width ?? 3}`,
21363
+ `fill:${path.fill_color ?? "none"}`
21364
+ ];
21365
+ const fitted = fitPolylineToBudget(pathVertices(path), maxEncodedChars);
21366
+ simplified ||= fitted.simplified;
21367
+ return `${style.join("|")}|enc:${fitted.encoded}`;
21368
+ });
21369
+ return { values, simplified };
21370
+ }
21371
+
21372
+ // src/tools/maps/static-map-image.ts
21112
21373
  var Schema11 = strictObject({
21374
+ center: CoordinateSchema.describe("Center of the map view"),
21375
+ zoom: number2().min(0).max(22).describe("Zoom level (0 = world, ~10 = city, ~15 = streets; fractional values allowed)"),
21376
+ markers: array(MarkerSchema).optional().describe("Markers to draw, each with optional color or custom icon"),
21377
+ ...PathsShape,
21378
+ ...StaticImageShape
21379
+ });
21380
+ var staticMapImage = {
21381
+ name: "static_map_image",
21382
+ title: "Static Map Image",
21383
+ description: 'Render a static map image centered on a location, with optional markers and line/polygon overlays; returns the image inline and also saves it to a local file (path in the result text) for clients that cannot display images. 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"}]}',
21384
+ inputSchema: Schema11,
21385
+ annotations: READ_ONLY,
21386
+ async run(args, nb) {
21387
+ const position = `${args.center.latitude},${args.center.longitude},${args.zoom}`;
21388
+ const path = staticImagePath(position, args);
21389
+ const markers = args.markers?.length ? markerParam(args.markers, "lng-first") : void 0;
21390
+ let overlay = args.paths?.length ? pathParams(args.paths) : void 0;
21391
+ for (const budget of [4e3, 2e3, 1e3, 500, 200]) {
21392
+ if (!overlay || nb.buildUrl(path, { markers, path: overlay.values }).length <= URL_BYTE_BUDGET)
21393
+ break;
21394
+ overlay = pathParams(args.paths, budget);
21395
+ }
21396
+ const parts = [
21397
+ `Map centered at (${args.center.latitude}, ${args.center.longitude}), zoom ${args.zoom}`
21398
+ ];
21399
+ if (args.markers?.length) parts.push(`${args.markers.length} marker(s)`);
21400
+ if (args.paths?.length)
21401
+ parts.push(
21402
+ `${args.paths.length} overlay path(s)${overlay?.simplified ? " (simplified to fit the URL limit)" : ""}`
21403
+ );
21404
+ return fetchImageResult(
21405
+ nb,
21406
+ path,
21407
+ { markers, path: overlay?.values },
21408
+ `${parts.join(", ")}.`,
21409
+ args,
21410
+ "map"
21411
+ );
21412
+ }
21413
+ };
21414
+
21415
+ // src/tools/maps/static-route-map.ts
21416
+ var Schema12 = strictObject({
21113
21417
  encoded_polyline: string2().optional().describe(
21114
21418
  "Route geometry as a Google encoded polyline (precision 5) \u2014 exactly what the directions tool returns with its default geometry setting"
21115
21419
  ),
21116
21420
  route_points: array(CoordinateSchema).min(2).optional().describe("Alternative to encoded_polyline: the route as an ordered list of coordinates"),
21117
- markers: array(CoordinateSchema.extend({ color: string2().optional() })).optional().describe("Extra markers, e.g. origin and destination, with optional color"),
21421
+ markers: array(MarkerSchema).optional().describe("Extra markers, e.g. origin and destination, with optional color or custom icon"),
21422
+ ...PathsShape,
21118
21423
  stroke_color: string2().optional().describe('Route line color (e.g. "blue", "#ff0000"; default blue)'),
21119
21424
  stroke_width: number2().int().min(1).max(20).optional().describe("Route line width in pixels (default 4)"),
21120
21425
  padding: number2().min(0).max(1).optional().describe("Margin around the route as a fraction of image size (default 0.1)"),
21121
21426
  ...StaticImageShape
21122
21427
  });
21123
- var URL_BYTE_BUDGET = 8e3;
21124
21428
  var ENCODED_CHAR_BUDGETS = [4e3, 3e3, 2e3, 1200, 600, 200];
21125
21429
  var staticRouteMap = {
21126
21430
  name: "static_route_map",
21127
21431
  title: "Static Route Map",
21128
- description: 'Render a static map with a route drawn on it, auto-fitted to show the whole route; very long routes are simplified automatically to fit the map API URL limit (distances are unaffected). Returns the image inline and also saves it to a local file (path in the result text). Parameters: exactly one of encoded_polyline (the geometry string from directions, preferred) or route_points (array of {latitude, longitude}); optional markers (array of {latitude, longitude, color}, e.g. origin and destination), stroke_color, stroke_width, padding, width, height, style, format, retina. Example: {"encoded_polyline": "<geometry from directions>", "markers": [{"latitude": 37.7749, "longitude": -122.4194, "color": "green"}, {"latitude": 34.0522, "longitude": -118.2437, "color": "red"}]}',
21129
- inputSchema: Schema11,
21432
+ 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 also saves it to a local 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"}]}',
21433
+ inputSchema: Schema12,
21130
21434
  annotations: READ_ONLY,
21131
21435
  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
21436
  if (args.encoded_polyline && args.route_points) {
21136
21437
  throw new ToolInputError("Provide either `encoded_polyline` or `route_points`, not both.");
21137
21438
  }
21138
- const styleSegments = [
21139
- `stroke:${args.stroke_color ?? "blue"}`,
21140
- `width:${args.stroke_width ?? 4}`,
21141
- "fill:none"
21142
- ];
21439
+ if (!args.encoded_polyline && !args.route_points && !args.paths?.length) {
21440
+ throw new ToolInputError(
21441
+ "Provide a route (`encoded_polyline` or `route_points`) and/or overlay `paths`."
21442
+ );
21443
+ }
21143
21444
  const markers = args.markers?.length ? markerParam(args.markers, "lat-first") : void 0;
21144
21445
  const padding = args.padding !== void 0 ? String(args.padding) : void 0;
21145
21446
  const path = staticImagePath("auto", args);
21146
- const buildQuery = (geometry2) => ({
21147
- path: `${styleSegments.join("|")}|${geometry2}`,
21148
- markers,
21149
- padding
21150
- });
21151
- let geometry = args.encoded_polyline ? `enc:${args.encoded_polyline}` : args.route_points.map((p) => `${p.latitude},${p.longitude}`).join("|");
21152
- let simplificationNote = "";
21153
- if (nb.buildUrl(path, buildQuery(geometry)).length > URL_BYTE_BUDGET) {
21154
- const points = args.encoded_polyline ? decodePolyline(args.encoded_polyline) : args.route_points;
21155
- for (const budget of ENCODED_CHAR_BUDGETS) {
21156
- const fitted = fitPolylineToBudget(points, budget);
21157
- geometry = `enc:${fitted.encoded}`;
21158
- if (nb.buildUrl(path, buildQuery(geometry)).length <= URL_BYTE_BUDGET) {
21159
- simplificationNote = ` Display geometry simplified from ${fitted.originalPointCount} to ${fitted.pointCount} points to fit the map URL limit; distances are unaffected.`;
21160
- break;
21161
- }
21162
- }
21447
+ const routeStyle = [
21448
+ `stroke:${args.stroke_color ?? "blue"}`,
21449
+ `width:${args.stroke_width ?? 4}`,
21450
+ "fill:none"
21451
+ ].join("|");
21452
+ const routePoints = args.encoded_polyline ? decodePolyline(args.encoded_polyline) : args.route_points ?? [];
21453
+ const build2 = (budget) => {
21454
+ const values = [];
21455
+ let simplified = false;
21456
+ if (routePoints.length) {
21457
+ const fitted = fitPolylineToBudget(routePoints, budget);
21458
+ simplified ||= fitted.simplified;
21459
+ values.push(`${routeStyle}|enc:${fitted.encoded}`);
21460
+ }
21461
+ if (args.paths?.length) {
21462
+ const overlay = pathParams(args.paths, budget);
21463
+ simplified ||= overlay.simplified;
21464
+ values.push(...overlay.values);
21465
+ }
21466
+ return { values, simplified };
21467
+ };
21468
+ let built = build2(Number.POSITIVE_INFINITY);
21469
+ for (const budget of ENCODED_CHAR_BUDGETS) {
21470
+ if (nb.buildUrl(path, { path: built.values, markers, padding }).length <= URL_BYTE_BUDGET)
21471
+ break;
21472
+ built = build2(budget);
21163
21473
  }
21474
+ const parts = ["Map rendered"];
21475
+ if (routePoints.length) parts.push("with the route");
21476
+ if (args.paths?.length) parts.push(`${args.paths.length} overlay path(s)`);
21477
+ if (args.markers?.length) parts.push(`${args.markers.length} marker(s)`);
21478
+ const note = built.simplified ? " Display geometry was simplified to fit the map URL limit; distances are unaffected." : "";
21164
21479
  return fetchImageResult(
21165
21480
  nb,
21166
21481
  path,
21167
- buildQuery(geometry),
21168
- `Route map rendered${args.markers?.length ? ` with ${args.markers.length} marker(s)` : ""}.` + simplificationNote,
21482
+ { path: built.values, markers, padding },
21483
+ `${parts.join(", ")}.${note}`,
21169
21484
  args,
21170
21485
  "route-map"
21171
21486
  );
@@ -21183,6 +21498,7 @@ var ALL_TOOLS = [
21183
21498
  geocodeReverse,
21184
21499
  geocodeStructured,
21185
21500
  isochrone,
21501
+ placeBrowse,
21186
21502
  placeLookup,
21187
21503
  placeSearch,
21188
21504
  postcodeLookup,
@@ -21353,7 +21669,7 @@ function extractApiMessage(bodyText) {
21353
21669
  }
21354
21670
 
21355
21671
  // src/index.ts
21356
- var pkg = true ? { version: "0.1.11" } : createRequire(import.meta.url)("../package.json");
21672
+ var pkg = true ? { version: "0.2.0" } : createRequire(import.meta.url)("../package.json");
21357
21673
  function main() {
21358
21674
  const args = process.argv.slice(2);
21359
21675
  if (args.includes("--version") || args.includes("-v")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nextbillion-mcp",
3
- "version": "0.1.11",
3
+ "version": "0.2.0",
4
4
  "description": "MCP server exposing NextBillion.ai location APIs (geocoding, places, routing, maps) as tools for AI agents",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",