Skip to content

Commit 89a6c2a

Browse files
[prompts] Add clean-gps-trace and optimize-deliveries prompts
Add two new prompts to guide users through using the Map Matching and Optimization tools added in PR #89. New Prompts: - clean-gps-trace: Clean and snap noisy GPS traces to roads * Guides through parsing GPS coordinates * Uses map_matching_tool with proper parameters * Shows before/after visualization with confidence scores * Example: "Clean up this GPS trace from my bike ride" - optimize-deliveries: Optimize multi-stop routes * Handles address geocoding automatically * Configures optimization with shipments format * Shows optimized route with efficiency metrics * Example: "Optimize my delivery route for these addresses" Changes: - Added CleanGpsTracePrompt.ts with map matching workflow - Added OptimizeDeliveriesPrompt.ts with route optimization workflow - Updated promptRegistry.ts with new prompts - Added comprehensive tests in promptRegistry.test.ts - Updated README.md with example prompts Testing: - All 424 tests passing ✓ - New prompt metadata tests verify correct structure - Prompts follow kebab-case naming convention 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 79f11aa commit 89a6c2a

5 files changed

Lines changed: 263 additions & 2 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ Try these prompts with Claude Desktop or other MCP clients after setup:
7676
- "Show me areas reachable within 30 minutes of downtown Portland by car"
7777
- "Calculate a travel time matrix between these 3 hotel locations (Marriott, Sheraton and Hilton) and the convention center in Denver"
7878
- "Find the optimal route visiting these 3 tourist attractions (Golden Gate, Musical Stairs and Fisherman's Wharf) in San Francisco"
79+
- "Clean up this GPS trace from my bike ride and snap it to actual roads"
80+
- "Optimize my delivery route for these 10 addresses: [list of addresses]"
7981

8082
### Tips for Better Results
8183

src/prompts/CleanGpsTracePrompt.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright (c) Mapbox, Inc.
2+
// Licensed under the MIT License.
3+
4+
import { BasePrompt } from './BasePrompt.js';
5+
import type {
6+
PromptArgument,
7+
PromptMessage
8+
} from '@modelcontextprotocol/sdk/types.js';
9+
10+
/**
11+
* Prompt for cleaning and snapping noisy GPS traces to actual roads.
12+
*
13+
* This prompt guides the agent through:
14+
* 1. Parsing and validating GPS coordinates
15+
* 2. Using map matching to snap the trace to roads
16+
* 3. Visualizing before/after comparison
17+
* 4. Analyzing confidence scores and improvements
18+
*
19+
* Example queries:
20+
* - "Clean up this GPS trace from my bike ride"
21+
* - "Fix GPS drift in my recorded delivery route"
22+
* - "Snap this tracking data to actual roads"
23+
*/
24+
export class CleanGpsTracePrompt extends BasePrompt {
25+
readonly name = 'clean-gps-trace';
26+
readonly description =
27+
'Clean and snap noisy GPS traces to actual roads using map matching';
28+
29+
readonly arguments: PromptArgument[] = [
30+
{
31+
name: 'coordinates',
32+
description:
33+
'GPS coordinates as array of [longitude, latitude] pairs or JSON string',
34+
required: true
35+
},
36+
{
37+
name: 'mode',
38+
description:
39+
'Travel mode: driving, walking, or cycling (default: driving)',
40+
required: false
41+
},
42+
{
43+
name: 'timestamps',
44+
description:
45+
'Optional Unix timestamps for each coordinate (as JSON array)',
46+
required: false
47+
}
48+
];
49+
50+
getMessages(args: Record<string, string>): PromptMessage[] {
51+
this.validateArguments(args);
52+
53+
const { coordinates, mode = 'driving', timestamps } = args;
54+
55+
return [
56+
{
57+
role: 'user',
58+
content: {
59+
type: 'text',
60+
text: `Clean and snap this GPS trace to actual roads using map matching.
61+
62+
GPS Coordinates: ${coordinates}
63+
Travel Mode: ${mode}${timestamps ? `\nTimestamps: ${timestamps}` : ''}
64+
65+
Please follow these steps:
66+
1. Parse and validate the GPS coordinates (ensure they're in [longitude, latitude] format)
67+
2. Use map_matching_tool with:
68+
- coordinates: parsed coordinate array
69+
- profile: mapbox/${mode}
70+
${timestamps ? '- timestamps: parsed timestamp array\n ' : ''}- geometries: geojson (to get the matched route)
71+
- annotations: true (to get confidence scores)
72+
3. Create a visual comparison showing:
73+
- Original noisy GPS trace (in red/orange)
74+
- Cleaned matched route (in blue/green)
75+
- Confidence scores for the matching
76+
4. Provide a summary including:
77+
- Number of points processed
78+
- Average confidence score
79+
- Distance before and after matching
80+
- Any segments with low confidence that may need review
81+
82+
Format the output to clearly show the improvements from map matching.`
83+
}
84+
}
85+
];
86+
}
87+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Copyright (c) Mapbox, Inc.
2+
// Licensed under the MIT License.
3+
4+
import { BasePrompt } from './BasePrompt.js';
5+
import type {
6+
PromptArgument,
7+
PromptMessage
8+
} from '@modelcontextprotocol/sdk/types.js';
9+
10+
/**
11+
* Prompt for optimizing multi-stop routes with time windows and constraints.
12+
*
13+
* This prompt guides the agent through:
14+
* 1. Geocoding addresses (if needed)
15+
* 2. Setting up optimization constraints
16+
* 3. Running the optimization API
17+
* 4. Visualizing the optimized route
18+
* 5. Showing time and distance savings
19+
*
20+
* Example queries:
21+
* - "Optimize my delivery route for these 10 addresses"
22+
* - "What's the best order to visit these locations?"
23+
* - "Plan a multi-stop trip with time constraints"
24+
*/
25+
export class OptimizeDeliveriesPrompt extends BasePrompt {
26+
readonly name = 'optimize-deliveries';
27+
readonly description =
28+
'Find the optimal route for multiple stops with optional time windows and capacity constraints';
29+
30+
readonly arguments: PromptArgument[] = [
31+
{
32+
name: 'stops',
33+
description:
34+
'List of stops as addresses or coordinates (comma-separated or JSON array)',
35+
required: true
36+
},
37+
{
38+
name: 'profile',
39+
description:
40+
'Routing profile: driving, driving-traffic, cycling, or walking (default: driving-traffic)',
41+
required: false
42+
},
43+
{
44+
name: 'start_location',
45+
description: 'Optional starting location (if different from first stop)',
46+
required: false
47+
},
48+
{
49+
name: 'end_location',
50+
description: 'Optional ending location (if different from last stop)',
51+
required: false
52+
}
53+
];
54+
55+
getMessages(args: Record<string, string>): PromptMessage[] {
56+
this.validateArguments(args);
57+
58+
const {
59+
stops,
60+
profile = 'driving-traffic',
61+
start_location,
62+
end_location
63+
} = args;
64+
65+
return [
66+
{
67+
role: 'user',
68+
content: {
69+
type: 'text',
70+
text: `Optimize the route for multiple stops to find the most efficient order.
71+
72+
Stops: ${stops}
73+
Profile: ${profile}${start_location ? `\nStart Location: ${start_location}` : ''}${end_location ? `\nEnd Location: ${end_location}` : ''}
74+
75+
Please follow these steps:
76+
1. Parse the stops list (handle both address strings and coordinates)
77+
2. Geocode any addresses to get coordinates (use search_and_geocode_tool)
78+
3. Prepare the optimization request:
79+
- Convert stops to shipments format (each stop is a delivery)
80+
- Set source=first and destination=last (or use specified start/end locations)
81+
- Use profile: mapbox/${profile}
82+
4. Call optimization_tool with the shipment configuration
83+
5. Display the results:
84+
- Show the optimized route on a map with numbered waypoints
85+
- Provide the optimized order of stops
86+
- Show total distance and estimated time
87+
- Compare against the original order (if applicable):
88+
* Time saved
89+
* Distance saved
90+
* Efficiency gain percentage
91+
6. Provide turn-by-turn summary for the optimized route
92+
93+
Format the output to clearly show the optimization benefits and the recommended stop order.
94+
95+
Note: The Optimization API is asynchronous, so the tool will poll for results. This may take a few seconds.`
96+
}
97+
}
98+
];
99+
}
100+
}

src/prompts/promptRegistry.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
// Copyright (c) Mapbox, Inc.
22
// Licensed under the MIT License.
33

4+
import { CleanGpsTracePrompt } from './CleanGpsTracePrompt.js';
45
import { FindPlacesNearbyPrompt } from './FindPlacesNearbyPrompt.js';
56
import { GetDirectionsPrompt } from './GetDirectionsPrompt.js';
7+
import { OptimizeDeliveriesPrompt } from './OptimizeDeliveriesPrompt.js';
68
import { ShowReachableAreasPrompt } from './ShowReachableAreasPrompt.js';
79

810
/**
@@ -14,8 +16,10 @@ import { ShowReachableAreasPrompt } from './ShowReachableAreasPrompt.js';
1416

1517
// Instantiate all prompts
1618
const ALL_PROMPTS = [
19+
new CleanGpsTracePrompt(),
1720
new FindPlacesNearbyPrompt(),
1821
new GetDirectionsPrompt(),
22+
new OptimizeDeliveriesPrompt(),
1923
new ShowReachableAreasPrompt()
2024
] as const;
2125

test/prompts/promptRegistry.test.ts

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ describe('Prompt Registry', () => {
1212
test('returns all registered prompts', () => {
1313
const prompts = getAllPrompts();
1414

15-
// Should have at least the 3 initial prompts
16-
expect(prompts.length).toBeGreaterThanOrEqual(3);
15+
// Should have at least the 5 prompts
16+
expect(prompts.length).toBeGreaterThanOrEqual(5);
1717

1818
// Verify expected prompts are present
1919
const promptNames = prompts.map((p) => p.name);
20+
expect(promptNames).toContain('clean-gps-trace');
2021
expect(promptNames).toContain('find-places-nearby');
2122
expect(promptNames).toContain('get-directions');
23+
expect(promptNames).toContain('optimize-deliveries');
2224
expect(promptNames).toContain('show-reachable-areas');
2325
});
2426

@@ -83,8 +85,10 @@ describe('Prompt Registry', () => {
8385

8486
test('returns correct prompt instances', () => {
8587
const promptNames = [
88+
'clean-gps-trace',
8689
'find-places-nearby',
8790
'get-directions',
91+
'optimize-deliveries',
8892
'show-reachable-areas'
8993
];
9094

@@ -185,5 +189,69 @@ describe('Prompt Registry', () => {
185189
const modeArg = metadata.arguments?.find((a) => a.name === 'mode');
186190
expect(modeArg?.required).toBe(false);
187191
});
192+
193+
test('clean-gps-trace has correct metadata', () => {
194+
const prompt = getPromptByName('clean-gps-trace');
195+
expect(prompt).toBeDefined();
196+
197+
const metadata = prompt!.getMetadata();
198+
199+
expect(metadata.name).toBe('clean-gps-trace');
200+
expect(metadata.description).toContain('GPS');
201+
202+
// Should have coordinates, mode, timestamps arguments
203+
const argNames = metadata.arguments?.map((a) => a.name) || [];
204+
expect(argNames).toContain('coordinates');
205+
expect(argNames).toContain('mode');
206+
expect(argNames).toContain('timestamps');
207+
208+
// coordinates should be required
209+
const coordinatesArg = metadata.arguments?.find(
210+
(a) => a.name === 'coordinates'
211+
);
212+
expect(coordinatesArg?.required).toBe(true);
213+
214+
// mode and timestamps should be optional
215+
const modeArg = metadata.arguments?.find((a) => a.name === 'mode');
216+
expect(modeArg?.required).toBe(false);
217+
218+
const timestampsArg = metadata.arguments?.find(
219+
(a) => a.name === 'timestamps'
220+
);
221+
expect(timestampsArg?.required).toBe(false);
222+
});
223+
224+
test('optimize-deliveries has correct metadata', () => {
225+
const prompt = getPromptByName('optimize-deliveries');
226+
expect(prompt).toBeDefined();
227+
228+
const metadata = prompt!.getMetadata();
229+
230+
expect(metadata.name).toBe('optimize-deliveries');
231+
expect(metadata.description).toContain('optimal');
232+
233+
// Should have stops, profile, start_location, end_location arguments
234+
const argNames = metadata.arguments?.map((a) => a.name) || [];
235+
expect(argNames).toContain('stops');
236+
expect(argNames).toContain('profile');
237+
expect(argNames).toContain('start_location');
238+
expect(argNames).toContain('end_location');
239+
240+
// stops should be required
241+
const stopsArg = metadata.arguments?.find((a) => a.name === 'stops');
242+
expect(stopsArg?.required).toBe(true);
243+
244+
// profile, start_location, end_location should be optional
245+
const profileArg = metadata.arguments?.find((a) => a.name === 'profile');
246+
expect(profileArg?.required).toBe(false);
247+
248+
const startArg = metadata.arguments?.find(
249+
(a) => a.name === 'start_location'
250+
);
251+
expect(startArg?.required).toBe(false);
252+
253+
const endArg = metadata.arguments?.find((a) => a.name === 'end_location');
254+
expect(endArg?.required).toBe(false);
255+
});
188256
});
189257
});

0 commit comments

Comments
 (0)