Skip to content

Commit 83ae101

Browse files
cablateclaude
andcommitted
feat: add photo URLs to maps_place_details via maxPhotos param
- Add optional maxPhotos param (0-10, default 0) to maps_place_details - Default returns photo_count only (no extra tokens) - When maxPhotos > 0, resolves actual photo URLs via getPhotoMedia API - Add getPhotoUri method to NewPlacesService - Update CLI exec to pass maxPhotos param - Update tool description to document photo behavior Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ffff728 commit 83ae101

4 files changed

Lines changed: 42 additions & 5 deletions

File tree

src/cli.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ async function execTool(toolName: string, params: any, apiKey: string): Promise<
124124
case "place-details":
125125
case "get_place_details":
126126
case "maps_place_details":
127-
return searcher.getPlaceDetails(params.placeId);
127+
return searcher.getPlaceDetails(params.placeId, params.maxPhotos || 0);
128128

129129
case "directions":
130130
case "maps_directions":

src/services/NewPlacesService.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,20 @@ export class NewPlacesService {
142142
}
143143
}
144144

145+
async getPhotoUri(photoName: string, maxWidthPx: number = 800): Promise<string> {
146+
try {
147+
const [response] = await this.client.getPhotoMedia({
148+
name: `${photoName}/media`,
149+
maxWidthPx,
150+
skipHttpRedirect: true,
151+
});
152+
return response.photoUri || "";
153+
} catch (error: any) {
154+
Logger.error("Error in getPhotoUri:", error);
155+
throw new Error(`Failed to get photo URI: ${this.extractErrorMessage(error)}`);
156+
}
157+
}
158+
145159
async getPlaceDetails(placeId: string) {
146160
try {
147161
const placeName = `places/${placeId}`;

src/services/PlacesSearcher.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,10 +194,25 @@ export class PlacesSearcher {
194194
}
195195
}
196196

197-
async getPlaceDetails(placeId: string): Promise<PlaceDetailsResponse> {
197+
async getPlaceDetails(placeId: string, maxPhotos: number = 0): Promise<PlaceDetailsResponse> {
198198
try {
199199
const details = await this.newPlacesService.getPlaceDetails(placeId);
200200

201+
// Resolve photo URLs if requested
202+
let photos: Array<{ url: string; width: number; height: number }> | undefined;
203+
if (maxPhotos > 0 && details.photos?.length > 0) {
204+
const photosToFetch = details.photos.slice(0, maxPhotos);
205+
photos = [];
206+
for (const photo of photosToFetch) {
207+
try {
208+
const url = await this.newPlacesService.getPhotoUri(photo.photo_reference);
209+
photos.push({ url, width: photo.width, height: photo.height });
210+
} catch {
211+
// Skip failed photos silently
212+
}
213+
}
214+
}
215+
201216
return {
202217
success: true,
203218
data: {
@@ -210,6 +225,8 @@ export class PlacesSearcher {
210225
phone: details.formatted_phone_number,
211226
website: details.website,
212227
price_level: details.price_level,
228+
photo_count: details.photos?.length || 0,
229+
...(photos && photos.length > 0 ? { photos } : {}),
213230
reviews: details.reviews?.map((review: any) => ({
214231
rating: review.rating,
215232
text: review.text,

src/tools/maps/placeDetails.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,26 @@ import { getCurrentApiKey } from "../../utils/requestContext.js";
44

55
const NAME = "maps_place_details";
66
const DESCRIPTION =
7-
"Get comprehensive details for a specific place using its Google Maps place_id. Use after search_nearby or maps_search_places to get full information including reviews, phone number, website, opening hours, and photos. Returns everything needed to evaluate or contact a business.";
7+
"Get comprehensive details for a specific place using its Google Maps place_id. Use after search_nearby or maps_search_places to get full information including reviews, phone number, website, and opening hours. Set maxPhotos (1-10) to include photo URLs — omit or set to 0 for no photos (saves tokens).";
88

99
const SCHEMA = {
1010
placeId: z.string().describe("Google Maps place ID"),
11+
maxPhotos: z
12+
.number()
13+
.int()
14+
.min(0)
15+
.max(10)
16+
.optional()
17+
.describe("Number of photo URLs to include (0 = none, max 10). Omit to skip photos and save tokens."),
1118
};
1219

1320
export type PlaceDetailsParams = z.infer<z.ZodObject<typeof SCHEMA>>;
1421

1522
async function ACTION(params: any): Promise<{ content: any[]; isError?: boolean }> {
1623
try {
17-
// Create a new PlacesSearcher instance with the current request's API key
1824
const apiKey = getCurrentApiKey();
1925
const placesSearcher = new PlacesSearcher(apiKey);
20-
const result = await placesSearcher.getPlaceDetails(params.placeId);
26+
const result = await placesSearcher.getPlaceDetails(params.placeId, params.maxPhotos || 0);
2127

2228
if (!result.success) {
2329
return {

0 commit comments

Comments
 (0)