@cablate/mcp-google-map 0.0.18 β†’ 0.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 CabLate
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1
+ MIT License
2
+
3
+ Copyright (c) 2025 CabLate
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
package/README.md CHANGED
@@ -1,271 +1,296 @@
1
- [![MseeP.ai Security Assessment Badge](https://mseep.net/pr/cablate-mcp-google-map-badge.png)](https://mseep.ai/app/cablate-mcp-google-map)
2
-
3
- <a href="https://glama.ai/mcp/servers/@cablate/mcp-google-map">
4
- <img width="380" height="200" src="https://glama.ai/mcp/servers/@cablate/mcp-google-map/badge" alt="Google Map Server MCP server" />
5
- </a>
6
-
7
- # MCP Google Map Server
8
-
9
- A powerful Model Context Protocol (MCP) server providing comprehensive Google Maps API integration with streamable HTTP transport support and LLM processing capabilities.
10
-
11
- ## πŸ™Œ Special Thanks
12
-
13
- This project has received contributions from the community.
14
- Special thanks to [@junyinnnn](https://github.com/junyinnnn) for helping add support for `streamablehttp`.
15
-
16
- ## βœ… Testing Status
17
-
18
- **This MCP server has been tested and verified to work correctly with:**
19
-
20
- - Claude Desktop
21
- - Dive Desktop
22
- - MCP protocol implementations
23
-
24
- All tools and features are confirmed functional through real-world testing.
25
-
26
- ## Features
27
-
28
- ### πŸ—ΊοΈ Google Maps Integration
29
-
30
- - **Location Search**
31
-
32
- - Search for places near a specific location with customizable radius and filters
33
- - Get detailed place information including ratings, opening hours, and contact details
34
-
35
- - **Geocoding Services**
36
-
37
- - Convert addresses to coordinates (geocoding)
38
- - Convert coordinates to addresses (reverse geocoding)
39
-
40
- - **Distance & Directions**
41
-
42
- - Calculate distances and travel times between multiple origins and destinations
43
- - Get detailed turn-by-turn directions between two points
44
- - Support for different travel modes (driving, walking, bicycling, transit)
45
-
46
- - **Elevation Data**
47
- - Retrieve elevation data for specific locations
48
-
49
- ### πŸš€ Advanced Features
50
-
51
- - **Streamable HTTP Transport**: Latest MCP protocol with real-time streaming capabilities
52
- - **Session Management**: Stateful sessions with UUID-based identification
53
- - **Multiple Connection Support**: Handle multiple concurrent client connections
54
- - **Echo Service**: Built-in testing tool for MCP server functionality
55
-
56
- ## Installation
57
-
58
- > ⚠️ **Important Notice**: This server uses HTTP transport, not stdio. Direct npx usage in MCP Server Settings is **NOT supported**.
59
-
60
- ### Method 1: Global Installation (Recommended)
61
-
62
- ```bash
63
- # Install globally
64
- npm install -g @cablate/mcp-google-map
65
-
66
- # Run the server
67
- mcp-google-map --port 3000 --apikey "your_api_key_here"
68
-
69
- # Using short options
70
- mcp-google-map -p 3000 -k "your_api_key_here"
71
- ```
72
-
73
- ### Method 2: Using npx (Quick Start)
74
-
75
- > ⚠️ **Warning**: Cannot be used directly in MCP Server Settings with stdio mode
76
-
77
- **Step 1: Launch HTTP Server in Terminal**
78
-
79
- ```bash
80
- # Run in a separate terminal
81
- npx @cablate/mcp-google-map --port 3000 --apikey "YOUR_API_KEY"
82
-
83
- # Or with environment variable
84
- GOOGLE_MAPS_API_KEY=YOUR_API_KEY npx @cablate/mcp-google-map
85
- ```
86
-
87
- **Step 2: Configure MCP Client to Use HTTP**
88
-
89
- ```json
90
- {
91
- "mcp-google-map": {
92
- "transport": "http",
93
- "url": "http://localhost:3000/mcp"
94
- }
95
- }
96
- ```
97
-
98
- ### ❌ Common Mistake to Avoid
99
-
100
- ```json
101
- // This WILL NOT WORK - stdio mode not supported with npx
102
- {
103
- "mcp-google-map": {
104
- "command": "npx",
105
- "args": ["@cablate/mcp-google-map"]
106
- }
107
- }
108
- ```
109
-
110
- ### Server Information
111
-
112
- - **Endpoint**: `http://localhost:3000/mcp`
113
- - **Transport**: HTTP (not stdio)
114
- - **Tools**: 8 Google Maps tools available
115
-
116
- ### API Key Configuration
117
-
118
- API keys can be provided in three ways (priority order):
119
-
120
- 1. **HTTP Headers** (Highest priority)
121
-
122
- ```json
123
- // MCP Client config
124
- {
125
- "mcp-google-map": {
126
- "transport": "streamableHttp",
127
- "url": "http://localhost:3000/mcp",
128
- // if your MCP Client support 'headers'
129
- "headers": {
130
- "X-Google-Maps-API-Key": "YOUR_API_KEY"
131
- }
132
- }
133
- }
134
- ```
135
-
136
- 2. **Command Line**
137
-
138
- ```bash
139
- mcp-google-map --apikey YOUR_API_KEY
140
- ```
141
-
142
- 3. **Environment Variable** (.env file or command line)
143
- ```env
144
- GOOGLE_MAPS_API_KEY=your_api_key_here
145
- MCP_SERVER_PORT=3000
146
- ```
147
-
148
- ## Available Tools
149
-
150
- The server provides the following tools:
151
-
152
- ### Google Maps Tools
153
-
154
- 1. **search_nearby** - Search for nearby places based on location, with optional filtering by keywords, distance, rating, and operating hours
155
- 2. **get_place_details** - Get detailed information about a specific place including contact details, reviews, ratings, and operating hours
156
- 3. **maps_geocode** - Convert addresses or place names to geographic coordinates (latitude and longitude)
157
- 4. **maps_reverse_geocode** - Convert geographic coordinates to a human-readable address
158
- 5. **maps_distance_matrix** - Calculate travel distances and durations between multiple origins and destinations
159
- 6. **maps_directions** - Get detailed turn-by-turn navigation directions between two locations
160
- 7. **maps_elevation** - Get elevation data (height above sea level) for specific geographic locations
161
-
162
- ## Development
163
-
164
- ### Local Development
165
-
166
- ```bash
167
- # Clone the repository
168
- git clone https://github.com/cablate/mcp-google-map.git
169
- cd mcp-google-map
170
-
171
- # Install dependencies
172
- npm install
173
-
174
- # Set up environment variables
175
- cp .env.example .env
176
- # Edit .env with your API key
177
-
178
- # Build the project
179
- npm run build
180
-
181
- # Start the server
182
- npm start
183
-
184
- # Or run in development mode
185
- npm run dev
186
- ```
187
-
188
- ### Project Structure
189
-
190
- ```
191
- src/
192
- β”œβ”€β”€ cli.ts # Main CLI entry point
193
- β”œβ”€β”€ config.ts # Server configuration
194
- β”œβ”€β”€ index.ts # Package exports
195
- β”œβ”€β”€ core/
196
- β”‚ └── BaseMcpServer.ts # Base MCP server with streamable HTTP
197
- └── tools/
198
- └── maps/ # Google Maps tools
199
- β”œβ”€β”€ toolclass.ts # Google Maps API client
200
- β”œβ”€β”€ searchPlaces.ts # Maps service layer
201
- β”œβ”€β”€ searchNearby.ts # Search nearby places
202
- β”œβ”€β”€ placeDetails.ts # Place details
203
- β”œβ”€β”€ geocode.ts # Geocoding
204
- β”œβ”€β”€ reverseGeocode.ts # Reverse geocoding
205
- β”œβ”€β”€ distanceMatrix.ts # Distance matrix
206
- β”œβ”€β”€ directions.ts # Directions
207
- └── elevation.ts # Elevation data
208
- ```
209
-
210
- ## Tech Stack
211
-
212
- - **TypeScript** - Type-safe development
213
- - **Node.js** - Runtime environment
214
- - **Google Maps Services JS** - Google Maps API integration
215
- - **Model Context Protocol SDK** - MCP protocol implementation
216
- - **Express.js** - HTTP server framework
217
- - **Zod** - Schema validation
218
-
219
- ## Security Considerations
220
-
221
- - API keys are handled server-side for security
222
- - DNS rebinding protection available for production
223
- - Input validation using Zod schemas
224
- - Error handling and logging
225
-
226
- ## License
227
-
228
- MIT
229
-
230
- ## Contributing
231
-
232
- Community participation and contributions are welcome! Here's how you can contribute:
233
-
234
- - ⭐️ Star the project if you find it helpful
235
- - πŸ› Submit Issues: Report bugs or provide suggestions
236
- - πŸ”§ Create Pull Requests: Submit code improvements
237
- - πŸ“– Documentation: Help improve documentation
238
-
239
- ## Contact
240
-
241
- If you have any questions or suggestions, feel free to reach out:
242
-
243
- - πŸ“§ Email: [reahtuoo310109@gmail.com](mailto:reahtuoo310109@gmail.com)
244
- - πŸ’» GitHub: [CabLate](https://github.com/cablate/)
245
- - 🀝 Collaboration: Welcome to discuss project cooperation
246
- - πŸ“š Technical Guidance: Sincere welcome for suggestions and guidance
247
-
248
- ## Changelog
249
-
250
- ### v0.0.18 (Latest)
251
-
252
- - **Error response improvements**: Now all error messages are in English with more detailed information (previously in Chinese)
253
-
254
- ### v0.0.17
255
-
256
- - **Added HTTP Header Authentication**: Support for passing API keys via `X-Google-Maps-API-Key` header in MCP Client config
257
- - **Fixed Concurrent User Issues**: Each session now uses its own API key without conflicts
258
- - **Fixed npx Execution**: Resolved module bundling issues
259
- - **Improved Documentation**: Clearer setup instructions
260
-
261
- ### v0.0.14
262
-
263
- - Added streamable HTTP transport support
264
- - Improved CLI interface with emoji indicators
265
- - Enhanced error handling and logging
266
- - Added comprehensive tool descriptions for LLM integration
267
- - Updated to latest MCP SDK version
268
-
269
- ## Star History
270
-
271
- [![Star History Chart](https://api.star-history.com/svg?repos=cablate/mcp-google-map&type=Date)](https://www.star-history.com/#cablate/mcp-google-map&Date)
1
+ [![MseeP.ai Security Assessment Badge](https://mseep.net/pr/cablate-mcp-google-map-badge.png)](https://mseep.ai/app/cablate-mcp-google-map)
2
+
3
+ <a href="https://glama.ai/mcp/servers/@cablate/mcp-google-map">
4
+ <img width="380" height="200" src="https://glama.ai/mcp/servers/@cablate/mcp-google-map/badge" alt="Google Map Server MCP server" />
5
+ </a>
6
+
7
+ # MCP Google Map Server
8
+
9
+ ---
10
+
11
+ > **πŸ“’ Important Notice**
12
+ >
13
+ > Google officially announced MCP support for Google Maps on December 10, 2025, introducing **[Maps Grounding Lite](https://cloud.google.com/blog/products/ai-machine-learning/announcing-official-mcp-support-for-google-services)** - a fully-managed MCP server for geospatial data and routing.
14
+ >
15
+ > This community project remains actively maintained as an alternative with different features and deployment options.
16
+
17
+ ---
18
+
19
+ A powerful Model Context Protocol (MCP) server providing comprehensive Google Maps API integration with streamable HTTP transport support and LLM processing capabilities.
20
+
21
+ ## πŸ™Œ Special Thanks
22
+
23
+ This project has received contributions from the community.
24
+ Special thanks to [@junyinnnn](https://github.com/junyinnnn) for helping add support for `streamablehttp`.
25
+
26
+ ## βœ… Testing Status
27
+
28
+ **This MCP server has been tested and verified to work correctly with:**
29
+
30
+ - Claude Desktop
31
+ - Dive Desktop
32
+ - MCP protocol implementations
33
+
34
+ All tools and features are confirmed functional through real-world testing.
35
+
36
+ ## Features
37
+
38
+ ### πŸ†• Latest Updates
39
+
40
+ - ℹ️ **Reminder: enable Places API (New) in https://console.cloud.google.com before using the new Place features.**
41
+
42
+
43
+ ### πŸ—ΊοΈ Google Maps Integration
44
+
45
+ - **Location Search**
46
+
47
+ - Search for places near a specific location with customizable radius and filters
48
+ - Get detailed place information including ratings, opening hours, and contact details
49
+
50
+ - **Geocoding Services**
51
+
52
+ - Convert addresses to coordinates (geocoding)
53
+ - Convert coordinates to addresses (reverse geocoding)
54
+
55
+ - **Distance & Directions**
56
+
57
+ - Calculate distances and travel times between multiple origins and destinations
58
+ - Get detailed turn-by-turn directions between two points
59
+ - Support for different travel modes (driving, walking, bicycling, transit)
60
+
61
+ - **Elevation Data**
62
+ - Retrieve elevation data for specific locations
63
+
64
+ ### πŸš€ Advanced Features
65
+
66
+ - **Streamable HTTP Transport**: Latest MCP protocol with real-time streaming capabilities
67
+ - **Session Management**: Stateful sessions with UUID-based identification
68
+ - **Multiple Connection Support**: Handle multiple concurrent client connections
69
+ - **Echo Service**: Built-in testing tool for MCP server functionality
70
+
71
+ ## Installation
72
+
73
+ > ⚠️ **Important Notice**: This server uses HTTP transport, not stdio. Direct npx usage in MCP Server Settings is **NOT supported**.
74
+
75
+ ### Method 1: Global Installation (Recommended)
76
+
77
+ ```bash
78
+ # Install globally
79
+ npm install -g @cablate/mcp-google-map
80
+
81
+ # Run the server
82
+ mcp-google-map --port 3000 --apikey "your_api_key_here"
83
+
84
+ # Using short options
85
+ mcp-google-map -p 3000 -k "your_api_key_here"
86
+ ```
87
+
88
+ ### Method 2: Using npx (Quick Start)
89
+
90
+ > ⚠️ **Warning**: Cannot be used directly in MCP Server Settings with stdio mode
91
+
92
+ **Step 1: Launch HTTP Server in Terminal**
93
+
94
+ ```bash
95
+ # Run in a separate terminal
96
+ npx @cablate/mcp-google-map --port 3000 --apikey "YOUR_API_KEY"
97
+
98
+ # Or with environment variable
99
+ GOOGLE_MAPS_API_KEY=YOUR_API_KEY npx @cablate/mcp-google-map
100
+ ```
101
+
102
+ **Step 2: Configure MCP Client to Use HTTP**
103
+
104
+ ```json
105
+ {
106
+ "mcp-google-map": {
107
+ "transport": "http",
108
+ "url": "http://localhost:3000/mcp"
109
+ }
110
+ }
111
+ ```
112
+
113
+ ### ❌ Common Mistake to Avoid
114
+
115
+ ```json
116
+ // This WILL NOT WORK - stdio mode not supported with npx
117
+ {
118
+ "mcp-google-map": {
119
+ "command": "npx",
120
+ "args": ["@cablate/mcp-google-map"]
121
+ }
122
+ }
123
+ ```
124
+
125
+ ### Server Information
126
+
127
+ - **Endpoint**: `http://localhost:3000/mcp`
128
+ - **Transport**: HTTP (not stdio)
129
+ - **Tools**: 8 Google Maps tools available
130
+
131
+ ### API Key Configuration
132
+
133
+ API keys can be provided in three ways (priority order):
134
+
135
+ 1. **HTTP Headers** (Highest priority)
136
+
137
+ ```json
138
+ // MCP Client config
139
+ {
140
+ "mcp-google-map": {
141
+ "transport": "streamableHttp",
142
+ "url": "http://localhost:3000/mcp",
143
+ // if your MCP Client support 'headers'
144
+ "headers": {
145
+ "X-Google-Maps-API-Key": "YOUR_API_KEY"
146
+ }
147
+ }
148
+ }
149
+ ```
150
+
151
+ 2. **Command Line**
152
+
153
+ ```bash
154
+ mcp-google-map --apikey YOUR_API_KEY
155
+ ```
156
+
157
+ 3. **Environment Variable** (.env file or command line)
158
+ ```env
159
+ GOOGLE_MAPS_API_KEY=your_api_key_here
160
+ MCP_SERVER_PORT=3000
161
+ ```
162
+
163
+ ## Available Tools
164
+
165
+ The server provides the following tools:
166
+
167
+ ### Google Maps Tools
168
+
169
+ 1. **search_nearby** - Search for nearby places based on location, with optional filtering by keywords, distance, rating, and operating hours
170
+ 2. **get_place_details** - Get detailed information about a specific place including contact details, reviews, ratings, and operating hours
171
+ 3. **maps_geocode** - Convert addresses or place names to geographic coordinates (latitude and longitude)
172
+ 4. **maps_reverse_geocode** - Convert geographic coordinates to a human-readable address
173
+ 5. **maps_distance_matrix** - Calculate travel distances and durations between multiple origins and destinations
174
+ 6. **maps_directions** - Get detailed turn-by-turn navigation directions between two locations
175
+ 7. **maps_elevation** - Get elevation data (height above sea level) for specific geographic locations
176
+
177
+ ## Development
178
+
179
+ ### Local Development
180
+
181
+ ```bash
182
+ # Clone the repository
183
+ git clone https://github.com/cablate/mcp-google-map.git
184
+ cd mcp-google-map
185
+
186
+ # Install dependencies
187
+ npm install
188
+
189
+ # Set up environment variables
190
+ cp .env.example .env
191
+ # Edit .env with your API key
192
+
193
+ # Build the project
194
+ npm run build
195
+
196
+ # Start the server
197
+ npm start
198
+
199
+ # Or run in development mode
200
+ npm run dev
201
+ ```
202
+
203
+ ### Project Structure
204
+
205
+ ```
206
+ src/
207
+ β”œβ”€β”€ cli.ts # Main CLI entry point
208
+ β”œβ”€β”€ config.ts # Server configuration
209
+ β”œβ”€β”€ index.ts # Package exports
210
+ β”œβ”€β”€ core/
211
+ β”‚ └── BaseMcpServer.ts # Base MCP server with streamable HTTP
212
+ └── tools/
213
+ └── maps/ # Google Maps tools
214
+ β”œβ”€β”€ toolclass.ts # Google Maps API client
215
+ β”œβ”€β”€ searchPlaces.ts # Maps service layer
216
+ β”œβ”€β”€ searchNearby.ts # Search nearby places
217
+ β”œβ”€β”€ placeDetails.ts # Place details
218
+ β”œβ”€β”€ geocode.ts # Geocoding
219
+ β”œβ”€β”€ reverseGeocode.ts # Reverse geocoding
220
+ β”œβ”€β”€ distanceMatrix.ts # Distance matrix
221
+ β”œβ”€β”€ directions.ts # Directions
222
+ └── elevation.ts # Elevation data
223
+ ```
224
+
225
+ ## Tech Stack
226
+
227
+ - **TypeScript** - Type-safe development
228
+ - **Node.js** - Runtime environment
229
+ - **Google Maps Services JS** - Google Maps API integration
230
+ - **Model Context Protocol SDK** - MCP protocol implementation
231
+ - **Express.js** - HTTP server framework
232
+ - **Zod** - Schema validation
233
+
234
+ ## Security Considerations
235
+
236
+ - API keys are handled server-side for security
237
+ - DNS rebinding protection available for production
238
+ - Input validation using Zod schemas
239
+ - Error handling and logging
240
+
241
+ ### Security Assessment Clarifications (2026-03)
242
+
243
+ For enterprise security reviews, see the standalone document:
244
+
245
+ - [Security Assessment Clarifications (23 items)](./SECURITY_ASSESSMENT.md)
246
+
247
+ ## License
248
+
249
+ MIT
250
+
251
+ ## Contributing
252
+
253
+ Community participation and contributions are welcome! Here's how you can contribute:
254
+
255
+ - ⭐️ Star the project if you find it helpful
256
+ - πŸ› Submit Issues: Report bugs or provide suggestions
257
+ - πŸ”§ Create Pull Requests: Submit code improvements
258
+ - πŸ“– Documentation: Help improve documentation
259
+
260
+ ## Contact
261
+
262
+ If you have any questions or suggestions, feel free to reach out:
263
+
264
+ - πŸ“§ Email: [reahtuoo310109@gmail.com](mailto:reahtuoo310109@gmail.com)
265
+ - πŸ’» GitHub: [CabLate](https://github.com/cablate/)
266
+ - 🀝 Collaboration: Welcome to discuss project cooperation
267
+ - πŸ“š Technical Guidance: Sincere welcome for suggestions and guidance
268
+
269
+ ## Changelog
270
+
271
+ ### v0.0.19 (Latest)
272
+
273
+ - **New Places API Integration**: Updated to use Google's new Places API (New) instead of the legacy API to resolve HTTP 403 errors and ensure continued functionality.
274
+
275
+ ### v0.0.18
276
+
277
+ - **Error response improvements**: Now all error messages are in English with more detailed information (previously in Chinese)
278
+
279
+ ### v0.0.17
280
+
281
+ - **Added HTTP Header Authentication**: Support for passing API keys via `X-Google-Maps-API-Key` header in MCP Client config
282
+ - **Fixed Concurrent User Issues**: Each session now uses its own API key without conflicts
283
+ - **Fixed npx Execution**: Resolved module bundling issues
284
+ - **Improved Documentation**: Clearer setup instructions
285
+
286
+ ### v0.0.14
287
+
288
+ - Added streamable HTTP transport support
289
+ - Improved CLI interface with emoji indicators
290
+ - Enhanced error handling and logging
291
+ - Added comprehensive tool descriptions for LLM integration
292
+ - Updated to latest MCP SDK version
293
+
294
+ ## Star History
295
+
296
+ [![Star History Chart](https://api.star-history.com/svg?repos=cablate/mcp-google-map&type=Date)](https://www.star-history.com/#cablate/mcp-google-map&Date)
@@ -0,0 +1 @@
1
+ import{Client as x,Language as R}from"@googlemaps/google-maps-services-js";import A from"dotenv";A.config();function y(c){let e=c?.response?.data?.error_message,r=c?.response?.status;return e?`${e} (HTTP ${r})`:c instanceof Error?c.message:String(c)}var P=class{constructor(e){this.defaultLanguage=R.en;if(this.client=new x({}),this.apiKey=e||process.env.GOOGLE_MAPS_API_KEY||"",!this.apiKey)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:this.apiKey};try{let n=(await this.client.placesNearby({params:r})).data.results;return e.minRating&&(n=n.filter(a=>(a.rating||0)>=(e.minRating||0))),n}catch(t){throw d.error("Error in searchNearbyPlaces:",t),new Error(`Failed to search nearby places: ${y(t)}`)}}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:this.apiKey}})).data.result}catch(r){throw d.error("Error in getPlaceDetails:",r),new Error(`Failed to get place details for ${e}: ${y(r)}`)}}async geocodeAddress(e){try{let r=await this.client.geocode({params:{address:e,key:this.apiKey,language:this.defaultLanguage}});if(r.data.results.length===0)throw new Error(`No location found for address: "${e}"`);let t=r.data.results[0],n=t.geometry.location;return{lat:n.lat,lng:n.lng,formatted_address:t.formatted_address,place_id:t.place_id}}catch(r){throw d.error("Error in geocodeAddress:",r),new Error(`Failed to geocode address "${e}": ${y(r)}`)}}parseCoordinates(e){let r=e.split(",").map(t=>parseFloat(t.trim()));if(r.length!==2||isNaN(r[0])||isNaN(r[1]))throw new Error(`Invalid coordinate format: "${e}". Please use "latitude,longitude" format (e.g., "25.033,121.564"`);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 d.error("Error in geocode:",r),new Error(`Failed to geocode address "${e}": ${y(r)}`)}}async reverseGeocode(e,r){try{let t=await this.client.reverseGeocode({params:{latlng:{lat:e,lng:r},language:this.defaultLanguage,key:this.apiKey}});if(t.data.results.length===0)throw new Error(`No address found for coordinates: (${e}, ${r})`);let n=t.data.results[0];return{formatted_address:n.formatted_address,place_id:n.place_id,address_components:n.address_components}}catch(t){throw d.error("Error in reverseGeocode:",t),new Error(`Failed to reverse geocode coordinates (${e}, ${r}): ${y(t)}`)}}async calculateDistanceMatrix(e,r,t="driving"){try{let a=(await this.client.distancematrix({params:{origins:e,destinations:r,mode:t,language:this.defaultLanguage,key:this.apiKey}})).data;if(a.status!=="OK")throw new Error(`Distance matrix calculation failed with status: ${a.status}`);let o=[],l=[];return a.rows.forEach(h=>{let u=[],m=[];h.elements.forEach(i=>{i.status==="OK"?(u.push({value:i.distance.value,text:i.distance.text}),m.push({value:i.duration.value,text:i.duration.text})):(u.push(null),m.push(null))}),o.push(u),l.push(m)}),{distances:o,durations:l,origin_addresses:a.origin_addresses,destination_addresses:a.destination_addresses}}catch(n){throw d.error("Error in calculateDistanceMatrix:",n),new Error(`Failed to calculate distance matrix: ${y(n)}`)}}async getDirections(e,r,t="driving",n,a){try{let o;a&&(o=Math.floor(a.getTime()/1e3));let l;o||(n instanceof Date?l=Math.floor(n.getTime()/1e3):n?l=n:l="now");let u=(await this.client.directions({params:{origin:e,destination:r,mode:t,language:this.defaultLanguage,key:this.apiKey,arrival_time:o,departure_time:l}})).data;if(u.status!=="OK")throw new Error(`Failed to get directions with status: ${u.status} (arrival_time: ${o}, departure_time: ${l}`);if(u.routes.length===0)throw new Error(`No route found from "${e}" to "${r}" with mode: ${t}`);let m=u.routes[0],i=m.legs[0],f=s=>{if(!s||typeof s.value!="number")return"";let g=new Date(s.value*1e3),p={year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1};return s.time_zone&&typeof s.time_zone=="string"&&(p.timeZone=s.time_zone),g.toLocaleString(this.defaultLanguage.toString(),p)};return{routes:u.routes,summary:m.summary,total_distance:{value:i.distance.value,text:i.distance.text},total_duration:{value:i.duration.value,text:i.duration.text},arrival_time:f(i.arrival_time),departure_time:f(i.departure_time)}}catch(o){throw d.error("Error in getDirections:",o),new Error(`Failed to get directions from "${e}" to "${r}": ${y(o)}`)}}async getElevation(e){try{let r=e.map(a=>({lat:a.latitude,lng:a.longitude})),n=(await this.client.elevation({params:{locations:r,key:this.apiKey}})).data;if(n.status!=="OK")throw new Error(`Failed to get elevation data with status: ${n.status}`);return n.results.map((a,o)=>({elevation:a.elevation,location:r[o]}))}catch(r){throw d.error("Error in getElevation:",r),new Error(`Failed to get elevation data for ${e.length} location(s): ${y(r)}`)}}};import{PlacesClient as T}from"@googlemaps/places";var _=class{constructor(e){this.defaultLanguage="en";this.placeFieldMask=["displayName","name","id","formattedAddress","location","utcOffsetMinutes","regularOpeningHours.periods","regularOpeningHours.weekdayDescriptions","currentOpeningHours.openNow","nationalPhoneNumber","websiteUri","priceLevel","rating","userRatingCount","reviews.rating","reviews.text","reviews.publishTime","reviews.authorAttribution.displayName","photos.heightPx","photos.widthPx","photos.name"].join(",");if(this.client=new T({apiKey:e||process.env.GOOGLE_MAPS_API_KEY||""}),!e&&!process.env.GOOGLE_MAPS_API_KEY)throw new Error("Google Maps API Key is required")}async getPlaceDetails(e){try{let r=`places/${e}`,[t]=await this.client.getPlace({name:r,languageCode:this.defaultLanguage},{otherArgs:{headers:{"X-Goog-FieldMask":this.placeFieldMask}}});return this.transformPlaceResponse(t)}catch(r){throw d.error("Error in getPlaceDetails (New API):",r),new Error(`Failed to get place details for ${e}: ${this.extractErrorMessage(r)}`)}}transformPlaceResponse(e){return{name:e.displayName?.text||e.name||"",place_id:this.extractLegacyPlaceId(e),formatted_address:e.formattedAddress||"",geometry:{location:{lat:e.location?.latitude||0,lng:e.location?.longitude||0}},rating:e.rating||0,user_ratings_total:e.userRatingCount||0,opening_hours:e.regularOpeningHours?{open_now:this.isCurrentlyOpen(e.regularOpeningHours,e.utcOffsetMinutes,e.currentOpeningHours),weekday_text:this.formatOpeningHours(e.regularOpeningHours)}:void 0,formatted_phone_number:e.nationalPhoneNumber||"",website:e.websiteUri||"",price_level:e.priceLevel||0,reviews:e.reviews?.map(r=>({rating:r.rating||0,text:r.text?.text||"",time:r.publishTime?.seconds||0,author_name:r.authorAttribution?.displayName||""}))||[],photos:e.photos?.map(r=>({photo_reference:r.name||"",height:r.heightPx||0,width:r.widthPx||0}))||[]}}extractLegacyPlaceId(e){let r=e?.name;if(typeof r=="string"&&r.startsWith("places/")){let t=r.substring(7);if(t)return t}return e?.id||""}isCurrentlyOpen(e,r,t){if(typeof t?.openNow=="boolean")return t.openNow;if(typeof e?.openNow=="boolean")return e.openNow;let n=e?.periods;if(!Array.isArray(n)||n.length===0)return!1;let a=1440,o=a*7,{day:l,minutes:h}=this.getLocalTimeComponents(r),u=l*a+h,m={SUNDAY:0,MONDAY:1,TUESDAY:2,WEDNESDAY:3,THURSDAY:4,FRIDAY:5,SATURDAY:6},i=s=>{if(typeof s=="number"&&s>=0&&s<=6)return s;if(typeof s=="string"){let g=s.toUpperCase();if(g in m)return m[g]}},f=s=>{if(!s)return;let g=typeof s.hours=="number"?s.hours:Number(s.hours??NaN),p=typeof s.minutes=="number"?s.minutes:Number(s.minutes??NaN);if(!(!Number.isFinite(g)||!Number.isFinite(p)))return g*60+p};for(let s of n){let g=i(s?.openDay),p=i(s?.closeDay??s?.openDay),D=f(s?.openTime),N=f(s?.closeTime);if(g===void 0||D===void 0)continue;let b=g*a+D,w;p===void 0||N===void 0?w=b+a:w=p*a+N,w<=b&&(w+=o);let v=u;for(;v<b;)v+=o;if(v>=b&&v<w)return!0}return!1}getLocalTimeComponents(e){let r=new Date;if(typeof e=="number"&&Number.isFinite(e)){let t=new Date(r.getTime()+e*6e4);return{day:t.getUTCDay(),minutes:t.getUTCHours()*60+t.getUTCMinutes()}}return{day:r.getDay(),minutes:r.getHours()*60+r.getMinutes()}}formatOpeningHours(e){return e?.weekdayDescriptions||[]}extractErrorMessage(e){let r=e?.message||e?.details||e?.status;return r?`${r}`:e instanceof Error?e.message:String(e)}};var E=class{constructor(e){this.mapsTools=new P(e),this.newPlacesService=new _(e)}async searchNearby(e){try{let r=await this.mapsTools.getLocation(e.center),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(n=>({name:n.name,place_id:n.place_id,address:n.formatted_address,location:n.geometry.location,rating:n.rating,total_ratings:n.user_ratings_total,open_now:n.opening_hours?.open_now}))}}catch(r){return{success:!1,error:r instanceof Error?r.message:"An error occurred during search"}}}async getPlaceDetails(e){try{let r=await this.newPlacesService.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:"An error occurred while getting place details"}}}async geocode(e){try{return{success:!0,data:await this.mapsTools.geocode(e)}}catch(r){return{success:!1,error:r instanceof Error?r.message:"An error occurred while geocoding address"}}}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:"An error occurred during reverse geocoding"}}}async calculateDistanceMatrix(e,r,t="driving"){try{return{success:!0,data:await this.mapsTools.calculateDistanceMatrix(e,r,t)}}catch(n){return{success:!1,error:n instanceof Error?n.message:"An error occurred while calculating distance matrix"}}}async getDirections(e,r,t="driving",n,a){try{let o=n?new Date(n):new Date,l=a?new Date(a):void 0;return{success:!0,data:await this.mapsTools.getDirections(e,r,t,o,l)}}catch(o){return{success:!1,error:o instanceof Error?o.message:"An error occurred while getting directions"}}}async getElevation(e){try{return{success:!0,data:await this.mapsTools.getElevation(e)}}catch(r){return{success:!1,error:r instanceof Error?r.message:"An error occurred while getting elevation data"}}}};var d={log:(...c)=>{console.error("[INFO]",...c)},error:(...c)=>{console.error("[ERROR]",...c)}};export{_ as a,d as b,E as c};
package/dist/cli.js CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import{a}from"./chunk-W2DM2HDK.js";import{config as L}from"dotenv";import{resolve as O}from"path";import Ne from"yargs";import{hideBin as Ce}from"yargs/helpers";import{z as y}from"zod";import{Client as F,Language as J}from"@googlemaps/google-maps-services-js";import j from"dotenv";j.config();function m(o){let e=o?.response?.data?.error_message,r=o?.response?.status;return e?`${e} (HTTP ${r})`:o instanceof Error?o.message:String(o)}var N=class{constructor(e){this.defaultLanguage=J.en;if(this.client=new F({}),this.apiKey=e||process.env.GOOGLE_MAPS_API_KEY||"",!this.apiKey)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:this.apiKey};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 a.error("Error in searchNearbyPlaces:",t),new Error(`Failed to search nearby places: ${m(t)}`)}}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:this.apiKey}})).data.result}catch(r){throw a.error("Error in getPlaceDetails:",r),new Error(`Failed to get place details for ${e}: ${m(r)}`)}}async geocodeAddress(e){try{let r=await this.client.geocode({params:{address:e,key:this.apiKey,language:this.defaultLanguage}});if(r.data.results.length===0)throw new Error(`No location found for address: "${e}"`);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 a.error("Error in geocodeAddress:",r),new Error(`Failed to geocode address "${e}": ${m(r)}`)}}parseCoordinates(e){let r=e.split(",").map(t=>parseFloat(t.trim()));if(r.length!==2||isNaN(r[0])||isNaN(r[1]))throw new Error(`Invalid coordinate format: "${e}". Please use "latitude,longitude" format (e.g., "25.033,121.564"`);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 a.error("Error in geocode:",r),new Error(`Failed to geocode address "${e}": ${m(r)}`)}}async reverseGeocode(e,r){try{let t=await this.client.reverseGeocode({params:{latlng:{lat:e,lng:r},language:this.defaultLanguage,key:this.apiKey}});if(t.data.results.length===0)throw new Error(`No address found for coordinates: (${e}, ${r})`);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 a.error("Error in reverseGeocode:",t),new Error(`Failed to reverse geocode coordinates (${e}, ${r}): ${m(t)}`)}}async calculateDistanceMatrix(e,r,t="driving"){try{let n=(await this.client.distancematrix({params:{origins:e,destinations:r,mode:t,language:this.defaultLanguage,key:this.apiKey}})).data;if(n.status!=="OK")throw new Error(`Distance matrix calculation failed with status: ${n.status}`);let i=[],c=[];return n.rows.forEach(h=>{let l=[],g=[];h.elements.forEach(p=>{p.status==="OK"?(l.push({value:p.distance.value,text:p.distance.text}),g.push({value:p.duration.value,text:p.duration.text})):(l.push(null),g.push(null))}),i.push(l),c.push(g)}),{distances:i,durations:c,origin_addresses:n.origin_addresses,destination_addresses:n.destination_addresses}}catch(s){throw a.error("Error in calculateDistanceMatrix:",s),new Error(`Failed to calculate distance matrix: ${m(s)}`)}}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 l=(await this.client.directions({params:{origin:e,destination:r,mode:t,language:this.defaultLanguage,key:this.apiKey,arrival_time:i,departure_time:c}})).data;if(l.status!=="OK")throw new Error(`Failed to get directions with status: ${l.status} (arrival_time: ${i}, departure_time: ${c}`);if(l.routes.length===0)throw new Error(`No route found from "${e}" to "${r}" with mode: ${t}`);let g=l.routes[0],p=g.legs[0],I=f=>{if(!f||typeof f.value!="number")return"";let z=new Date(f.value*1e3),$={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"&&($.timeZone=f.time_zone),z.toLocaleString(this.defaultLanguage.toString(),$)};return{routes:l.routes,summary:g.summary,total_distance:{value:p.distance.value,text:p.distance.text},total_duration:{value:p.duration.value,text:p.duration.text},arrival_time:I(p.arrival_time),departure_time:I(p.departure_time)}}catch(i){throw a.error("Error in getDirections:",i),new Error(`Failed to get directions from "${e}" to "${r}": ${m(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:this.apiKey}})).data;if(s.status!=="OK")throw new Error(`Failed to get elevation data with status: ${s.status}`);return s.results.map((n,i)=>({elevation:n.elevation,location:r[i]}))}catch(r){throw a.error("Error in getElevation:",r),new Error(`Failed to get elevation data for ${e.length} location(s): ${m(r)}`)}}};var d=class{constructor(e){this.mapsTools=new N(e)}async searchNearby(e){try{let r=await this.mapsTools.getLocation(e.center),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:"An error occurred during search"}}}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:"An error occurred while getting place details"}}}async geocode(e){try{return{success:!0,data:await this.mapsTools.geocode(e)}}catch(r){return{success:!1,error:r instanceof Error?r.message:"An error occurred while geocoding address"}}}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:"An error occurred during reverse geocoding"}}}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:"An error occurred while calculating distance matrix"}}}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:"An error occurred while getting directions"}}}async getElevation(e){try{return{success:!0,data:await this.mapsTools.getElevation(e)}}catch(r){return{success:!1,error:r instanceof Error?r.message:"An error occurred while getting elevation data"}}}};import{AsyncLocalStorage as V}from"node:async_hooks";var D=new V;function u(){return D.getStore()?.apiKey||process.env.GOOGLE_MAPS_API_KEY}function R(o,e){return D.run(o,e)}var q="search_nearby",W="Search for nearby places based on location, with optional filtering by keywords, distance, rating, and operating hours",Y={center:y.object({value:y.string().describe("Address, landmark name, or coordinates (coordinate format: lat,lng)"),isCoordinates:y.boolean().default(!1).describe("Whether the value is coordinates")}).describe("Search center point"),keyword:y.string().optional().describe("Search keyword (e.g., restaurant, cafe, hotel)"),radius:y.number().default(1e3).describe("Search radius in meters"),openNow:y.boolean().default(!1).describe("Only show places that are currently open"),minRating:y.number().min(0).max(5).optional().describe("Minimum rating requirement (0-5)")};async function Z(o){try{let e=u(),t=await new d(e).searchNearby(o);return t.success?{content:[{type:"text",text:`location: ${JSON.stringify(t.location,null,2)}
3
- `+JSON.stringify(t.data,null,2)}],isError:!1}:{content:[{type:"text",text:t.error||"Search failed"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`Error searching nearby places: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var v={NAME:q,DESCRIPTION:W,SCHEMA:Y,ACTION:Z};import{z as B}from"zod";var U="get_place_details",Q="Get detailed information about a specific place including contact details, reviews, ratings, and operating hours",X={placeId:B.string().describe("Google Maps place ID")};async function ee(o){try{let e=u(),t=await new d(e).getPlaceDetails(o.placeId);return t.success?{content:[{type:"text",text:JSON.stringify(t.data,null,2)}],isError:!1}:{content:[{type:"text",text:t.error||"Failed to get place details"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`Error getting place details: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var E={NAME:U,DESCRIPTION:Q,SCHEMA:X,ACTION:ee};import{z as re}from"zod";var te="maps_geocode",se="Convert addresses or place names to geographic coordinates (latitude and longitude)",oe={address:re.string().describe("Address or place name to convert to coordinates")};async function ae(o){try{let e=u(),t=await new d(e).geocode(o.address);return t.success?{content:[{type:"text",text:JSON.stringify(t.data,null,2)}],isError:!1}:{content:[{type:"text",text:t.error||"Failed to geocode address"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`Error geocoding address: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var b={NAME:te,DESCRIPTION:se,SCHEMA:oe,ACTION:ae};import{z as K}from"zod";var ne="maps_reverse_geocode",ie="Convert geographic coordinates (latitude and longitude) to a human-readable address",ce={latitude:K.number().describe("Latitude coordinate"),longitude:K.number().describe("Longitude coordinate")};async function le(o){try{let e=u(),t=await new d(e).reverseGeocode(o.latitude,o.longitude);return t.success?{content:[{type:"text",text:JSON.stringify(t.data,null,2)}],isError:!1}:{content:[{type:"text",text:t.error||"Failed to reverse geocode coordinates"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`Error reverse geocoding: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var P={NAME:ne,DESCRIPTION:ie,SCHEMA:ce,ACTION:le};import{z as S}from"zod";var de="maps_distance_matrix",pe="Calculate travel distances and durations between multiple origins and destinations for different travel modes",ue={origins:S.array(S.string()).describe("List of origin addresses or coordinates"),destinations:S.array(S.string()).describe("List of destination addresses or coordinates"),mode:S.enum(["driving","walking","bicycling","transit"]).default("driving").describe("Travel mode for calculation")};async function ge(o){try{let e=u(),t=await new d(e).calculateDistanceMatrix(o.origins,o.destinations,o.mode);return t.success?{content:[{type:"text",text:JSON.stringify(t.data,null,2)}],isError:!1}:{content:[{type:"text",text:t.error||"Failed to calculate distance matrix"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`Error calculating distance matrix: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var _={NAME:de,DESCRIPTION:pe,SCHEMA:ue,ACTION:ge};import{z as w}from"zod";var me="maps_directions",ye="Get detailed turn-by-turn navigation directions between two locations with route information",fe={origin:w.string().describe("Starting point address or coordinates"),destination:w.string().describe("Destination address or coordinates"),mode:w.enum(["driving","walking","bicycling","transit"]).default("driving").describe("Travel mode for directions"),departure_time:w.string().optional().describe("Departure time (ISO string format)"),arrival_time:w.string().optional().describe("Arrival time (ISO string format)")};async function he(o){try{let e=u(),t=await new d(e).getDirections(o.origin,o.destination,o.mode,o.departure_time,o.arrival_time);return t.success?{content:[{type:"text",text:JSON.stringify(t.data,null,2)}],isError:!1}:{content:[{type:"text",text:t.error||"Failed to get directions"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`Error getting directions: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var x={NAME:me,DESCRIPTION:ye,SCHEMA:fe,ACTION:he};import{z as C}from"zod";var ve="maps_elevation",Ee="Get elevation data (height above sea level) for specific geographic locations",be={locations:C.array(C.object({latitude:C.number().describe("Latitude coordinate"),longitude:C.number().describe("Longitude coordinate")})).describe("List of locations to get elevation data for")};async function Pe(o){try{let e=u(),t=await new d(e).getElevation(o.locations);return t.success?{content:[{type:"text",text:JSON.stringify(t.data,null,2)}],isError:!1}:{content:[{type:"text",text:t.error||"Failed to get elevation data"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`Error getting elevation data: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var A={NAME:ve,DESCRIPTION:Ee,SCHEMA:be,ACTION:Pe};var Se=[{name:"MCP-Server",portEnvVar:"MCP_SERVER_PORT",tools:[{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:P.NAME,description:P.DESCRIPTION,schema:P.SCHEMA,action:o=>P.ACTION(o)},{name:_.NAME,description:_.DESCRIPTION,schema:_.SCHEMA,action:o=>_.ACTION(o)},{name:x.NAME,description:x.DESCRIPTION,schema:x.SCHEMA,action:o=>x.ACTION(o)},{name:A.NAME,description:A.DESCRIPTION,schema:A.SCHEMA,action:o=>A.ACTION(o)}]}],G=Se;import{McpServer as _e}from"@modelcontextprotocol/sdk/server/mcp.js";import{StreamableHTTPServerTransport as we}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import{isInitializeRequest as xe}from"@modelcontextprotocol/sdk/types.js";import k from"express";import{randomUUID as Ae}from"node:crypto";var M=class o{constructor(){this.defaultApiKey=process.env.GOOGLE_MAPS_API_KEY}static getInstance(){return o.instance||(o.instance=new o),o.instance}setDefaultApiKey(e){this.defaultApiKey=e,process.env.GOOGLE_MAPS_API_KEY=e}getApiKey(e,r){if(e){let t=e.headers["x-google-maps-api-key"];if(t)return t;let s=e.headers.authorization;if(s&&s.startsWith("Bearer "))return s.substring(7)}return r||this.defaultApiKey}hasApiKey(e,r){return!!this.getApiKey(e,r)}isValidApiKeyFormat(e){return/^[A-Za-z0-9_-]{20,50}$/.test(e)}};var Me="0.0.1",T=class{constructor(e,r){this.sessions={};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),a.log(`${this.serverName} connected and ready to process requests`)}async startHttpServer(e){let r=k();r.use(k.json()),r.post("/mcp",async(s,n)=>{let i=s.headers["mcp-session-id"],c,l=M.getInstance().getApiKey(s);if(a.log(`${this.serverName} Get API KEY: ${l}`),i&&this.sessions[i])c=this.sessions[i],l&&(c.apiKey=l);else if(!i&&xe(s.body)){let g=new we({sessionIdGenerator:()=>Ae(),onsessioninitialized:p=>{this.sessions[p]=c,a.log(`[${this.serverName}] New session initialized: ${p}`)}});c={transport:g,apiKey:l},g.onclose=()=>{g.sessionId&&(delete this.sessions[g.sessionId],a.log(`[${this.serverName}] Session closed: ${g.sessionId}`))},await this.server.connect(g)}else{n.status(400).json({jsonrpc:"2.0",error:{code:-32e3,message:"Bad Request: No valid session ID provided"},id:null});return}await R({apiKey:c.apiKey,sessionId:i},async()=>{await c.transport.handleRequest(s,n,s.body)})});let t=async(s,n)=>{let i=s.headers["mcp-session-id"];if(!i||!this.sessions[i]){n.status(400).send("Invalid or missing session ID");return}let c=this.sessions[i],l=M.getInstance().getApiKey(s);l&&(c.apiKey=l),await R({apiKey:c.apiKey,sessionId:i},async()=>{await c.transport.handleRequest(s,n)})};r.get("/mcp",t),r.delete("/mcp",t),this.httpServer=r.listen(e,()=>{a.log(`[${this.serverName}] HTTP server listening on port ${e}`),a.log(`[${this.serverName}] MCP endpoint available at http://localhost:${e}/mcp`)})}async stopHttpServer(){if(!this.httpServer){a.error(`[${this.serverName}] HTTP server is not running or already stopped.`);return}return new Promise((e,r)=>{this.httpServer.close(t=>{if(t){a.error(`[${this.serverName}] Error stopping HTTP server:`,t),r(t);return}a.log(`[${this.serverName}] HTTP server stopped.`),this.httpServer=null;let s=Object.values(this.sessions).map(n=>(n.transport.sessionId&&delete this.sessions[n.transport.sessionId],Promise.resolve()));Promise.all(s).then(()=>{a.log(`[${this.serverName}] All transports closed.`),e()}).catch(n=>{a.error(`[${this.serverName}] Error during bulk transport closing:`,n),r(n)})})})}};import{fileURLToPath as Te}from"url";import{dirname as Re}from"path";import{readFileSync as Oe}from"fs";var Ie=Te(import.meta.url),H=Re(Ie);L({path:O(process.cwd(),".env")});L({path:O(H,"../.env")});async function $e(o,e){o&&(process.env.MCP_SERVER_PORT=o.toString()),e&&(process.env.GOOGLE_MAPS_API_KEY=e),a.log("\u{1F680} Starting Google Maps MCP Server..."),a.log("\u{1F4CD} Available tools: search_nearby, get_place_details, maps_geocode, maps_reverse_geocode, maps_distance_matrix, maps_directions, maps_elevation, echo"),a.log("");let r=G.map(async t=>{let s=process.env[t.portEnvVar];if(!s){a.error(`\u26A0\uFE0F [${t.name}] Port environment variable ${t.portEnvVar} not set.`),a.log(`\u{1F4A1} Please set ${t.portEnvVar} in your .env file or use --port parameter.`),a.log(` Example: ${t.portEnvVar}=3000 or --port 3000`);return}let n=Number(s);if(isNaN(n)||n<=0){a.error(`\u274C [${t.name}] Invalid port number "${s}" defined in ${t.portEnvVar}.`);return}try{let i=new T(t.name,t.tools);a.log(`\u{1F527} [${t.name}] Initializing MCP Server in HTTP mode on port ${n}...`),await i.startHttpServer(n),a.log(`\u2705 [${t.name}] MCP Server started successfully!`),a.log(` \u{1F310} Endpoint: http://localhost:${n}/mcp`),a.log(` \u{1F4DA} Tools: ${t.tools.length} available`)}catch(i){a.error(`\u274C [${t.name}] Failed to start MCP Server on port ${n}:`,i)}});await Promise.allSettled(r),a.log(""),a.log("\u{1F389} Server initialization completed!"),a.log("\u{1F4A1} Need help? Check the README.md for configuration details.")}var De=process.argv[1]&&(process.argv[1].endsWith("cli.ts")||process.argv[1].endsWith("cli.js")||process.argv[1].endsWith("mcp-google-map")||process.argv[1].includes("mcp-google-map")),Ke=import.meta.url===`file://${process.argv[1]}`;if(De||Ke){let o="0.0.0";try{let r=O(H,"../package.json");o=JSON.parse(Oe(r,"utf-8")).version}catch{o="0.0.0"}let e=Ne(Ce(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"}).version(o).alias("version","v").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();a.log("\u{1F5FA}\uFE0F Google Maps MCP Server"),a.log(" A Model Context Protocol server for Google Maps services"),a.log(""),e.apikey||(a.log("\u26A0\uFE0F Google Maps API Key not found!"),a.log(" Please provide --apikey parameter or set GOOGLE_MAPS_API_KEY in your .env file"),a.log(" Example: mcp-google-map --apikey your_api_key_here"),a.log(" Or: GOOGLE_MAPS_API_KEY=your_api_key_here"),a.log("")),$e(e.port,e.apikey).catch(r=>{a.error("\u274C Failed to start server:",r),process.exit(1)})}export{$e as startServer};
2
+ import{b as s,c}from"./chunk-6ECLWDYH.js";import{config as w}from"dotenv";import{resolve as N}from"path";import Se from"yargs";import{hideBin as Ee}from"yargs/helpers";import{z as d}from"zod";import{AsyncLocalStorage as D}from"async_hooks";var O=new D;function p(){return O.getStore()?.apiKey||process.env.GOOGLE_MAPS_API_KEY}function C(t,e){return O.run(t,e)}var G="search_nearby",H="Search for nearby places based on location, with optional filtering by keywords, distance, rating, and operating hours",z={center:d.object({value:d.string().describe("Address, landmark name, or coordinates (coordinate format: lat,lng)"),isCoordinates:d.boolean().default(!1).describe("Whether the value is coordinates")}).describe("Search center point (e.g. value: 49.3268778,-123.0585982, isCoordinates: true)"),keyword:d.string().optional().describe("Search keyword (e.g., restaurant, cafe, hotel)"),radius:d.number().default(1e3).describe("Search radius in meters"),openNow:d.boolean().default(!1).describe("Only show places that are currently open"),minRating:d.number().min(0).max(5).optional().describe("Minimum rating requirement (0-5)")};async function k(t){try{let e=p(),r=await new c(e).searchNearby(t);return r.success?{content:[{type:"text",text:`location: ${JSON.stringify(r.location,null,2)}
3
+ `+JSON.stringify(r.data,null,2)}],isError:!1}:{content:[{type:"text",text:r.error||"Search failed"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`Error searching nearby places: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var u={NAME:G,DESCRIPTION:H,SCHEMA:z,ACTION:k};import{z as J}from"zod";var L="get_place_details",j="Get detailed information about a specific place including contact details, reviews, ratings, and operating hours",V={placeId:J.string().describe("Google Maps place ID")};async function q(t){try{let e=p(),r=await new c(e).getPlaceDetails(t.placeId);return r.success?{content:[{type:"text",text:JSON.stringify(r.data,null,2)}],isError:!1}:{content:[{type:"text",text:r.error||"Failed to get place details"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`Error getting place details: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var y={NAME:L,DESCRIPTION:j,SCHEMA:V,ACTION:q};import{z as F}from"zod";var W="maps_geocode",Z="Convert addresses or place names to geographic coordinates (latitude and longitude)",Y={address:F.string().describe("Address or place name to convert to coordinates")};async function B(t){try{let e=p(),r=await new c(e).geocode(t.address);return r.success?{content:[{type:"text",text:JSON.stringify(r.data,null,2)}],isError:!1}:{content:[{type:"text",text:r.error||"Failed to geocode address"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`Error geocoding address: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var f={NAME:W,DESCRIPTION:Z,SCHEMA:Y,ACTION:B};import{z as T}from"zod";var U="maps_reverse_geocode",Q="Convert geographic coordinates (latitude and longitude) to a human-readable address",X={latitude:T.number().describe("Latitude coordinate"),longitude:T.number().describe("Longitude coordinate")};async function ee(t){try{let e=p(),r=await new c(e).reverseGeocode(t.latitude,t.longitude);return r.success?{content:[{type:"text",text:JSON.stringify(r.data,null,2)}],isError:!1}:{content:[{type:"text",text:r.error||"Failed to reverse geocode coordinates"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`Error reverse geocoding: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var v={NAME:U,DESCRIPTION:Q,SCHEMA:X,ACTION:ee};import{z as h}from"zod";var re="maps_distance_matrix",te="Calculate travel distances and durations between multiple origins and destinations for different travel modes",oe={origins:h.array(h.string()).describe("List of origin addresses or coordinates"),destinations:h.array(h.string()).describe("List of destination addresses or coordinates"),mode:h.enum(["driving","walking","bicycling","transit"]).default("driving").describe("Travel mode for calculation")};async function se(t){try{let e=p(),r=await new c(e).calculateDistanceMatrix(t.origins,t.destinations,t.mode);return r.success?{content:[{type:"text",text:JSON.stringify(r.data,null,2)}],isError:!1}:{content:[{type:"text",text:r.error||"Failed to calculate distance matrix"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`Error calculating distance matrix: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var S={NAME:re,DESCRIPTION:te,SCHEMA:oe,ACTION:se};import{z as E}from"zod";var ne="maps_directions",ie="Get detailed turn-by-turn navigation directions between two locations with route information",ae={origin:E.string().describe("Starting point address or coordinates"),destination:E.string().describe("Destination address or coordinates"),mode:E.enum(["driving","walking","bicycling","transit"]).default("driving").describe("Travel mode for directions"),departure_time:E.string().optional().describe("Departure time (ISO string format)"),arrival_time:E.string().optional().describe("Arrival time (ISO string format)")};async function ce(t){try{let e=p(),r=await new c(e).getDirections(t.origin,t.destination,t.mode,t.departure_time,t.arrival_time);return r.success?{content:[{type:"text",text:JSON.stringify(r.data,null,2)}],isError:!1}:{content:[{type:"text",text:r.error||"Failed to get directions"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`Error getting directions: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var P={NAME:ne,DESCRIPTION:ie,SCHEMA:ae,ACTION:ce};import{z as M}from"zod";var pe="maps_elevation",le="Get elevation data (height above sea level) for specific geographic locations",de={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")};async function me(t){try{let e=p(),r=await new c(e).getElevation(t.locations);return r.success?{content:[{type:"text",text:JSON.stringify(r.data,null,2)}],isError:!1}:{content:[{type:"text",text:r.error||"Failed to get elevation data"}],isError:!0}}catch(e){return{isError:!0,content:[{type:"text",text:`Error getting elevation data: ${e instanceof Error?e.message:JSON.stringify(e)}`}]}}}var A={NAME:pe,DESCRIPTION:le,SCHEMA:de,ACTION:me};var ge=[{name:"MCP-Server",portEnvVar:"MCP_SERVER_PORT",tools:[{name:u.NAME,description:u.DESCRIPTION,schema:u.SCHEMA,action:t=>u.ACTION(t)},{name:y.NAME,description:y.DESCRIPTION,schema:y.SCHEMA,action:t=>y.ACTION(t)},{name:f.NAME,description:f.DESCRIPTION,schema:f.SCHEMA,action:t=>f.ACTION(t)},{name:v.NAME,description:v.DESCRIPTION,schema:v.SCHEMA,action:t=>v.ACTION(t)},{name:S.NAME,description:S.DESCRIPTION,schema:S.SCHEMA,action:t=>S.ACTION(t)},{name:P.NAME,description:P.DESCRIPTION,schema:P.SCHEMA,action:t=>P.ACTION(t)},{name:A.NAME,description:A.DESCRIPTION,schema:A.SCHEMA,action:t=>A.ACTION(t)}]}],_=ge;import{McpServer as ue}from"@modelcontextprotocol/sdk/server/mcp.js";import{StreamableHTTPServerTransport as ye}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import{isInitializeRequest as fe}from"@modelcontextprotocol/sdk/types.js";import R from"express";import{randomUUID as ve}from"crypto";var x=class t{constructor(){this.defaultApiKey=process.env.GOOGLE_MAPS_API_KEY}static getInstance(){return t.instance||(t.instance=new t),t.instance}setDefaultApiKey(e){this.defaultApiKey=e,process.env.GOOGLE_MAPS_API_KEY=e}getApiKey(e,o){if(e){let r=e.headers["x-google-maps-api-key"];if(r)return r;let n=e.headers.authorization;if(n&&n.startsWith("Bearer "))return n.substring(7)}return o||this.defaultApiKey}hasApiKey(e,o){return!!this.getApiKey(e,o)}isValidApiKeyFormat(e){return/^[A-Za-z0-9_-]{20,50}$/.test(e)}};var he="0.0.1",b=class{constructor(e,o){this.sessions={};this.httpServer=null;this.serverName=e,this.tools=o,this.server=this.createMcpServer()}createMcpServer(){let e=new ue({name:this.serverName,version:he},{capabilities:{logging:{},tools:{}}});return this.tools.forEach(o=>{e.tool(o.name,o.description,o.schema,async r=>o.action(r))}),e}async connect(e){await this.server.connect(e);let o=process.stdout.write.bind(process.stdout);process.stdout.write=(r,n,i)=>typeof r=="string"&&!r.startsWith("{")?!0:o(r,n,i),s.log(`${this.serverName} connected and ready to process requests`)}async startHttpServer(e){let o=R();o.use(R.json()),o.post("/mcp",async(n,i)=>{let a=n.headers["mcp-session-id"],l,m=x.getInstance().getApiKey(n);if(s.log(`${this.serverName} API key received from request context`),a&&this.sessions[a])l=this.sessions[a],m&&(l.apiKey=m);else if(!a&&fe(n.body)){let g=new ye({sessionIdGenerator:()=>ve(),onsessioninitialized:I=>{this.sessions[I]=l,s.log(`[${this.serverName}] New session initialized: ${I}`)}});l={transport:g,apiKey:m},g.onclose=()=>{g.sessionId&&(delete this.sessions[g.sessionId],s.log(`[${this.serverName}] Session closed: ${g.sessionId}`))},await this.createMcpServer().connect(g)}else{i.status(400).json({jsonrpc:"2.0",error:{code:-32e3,message:"Bad Request: No valid session ID provided"},id:null});return}await C({apiKey:l.apiKey,sessionId:a},async()=>{await l.transport.handleRequest(n,i,n.body)})});let r=async(n,i)=>{let a=n.headers["mcp-session-id"];if(!a||!this.sessions[a]){i.status(400).send("Invalid or missing session ID");return}let l=this.sessions[a],m=x.getInstance().getApiKey(n);m&&(l.apiKey=m),await C({apiKey:l.apiKey,sessionId:a},async()=>{await l.transport.handleRequest(n,i)})};o.get("/mcp",r),o.delete("/mcp",r),this.httpServer=o.listen(e,()=>{s.log(`[${this.serverName}] HTTP server listening on port ${e}`),s.log(`[${this.serverName}] MCP endpoint available at http://localhost:${e}/mcp`)})}async stopHttpServer(){if(!this.httpServer){s.error(`[${this.serverName}] HTTP server is not running or already stopped.`);return}return new Promise((e,o)=>{this.httpServer.close(r=>{if(r){s.error(`[${this.serverName}] Error stopping HTTP server:`,r),o(r);return}s.log(`[${this.serverName}] HTTP server stopped.`),this.httpServer=null;let n=Object.values(this.sessions).map(i=>(i.transport.sessionId&&delete this.sessions[i.transport.sessionId],Promise.resolve()));Promise.all(n).then(()=>{s.log(`[${this.serverName}] All transports closed.`),e()}).catch(i=>{s.error(`[${this.serverName}] Error during bulk transport closing:`,i),o(i)})})})}};import{fileURLToPath as Pe}from"url";import{dirname as Ae}from"path";import{readFileSync as xe}from"fs";var Me=Pe(import.meta.url),K=Ae(Me);w({path:N(process.cwd(),".env")});w({path:N(K,"../.env")});async function be(t,e){t&&(process.env.MCP_SERVER_PORT=t.toString()),e&&(process.env.GOOGLE_MAPS_API_KEY=e),s.log("\u{1F680} Starting Google Maps MCP Server..."),s.log("\u{1F4CD} Available tools: search_nearby, get_place_details, maps_geocode, maps_reverse_geocode, maps_distance_matrix, maps_directions, maps_elevation, echo"),s.log("\u2139\uFE0F Reminder: enable Places API (New) in https://console.cloud.google.com before using the new Place features."),s.log("");let o=_.map(async r=>{let n=process.env[r.portEnvVar];if(!n){s.error(`\u26A0\uFE0F [${r.name}] Port environment variable ${r.portEnvVar} not set.`),s.log(`\u{1F4A1} Please set ${r.portEnvVar} in your .env file or use --port parameter.`),s.log(` Example: ${r.portEnvVar}=3000 or --port 3000`);return}let i=Number(n);if(isNaN(i)||i<=0){s.error(`\u274C [${r.name}] Invalid port number "${n}" defined in ${r.portEnvVar}.`);return}try{let a=new b(r.name,r.tools);s.log(`\u{1F527} [${r.name}] Initializing MCP Server in HTTP mode on port ${i}...`),await a.startHttpServer(i),s.log(`\u2705 [${r.name}] MCP Server started successfully!`),s.log(` \u{1F310} Endpoint: http://localhost:${i}/mcp`),s.log(` \u{1F4DA} Tools: ${r.tools.length} available`)}catch(a){s.error(`\u274C [${r.name}] Failed to start MCP Server on port ${i}:`,a)}});await Promise.allSettled(o),s.log(""),s.log("\u{1F389} Server initialization completed!"),s.log("\u{1F4A1} Need help? Check the README.md for configuration details.")}var Ce=process.argv[1]&&(process.argv[1].endsWith("cli.ts")||process.argv[1].endsWith("cli.js")||process.argv[1].endsWith("mcp-google-map")||process.argv[1].includes("mcp-google-map")),Ne=import.meta.url===`file://${process.argv[1]}`;if(Ce||Ne){let t="0.0.0";try{let o=N(K,"../package.json");t=JSON.parse(xe(o,"utf-8")).version}catch{t="0.0.0"}let e=Se(Ee(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"}).version(t).alias("version","v").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();s.log("\u{1F5FA}\uFE0F Google Maps MCP Server"),s.log(" A Model Context Protocol server for Google Maps services"),s.log(""),e.apikey||(s.log("\u26A0\uFE0F Google Maps API Key not found!"),s.log(" Please provide --apikey parameter or set GOOGLE_MAPS_API_KEY in your .env file"),s.log(" Example: mcp-google-map --apikey your_api_key_here"),s.log(" Or: GOOGLE_MAPS_API_KEY=your_api_key_here"),s.log("")),be(e.port,e.apikey).catch(o=>{s.error("\u274C Failed to start server:",o),process.exit(1)})}export{be as startServer};
package/dist/index.d.ts CHANGED
@@ -1,6 +1,135 @@
1
+ interface SearchNearbyResponse {
2
+ success: boolean;
3
+ error?: string;
4
+ data?: any[];
5
+ location?: any;
6
+ }
7
+ interface PlaceDetailsResponse {
8
+ success: boolean;
9
+ error?: string;
10
+ data?: any;
11
+ }
12
+ interface GeocodeResponse {
13
+ success: boolean;
14
+ error?: string;
15
+ data?: {
16
+ location: {
17
+ lat: number;
18
+ lng: number;
19
+ };
20
+ formatted_address: string;
21
+ place_id: string;
22
+ };
23
+ }
24
+ interface ReverseGeocodeResponse {
25
+ success: boolean;
26
+ error?: string;
27
+ data?: {
28
+ formatted_address: string;
29
+ place_id: string;
30
+ address_components: any[];
31
+ };
32
+ }
33
+ interface DistanceMatrixResponse {
34
+ success: boolean;
35
+ error?: string;
36
+ data?: {
37
+ distances: any[][];
38
+ durations: any[][];
39
+ origin_addresses: string[];
40
+ destination_addresses: string[];
41
+ };
42
+ }
43
+ interface DirectionsResponse {
44
+ success: boolean;
45
+ error?: string;
46
+ data?: {
47
+ routes: any[];
48
+ summary: string;
49
+ total_distance: {
50
+ value: number;
51
+ text: string;
52
+ };
53
+ total_duration: {
54
+ value: number;
55
+ text: string;
56
+ };
57
+ };
58
+ }
59
+ interface ElevationResponse {
60
+ success: boolean;
61
+ error?: string;
62
+ data?: Array<{
63
+ elevation: number;
64
+ location: {
65
+ lat: number;
66
+ lng: number;
67
+ };
68
+ }>;
69
+ }
70
+ declare class PlacesSearcher {
71
+ private mapsTools;
72
+ private newPlacesService;
73
+ constructor(apiKey?: string);
74
+ searchNearby(params: {
75
+ center: {
76
+ value: string;
77
+ isCoordinates: boolean;
78
+ };
79
+ keyword?: string;
80
+ radius?: number;
81
+ openNow?: boolean;
82
+ minRating?: number;
83
+ }): Promise<SearchNearbyResponse>;
84
+ getPlaceDetails(placeId: string): Promise<PlaceDetailsResponse>;
85
+ geocode(address: string): Promise<GeocodeResponse>;
86
+ reverseGeocode(latitude: number, longitude: number): Promise<ReverseGeocodeResponse>;
87
+ calculateDistanceMatrix(origins: string[], destinations: string[], mode?: "driving" | "walking" | "bicycling" | "transit"): Promise<DistanceMatrixResponse>;
88
+ getDirections(origin: string, destination: string, mode?: "driving" | "walking" | "bicycling" | "transit", departure_time?: string, arrival_time?: string): Promise<DirectionsResponse>;
89
+ getElevation(locations: Array<{
90
+ latitude: number;
91
+ longitude: number;
92
+ }>): Promise<ElevationResponse>;
93
+ }
94
+
95
+ declare class NewPlacesService {
96
+ private client;
97
+ private readonly defaultLanguage;
98
+ private readonly placeFieldMask;
99
+ constructor(apiKey?: string);
100
+ getPlaceDetails(placeId: string): Promise<{
101
+ name: any;
102
+ place_id: string;
103
+ formatted_address: any;
104
+ geometry: {
105
+ location: {
106
+ lat: any;
107
+ lng: any;
108
+ };
109
+ };
110
+ rating: any;
111
+ user_ratings_total: any;
112
+ opening_hours: {
113
+ open_now: boolean;
114
+ weekday_text: string[];
115
+ } | undefined;
116
+ formatted_phone_number: any;
117
+ website: any;
118
+ price_level: any;
119
+ reviews: any;
120
+ photos: any;
121
+ }>;
122
+ private transformPlaceResponse;
123
+ private extractLegacyPlaceId;
124
+ private isCurrentlyOpen;
125
+ private getLocalTimeComponents;
126
+ private formatOpeningHours;
127
+ private extractErrorMessage;
128
+ }
129
+
1
130
  declare const Logger: {
2
131
  log: (...args: any[]) => void;
3
132
  error: (...args: any[]) => void;
4
133
  };
5
134
 
6
- export { Logger };
135
+ export { Logger, NewPlacesService, PlacesSearcher };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{a}from"./chunk-W2DM2HDK.js";export{a as Logger};
1
+ import{a,b,c}from"./chunk-6ECLWDYH.js";export{b as Logger,a as NewPlacesService,c as PlacesSearcher};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cablate/mcp-google-map",
3
- "version": "0.0.18",
3
+ "version": "0.0.20",
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",
@@ -16,6 +16,11 @@
16
16
  "build": "tsup --dts",
17
17
  "start": "node dist/cli.js",
18
18
  "dev": "cross-env NODE_ENV=development tsup --watch",
19
+ "test": "tsx tests/smoke.test.ts",
20
+ "test:e2e": "tsx tests/smoke.test.ts",
21
+ "lint": "eslint .",
22
+ "format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"",
23
+ "format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"",
19
24
  "prepublishOnly": "npm run build"
20
25
  },
21
26
  "engines": {
@@ -46,16 +51,18 @@
46
51
  },
47
52
  "dependencies": {
48
53
  "@googlemaps/google-maps-services-js": "^3.4.0",
49
- "@modelcontextprotocol/sdk": "^1.11.0",
54
+ "@googlemaps/places": "^2.1.0",
55
+ "@modelcontextprotocol/sdk": "^1.27.1",
50
56
  "@types/yargs": "^17.0.33",
51
57
  "cross-env": "^7.0.3",
52
58
  "dotenv": "^16.4.7",
53
59
  "express": "^4.21.2",
54
60
  "yargs": "^17.7.2",
55
- "zod": "^3.24.2"
61
+ "zod": "^3.25.0"
56
62
  },
57
63
  "devDependencies": {
58
- "@types/express": "^5.0.0",
64
+ "@eslint/js": "^9.39.4",
65
+ "@types/express": "^4.17.21",
59
66
  "@types/jest": "^29.5.14",
60
67
  "@types/js-yaml": "^4.0.9",
61
68
  "@types/node": "^20.17.0",
@@ -68,6 +75,7 @@
68
75
  "ts-jest": "^29.2.5",
69
76
  "tsup": "^8.4.0",
70
77
  "tsx": "^4.19.2",
71
- "typescript": "^5.7.3"
78
+ "typescript": "^5.7.3",
79
+ "typescript-eslint": "^8.57.0"
72
80
  }
73
81
  }
@@ -1 +0,0 @@
1
- var r={log:(...o)=>{console.error("[INFO]",...o)},error:(...o)=>{console.error("[ERROR]",...o)}};export{r as a};