Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

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

### Tips for Better Results

Expand Down
87 changes: 87 additions & 0 deletions src/prompts/CleanGpsTracePrompt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright (c) Mapbox, Inc.
// Licensed under the MIT License.

import { BasePrompt } from './BasePrompt.js';
import type {
PromptArgument,
PromptMessage
} from '@modelcontextprotocol/sdk/types.js';

/**
* Prompt for cleaning and snapping noisy GPS traces to actual roads.
*
* This prompt guides the agent through:
* 1. Parsing and validating GPS coordinates
* 2. Using map matching to snap the trace to roads
* 3. Visualizing before/after comparison
* 4. Analyzing confidence scores and improvements
*
* Example queries:
* - "Clean up this GPS trace from my bike ride"
* - "Fix GPS drift in my recorded delivery route"
* - "Snap this tracking data to actual roads"
*/
export class CleanGpsTracePrompt extends BasePrompt {
readonly name = 'clean-gps-trace';
readonly description =
'Clean and snap noisy GPS traces to actual roads using map matching';

readonly arguments: PromptArgument[] = [
{
name: 'coordinates',
description:
'GPS coordinates as array of [longitude, latitude] pairs or JSON string',
required: true
},
{
name: 'mode',
description:
'Travel mode: driving, walking, or cycling (default: driving)',
required: false
},
{
name: 'timestamps',
description:
'Optional Unix timestamps for each coordinate (as JSON array)',
required: false
}
];

getMessages(args: Record<string, string>): PromptMessage[] {
this.validateArguments(args);

const { coordinates, mode = 'driving', timestamps } = args;

return [
{
role: 'user',
content: {
type: 'text',
text: `Clean and snap this GPS trace to actual roads using map matching.

GPS Coordinates: ${coordinates}
Travel Mode: ${mode}${timestamps ? `\nTimestamps: ${timestamps}` : ''}

Please follow these steps:
1. Parse and validate the GPS coordinates (ensure they're in [longitude, latitude] format)
2. Use map_matching_tool with:
- coordinates: parsed coordinate array
- profile: mapbox/${mode}
${timestamps ? '- timestamps: parsed timestamp array\n ' : ''}- geometries: geojson (to get the matched route)
- annotations: true (to get confidence scores)
3. Create a visual comparison showing:
- Original noisy GPS trace (in red/orange)
- Cleaned matched route (in blue/green)
- Confidence scores for the matching
4. Provide a summary including:
- Number of points processed
- Average confidence score
- Distance before and after matching
- Any segments with low confidence that may need review

Format the output to clearly show the improvements from map matching.`
}
}
];
}
}
100 changes: 100 additions & 0 deletions src/prompts/OptimizeDeliveriesPrompt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright (c) Mapbox, Inc.
// Licensed under the MIT License.

import { BasePrompt } from './BasePrompt.js';
import type {
PromptArgument,
PromptMessage
} from '@modelcontextprotocol/sdk/types.js';

/**
* Prompt for optimizing multi-stop routes with time windows and constraints.
*
* This prompt guides the agent through:
* 1. Geocoding addresses (if needed)
* 2. Setting up optimization constraints
* 3. Running the optimization API
* 4. Visualizing the optimized route
* 5. Showing time and distance savings
*
* Example queries:
* - "Optimize my delivery route for these 10 addresses"
* - "What's the best order to visit these locations?"
* - "Plan a multi-stop trip with time constraints"
*/
export class OptimizeDeliveriesPrompt extends BasePrompt {
readonly name = 'optimize-deliveries';
readonly description =
'Find the optimal route for multiple stops with optional time windows and capacity constraints';

readonly arguments: PromptArgument[] = [
{
name: 'stops',
description:
'List of stops as addresses or coordinates (comma-separated or JSON array)',
required: true
},
{
name: 'profile',
description:
'Routing profile: driving, driving-traffic, cycling, or walking (default: driving-traffic)',
required: false
},
{
name: 'start_location',
description: 'Optional starting location (if different from first stop)',
required: false
},
{
name: 'end_location',
description: 'Optional ending location (if different from last stop)',
required: false
}
];

getMessages(args: Record<string, string>): PromptMessage[] {
this.validateArguments(args);

const {
stops,
profile = 'driving-traffic',
start_location,
end_location
} = args;

return [
{
role: 'user',
content: {
type: 'text',
text: `Optimize the route for multiple stops to find the most efficient order.

Stops: ${stops}
Profile: ${profile}${start_location ? `\nStart Location: ${start_location}` : ''}${end_location ? `\nEnd Location: ${end_location}` : ''}

Please follow these steps:
1. Parse the stops list (handle both address strings and coordinates)
2. Geocode any addresses to get coordinates (use search_and_geocode_tool)
3. Prepare the optimization request:
- Convert stops to shipments format (each stop is a delivery)
- Set source=first and destination=last (or use specified start/end locations)
- Use profile: mapbox/${profile}
4. Call optimization_tool with the shipment configuration
5. Display the results:
- Show the optimized route on a map with numbered waypoints
- Provide the optimized order of stops
- Show total distance and estimated time
- Compare against the original order (if applicable):
* Time saved
* Distance saved
* Efficiency gain percentage
6. Provide turn-by-turn summary for the optimized route

Format the output to clearly show the optimization benefits and the recommended stop order.

Note: The Optimization API is asynchronous, so the tool will poll for results. This may take a few seconds.`
}
}
];
}
}
4 changes: 4 additions & 0 deletions src/prompts/promptRegistry.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// Copyright (c) Mapbox, Inc.
// Licensed under the MIT License.

import { CleanGpsTracePrompt } from './CleanGpsTracePrompt.js';
import { FindPlacesNearbyPrompt } from './FindPlacesNearbyPrompt.js';
import { GetDirectionsPrompt } from './GetDirectionsPrompt.js';
import { OptimizeDeliveriesPrompt } from './OptimizeDeliveriesPrompt.js';
import { ShowReachableAreasPrompt } from './ShowReachableAreasPrompt.js';

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

// Instantiate all prompts
const ALL_PROMPTS = [
new CleanGpsTracePrompt(),
new FindPlacesNearbyPrompt(),
new GetDirectionsPrompt(),
new OptimizeDeliveriesPrompt(),
new ShowReachableAreasPrompt()
] as const;

Expand Down
72 changes: 70 additions & 2 deletions test/prompts/promptRegistry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ describe('Prompt Registry', () => {
test('returns all registered prompts', () => {
const prompts = getAllPrompts();

// Should have at least the 3 initial prompts
expect(prompts.length).toBeGreaterThanOrEqual(3);
// Should have at least the 5 prompts
expect(prompts.length).toBeGreaterThanOrEqual(5);

// Verify expected prompts are present
const promptNames = prompts.map((p) => p.name);
expect(promptNames).toContain('clean-gps-trace');
expect(promptNames).toContain('find-places-nearby');
expect(promptNames).toContain('get-directions');
expect(promptNames).toContain('optimize-deliveries');
expect(promptNames).toContain('show-reachable-areas');
});

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

test('returns correct prompt instances', () => {
const promptNames = [
'clean-gps-trace',
'find-places-nearby',
'get-directions',
'optimize-deliveries',
'show-reachable-areas'
];

Expand Down Expand Up @@ -185,5 +189,69 @@ describe('Prompt Registry', () => {
const modeArg = metadata.arguments?.find((a) => a.name === 'mode');
expect(modeArg?.required).toBe(false);
});

test('clean-gps-trace has correct metadata', () => {
const prompt = getPromptByName('clean-gps-trace');
expect(prompt).toBeDefined();

const metadata = prompt!.getMetadata();

expect(metadata.name).toBe('clean-gps-trace');
expect(metadata.description).toContain('GPS');

// Should have coordinates, mode, timestamps arguments
const argNames = metadata.arguments?.map((a) => a.name) || [];
expect(argNames).toContain('coordinates');
expect(argNames).toContain('mode');
expect(argNames).toContain('timestamps');

// coordinates should be required
const coordinatesArg = metadata.arguments?.find(
(a) => a.name === 'coordinates'
);
expect(coordinatesArg?.required).toBe(true);

// mode and timestamps should be optional
const modeArg = metadata.arguments?.find((a) => a.name === 'mode');
expect(modeArg?.required).toBe(false);

const timestampsArg = metadata.arguments?.find(
(a) => a.name === 'timestamps'
);
expect(timestampsArg?.required).toBe(false);
});

test('optimize-deliveries has correct metadata', () => {
const prompt = getPromptByName('optimize-deliveries');
expect(prompt).toBeDefined();

const metadata = prompt!.getMetadata();

expect(metadata.name).toBe('optimize-deliveries');
expect(metadata.description).toContain('optimal');

// Should have stops, profile, start_location, end_location arguments
const argNames = metadata.arguments?.map((a) => a.name) || [];
expect(argNames).toContain('stops');
expect(argNames).toContain('profile');
expect(argNames).toContain('start_location');
expect(argNames).toContain('end_location');

// stops should be required
const stopsArg = metadata.arguments?.find((a) => a.name === 'stops');
expect(stopsArg?.required).toBe(true);

// profile, start_location, end_location should be optional
const profileArg = metadata.arguments?.find((a) => a.name === 'profile');
expect(profileArg?.required).toBe(false);

const startArg = metadata.arguments?.find(
(a) => a.name === 'start_location'
);
expect(startArg?.required).toBe(false);

const endArg = metadata.arguments?.find((a) => a.name === 'end_location');
expect(endArg?.required).toBe(false);
});
});
});