Skip to content

Commit 07d59a4

Browse files
[prompts] Add MCP prompts for multi-step workflows
Implement three high-value prompts that orchestrate multiple tools: 1. create-and-preview-style: Create a map style with automatic token management. Checks for existing public tokens, creates one if needed, then creates the style and generates a preview link. 2. build-custom-map: Use conversational AI to build themed map styles (e.g., "dark cyberpunk", "nature-focused"). Leverages the Style Builder tool for AI-powered style generation. 3. analyze-geojson: Complete GeoJSON analysis workflow with validation, bounding box calculation, coordinate conversion, and visualization. Infrastructure: - BasePrompt abstract class for all prompts - promptRegistry for centralized prompt management - Integration with MCP server using Zod schemas - Full test coverage (37 new tests, all passing) Documentation updated in README.md and CLAUDE.md with usage examples. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
1 parent 1a7c843 commit 07d59a4

12 files changed

+1039
-2
lines changed

CLAUDE.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,25 @@ The codebase organizes into:
1818
- `src/index.ts` - Main entry point with .env loading and server initialization
1919
- `src/config/toolConfig.ts` - Configuration parser for tool filtering and MCP-UI toggles
2020
- `src/tools/` - MCP tool implementations with `BaseTool` abstract class and registry
21+
- `src/prompts/` - MCP prompt implementations with `BasePrompt` abstract class and registry
2122
- `src/resources/` - Static reference data (style specs, token scopes, Streets v8 fields)
2223
- `src/utils/` - HTTP pipeline, JWT parsing, tracing, and version utilities
2324

2425
## Key Architectural Patterns
2526

2627
**Tool Architecture:** All tools extend `BaseTool<InputSchema, OutputSchema>`. Tools auto-validate inputs using Zod schemas. Each tool lives in `src/tools/tool-name-tool/` with separate `*.schema.ts` and `*.tool.ts` files.
2728

29+
**Prompt Architecture:** All prompts extend `BasePrompt` abstract class. Prompts orchestrate multi-step workflows, guiding AI assistants through complex tasks with best practices built-in. Each prompt lives in `src/prompts/` with separate files per prompt (e.g., `CreateAndPreviewStylePrompt.ts`). Prompts use kebab-case naming (e.g., `create-and-preview-style`).
30+
2831
**HTTP Pipeline System:** "Never patch global.fetch—use HttpPipeline with dependency injection instead." The `HttpPipeline` class applies policies (User-Agent, retry logic) via a chain-of-responsibility pattern. See `src/utils/httpPipeline.ts:20`.
2932

3033
**Resource System:** Static reference data exposed as MCP resources using URI pattern `resource://mapbox-*`, including style layer specs, Streets v8 field definitions, and token scope documentation.
3134

3235
**Token Management:** Tools receive `MAPBOX_ACCESS_TOKEN` via `extra.authInfo.token` or environment variable. Token scope validation is critical—most tool failures stem from insufficient scopes (see `README.md` for per-tool requirements).
3336

34-
**Tool Registry:** Tools are auto-discovered via `src/tools/index.ts` exports. No manual registration required—just export from index.
37+
**Tool Registry:** Tools are auto-discovered via `src/tools/toolRegistry.ts` exports. No manual registration required—just export from registry.
38+
39+
**Prompt Registry:** Prompts are registered in `src/prompts/promptRegistry.ts`. To add a new prompt, create the prompt class and add it to the `ALL_PROMPTS` array. The main server automatically registers all prompts with proper Zod schema conversion.
3540

3641
## Essential Workflows
3742

README.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ https://github.com/user-attachments/assets/8b1b8ef2-9fba-4951-bc9a-beaed4f6aff6
1515
- [Hosted MCP Endpoint](#hosted-mcp-endpoint)
1616
- [Getting Your Mapbox Access Token](#getting-your-mapbox-access-token)
1717
- [Tools](#tools)
18+
- [Prompts](#prompts)
1819
- [Documentation Tools](#documentation-tools)
1920
- [Reference Tools](#reference-tools)
2021
- [Style Management Tools](#style-management-tools)
@@ -453,6 +454,98 @@ An array of four numbers representing the bounding box: `[minX, minY, maxX, maxY
453454
- "Calculate the bounding box of this GeoJSON file" (then upload a .geojson file)
454455
- "What's the bounding box for the coordinates in the uploaded parks.geojson file?"
455456

457+
## Prompts
458+
459+
MCP Prompts are pre-built workflow templates that guide AI assistants through multi-step tasks. They orchestrate multiple tools in the correct sequence, providing best practices and error handling built-in.
460+
461+
**Available Prompts:**
462+
463+
### create-and-preview-style
464+
465+
Create a new Mapbox map style and generate a shareable preview link with automatic token management.
466+
467+
**Arguments:**
468+
469+
- `style_name` (required): Name for the new map style
470+
- `style_description` (optional): Description of the style theme or purpose
471+
- `base_style` (optional): Base style to start from (e.g., "streets-v12", "dark-v11")
472+
- `preview_location` (optional): Location to center the preview map
473+
- `preview_zoom` (optional): Zoom level for the preview (0-22, default: 12)
474+
475+
**What it does:**
476+
477+
1. Checks for an existing public token with `styles:read` scope
478+
2. Creates a new public token if needed
479+
3. Creates the map style
480+
4. Generates a preview link
481+
482+
**Example usage:**
483+
484+
```
485+
Use prompt: create-and-preview-style
486+
Arguments:
487+
style_name: "My Custom Map"
488+
style_description: "A dark-themed map for nighttime navigation"
489+
base_style: "dark-v11"
490+
preview_location: "San Francisco"
491+
preview_zoom: "13"
492+
```
493+
494+
### build-custom-map
495+
496+
Use conversational AI to build a custom styled map based on a theme description.
497+
498+
**Arguments:**
499+
500+
- `theme` (required): Theme description (e.g., "dark cyberpunk", "nature-focused", "minimal monochrome")
501+
- `emphasis` (optional): Features to emphasize (e.g., "parks and green spaces", "transit lines")
502+
- `preview_location` (optional): Location to center the preview map
503+
- `preview_zoom` (optional): Zoom level for the preview (0-22, default: 12)
504+
505+
**What it does:**
506+
507+
1. Uses the Style Builder tool to create a themed style based on your description
508+
2. Creates the style in your Mapbox account
509+
3. Generates a preview link
510+
511+
**Example usage:**
512+
513+
```
514+
Use prompt: build-custom-map
515+
Arguments:
516+
theme: "retro 80s neon"
517+
emphasis: "nightlife and entertainment venues"
518+
preview_location: "Tokyo"
519+
preview_zoom: "14"
520+
```
521+
522+
### analyze-geojson
523+
524+
Analyze and visualize GeoJSON data with automatic validation and bounding box calculation.
525+
526+
**Arguments:**
527+
528+
- `geojson_data` (required): GeoJSON object or string to analyze
529+
- `show_bounds` (optional): Calculate and display bounding box (true/false, default: true)
530+
- `convert_coordinates` (optional): Provide Web Mercator conversion examples (true/false, default: false)
531+
532+
**What it does:**
533+
534+
1. Validates GeoJSON format
535+
2. Calculates bounding box (if requested)
536+
3. Provides coordinate conversion examples (if requested)
537+
4. Generates an interactive visualization link
538+
539+
**Example usage:**
540+
541+
```
542+
Use prompt: analyze-geojson
543+
Arguments:
544+
geojson_data: {"type":"FeatureCollection","features":[...]}
545+
show_bounds: "true"
546+
convert_coordinates: "false"
547+
```
548+
456549
## Resources
457550

458551
This server exposes static reference documentation as MCP Resources. While these are primarily accessed through the `get_reference_tool`, MCP clients that fully support the resources protocol can access them directly.

src/index.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ import { SpanStatusCode } from '@opentelemetry/api';
1111

1212
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
1313
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
14+
import { z } from 'zod';
1415
import { parseToolConfigFromArgs, filterTools } from './config/toolConfig.js';
1516
import { getAllTools } from './tools/toolRegistry.js';
1617
import { getAllResources } from './resources/resourceRegistry.js';
18+
import { getAllPrompts } from './prompts/promptRegistry.js';
1719
import { getVersionInfo } from './utils/versionUtils.js';
1820
import {
1921
initializeTracing,
@@ -64,7 +66,8 @@ const server = new McpServer(
6466
{
6567
capabilities: {
6668
tools: {},
67-
resources: {}
69+
resources: {},
70+
prompts: {}
6871
}
6972
}
7073
);
@@ -80,6 +83,37 @@ resources.forEach((resource) => {
8083
resource.installTo(server);
8184
});
8285

86+
// Register prompts to the server
87+
const prompts = getAllPrompts();
88+
prompts.forEach((prompt) => {
89+
const argsSchema: Record<string, z.ZodString | z.ZodOptional<z.ZodString>> =
90+
{};
91+
92+
// Convert prompt arguments to Zod schema format
93+
prompt.arguments.forEach((arg) => {
94+
const zodString = z.string().describe(arg.description);
95+
argsSchema[arg.name] = arg.required ? zodString : zodString.optional();
96+
});
97+
98+
server.registerPrompt(
99+
prompt.name,
100+
{
101+
description: prompt.description,
102+
argsSchema: argsSchema
103+
},
104+
async (args) => {
105+
// Filter out undefined values from optional arguments
106+
const filteredArgs: Record<string, string> = {};
107+
for (const [key, value] of Object.entries(args || {})) {
108+
if (value !== undefined) {
109+
filteredArgs[key] = value;
110+
}
111+
}
112+
return prompt.execute(filteredArgs);
113+
}
114+
);
115+
});
116+
83117
async function main() {
84118
// Send MCP logging messages about .env loading
85119
if (envLoadError) {
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Copyright (c) Mapbox, Inc.
2+
// Licensed under the MIT License.
3+
4+
import type { PromptMessage } from '@modelcontextprotocol/sdk/types.js';
5+
import { BasePrompt, type PromptArgument } from './BasePrompt.js';
6+
7+
/**
8+
* Prompt for analyzing and visualizing GeoJSON data
9+
*
10+
* This prompt orchestrates multiple tools to:
11+
* 1. Validate GeoJSON format
12+
* 2. Calculate bounding box
13+
* 3. Generate visualization link
14+
* 4. Provide analysis summary
15+
*/
16+
export class AnalyzeGeojsonPrompt extends BasePrompt {
17+
readonly name = 'analyze-geojson';
18+
readonly description =
19+
'Analyze and visualize GeoJSON data. Validates format, calculates bounding box, and generates an interactive map visualization.';
20+
21+
readonly arguments: ReadonlyArray<PromptArgument> = [
22+
{
23+
name: 'geojson_data',
24+
description:
25+
'GeoJSON object or string to analyze (Point, LineString, Polygon, Feature, FeatureCollection, etc.)',
26+
required: true
27+
},
28+
{
29+
name: 'show_bounds',
30+
description:
31+
'Whether to calculate and display the bounding box (true/false, default: true)',
32+
required: false
33+
},
34+
{
35+
name: 'convert_coordinates',
36+
description:
37+
'Whether to provide coordinate conversion examples for Web Mercator (true/false, default: false)',
38+
required: false
39+
}
40+
];
41+
42+
getMessages(args: Record<string, string>): PromptMessage[] {
43+
const geojsonData = args['geojson_data'];
44+
const showBounds = args['show_bounds'] !== 'false'; // Default to true
45+
const convertCoordinates = args['convert_coordinates'] === 'true'; // Default to false
46+
47+
let instructionText = `Analyze the provided GeoJSON data and generate an interactive visualization.
48+
49+
**GeoJSON Data:**
50+
\`\`\`json
51+
${geojsonData}
52+
\`\`\`
53+
54+
Follow these steps to analyze and visualize the data:
55+
56+
1. **Parse and validate the GeoJSON**
57+
- Parse the GeoJSON data (it may be provided as a string or object)
58+
- Verify it's valid GeoJSON with proper structure
59+
- Identify the geometry type (Point, LineString, Polygon, Feature, FeatureCollection, etc.)
60+
- Count the number of features if it's a FeatureCollection`;
61+
62+
if (showBounds) {
63+
instructionText += `
64+
65+
2. **Calculate bounding box**
66+
- Use the bounding_box_tool to calculate the geographic extent
67+
- The tool will return [minX, minY, maxX, maxY] (west, south, east, north)
68+
- Present the bounds in a clear format:
69+
* Western edge (minX): [longitude]
70+
* Eastern edge (maxX): [longitude]
71+
* Southern edge (minY): [latitude]
72+
* Northern edge (maxY): [latitude]
73+
- Calculate and display the width and height in degrees`;
74+
}
75+
76+
if (convertCoordinates) {
77+
instructionText += `
78+
79+
3. **Provide coordinate conversion examples**
80+
- Use the coordinate_conversion_tool to show Web Mercator equivalents
81+
- Convert a sample point from the GeoJSON from EPSG:4326 to EPSG:3857
82+
- Explain when Web Mercator coordinates might be useful (web mapping, tilesets)`;
83+
}
84+
85+
const nextStep =
86+
showBounds && convertCoordinates
87+
? '4'
88+
: showBounds || convertCoordinates
89+
? '3'
90+
: '2';
91+
92+
instructionText += `
93+
94+
${nextStep}. **Generate visualization**
95+
- Use the geojson_preview_tool to create an interactive map
96+
- This will generate a geojson.io URL where the data can be viewed and edited
97+
- The visualization will show:
98+
* The geometry rendered on a map
99+
* Feature properties in a side panel
100+
* Interactive editing capabilities
101+
102+
${parseInt(nextStep) + 1}. **Provide analysis summary**
103+
Present a comprehensive summary including:
104+
- **Geometry type**: [Point/LineString/Polygon/etc.]
105+
- **Feature count**: [number of features]
106+
- **Coordinate system**: WGS84 (EPSG:4326)`;
107+
108+
if (showBounds) {
109+
instructionText += `\n - **Geographic extent**: [bounding box summary]`;
110+
}
111+
112+
instructionText += `\n - **Visualization link**: [geojson.io URL - clickable]
113+
- **Properties**: [list any feature properties found]
114+
- **Data quality notes**: [any issues or observations]
115+
116+
**Analysis guidelines:**
117+
- Check for common issues: invalid coordinates, missing required fields, topology errors
118+
- Note if coordinates are in the correct order (longitude, latitude)
119+
- Identify if the data uses right-hand rule for polygon winding
120+
- Suggest improvements if the GeoJSON could be optimized
121+
- For large datasets, note that the preview URL may be long
122+
123+
**Important notes:**
124+
- GeoJSON coordinates must be [longitude, latitude], not [latitude, longitude]
125+
- Valid longitude range: -180 to 180
126+
- Valid latitude range: -90 to 90
127+
- The preview tool works best with small to medium-sized datasets`;
128+
129+
return [
130+
{
131+
role: 'user',
132+
content: {
133+
type: 'text',
134+
text: instructionText
135+
}
136+
}
137+
];
138+
}
139+
}

0 commit comments

Comments
 (0)