@cablate/mcp-google-map 0.0.5 β†’ 0.0.7

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
@@ -8,6 +8,15 @@
8
8
 
9
9
  A powerful Model Context Protocol (MCP) server providing comprehensive Google Maps API integration with streamable HTTP transport support and LLM processing capabilities.
10
10
 
11
+ ## βœ… Testing Status
12
+
13
+ **This MCP server has been tested and verified to work correctly with:**
14
+ - Claude Desktop
15
+ - Dive Desktop
16
+ - MCP protocol implementations
17
+
18
+ All tools and features are confirmed functional through real-world testing.
19
+
11
20
  ## Features
12
21
 
13
22
  ### πŸ—ΊοΈ Google Maps Integration
@@ -37,46 +46,23 @@ A powerful Model Context Protocol (MCP) server providing comprehensive Google Ma
37
46
 
38
47
  ## Installation
39
48
 
40
- ### Via NPM
49
+ ### 1. via NPM
41
50
 
42
51
  ```bash
43
52
  npm install -g @cablate/mcp-google-map
44
53
  ```
45
54
 
46
- ### Via NPX (Recommended)
55
+ ### 2. Run the Server
47
56
 
48
57
  ```bash
49
- npx @cablate/mcp-google-map
50
- ```
51
58
 
52
- ## Quick Start
59
+ mcp-google-map --port 3000 --apikey "your_api_key_here"
53
60
 
54
- ### 1. Get Google Maps API Key
61
+ # Using short options
62
+ mcp-google-map -p 3000 -k "your_api_key_here"
55
63
 
56
- 1. Create a project in [Google Cloud Console](https://console.cloud.google.com/)
57
- 2. Enable the following APIs:
58
- - Maps JavaScript API
59
- - Places API
60
- - Geocoding API
61
- - Distance Matrix API
62
- - Directions API
63
- - Elevation API
64
- 3. Create an API key
65
- 4. Set up your environment:
66
-
67
- ```bash
68
- export GOOGLE_MAPS_API_KEY="your_api_key_here"
69
- export MCP_SERVER_PORT=3000
70
- ```
71
-
72
- ### 2. Run the Server
73
-
74
- ```bash
75
- # Using NPX (recommended)
76
- npx @cablate/mcp-google-map
77
-
78
- # Or if installed globally
79
- mcp-google-map
64
+ # Show help information
65
+ mcp-google-map --help
80
66
  ```
81
67
 
82
68
  ### 3. Server Endpoints
@@ -84,11 +70,9 @@ mcp-google-map
84
70
  - **Main MCP Endpoint**: `http://localhost:3000/mcp`
85
71
  - **Available Tools**: 8 tools including Google Maps services and echo
86
72
 
87
- ## Configuration
88
-
89
73
  ### Environment Variables
90
74
 
91
- Create a `.env` file in your working directory:
75
+ Alternatively, create a `.env` file in your working directory:
92
76
 
93
77
  ```env
94
78
  # Required
@@ -98,6 +82,8 @@ GOOGLE_MAPS_API_KEY=your_google_maps_api_key_here
98
82
  MCP_SERVER_PORT=3000
99
83
  ```
100
84
 
85
+ **Note**: Command line options take precedence over environment variables.
86
+
101
87
  ## Available Tools
102
88
 
103
89
  The server provides the following tools:
@@ -112,97 +98,6 @@ The server provides the following tools:
112
98
  6. **maps_directions** - Get detailed turn-by-turn navigation directions between two locations
113
99
  7. **maps_elevation** - Get elevation data (height above sea level) for specific geographic locations
114
100
 
115
- ### Utility Tools
116
-
117
- 8. **echo** - Echo back input messages with optional streaming support for testing MCP server functionality
118
-
119
- ## Usage Examples
120
-
121
- ### Search Nearby Places
122
-
123
- ```json
124
- {
125
- "tool": "search_nearby",
126
- "arguments": {
127
- "center": {
128
- "value": "Taipei 101",
129
- "isCoordinates": false
130
- },
131
- "keyword": "restaurant",
132
- "radius": 1000,
133
- "openNow": true,
134
- "minRating": 4.0
135
- }
136
- }
137
- ```
138
-
139
- ### Get Directions
140
-
141
- ```json
142
- {
143
- "tool": "maps_directions",
144
- "arguments": {
145
- "origin": "Taipei Main Station",
146
- "destination": "Taipei 101",
147
- "mode": "walking"
148
- }
149
- }
150
- ```
151
-
152
- ### Geocode Address
153
-
154
- ```json
155
- {
156
- "tool": "maps_geocode",
157
- "arguments": {
158
- "address": "Taipei 101, Taiwan"
159
- }
160
- }
161
- ```
162
-
163
- ## Integration with MCP Clients
164
-
165
- ### [Dive Desktop](https://github.com/OpenAgentPlatform/Dive)
166
-
167
- 1. Click "+ Add MCP Server" in Dive Desktop
168
- 2. Copy and paste the following configuration:
169
-
170
- ```json
171
- {
172
- "mcpServers": {
173
- "google-map": {
174
- "command": "npx",
175
- "args": ["-y", "@cablate/mcp-google-map"],
176
- "env": {
177
- "GOOGLE_MAPS_API_KEY": "your_api_key",
178
- "MCP_SERVER_PORT": "3000"
179
- },
180
- "enabled": true
181
- }
182
- }
183
- }
184
- ```
185
-
186
- 3. Click "Save" to complete the installation
187
-
188
- ### Claude Desktop
189
-
190
- Add to your Claude Desktop configuration:
191
-
192
- ```json
193
- {
194
- "mcpServers": {
195
- "google-map": {
196
- "command": "npx",
197
- "args": ["@cablate/mcp-google-map"],
198
- "env": {
199
- "GOOGLE_MAPS_API_KEY": "your_api_key"
200
- }
201
- }
202
- }
203
- }
204
- ```
205
-
206
101
  ## Development
207
102
 
208
103
  ### Local Development
@@ -268,26 +163,6 @@ src/
268
163
  - Input validation using Zod schemas
269
164
  - Error handling and logging
270
165
 
271
- ## Troubleshooting
272
-
273
- ### Common Issues
274
-
275
- 1. **API Key Issues**
276
- ```bash
277
- # Check if API key is set
278
- echo $GOOGLE_MAPS_API_KEY
279
- ```
280
-
281
- 2. **Port Conflicts**
282
- ```bash
283
- # Change port in .env file
284
- MCP_SERVER_PORT=3001
285
- ```
286
-
287
- 3. **API Quotas**
288
- - Check your Google Cloud Console for API usage
289
- - Ensure billing is enabled for your project
290
-
291
166
  ## License
292
167
 
293
168
  MIT
@@ -0,0 +1 @@
1
+ var r={log:(...o)=>{console.error("[INFO]",...o)},error:(...o)=>{console.error("[ERROR]",...o)}};export{r as a};
package/dist/cli.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- declare function startServer(): Promise<void>;
2
+ declare function startServer(port?: number, apiKey?: string): Promise<void>;
3
3
 
4
4
  export { startServer };
package/dist/cli.js CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import{b as d}from"./chunk-U2CGP7BJ.js";import{config as Ie}from"dotenv";import{resolve as Re}from"path";import{z as T}from"zod";var $="echo",H="Echo back the input message with optional streaming support for testing MCP server functionality",L={message:T.string().describe("The message to echo back"),stream:T.boolean().optional().describe("Whether to stream the response as chunks (default: false)")};async function z({message:o,stream:e=!1}){try{return e?{content:o.split(" ").map((s,n)=>({type:"text",text:n===0?s:` ${s}`}))}:{content:[{type:"text",text:`Echo: ${o}`}]}}catch(r){return{isError:!0,content:[{type:"text",text:`Echo error: ${r instanceof Error?r.message:JSON.stringify(r)}`}]}}}var y={NAME:$,DESCRIPTION:H,SCHEMA:L,ACTION:z};import{z as p}from"zod";import{Client as k,Language as K}from"@googlemaps/google-maps-services-js";import J from"dotenv";J.config();var x=class{constructor(){this.defaultLanguage=K.zh_TW;if(this.client=new k({}),!process.env.GOOGLE_MAPS_API_KEY)throw new Error("Google Maps API Key is required")}async searchNearbyPlaces(e){let r={location:e.location,radius:e.radius||1e3,keyword:e.keyword,opennow:e.openNow,language:this.defaultLanguage,key:process.env.GOOGLE_MAPS_API_KEY||""};try{let s=(await this.client.placesNearby({params:r})).data.results;return e.minRating&&(s=s.filter(n=>(n.rating||0)>=(e.minRating||0))),s}catch(t){throw console.error("Error in searchNearbyPlaces:",t),new Error("\u641C\u5C0B\u9644\u8FD1\u5730\u9EDE\u6642\u767C\u751F\u932F\u8AA4")}}async getPlaceDetails(e){try{return(await this.client.placeDetails({params:{place_id:e,fields:["name","rating","formatted_address","opening_hours","reviews","geometry","formatted_phone_number","website","price_level","photos"],language:this.defaultLanguage,key:process.env.GOOGLE_MAPS_API_KEY||""}})).data.result}catch(r){throw console.error("Error in getPlaceDetails:",r),new Error("\u7372\u53D6\u5730\u9EDE\u8A73\u7D30\u8CC7\u8A0A\u6642\u767C\u751F\u932F\u8AA4")}}async geocodeAddress(e){try{let r=await this.client.geocode({params:{address:e,key:process.env.GOOGLE_MAPS_API_KEY||"",language:this.defaultLanguage}});if(r.data.results.length===0)throw new Error("\u627E\u4E0D\u5230\u8A72\u5730\u5740\u7684\u4F4D\u7F6E");let t=r.data.results[0],s=t.geometry.location;return{lat:s.lat,lng:s.lng,formatted_address:t.formatted_address,place_id:t.place_id}}catch(r){throw console.error("Error in geocodeAddress:",r),new Error("\u5730\u5740\u8F49\u63DB\u5EA7\u6A19\u6642\u767C\u751F\u932F\u8AA4")}}parseCoordinates(e){let r=e.split(",").map(t=>parseFloat(t.trim()));if(r.length!==2||isNaN(r[0])||isNaN(r[1]))throw new Error("\u7121\u6548\u7684\u5EA7\u6A19\u683C\u5F0F\uFF0C\u8ACB\u4F7F\u7528\u300C\u7DEF\u5EA6,\u7D93\u5EA6\u300D\u683C\u5F0F");return{lat:r[0],lng:r[1]}}async getLocation(e){return e.isCoordinates?this.parseCoordinates(e.value):this.geocodeAddress(e.value)}async geocode(e){try{let r=await this.geocodeAddress(e);return{location:{lat:r.lat,lng:r.lng},formatted_address:r.formatted_address||"",place_id:r.place_id||""}}catch(r){throw console.error("Error in geocode:",r),new Error("\u5730\u5740\u8F49\u63DB\u5EA7\u6A19\u6642\u767C\u751F\u932F\u8AA4")}}async reverseGeocode(e,r){try{let t=await this.client.reverseGeocode({params:{latlng:{lat:e,lng:r},language:this.defaultLanguage,key:process.env.GOOGLE_MAPS_API_KEY||""}});if(t.data.results.length===0)throw new Error("\u627E\u4E0D\u5230\u8A72\u5EA7\u6A19\u7684\u5730\u5740");let s=t.data.results[0];return{formatted_address:s.formatted_address,place_id:s.place_id,address_components:s.address_components}}catch(t){throw console.error("Error in reverseGeocode:",t),new Error("\u5EA7\u6A19\u8F49\u63DB\u5730\u5740\u6642\u767C\u751F\u932F\u8AA4")}}async calculateDistanceMatrix(e,r,t="driving"){try{let n=(await this.client.distancematrix({params:{origins:e,destinations:r,mode:t,language:this.defaultLanguage,key:process.env.GOOGLE_MAPS_API_KEY||""}})).data;if(n.status!=="OK")throw new Error(`\u8DDD\u96E2\u77E9\u9663\u8A08\u7B97\u5931\u6557: ${n.status}`);let a=[],c=[];return n.rows.forEach(u=>{let i=[],g=[];u.elements.forEach(m=>{m.status==="OK"?(i.push({value:m.distance.value,text:m.distance.text}),g.push({value:m.duration.value,text:m.duration.text})):(i.push(null),g.push(null))}),a.push(i),c.push(g)}),{distances:a,durations:c,origin_addresses:n.origin_addresses,destination_addresses:n.destination_addresses}}catch(s){throw console.error("Error in calculateDistanceMatrix:",s),new Error("\u8A08\u7B97\u8DDD\u96E2\u77E9\u9663\u6642\u767C\u751F\u932F\u8AA4")}}async getDirections(e,r,t="driving",s,n){try{let a;n&&(a=Math.floor(n.getTime()/1e3));let c;a||(s instanceof Date?c=Math.floor(s.getTime()/1e3):s?c=s:c="now");let i=(await this.client.directions({params:{origin:e,destination:r,mode:t,language:this.defaultLanguage,key:process.env.GOOGLE_MAPS_API_KEY||"",arrival_time:a,departure_time:c}})).data;if(i.status!=="OK")throw new Error(`\u8DEF\u7DDA\u6307\u5F15\u7372\u53D6\u5931\u6557: ${i.status} (arrival_time: ${a}, departure_time: ${c})`);if(i.routes.length===0)throw new Error("\u627E\u4E0D\u5230\u8DEF\u7DDA");let g=i.routes[0],m=g.legs[0],C=f=>{if(!f||typeof f.value!="number")return"";let G=new Date(f.value*1e3),O={year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1};return f.time_zone&&typeof f.time_zone=="string"&&(O.timeZone=f.time_zone),G.toLocaleString(this.defaultLanguage.toString(),O)};return{routes:i.routes,summary:g.summary,total_distance:{value:m.distance.value,text:m.distance.text},total_duration:{value:m.duration.value,text:m.duration.text},arrival_time:C(m.arrival_time),departure_time:C(m.departure_time)}}catch(a){throw console.error("Error in getDirections:",a),new Error("\u7372\u53D6\u8DEF\u7DDA\u6307\u5F15\u6642\u767C\u751F\u932F\u8AA4"+a)}}async getElevation(e){try{let r=e.map(n=>({lat:n.latitude,lng:n.longitude})),s=(await this.client.elevation({params:{locations:r,key:process.env.GOOGLE_MAPS_API_KEY||""}})).data;if(s.status!=="OK")throw new Error(`\u6D77\u62D4\u6578\u64DA\u7372\u53D6\u5931\u6557: ${s.status}`);return s.results.map((n,a)=>({elevation:n.elevation,location:r[a]}))}catch(r){throw console.error("Error in getElevation:",r),new Error("\u7372\u53D6\u6D77\u62D4\u6578\u64DA\u6642\u767C\u751F\u932F\u8AA4")}}};var l=class{constructor(){this.mapsTools=new x}async searchNearby(e){try{let r=await this.mapsTools.getLocation(e.center);console.error(r);let t=await this.mapsTools.searchNearbyPlaces({location:r,keyword:e.keyword,radius:e.radius,openNow:e.openNow,minRating:e.minRating});return{location:r,success:!0,data:t.map(s=>({name:s.name,place_id:s.place_id,address:s.formatted_address,location:s.geometry.location,rating:s.rating,total_ratings:s.user_ratings_total,open_now:s.opening_hours?.open_now}))}}catch(r){return{success:!1,error:r instanceof Error?r.message:"\u641C\u5C0B\u6642\u767C\u751F\u932F\u8AA4"}}}async getPlaceDetails(e){try{let r=await this.mapsTools.getPlaceDetails(e);return{success:!0,data:{name:r.name,address:r.formatted_address,location:r.geometry?.location,rating:r.rating,total_ratings:r.user_ratings_total,open_now:r.opening_hours?.open_now,phone:r.formatted_phone_number,website:r.website,price_level:r.price_level,reviews:r.reviews?.map(t=>({rating:t.rating,text:t.text,time:t.time,author_name:t.author_name}))}}}catch(r){return{success:!1,error:r instanceof Error?r.message:"\u7372\u53D6\u8A73\u7D30\u8CC7\u8A0A\u6642\u767C\u751F\u932F\u8AA4"}}}async geocode(e){try{return{success:!0,data:await this.mapsTools.geocode(e)}}catch(r){return{success:!1,error:r instanceof Error?r.message:"\u5730\u5740\u8F49\u63DB\u5EA7\u6A19\u6642\u767C\u751F\u932F\u8AA4"}}}async reverseGeocode(e,r){try{return{success:!0,data:await this.mapsTools.reverseGeocode(e,r)}}catch(t){return{success:!1,error:t instanceof Error?t.message:"\u5EA7\u6A19\u8F49\u63DB\u5730\u5740\u6642\u767C\u751F\u932F\u8AA4"}}}async calculateDistanceMatrix(e,r,t="driving"){try{return{success:!0,data:await this.mapsTools.calculateDistanceMatrix(e,r,t)}}catch(s){return{success:!1,error:s instanceof Error?s.message:"\u8A08\u7B97\u8DDD\u96E2\u77E9\u9663\u6642\u767C\u751F\u932F\u8AA4"}}}async getDirections(e,r,t="driving",s,n){try{let a=s?new Date(s):new Date,c=n?new Date(n):void 0;return{success:!0,data:await this.mapsTools.getDirections(e,r,t,a,c)}}catch(a){return{success:!1,error:a instanceof Error?a.message:"\u7372\u53D6\u8DEF\u7DDA\u6307\u5F15\u6642\u767C\u751F\u932F\u8AA4"}}}async getElevation(e){try{return{success:!0,data:await this.mapsTools.getElevation(e)}}catch(r){return{success:!1,error:r instanceof Error?r.message:"\u7372\u53D6\u6D77\u62D4\u6578\u64DA\u6642\u767C\u751F\u932F\u8AA4"}}}};var j="search_nearby",Y="Search for nearby places based on location, with optional filtering by keywords, distance, rating, and operating hours",V={center:p.object({value:p.string().describe("Address, landmark name, or coordinates (coordinate format: lat,lng)"),isCoordinates:p.boolean().default(!1).describe("Whether the value is coordinates")}).describe("Search center point"),keyword:p.string().optional().describe("Search keyword (e.g., restaurant, cafe, hotel)"),radius:p.number().default(1e3).describe("Search radius in meters"),openNow:p.boolean().default(!1).describe("Only show places that are currently open"),minRating:p.number().min(0).max(5).optional().describe("Minimum rating requirement (0-5)")},Z=new l;async function q(o){try{let e=await Z.searchNearby(o);return e.success?{content:[{type:"text",text:`location: ${JSON.stringify(e.location,null,2)}
3
- `+JSON.stringify(e.data,null,2)}],isError:!1}:{content:[{type:"text",text:e.error||"\u641C\u5C0B\u5931\u6557"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`\u641C\u5C0B\u9644\u8FD1\u5730\u9EDE\u932F\u8AA4: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var h={NAME:j,DESCRIPTION:Y,SCHEMA:V,ACTION:q};import{z as W}from"zod";var F="get_place_details",U="Get detailed information about a specific place including contact details, reviews, ratings, and operating hours",B={placeId:W.string().describe("Google Maps place ID")},Q=new l;async function X(o){try{let e=await Q.getPlaceDetails(o.placeId);return e.success?{content:[{type:"text",text:JSON.stringify(e.data,null,2)}],isError:!1}:{content:[{type:"text",text:e.error||"\u7372\u53D6\u8A73\u7D30\u8CC7\u8A0A\u5931\u6557"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`\u7372\u53D6\u5730\u9EDE\u8A73\u7D30\u8CC7\u8A0A\u932F\u8AA4: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var v={NAME:F,DESCRIPTION:U,SCHEMA:B,ACTION:X};import{z as ee}from"zod";var re="maps_geocode",te="Convert addresses or place names to geographic coordinates (latitude and longitude)",se={address:ee.string().describe("Address or place name to convert to coordinates")},oe=new l;async function ne(o){try{let e=await oe.geocode(o.address);return e.success?{content:[{type:"text",text:JSON.stringify(e.data,null,2)}],isError:!1}:{content:[{type:"text",text:e.error||"\u5730\u5740\u8F49\u63DB\u5EA7\u6A19\u5931\u6557"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`\u5730\u5740\u8F49\u63DB\u5EA7\u6A19\u932F\u8AA4: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var E={NAME:re,DESCRIPTION:te,SCHEMA:se,ACTION:ne};import{z as I}from"zod";var ae="maps_reverse_geocode",ie="Convert geographic coordinates (latitude and longitude) to a human-readable address",ce={latitude:I.number().describe("Latitude coordinate"),longitude:I.number().describe("Longitude coordinate")},le=new l;async function de(o){try{let e=await le.reverseGeocode(o.latitude,o.longitude);return e.success?{content:[{type:"text",text:JSON.stringify(e.data,null,2)}],isError:!1}:{content:[{type:"text",text:e.error||"\u5EA7\u6A19\u8F49\u63DB\u5730\u5740\u5931\u6557"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`\u5EA7\u6A19\u8F49\u63DB\u5730\u5740\u932F\u8AA4: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var b={NAME:ae,DESCRIPTION:ie,SCHEMA:ce,ACTION:de};import{z as P}from"zod";var ue="maps_distance_matrix",me="Calculate travel distances and durations between multiple origins and destinations for different travel modes",ge={origins:P.array(P.string()).describe("List of origin addresses or coordinates"),destinations:P.array(P.string()).describe("List of destination addresses or coordinates"),mode:P.enum(["driving","walking","bicycling","transit"]).default("driving").describe("Travel mode for calculation")},pe=new l;async function fe(o){try{let e=await pe.calculateDistanceMatrix(o.origins,o.destinations,o.mode);return e.success?{content:[{type:"text",text:JSON.stringify(e.data,null,2)}],isError:!1}:{content:[{type:"text",text:e.error||"\u8A08\u7B97\u8DDD\u96E2\u77E9\u9663\u5931\u6557"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`\u8A08\u7B97\u8DDD\u96E2\u77E9\u9663\u932F\u8AA4: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var _={NAME:ue,DESCRIPTION:me,SCHEMA:ge,ACTION:fe};import{z as S}from"zod";var ye="maps_directions",he="Get detailed turn-by-turn navigation directions between two locations with route information",ve={origin:S.string().describe("Starting point address or coordinates"),destination:S.string().describe("Destination address or coordinates"),mode:S.enum(["driving","walking","bicycling","transit"]).default("driving").describe("Travel mode for directions"),departure_time:S.string().optional().describe("Departure time (ISO string format)"),arrival_time:S.string().optional().describe("Arrival time (ISO string format)")},Ee=new l;async function be(o){try{let e=await Ee.getDirections(o.origin,o.destination,o.mode,o.departure_time,o.arrival_time);return e.success?{content:[{type:"text",text:JSON.stringify(e.data,null,2)}],isError:!1}:{content:[{type:"text",text:e.error||"\u7372\u53D6\u8DEF\u7DDA\u6307\u5F15\u5931\u6557"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`\u7372\u53D6\u8DEF\u7DDA\u6307\u5F15\u932F\u8AA4: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var w={NAME:ye,DESCRIPTION:he,SCHEMA:ve,ACTION:be};import{z as M}from"zod";var Pe="maps_elevation",_e="Get elevation data (height above sea level) for specific geographic locations",Se={locations:M.array(M.object({latitude:M.number().describe("Latitude coordinate"),longitude:M.number().describe("Longitude coordinate")})).describe("List of locations to get elevation data for")},we=new l;async function Ne(o){try{let e=await we.getElevation(o.locations);return e.success?{content:[{type:"text",text:JSON.stringify(e.data,null,2)}],isError:!1}:{content:[{type:"text",text:e.error||"\u7372\u53D6\u6D77\u62D4\u6578\u64DA\u5931\u6557"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`\u7372\u53D6\u6D77\u62D4\u6578\u64DA\u932F\u8AA4: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var N={NAME:Pe,DESCRIPTION:_e,SCHEMA:Se,ACTION:Ne};var xe=[{name:"MCP-Server",portEnvVar:"MCP_SERVER_PORT",tools:[{name:y.NAME,description:y.DESCRIPTION,schema:y.SCHEMA,action:o=>y.ACTION(o)},{name:h.NAME,description:h.DESCRIPTION,schema:h.SCHEMA,action:o=>h.ACTION(o)},{name:v.NAME,description:v.DESCRIPTION,schema:v.SCHEMA,action:o=>v.ACTION(o)},{name:E.NAME,description:E.DESCRIPTION,schema:E.SCHEMA,action:o=>E.ACTION(o)},{name:b.NAME,description:b.DESCRIPTION,schema:b.SCHEMA,action:o=>b.ACTION(o)},{name:_.NAME,description:_.DESCRIPTION,schema:_.SCHEMA,action:o=>_.ACTION(o)},{name:w.NAME,description:w.DESCRIPTION,schema:w.SCHEMA,action:o=>w.ACTION(o)},{name:N.NAME,description:N.DESCRIPTION,schema:N.SCHEMA,action:o=>N.ACTION(o)}]}],R=xe;import{McpServer as Me}from"@modelcontextprotocol/sdk/server/mcp.js";import{StreamableHTTPServerTransport as Ae}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import{isInitializeRequest as Ce}from"@modelcontextprotocol/sdk/types.js";import D from"express";import{randomUUID as Oe}from"node:crypto";var Te="0.0.1",A=class{constructor(e,r){this.transports={};this.httpServer=null;this.serverName=e,this.server=new Me({name:this.serverName,version:Te},{capabilities:{logging:{},tools:{}}}),this.registerTools(r)}registerTools(e){e.forEach(r=>{this.server.tool(r.name,r.description,r.schema,async t=>r.action(t))})}async connect(e){await this.server.connect(e),d.log=(...t)=>{console.error("[INFO]",...t)},d.error=(...t)=>{console.error("[ERROR]",...t)};let r=process.stdout.write.bind(process.stdout);process.stdout.write=(t,s,n)=>typeof t=="string"&&!t.startsWith("{")?!0:r(t,s,n),d.log(`${this.serverName} connected and ready to process requests`)}async startHttpServer(e){let r=D();r.use(D.json()),r.post("/mcp",async(a,c)=>{let u=a.headers["mcp-session-id"],i;if(u&&this.transports[u])i=this.transports[u];else if(!u&&Ce(a.body))i=new Ae({sessionIdGenerator:()=>Oe(),onsessioninitialized:g=>{this.transports[g]=i,console.log(`[${this.serverName}] New session initialized: ${g}`)}}),i.onclose=()=>{i.sessionId&&(delete this.transports[i.sessionId],console.log(`[${this.serverName}] Session closed: ${i.sessionId}`))},await this.server.connect(i);else{c.status(400).json({jsonrpc:"2.0",error:{code:-32e3,message:"Bad Request: No valid session ID provided"},id:null});return}await i.handleRequest(a,c,a.body)});let t=async(a,c)=>{let u=a.headers["mcp-session-id"];if(!u||!this.transports[u]){c.status(400).send("Invalid or missing session ID");return}await this.transports[u].handleRequest(a,c)};r.get("/mcp",t),r.delete("/mcp",t);let s=console.log,n=console.error;d.log=s,d.error=n,this.httpServer=r.listen(e,()=>{d.log(`[${this.serverName}] HTTP server listening on port ${e}`),d.log(`[${this.serverName}] MCP endpoint available at http://localhost:${e}/mcp`)})}async stopHttpServer(){if(!this.httpServer){d.error(`[${this.serverName}] HTTP server is not running or already stopped.`);return}return new Promise((e,r)=>{this.httpServer.close(t=>{if(t){d.error(`[${this.serverName}] Error stopping HTTP server:`,t),r(t);return}d.log(`[${this.serverName}] HTTP server stopped.`),this.httpServer=null;let s=Object.values(this.transports).map(n=>(n.sessionId&&delete this.transports[n.sessionId],Promise.resolve()));Promise.all(s).then(()=>{d.log(`[${this.serverName}] All transports closed.`),e()}).catch(n=>{d.error(`[${this.serverName}] Error during bulk transport closing:`,n),r(n)})})})}};Ie({path:Re(process.cwd(),".env")});async function De(){console.log("\u{1F680} Starting Google Maps MCP Server..."),console.log("\u{1F4CD} Available tools: search_nearby, get_place_details, maps_geocode, maps_reverse_geocode, maps_distance_matrix, maps_directions, maps_elevation, echo"),console.log("");let o=R.map(async e=>{let r=process.env[e.portEnvVar];if(!r){console.warn(`\u26A0\uFE0F [${e.name}] Port environment variable ${e.portEnvVar} not set.`),console.log(`\u{1F4A1} Please set ${e.portEnvVar} in your .env file or environment variables.`),console.log(` Example: ${e.portEnvVar}=3000`);return}let t=Number(r);if(isNaN(t)||t<=0){console.error(`\u274C [${e.name}] Invalid port number "${r}" defined in ${e.portEnvVar}.`);return}try{let s=new A(e.name,e.tools);console.log(`\u{1F527} [${e.name}] Initializing MCP Server in HTTP mode on port ${t}...`),await s.startHttpServer(t),console.log(`\u2705 [${e.name}] MCP Server started successfully!`),console.log(` \u{1F310} Endpoint: http://localhost:${t}/mcp`),console.log(` \u{1F4DA} Tools: ${e.tools.length} available`)}catch(s){console.error(`\u274C [${e.name}] Failed to start MCP Server on port ${t}:`,s)}});await Promise.allSettled(o),console.log(""),console.log("\u{1F389} Server initialization completed!"),console.log("\u{1F4A1} Need help? Check the README.md for configuration details.")}process.argv[1]&&(process.argv[1].endsWith("cli.ts")||process.argv[1].endsWith("cli.js"))&&(console.log("\u{1F5FA}\uFE0F Google Maps MCP Server"),console.log(" A Model Context Protocol server for Google Maps services"),console.log(""),process.env.GOOGLE_MAPS_API_KEY||(console.log("\u26A0\uFE0F Google Maps API Key not found!"),console.log(" Please set GOOGLE_MAPS_API_KEY in your .env file"),console.log(" Example: GOOGLE_MAPS_API_KEY=your_api_key_here"),console.log("")),De().catch(o=>{console.error("\u274C Failed to start server:",o),process.exit(1)}));export{De as startServer};
2
+ import{a as o}from"./chunk-W2DM2HDK.js";import{config as Ne}from"dotenv";import{resolve as xe}from"path";import Ae from"yargs";import{hideBin as Oe}from"yargs/helpers";import{z as m}from"zod";import{Client as D,Language as G}from"@googlemaps/google-maps-services-js";import $ from"dotenv";$.config();var M=class{constructor(){this.defaultLanguage=G.zh_TW;if(this.client=new D({}),!process.env.GOOGLE_MAPS_API_KEY)throw new Error("Google Maps API Key is required")}async searchNearbyPlaces(e){let r={location:e.location,radius:e.radius||1e3,keyword:e.keyword,opennow:e.openNow,language:this.defaultLanguage,key:process.env.GOOGLE_MAPS_API_KEY||""};try{let s=(await this.client.placesNearby({params:r})).data.results;return e.minRating&&(s=s.filter(n=>(n.rating||0)>=(e.minRating||0))),s}catch(t){throw o.error("Error in searchNearbyPlaces:",t),new Error("\u641C\u5C0B\u9644\u8FD1\u5730\u9EDE\u6642\u767C\u751F\u932F\u8AA4")}}async getPlaceDetails(e){try{return(await this.client.placeDetails({params:{place_id:e,fields:["name","rating","formatted_address","opening_hours","reviews","geometry","formatted_phone_number","website","price_level","photos"],language:this.defaultLanguage,key:process.env.GOOGLE_MAPS_API_KEY||""}})).data.result}catch(r){throw o.error("Error in getPlaceDetails:",r),new Error("\u7372\u53D6\u5730\u9EDE\u8A73\u7D30\u8CC7\u8A0A\u6642\u767C\u751F\u932F\u8AA4")}}async geocodeAddress(e){try{let r=await this.client.geocode({params:{address:e,key:process.env.GOOGLE_MAPS_API_KEY||"",language:this.defaultLanguage}});if(r.data.results.length===0)throw new Error("\u627E\u4E0D\u5230\u8A72\u5730\u5740\u7684\u4F4D\u7F6E");let t=r.data.results[0],s=t.geometry.location;return{lat:s.lat,lng:s.lng,formatted_address:t.formatted_address,place_id:t.place_id}}catch(r){throw o.error("Error in geocodeAddress:",r),new Error("\u5730\u5740\u8F49\u63DB\u5EA7\u6A19\u6642\u767C\u751F\u932F\u8AA4")}}parseCoordinates(e){let r=e.split(",").map(t=>parseFloat(t.trim()));if(r.length!==2||isNaN(r[0])||isNaN(r[1]))throw new Error("\u7121\u6548\u7684\u5EA7\u6A19\u683C\u5F0F\uFF0C\u8ACB\u4F7F\u7528\u300C\u7DEF\u5EA6,\u7D93\u5EA6\u300D\u683C\u5F0F");return{lat:r[0],lng:r[1]}}async getLocation(e){return e.isCoordinates?this.parseCoordinates(e.value):this.geocodeAddress(e.value)}async geocode(e){try{let r=await this.geocodeAddress(e);return{location:{lat:r.lat,lng:r.lng},formatted_address:r.formatted_address||"",place_id:r.place_id||""}}catch(r){throw o.error("Error in geocode:",r),new Error("\u5730\u5740\u8F49\u63DB\u5EA7\u6A19\u6642\u767C\u751F\u932F\u8AA4")}}async reverseGeocode(e,r){try{let t=await this.client.reverseGeocode({params:{latlng:{lat:e,lng:r},language:this.defaultLanguage,key:process.env.GOOGLE_MAPS_API_KEY||""}});if(t.data.results.length===0)throw new Error("\u627E\u4E0D\u5230\u8A72\u5EA7\u6A19\u7684\u5730\u5740");let s=t.data.results[0];return{formatted_address:s.formatted_address,place_id:s.place_id,address_components:s.address_components}}catch(t){throw o.error("Error in reverseGeocode:",t),new Error("\u5EA7\u6A19\u8F49\u63DB\u5730\u5740\u6642\u767C\u751F\u932F\u8AA4")}}async calculateDistanceMatrix(e,r,t="driving"){try{let n=(await this.client.distancematrix({params:{origins:e,destinations:r,mode:t,language:this.defaultLanguage,key:process.env.GOOGLE_MAPS_API_KEY||""}})).data;if(n.status!=="OK")throw new Error(`\u8DDD\u96E2\u77E9\u9663\u8A08\u7B97\u5931\u6557: ${n.status}`);let i=[],c=[];return n.rows.forEach(p=>{let u=[],g=[];p.elements.forEach(d=>{d.status==="OK"?(u.push({value:d.distance.value,text:d.distance.text}),g.push({value:d.duration.value,text:d.duration.text})):(u.push(null),g.push(null))}),i.push(u),c.push(g)}),{distances:i,durations:c,origin_addresses:n.origin_addresses,destination_addresses:n.destination_addresses}}catch(s){throw o.error("Error in calculateDistanceMatrix:",s),new Error("\u8A08\u7B97\u8DDD\u96E2\u77E9\u9663\u6642\u767C\u751F\u932F\u8AA4")}}async getDirections(e,r,t="driving",s,n){try{let i;n&&(i=Math.floor(n.getTime()/1e3));let c;i||(s instanceof Date?c=Math.floor(s.getTime()/1e3):s?c=s:c="now");let u=(await this.client.directions({params:{origin:e,destination:r,mode:t,language:this.defaultLanguage,key:process.env.GOOGLE_MAPS_API_KEY||"",arrival_time:i,departure_time:c}})).data;if(u.status!=="OK")throw new Error(`\u8DEF\u7DDA\u6307\u5F15\u7372\u53D6\u5931\u6557: ${u.status} (arrival_time: ${i}, departure_time: ${c})`);if(u.routes.length===0)throw new Error("\u627E\u4E0D\u5230\u8DEF\u7DDA");let g=u.routes[0],d=g.legs[0],A=f=>{if(!f||typeof f.value!="number")return"";let R=new Date(f.value*1e3),O={year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1};return f.time_zone&&typeof f.time_zone=="string"&&(O.timeZone=f.time_zone),R.toLocaleString(this.defaultLanguage.toString(),O)};return{routes:u.routes,summary:g.summary,total_distance:{value:d.distance.value,text:d.distance.text},total_duration:{value:d.duration.value,text:d.duration.text},arrival_time:A(d.arrival_time),departure_time:A(d.departure_time)}}catch(i){throw o.error("Error in getDirections:",i),new Error("\u7372\u53D6\u8DEF\u7DDA\u6307\u5F15\u6642\u767C\u751F\u932F\u8AA4"+i)}}async getElevation(e){try{let r=e.map(n=>({lat:n.latitude,lng:n.longitude})),s=(await this.client.elevation({params:{locations:r,key:process.env.GOOGLE_MAPS_API_KEY||""}})).data;if(s.status!=="OK")throw new Error(`\u6D77\u62D4\u6578\u64DA\u7372\u53D6\u5931\u6557: ${s.status}`);return s.results.map((n,i)=>({elevation:n.elevation,location:r[i]}))}catch(r){throw o.error("Error in getElevation:",r),new Error("\u7372\u53D6\u6D77\u62D4\u6578\u64DA\u6642\u767C\u751F\u932F\u8AA4")}}};var l=class{constructor(){this.mapsTools=new M}async searchNearby(e){try{let r=await this.mapsTools.getLocation(e.center);o.error(r);let t=await this.mapsTools.searchNearbyPlaces({location:r,keyword:e.keyword,radius:e.radius,openNow:e.openNow,minRating:e.minRating});return{location:r,success:!0,data:t.map(s=>({name:s.name,place_id:s.place_id,address:s.formatted_address,location:s.geometry.location,rating:s.rating,total_ratings:s.user_ratings_total,open_now:s.opening_hours?.open_now}))}}catch(r){return{success:!1,error:r instanceof Error?r.message:"\u641C\u5C0B\u6642\u767C\u751F\u932F\u8AA4"}}}async getPlaceDetails(e){try{let r=await this.mapsTools.getPlaceDetails(e);return{success:!0,data:{name:r.name,address:r.formatted_address,location:r.geometry?.location,rating:r.rating,total_ratings:r.user_ratings_total,open_now:r.opening_hours?.open_now,phone:r.formatted_phone_number,website:r.website,price_level:r.price_level,reviews:r.reviews?.map(t=>({rating:t.rating,text:t.text,time:t.time,author_name:t.author_name}))}}}catch(r){return{success:!1,error:r instanceof Error?r.message:"\u7372\u53D6\u8A73\u7D30\u8CC7\u8A0A\u6642\u767C\u751F\u932F\u8AA4"}}}async geocode(e){try{return{success:!0,data:await this.mapsTools.geocode(e)}}catch(r){return{success:!1,error:r instanceof Error?r.message:"\u5730\u5740\u8F49\u63DB\u5EA7\u6A19\u6642\u767C\u751F\u932F\u8AA4"}}}async reverseGeocode(e,r){try{return{success:!0,data:await this.mapsTools.reverseGeocode(e,r)}}catch(t){return{success:!1,error:t instanceof Error?t.message:"\u5EA7\u6A19\u8F49\u63DB\u5730\u5740\u6642\u767C\u751F\u932F\u8AA4"}}}async calculateDistanceMatrix(e,r,t="driving"){try{return{success:!0,data:await this.mapsTools.calculateDistanceMatrix(e,r,t)}}catch(s){return{success:!1,error:s instanceof Error?s.message:"\u8A08\u7B97\u8DDD\u96E2\u77E9\u9663\u6642\u767C\u751F\u932F\u8AA4"}}}async getDirections(e,r,t="driving",s,n){try{let i=s?new Date(s):new Date,c=n?new Date(n):void 0;return{success:!0,data:await this.mapsTools.getDirections(e,r,t,i,c)}}catch(i){return{success:!1,error:i instanceof Error?i.message:"\u7372\u53D6\u8DEF\u7DDA\u6307\u5F15\u6642\u767C\u751F\u932F\u8AA4"}}}async getElevation(e){try{return{success:!0,data:await this.mapsTools.getElevation(e)}}catch(r){return{success:!1,error:r instanceof Error?r.message:"\u7372\u53D6\u6D77\u62D4\u6578\u64DA\u6642\u767C\u751F\u932F\u8AA4"}}}};var k="search_nearby",L="Search for nearby places based on location, with optional filtering by keywords, distance, rating, and operating hours",H={center:m.object({value:m.string().describe("Address, landmark name, or coordinates (coordinate format: lat,lng)"),isCoordinates:m.boolean().default(!1).describe("Whether the value is coordinates")}).describe("Search center point"),keyword:m.string().optional().describe("Search keyword (e.g., restaurant, cafe, hotel)"),radius:m.number().default(1e3).describe("Search radius in meters"),openNow:m.boolean().default(!1).describe("Only show places that are currently open"),minRating:m.number().min(0).max(5).optional().describe("Minimum rating requirement (0-5)")},z=new l;async function K(a){try{let e=await z.searchNearby(a);return e.success?{content:[{type:"text",text:`location: ${JSON.stringify(e.location,null,2)}
3
+ `+JSON.stringify(e.data,null,2)}],isError:!1}:{content:[{type:"text",text:e.error||"\u641C\u5C0B\u5931\u6557"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`\u641C\u5C0B\u9644\u8FD1\u5730\u9EDE\u932F\u8AA4: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var y={NAME:k,DESCRIPTION:L,SCHEMA:H,ACTION:K};import{z as J}from"zod";var j="get_place_details",V="Get detailed information about a specific place including contact details, reviews, ratings, and operating hours",Y={placeId:J.string().describe("Google Maps place ID")},Z=new l;async function q(a){try{let e=await Z.getPlaceDetails(a.placeId);return e.success?{content:[{type:"text",text:JSON.stringify(e.data,null,2)}],isError:!1}:{content:[{type:"text",text:e.error||"\u7372\u53D6\u8A73\u7D30\u8CC7\u8A0A\u5931\u6557"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`\u7372\u53D6\u5730\u9EDE\u8A73\u7D30\u8CC7\u8A0A\u932F\u8AA4: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var h={NAME:j,DESCRIPTION:V,SCHEMA:Y,ACTION:q};import{z as W}from"zod";var F="maps_geocode",B="Convert addresses or place names to geographic coordinates (latitude and longitude)",U={address:W.string().describe("Address or place name to convert to coordinates")},Q=new l;async function X(a){try{let e=await Q.geocode(a.address);return e.success?{content:[{type:"text",text:JSON.stringify(e.data,null,2)}],isError:!1}:{content:[{type:"text",text:e.error||"\u5730\u5740\u8F49\u63DB\u5EA7\u6A19\u5931\u6557"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`\u5730\u5740\u8F49\u63DB\u5EA7\u6A19\u932F\u8AA4: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var v={NAME:F,DESCRIPTION:B,SCHEMA:U,ACTION:X};import{z as T}from"zod";var ee="maps_reverse_geocode",re="Convert geographic coordinates (latitude and longitude) to a human-readable address",te={latitude:T.number().describe("Latitude coordinate"),longitude:T.number().describe("Longitude coordinate")},se=new l;async function oe(a){try{let e=await se.reverseGeocode(a.latitude,a.longitude);return e.success?{content:[{type:"text",text:JSON.stringify(e.data,null,2)}],isError:!1}:{content:[{type:"text",text:e.error||"\u5EA7\u6A19\u8F49\u63DB\u5730\u5740\u5931\u6557"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`\u5EA7\u6A19\u8F49\u63DB\u5730\u5740\u932F\u8AA4: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var E={NAME:ee,DESCRIPTION:re,SCHEMA:te,ACTION:oe};import{z as P}from"zod";var ae="maps_distance_matrix",ne="Calculate travel distances and durations between multiple origins and destinations for different travel modes",ie={origins:P.array(P.string()).describe("List of origin addresses or coordinates"),destinations:P.array(P.string()).describe("List of destination addresses or coordinates"),mode:P.enum(["driving","walking","bicycling","transit"]).default("driving").describe("Travel mode for calculation")},ce=new l;async function le(a){try{let e=await ce.calculateDistanceMatrix(a.origins,a.destinations,a.mode);return e.success?{content:[{type:"text",text:JSON.stringify(e.data,null,2)}],isError:!1}:{content:[{type:"text",text:e.error||"\u8A08\u7B97\u8DDD\u96E2\u77E9\u9663\u5931\u6557"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`\u8A08\u7B97\u8DDD\u96E2\u77E9\u9663\u932F\u8AA4: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var _={NAME:ae,DESCRIPTION:ne,SCHEMA:ie,ACTION:le};import{z as b}from"zod";var de="maps_directions",ue="Get detailed turn-by-turn navigation directions between two locations with route information",me={origin:b.string().describe("Starting point address or coordinates"),destination:b.string().describe("Destination address or coordinates"),mode:b.enum(["driving","walking","bicycling","transit"]).default("driving").describe("Travel mode for directions"),departure_time:b.string().optional().describe("Departure time (ISO string format)"),arrival_time:b.string().optional().describe("Arrival time (ISO string format)")},pe=new l;async function ge(a){try{let e=await pe.getDirections(a.origin,a.destination,a.mode,a.departure_time,a.arrival_time);return e.success?{content:[{type:"text",text:JSON.stringify(e.data,null,2)}],isError:!1}:{content:[{type:"text",text:e.error||"\u7372\u53D6\u8DEF\u7DDA\u6307\u5F15\u5931\u6557"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`\u7372\u53D6\u8DEF\u7DDA\u6307\u5F15\u932F\u8AA4: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var S={NAME:de,DESCRIPTION:ue,SCHEMA:me,ACTION:ge};import{z as N}from"zod";var fe="maps_elevation",ye="Get elevation data (height above sea level) for specific geographic locations",he={locations:N.array(N.object({latitude:N.number().describe("Latitude coordinate"),longitude:N.number().describe("Longitude coordinate")})).describe("List of locations to get elevation data for")},ve=new l;async function Ee(a){try{let e=await ve.getElevation(a.locations);return e.success?{content:[{type:"text",text:JSON.stringify(e.data,null,2)}],isError:!1}:{content:[{type:"text",text:e.error||"\u7372\u53D6\u6D77\u62D4\u6578\u64DA\u5931\u6557"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`\u7372\u53D6\u6D77\u62D4\u6578\u64DA\u932F\u8AA4: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var w={NAME:fe,DESCRIPTION:ye,SCHEMA:he,ACTION:Ee};var Pe=[{name:"MCP-Server",portEnvVar:"MCP_SERVER_PORT",tools:[{name:y.NAME,description:y.DESCRIPTION,schema:y.SCHEMA,action:a=>y.ACTION(a)},{name:h.NAME,description:h.DESCRIPTION,schema:h.SCHEMA,action:a=>h.ACTION(a)},{name:v.NAME,description:v.DESCRIPTION,schema:v.SCHEMA,action:a=>v.ACTION(a)},{name:E.NAME,description:E.DESCRIPTION,schema:E.SCHEMA,action:a=>E.ACTION(a)},{name:_.NAME,description:_.DESCRIPTION,schema:_.SCHEMA,action:a=>_.ACTION(a)},{name:S.NAME,description:S.DESCRIPTION,schema:S.SCHEMA,action:a=>S.ACTION(a)},{name:w.NAME,description:w.DESCRIPTION,schema:w.SCHEMA,action:a=>w.ACTION(a)}]}],C=Pe;import{McpServer as _e}from"@modelcontextprotocol/sdk/server/mcp.js";import{StreamableHTTPServerTransport as be}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import{isInitializeRequest as Se}from"@modelcontextprotocol/sdk/types.js";import I from"express";import{randomUUID as we}from"node:crypto";var Me="0.0.1",x=class{constructor(e,r){this.transports={};this.httpServer=null;this.serverName=e,this.server=new _e({name:this.serverName,version:Me},{capabilities:{logging:{},tools:{}}}),this.registerTools(r)}registerTools(e){e.forEach(r=>{this.server.tool(r.name,r.description,r.schema,async t=>r.action(t))})}async connect(e){await this.server.connect(e);let r=process.stdout.write.bind(process.stdout);process.stdout.write=(t,s,n)=>typeof t=="string"&&!t.startsWith("{")?!0:r(t,s,n),o.log(`${this.serverName} connected and ready to process requests`)}async startHttpServer(e){let r=I();r.use(I.json()),r.post("/mcp",async(s,n)=>{let i=s.headers["mcp-session-id"],c;if(i&&this.transports[i])c=this.transports[i];else if(!i&&Se(s.body))c=new be({sessionIdGenerator:()=>we(),onsessioninitialized:p=>{this.transports[p]=c,o.log(`[${this.serverName}] New session initialized: ${p}`)}}),c.onclose=()=>{c.sessionId&&(delete this.transports[c.sessionId],o.log(`[${this.serverName}] Session closed: ${c.sessionId}`))},await this.server.connect(c);else{n.status(400).json({jsonrpc:"2.0",error:{code:-32e3,message:"Bad Request: No valid session ID provided"},id:null});return}await c.handleRequest(s,n,s.body)});let t=async(s,n)=>{let i=s.headers["mcp-session-id"];if(!i||!this.transports[i]){n.status(400).send("Invalid or missing session ID");return}await this.transports[i].handleRequest(s,n)};r.get("/mcp",t),r.delete("/mcp",t),this.httpServer=r.listen(e,()=>{o.log(`[${this.serverName}] HTTP server listening on port ${e}`),o.log(`[${this.serverName}] MCP endpoint available at http://localhost:${e}/mcp`)})}async stopHttpServer(){if(!this.httpServer){o.error(`[${this.serverName}] HTTP server is not running or already stopped.`);return}return new Promise((e,r)=>{this.httpServer.close(t=>{if(t){o.error(`[${this.serverName}] Error stopping HTTP server:`,t),r(t);return}o.log(`[${this.serverName}] HTTP server stopped.`),this.httpServer=null;let s=Object.values(this.transports).map(n=>(n.sessionId&&delete this.transports[n.sessionId],Promise.resolve()));Promise.all(s).then(()=>{o.log(`[${this.serverName}] All transports closed.`),e()}).catch(n=>{o.error(`[${this.serverName}] Error during bulk transport closing:`,n),r(n)})})})}};Ne({path:xe(process.cwd(),".env")});async function Te(a,e){a&&(process.env.MCP_SERVER_PORT=a.toString()),e&&(process.env.GOOGLE_MAPS_API_KEY=e),o.log("\u{1F680} Starting Google Maps MCP Server..."),o.log("\u{1F4CD} Available tools: search_nearby, get_place_details, maps_geocode, maps_reverse_geocode, maps_distance_matrix, maps_directions, maps_elevation, echo"),o.log("");let r=C.map(async t=>{let s=process.env[t.portEnvVar];if(!s){o.error(`\u26A0\uFE0F [${t.name}] Port environment variable ${t.portEnvVar} not set.`),o.log(`\u{1F4A1} Please set ${t.portEnvVar} in your .env file or use --port parameter.`),o.log(` Example: ${t.portEnvVar}=3000 or --port 3000`);return}let n=Number(s);if(isNaN(n)||n<=0){o.error(`\u274C [${t.name}] Invalid port number "${s}" defined in ${t.portEnvVar}.`);return}try{let i=new x(t.name,t.tools);o.log(`\u{1F527} [${t.name}] Initializing MCP Server in HTTP mode on port ${n}...`),await i.startHttpServer(n),o.log(`\u2705 [${t.name}] MCP Server started successfully!`),o.log(` \u{1F310} Endpoint: http://localhost:${n}/mcp`),o.log(` \u{1F4DA} Tools: ${t.tools.length} available`)}catch(i){o.error(`\u274C [${t.name}] Failed to start MCP Server on port ${n}:`,i)}});await Promise.allSettled(r),o.log(""),o.log("\u{1F389} Server initialization completed!"),o.log("\u{1F4A1} Need help? Check the README.md for configuration details.")}if(process.argv[1]&&(process.argv[1].endsWith("cli.ts")||process.argv[1].endsWith("cli.js"))){let a=Ae(Oe(process.argv)).option("port",{alias:"p",type:"number",description:"Port to run the MCP server on",default:process.env.MCP_SERVER_PORT?parseInt(process.env.MCP_SERVER_PORT):3e3}).option("apikey",{alias:"k",type:"string",description:"Google Maps API key",default:process.env.GOOGLE_MAPS_API_KEY}).option("help",{alias:"h",type:"boolean",description:"Show help"}).example([["$0","Start server with default settings"],['$0 --port 3000 --apikey "your_api_key"',"Start server with custom port and API key"],['$0 -p 3001 -k "your_api_key"',"Start server with short options"]]).help().parseSync();o.log("\u{1F5FA}\uFE0F Google Maps MCP Server"),o.log(" A Model Context Protocol server for Google Maps services"),o.log(""),a.apikey||(o.log("\u26A0\uFE0F Google Maps API Key not found!"),o.log(" Please provide --apikey parameter or set GOOGLE_MAPS_API_KEY in your .env file"),o.log(" Example: mcp-google-map --apikey your_api_key_here"),o.log(" Or: GOOGLE_MAPS_API_KEY=your_api_key_here"),o.log("")),Te(a.port,a.apikey).catch(e=>{o.error("\u274C Failed to start server:",e),process.exit(1)})}export{Te as startServer};
package/dist/index.d.ts CHANGED
@@ -1,10 +1,6 @@
1
- declare enum ImagePurpose {
2
- DISPLAY = "display",
3
- LLM = "llm"
4
- }
5
1
  declare const Logger: {
6
2
  log: (...args: any[]) => void;
7
3
  error: (...args: any[]) => void;
8
4
  };
9
5
 
10
- export { ImagePurpose, Logger };
6
+ export { Logger };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{a,b}from"./chunk-U2CGP7BJ.js";export{a as ImagePurpose,b as Logger};
1
+ import{a}from"./chunk-W2DM2HDK.js";export{a as Logger};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cablate/mcp-google-map",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "Google Maps MCP server with streamable HTTP transport support for location services, geocoding, and navigation",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1 +0,0 @@
1
- var n=(r=>(r.DISPLAY="display",r.LLM="llm",r))(n||{}),o={log:(...l)=>{},error:(...l)=>{}};export{n as a,o as b};