@cablate/mcp-google-map 0.0.20 → 0.0.21
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/dist/chunk-Z5SWQKLS.js +1 -0
- package/dist/cli.js +2 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-6ECLWDYH.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Client as N,Language as x}from"@googlemaps/google-maps-services-js";import R from"dotenv";R.config();function y(c){let r=c?.response?.status,e=c?.response?.data?.error_message,t=c?.response?.data?.status;return r===403?"API key invalid or required API not enabled. Check: console.cloud.google.com \u2192 APIs & Services \u2192 Enable the relevant API (Places, Geocoding, etc.)":r===429?"API quota exceeded. Wait and retry, or check quota at console.cloud.google.com \u2192 Quotas":t==="ZERO_RESULTS"?"No results found. Try broader search terms or a larger radius.":t==="OVER_QUERY_LIMIT"?"API quota exceeded. Wait and retry, or upgrade your billing plan.":t==="REQUEST_DENIED"?`Request denied by Google Maps API. ${e||"Check your API key and enabled APIs."}`:t==="INVALID_REQUEST"?`Invalid request parameters. ${e||"Check your input values."}`:e?`${e} (HTTP ${r})`:c instanceof Error?c.message:String(c)}var P=class{constructor(r){this.defaultLanguage=x.en;if(this.client=new N({}),this.apiKey=r||process.env.GOOGLE_MAPS_API_KEY||"",!this.apiKey)throw new Error("Google Maps API Key is required")}async searchNearbyPlaces(r){let e={location:r.location,radius:r.radius||1e3,keyword:r.keyword,opennow:r.openNow,language:this.defaultLanguage,key:this.apiKey};try{let n=(await this.client.placesNearby({params:e})).data.results;return r.minRating&&(n=n.filter(a=>(a.rating||0)>=(r.minRating||0))),n}catch(t){throw d.error("Error in searchNearbyPlaces:",t),new Error(`Failed to search nearby places: ${y(t)}`)}}async getPlaceDetails(r){try{return(await this.client.placeDetails({params:{place_id:r,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(e){throw d.error("Error in getPlaceDetails:",e),new Error(`Failed to get place details for ${r}: ${y(e)}`)}}async geocodeAddress(r){try{let e=await this.client.geocode({params:{address:r,key:this.apiKey,language:this.defaultLanguage}});if(e.data.results.length===0)throw new Error(`No location found for address: "${r}"`);let t=e.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(e){throw d.error("Error in geocodeAddress:",e),new Error(`Failed to geocode address "${r}": ${y(e)}`)}}parseCoordinates(r){let e=r.split(",").map(t=>parseFloat(t.trim()));if(e.length!==2||isNaN(e[0])||isNaN(e[1]))throw new Error(`Invalid coordinate format: "${r}". Please use "latitude,longitude" format (e.g., "25.033,121.564"`);return{lat:e[0],lng:e[1]}}async getLocation(r){return r.isCoordinates?this.parseCoordinates(r.value):this.geocodeAddress(r.value)}async geocode(r){try{let e=await this.geocodeAddress(r);return{location:{lat:e.lat,lng:e.lng},formatted_address:e.formatted_address||"",place_id:e.place_id||""}}catch(e){throw d.error("Error in geocode:",e),new Error(`Failed to geocode address "${r}": ${y(e)}`)}}async reverseGeocode(r,e){try{let t=await this.client.reverseGeocode({params:{latlng:{lat:r,lng:e},language:this.defaultLanguage,key:this.apiKey}});if(t.data.results.length===0)throw new Error(`No address found for coordinates: (${r}, ${e})`);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 (${r}, ${e}): ${y(t)}`)}}async calculateDistanceMatrix(r,e,t="driving"){try{let a=(await this.client.distancematrix({params:{origins:r,destinations:e,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(r,e,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:r,destination:e,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 "${r}" to "${e}" 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 "${r}" to "${e}": ${y(o)}`)}}async getElevation(r){try{let e=r.map(a=>({lat:a.latitude,lng:a.longitude})),n=(await this.client.elevation({params:{locations:e,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:e[o]}))}catch(e){throw d.error("Error in getElevation:",e),new Error(`Failed to get elevation data for ${r.length} location(s): ${y(e)}`)}}};import{PlacesClient as T}from"@googlemaps/places";var _=class{constructor(r){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:r||process.env.GOOGLE_MAPS_API_KEY||""}),!r&&!process.env.GOOGLE_MAPS_API_KEY)throw new Error("Google Maps API Key is required")}async getPlaceDetails(r){try{let e=`places/${r}`,[t]=await this.client.getPlace({name:e,languageCode:this.defaultLanguage},{otherArgs:{headers:{"X-Goog-FieldMask":this.placeFieldMask}}});return this.transformPlaceResponse(t)}catch(e){throw d.error("Error in getPlaceDetails (New API):",e),new Error(`Failed to get place details for ${r}: ${this.extractErrorMessage(e)}`)}}transformPlaceResponse(r){return{name:r.displayName?.text||r.name||"",place_id:this.extractLegacyPlaceId(r),formatted_address:r.formattedAddress||"",geometry:{location:{lat:r.location?.latitude||0,lng:r.location?.longitude||0}},rating:r.rating||0,user_ratings_total:r.userRatingCount||0,opening_hours:r.regularOpeningHours?{open_now:this.isCurrentlyOpen(r.regularOpeningHours,r.utcOffsetMinutes,r.currentOpeningHours),weekday_text:this.formatOpeningHours(r.regularOpeningHours)}:void 0,formatted_phone_number:r.nationalPhoneNumber||"",website:r.websiteUri||"",price_level:r.priceLevel||0,reviews:r.reviews?.map(e=>({rating:e.rating||0,text:e.text?.text||"",time:e.publishTime?.seconds||0,author_name:e.authorAttribution?.displayName||""}))||[],photos:r.photos?.map(e=>({photo_reference:e.name||"",height:e.heightPx||0,width:e.widthPx||0}))||[]}}extractLegacyPlaceId(r){let e=r?.name;if(typeof e=="string"&&e.startsWith("places/")){let t=e.substring(7);if(t)return t}return r?.id||""}isCurrentlyOpen(r,e,t){if(typeof t?.openNow=="boolean")return t.openNow;if(typeof r?.openNow=="boolean")return r.openNow;let n=r?.periods;if(!Array.isArray(n)||n.length===0)return!1;let a=1440,o=a*7,{day:l,minutes:h}=this.getLocalTimeComponents(e),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),A=f(s?.closeTime);if(g===void 0||D===void 0)continue;let b=g*a+D,w;p===void 0||A===void 0?w=b+a:w=p*a+A,w<=b&&(w+=o);let v=u;for(;v<b;)v+=o;if(v>=b&&v<w)return!0}return!1}getLocalTimeComponents(r){let e=new Date;if(typeof r=="number"&&Number.isFinite(r)){let t=new Date(e.getTime()+r*6e4);return{day:t.getUTCDay(),minutes:t.getUTCHours()*60+t.getUTCMinutes()}}return{day:e.getDay(),minutes:e.getHours()*60+e.getMinutes()}}formatOpeningHours(r){return r?.weekdayDescriptions||[]}extractErrorMessage(r){let e=r?.code,t=r?.message||r?.details;return e===7||e===403?"API key invalid or Places API (New) not enabled. Check: console.cloud.google.com \u2192 APIs & Services \u2192 Enable 'Places API (New)'":e===8||e===429?"API quota exceeded. Wait and retry, or check quota at console.cloud.google.com \u2192 Quotas":t||(r instanceof Error?r.message:String(r))}};var E=class{constructor(r){this.mapsTools=new P(r),this.newPlacesService=new _(r)}async searchNearby(r){try{let e=await this.mapsTools.getLocation(r.center),t=await this.mapsTools.searchNearbyPlaces({location:e,keyword:r.keyword,radius:r.radius,openNow:r.openNow,minRating:r.minRating});return{location:e,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(e){return{success:!1,error:e instanceof Error?e.message:"An error occurred during search"}}}async getPlaceDetails(r){try{let e=await this.newPlacesService.getPlaceDetails(r);return{success:!0,data:{name:e.name,address:e.formatted_address,location:e.geometry?.location,rating:e.rating,total_ratings:e.user_ratings_total,open_now:e.opening_hours?.open_now,phone:e.formatted_phone_number,website:e.website,price_level:e.price_level,reviews:e.reviews?.map(t=>({rating:t.rating,text:t.text,time:t.time,author_name:t.author_name}))}}}catch(e){return{success:!1,error:e instanceof Error?e.message:"An error occurred while getting place details"}}}async geocode(r){try{return{success:!0,data:await this.mapsTools.geocode(r)}}catch(e){return{success:!1,error:e instanceof Error?e.message:"An error occurred while geocoding address"}}}async reverseGeocode(r,e){try{return{success:!0,data:await this.mapsTools.reverseGeocode(r,e)}}catch(t){return{success:!1,error:t instanceof Error?t.message:"An error occurred during reverse geocoding"}}}async calculateDistanceMatrix(r,e,t="driving"){try{return{success:!0,data:await this.mapsTools.calculateDistanceMatrix(r,e,t)}}catch(n){return{success:!1,error:n instanceof Error?n.message:"An error occurred while calculating distance matrix"}}}async getDirections(r,e,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(r,e,t,o,l)}}catch(o){return{success:!1,error:o instanceof Error?o.message:"An error occurred while getting directions"}}}async getElevation(r){try{return{success:!0,data:await this.mapsTools.getElevation(r)}}catch(e){return{success:!1,error:e instanceof Error?e.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{b as s,c}from"./chunk-
|
|
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};
|
|
2
|
+
import{b as s,c}from"./chunk-Z5SWQKLS.js";import{config as K}from"dotenv";import{resolve as I}from"path";import Pe from"yargs";import{hideBin as Ae}from"yargs/helpers";import{z as d}from"zod";import{AsyncLocalStorage as H}from"async_hooks";var T=new H;function p(){return T.getStore()?.apiKey||process.env.GOOGLE_MAPS_API_KEY}function N(t,e){return T.run(t,e)}var G="search_nearby",z="Search for nearby places based on location, with optional filtering by keywords, distance, rating, and operating hours",k={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 J(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 y={NAME:G,DESCRIPTION:z,SCHEMA:k,ACTION:J};import{z as L}from"zod";var j="get_place_details",V="Get detailed information about a specific place including contact details, reviews, ratings, and operating hours",q={placeId:L.string().describe("Google Maps place ID")};async function F(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 f={NAME:j,DESCRIPTION:V,SCHEMA:q,ACTION:F};import{z as W}from"zod";var Z="maps_geocode",Y="Convert addresses or place names to geographic coordinates (latitude and longitude)",B={address:W.string().describe("Address or place name to convert to coordinates")};async function U(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 v={NAME:Z,DESCRIPTION:Y,SCHEMA:B,ACTION:U};import{z as _}from"zod";var Q="maps_reverse_geocode",X="Convert geographic coordinates (latitude and longitude) to a human-readable address",ee={latitude:_.number().describe("Latitude coordinate"),longitude:_.number().describe("Longitude coordinate")};async function re(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 h={NAME:Q,DESCRIPTION:X,SCHEMA:ee,ACTION:re};import{z as S}from"zod";var te="maps_distance_matrix",oe="Calculate travel distances and durations between multiple origins and destinations for different travel modes",se={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 ne(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 E={NAME:te,DESCRIPTION:oe,SCHEMA:se,ACTION:ne};import{z as P}from"zod";var ie="maps_directions",ae="Get detailed turn-by-turn navigation directions between two locations with route information",ce={origin:P.string().describe("Starting point address or coordinates"),destination:P.string().describe("Destination address or coordinates"),mode:P.enum(["driving","walking","bicycling","transit"]).default("driving").describe("Travel mode for directions"),departure_time:P.string().optional().describe("Departure time (ISO string format)"),arrival_time:P.string().optional().describe("Arrival time (ISO string format)")};async function pe(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 A={NAME:ie,DESCRIPTION:ae,SCHEMA:ce,ACTION:pe};import{z as x}from"zod";var le="maps_elevation",de="Get elevation data (height above sea level) for specific geographic locations",me={locations:x.array(x.object({latitude:x.number().describe("Latitude coordinate"),longitude:x.number().describe("Longitude coordinate")})).describe("List of locations to get elevation data for")};async function ge(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 M={NAME:le,DESCRIPTION:de,SCHEMA:me,ACTION:ge};var m={readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},ue=[{name:"MCP-Server",portEnvVar:"MCP_SERVER_PORT",tools:[{name:y.NAME,description:y.DESCRIPTION,schema:y.SCHEMA,annotations:m,action:t=>y.ACTION(t)},{name:f.NAME,description:f.DESCRIPTION,schema:f.SCHEMA,annotations:m,action:t=>f.ACTION(t)},{name:v.NAME,description:v.DESCRIPTION,schema:v.SCHEMA,annotations:m,action:t=>v.ACTION(t)},{name:h.NAME,description:h.DESCRIPTION,schema:h.SCHEMA,annotations:m,action:t=>h.ACTION(t)},{name:E.NAME,description:E.DESCRIPTION,schema:E.SCHEMA,annotations:m,action:t=>E.ACTION(t)},{name:A.NAME,description:A.DESCRIPTION,schema:A.SCHEMA,annotations:m,action:t=>A.ACTION(t)},{name:M.NAME,description:M.DESCRIPTION,schema:M.SCHEMA,annotations:m,action:t=>M.ACTION(t)}]}],R=ue;import{McpServer as ye}from"@modelcontextprotocol/sdk/server/mcp.js";import{StreamableHTTPServerTransport as fe}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import{isInitializeRequest as ve}from"@modelcontextprotocol/sdk/types.js";import w from"express";import{randomUUID as he}from"crypto";import{z as Se}from"zod";var b=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 Ee="0.0.1",C=class{constructor(e,o){this.sessions={};this.httpServer=null;this.serverName=e,this.tools=o,this.server=this.createMcpServer()}createMcpServer(){let e=new ye({name:this.serverName,version:Ee},{capabilities:{logging:{},tools:{}}});return this.tools.forEach(o=>{e.registerTool(o.name,{description:o.description,inputSchema:Se.object(o.schema),annotations:o.annotations},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=w();o.use(w.json()),o.post("/mcp",async(n,i)=>{let a=n.headers["mcp-session-id"],l,g=b.getInstance().getApiKey(n);if(s.log(`${this.serverName} API key received from request context`),a&&this.sessions[a])l=this.sessions[a],g&&(l.apiKey=g);else if(!a&&ve(n.body)){let u=new fe({sessionIdGenerator:()=>he(),onsessioninitialized:O=>{this.sessions[O]=l,s.log(`[${this.serverName}] New session initialized: ${O}`)}});l={transport:u,apiKey:g},u.onclose=()=>{u.sessionId&&(delete this.sessions[u.sessionId],s.log(`[${this.serverName}] Session closed: ${u.sessionId}`))},await this.createMcpServer().connect(u)}else{i.status(400).json({jsonrpc:"2.0",error:{code:-32e3,message:"Bad Request: No valid session ID provided"},id:null});return}await N({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],g=b.getInstance().getApiKey(n);g&&(l.apiKey=g),await N({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 Me}from"url";import{dirname as be}from"path";import{readFileSync as xe}from"fs";var Ce=Me(import.meta.url),$=be(Ce);K({path:I(process.cwd(),".env")});K({path:I($,"../.env")});async function Ne(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=R.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 C(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 Ie=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")),Oe=import.meta.url===`file://${process.argv[1]}`;if(Ie||Oe){let t="0.0.0";try{let o=I($,"../package.json");t=JSON.parse(xe(o,"utf-8")).version}catch{t="0.0.0"}let e=Pe(Ae(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("")),Ne(e.port,e.apikey).catch(o=>{s.error("\u274C Failed to start server:",o),process.exit(1)})}export{Ne as startServer};
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a,b,c}from"./chunk-
|
|
1
|
+
import{a,b,c}from"./chunk-Z5SWQKLS.js";export{b as Logger,a as NewPlacesService,c as PlacesSearcher};
|
package/package.json
CHANGED
package/dist/chunk-6ECLWDYH.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
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};
|