Skip to content
Open
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
149 changes: 149 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ https://github.com/user-attachments/assets/8b1b8ef2-9fba-4951-bc9a-beaed4f6aff6
- [GeoJSON Preview tool (Beta)](#geojson-preview-tool-beta)
- [Coordinate Conversion tool](#coordinate-conversion-tool)
- [Bounding Box tool](#bounding-box-tool)
- [Validation Tools](#validation-tools)
- [validate_geojson_tool](#validate_geojson_tool)
- [validate_expression_tool](#validate_expression_tool)
- [compare_styles_tool](#compare_styles_tool)
- [Resources](#resources)
- [Observability \& Tracing](#observability--tracing)
- [Features](#features)
Expand Down Expand Up @@ -453,6 +457,151 @@ An array of four numbers representing the bounding box: `[minX, minY, maxX, maxY
- "Calculate the bounding box of this GeoJSON file" (then upload a .geojson file)
- "What's the bounding box for the coordinates in the uploaded parks.geojson file?"

#### Validation Tools

##### validate_geojson_tool

Validates GeoJSON data structure against the GeoJSON specification (RFC 7946), performing comprehensive format and semantic validation.

**Parameters:**

- `geojson` (string or object, required): GeoJSON content to validate. Can be provided as:
- A JSON string that will be parsed
- A GeoJSON object

**Supported GeoJSON types:**

- Point, MultiPoint
- LineString, MultiLineString
- Polygon, MultiPolygon
- GeometryCollection
- Feature, FeatureCollection

**Validation checks:**

- Type field presence and validity
- Coordinates array structure
- Position format (longitude, latitude, optional altitude)
- Coordinate range validation (longitude: -180 to 180, latitude: -90 to 90)
- Ring closure for Polygons
- Minimum coordinate requirements per geometry type
- Feature properties validation
- Nested geometry validation in Collections

**Returns:**

```json
{
"valid": true,
"errors": [],
"warnings": [],
"summary": {
"type": "FeatureCollection",
"featureCount": 5,
"geometryTypes": ["Point", "LineString"]
}
}
```

**Example prompts:**

- "Validate this GeoJSON file" (then upload a .geojson file)
- "Check if my GeoJSON is valid"
- "Is this GeoJSON structure correct?"

##### validate_expression_tool

Validates Mapbox GL JS style expressions against the Mapbox Style Specification, checking syntax, types, and semantic correctness.

**Parameters:**

- `expression` (array or object, required): Mapbox expression to validate
- `expectedType` (string, optional): Expected return type (e.g., "boolean", "number", "string", "color")
- `context` (string, optional): Context where expression will be used ("layer", "filter", "paint", "layout")

**Validation checks:**

- Operator existence and validity
- Argument count and types
- Type coercion rules
- Nested expression validation
- Context-appropriate operators
- Return type matching

**Supported operators:**

- Mathematical: `+`, `-`, `*`, `/`, `%`, `^`, `sqrt`, `log10`, `ln`, `abs`, `ceil`, `floor`, `round`
- Comparison: `==`, `!=`, `<`, `>`, `<=`, `>=`
- Logical: `all`, `any`, `!`, `case`, `match`
- String: `concat`, `downcase`, `upcase`
- Type conversion: `to-boolean`, `to-number`, `to-string`, `to-color`
- Lookup: `get`, `has`, `in`, `index-of`
- Decision: `coalesce`, `step`, `interpolate`

**Returns:**

```json
{
"valid": true,
"errors": [],
"warnings": [],
"expressionType": "number"
}
```

**Example prompts:**

- "Validate this Mapbox expression: ['get', 'population']"
- "Check if this filter expression is valid"
- "Is this paint property expression correct?"

##### compare_styles_tool

Compares two Mapbox styles and reports structural differences in layers, sources, and properties.

**Parameters:**

- `styleA` (string or object, required): First Mapbox style to compare (JSON string or style object)
- `styleB` (string or object, required): Second Mapbox style to compare (JSON string or style object)
- `ignoreMetadata` (boolean, optional): Ignore metadata fields like id, owner, created, modified, draft, visibility

**Comparison features:**

- Deep structural comparison of style objects
- Layer comparison by ID (not position)
- Source comparison with nested property checks
- Property-level difference detection
- Metadata filtering for logical comparisons

**Returns:**

```json
{
"identical": false,
"differences": [
{
"path": "layers[id=\"background\"].paint.background-color",
"type": "modified",
"valueA": "#ffffff",
"valueB": "#000000",
"description": "Value changed from \"#ffffff\" to \"#000000\""
}
],
"summary": {
"totalDifferences": 1,
"added": 0,
"removed": 0,
"modified": 1
}
}
```

**Example prompts:**

- "Compare these two Mapbox styles and show me the differences"
- "What changed between my old style and new style?"
- "Compare styles ignoring metadata fields"

## Agent Skills

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.
Expand Down
19 changes: 19 additions & 0 deletions src/tools/compare-styles-tool/CompareStylesTool.input.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) Mapbox, Inc.
// Licensed under the MIT License.

import { z } from 'zod';

export const CompareStylesInputSchema = z.object({
styleA: z
.union([z.string(), z.record(z.unknown())])
.describe('First Mapbox style (JSON string or style object)'),
styleB: z
.union([z.string(), z.record(z.unknown())])
.describe('Second Mapbox style (JSON string or style object)'),
ignoreMetadata: z
.boolean()
.optional()
.describe('Ignore metadata fields like id, owner, created, modified')
});

export type CompareStylesInput = z.infer<typeof CompareStylesInputSchema>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) Mapbox, Inc.
// Licensed under the MIT License.

import { z } from 'zod';

const DifferenceSchema = z.object({
path: z.string().describe('JSON path to the difference'),
type: z.enum(['added', 'removed', 'modified']).describe('Type of difference'),
valueA: z.unknown().optional().describe('Value in style A (if exists)'),
valueB: z.unknown().optional().describe('Value in style B (if exists)'),
description: z.string().optional().describe('Human-readable description')
});

export const CompareStylesOutputSchema = z.object({
identical: z.boolean().describe('Whether the styles are identical'),
differences: z.array(DifferenceSchema).describe('List of differences found'),
summary: z
.object({
totalDifferences: z.number().describe('Total number of differences'),
added: z.number().describe('Number of additions in style B'),
removed: z.number().describe('Number of removals from style A'),
modified: z.number().describe('Number of modifications')
})
.describe('Summary of differences')
});

export type CompareStylesOutput = z.infer<typeof CompareStylesOutputSchema>;
export type Difference = z.infer<typeof DifferenceSchema>;
Loading