Skip to content

Conversation

@mattpodwysocki
Copy link
Contributor

@mattpodwysocki mattpodwysocki commented Jan 26, 2026

Summary

Introduces a new search-along-route MCP prompt that orchestrates existing tools to find places of interest along a route corridor. This enables use cases like:

  • "I want to go from Seattle to Portland, is there a Starbucks along the way?"
  • "Find gas stations along my route from LA to San Francisco"
  • "Show me rest stops between Denver and Salt Lake City"

Motivation

With the addition of offline geospatial tools (buffer, point-in-polygon, distance), we can now compose powerful workflows. Search-along-route combines:

  1. directions_tool - Get route geometry
  2. buffer_tool - Create corridor around route
  3. search_and_geocode_tool / category_search_tool - Find POIs
  4. point_in_polygon_tool - Filter to corridor
  5. distance_tool - Order by proximity

This showcases how offline tools enhance search workflows without requiring new API endpoints.

Implementation

New Files

  • src/prompts/SearchAlongRoutePrompt.ts - Prompt implementation with detailed orchestration instructions

Modified Files

  • src/prompts/promptRegistry.ts - Register new prompt
  • test/prompts/promptRegistry.test.ts - Test coverage for new prompt

Prompt Arguments

Required:

  • from - Starting location (address, place name, or coordinates)
  • to - Destination location (address, place name, or coordinates)
  • search_for - Type of place to search for (e.g., "Starbucks", "gas stations")

Optional:

  • mode - Travel mode: driving, walking, or cycling (default: driving)
  • buffer_meters - Search corridor width on each side of route (default: 1000m)

Orchestration Steps

The prompt guides the AI through:

  1. Geocode locations if needed
  2. Get the route using directions_tool
  3. Create search corridor using buffer_tool (configurable width)
  4. Search for places within bounding box
  5. Filter results using point_in_polygon_tool
  6. Order results by route progress
  7. Visualize and present on a map with clear formatting

Example Usage

User: "I want to go from Seattle to Portland, is there a Starbucks along the way?"

Prompt provides:
- Route line visualization
- All Starbucks locations within 1km corridor
- Each location's distance from route
- Position along route (e.g., "15 miles into your trip")
- Total route distance and time

Testing

✅ All 581 tests pass

  • Added metadata tests for new prompt
  • Verified argument validation (3 required, 2 optional)
  • Follows existing prompt patterns

Prompt Generation

Entering the following data:

  • Start: Seattle, WA
  • End: Portland, OR
  • POI: Starbucks

Output is the following:

{
  description: "Finds specific types of places along a route between two locations, using buffer analysis and POI search";
  messages: [
    0,
    {
      role: "user",

      content: {
        type: "text",
        text: `Find Starbucks along the driving route from Seattle, WA to Portland, OR.

Please follow these steps:

1. **Geocode locations** (if needed):
   - Use search_and_geocode_tool to convert Seattle, WA and Portland, OR to coordinates if they're not already in coordinate format

2. **Get the route**:
   - Use directions_tool with profile=driving to get the route geometry between the two points
   - Extract the route LineString geometry from the response

3. **Create search corridor**:
   - Use buffer_tool on the route geometry with distance=1000 meters
   - This creates a polygon corridor around the route

4. **Search for places**:
   - Use category_search_tool or search_and_geocode_tool to find "Starbucks"
   - Use the buffer polygon's bounding box as the search area
   - Consider searching in segments if the route is very long (>100km)

5. **Filter results**:
   - Use point_in_polygon_tool to confirm each result is actually within the buffer corridor
   - Use distance_tool to calculate distance from each POI to the route
   - Order results by their position along the route (use bearing or route progress)

6. **Visualize and present**:
   - Display a map showing:
     * The route line
     * Start and end markers
     * All found locations as markers
     * Optionally, the buffer corridor (semi-transparent)
   - Provide a list of results including:
     * Name and address of each place
     * Distance from route
     * Approximate position along route (e.g., "15 miles into your trip")
     * Total results found

7. **Additional context**:
   - Mention the total route distance and estimated travel time
   - Note if no results were found and suggest widening the search corridor
   - If many results (>20), show top 10-15 and mention there are more

Tips:
- For long routes, you may need to search in multiple segments
- Adjust buffer_meters if too many/few results (wider for highways, narrower for city streets)
- Consider the travel mode when setting buffer defaults (walking=500m, driving=1000m)

Make the output clear, actionable, and well-formatted.`,
      },
    },
  ];
}

Design Decisions

Why a prompt vs a tool?

  • Composition: Reuses 5+ existing tools
  • Flexibility: Users can adjust buffer, POI types, mode
  • Educational: Shows how to combine geospatial operations
  • Maintainability: No new API dependencies

Why not extend existing prompts?

  • Different use case: route corridor vs point-based search
  • Requires buffer analysis (not in find-places-nearby)
  • Ordered by route progress (not just distance from point)

Follow-up Ideas

Future enhancements could include:

  • Multiple search categories in one query
  • Minimum spacing between results
  • Route segment clustering (for very long routes)
  • Integration with optimization_tool for multi-stop planning

@mattpodwysocki mattpodwysocki requested a review from a team as a code owner January 26, 2026 18:48
mattpodwysocki and others added 2 commits January 26, 2026 14:03
Introduces a new MCP prompt that orchestrates existing tools to find
places of interest along a route corridor. This enables use cases like
"find Starbucks along my route from Seattle to Portland."

Features:
- Combines directions_tool, buffer_tool, and search tools
- Configurable buffer distance (default 1km corridor)
- Filters results to route corridor using point_in_polygon_tool
- Orders results by distance from route
- Supports all travel modes (driving, walking, cycling)

Implementation:
- Created SearchAlongRoutePrompt with 5 arguments (from, to, search_for, mode, buffer_meters)
- Added to prompt registry
- Comprehensive test coverage (28 tests passing)
- Follows existing prompt patterns

Example usage:
"Find gas stations along the drive from LA to San Francisco"

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…rompt

Creates detailed testing guide with:
- 5 basic examples (coffee shops, gas stations, rest stops, restaurants, bike shops)
- MCP Inspector testing instructions
- Advanced examples (long routes, buffer adjustments)
- Verification checklist
- Common use cases

Also updates README with new example prompt and reference to examples directory.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@mattpodwysocki mattpodwysocki force-pushed the add-search-along-route-prompt branch from 4973bc5 to 0d44345 Compare January 26, 2026 19:03
mattpodwysocki and others added 12 commits January 26, 2026 14:33
Addresses issue where buffer_tool fails on very long LineStrings
(e.g., Seattle to Portland ~280km route).

Changes:
- Added explicit route length check before buffering
- Provides two strategies based on route length:
  * Short routes (<100km): Buffer entire route at once
  * Long routes (≥100km): Use segmentation or point sampling
- Moved segmentation guidance from "tips" to main workflow
- Added specific thresholds (100km, 200 coordinate points)
- Better error prevention with upfront approach selection

This prevents the AI from attempting buffer operations that will
fail and then falling back to suboptimal approaches like simple
bounding boxes that include results far from the route.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Addresses token/complexity issues with long routes by using simpler
filtering strategies based on route length.

Approach:
- SHORT routes (<50km): Full buffer corridor + point-in-polygon filtering
  * Most accurate, precise corridor results
  * Manageable complexity

- LONG routes (≥50km): Bounding box + distance filtering
  * Pragmatic approach avoiding token limits
  * Still filters to approximate corridor
  * Explicitly notes to user that results are approximate

Why this works:
- Leesburg to Herndon (~30km): Uses precise corridor filtering
- Seattle to Portland (~280km): Uses simpler bbox approach
- Prevents AI from hitting token/complexity limits
- Sets clear expectations with users about filtering method

This is more practical than complex segmentation strategies that
still overwhelm the AI on very long routes.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixes issue where Claude Desktop tries to use mapboxgl directly,
which is not available in that environment.

Changes:
- Replaced generic "display a map" with explicit static_map_image_tool usage
- Instructs AI to use route geometry as path overlay
- Adds start/end markers and found locations
- Works in Claude Desktop and other MCP clients without mapboxgl

This ensures the prompt works across all MCP client environments.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Addresses token/timeout issues with very long routes (e.g., Seattle to Portland ~280km).

Three-tier strategy:

1. SHORT routes (<50km): Full corridor with buffer + point-in-polygon
   - Most accurate, precise filtering
   - Example: Leesburg to Herndon

2. MEDIUM routes (50-150km): Bounding box + distance filtering
   - Balanced approach, filters to general corridor
   - Avoids full buffer complexity

3. VERY LONG routes (>150km): Strategic point sampling
   - Samples 5-7 evenly spaced points along route
   - Searches small radius (5-10km) around each point
   - Prevents token/timeout issues
   - Example: Seattle to Portland - samples major points along I-5

This approach ensures the prompt works reliably for:
- Short local routes (full precision)
- Regional routes (balanced filtering)
- Long interstate routes (strategic sampling)

Each approach explicitly informs the user which strategy was used
and sets appropriate expectations about result coverage.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…ry long routes

Addresses slow map encoding for routes with many coordinate points.

Changes:
- SHORT routes (<50km): Use simplify_tool with tolerance=0.001 before mapping
  * Reduces coordinates while maintaining accuracy
  * Shows detailed map with route, markers

- MEDIUM routes (50-150km): Use simplify_tool with tolerance=0.01
  * Aggressive simplification for faster encoding
  * Shows simplified map with fewer markers (top 5-8)

- VERY LONG routes (>150km): Skip map generation entirely
  * Avoids slow encoding of complex 280km+ routes
  * Notes to user: "Map visualization skipped for route length"
  * Focuses on text list of results

This dramatically improves performance:
- Leesburg to Herndon: Fast detailed map
- Seattle to Portland: No slow map encoding, instant results list

The simplify_tool reduces coordinate count before static_map_image_tool,
preventing the long encoding delays the user experienced.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…ions

Addresses validation error where Claude was passing route geometry
incorrectly to bounding_box_tool, causing input schema validation failures.

Changes:
- Added explicit instructions for extracting route LineString coordinates
  from directions_tool response as array of [lon, lat] pairs

- SHORT routes: Clarified buffer_tool input format and how to get bbox
  from buffer polygon using bounding_box_tool

- MEDIUM routes: Added explicit bounding_box_tool usage instructions
  with correct geometry format: {"geometry": [[lon1,lat1], [lon2,lat2], ...]}

- VERY LONG routes: Changed to use proximity parameter instead of bbox
  to avoid coordinate extraction complexity

- Added bbox format specification for category_search_tool: "minLon,minLat,maxLon,maxLat"

This provides Claude with clear, unambiguous instructions about:
1. How to extract coordinates from directions response
2. Correct format for passing to buffer_tool and bounding_box_tool
3. How to use the resulting bbox with search tools

Fixes MCP error -32602 validation failures on Leesburg to Herndon route.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
After encountering buffer issues even on short routes (Leesburg to Herndon),
this simplifies the entire workflow to use a single, reliable approach for
all route lengths: sample points + proximity searches.

Changes:
- REMOVED: buffer_tool, bounding_box_tool, point_in_polygon_tool complexity
- REMOVED: Three different strategies for different route lengths
- NEW: Single unified approach - sample route points and proximity search

How it works:
1. Get route with directions_tool
2. Sample points along route (density based on length):
   - Short routes (<50km): Every 5-10 points (comprehensive coverage)
   - Medium routes (50-150km): Every 15-20 points
   - Long routes (>150km): 5-7 strategic points
3. For each sample point, use category_search_tool with proximity parameter
4. Combine results, deduplicate, order by distance from start

Why this is better:
- Simple and reliable - no complex geometry operations
- No buffer validation errors or "buffer result is very large" issues
- No bbox calculation failures
- Works consistently for ALL route lengths
- Fast execution, no token/timeout issues
- Proximity searches naturally cover route corridor

Leesburg to Herndon: Sample every 5-10 points, comprehensive coverage
Seattle to Portland: Sample 5-7 major points, fast and reliable

This is the pragmatic solution that actually works in production.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
SimplifyTool expects closed polygons (first and last coordinates match)
but routes are open LineStrings, causing error:
"First and last Position are not equivalent"

Changes:
- REMOVED: simplify_tool preprocessing step entirely
- Routes < 150km: Pass route geometry directly to static_map_image_tool
- Routes ≥ 150km: Skip map generation to avoid slow rendering

Why this works:
- static_map_image_tool can handle route LineStrings without simplification
- Routes are not polygons and don't need to be closed
- Removing simplify_tool removes another point of failure
- Keeps the workflow even simpler and more reliable

This is now the absolute simplest approach:
1. Get route with directions_tool
2. Sample points and do proximity searches
3. Pass route directly to static_map_image_tool (no preprocessing)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Addresses confusion where Claude tried to use "Starbucks" as a category
with category_search_tool, which failed because Starbucks is a brand name,
not a category. Claude self-corrected but we can avoid the initial error.

Changes:
- Added explicit guidance on choosing the right search tool:
  * Specific places/brands (e.g., "Starbucks", "McDonald's")
    → Use search_and_geocode_tool
  * Categories (e.g., "coffee shops", "gas stations")
    → Use category_search_tool

- Updated parameter names to match tool schemas:
  * search_and_geocode_tool: q="${search_for}", proximity="lon,lat"
  * category_search_tool: types="${search_for}", proximity="lon,lat"

This helps Claude make the right choice upfront, though it will still
need to use judgment. The self-correction behavior shows the AI can
recover, but this should reduce unnecessary errors.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Even short routes (Leesburg to Herndon) take a long time to encode
with static_map_image_tool. Make maps optional and focus on fast
text results instead.

Changes:
- Skip static_map_image_tool by default
- Note to user: "Map visualization can be generated separately if needed"
- Focus on excellent text results with all necessary data:
  * Route summary (distance, time)
  * Each result with name, address, distance from start, coordinates
  * Sampling strategy used

- Include coordinates in results so user could map them separately
- Only generate map if user explicitly requests it

Why this is better:
- Much faster workflow (no encoding delays)
- User gets results immediately
- Client apps (Claude Desktop, etc.) may have their own map capabilities
- User can always request "show me a map" as a follow-up if desired
- Avoids the slow static image encoding that was delaying results

For Leesburg to Herndon: Results appear instantly instead of waiting
for static map encoding.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
All searches at sample points are independent and can be executed
concurrently, but Claude may not realize this without explicit guidance.

Changes:
- Added "IMPORTANT - Execute searches in parallel" section
- Instructs Claude to make ALL search tool calls in a single message
- Provides example: "If you have 5 sample points, make 5 search calls in one turn"
- Emphasizes this dramatically speeds up the workflow

Why this matters:
- Sequential searches: 5 points × 2 seconds = 10 seconds
- Parallel searches: 5 points in one turn = 2-3 seconds total
- Especially important for medium/long routes with many sample points

The agent (Claude) ultimately decides execution strategy, but explicit
guidance should encourage parallel execution when possible. This is
standard MCP pattern - multiple independent tool calls in one message.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Distance calculations are also independent and can be parallelized,
just like the search operations.

Changes:
- Added explicit parallel execution guidance for distance_tool calls
- Instructs Claude to make ALL distance calculations in a single message
- Example: "If you have 10 unique results, make 10 distance_tool calls in one turn"

Why this matters:
- Sequential distance calculations: 10 results × 0.5s = 5 seconds
- Parallel distance calculations: 10 results in one turn = 0.5-1s total
- Further speeds up the workflow after search results are collected

Complete parallel execution strategy:
1. Get route (1 call)
2. Search all sample points in parallel (N calls in one turn)
3. Calculate all distances in parallel (M calls in one turn)
4. Present results

This minimizes total round-trips and execution time.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant