@cablate/mcp-google-map 0.0.4 → 0.0.5

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
@@ -1,113 +1,325 @@
1
- # MCP Google Map Server
2
-
3
- A powerful Model Context Protocol (MCP) server providing comprehensive Google Maps API integration with LLM processing capabilities.
4
-
5
- ## Features
6
-
7
- ### Google Maps Features
8
-
9
- - **Location Search**
10
-
11
- - Search for places near a specific location with customizable radius and filters
12
- - Get detailed place information including ratings, opening hours, and contact details
13
-
14
- - **Geocoding Services**
15
-
16
- - Convert addresses to coordinates (geocoding)
17
- - Convert coordinates to addresses (reverse geocoding)
18
-
19
- - **Distance & Directions**
20
-
21
- - Calculate distances and travel times between multiple origins and destinations
22
- - Get detailed directions between two points with step-by-step instructions
23
- - Support for different travel modes (driving, walking, bicycling, transit)
24
-
25
- - **Elevation Data**
26
- - Retrieve elevation data for specific locations
27
-
28
- ## Installation
29
-
30
- ### Via NPM
31
-
32
- ```bash
33
- npm install -g @cablate/mcp-google-map
34
- ```
35
-
36
- ## Usage
37
-
38
- ### Command Line
39
-
40
- ```bash
41
- mcp-google-map
42
- ```
43
-
44
- ### Integration with [Dive Desktop](https://github.com/OpenAgentPlatform/Dive)
45
-
46
- 1. Click "+ Add MCP Server" in Dive Desktop
47
- 2. Copy and paste the following configuration:
48
-
49
- ```json
50
- {
51
- "mcpServers": {
52
- "google-map": {
53
- "command": "npx",
54
- "args": ["-y", "@cablate/mcp-google-map"],
55
- "env": {
56
- "GOOGLE_MAPS_API_KEY": "your_api_key"
57
- },
58
- "enabled": true
59
- }
60
- }
61
- }
62
- ```
63
-
64
- 3. Click "Save" to complete the installation
65
-
66
- ## Available Tools
67
-
68
- The server provides the following tools:
69
-
70
- 1. **search_nearby** - Search for places near a specific location
71
- 2. **get_place_details** - Get detailed information about a specific place
72
- 3. **maps_geocode** - Convert an address to coordinates
73
- 4. **maps_reverse_geocode** - Convert coordinates to an address
74
- 5. **maps_distance_matrix** - Calculate distances and times between multiple origins and destinations
75
- 6. **maps_directions** - Get directions between two points
76
- 7. **maps_elevation** - Get elevation data for specific locations
77
-
78
- ## Google Maps API Setup
79
-
80
- To use this service, you need to:
81
-
82
- 1. Create a project in [Google Cloud Console](https://console.cloud.google.com/)
83
- 2. Enable Google Maps API services
84
- 3. Obtain an API key
85
- 4. Set the `GOOGLE_MAPS_API_KEY` environment variable
86
-
87
- ## Tech Stack
88
-
89
- - TypeScript
90
- - Node.js
91
- - Google Maps Services JS
92
- - Model Context Protocol SDK
93
-
94
- ## License
95
-
96
- MIT
97
-
98
- ## Contributing
99
-
100
- Community participation and contributions are welcome! Here's how you can contribute:
101
-
102
- - ⭐️ Star the project if you find it helpful
103
- - 🐛 Submit Issues: Report bugs or provide suggestions
104
- - 🔧 Create Pull Requests: Submit code improvements
105
-
106
- ## Contact
107
-
108
- If you have any questions or suggestions, feel free to reach out:
109
-
110
- - 📧 Email: [reahtuoo310109@gmail.com](mailto:reahtuoo310109@gmail.com)
111
- - 📧 GitHub: [CabLate](https://github.com/cablate/)
112
- - 🤝 Collaboration: Welcome to discuss project cooperation
113
- - 📚 Technical Guidance: Sincere welcome for suggestions and guidance
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
+ ## Features
12
+
13
+ ### 🗺️ Google Maps Integration
14
+
15
+ - **Location Search**
16
+ - Search for places near a specific location with customizable radius and filters
17
+ - Get detailed place information including ratings, opening hours, and contact details
18
+
19
+ - **Geocoding Services**
20
+ - Convert addresses to coordinates (geocoding)
21
+ - Convert coordinates to addresses (reverse geocoding)
22
+
23
+ - **Distance & Directions**
24
+ - Calculate distances and travel times between multiple origins and destinations
25
+ - Get detailed turn-by-turn directions between two points
26
+ - Support for different travel modes (driving, walking, bicycling, transit)
27
+
28
+ - **Elevation Data**
29
+ - Retrieve elevation data for specific locations
30
+
31
+ ### 🚀 Advanced Features
32
+
33
+ - **Streamable HTTP Transport**: Latest MCP protocol with real-time streaming capabilities
34
+ - **Session Management**: Stateful sessions with UUID-based identification
35
+ - **Multiple Connection Support**: Handle multiple concurrent client connections
36
+ - **Echo Service**: Built-in testing tool for MCP server functionality
37
+
38
+ ## Installation
39
+
40
+ ### Via NPM
41
+
42
+ ```bash
43
+ npm install -g @cablate/mcp-google-map
44
+ ```
45
+
46
+ ### Via NPX (Recommended)
47
+
48
+ ```bash
49
+ npx @cablate/mcp-google-map
50
+ ```
51
+
52
+ ## Quick Start
53
+
54
+ ### 1. Get Google Maps API Key
55
+
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
80
+ ```
81
+
82
+ ### 3. Server Endpoints
83
+
84
+ - **Main MCP Endpoint**: `http://localhost:3000/mcp`
85
+ - **Available Tools**: 8 tools including Google Maps services and echo
86
+
87
+ ## Configuration
88
+
89
+ ### Environment Variables
90
+
91
+ Create a `.env` file in your working directory:
92
+
93
+ ```env
94
+ # Required
95
+ GOOGLE_MAPS_API_KEY=your_google_maps_api_key_here
96
+
97
+ # Optional
98
+ MCP_SERVER_PORT=3000
99
+ ```
100
+
101
+ ## Available Tools
102
+
103
+ The server provides the following tools:
104
+
105
+ ### Google Maps Tools
106
+
107
+ 1. **search_nearby** - Search for nearby places based on location, with optional filtering by keywords, distance, rating, and operating hours
108
+ 2. **get_place_details** - Get detailed information about a specific place including contact details, reviews, ratings, and operating hours
109
+ 3. **maps_geocode** - Convert addresses or place names to geographic coordinates (latitude and longitude)
110
+ 4. **maps_reverse_geocode** - Convert geographic coordinates to a human-readable address
111
+ 5. **maps_distance_matrix** - Calculate travel distances and durations between multiple origins and destinations
112
+ 6. **maps_directions** - Get detailed turn-by-turn navigation directions between two locations
113
+ 7. **maps_elevation** - Get elevation data (height above sea level) for specific geographic locations
114
+
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
+ ## Development
207
+
208
+ ### Local Development
209
+
210
+ ```bash
211
+ # Clone the repository
212
+ git clone https://github.com/cablate/mcp-google-map.git
213
+ cd mcp-google-map
214
+
215
+ # Install dependencies
216
+ npm install
217
+
218
+ # Set up environment variables
219
+ cp .env.example .env
220
+ # Edit .env with your API key
221
+
222
+ # Build the project
223
+ npm run build
224
+
225
+ # Start the server
226
+ npm start
227
+
228
+ # Or run in development mode
229
+ npm run dev
230
+ ```
231
+
232
+ ### Project Structure
233
+
234
+ ```
235
+ src/
236
+ ├── cli.ts # Main CLI entry point
237
+ ├── config.ts # Server configuration
238
+ ├── index.ts # Package exports
239
+ ├── core/
240
+ │ └── BaseMcpServer.ts # Base MCP server with streamable HTTP
241
+ └── tools/
242
+ ├── echo.ts # Echo service tool
243
+ └── maps/ # Google Maps tools
244
+ ├── toolclass.ts # Google Maps API client
245
+ ├── searchPlaces.ts # Maps service layer
246
+ ├── searchNearby.ts # Search nearby places
247
+ ├── placeDetails.ts # Place details
248
+ ├── geocode.ts # Geocoding
249
+ ├── reverseGeocode.ts # Reverse geocoding
250
+ ├── distanceMatrix.ts # Distance matrix
251
+ ├── directions.ts # Directions
252
+ └── elevation.ts # Elevation data
253
+ ```
254
+
255
+ ## Tech Stack
256
+
257
+ - **TypeScript** - Type-safe development
258
+ - **Node.js** - Runtime environment
259
+ - **Google Maps Services JS** - Google Maps API integration
260
+ - **Model Context Protocol SDK** - MCP protocol implementation
261
+ - **Express.js** - HTTP server framework
262
+ - **Zod** - Schema validation
263
+
264
+ ## Security Considerations
265
+
266
+ - API keys are handled server-side for security
267
+ - DNS rebinding protection available for production
268
+ - Input validation using Zod schemas
269
+ - Error handling and logging
270
+
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
+ ## License
292
+
293
+ MIT
294
+
295
+ ## Contributing
296
+
297
+ Community participation and contributions are welcome! Here's how you can contribute:
298
+
299
+ - ⭐️ Star the project if you find it helpful
300
+ - 🐛 Submit Issues: Report bugs or provide suggestions
301
+ - 🔧 Create Pull Requests: Submit code improvements
302
+ - 📖 Documentation: Help improve documentation
303
+
304
+ ## Contact
305
+
306
+ If you have any questions or suggestions, feel free to reach out:
307
+
308
+ - 📧 Email: [reahtuoo310109@gmail.com](mailto:reahtuoo310109@gmail.com)
309
+ - 💻 GitHub: [CabLate](https://github.com/cablate/)
310
+ - 🤝 Collaboration: Welcome to discuss project cooperation
311
+ - 📚 Technical Guidance: Sincere welcome for suggestions and guidance
312
+
313
+ ## Changelog
314
+
315
+ ### v0.0.5
316
+ - Added streamable HTTP transport support
317
+ - Improved CLI interface with emoji indicators
318
+ - Enhanced error handling and logging
319
+ - Added comprehensive tool descriptions for LLM integration
320
+ - Updated to latest MCP SDK version
321
+
322
+ ### v0.0.4
323
+ - Initial release with basic Google Maps integration
324
+ - Support for location search, geocoding, and directions
325
+ - Compatible with MCP protocol
@@ -0,0 +1 @@
1
+ var n=(r=>(r.DISPLAY="display",r.LLM="llm",r))(n||{}),o={log:(...l)=>{},error:(...l)=>{}};export{n as a,o as b};
package/dist/cli.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ declare function startServer(): Promise<void>;
3
+
4
+ export { startServer };
package/dist/cli.js ADDED
@@ -0,0 +1,3 @@
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};
@@ -0,0 +1,10 @@
1
+ declare enum ImagePurpose {
2
+ DISPLAY = "display",
3
+ LLM = "llm"
4
+ }
5
+ declare const Logger: {
6
+ log: (...args: any[]) => void;
7
+ error: (...args: any[]) => void;
8
+ };
9
+
10
+ export { ImagePurpose, Logger };