@cablate/mcp-google-map 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,9 +6,24 @@ A powerful Model Context Protocol (MCP) server providing comprehensive Google Ma
6
6
 
7
7
  ### Google Maps Features
8
8
 
9
- - Location search and information retrieval
10
- - Geocoding and reverse geocoding
11
- - Detailed place information
9
+ - **Location Search**
10
+
11
+ - Search for places near a specific location with customizable radius and filters
12
+ - Get detailed place information including ratings, opening hours, and contact details
13
+
14
+ - **Geocoding Services**
15
+
16
+ - Convert addresses to coordinates (geocoding)
17
+ - Convert coordinates to addresses (reverse geocoding)
18
+
19
+ - **Distance & Directions**
20
+
21
+ - Calculate distances and travel times between multiple origins and destinations
22
+ - Get detailed directions between two points with step-by-step instructions
23
+ - Support for different travel modes (driving, walking, bicycling, transit)
24
+
25
+ - **Elevation Data**
26
+ - Retrieve elevation data for specific locations
12
27
 
13
28
  ## Installation
14
29
 
@@ -48,6 +63,18 @@ mcp-google-map
48
63
 
49
64
  3. Click "Save" to complete the installation
50
65
 
66
+ ## Available Tools
67
+
68
+ The server provides the following tools:
69
+
70
+ 1. **search_nearby** - Search for places near a specific location
71
+ 2. **get_place_details** - Get detailed information about a specific place
72
+ 3. **maps_geocode** - Convert an address to coordinates
73
+ 4. **maps_reverse_geocode** - Convert coordinates to an address
74
+ 5. **maps_distance_matrix** - Calculate distances and times between multiple origins and destinations
75
+ 6. **maps_directions** - Get directions between two points
76
+ 7. **maps_elevation** - Get elevation data for specific locations
77
+
51
78
  ## Google Maps API Setup
52
79
 
53
80
  To use this service, you need to:
package/dist/index.cjs CHANGED
@@ -17127,13 +17127,13 @@ var require_common = __commonJS({
17127
17127
  Language3["zh_CN"] = "zh-CN";
17128
17128
  Language3["zh_TW"] = "zh-TW";
17129
17129
  })(Language2 || (exports2.Language = Language2 = {}));
17130
- var TravelMode;
17131
- (function(TravelMode2) {
17132
- TravelMode2["driving"] = "driving";
17133
- TravelMode2["walking"] = "walking";
17134
- TravelMode2["bicycling"] = "bicycling";
17135
- TravelMode2["transit"] = "transit";
17136
- })(TravelMode || (exports2.TravelMode = TravelMode = {}));
17130
+ var TravelMode2;
17131
+ (function(TravelMode3) {
17132
+ TravelMode3["driving"] = "driving";
17133
+ TravelMode3["walking"] = "walking";
17134
+ TravelMode3["bicycling"] = "bicycling";
17135
+ TravelMode3["transit"] = "transit";
17136
+ })(TravelMode2 || (exports2.TravelMode = TravelMode2 = {}));
17137
17137
  var TravelRestriction;
17138
17138
  (function(TravelRestriction2) {
17139
17139
  TravelRestriction2["tolls"] = "tolls";
@@ -23407,6 +23407,129 @@ var SEARCH_NEARBY_TOOL = {
23407
23407
  required: ["center"]
23408
23408
  }
23409
23409
  };
23410
+ var GEOCODE_TOOL = {
23411
+ name: "maps_geocode",
23412
+ description: "\u5C07\u5730\u5740\u8F49\u63DB\u70BA\u5EA7\u6A19",
23413
+ inputSchema: {
23414
+ type: "object",
23415
+ properties: {
23416
+ address: {
23417
+ type: "string",
23418
+ description: "\u8981\u8F49\u63DB\u7684\u5730\u5740\u6216\u5730\u6A19\u540D\u7A31"
23419
+ }
23420
+ },
23421
+ required: ["address"]
23422
+ }
23423
+ };
23424
+ var REVERSE_GEOCODE_TOOL = {
23425
+ name: "maps_reverse_geocode",
23426
+ description: "\u5C07\u5EA7\u6A19\u8F49\u63DB\u70BA\u5730\u5740",
23427
+ inputSchema: {
23428
+ type: "object",
23429
+ properties: {
23430
+ latitude: {
23431
+ type: "number",
23432
+ description: "\u7DEF\u5EA6"
23433
+ },
23434
+ longitude: {
23435
+ type: "number",
23436
+ description: "\u7D93\u5EA6"
23437
+ }
23438
+ },
23439
+ required: ["latitude", "longitude"]
23440
+ }
23441
+ };
23442
+ var DISTANCE_MATRIX_TOOL = {
23443
+ name: "maps_distance_matrix",
23444
+ description: "\u8A08\u7B97\u591A\u500B\u8D77\u9EDE\u548C\u7D42\u9EDE\u4E4B\u9593\u7684\u8DDD\u96E2\u548C\u6642\u9593",
23445
+ inputSchema: {
23446
+ type: "object",
23447
+ properties: {
23448
+ origins: {
23449
+ type: "array",
23450
+ items: {
23451
+ type: "string"
23452
+ },
23453
+ description: "\u8D77\u9EDE\u5730\u5740\u6216\u5EA7\u6A19\u5217\u8868"
23454
+ },
23455
+ destinations: {
23456
+ type: "array",
23457
+ items: {
23458
+ type: "string"
23459
+ },
23460
+ description: "\u7D42\u9EDE\u5730\u5740\u6216\u5EA7\u6A19\u5217\u8868"
23461
+ },
23462
+ mode: {
23463
+ type: "string",
23464
+ enum: ["driving", "walking", "bicycling", "transit"],
23465
+ description: "\u4EA4\u901A\u6A21\u5F0F",
23466
+ default: "driving"
23467
+ }
23468
+ },
23469
+ required: ["origins", "destinations"]
23470
+ }
23471
+ };
23472
+ var DIRECTIONS_TOOL = {
23473
+ name: "maps_directions",
23474
+ description: "\u7372\u53D6\u5169\u9EDE\u4E4B\u9593\u7684\u8A73\u7D30\u5C0E\u822A\u8DEF\u7DDA",
23475
+ inputSchema: {
23476
+ type: "object",
23477
+ properties: {
23478
+ origin: {
23479
+ type: "string",
23480
+ description: "\u8D77\u9EDE\u5730\u5740\u6216\u5EA7\u6A19"
23481
+ },
23482
+ destination: {
23483
+ type: "string",
23484
+ description: "\u7D42\u9EDE\u5730\u5740\u6216\u5EA7\u6A19"
23485
+ },
23486
+ mode: {
23487
+ type: "string",
23488
+ enum: ["driving", "walking", "bicycling", "transit"],
23489
+ description: "\u4EA4\u901A\u6A21\u5F0F",
23490
+ default: "driving"
23491
+ },
23492
+ departure_time: {
23493
+ type: "string",
23494
+ description: "\u51FA\u767C\u6642\u9593",
23495
+ default: (/* @__PURE__ */ new Date()).toISOString()
23496
+ },
23497
+ arrival_time: {
23498
+ type: "string",
23499
+ description: "\u62B5\u9054\u6642\u9593"
23500
+ }
23501
+ },
23502
+ required: ["origin", "destination"]
23503
+ }
23504
+ };
23505
+ var ELEVATION_TOOL = {
23506
+ name: "maps_elevation",
23507
+ description: "\u7372\u53D6\u4F4D\u7F6E\u7684\u6D77\u62D4\u6578\u64DA",
23508
+ inputSchema: {
23509
+ type: "object",
23510
+ properties: {
23511
+ locations: {
23512
+ type: "array",
23513
+ items: {
23514
+ type: "object",
23515
+ properties: {
23516
+ latitude: {
23517
+ type: "number",
23518
+ description: "\u7DEF\u5EA6"
23519
+ },
23520
+ longitude: {
23521
+ type: "number",
23522
+ description: "\u7D93\u5EA6"
23523
+ }
23524
+ },
23525
+ required: ["latitude", "longitude"]
23526
+ },
23527
+ description: "\u8981\u7372\u53D6\u6D77\u62D4\u6578\u64DA\u7684\u4F4D\u7F6E\u5217\u8868"
23528
+ }
23529
+ },
23530
+ required: ["locations"]
23531
+ }
23532
+ };
23410
23533
  var GET_PLACE_DETAILS_TOOL = {
23411
23534
  name: "get_place_details",
23412
23535
  description: "\u7372\u53D6\u7279\u5B9A\u5730\u9EDE\u7684\u8A73\u7D30\u8CC7\u8A0A",
@@ -23485,10 +23608,13 @@ var GoogleMapsTools = class {
23485
23608
  if (response.data.results.length === 0) {
23486
23609
  throw new Error("\u627E\u4E0D\u5230\u8A72\u5730\u5740\u7684\u4F4D\u7F6E");
23487
23610
  }
23488
- const location = response.data.results[0].geometry.location;
23611
+ const result = response.data.results[0];
23612
+ const location = result.geometry.location;
23489
23613
  return {
23490
23614
  lat: location.lat,
23491
- lng: location.lng
23615
+ lng: location.lng,
23616
+ formatted_address: result.formatted_address,
23617
+ place_id: result.place_id
23492
23618
  };
23493
23619
  } catch (error) {
23494
23620
  console.error("Error in geocodeAddress:", error);
@@ -23508,6 +23634,190 @@ var GoogleMapsTools = class {
23508
23634
  }
23509
23635
  return this.geocodeAddress(center.value);
23510
23636
  }
23637
+ // 新增公開方法用於地址轉座標
23638
+ async geocode(address) {
23639
+ try {
23640
+ const result = await this.geocodeAddress(address);
23641
+ return {
23642
+ location: { lat: result.lat, lng: result.lng },
23643
+ formatted_address: result.formatted_address || "",
23644
+ place_id: result.place_id || ""
23645
+ };
23646
+ } catch (error) {
23647
+ console.error("Error in geocode:", error);
23648
+ throw new Error("\u5730\u5740\u8F49\u63DB\u5EA7\u6A19\u6642\u767C\u751F\u932F\u8AA4");
23649
+ }
23650
+ }
23651
+ async reverseGeocode(latitude, longitude) {
23652
+ try {
23653
+ const response = await this.client.reverseGeocode({
23654
+ params: {
23655
+ latlng: { lat: latitude, lng: longitude },
23656
+ language: this.defaultLanguage,
23657
+ key: process.env.GOOGLE_MAPS_API_KEY || ""
23658
+ }
23659
+ });
23660
+ if (response.data.results.length === 0) {
23661
+ throw new Error("\u627E\u4E0D\u5230\u8A72\u5EA7\u6A19\u7684\u5730\u5740");
23662
+ }
23663
+ const result = response.data.results[0];
23664
+ return {
23665
+ formatted_address: result.formatted_address,
23666
+ place_id: result.place_id,
23667
+ address_components: result.address_components
23668
+ };
23669
+ } catch (error) {
23670
+ console.error("Error in reverseGeocode:", error);
23671
+ throw new Error("\u5EA7\u6A19\u8F49\u63DB\u5730\u5740\u6642\u767C\u751F\u932F\u8AA4");
23672
+ }
23673
+ }
23674
+ async calculateDistanceMatrix(origins, destinations, mode = "driving") {
23675
+ try {
23676
+ const response = await this.client.distancematrix({
23677
+ params: {
23678
+ origins,
23679
+ destinations,
23680
+ mode,
23681
+ language: this.defaultLanguage,
23682
+ key: process.env.GOOGLE_MAPS_API_KEY || ""
23683
+ }
23684
+ });
23685
+ const result = response.data;
23686
+ if (result.status !== "OK") {
23687
+ throw new Error(`\u8DDD\u96E2\u77E9\u9663\u8A08\u7B97\u5931\u6557: ${result.status}`);
23688
+ }
23689
+ const distances = [];
23690
+ const durations = [];
23691
+ result.rows.forEach((row) => {
23692
+ const distanceRow = [];
23693
+ const durationRow = [];
23694
+ row.elements.forEach((element) => {
23695
+ if (element.status === "OK") {
23696
+ distanceRow.push({
23697
+ value: element.distance.value,
23698
+ text: element.distance.text
23699
+ });
23700
+ durationRow.push({
23701
+ value: element.duration.value,
23702
+ text: element.duration.text
23703
+ });
23704
+ } else {
23705
+ distanceRow.push(null);
23706
+ durationRow.push(null);
23707
+ }
23708
+ });
23709
+ distances.push(distanceRow);
23710
+ durations.push(durationRow);
23711
+ });
23712
+ return {
23713
+ distances,
23714
+ durations,
23715
+ origin_addresses: result.origin_addresses,
23716
+ destination_addresses: result.destination_addresses
23717
+ };
23718
+ } catch (error) {
23719
+ console.error("Error in calculateDistanceMatrix:", error);
23720
+ throw new Error("\u8A08\u7B97\u8DDD\u96E2\u77E9\u9663\u6642\u767C\u751F\u932F\u8AA4");
23721
+ }
23722
+ }
23723
+ async getDirections(origin, destination, mode = "driving", departure_time, arrival_time) {
23724
+ try {
23725
+ let apiArrivalTime = void 0;
23726
+ if (arrival_time) {
23727
+ apiArrivalTime = Math.floor(arrival_time.getTime() / 1e3);
23728
+ }
23729
+ let apiDepartureTime = void 0;
23730
+ if (!apiArrivalTime) {
23731
+ if (departure_time instanceof Date) {
23732
+ apiDepartureTime = Math.floor(departure_time.getTime() / 1e3);
23733
+ } else if (departure_time) {
23734
+ apiDepartureTime = departure_time;
23735
+ } else {
23736
+ apiDepartureTime = "now";
23737
+ }
23738
+ }
23739
+ const response = await this.client.directions({
23740
+ params: {
23741
+ origin,
23742
+ destination,
23743
+ mode,
23744
+ language: this.defaultLanguage,
23745
+ key: process.env.GOOGLE_MAPS_API_KEY || "",
23746
+ arrival_time: apiArrivalTime,
23747
+ departure_time: apiDepartureTime
23748
+ }
23749
+ });
23750
+ const result = response.data;
23751
+ if (result.status !== "OK") {
23752
+ throw new Error(`\u8DEF\u7DDA\u6307\u5F15\u7372\u53D6\u5931\u6557: ${result.status} (arrival_time: ${apiArrivalTime}, departure_time: ${apiDepartureTime})`);
23753
+ }
23754
+ if (result.routes.length === 0) {
23755
+ throw new Error("\u627E\u4E0D\u5230\u8DEF\u7DDA");
23756
+ }
23757
+ const route = result.routes[0];
23758
+ const legs = route.legs[0];
23759
+ const formatTime = (timeInfo) => {
23760
+ if (!timeInfo || typeof timeInfo.value !== "number") return "";
23761
+ const date = new Date(timeInfo.value * 1e3);
23762
+ const options = {
23763
+ year: "numeric",
23764
+ month: "2-digit",
23765
+ day: "2-digit",
23766
+ hour: "2-digit",
23767
+ minute: "2-digit",
23768
+ second: "2-digit",
23769
+ hour12: false
23770
+ // Use 24-hour format
23771
+ };
23772
+ if (timeInfo.time_zone && typeof timeInfo.time_zone === "string") {
23773
+ options.timeZone = timeInfo.time_zone;
23774
+ }
23775
+ return date.toLocaleString(this.defaultLanguage.toString(), options);
23776
+ };
23777
+ return {
23778
+ routes: result.routes,
23779
+ summary: route.summary,
23780
+ total_distance: {
23781
+ value: legs.distance.value,
23782
+ text: legs.distance.text
23783
+ },
23784
+ total_duration: {
23785
+ value: legs.duration.value,
23786
+ text: legs.duration.text
23787
+ },
23788
+ arrival_time: formatTime(legs.arrival_time),
23789
+ departure_time: formatTime(legs.departure_time)
23790
+ };
23791
+ } catch (error) {
23792
+ console.error("Error in getDirections:", error);
23793
+ throw new Error("\u7372\u53D6\u8DEF\u7DDA\u6307\u5F15\u6642\u767C\u751F\u932F\u8AA4" + error);
23794
+ }
23795
+ }
23796
+ async getElevation(locations) {
23797
+ try {
23798
+ const formattedLocations = locations.map((loc) => ({
23799
+ lat: loc.latitude,
23800
+ lng: loc.longitude
23801
+ }));
23802
+ const response = await this.client.elevation({
23803
+ params: {
23804
+ locations: formattedLocations,
23805
+ key: process.env.GOOGLE_MAPS_API_KEY || ""
23806
+ }
23807
+ });
23808
+ const result = response.data;
23809
+ if (result.status !== "OK") {
23810
+ throw new Error(`\u6D77\u62D4\u6578\u64DA\u7372\u53D6\u5931\u6557: ${result.status}`);
23811
+ }
23812
+ return result.results.map((item, index) => ({
23813
+ elevation: item.elevation,
23814
+ location: formattedLocations[index]
23815
+ }));
23816
+ } catch (error) {
23817
+ console.error("Error in getElevation:", error);
23818
+ throw new Error("\u7372\u53D6\u6D77\u62D4\u6578\u64DA\u6642\u767C\u751F\u932F\u8AA4");
23819
+ }
23820
+ }
23511
23821
  };
23512
23822
 
23513
23823
  // src/maps-tools/searchPlaces.ts
@@ -23576,10 +23886,82 @@ var PlacesSearcher = class {
23576
23886
  };
23577
23887
  }
23578
23888
  }
23889
+ async geocode(address) {
23890
+ try {
23891
+ const result = await this.mapsTools.geocode(address);
23892
+ return {
23893
+ success: true,
23894
+ data: result
23895
+ };
23896
+ } catch (error) {
23897
+ return {
23898
+ success: false,
23899
+ error: error instanceof Error ? error.message : "\u5730\u5740\u8F49\u63DB\u5EA7\u6A19\u6642\u767C\u751F\u932F\u8AA4"
23900
+ };
23901
+ }
23902
+ }
23903
+ async reverseGeocode(latitude, longitude) {
23904
+ try {
23905
+ const result = await this.mapsTools.reverseGeocode(latitude, longitude);
23906
+ return {
23907
+ success: true,
23908
+ data: result
23909
+ };
23910
+ } catch (error) {
23911
+ return {
23912
+ success: false,
23913
+ error: error instanceof Error ? error.message : "\u5EA7\u6A19\u8F49\u63DB\u5730\u5740\u6642\u767C\u751F\u932F\u8AA4"
23914
+ };
23915
+ }
23916
+ }
23917
+ async calculateDistanceMatrix(origins, destinations, mode = "driving") {
23918
+ try {
23919
+ const result = await this.mapsTools.calculateDistanceMatrix(origins, destinations, mode);
23920
+ return {
23921
+ success: true,
23922
+ data: result
23923
+ };
23924
+ } catch (error) {
23925
+ return {
23926
+ success: false,
23927
+ error: error instanceof Error ? error.message : "\u8A08\u7B97\u8DDD\u96E2\u77E9\u9663\u6642\u767C\u751F\u932F\u8AA4"
23928
+ };
23929
+ }
23930
+ }
23931
+ async getDirections(origin, destination, mode = "driving", departure_time, arrival_time) {
23932
+ try {
23933
+ const departureTime = departure_time ? new Date(departure_time) : /* @__PURE__ */ new Date();
23934
+ const arrivalTime = arrival_time ? new Date(arrival_time) : void 0;
23935
+ const result = await this.mapsTools.getDirections(origin, destination, mode, departureTime, arrivalTime);
23936
+ return {
23937
+ success: true,
23938
+ data: result
23939
+ };
23940
+ } catch (error) {
23941
+ return {
23942
+ success: false,
23943
+ error: error instanceof Error ? error.message : "\u7372\u53D6\u8DEF\u7DDA\u6307\u5F15\u6642\u767C\u751F\u932F\u8AA4"
23944
+ };
23945
+ }
23946
+ }
23947
+ async getElevation(locations) {
23948
+ try {
23949
+ const result = await this.mapsTools.getElevation(locations);
23950
+ return {
23951
+ success: true,
23952
+ data: result
23953
+ };
23954
+ } catch (error) {
23955
+ return {
23956
+ success: false,
23957
+ error: error instanceof Error ? error.message : "\u7372\u53D6\u6D77\u62D4\u6578\u64DA\u6642\u767C\u751F\u932F\u8AA4"
23958
+ };
23959
+ }
23960
+ }
23579
23961
  };
23580
23962
 
23581
23963
  // src/index.ts
23582
- var tools = [SEARCH_NEARBY_TOOL, GET_PLACE_DETAILS_TOOL];
23964
+ var tools = [SEARCH_NEARBY_TOOL, GET_PLACE_DETAILS_TOOL, GEOCODE_TOOL, REVERSE_GEOCODE_TOOL, DISTANCE_MATRIX_TOOL, DIRECTIONS_TOOL, ELEVATION_TOOL];
23583
23965
  var placesSearcher = new PlacesSearcher();
23584
23966
  var server = new Server(
23585
23967
  {
@@ -23647,6 +24029,101 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
23647
24029
  isError: false
23648
24030
  };
23649
24031
  }
24032
+ if (name === "maps_geocode") {
24033
+ const { address } = args;
24034
+ const result = await placesSearcher.geocode(address);
24035
+ if (!result.success) {
24036
+ return {
24037
+ content: [{ type: "text", text: result.error || "\u5730\u5740\u8F49\u63DB\u5EA7\u6A19\u5931\u6557" }],
24038
+ isError: true
24039
+ };
24040
+ }
24041
+ return {
24042
+ content: [
24043
+ {
24044
+ type: "text",
24045
+ text: JSON.stringify(result.data, null, 2)
24046
+ }
24047
+ ],
24048
+ isError: false
24049
+ };
24050
+ }
24051
+ if (name === "maps_reverse_geocode") {
24052
+ const { latitude, longitude } = args;
24053
+ const result = await placesSearcher.reverseGeocode(latitude, longitude);
24054
+ if (!result.success) {
24055
+ return {
24056
+ content: [{ type: "text", text: result.error || "\u5EA7\u6A19\u8F49\u63DB\u5730\u5740\u5931\u6557" }],
24057
+ isError: true
24058
+ };
24059
+ }
24060
+ return {
24061
+ content: [
24062
+ {
24063
+ type: "text",
24064
+ text: JSON.stringify(result.data, null, 2)
24065
+ }
24066
+ ],
24067
+ isError: false
24068
+ };
24069
+ }
24070
+ if (name === "maps_distance_matrix") {
24071
+ const { origins, destinations, mode } = args;
24072
+ const result = await placesSearcher.calculateDistanceMatrix(origins, destinations, mode || "driving");
24073
+ if (!result.success) {
24074
+ return {
24075
+ content: [{ type: "text", text: result.error || "\u8A08\u7B97\u8DDD\u96E2\u77E9\u9663\u5931\u6557" }],
24076
+ isError: true
24077
+ };
24078
+ }
24079
+ return {
24080
+ content: [
24081
+ {
24082
+ type: "text",
24083
+ text: JSON.stringify(result.data, null, 2)
24084
+ }
24085
+ ],
24086
+ isError: false
24087
+ };
24088
+ }
24089
+ if (name === "maps_directions") {
24090
+ const { origin, destination, mode, arrival_time, departure_time } = args;
24091
+ const result = await placesSearcher.getDirections(origin, destination, mode, departure_time, arrival_time);
24092
+ if (!result.success) {
24093
+ return {
24094
+ content: [{ type: "text", text: result.error || "\u7372\u53D6\u8DEF\u7DDA\u6307\u5F15\u5931\u6557" }],
24095
+ isError: true
24096
+ };
24097
+ }
24098
+ return {
24099
+ content: [
24100
+ {
24101
+ type: "text",
24102
+ text: JSON.stringify(result.data, null, 2)
24103
+ }
24104
+ ],
24105
+ isError: false
24106
+ };
24107
+ }
24108
+ if (name === "maps_elevation") {
24109
+ const { locations } = args;
24110
+ const result = await placesSearcher.getElevation(locations);
24111
+ if (!result.success) {
24112
+ return {
24113
+ content: [{ type: "text", text: result.error || "\u7372\u53D6\u6D77\u62D4\u6578\u64DA\u5931\u6557" }],
24114
+ isError: true
24115
+ };
24116
+ }
24117
+ return {
24118
+ content: [
24119
+ {
24120
+ type: "text",
24121
+ text: JSON.stringify(result.data, null, 2)
24122
+ }
24123
+ ],
24124
+ isError: false
24125
+ };
24126
+ }
23650
24127
  return {
23651
24128
  content: [{ type: "text", text: `\u932F\u8AA4\uFF1A\u672A\u77E5\u7684\u5DE5\u5177 ${name}` }],
23652
24129
  isError: true
package/dist/index.js ADDED
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
5
+ import { DIRECTIONS_TOOL, DISTANCE_MATRIX_TOOL, ELEVATION_TOOL, GEOCODE_TOOL, GET_PLACE_DETAILS_TOOL, REVERSE_GEOCODE_TOOL, SEARCH_NEARBY_TOOL } from "./maps-tools/mapsTools.js";
6
+ import { PlacesSearcher } from "./maps-tools/searchPlaces.js";
7
+ const tools = [SEARCH_NEARBY_TOOL, GET_PLACE_DETAILS_TOOL, GEOCODE_TOOL, REVERSE_GEOCODE_TOOL, DISTANCE_MATRIX_TOOL, DIRECTIONS_TOOL, ELEVATION_TOOL];
8
+ const placesSearcher = new PlacesSearcher();
9
+ const server = new Server({
10
+ name: "mcp-server/maps_executor",
11
+ version: "0.0.1",
12
+ }, {
13
+ capabilities: {
14
+ description: "An MCP server providing Google Maps integration!",
15
+ tools: {},
16
+ },
17
+ });
18
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
19
+ tools,
20
+ }));
21
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
22
+ try {
23
+ const { name, arguments: args } = request.params;
24
+ if (!args) {
25
+ throw new Error("No parameters provided");
26
+ }
27
+ if (name === "search_nearby") {
28
+ const { center, keyword, radius, openNow, minRating } = args;
29
+ const result = await placesSearcher.searchNearby({
30
+ center,
31
+ keyword,
32
+ radius,
33
+ openNow,
34
+ minRating,
35
+ });
36
+ if (!result.success) {
37
+ return {
38
+ content: [{ type: "text", text: result.error || "搜尋失敗" }],
39
+ isError: true,
40
+ };
41
+ }
42
+ return {
43
+ content: [
44
+ {
45
+ type: "text",
46
+ text: `location: ${JSON.stringify(result.location, null, 2)}\n` + JSON.stringify(result.data, null, 2),
47
+ },
48
+ ],
49
+ isError: false,
50
+ };
51
+ }
52
+ if (name === "get_place_details") {
53
+ const { placeId } = args;
54
+ const result = await placesSearcher.getPlaceDetails(placeId);
55
+ if (!result.success) {
56
+ return {
57
+ content: [{ type: "text", text: result.error || "獲取詳細資訊失敗" }],
58
+ isError: true,
59
+ };
60
+ }
61
+ return {
62
+ content: [
63
+ {
64
+ type: "text",
65
+ text: JSON.stringify(result.data, null, 2),
66
+ },
67
+ ],
68
+ isError: false,
69
+ };
70
+ }
71
+ if (name === "maps_geocode") {
72
+ const { address } = args;
73
+ const result = await placesSearcher.geocode(address);
74
+ if (!result.success) {
75
+ return {
76
+ content: [{ type: "text", text: result.error || "地址轉換座標失敗" }],
77
+ isError: true,
78
+ };
79
+ }
80
+ return {
81
+ content: [
82
+ {
83
+ type: "text",
84
+ text: JSON.stringify(result.data, null, 2),
85
+ },
86
+ ],
87
+ isError: false,
88
+ };
89
+ }
90
+ if (name === "maps_reverse_geocode") {
91
+ const { latitude, longitude } = args;
92
+ const result = await placesSearcher.reverseGeocode(latitude, longitude);
93
+ if (!result.success) {
94
+ return {
95
+ content: [{ type: "text", text: result.error || "座標轉換地址失敗" }],
96
+ isError: true,
97
+ };
98
+ }
99
+ return {
100
+ content: [
101
+ {
102
+ type: "text",
103
+ text: JSON.stringify(result.data, null, 2),
104
+ },
105
+ ],
106
+ isError: false,
107
+ };
108
+ }
109
+ if (name === "maps_distance_matrix") {
110
+ const { origins, destinations, mode } = args;
111
+ const result = await placesSearcher.calculateDistanceMatrix(origins, destinations, mode || "driving");
112
+ if (!result.success) {
113
+ return {
114
+ content: [{ type: "text", text: result.error || "計算距離矩陣失敗" }],
115
+ isError: true,
116
+ };
117
+ }
118
+ return {
119
+ content: [
120
+ {
121
+ type: "text",
122
+ text: JSON.stringify(result.data, null, 2),
123
+ },
124
+ ],
125
+ isError: false,
126
+ };
127
+ }
128
+ if (name === "maps_directions") {
129
+ const { origin, destination, mode } = args;
130
+ const result = await placesSearcher.getDirections(origin, destination, mode || "driving");
131
+ if (!result.success) {
132
+ return {
133
+ content: [{ type: "text", text: result.error || "獲取路線指引失敗" }],
134
+ isError: true,
135
+ };
136
+ }
137
+ return {
138
+ content: [
139
+ {
140
+ type: "text",
141
+ text: JSON.stringify(result.data, null, 2),
142
+ },
143
+ ],
144
+ isError: false,
145
+ };
146
+ }
147
+ if (name === "maps_elevation") {
148
+ const { locations } = args;
149
+ const result = await placesSearcher.getElevation(locations);
150
+ if (!result.success) {
151
+ return {
152
+ content: [{ type: "text", text: result.error || "獲取海拔數據失敗" }],
153
+ isError: true,
154
+ };
155
+ }
156
+ return {
157
+ content: [
158
+ {
159
+ type: "text",
160
+ text: JSON.stringify(result.data, null, 2),
161
+ },
162
+ ],
163
+ isError: false,
164
+ };
165
+ }
166
+ return {
167
+ content: [{ type: "text", text: `錯誤:未知的工具 ${name}` }],
168
+ isError: true,
169
+ };
170
+ }
171
+ catch (error) {
172
+ return {
173
+ content: [
174
+ {
175
+ type: "text",
176
+ text: `錯誤:${error instanceof Error ? error.message : String(error)}`,
177
+ },
178
+ ],
179
+ isError: true,
180
+ };
181
+ }
182
+ });
183
+ async function runServer() {
184
+ try {
185
+ const transport = new StdioServerTransport();
186
+ await server.connect(transport);
187
+ console.log("MCP Maps Server started");
188
+ }
189
+ catch (error) {
190
+ console.error("Server startup failed:", error);
191
+ process.exit(1);
192
+ }
193
+ }
194
+ runServer().catch((error) => {
195
+ console.error("Server encountered a critical error:", error);
196
+ process.exit(1);
197
+ });
@@ -0,0 +1,167 @@
1
+ export const SEARCH_NEARBY_TOOL = {
2
+ name: "search_nearby",
3
+ description: "搜尋附近的地點",
4
+ inputSchema: {
5
+ type: "object",
6
+ properties: {
7
+ center: {
8
+ type: "object",
9
+ properties: {
10
+ value: { type: "string", description: "地址、地標名稱或經緯度座標(經緯度座標格式: lat,lng)" },
11
+ isCoordinates: { type: "boolean", description: "是否為經緯度座標", default: false },
12
+ },
13
+ required: ["value"],
14
+ description: "搜尋中心點",
15
+ },
16
+ keyword: {
17
+ type: "string",
18
+ description: "搜尋關鍵字(例如:餐廳、咖啡廳)",
19
+ },
20
+ radius: {
21
+ type: "number",
22
+ description: "搜尋半徑(公尺)",
23
+ default: 1000,
24
+ },
25
+ openNow: {
26
+ type: "boolean",
27
+ description: "是否只顯示營業中的地點",
28
+ default: false,
29
+ },
30
+ minRating: {
31
+ type: "number",
32
+ description: "最低評分要求(0-5)",
33
+ minimum: 0,
34
+ maximum: 5,
35
+ },
36
+ },
37
+ required: ["center"],
38
+ },
39
+ };
40
+ export const GEOCODE_TOOL = {
41
+ name: "maps_geocode",
42
+ description: "將地址轉換為座標",
43
+ inputSchema: {
44
+ type: "object",
45
+ properties: {
46
+ address: {
47
+ type: "string",
48
+ description: "要轉換的地址或地標名稱",
49
+ },
50
+ },
51
+ required: ["address"],
52
+ },
53
+ };
54
+ export const REVERSE_GEOCODE_TOOL = {
55
+ name: "maps_reverse_geocode",
56
+ description: "將座標轉換為地址",
57
+ inputSchema: {
58
+ type: "object",
59
+ properties: {
60
+ latitude: {
61
+ type: "number",
62
+ description: "緯度",
63
+ },
64
+ longitude: {
65
+ type: "number",
66
+ description: "經度",
67
+ },
68
+ },
69
+ required: ["latitude", "longitude"],
70
+ },
71
+ };
72
+ export const DISTANCE_MATRIX_TOOL = {
73
+ name: "maps_distance_matrix",
74
+ description: "計算多個起點和終點之間的距離和時間",
75
+ inputSchema: {
76
+ type: "object",
77
+ properties: {
78
+ origins: {
79
+ type: "array",
80
+ items: {
81
+ type: "string",
82
+ },
83
+ description: "起點地址或座標列表",
84
+ },
85
+ destinations: {
86
+ type: "array",
87
+ items: {
88
+ type: "string",
89
+ },
90
+ description: "終點地址或座標列表",
91
+ },
92
+ mode: {
93
+ type: "string",
94
+ enum: ["driving", "walking", "bicycling", "transit"],
95
+ description: "交通模式",
96
+ default: "driving",
97
+ },
98
+ },
99
+ required: ["origins", "destinations"],
100
+ },
101
+ };
102
+ export const DIRECTIONS_TOOL = {
103
+ name: "maps_directions",
104
+ description: "獲取兩點之間的路線指引",
105
+ inputSchema: {
106
+ type: "object",
107
+ properties: {
108
+ origin: {
109
+ type: "string",
110
+ description: "起點地址或座標",
111
+ },
112
+ destination: {
113
+ type: "string",
114
+ description: "終點地址或座標",
115
+ },
116
+ mode: {
117
+ type: "string",
118
+ enum: ["driving", "walking", "bicycling", "transit"],
119
+ description: "交通模式",
120
+ default: "driving",
121
+ },
122
+ },
123
+ required: ["origin", "destination"],
124
+ },
125
+ };
126
+ export const ELEVATION_TOOL = {
127
+ name: "maps_elevation",
128
+ description: "獲取位置的海拔數據",
129
+ inputSchema: {
130
+ type: "object",
131
+ properties: {
132
+ locations: {
133
+ type: "array",
134
+ items: {
135
+ type: "object",
136
+ properties: {
137
+ latitude: {
138
+ type: "number",
139
+ description: "緯度",
140
+ },
141
+ longitude: {
142
+ type: "number",
143
+ description: "經度",
144
+ },
145
+ },
146
+ required: ["latitude", "longitude"],
147
+ },
148
+ description: "要獲取海拔數據的位置列表",
149
+ },
150
+ },
151
+ required: ["locations"],
152
+ },
153
+ };
154
+ export const GET_PLACE_DETAILS_TOOL = {
155
+ name: "get_place_details",
156
+ description: "獲取特定地點的詳細資訊",
157
+ inputSchema: {
158
+ type: "object",
159
+ properties: {
160
+ placeId: {
161
+ type: "string",
162
+ description: "Google Maps 地點 ID",
163
+ },
164
+ },
165
+ required: ["placeId"],
166
+ },
167
+ };
@@ -0,0 +1,144 @@
1
+ import { GoogleMapsTools } from "./toolclass.js";
2
+ export class PlacesSearcher {
3
+ constructor() {
4
+ this.mapsTools = new GoogleMapsTools();
5
+ }
6
+ async searchNearby(params) {
7
+ try {
8
+ const location = await this.mapsTools.getLocation(params.center);
9
+ console.error(location);
10
+ const places = await this.mapsTools.searchNearbyPlaces({
11
+ location,
12
+ keyword: params.keyword,
13
+ radius: params.radius,
14
+ openNow: params.openNow,
15
+ minRating: params.minRating,
16
+ });
17
+ return {
18
+ location: location,
19
+ success: true,
20
+ data: places.map((place) => ({
21
+ name: place.name,
22
+ place_id: place.place_id,
23
+ address: place.formatted_address,
24
+ location: place.geometry.location,
25
+ rating: place.rating,
26
+ total_ratings: place.user_ratings_total,
27
+ open_now: place.opening_hours?.open_now,
28
+ })),
29
+ };
30
+ }
31
+ catch (error) {
32
+ return {
33
+ success: false,
34
+ error: error instanceof Error ? error.message : "搜尋時發生錯誤",
35
+ };
36
+ }
37
+ }
38
+ async getPlaceDetails(placeId) {
39
+ try {
40
+ const details = await this.mapsTools.getPlaceDetails(placeId);
41
+ return {
42
+ success: true,
43
+ data: {
44
+ name: details.name,
45
+ address: details.formatted_address,
46
+ location: details.geometry?.location,
47
+ rating: details.rating,
48
+ total_ratings: details.user_ratings_total,
49
+ open_now: details.opening_hours?.open_now,
50
+ phone: details.formatted_phone_number,
51
+ website: details.website,
52
+ price_level: details.price_level,
53
+ reviews: details.reviews?.map((review) => ({
54
+ rating: review.rating,
55
+ text: review.text,
56
+ time: review.time,
57
+ author_name: review.author_name,
58
+ })),
59
+ },
60
+ };
61
+ }
62
+ catch (error) {
63
+ return {
64
+ success: false,
65
+ error: error instanceof Error ? error.message : "獲取詳細資訊時發生錯誤",
66
+ };
67
+ }
68
+ }
69
+ async geocode(address) {
70
+ try {
71
+ const result = await this.mapsTools.geocode(address);
72
+ return {
73
+ success: true,
74
+ data: result,
75
+ };
76
+ }
77
+ catch (error) {
78
+ return {
79
+ success: false,
80
+ error: error instanceof Error ? error.message : "地址轉換座標時發生錯誤",
81
+ };
82
+ }
83
+ }
84
+ async reverseGeocode(latitude, longitude) {
85
+ try {
86
+ const result = await this.mapsTools.reverseGeocode(latitude, longitude);
87
+ return {
88
+ success: true,
89
+ data: result,
90
+ };
91
+ }
92
+ catch (error) {
93
+ return {
94
+ success: false,
95
+ error: error instanceof Error ? error.message : "座標轉換地址時發生錯誤",
96
+ };
97
+ }
98
+ }
99
+ async calculateDistanceMatrix(origins, destinations, mode = "driving") {
100
+ try {
101
+ const result = await this.mapsTools.calculateDistanceMatrix(origins, destinations, mode);
102
+ return {
103
+ success: true,
104
+ data: result,
105
+ };
106
+ }
107
+ catch (error) {
108
+ return {
109
+ success: false,
110
+ error: error instanceof Error ? error.message : "計算距離矩陣時發生錯誤",
111
+ };
112
+ }
113
+ }
114
+ async getDirections(origin, destination, mode = "driving") {
115
+ try {
116
+ const result = await this.mapsTools.getDirections(origin, destination, mode);
117
+ return {
118
+ success: true,
119
+ data: result,
120
+ };
121
+ }
122
+ catch (error) {
123
+ return {
124
+ success: false,
125
+ error: error instanceof Error ? error.message : "獲取路線指引時發生錯誤",
126
+ };
127
+ }
128
+ }
129
+ async getElevation(locations) {
130
+ try {
131
+ const result = await this.mapsTools.getElevation(locations);
132
+ return {
133
+ success: true,
134
+ data: result,
135
+ };
136
+ }
137
+ catch (error) {
138
+ return {
139
+ success: false,
140
+ error: error instanceof Error ? error.message : "獲取海拔數據時發生錯誤",
141
+ };
142
+ }
143
+ }
144
+ }
@@ -0,0 +1,248 @@
1
+ import { Client, Language } from "@googlemaps/google-maps-services-js";
2
+ import dotenv from "dotenv";
3
+ // 確保環境變數被載入
4
+ dotenv.config();
5
+ export class GoogleMapsTools {
6
+ constructor() {
7
+ this.defaultLanguage = Language.zh_TW;
8
+ this.client = new Client({});
9
+ if (!process.env.GOOGLE_MAPS_API_KEY) {
10
+ throw new Error("Google Maps API Key is required");
11
+ }
12
+ }
13
+ async searchNearbyPlaces(params) {
14
+ const searchParams = {
15
+ location: params.location,
16
+ radius: params.radius || 1000,
17
+ keyword: params.keyword,
18
+ opennow: params.openNow,
19
+ language: this.defaultLanguage,
20
+ key: process.env.GOOGLE_MAPS_API_KEY || "",
21
+ };
22
+ try {
23
+ const response = await this.client.placesNearby({
24
+ params: searchParams,
25
+ });
26
+ let results = response.data.results;
27
+ // 如果有最低評分要求,進行過濾
28
+ if (params.minRating) {
29
+ results = results.filter((place) => (place.rating || 0) >= (params.minRating || 0));
30
+ }
31
+ return results;
32
+ }
33
+ catch (error) {
34
+ console.error("Error in searchNearbyPlaces:", error);
35
+ throw new Error("搜尋附近地點時發生錯誤");
36
+ }
37
+ }
38
+ async getPlaceDetails(placeId) {
39
+ try {
40
+ const response = await this.client.placeDetails({
41
+ params: {
42
+ place_id: placeId,
43
+ fields: ["name", "rating", "formatted_address", "opening_hours", "reviews", "geometry", "formatted_phone_number", "website", "price_level", "photos"],
44
+ language: this.defaultLanguage,
45
+ key: process.env.GOOGLE_MAPS_API_KEY || "",
46
+ },
47
+ });
48
+ return response.data.result;
49
+ }
50
+ catch (error) {
51
+ console.error("Error in getPlaceDetails:", error);
52
+ throw new Error("獲取地點詳細資訊時發生錯誤");
53
+ }
54
+ }
55
+ async geocodeAddress(address) {
56
+ try {
57
+ const response = await this.client.geocode({
58
+ params: {
59
+ address: address,
60
+ key: process.env.GOOGLE_MAPS_API_KEY || "",
61
+ language: this.defaultLanguage,
62
+ },
63
+ });
64
+ if (response.data.results.length === 0) {
65
+ throw new Error("找不到該地址的位置");
66
+ }
67
+ const result = response.data.results[0];
68
+ const location = result.geometry.location;
69
+ return {
70
+ lat: location.lat,
71
+ lng: location.lng,
72
+ formatted_address: result.formatted_address,
73
+ place_id: result.place_id,
74
+ };
75
+ }
76
+ catch (error) {
77
+ console.error("Error in geocodeAddress:", error);
78
+ throw new Error("地址轉換座標時發生錯誤");
79
+ }
80
+ }
81
+ parseCoordinates(coordString) {
82
+ const coords = coordString.split(",").map((c) => parseFloat(c.trim()));
83
+ if (coords.length !== 2 || isNaN(coords[0]) || isNaN(coords[1])) {
84
+ throw new Error("無效的座標格式,請使用「緯度,經度」格式");
85
+ }
86
+ return { lat: coords[0], lng: coords[1] };
87
+ }
88
+ async getLocation(center) {
89
+ if (center.isCoordinates) {
90
+ return this.parseCoordinates(center.value);
91
+ }
92
+ return this.geocodeAddress(center.value);
93
+ }
94
+ // 新增公開方法用於地址轉座標
95
+ async geocode(address) {
96
+ try {
97
+ const result = await this.geocodeAddress(address);
98
+ return {
99
+ location: { lat: result.lat, lng: result.lng },
100
+ formatted_address: result.formatted_address || "",
101
+ place_id: result.place_id || "",
102
+ };
103
+ }
104
+ catch (error) {
105
+ console.error("Error in geocode:", error);
106
+ throw new Error("地址轉換座標時發生錯誤");
107
+ }
108
+ }
109
+ async reverseGeocode(latitude, longitude) {
110
+ try {
111
+ const response = await this.client.reverseGeocode({
112
+ params: {
113
+ latlng: { lat: latitude, lng: longitude },
114
+ language: this.defaultLanguage,
115
+ key: process.env.GOOGLE_MAPS_API_KEY || "",
116
+ },
117
+ });
118
+ if (response.data.results.length === 0) {
119
+ throw new Error("找不到該座標的地址");
120
+ }
121
+ const result = response.data.results[0];
122
+ return {
123
+ formatted_address: result.formatted_address,
124
+ place_id: result.place_id,
125
+ address_components: result.address_components,
126
+ };
127
+ }
128
+ catch (error) {
129
+ console.error("Error in reverseGeocode:", error);
130
+ throw new Error("座標轉換地址時發生錯誤");
131
+ }
132
+ }
133
+ async calculateDistanceMatrix(origins, destinations, mode = "driving") {
134
+ try {
135
+ const response = await this.client.distancematrix({
136
+ params: {
137
+ origins: origins,
138
+ destinations: destinations,
139
+ mode: mode,
140
+ language: this.defaultLanguage,
141
+ key: process.env.GOOGLE_MAPS_API_KEY || "",
142
+ },
143
+ });
144
+ const result = response.data;
145
+ if (result.status !== "OK") {
146
+ throw new Error(`距離矩陣計算失敗: ${result.status}`);
147
+ }
148
+ const distances = [];
149
+ const durations = [];
150
+ result.rows.forEach((row) => {
151
+ const distanceRow = [];
152
+ const durationRow = [];
153
+ row.elements.forEach((element) => {
154
+ if (element.status === "OK") {
155
+ distanceRow.push({
156
+ value: element.distance.value,
157
+ text: element.distance.text,
158
+ });
159
+ durationRow.push({
160
+ value: element.duration.value,
161
+ text: element.duration.text,
162
+ });
163
+ }
164
+ else {
165
+ distanceRow.push(null);
166
+ durationRow.push(null);
167
+ }
168
+ });
169
+ distances.push(distanceRow);
170
+ durations.push(durationRow);
171
+ });
172
+ return {
173
+ distances: distances,
174
+ durations: durations,
175
+ origin_addresses: result.origin_addresses,
176
+ destination_addresses: result.destination_addresses,
177
+ };
178
+ }
179
+ catch (error) {
180
+ console.error("Error in calculateDistanceMatrix:", error);
181
+ throw new Error("計算距離矩陣時發生錯誤");
182
+ }
183
+ }
184
+ async getDirections(origin, destination, mode = "driving") {
185
+ try {
186
+ const response = await this.client.directions({
187
+ params: {
188
+ origin: origin,
189
+ destination: destination,
190
+ mode: mode,
191
+ language: this.defaultLanguage,
192
+ key: process.env.GOOGLE_MAPS_API_KEY || "",
193
+ },
194
+ });
195
+ const result = response.data;
196
+ if (result.status !== "OK") {
197
+ throw new Error(`路線指引獲取失敗: ${result.status}`);
198
+ }
199
+ if (result.routes.length === 0) {
200
+ throw new Error("找不到路線");
201
+ }
202
+ const route = result.routes[0];
203
+ const legs = route.legs[0];
204
+ return {
205
+ routes: result.routes,
206
+ summary: route.summary,
207
+ total_distance: {
208
+ value: legs.distance.value,
209
+ text: legs.distance.text,
210
+ },
211
+ total_duration: {
212
+ value: legs.duration.value,
213
+ text: legs.duration.text,
214
+ },
215
+ };
216
+ }
217
+ catch (error) {
218
+ console.error("Error in getDirections:", error);
219
+ throw new Error("獲取路線指引時發生錯誤");
220
+ }
221
+ }
222
+ async getElevation(locations) {
223
+ try {
224
+ const formattedLocations = locations.map((loc) => ({
225
+ lat: loc.latitude,
226
+ lng: loc.longitude,
227
+ }));
228
+ const response = await this.client.elevation({
229
+ params: {
230
+ locations: formattedLocations,
231
+ key: process.env.GOOGLE_MAPS_API_KEY || "",
232
+ },
233
+ });
234
+ const result = response.data;
235
+ if (result.status !== "OK") {
236
+ throw new Error(`海拔數據獲取失敗: ${result.status}`);
237
+ }
238
+ return result.results.map((item, index) => ({
239
+ elevation: item.elevation,
240
+ location: formattedLocations[index],
241
+ }));
242
+ }
243
+ catch (error) {
244
+ console.error("Error in getElevation:", error);
245
+ throw new Error("獲取海拔數據時發生錯誤");
246
+ }
247
+ }
248
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cablate/mcp-google-map",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "build": "esbuild src/index.ts --bundle --platform=node --outfile=dist/index.cjs --external:pdfreader --external:jsdom --external:mammoth --external:csv-parse --external:libreoffice-convert && shx chmod +x dist/index.cjs",