Skip to content

Commit 91fc7ad

Browse files
Resolve merge conflicts - include check_color_contrast_tool, compare_styles_tool, and optimize_style_tool
2 parents dab3bf0 + c89f2ec commit 91fc7ad

23 files changed

+4844
-2
lines changed

README.md

Lines changed: 283 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ https://github.com/user-attachments/assets/8b1b8ef2-9fba-4951-bc9a-beaed4f6aff6
2626
- [GeoJSON Preview tool (Beta)](#geojson-preview-tool-beta)
2727
- [Coordinate Conversion tool](#coordinate-conversion-tool)
2828
- [Bounding Box tool](#bounding-box-tool)
29-
- [Color Contrast Checker tool](#color-contrast-checker-tool)
29+
- [compare_styles_tool](#compare_styles_tool)
30+
- [Style Optimization tool](#style-optimization-tool)
3031
- [Resources](#resources)
3132
- [Observability \& Tracing](#observability--tracing)
3233
- [Features](#features)
@@ -190,6 +191,17 @@ Complete set of tools for managing Mapbox styles via the Styles API:
190191
- Returns: URL to open the style preview in browser
191192
- **Note**: This tool automatically fetches the first available public token from your account for the preview URL. Requires at least one public token with `styles:read` scope.
192193

194+
**ValidateStyleTool** - Validate Mapbox style JSON against the Mapbox Style Specification
195+
196+
- Input: `style` (Mapbox style JSON object or JSON string)
197+
- Returns: Validation results including errors, warnings, info messages, and style summary
198+
- Performs comprehensive offline validation checking:
199+
- Required fields (version, sources, layers)
200+
- Valid layer and source types
201+
- Source references and layer IDs
202+
- Common configuration issues
203+
- **Note**: This is an offline validation tool that doesn't require API access or token scopes
204+
193205
**⚠️ Required Token Scopes:**
194206

195207
**All style tools require a valid Mapbox access token with specific scopes. Using a token without the correct scope will result in authentication errors.**
@@ -358,6 +370,165 @@ Generate a geojson.io URL to visualize GeoJSON data. This tool:
358370
- "Generate a preview URL for this GeoJSON data"
359371
- "Create a geojson.io link for my uploaded route.geojson file"
360372

373+
#### Validate GeoJSON tool
374+
375+
Validates GeoJSON objects for correctness, checking structure, coordinates, and geometry types. This offline validation tool performs comprehensive checks on GeoJSON data without requiring API access.
376+
377+
**Parameters:**
378+
379+
- `geojson` (string or object, required): GeoJSON object or JSON string to validate
380+
381+
**What it validates:**
382+
383+
- GeoJSON type validity (Feature, FeatureCollection, Point, LineString, Polygon, etc.)
384+
- Required properties (type, coordinates, geometry, features)
385+
- Coordinate array structure and position validity
386+
- Longitude ranges [-180, 180] and latitude ranges [-90, 90]
387+
- Polygon ring closure (first and last coordinates should match)
388+
- Minimum position requirements (LineString needs 2+, Polygon rings need 4+ positions)
389+
390+
**Returns:**
391+
392+
Validation results including:
393+
394+
- `valid` (boolean): Overall validity
395+
- `errors` (array): Critical errors that make the GeoJSON invalid
396+
- `warnings` (array): Non-critical issues (e.g., unclosed polygon rings, out-of-range coordinates)
397+
- `info` (array): Informational messages
398+
- `statistics`: Object with type, feature count, geometry types, and bounding box
399+
400+
Each issue includes:
401+
402+
- `severity`: "error", "warning", or "info"
403+
- `message`: Description of the issue
404+
- `path`: JSON path to the problem (optional)
405+
- `suggestion`: How to fix the issue (optional)
406+
407+
**Example:**
408+
409+
```json
410+
{
411+
"geojson": {
412+
"type": "Feature",
413+
"geometry": {
414+
"type": "Point",
415+
"coordinates": [102.0, 0.5]
416+
},
417+
"properties": {
418+
"name": "Test Point"
419+
}
420+
}
421+
}
422+
```
423+
424+
**Returns:**
425+
426+
```json
427+
{
428+
"valid": true,
429+
"errors": [],
430+
"warnings": [],
431+
"info": [],
432+
"statistics": {
433+
"type": "Feature",
434+
"featureCount": 1,
435+
"geometryTypes": ["Point"],
436+
"bbox": [102.0, 0.5, 102.0, 0.5]
437+
}
438+
}
439+
```
440+
441+
**Example prompts:**
442+
443+
- "Validate this GeoJSON file and tell me if there are any errors"
444+
- "Check if my GeoJSON coordinates are valid"
445+
- "Is this Feature Collection properly formatted?"
446+
447+
**Note:** This is an offline validation tool that doesn't require API access or token scopes.
448+
449+
#### Validate Expression tool
450+
451+
Validates Mapbox style expressions for syntax, operators, and argument correctness. This offline validation tool performs comprehensive checks on Mapbox expressions without requiring API access.
452+
453+
**Parameters:**
454+
455+
- `expression` (array or string, required): Mapbox expression to validate (array format or JSON string)
456+
457+
**What it validates:**
458+
459+
- Expression syntax and structure
460+
- Valid operator names
461+
- Correct argument counts for each operator
462+
- Nested expression validation
463+
- Expression depth (warns about deeply nested expressions)
464+
465+
**Returns:**
466+
467+
Validation results including:
468+
469+
- `valid` (boolean): Overall validity
470+
- `errors` (array): Critical errors that make the expression invalid
471+
- `warnings` (array): Non-critical issues (e.g., deeply nested expressions)
472+
- `info` (array): Informational messages
473+
- `metadata`: Object with expressionType, returnType, and depth
474+
475+
Each issue includes:
476+
477+
- `severity`: "error", "warning", or "info"
478+
- `message`: Description of the issue
479+
- `path`: Path to the problem in the expression (optional)
480+
- `suggestion`: How to fix the issue (optional)
481+
482+
**Supported expression types:**
483+
484+
- **Data**: get, has, id, geometry-type, feature-state, properties
485+
- **Lookup**: at, in, index-of, slice, length
486+
- **Decision**: case, match, coalesce
487+
- **Ramps & interpolation**: interpolate, step
488+
- **Math**: +, -, \*, /, %, ^, sqrt, log10, log2, ln, abs, etc.
489+
- **String**: concat, downcase, upcase, is-supported-script
490+
- **Color**: rgb, rgba, to-rgba, hsl, hsla
491+
- **Type**: array, boolean, collator, format, image, literal, number, number-format, object, string, to-boolean, to-color, to-number, to-string, typeof
492+
- **Camera**: zoom, pitch, distance-from-center
493+
- **Variable binding**: let, var
494+
495+
**Example:**
496+
497+
```json
498+
{
499+
"expression": ["get", "population"]
500+
}
501+
```
502+
503+
**Returns:**
504+
505+
```json
506+
{
507+
"valid": true,
508+
"errors": [],
509+
"warnings": [],
510+
"info": [
511+
{
512+
"severity": "info",
513+
"message": "Expression validated successfully"
514+
}
515+
],
516+
"metadata": {
517+
"expressionType": "data",
518+
"returnType": "any",
519+
"depth": 1
520+
}
521+
}
522+
```
523+
524+
**Example prompts:**
525+
526+
- "Validate this Mapbox expression: [\"get\", \"population\"]"
527+
- "Check if this interpolation expression is correct"
528+
- "Is this expression syntax valid for Mapbox styles?"
529+
530+
**Note:** This is an offline validation tool that doesn't require API access or token scopes.
531+
361532
#### Coordinate Conversion tool
362533

363534
Convert coordinates between different coordinate reference systems (CRS), specifically between WGS84 (EPSG:4326) and Web Mercator (EPSG:3857).
@@ -514,6 +685,117 @@ A JSON object with:
514685
- "Check color contrast for rgb(51, 51, 51) on rgb(245, 245, 245)"
515686
- "Is this color combination accessible: foreground 'navy' on background 'lightblue'?"
516687

688+
#### compare_styles_tool
689+
690+
Compares two Mapbox styles and reports structural differences, including changes to layers, sources, and properties. This offline comparison tool performs deep object comparison without requiring API access.
691+
692+
**Parameters:**
693+
694+
- `styleA` (string or object, required): First Mapbox style to compare (JSON string or style object)
695+
- `styleB` (string or object, required): Second Mapbox style to compare (JSON string or style object)
696+
- `ignoreMetadata` (boolean, optional): If true, ignores metadata fields (id, owner, created, modified, draft, visibility) when comparing
697+
698+
**Comparison features:**
699+
700+
- Deep recursive comparison of nested structures
701+
- Layer comparison by ID (not array position)
702+
- Detailed diff reporting with JSON paths
703+
- Identifies additions, removals, and modifications
704+
- Optional metadata filtering
705+
706+
**Returns:**
707+
708+
```json
709+
{
710+
"identical": false,
711+
"differences": [
712+
{
713+
"path": "layers.water.paint.fill-color",
714+
"type": "modified",
715+
"valueA": "#a0c8f0",
716+
"valueB": "#b0d0ff",
717+
"description": "Modified property at layers.water.paint.fill-color"
718+
}
719+
],
720+
"summary": {
721+
"totalDifferences": 1,
722+
"added": 0,
723+
"removed": 0,
724+
"modified": 1
725+
}
726+
}
727+
```
728+
729+
**Example prompts:**
730+
731+
- "Compare these two Mapbox styles and show me the differences"
732+
- "What changed between my old style and new style?"
733+
- "Compare styles ignoring metadata fields"
734+
735+
#### Style Optimization tool
736+
737+
Optimizes Mapbox styles by removing redundancies, simplifying expressions, and reducing file size.
738+
739+
**Parameters:**
740+
741+
- `style` (string or object, required): Mapbox style to optimize (JSON string or style object)
742+
- `optimizations` (array, optional): Specific optimizations to apply. If not specified, all optimizations are applied. Available optimizations:
743+
- `remove-unused-sources`: Remove sources not referenced by any layer
744+
- `remove-duplicate-layers`: Remove layers that are exact duplicates
745+
- `simplify-expressions`: Simplify boolean expressions (e.g., `["all", true]``true`)
746+
- `remove-empty-layers`: Remove layers with no visible properties (excluding background layers)
747+
- `consolidate-filters`: Identify layers with identical filters that could be consolidated
748+
749+
**Optimizations performed:**
750+
751+
- **Remove unused sources**: Identifies and removes source definitions that aren't referenced by any layer
752+
- **Remove duplicate layers**: Detects layers with identical properties (excluding ID) and removes duplicates
753+
- **Simplify expressions**: Simplifies boolean logic in filters and property expressions:
754+
- `["all", true]``true`
755+
- `["any", false]``false`
756+
- `["!", false]``true`
757+
- `["!", true]``false`
758+
- **Remove empty layers**: Removes layers with no paint or layout properties (background layers are preserved)
759+
- **Consolidate filters**: Identifies groups of layers with identical filter expressions
760+
761+
**Returns:**
762+
763+
A JSON object with:
764+
765+
- `optimizedStyle`: The optimized Mapbox style
766+
- `optimizations`: Array of optimizations applied
767+
- `summary`: Statistics including size savings and percent reduction
768+
769+
**Example:**
770+
771+
```json
772+
{
773+
"optimizedStyle": { "version": 8, "sources": {}, "layers": [] },
774+
"optimizations": [
775+
{
776+
"type": "remove-unused-sources",
777+
"description": "Removed 2 unused source(s): unused-source1, unused-source2",
778+
"count": 2
779+
}
780+
],
781+
"summary": {
782+
"totalOptimizations": 2,
783+
"originalSize": 1234,
784+
"optimizedSize": 890,
785+
"sizeSaved": 344,
786+
"percentReduction": 27.88
787+
}
788+
}
789+
```
790+
791+
**Example prompts:**
792+
793+
- "Optimize this Mapbox style to reduce its file size"
794+
- "Remove unused sources from my style"
795+
- "Simplify the expressions in this style"
796+
- "Find and remove duplicate layers in my map style"
797+
- "Optimize my style but only remove unused sources and empty layers"
798+
517799
## Agent Skills
518800

519801
This repository includes [Agent Skills](https://agentskills.io) that provide domain expertise for building maps with Mapbox. Skills teach AI assistants about map design, security best practices, and common implementation patterns.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) Mapbox, Inc.
2+
// Licensed under the MIT License.
3+
4+
import { z } from 'zod';
5+
6+
export const CompareStylesInputSchema = z.object({
7+
styleA: z
8+
.union([z.string(), z.record(z.unknown())])
9+
.describe('First Mapbox style (JSON string or style object)'),
10+
styleB: z
11+
.union([z.string(), z.record(z.unknown())])
12+
.describe('Second Mapbox style (JSON string or style object)'),
13+
ignoreMetadata: z
14+
.boolean()
15+
.optional()
16+
.describe('Ignore metadata fields like id, owner, created, modified')
17+
});
18+
19+
export type CompareStylesInput = z.infer<typeof CompareStylesInputSchema>;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) Mapbox, Inc.
2+
// Licensed under the MIT License.
3+
4+
import { z } from 'zod';
5+
6+
const DifferenceSchema = z.object({
7+
path: z.string().describe('JSON path to the difference'),
8+
type: z.enum(['added', 'removed', 'modified']).describe('Type of difference'),
9+
valueA: z.unknown().optional().describe('Value in style A (if exists)'),
10+
valueB: z.unknown().optional().describe('Value in style B (if exists)'),
11+
description: z.string().optional().describe('Human-readable description')
12+
});
13+
14+
export const CompareStylesOutputSchema = z.object({
15+
identical: z.boolean().describe('Whether the styles are identical'),
16+
differences: z.array(DifferenceSchema).describe('List of differences found'),
17+
summary: z
18+
.object({
19+
totalDifferences: z.number().describe('Total number of differences'),
20+
added: z.number().describe('Number of additions in style B'),
21+
removed: z.number().describe('Number of removals from style A'),
22+
modified: z.number().describe('Number of modifications')
23+
})
24+
.describe('Summary of differences')
25+
});
26+
27+
export type CompareStylesOutput = z.infer<typeof CompareStylesOutputSchema>;
28+
export type Difference = z.infer<typeof DifferenceSchema>;

0 commit comments

Comments
 (0)