From 8b91bb3cba56dab60a31b78bcd82ada6599a9b1f Mon Sep 17 00:00:00 2001 From: Matthew Podwysocki Date: Tue, 21 Oct 2025 17:49:05 -0400 Subject: [PATCH 1/4] [resources] Add new resources to server with fallback tool --- README.md | 59 ++++++ eslint.config.mjs | 29 ++- src/resources/BaseResource.ts | 17 +- .../MapboxLayerTypeMappingResource.ts | 175 ++++++++++++++++++ .../MapboxStreetsV8FieldsResource.ts | 29 +++ .../MapboxStyleLayersResource.ts | 5 +- .../MapboxTokenScopesResource.ts | 149 +++++++++++++++ src/resources/resourceRegistry.ts | 13 +- .../GetReferenceTool.input.schema.ts | 23 +++ .../get-reference-tool/GetReferenceTool.ts | 89 +++++++++ src/tools/toolRegistry.ts | 2 + .../MapboxLayerTypeMappingResource.test.ts | 146 +++++++++++++++ .../MapboxStreetsV8FieldsResource.test.ts | 70 +++++++ .../MapboxTokenScopesResource.test.ts | 92 +++++++++ .../tool-naming-convention.test.ts.snap | 5 + 15 files changed, 886 insertions(+), 17 deletions(-) create mode 100644 src/resources/mapbox-layer-type-mapping-resource/MapboxLayerTypeMappingResource.ts create mode 100644 src/resources/mapbox-streets-v8-fields-resource/MapboxStreetsV8FieldsResource.ts create mode 100644 src/resources/mapbox-token-scopes-resource/MapboxTokenScopesResource.ts create mode 100644 src/tools/get-reference-tool/GetReferenceTool.input.schema.ts create mode 100644 src/tools/get-reference-tool/GetReferenceTool.ts create mode 100644 test/resources/MapboxLayerTypeMappingResource.test.ts create mode 100644 test/resources/MapboxStreetsV8FieldsResource.test.ts create mode 100644 test/resources/MapboxTokenScopesResource.test.ts diff --git a/README.md b/README.md index 39616da..d80fa40 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ https://github.com/user-attachments/assets/8b1b8ef2-9fba-4951-bc9a-beaed4f6aff6 - [Getting Your Mapbox Access Token](#getting-your-mapbox-access-token) - [Tools](#tools) - [Documentation Tools](#documentation-tools) + - [Reference Tools](#reference-tools) - [Style Management Tools](#style-management-tools) - [Token Management Tools](#token-management-tools) - [create-token](#create-token) @@ -24,6 +25,7 @@ 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) + - [Resources](#resources) - [Development](#development) - [Testing](#testing) - [Tool Snapshot Tests](#tool-snapshot-tests) @@ -116,6 +118,27 @@ The `MAPBOX_ACCESS_TOKEN` environment variable is required. **Each tool requires 📖 **[See more examples and interactive demo →](./docs/mapbox-docs-tool-demo.md)** +### Reference Tools + +**get_reference_tool** - Access static Mapbox reference documentation and schemas. This tool provides essential reference information that helps AI assistants understand Mapbox concepts and build correct styles and tokens. + +> **Note:** This tool exists as a workaround for Claude Desktop's current limitation with MCP resources. Claude Desktop can see resources (via `resources/list`) but doesn't automatically call `resources/read` to fetch their content. This tool provides the same reference data through the tool interface, which Claude Desktop does support. Other MCP clients that fully support the resources protocol can access this data directly as MCP Resources (see [Resources](#resources) section below). + +**Available References:** + +- **`resource://mapbox-style-layers`** - Mapbox GL JS style specification reference guide covering all layer types (fill, line, symbol, circle, fill-extrusion) and their properties +- **`resource://mapbox-streets-v8-fields`** - Complete field definitions for all Mapbox Streets v8 source layers, including enumerated values for each field (useful for building filters) +- **`resource://mapbox-token-scopes`** - Comprehensive token scope reference explaining what each scope allows and which scopes are needed for different operations +- **`resource://mapbox-layer-type-mapping`** - Mapping of Mapbox Streets v8 source layers to compatible GL JS layer types, with common usage patterns + +**Example prompts:** + +- "What fields are available for the landuse layer?" +- "Show me the token scopes reference" +- "What layer type should I use for roads?" +- "Get the Streets v8 fields reference" +- "What scopes do I need to display a map?" + ### Style Management Tools Complete set of tools for managing Mapbox styles via the Styles API: @@ -374,6 +397,42 @@ 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?" +## Resources + +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. + +**Available Resources:** + +1. **Mapbox Style Specification Guide** (`resource://mapbox-style-layers`) + - Complete reference for Mapbox GL JS layer types and properties + - Covers fill, line, symbol, circle, and fill-extrusion layers + - Includes paint and layout properties for each layer type + +2. **Mapbox Streets v8 Fields Reference** (`resource://mapbox-streets-v8-fields`) + - Field definitions for all Streets v8 source layers + - Enumerated values for filterable fields + - Essential for building accurate style filters + - Example: `landuse` layer has `class` field with values like `park`, `cemetery`, `hospital`, etc. + +3. **Mapbox Token Scopes Reference** (`resource://mapbox-token-scopes`) + - Comprehensive documentation of token scopes + - Explains public vs. secret token scopes + - Common scope combinations for different use cases + - Best practices for token management + +4. **Mapbox Layer Type Mapping** (`resource://mapbox-layer-type-mapping`) + - Maps Streets v8 source layers to compatible GL JS layer types + - Organized by geometry type (polygon, line, point) + - Includes common usage patterns and examples + - Helps avoid incompatible layer type/source layer combinations + +**Accessing Resources:** + +- **Claude Desktop & Most MCP Clients**: Use the `get_reference_tool` to access these references +- **Future MCP Clients**: May support direct resource access via the MCP resources protocol + +**Note:** Resources provide static reference data that doesn't change frequently, while tools provide dynamic, user-specific data (like listing your styles or tokens) and perform actions (like creating styles or tokens). + ## Development ### Testing diff --git a/eslint.config.mjs b/eslint.config.mjs index f97ebbf..995a3ce 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -13,34 +13,43 @@ export default defineConfig( { plugins: { n: nPlugin, - 'unused-imports': unusedImports, + 'unused-imports': unusedImports }, languageOptions: { ecmaVersion: 'latest', sourceType: 'module', globals: { - ...globals.node, + ...globals.node } }, rules: { '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/no-unused-vars': [ + 'error', + { + vars: 'all', + varsIgnorePattern: '^_', + args: 'after-used', + argsIgnorePattern: '^_' + } + ], 'n/prefer-node-protocol': 'warn', 'unused-imports/no-unused-imports': 'error', 'unused-imports/no-unused-vars': [ 'warn', { - 'vars': 'all', - 'varsIgnorePattern': '^_', - 'args': 'after-used', - 'argsIgnorePattern': '^_', - }, + vars: 'all', + varsIgnorePattern: '^_', + args: 'after-used', + argsIgnorePattern: '^_' + } ] - } + } }, { - files: ['test/**/*.ts'], + files: ['test/**/*.ts'], rules: { - '@typescript-eslint/no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': 'off' } } ); diff --git a/src/resources/BaseResource.ts b/src/resources/BaseResource.ts index cf4c21f..b3aaaa5 100644 --- a/src/resources/BaseResource.ts +++ b/src/resources/BaseResource.ts @@ -1,4 +1,13 @@ +// Copyright (c) Mapbox, Inc. +// Licensed under the MIT License. + import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import type { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol.js'; +import type { + ReadResourceResult, + ServerNotification, + ServerRequest +} from '@modelcontextprotocol/sdk/types.js'; /** * Base class for MCP resources @@ -27,10 +36,8 @@ export abstract class BaseResource { /** * Callback to read the resource content */ - protected abstract readCallback( + public abstract readCallback( uri: URL, - extra: unknown - ): Promise<{ - contents: Array<{ uri: string; mimeType: string; text: string }>; - }>; + extra: RequestHandlerExtra + ): Promise; } diff --git a/src/resources/mapbox-layer-type-mapping-resource/MapboxLayerTypeMappingResource.ts b/src/resources/mapbox-layer-type-mapping-resource/MapboxLayerTypeMappingResource.ts new file mode 100644 index 0000000..722b1a6 --- /dev/null +++ b/src/resources/mapbox-layer-type-mapping-resource/MapboxLayerTypeMappingResource.ts @@ -0,0 +1,175 @@ +// Copyright (c) Mapbox, Inc. +// Licensed under the MIT License. + +import { BaseResource } from '../BaseResource.js'; + +/** + * Resource providing mapping between Streets v8 source layers and GL JS layer types + * Helps determine which layer types are compatible with which source layers + */ +export class MapboxLayerTypeMappingResource extends BaseResource { + readonly name = 'Mapbox Layer Type Mapping'; + readonly uri = 'resource://mapbox-layer-type-mapping'; + readonly description = + 'Mapping of Mapbox Streets v8 source layers to compatible Mapbox GL JS layer types. Use this to understand which layer types (fill, line, symbol, etc.) work with which source layers.'; + readonly mimeType = 'application/json'; + + public async readCallback(uri: URL, _extra: unknown) { + const mapping = { + description: + 'Mapping of Mapbox Streets v8 source layers to compatible Mapbox GL JS layer types', + geometry_types: { + polygon: { + description: 'Source layers containing polygon geometry', + source_layers: ['landuse', 'water', 'building', 'landuse_overlay'], + compatible_layer_types: ['fill', 'fill-extrusion'] + }, + line: { + description: 'Source layers containing line geometry', + source_layers: [ + 'road', + 'admin', + 'waterway', + 'aeroway', + 'structure', + 'natural_label' + ], + compatible_layer_types: ['line'] + }, + point: { + description: 'Source layers containing point geometry', + source_layers: [ + 'place_label', + 'poi_label', + 'airport_label', + 'transit_stop_label', + 'motorway_junction', + 'housenum_label' + ], + compatible_layer_types: ['symbol', 'circle'] + } + }, + layer_type_compatibility: { + fill: { + description: 'Filled polygons with optional outlines', + compatible_source_layers: [ + 'landuse', + 'water', + 'building', + 'landuse_overlay' + ], + common_uses: [ + 'Coloring parks, water bodies, and land areas', + 'Creating custom area visualizations', + 'Styling building footprints' + ] + }, + line: { + description: 'Stroked lines for linear features', + compatible_source_layers: [ + 'road', + 'admin', + 'waterway', + 'aeroway', + 'structure' + ], + common_uses: [ + 'Styling roads and highways', + 'Drawing administrative boundaries', + 'Showing rivers and canals', + 'Displaying airport runways' + ] + }, + symbol: { + description: 'Text labels and/or icons', + compatible_source_layers: [ + 'place_label', + 'poi_label', + 'airport_label', + 'transit_stop_label', + 'natural_label', + 'motorway_junction', + 'housenum_label' + ], + common_uses: [ + 'Displaying place names and city labels', + 'Showing POI icons and labels', + 'Adding custom icons to points', + 'Labeling features with text' + ] + }, + circle: { + description: 'Circular points with configurable radius', + compatible_source_layers: [ + 'place_label', + 'poi_label', + 'airport_label', + 'transit_stop_label', + 'motorway_junction' + ], + common_uses: [ + 'Creating simple point markers', + 'Heatmap-style visualizations', + 'Highlighting specific locations' + ] + }, + 'fill-extrusion': { + description: '3D extruded polygons (typically buildings)', + compatible_source_layers: ['building'], + common_uses: [ + 'Creating 3D building visualizations', + 'Extruding buildings by height', + 'Making 3D city views' + ], + required_fields: ['height', 'extrude'] + } + }, + common_patterns: { + styling_parks: { + source_layer: 'landuse', + filter_by: { class: 'park' }, + layer_type: 'fill', + example: + 'Use fill layer on landuse source layer with filter ["==", ["get", "class"], "park"]' + }, + styling_roads: { + source_layer: 'road', + filter_by: { class: ['motorway', 'trunk', 'primary'] }, + layer_type: 'line', + example: + 'Use line layer on road source layer with filter ["in", ["get", "class"], ["literal", ["motorway", "trunk", "primary"]]]' + }, + labeling_cities: { + source_layer: 'place_label', + filter_by: { type: ['city', 'town'] }, + layer_type: 'symbol', + example: + 'Use symbol layer on place_label source layer with text-field: ["get", "name"]' + }, + building_extrusions: { + source_layer: 'building', + filter_by: { extrude: 'true' }, + layer_type: 'fill-extrusion', + example: + 'Use fill-extrusion layer on building source layer with fill-extrusion-height: ["get", "height"]' + } + }, + notes: [ + 'Source layers are defined by the Mapbox Streets v8 tileset', + 'Layer types are defined by the Mapbox GL JS specification', + 'Not all source layers work with all layer types - match geometry types', + 'Use the Mapbox Streets v8 Fields Reference resource to see available filter fields' + ] + }; + + return { + contents: [ + { + uri: uri.href, + mimeType: this.mimeType, + text: JSON.stringify(mapping, null, 2) + } + ] + }; + } +} diff --git a/src/resources/mapbox-streets-v8-fields-resource/MapboxStreetsV8FieldsResource.ts b/src/resources/mapbox-streets-v8-fields-resource/MapboxStreetsV8FieldsResource.ts new file mode 100644 index 0000000..b2b28fe --- /dev/null +++ b/src/resources/mapbox-streets-v8-fields-resource/MapboxStreetsV8FieldsResource.ts @@ -0,0 +1,29 @@ +// Copyright (c) Mapbox, Inc. +// Licensed under the MIT License. + +import { BaseResource } from '../BaseResource.js'; +import { STREETS_V8_FIELDS } from '../../constants/mapboxStreetsV8Fields.js'; + +/** + * Resource providing Mapbox Streets v8 source layer field definitions + * Exposes complete field metadata for all Streets v8 layers with enumerated values + */ +export class MapboxStreetsV8FieldsResource extends BaseResource { + readonly name = 'Mapbox Streets v8 Fields Reference'; + readonly uri = 'resource://mapbox-streets-v8-fields'; + readonly description = + 'Complete field definitions for all Mapbox Streets v8 source layers including available values for each field. Use this to understand what fields and values are available when filtering or styling map features.'; + readonly mimeType = 'application/json'; + + public async readCallback(uri: URL, _extra: unknown) { + return { + contents: [ + { + uri: uri.href, + mimeType: this.mimeType, + text: JSON.stringify(STREETS_V8_FIELDS, null, 2) + } + ] + }; + } +} diff --git a/src/resources/mapbox-style-layers-resource/MapboxStyleLayersResource.ts b/src/resources/mapbox-style-layers-resource/MapboxStyleLayersResource.ts index 048cd9d..5fb1c68 100644 --- a/src/resources/mapbox-style-layers-resource/MapboxStyleLayersResource.ts +++ b/src/resources/mapbox-style-layers-resource/MapboxStyleLayersResource.ts @@ -1,3 +1,6 @@ +// Copyright (c) Mapbox, Inc. +// Licensed under the MIT License. + import { BaseResource } from '../BaseResource.js'; /** @@ -11,7 +14,7 @@ export class MapboxStyleLayersResource extends BaseResource { 'Mapbox GL JS style specification reference for layer types, paint/layout properties, and Streets v8 source layers'; readonly mimeType = 'text/markdown'; - protected async readCallback(uri: URL) { + public async readCallback(uri: URL, _extra: unknown) { // Generate comprehensive markdown documentation const markdown = this.generateMarkdown(); diff --git a/src/resources/mapbox-token-scopes-resource/MapboxTokenScopesResource.ts b/src/resources/mapbox-token-scopes-resource/MapboxTokenScopesResource.ts new file mode 100644 index 0000000..5e094f4 --- /dev/null +++ b/src/resources/mapbox-token-scopes-resource/MapboxTokenScopesResource.ts @@ -0,0 +1,149 @@ +// Copyright (c) Mapbox, Inc. +// Licensed under the MIT License. + +import { BaseResource } from '../BaseResource.js'; + +/** + * Resource providing Mapbox token scope reference documentation + * Helps users and AI understand what each token scope allows + */ +export class MapboxTokenScopesResource extends BaseResource { + readonly name = 'Mapbox Token Scopes Reference'; + readonly uri = 'resource://mapbox-token-scopes'; + readonly description = + 'Reference guide for Mapbox access token scopes and their permissions. Use this to understand what each scope allows and which scopes are needed for different operations.'; + readonly mimeType = 'text/markdown'; + + public async readCallback(uri: URL, _extra: unknown) { + const markdown = this.generateMarkdown(); + + return { + contents: [ + { + uri: uri.href, + mimeType: this.mimeType, + text: markdown + } + ] + }; + } + + private generateMarkdown(): string { + return `# Mapbox Token Scopes Reference + +This guide explains the available scopes for Mapbox access tokens and what operations they enable. + +## Token Types + +### Public Tokens +Public tokens are used in client-side applications (web browsers, mobile apps). They have limited scopes for security. + +### Secret Tokens +Secret tokens are used server-side and have broader permissions including write operations. + +## Public Token Scopes + +These scopes can be used with public tokens created via the \`create_token_tool\`: + +### \`styles:tiles\` +- **Purpose**: Access map tiles from your styles +- **Required for**: Rendering maps with Mapbox GL JS, Mobile SDKs, or Static Images API +- **Use case**: Production map display in applications +- **Security**: Safe for public use, only allows reading pre-rendered tiles + +### \`styles:read\` +- **Purpose**: Read style JSON definitions +- **Required for**: Retrieving style configurations via the Styles API +- **Use case**: Style inspection, analysis, and management +- **Note**: Does not allow modifying styles (requires secret token) + +### \`fonts:read\` +- **Purpose**: Access font glyphs for map labels +- **Required for**: Rendering text and labels on maps +- **Use case**: Map rendering with custom or default fonts +- **Note**: Required whenever maps display text labels + +### \`datasets:read\` +- **Purpose**: Read from Mapbox Datasets API +- **Required for**: Accessing features from uploaded datasets +- **Use case**: Reading custom geographic data, querying dataset features +- **Note**: Does not allow modifying datasets (requires secret token) + +### \`vision:read\` +- **Purpose**: Access Mapbox Vision API +- **Required for**: Computer vision features in navigation and AR applications +- **Use case**: Advanced navigation, object detection, AR overlays +- **Note**: Specialized scope for Vision SDK users + +## Secret Token Scopes + +Secret tokens (like \`MAPBOX_ACCESS_TOKEN\` environment variable) typically have these scopes: + +### \`styles:write\` +- **Purpose**: Create, update, and delete styles +- **Required for**: \`create_style_tool\`, \`update_style_tool\`, \`delete_style_tool\` +- **Security**: Keep secret, never expose in client-side code + +### \`styles:list\` +- **Purpose**: List all styles in an account +- **Required for**: \`list_styles_tool\` +- **Security**: Keep secret, reveals account information + +### \`tokens:read\` +- **Purpose**: List and read token information +- **Required for**: \`list_tokens_tool\` +- **Security**: Keep secret, reveals account tokens + +### \`tokens:write\` +- **Purpose**: Create, update, and delete tokens +- **Required for**: \`create_token_tool\` +- **Security**: Keep secret, allows managing access tokens + +## Common Scope Combinations + +### For Map Display (Public Token) +\`\`\` +["styles:tiles", "fonts:read"] +\`\`\` +Minimal scopes for rendering a map with labels. + +### For Map Display with Custom Data (Public Token) +\`\`\` +["styles:tiles", "fonts:read", "datasets:read"] +\`\`\` +Allows displaying maps with data from Mapbox Datasets. + +### For Development Tools (Public Token) +\`\`\` +["styles:tiles", "styles:read", "fonts:read"] +\`\`\` +Allows rendering maps and inspecting style configurations. + +### For Server-Side Operations (Secret Token) +Secret tokens typically have comprehensive scopes: +\`\`\` +["styles:read", "styles:write", "styles:list", "fonts:read", "datasets:read", "tokens:read", "tokens:write"] +\`\`\` + +## Best Practices + +1. **Use Public Tokens in Client Applications**: Never expose secret tokens in browsers or mobile apps +2. **Minimal Scopes**: Only grant the scopes needed for your specific use case +3. **URL Restrictions**: Use \`allowedUrls\` parameter when creating public tokens to restrict usage to your domains +4. **Token Expiration**: Set expiration dates for temporary access tokens +5. **Rotate Regularly**: Periodically rotate secret tokens used in production + +## Token Management Tools + +- **\`create_token_tool\`**: Create new public tokens with specific scopes +- **\`list_tokens_tool\`**: List all tokens in your account +- **\`list_styles_tool\`**: List styles (requires \`styles:list\` scope) +- **\`create_style_tool\`**: Create styles (requires \`styles:write\` scope) + +## More Information + +- [Mapbox Access Tokens Documentation](https://docs.mapbox.com/help/getting-started/access-tokens/) +- [Mapbox Token API Reference](https://docs.mapbox.com/api/accounts/tokens/) +`; + } +} diff --git a/src/resources/resourceRegistry.ts b/src/resources/resourceRegistry.ts index 2b4d2e5..a53c734 100644 --- a/src/resources/resourceRegistry.ts +++ b/src/resources/resourceRegistry.ts @@ -1,7 +1,18 @@ +// Copyright (c) Mapbox, Inc. +// Licensed under the MIT License. + import { MapboxStyleLayersResource } from './mapbox-style-layers-resource/MapboxStyleLayersResource.js'; +import { MapboxStreetsV8FieldsResource } from './mapbox-streets-v8-fields-resource/MapboxStreetsV8FieldsResource.js'; +import { MapboxTokenScopesResource } from './mapbox-token-scopes-resource/MapboxTokenScopesResource.js'; +import { MapboxLayerTypeMappingResource } from './mapbox-layer-type-mapping-resource/MapboxLayerTypeMappingResource.js'; // Central registry of all resources -export const ALL_RESOURCES = [new MapboxStyleLayersResource()] as const; +export const ALL_RESOURCES = [ + new MapboxStyleLayersResource(), + new MapboxStreetsV8FieldsResource(), + new MapboxTokenScopesResource(), + new MapboxLayerTypeMappingResource() +] as const; export type ResourceInstance = (typeof ALL_RESOURCES)[number]; diff --git a/src/tools/get-reference-tool/GetReferenceTool.input.schema.ts b/src/tools/get-reference-tool/GetReferenceTool.input.schema.ts new file mode 100644 index 0000000..29237c1 --- /dev/null +++ b/src/tools/get-reference-tool/GetReferenceTool.input.schema.ts @@ -0,0 +1,23 @@ +// Copyright (c) Mapbox, Inc. +// Licensed under the MIT License. + +import { z } from 'zod'; + +export const GetReferenceSchema = z.object({ + reference: z + .enum([ + 'resource://mapbox-style-layers', + 'resource://mapbox-streets-v8-fields', + 'resource://mapbox-token-scopes', + 'resource://mapbox-layer-type-mapping' + ]) + .describe( + 'The reference documentation to retrieve. Available references: ' + + 'resource://mapbox-style-layers (Mapbox GL JS style specification for layer types and properties), ' + + 'resource://mapbox-streets-v8-fields (Complete field definitions for all Streets v8 source layers), ' + + 'resource://mapbox-token-scopes (Token scope reference and permissions guide), ' + + 'resource://mapbox-layer-type-mapping (Mapping of source layers to compatible layer types)' + ) +}); + +export type GetReferenceInput = z.infer; diff --git a/src/tools/get-reference-tool/GetReferenceTool.ts b/src/tools/get-reference-tool/GetReferenceTool.ts new file mode 100644 index 0000000..5bc8bf5 --- /dev/null +++ b/src/tools/get-reference-tool/GetReferenceTool.ts @@ -0,0 +1,89 @@ +// Copyright (c) Mapbox, Inc. +// Licensed under the MIT License. + +import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; +import { BaseTool } from '../BaseTool.js'; +import { + GetReferenceSchema, + GetReferenceInput +} from './GetReferenceTool.input.schema.js'; +import { getAllResources } from '../../resources/resourceRegistry.js'; + +/** + * Tool to access Mapbox reference documentation and schemas + * This tool provides access to static reference data that helps understand + * Mapbox concepts, field definitions, token scopes, and layer type mappings. + */ +export class GetReferenceTool extends BaseTool { + readonly name = 'get_reference_tool'; + readonly description = + 'Get Mapbox reference documentation including Streets v8 field definitions, token scopes, layer type mappings, and style specifications. Use this tool to understand what fields, scopes, or layer types are available before creating styles or tokens.'; + readonly annotations = { + readOnlyHint: true, + destructiveHint: false, + idempotentHint: true, + openWorldHint: false, + title: 'Get Mapbox Reference Documentation' + }; + + constructor() { + super({ inputSchema: GetReferenceSchema }); + } + + protected async execute(input: GetReferenceInput): Promise { + const resources = getAllResources(); + const resource = resources.find((r) => r.uri === input.reference); + + if (!resource) { + return { + content: [ + { + type: 'text', + text: `Reference not found: ${input.reference}\n\nAvailable references:\n${resources.map((r) => `- ${r.uri}: ${r.description}`).join('\n')}` + } + ], + isError: true + }; + } + + try { + // Call the resource's readCallback to get the content + const uri = new URL(resource.uri); + // Pass empty object for extra parameter (not used by resources) + const result = await resource.readCallback(uri, {} as any); + + // Return the first content item (resources can return multiple but we typically have one) + if (result.contents.length === 0) { + return { + content: [ + { + type: 'text', + text: `No content available for reference: ${input.reference}` + } + ], + isError: true + }; + } + + return { + content: [ + { + type: 'text', + text: result.contents[0].text + } + ], + isError: false + }; + } catch (error) { + return { + content: [ + { + type: 'text', + text: `Error reading reference: ${error instanceof Error ? error.message : String(error)}` + } + ], + isError: true + }; + } + } +} diff --git a/src/tools/toolRegistry.ts b/src/tools/toolRegistry.ts index fdfd910..eb29e86 100644 --- a/src/tools/toolRegistry.ts +++ b/src/tools/toolRegistry.ts @@ -9,6 +9,7 @@ import { CreateTokenTool } from './create-token-tool/CreateTokenTool.js'; import { DeleteStyleTool } from './delete-style-tool/DeleteStyleTool.js'; import { GeojsonPreviewTool } from './geojson-preview-tool/GeojsonPreviewTool.js'; import { GetMapboxDocSourceTool } from './get-mapbox-doc-source-tool/GetMapboxDocSourceTool.js'; +import { GetReferenceTool } from './get-reference-tool/GetReferenceTool.js'; import { ListStylesTool } from './list-styles-tool/ListStylesTool.js'; import { ListTokensTool } from './list-tokens-tool/ListTokensTool.js'; import { PreviewStyleTool } from './preview-style-tool/PreviewStyleTool.js'; @@ -35,6 +36,7 @@ export const ALL_TOOLS = [ new CountryBoundingBoxTool(), new CoordinateConversionTool(), new GetMapboxDocSourceTool({ httpRequest }), + new GetReferenceTool(), new StyleComparisonTool(), new TilequeryTool({ httpRequest }) ] as const; diff --git a/test/resources/MapboxLayerTypeMappingResource.test.ts b/test/resources/MapboxLayerTypeMappingResource.test.ts new file mode 100644 index 0000000..23263e3 --- /dev/null +++ b/test/resources/MapboxLayerTypeMappingResource.test.ts @@ -0,0 +1,146 @@ +// Copyright (c) Mapbox, Inc. +// Licensed under the MIT License. + +import { describe, it, expect, beforeEach } from 'vitest'; +import { MapboxLayerTypeMappingResource } from '../../src/resources/mapbox-layer-type-mapping-resource/MapboxLayerTypeMappingResource.js'; + +describe('MapboxLayerTypeMappingResource', () => { + let resource: MapboxLayerTypeMappingResource; + + beforeEach(() => { + resource = new MapboxLayerTypeMappingResource(); + }); + + describe('basic properties', () => { + it('should have correct name and URI', () => { + expect(resource.name).toBe('Mapbox Layer Type Mapping'); + expect(resource.uri).toBe('resource://mapbox-layer-type-mapping'); + expect(resource.mimeType).toBe('application/json'); + }); + + it('should have a description', () => { + expect(resource.description).toContain('source layers'); + expect(resource.description).toContain('layer types'); + }); + }); + + describe('readCallback', () => { + it('should return JSON content', async () => { + const uri = new URL('resource://mapbox-layer-type-mapping'); + const result = await resource['readCallback'](uri, {}); + + expect(result.contents).toHaveLength(1); + expect(result.contents[0].uri).toBe(uri.href); + expect(result.contents[0].mimeType).toBe('application/json'); + + // Verify it's valid JSON + const parsed = JSON.parse(result.contents[0].text); + expect(parsed).toBeDefined(); + }); + + it('should include geometry type mappings', async () => { + const uri = new URL('resource://mapbox-layer-type-mapping'); + const result = await resource['readCallback'](uri, {}); + const mapping = JSON.parse(result.contents[0].text); + + expect(mapping.geometry_types).toBeDefined(); + expect(mapping.geometry_types.polygon).toBeDefined(); + expect(mapping.geometry_types.line).toBeDefined(); + expect(mapping.geometry_types.point).toBeDefined(); + }); + + it('should map polygon source layers correctly', async () => { + const uri = new URL('resource://mapbox-layer-type-mapping'); + const result = await resource['readCallback'](uri, {}); + const mapping = JSON.parse(result.contents[0].text); + + const polygonLayers = mapping.geometry_types.polygon.source_layers; + expect(polygonLayers).toContain('landuse'); + expect(polygonLayers).toContain('water'); + expect(polygonLayers).toContain('building'); + expect(polygonLayers).toContain('landuse_overlay'); + + const compatibleTypes = + mapping.geometry_types.polygon.compatible_layer_types; + expect(compatibleTypes).toContain('fill'); + expect(compatibleTypes).toContain('fill-extrusion'); + }); + + it('should map line source layers correctly', async () => { + const uri = new URL('resource://mapbox-layer-type-mapping'); + const result = await resource['readCallback'](uri, {}); + const mapping = JSON.parse(result.contents[0].text); + + const lineLayers = mapping.geometry_types.line.source_layers; + expect(lineLayers).toContain('road'); + expect(lineLayers).toContain('admin'); + expect(lineLayers).toContain('waterway'); + + const compatibleTypes = + mapping.geometry_types.line.compatible_layer_types; + expect(compatibleTypes).toContain('line'); + }); + + it('should map point source layers correctly', async () => { + const uri = new URL('resource://mapbox-layer-type-mapping'); + const result = await resource['readCallback'](uri, {}); + const mapping = JSON.parse(result.contents[0].text); + + const pointLayers = mapping.geometry_types.point.source_layers; + expect(pointLayers).toContain('place_label'); + expect(pointLayers).toContain('poi_label'); + expect(pointLayers).toContain('airport_label'); + + const compatibleTypes = + mapping.geometry_types.point.compatible_layer_types; + expect(compatibleTypes).toContain('symbol'); + expect(compatibleTypes).toContain('circle'); + }); + + it('should include layer type compatibility details', async () => { + const uri = new URL('resource://mapbox-layer-type-mapping'); + const result = await resource['readCallback'](uri, {}); + const mapping = JSON.parse(result.contents[0].text); + + expect(mapping.layer_type_compatibility).toBeDefined(); + expect(mapping.layer_type_compatibility.fill).toBeDefined(); + expect(mapping.layer_type_compatibility.line).toBeDefined(); + expect(mapping.layer_type_compatibility.symbol).toBeDefined(); + expect(mapping.layer_type_compatibility.circle).toBeDefined(); + expect(mapping.layer_type_compatibility['fill-extrusion']).toBeDefined(); + }); + + it('should include common usage patterns', async () => { + const uri = new URL('resource://mapbox-layer-type-mapping'); + const result = await resource['readCallback'](uri, {}); + const mapping = JSON.parse(result.contents[0].text); + + expect(mapping.layer_type_compatibility.fill.common_uses).toBeDefined(); + expect( + mapping.layer_type_compatibility.fill.common_uses.length + ).toBeGreaterThan(0); + }); + + it('should include common patterns examples', async () => { + const uri = new URL('resource://mapbox-layer-type-mapping'); + const result = await resource['readCallback'](uri, {}); + const mapping = JSON.parse(result.contents[0].text); + + expect(mapping.common_patterns).toBeDefined(); + expect(mapping.common_patterns.styling_parks).toBeDefined(); + expect(mapping.common_patterns.styling_roads).toBeDefined(); + expect(mapping.common_patterns.labeling_cities).toBeDefined(); + expect(mapping.common_patterns.building_extrusions).toBeDefined(); + }); + + it('should include helpful notes', async () => { + const uri = new URL('resource://mapbox-layer-type-mapping'); + const result = await resource['readCallback'](uri, {}); + const mapping = JSON.parse(result.contents[0].text); + + expect(mapping.notes).toBeDefined(); + expect(Array.isArray(mapping.notes)).toBe(true); + expect(mapping.notes.length).toBeGreaterThan(0); + }); + }); +}); diff --git a/test/resources/MapboxStreetsV8FieldsResource.test.ts b/test/resources/MapboxStreetsV8FieldsResource.test.ts new file mode 100644 index 0000000..96f5b5b --- /dev/null +++ b/test/resources/MapboxStreetsV8FieldsResource.test.ts @@ -0,0 +1,70 @@ +// Copyright (c) Mapbox, Inc. +// Licensed under the MIT License. + +import { describe, it, expect, beforeEach } from 'vitest'; +import { MapboxStreetsV8FieldsResource } from '../../src/resources/mapbox-streets-v8-fields-resource/MapboxStreetsV8FieldsResource.js'; +import { STREETS_V8_FIELDS } from '../../src/constants/mapboxStreetsV8Fields.js'; + +describe('MapboxStreetsV8FieldsResource', () => { + let resource: MapboxStreetsV8FieldsResource; + + beforeEach(() => { + resource = new MapboxStreetsV8FieldsResource(); + }); + + describe('basic properties', () => { + it('should have correct name and URI', () => { + expect(resource.name).toBe('Mapbox Streets v8 Fields Reference'); + expect(resource.uri).toBe('resource://mapbox-streets-v8-fields'); + expect(resource.mimeType).toBe('application/json'); + }); + + it('should have a description', () => { + expect(resource.description).toContain('Streets v8 source layers'); + expect(resource.description).toContain('field definitions'); + }); + }); + + describe('readCallback', () => { + it('should return STREETS_V8_FIELDS as JSON', async () => { + const uri = new URL('resource://mapbox-streets-v8-fields'); + const result = await resource['readCallback'](uri, {}); + + expect(result.contents).toHaveLength(1); + expect(result.contents[0].uri).toBe(uri.href); + expect(result.contents[0].mimeType).toBe('application/json'); + + // Parse the JSON and verify it matches STREETS_V8_FIELDS + const parsed = JSON.parse(result.contents[0].text); + expect(parsed).toEqual(STREETS_V8_FIELDS); + }); + + it('should include known source layers', async () => { + const uri = new URL('resource://mapbox-streets-v8-fields'); + const result = await resource['readCallback'](uri, {}); + const parsed = JSON.parse(result.contents[0].text); + + // Verify some key source layers exist + expect(parsed.landuse).toBeDefined(); + expect(parsed.road).toBeDefined(); + expect(parsed.water).toBeDefined(); + expect(parsed.building).toBeDefined(); + expect(parsed.poi_label).toBeDefined(); + }); + + it('should include field definitions with values', async () => { + const uri = new URL('resource://mapbox-streets-v8-fields'); + const result = await resource['readCallback'](uri, {}); + const parsed = JSON.parse(result.contents[0].text); + + // Check that landuse has class field with values + expect(parsed.landuse.class).toBeDefined(); + expect(parsed.landuse.class.values).toBeInstanceOf(Array); + expect(parsed.landuse.class.values).toContain('park'); + + // Check that road has class field with values + expect(parsed.road.class).toBeDefined(); + expect(parsed.road.class.values).toContain('motorway'); + }); + }); +}); diff --git a/test/resources/MapboxTokenScopesResource.test.ts b/test/resources/MapboxTokenScopesResource.test.ts new file mode 100644 index 0000000..897fb96 --- /dev/null +++ b/test/resources/MapboxTokenScopesResource.test.ts @@ -0,0 +1,92 @@ +// Copyright (c) Mapbox, Inc. +// Licensed under the MIT License. + +import { describe, it, expect, beforeEach } from 'vitest'; +import { MapboxTokenScopesResource } from '../../src/resources/mapbox-token-scopes-resource/MapboxTokenScopesResource.js'; + +describe('MapboxTokenScopesResource', () => { + let resource: MapboxTokenScopesResource; + + beforeEach(() => { + resource = new MapboxTokenScopesResource(); + }); + + describe('basic properties', () => { + it('should have correct name and URI', () => { + expect(resource.name).toBe('Mapbox Token Scopes Reference'); + expect(resource.uri).toBe('resource://mapbox-token-scopes'); + expect(resource.mimeType).toBe('text/markdown'); + }); + + it('should have a description', () => { + expect(resource.description).toContain('token scopes'); + expect(resource.description).toContain('permissions'); + }); + }); + + describe('readCallback', () => { + it('should return markdown content', async () => { + const uri = new URL('resource://mapbox-token-scopes'); + const result = await resource['readCallback'](uri, {}); + + expect(result.contents).toHaveLength(1); + expect(result.contents[0].uri).toBe(uri.href); + expect(result.contents[0].mimeType).toBe('text/markdown'); + expect(result.contents[0].text).toBeTruthy(); + }); + + it('should document public token scopes', async () => { + const uri = new URL('resource://mapbox-token-scopes'); + const result = await resource['readCallback'](uri, {}); + const markdown = result.contents[0].text; + + // Verify public scopes are documented + expect(markdown).toContain('styles:tiles'); + expect(markdown).toContain('styles:read'); + expect(markdown).toContain('fonts:read'); + expect(markdown).toContain('datasets:read'); + expect(markdown).toContain('vision:read'); + }); + + it('should document secret token scopes', async () => { + const uri = new URL('resource://mapbox-token-scopes'); + const result = await resource['readCallback'](uri, {}); + const markdown = result.contents[0].text; + + // Verify secret scopes are documented + expect(markdown).toContain('styles:write'); + expect(markdown).toContain('styles:list'); + expect(markdown).toContain('tokens:read'); + expect(markdown).toContain('tokens:write'); + }); + + it('should include scope descriptions and use cases', async () => { + const uri = new URL('resource://mapbox-token-scopes'); + const result = await resource['readCallback'](uri, {}); + const markdown = result.contents[0].text; + + // Verify scope descriptions are included + expect(markdown).toContain('Purpose'); + expect(markdown).toContain('Required for'); + expect(markdown).toContain('Use case'); + }); + + it('should include common scope combinations', async () => { + const uri = new URL('resource://mapbox-token-scopes'); + const result = await resource['readCallback'](uri, {}); + const markdown = result.contents[0].text; + + expect(markdown).toContain('Common Scope Combinations'); + expect(markdown).toContain('For Map Display'); + }); + + it('should include best practices', async () => { + const uri = new URL('resource://mapbox-token-scopes'); + const result = await resource['readCallback'](uri, {}); + const markdown = result.contents[0].text; + + expect(markdown).toContain('Best Practices'); + expect(markdown).toContain('Public Tokens in Client Applications'); + }); + }); +}); diff --git a/test/tools/__snapshots__/tool-naming-convention.test.ts.snap b/test/tools/__snapshots__/tool-naming-convention.test.ts.snap index bbcac06..379ca93 100644 --- a/test/tools/__snapshots__/tool-naming-convention.test.ts.snap +++ b/test/tools/__snapshots__/tool-naming-convention.test.ts.snap @@ -42,6 +42,11 @@ exports[`Tool Naming Convention > should maintain consistent tool list (snapshot "description": "Get the latest official Mapbox documentation, APIs, SDKs, and developer resources directly from Mapbox. Always up-to-date, comprehensive coverage of all current Mapbox services including mapping, navigation, search, geocoding, and mobile SDKs. Use this for accurate, official Mapbox information instead of web search.", "toolName": "get_latest_mapbox_docs_tool", }, + { + "className": "GetReferenceTool", + "description": "Get Mapbox reference documentation including Streets v8 field definitions, token scopes, layer type mappings, and style specifications. Use this tool to understand what fields, scopes, or layer types are available before creating styles or tokens.", + "toolName": "get_reference_tool", + }, { "className": "ListStylesTool", "description": "List styles for a Mapbox account. Use limit parameter to avoid large responses (recommended: limit=5-10). Use start parameter for pagination.", From d3337f23ac5768f59a357be125972419e1006a68 Mon Sep 17 00:00:00 2001 From: Matthew Podwysocki Date: Wed, 22 Oct 2025 17:33:40 -0400 Subject: [PATCH 2/4] [quality tests] ensure quality of structuredContent From aeb105e67493a1642cde39f7979c7421f0cde518 Mon Sep 17 00:00:00 2001 From: Matthew Podwysocki Date: Fri, 24 Oct 2025 15:05:34 -0400 Subject: [PATCH 3/4] [resources] Add new resources to server with fallback tool --- .../mapboxStreetsV8Fields.trimmed.ts | 2049 +++++++++++++++++ .../MapboxStreetsV8FieldsResource.ts | 2 +- .../MapboxStreetsV8FieldsResource.test.ts | 2 +- 3 files changed, 2051 insertions(+), 2 deletions(-) create mode 100644 src/constants/mapboxStreetsV8Fields.trimmed.ts diff --git a/src/constants/mapboxStreetsV8Fields.trimmed.ts b/src/constants/mapboxStreetsV8Fields.trimmed.ts new file mode 100644 index 0000000..6d30e49 --- /dev/null +++ b/src/constants/mapboxStreetsV8Fields.trimmed.ts @@ -0,0 +1,2049 @@ +/** + * Trimmed Mapbox Streets v8 source layer field definitions + * Only includes fields with enumerated values for filtering and styling + * + * Omitted for brevity: + * - Fields with unbounded string/number values (name, ref, house_num, height, etc.) + * - Localized name fields (name_de, name_en, name_es, etc.) - these exist but have no fixed values + * - Description fields that just repeat the key name + * - iso_3166_2 field (always unbounded string) + */ + +// Common field that appears identically in 13 layers +const ISO_3166_1_FIELD = { + values: [ + 'EG', + 'ET', + 'CD', + 'ZA', + 'TZ', + 'KE', + 'SD', + 'UG', + 'MA', + 'DZ', + 'GH', + 'CI', + 'CM', + 'MG', + 'MZ', + 'NG', + 'NE', + 'BF', + 'MW', + 'ML', + 'TD', + 'SN', + 'AO', + 'ZW', + 'CN', + 'IN', + 'ID', + 'PK', + 'BD', + 'RU', + 'JP', + 'PH', + 'VN', + 'TR', + 'IR', + 'TH', + 'MM', + 'KR', + 'IQ', + 'AF', + 'MY', + 'NP', + 'DE', + 'FR', + 'GB', + 'IT', + 'ES', + 'UA', + 'PL', + 'RO', + 'NL', + 'GR', + 'HR', + 'BE', + 'PT', + 'CZ', + 'HU', + 'BY', + 'SE', + 'AT', + 'CH', + 'BG', + 'RS', + 'DK', + 'FI', + 'US', + 'MX', + 'CA', + 'GT', + 'CU', + 'HT', + 'DO', + 'HN', + 'NI', + 'SV', + 'CR', + 'PR', + 'PA', + 'JM', + 'TT', + 'GP', + 'MQ', + 'AU', + 'PG', + 'NZ', + 'FJ', + 'MU', + 'RE', + 'MV', + 'SC', + 'BR', + 'CO', + 'AR', + 'PE', + 'VE', + 'CL', + 'EC', + 'BO', + 'PY', + 'UY' + ] as const +} as const; + +export const STREETS_V8_FIELDS = { + // ============ landuse ============ + landuse: { + class: { + values: [ + 'aboriginal_lands', + 'agriculture', + 'airport', + 'cemetery', + 'commercial_area', + 'facility', + 'glacier', + 'grass', + 'hospital', + 'industrial', + 'park', + 'parking', + 'piste', + 'pitch', + 'residential', + 'rock', + 'sand', + 'school', + 'scrub', + 'wood' + ] as const + }, + type: { + values: [ + 'wood', + 'farmland', + 'forest', + 'grass', + 'meadow', + 'scrub', + 'parking', + 'surface', + 'park', + 'farmyard', + 'orchard', + 'grassland', + 'garden', + 'school', + 'soccer', + 'vineyard', + 'playground', + 'tennis', + 'bare_rock', + 'allotments', + 'pitch', + 'heath', + 'baseball', + 'bunker', + 'quarry', + 'beach', + 'basketball', + 'scree', + 'village_green', + 'recreation_ground', + 'sports_centre', + 'common', + 'christian', + 'tee', + 'sand', + 'green', + 'multi', + 'hospital', + 'greenhouse_horticulture', + 'glacier', + 'fairway', + 'farm', + 'golf_course', + 'camp_site', + 'university', + 'college', + 'plant_nursery', + 'equestrian', + 'fell', + 'beachvolleyball', + 'volleyball', + 'american_football', + 'athletics', + 'caravan_site', + 'rock', + 'muslim', + 'skateboard', + 'wetland', + 'bowls', + 'picnic_site', + 'boules', + 'cricket', + 'dog_park', + 'running', + 'conservation', + 'track', + 'netball', + 'underground', + 'lane', + 'rugby_union', + 'zoo', + 'hockey', + 'shooting', + 'downhill', + 'jewish', + 'field', + 'football', + 'table_tennis', + 'handball', + 'rough', + 'field_hockey', + 'team_handball', + 'carports', + 'pelota', + 'rugby', + 'paddle_tennis', + 'archery', + 'horse_racing', + 'gaelic_games', + 'softball', + 'golf', + 'ice_hockey', + 'basin', + 'coastline', + 'badminton', + 'driving_range', + 'bog', + 'cricket_nets', + 'swimming', + 'futsal' + ] as const + } + }, + + // ============ waterway ============ + waterway: { + iso_3166_1: ISO_3166_1_FIELD, + class: { + values: [ + 'river', + 'canal', + 'stream', + 'stream_intermittent', + 'ditch', + 'drain' + ] as const + }, + type: { + values: ['river', 'canal', 'stream', 'ditch', 'drain'] as const + } + }, + + // ============ water ============ + water: {}, + + // ============ aeroway ============ + aeroway: { + iso_3166_1: ISO_3166_1_FIELD, + type: { + values: ['runway', 'taxiway', 'apron', 'helipad'] as const + } + }, + + // ============ structure ============ + structure: { + iso_3166_1: ISO_3166_1_FIELD, + class: { + values: [ + 'cliff', + 'crosswalk', + 'entrance', + 'fence', + 'gate', + 'hedge', + 'land' + ] as const + }, + type: { + values: [ + 'bollard', + 'breakwater', + 'bridge', + 'city_wall', + 'cliff', + 'crosswalk', + 'earth_bank', + 'entrance', + 'fence', + 'gate', + 'hedge', + 'home', + 'kissing_gate', + 'lift_gate', + 'main', + 'pier', + 'retaining_wall', + 'sliding_gate', + 'spikes', + 'staircase', + 'swing_gate', + 'wall', + 'wire_fence', + 'yes' + ] as const + } + }, + + // ============ building ============ + building: { + iso_3166_1: ISO_3166_1_FIELD, + extrude: { + values: ['true', 'false'] as const + }, + type: { + values: [ + 'building', + 'house', + 'residential', + 'garage', + 'apartments', + 'industrial', + 'hut', + 'detached', + 'shed', + 'roof', + 'commercial', + 'terrace', + 'garages', + 'school', + 'building:part', + 'retail', + 'construction', + 'greenhouse', + 'barn', + 'farm_auxiliary', + 'church', + 'warehouse', + 'service', + 'farm', + 'civic', + 'cabin', + 'manufacture', + 'university', + 'office', + 'static_caravan', + 'hangar', + 'public', + 'collapsed', + 'hospital', + 'semidetached_house', + 'hotel', + 'bungalow', + 'chapel', + 'ger', + 'kindergarten', + 'ruins', + 'parking', + 'storage_tank', + 'dormitory', + 'mosque', + 'commercial;residential', + 'transportation', + 'stable', + 'train_station', + 'damaged', + 'college', + 'semi', + 'transformer_tower', + 'houseboat', + 'trullo', + 'bunker', + 'station', + 'slurry_tank', + 'shop', + 'cowshed', + 'silo', + 'supermarket', + 'pajaru', + 'terminal', + 'carport', + 'residence', + 'dam', + 'temple', + 'duplex', + 'factory', + 'agricultural', + 'constructie', + 'allotment_house', + 'chalet', + 'kiosk', + 'tower', + 'tank', + 'shelter', + 'dwelling_house', + 'pavilion', + 'grandstand', + 'Residence', + 'ruin', + 'boathouse', + 'store', + 'summer_cottage', + 'mobile_home', + 'government_office', + 'outbuilding', + 'beach_hut', + 'pub', + 'glasshouse', + 'apartment', + 'storage', + 'community_group_office', + 'clinic', + 'residences', + 'cathedral', + 'bangunan', + 'semi-detached' + ] as const + }, + underground: { + values: ['true', 'false'] as const + } + }, + + // ============ landuse_overlay ============ + landuse_overlay: { + class: { + values: ['national_park', 'wetland', 'wetland_noveg'] as const + }, + type: { + values: [ + 'wetland', + 'bog', + 'basin', + 'marsh', + 'swamp', + 'nature_reserve', + 'protected_area', + 'reedbed', + 'wet_meadow', + 'tidalflat', + 'mangrove', + 'mud', + 'saltmarsh', + 'national_park', + 'string_bog', + 'saltern', + 'fen', + 'palsa_bog', + 'tundra_pond', + 'peat_bog', + 'reed', + 'raised_bog', + 'reef' + ] as const + } + }, + + // ============ road ============ + road: { + name_script: { + values: [ + 'Arabic', + 'Armenian', + 'Bengali', + 'Bopomofo', + 'Canadian_Aboriginal', + 'Common', + 'Cyrillic', + 'Devanagari', + 'Ethiopic', + 'Georgian', + 'Glagolitic', + 'Greek', + 'Gujarati', + 'Gurmukhi', + 'Han', + 'Hangul', + 'Hebrew', + 'Hiragana', + 'Kannada', + 'Katakana', + 'Khmer', + 'Lao', + 'Latin', + 'Malayalam', + 'Mongolian', + 'Myanmar', + 'Nko', + 'Sinhala', + 'Syriac', + 'Tamil', + 'Telugu', + 'Thaana', + 'Thai', + 'Tibetan', + 'Tifinagh', + 'Unknown' + ] as const + }, + oneway: { + values: ['true', 'false'] as const + }, + bike_lane: { + values: ['left', 'right', 'both', 'no', 'yes'] as const + }, + layer: { + values: [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5] as const + }, + access: { + values: ['restricted'] as const + }, + dual_carriageway: { + values: ['true', 'false'] as const + }, + structure: { + values: ['none', 'bridge', 'tunnel', 'ford'] as const + }, + surface: { + values: ['paved', 'unpaved'] as const + }, + shield: { + values: [ + 'default', + 'rectangle-white', + 'rectangle-red', + 'rectangle-yellow', + 'rectangle-green', + 'rectangle-blue', + 'circle-white', + 'ae-national', + 'ae-d-route', + 'ae-f-route', + 'ae-s-route', + 'au-national-highway', + 'au-national-route', + 'au-state', + 'au-tourist', + 'br-federal', + 'br-state', + 'ch-motorway', + 'cn-nths-expy', + 'cn-provincial-expy', + 'de-motorway', + 'gr-motorway', + 'hk-strategic-route', + 'hr-motorway', + 'hu-motorway', + 'hu-main', + 'in-national', + 'in-state', + 'kr-natl-expy', + 'kr-natl-hwy', + 'kr-metro-expy', + 'kr-metropolitan', + 'kr-local', + 'mx-federal', + 'mx-state', + 'nz-state', + 'pe-national', + 'pe-regional', + 'ro-national', + 'ro-county', + 'ro-communal', + 'si-motorway', + 'tw-national', + 'tw-provincial-expy', + 'tw-provincial', + 'tw-county-township', + 'us-interstate', + 'us-interstate-duplex', + 'us-interstate-business', + 'us-interstate-truck', + 'us-highway', + 'us-highway-duplex', + 'us-highway-alternate', + 'us-highway-business', + 'us-highway-bypass', + 'us-highway-truck', + 'us-bia', + 'za-national', + 'za-provincial' + ] as const + }, + shield_beta: { + values: [ + 'default', + 'rectangle-white', + 'rectangle-red', + 'rectangle-yellow', + 'rectangle-green', + 'rectangle-blue', + 'circle-white', + 'ae-national', + 'ae-d-route', + 'ae-f-route', + 'ae-s-route', + 'au-national-highway', + 'au-national-route', + 'au-state', + 'au-tourist', + 'br-federal', + 'br-state', + 'ch-motorway', + 'cn-nths-expy', + 'cn-provincial-expy', + 'de-motorway', + 'gr-motorway', + 'hk-strategic-route', + 'hr-motorway', + 'hu-motorway', + 'hu-main', + 'in-national', + 'in-state', + 'kr-natl-expy', + 'kr-natl-hwy', + 'kr-metro-expy', + 'kr-metropolitan', + 'kr-local', + 'mx-federal', + 'mx-state', + 'nz-state', + 'pe-national', + 'pe-regional', + 'ro-national', + 'ro-county', + 'ro-communal', + 'si-motorway', + 'tw-national', + 'tw-provincial-expy', + 'tw-provincial', + 'tw-county-township', + 'us-interstate', + 'us-interstate-duplex', + 'us-interstate-business', + 'us-interstate-truck', + 'us-highway', + 'us-highway-duplex', + 'us-highway-alternate', + 'us-highway-business', + 'us-highway-bypass', + 'us-highway-truck', + 'us-bia', + 'za-national', + 'za-provincial', + 'al-motorway', + 'ar-national', + 'cl-highway', + 'co-national', + 'cy-motorway', + 'il-highway-black', + 'il-highway-blue', + 'il-highway-green', + 'il-highway-red', + 'it-motorway', + 'md-local', + 'md-main', + 'my-expressway', + 'my-federal', + 'nz-urban', + 'ph-expressway', + 'ph-primary', + 'qa-main', + 'sa-highway', + 'th-highway', + 'th-motorway-toll', + 'tr-motorway' + ] as const + }, + shield_text_color: { + values: ['black', 'blue', 'white', 'yellow', 'orange'] as const + }, + shield_text_color_beta: { + values: ['black', 'blue', 'white', 'yellow', 'red', 'green'] as const + }, + iso_3166_1: ISO_3166_1_FIELD, + class: { + values: [ + 'motorway', + 'motorway_link', + 'trunk', + 'trunk_link', + 'primary', + 'primary_link', + 'secondary', + 'secondary_link', + 'tertiary', + 'tertiary_link', + 'level_crossing', + 'street', + 'street_limited', + 'pedestrian', + 'construction', + 'track', + 'service', + 'ferry', + 'path', + 'major_rail', + 'minor_rail', + 'service_rail', + 'aerialway', + 'golf', + 'turning_circle', + 'roundabout', + 'mini_roundabout', + 'turning_loop', + 'traffic_signals', + 'intersection' + ] as const + }, + type: { + values: [ + 'motorway', + 'motorway_link', + 'trunk', + 'primary', + 'secondary', + 'tertiary', + 'trunk_link', + 'primary_link', + 'secondary_link', + 'tertiary_link', + 'residential', + 'unclassified', + 'road', + 'living_street', + 'level_crossing', + 'raceway', + 'pedestrian', + 'platform', + 'construction:motorway', + 'construction:motorway_link', + 'construction:trunk', + 'construction:trunk_link', + 'construction:primary', + 'construction:primary_link', + 'construction:secondary', + 'construction:secondary_link', + 'construction:tertiary', + 'construction:tertiary_link', + 'construction:unclassified', + 'construction:residential', + 'construction:road', + 'construction:living_street', + 'construction:pedestrian', + 'construction', + 'track:grade1', + 'track:grade2', + 'track:grade3', + 'track:grade4', + 'track:grade5', + 'track', + 'service:alley', + 'service:emergency_access', + 'service:drive_through', + 'service:driveway', + 'service:parking_aisle', + 'service', + 'ferry', + 'ferry_auto', + 'steps', + 'corridor', + 'sidewalk', + 'crossing', + 'piste', + 'mountain_bike', + 'hiking', + 'trail', + 'cycleway', + 'footway', + 'path', + 'bridleway', + 'rail', + 'subway', + 'narrow_gauge', + 'funicular', + 'light_rail', + 'miniature', + 'monorail', + 'preserved', + 'tram', + 'aerialway:cable_car', + 'aerialway:gondola', + 'aerialway:mixed_lift', + 'aerialway:chair_lift', + 'aerialway:drag_lift', + 'aerialway:magic_carpet', + 'aerialway', + 'hole', + 'turning_circle', + 'mini_roundabout', + 'traffic_signals' + ] as const + }, + toll: { + values: ['true'] as const + } + }, + + // ============ admin ============ + admin: { + admin_level: { + values: [0, 1, 2] as const + }, + disputed: { + values: ['true', 'false'] as const + }, + iso_3166_1: ISO_3166_1_FIELD, + maritime: { + values: ['true', 'false'] as const + }, + worldview: { + values: ['JP', 'CN', 'IN', 'US', 'all'] as const + } + }, + + // ============ place_label ============ + place_label: { + class: { + values: [ + 'country', + 'disputed_country', + 'state', + 'disputed_state', + 'settlement', + 'settlement_subdivision' + ] as const + }, + name_script: { + values: [ + 'Arabic', + 'Armenian', + 'Bengali', + 'Bopomofo', + 'Canadian_Aboriginal', + 'Common', + 'Cyrillic', + 'Devanagari', + 'Ethiopic', + 'Georgian', + 'Glagolitic', + 'Greek', + 'Gujarati', + 'Gurmukhi', + 'Han', + 'Hangul', + 'Hebrew', + 'Hiragana', + 'Kannada', + 'Katakana', + 'Khmer', + 'Lao', + 'Latin', + 'Malayalam', + 'Mongolian', + 'Myanmar', + 'Nko', + 'Sinhala', + 'Syriac', + 'Tamil', + 'Telugu', + 'Thaana', + 'Thai', + 'Tibetan', + 'Tifinagh', + 'Unknown' + ] as const + }, + filterrank: { + values: [0, 1, 2, 3, 4, 5] as const + }, + capital: { + values: [2, 3, 4, 5, 6] as const + }, + text_anchor: { + values: [ + 'left', + 'right', + 'top', + 'top-left', + 'top-right', + 'bottom', + 'bottom-left', + 'bottom-right' + ] as const + }, + symbolrank: { + values: [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 + ] as const + }, + type: { + values: [ + 'country', + 'territory', + 'sar', + 'disputed_territory', + 'state', + 'city', + 'town', + 'village', + 'hamlet', + 'suburb', + 'neighbourhood', + 'quarter' + ] as const + }, + iso_3166_1: ISO_3166_1_FIELD, + worldview: { + values: ['JP', 'CN', 'IN', 'US', 'all'] as const + } + }, + + // ============ airport_label ============ + airport_label: { + class: { + values: ['military', 'civil'] as const + }, + name_script: { + values: [ + 'Arabic', + 'Armenian', + 'Bengali', + 'Bopomofo', + 'Canadian_Aboriginal', + 'Common', + 'Cyrillic', + 'Devanagari', + 'Ethiopic', + 'Georgian', + 'Glagolitic', + 'Greek', + 'Gujarati', + 'Gurmukhi', + 'Han', + 'Hangul', + 'Hebrew', + 'Hiragana', + 'Kannada', + 'Katakana', + 'Khmer', + 'Lao', + 'Latin', + 'Malayalam', + 'Mongolian', + 'Myanmar', + 'Nko', + 'Sinhala', + 'Syriac', + 'Tamil', + 'Telugu', + 'Thaana', + 'Thai', + 'Tibetan', + 'Tifinagh', + 'Unknown' + ] as const + }, + maki: { + values: ['airport', 'heliport', 'rocket', 'airfield'] as const + }, + sizerank: { + values: [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 + ] as const + }, + worldview: { + values: ['JP', 'CN', 'IN', 'US', 'all'] as const + }, + iso_3166_1: ISO_3166_1_FIELD + }, + + // ============ transit_stop_label ============ + transit_stop_label: { + name_script: { + values: [ + 'Arabic', + 'Armenian', + 'Bengali', + 'Bopomofo', + 'Canadian_Aboriginal', + 'Common', + 'Cyrillic', + 'Devanagari', + 'Ethiopic', + 'Georgian', + 'Glagolitic', + 'Greek', + 'Gujarati', + 'Gurmukhi', + 'Han', + 'Hangul', + 'Hebrew', + 'Hiragana', + 'Kannada', + 'Katakana', + 'Khmer', + 'Lao', + 'Latin', + 'Malayalam', + 'Mongolian', + 'Myanmar', + 'Nko', + 'Sinhala', + 'Syriac', + 'Tamil', + 'Telugu', + 'Thaana', + 'Thai', + 'Tibetan', + 'Tifinagh', + 'Unknown' + ] as const + }, + mode: { + values: [ + 'metro_rail', + 'rail', + 'light_rail', + 'tram', + 'monorail', + 'funicular', + 'bicycle', + 'bus', + 'ferry', + 'narrow_gauge', + 'preserved', + 'miniature' + ] as const + }, + stop_type: { + values: ['stop', 'station', 'entrance'] as const + }, + maki: { + values: [ + 'bus', + 'rail', + 'rail-light', + 'entrance', + 'ferry', + 'bicycle-share', + 'rail-metro' + ] as const + }, + network: { + values: [ + 'barcelona-metro', + 'boston-t', + 'chongqing-rail-transit', + 'de-s-bahn', + 'de-s-bahn.de-u-bahn', + 'de-u-bahn', + 'delhi-metro', + 'gb-national-rail', + 'gb-national-rail.london-dlr', + 'gb-national-rail.london-dlr.london-overground.london-tfl-rail.london-underground', + 'gb-national-rail.london-dlr.london-overground.london-underground', + 'gb-national-rail.london-dlr.london-underground', + 'gb-national-rail.london-overground', + 'gb-national-rail.london-overground.london-underground', + 'gb-national-rail.london-overground.london-tfl-rail.london-underground', + 'gb-national-rail.london-tfl-rail', + 'gb-national-rail.london-tfl-rail.london-overground', + 'gb-national-rail.london-tfl-rail.london-underground', + 'gb-national-rail.london-underground', + 'hong-kong-mtr', + 'kiev-metro', + 'london-dlr', + 'london-dlr.london-tfl-rail', + 'london-dlr.london-tfl-rail.london-underground', + 'london-dlr.london-underground', + 'london-overground', + 'london-overground.london-tfl-rail', + 'london-overground.london-tfl-rail.london-underground', + 'london-overground.london-underground', + 'london-tfl-rail', + 'london-tfl-rail.london-underground', + 'london-underground', + 'madrid-metro', + 'mexico-city-metro', + 'milan-metro', + 'moscow-metro', + 'new-york-subway', + 'osaka-subway', + 'oslo-metro', + 'paris-metro', + 'paris-metro.paris-rer', + 'paris-rer', + 'paris-rer.paris-transilien', + 'paris-transilien', + 'philadelphia-septa', + 'san-francisco-bart', + 'singapore-mrt', + 'stockholm-metro', + 'taipei-metro', + 'tokyo-metro', + 'vienna-u-bahn', + 'washington-metro', + 'rail', + 'rail-metro', + 'rail-light', + 'entrance', + 'bus', + 'ferry', + 'bicycle-share' + ] as const + }, + network_beta: { + values: [ + 'jp-shinkansen', + 'jp-shinkansen.jp-jr', + 'jp-shinkansen.tokyo-metro', + 'jp-shinkansen.osaka-subway', + 'jp-shinkansen.jp-jr.tokyo-metro', + 'jp-shinkansen.jp-jr.osaka-subway', + 'jp-jr', + 'jp-jr.tokyo-metro', + 'jp-jr.osaka-subway' + ] as const + }, + iso_3166_1: ISO_3166_1_FIELD, + filterrank: { + values: [0, 1, 2, 3, 4, 5] as const + } + }, + + // ============ natural_label ============ + natural_label: { + iso_3166_1: ISO_3166_1_FIELD, + name_script: { + values: [ + 'Arabic', + 'Armenian', + 'Bengali', + 'Bopomofo', + 'Canadian_Aboriginal', + 'Common', + 'Cyrillic', + 'Devanagari', + 'Ethiopic', + 'Georgian', + 'Glagolitic', + 'Greek', + 'Gujarati', + 'Gurmukhi', + 'Han', + 'Hangul', + 'Hebrew', + 'Hiragana', + 'Kannada', + 'Katakana', + 'Khmer', + 'Lao', + 'Latin', + 'Malayalam', + 'Mongolian', + 'Myanmar', + 'Nko', + 'Sinhala', + 'Syriac', + 'Tamil', + 'Telugu', + 'Thaana', + 'Thai', + 'Tibetan', + 'Tifinagh', + 'Unknown' + ] as const + }, + sizerank: { + values: [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 + ] as const + }, + class: { + values: [ + 'ocean', + 'sea', + 'disputed_sea', + 'water', + 'reservoir', + 'river', + 'bay', + 'dock', + 'river', + 'canal', + 'stream', + 'landform', + 'wetland', + 'water_feature', + 'glacier', + 'continent' + ] as const + }, + maki: { + values: ['marker', 'waterfall', 'volcano', 'mountain'] as const + }, + worldview: { + values: ['JP', 'CN', 'IN', 'US', 'all'] as const + } + }, + + // ============ poi_label ============ + poi_label: { + name_script: { + values: [ + 'Arabic', + 'Armenian', + 'Bengali', + 'Bopomofo', + 'Canadian_Aboriginal', + 'Common', + 'Cyrillic', + 'Devanagari', + 'Ethiopic', + 'Georgian', + 'Glagolitic', + 'Greek', + 'Gujarati', + 'Gurmukhi', + 'Han', + 'Hangul', + 'Hebrew', + 'Hiragana', + 'Kannada', + 'Katakana', + 'Khmer', + 'Lao', + 'Latin', + 'Malayalam', + 'Mongolian', + 'Myanmar', + 'Nko', + 'Sinhala', + 'Syriac', + 'Tamil', + 'Telugu', + 'Thaana', + 'Thai', + 'Tibetan', + 'Tifinagh', + 'Unknown' + ] as const + }, + maki: { + values: [ + 'amusement-park', + 'aquarium', + 'art-gallery', + 'attraction', + 'cinema', + 'casino', + 'museum', + 'stadium', + 'theatre', + 'zoo', + 'marker', + 'bank', + 'bicycle', + 'car-rental', + 'laundry', + 'suitcase', + 'veterinary', + 'college', + 'school', + 'bar', + 'beer', + 'cafe', + 'fast-food', + 'ice-cream', + 'restaurant', + 'restaurant-noodle', + 'restaurant-pizza', + 'restaurant-seafood', + 'alcohol-shop', + 'bakery', + 'grocery', + 'convenience', + 'confectionery', + 'castle', + 'monument', + 'harbor', + 'farm', + 'bridge', + 'communications-tower', + 'watermill', + 'windmill', + 'lodging', + 'dentist', + 'doctor', + 'hospital', + 'pharmacy', + 'fuel', + 'car-repair', + 'charging-station', + 'parking', + 'parking-garage', + 'campsite', + 'cemetery', + 'dog-park', + 'garden', + 'golf', + 'park', + 'picnic-site', + 'playground', + 'embassy', + 'fire-station', + 'library', + 'police', + 'post', + 'prison', + 'town-hall', + 'place-of-worship', + 'religious-buddhist', + 'religious-christian', + 'religious-jewish', + 'religious-muslim', + 'viewpoint', + 'horse-riding', + 'swimming', + 'beach', + 'american-football', + 'basketball', + 'tennis', + 'table-tennis', + 'volleyball', + 'bowling-alley', + 'slipway', + 'pitch', + 'fitness-centre', + 'skateboard', + 'car', + 'clothing-store', + 'furniture', + 'hardware', + 'globe', + 'jewelry-store', + 'mobile-phone', + 'optician', + 'shoe', + 'watch', + 'shop', + 'music', + 'drinking-water', + 'information', + 'toilet', + 'ranger-station' + ] as const + }, + maki_beta: { + values: [ + 'baseball', + 'lighthouse', + 'landmark', + 'industry', + 'highway-services', + 'highway-rest-area', + 'racetrack-cycling', + 'racetrack-horse', + 'racetrack-boat', + 'racetrack', + 'religious-shinto', + 'observation-tower', + 'restaurant-bbq', + 'tunnel' + ] as const + }, + maki_modifier: { + values: ['JP'] as const + }, + class: { + values: [ + 'arts_and_entertainment', + 'building', + 'commercial_services', + 'education', + 'food_and_drink', + 'food_and_drink_stores', + 'historic', + 'industrial', + 'landmark', + 'lodging', + 'medical', + 'motorist', + 'park_like', + 'place_like', + 'public_facilities', + 'religion', + 'sport_and_leisure', + 'store_like', + 'visitor_amenities', + 'general' + ] as const + }, + type: { + values: [ + 'Parking', + 'Locality', + 'Yes', + 'School', + 'Restaurant', + 'Place Of Worship', + 'Pitch', + 'Swimming Pool', + 'Retail', + 'Playground', + 'Convenience', + 'Residential', + 'Park', + 'Fuel', + 'Fast Food', + 'Isolated Dwelling', + 'Cafe', + 'Supermarket', + 'Cemetery', + 'Hotel', + 'Bank', + 'Industrial', + 'Pharmacy', + 'Clothes', + 'Guidepost', + 'Allotments', + 'Hospital', + 'Apartments', + 'Kindergarten', + 'Toilets', + 'Memorial', + 'Hairdresser', + 'Car Repair', + 'Bar', + 'Commercial', + 'Bakery', + 'Government', + 'Board', + 'Bridge', + 'House', + 'Company', + 'Grave Yard', + 'Drinking Water', + 'Post Office', + 'Pub', + 'Clinic', + 'Beach', + 'Guest House', + 'Sports Centre', + 'Attraction', + 'Viewpoint', + 'Doctors', + 'Car', + 'Townhall', + 'Police', + 'Fire Station', + 'University', + 'Camp Site', + 'Picnic Site', + 'Beauty', + 'Community Centre', + 'Dentist', + 'Works', + 'Library', + 'Shinto', + 'Museum', + 'Social Facility', + 'Wood', + 'Nature Reserve', + 'Mobile Phone', + 'Information', + 'Hardware', + 'Furniture', + 'Buddhist', + 'Chalet', + 'Electronics', + 'Marketplace', + 'Butcher', + 'College', + 'Forest', + 'Mall', + 'Estate Agent', + 'Shoes', + 'Alcohol', + 'Florist', + 'Archaeological Site', + 'Picnic Table', + 'Ruins', + 'Doityourself', + 'Fitness Centre', + 'Car Parts', + 'Monument', + 'Map', + 'Optician', + 'Office', + 'Jewelry', + 'Variety Store', + 'Hostel', + 'Construction', + 'Insurance' + ] as const + }, + brand: { + values: [ + '21rentacar', + '2nd-street', + '31-ice-cream', + '7-eleven', + 'aen', + 'aeon', + 'aiya', + 'alpen', + 'aoki', + 'aoyama', + 'asakuma', + 'atom', + 'audi', + 'autobacs', + 'b-kids', + 'bamiyan', + 'barneys-newyork', + 'benz', + 'best-denki', + 'big-boy', + 'bikkuri-donkey', + 'bmw', + 'bon-belta', + 'book-off', + 'budget', + 'carenex', + 'casa', + 'citroen', + 'cockpit', + 'coco-ichibanya', + 'cocos', + 'community-store', + 'cosmo-oil', + 'costco', + 'daiei', + 'daihatsu', + 'daily-store', + 'daimaru', + 'daiwa', + 'dennys', + 'dio', + 'doutor-coffee', + 'eki-rent-a-car', + 'eneos', + 'f-rent-a-car', + 'familymart', + 'ferrari', + 'fiat', + 'forus', + 'fukudaya-department-store', + 'fukuya', + 'futata', + 'garage-off', + 'general-motors', + 'gmdat', + 'grache-gardens', + 'gulliver', + 'gusto', + 'hamacho', + 'hamazushi', + 'hamburg-restaurant-bell', + 'hankyu-department-store', + 'hanshin', + 'hard-off', + 'haruyama', + 'heisei-car', + 'heiwado', + 'hihirose', + 'hino', + 'hobby-off', + 'hokuren', + 'honda', + 'honda-cars', + 'ichibata-department-store', + 'idemitsu-oil', + 'inageya', + 'isetan', + 'isuzu', + 'ito-yokado', + 'iwataya', + 'izumi', + 'izumiya', + 'izutsuya', + 'j-net-rentcar', + 'ja-ss', + 'jaguar', + 'japan-post-bank', + 'japan-post-insurance', + 'japan-rent-a-car', + 'jolly-ox', + 'jolly-pasta', + 'jonathans', + 'joyfull', + 'jumble-store', + 'kaisen-misakiko', + 'kasumi', + 'kawatoku', + 'keihan-department-store', + 'keio-department-store', + 'kfc', + 'kintetsu-department-store', + 'kygnus-oil', + 'kyubeiya', + 'laforet-harajuku', + 'lamborghini', + 'lamu', + 'landrover', + 'lawson', + 'lexus', + 'life', + 'lotteria', + 'lumine', + 'maruetsu', + 'maruetsupetit', + 'maruhiro-department-store', + 'maruhoncowboy', + 'marui', + 'marunen-me', + 'matsubishi', + 'matsuya', + 'matsuya-department-store', + 'matsuyadenki', + 'matsuzakaya', + 'mazda-autozam', + 'mazda-enfini', + 'mcdonalds', + 'meitetsu-pare-department-store', + 'melsa', + 'michi-no-eki', + 'milky-way', + 'mini', + 'mini-piago', + 'ministop', + 'mitsubishi-corporation-energy', + 'mitsubishi-fuso', + 'mitsubishi-motors', + 'mitsukoshi', + 'mizuho-bank', + 'mode-off', + 'mos-burger', + 'mufg-bank', + 'my-basket', + 'nagasakiya', + 'nakago', + 'nakasan', + 'nakau', + 'natural-lawson', + 'navi', + 'netz-toyota', + 'niconicorentacar', + 'nippo-rent-a-car-system', + 'nippon-rent-a-car', + 'nissan', + 'nissan-cherry', + 'nissan-motor', + 'nissan-parts', + 'nissan-prince', + 'nissan-rent-a-car', + 'nissan-satio', + 'odakyu-department-store', + 'off-house', + 'ohsho', + 'oita-rental', + 'ok', + 'okajima', + 'okuno', + 'okuwa', + 'onuma', + 'orix-rent-a-car', + 'osaka-ohsho', + 'ots-rentacar', + 'palty-fuji', + 'parco', + 'petras', + 'peugeot', + 'plaka', + 'poplar', + 'popolo', + 'pork-cutlet-hamakatsu', + 'porsche', + 'ralse', + 'recycle-mart', + 'red-cabbage', + 'red-lobster', + 'renault', + 'resona-bank', + 'ringer-hut', + 'rolls-royce', + 'royal-host', + 'saga-rent-lease', + 'saijo-department-store', + 'saikaya', + 'saint-marc', + 'saitama-resona-bank', + 'saizeriya', + 'sanbangai', + 'sanei', + 'santa-no-souko', + 'sato', + 'seibu', + 'seicomart', + 'seiyu', + 'shabushabu-dontei', + 'shinkin-bank', + 'showa-shell-oil', + 'sizzler', + 'sky-rentallease', + 'smile-santa', + 'sogo', + 'sokoseikatsukan', + 'solato', + 'starbucks-coffee', + 'steak-hamburg-ken', + 'steak-miya', + 'steak-no-don', + 'store100', + 'subaru', + 'suehiro', + 'sukiya', + 'sumitomo-mitsui-banking-corporation', + 'sunpiazza', + 'sushihan', + 'suzuki', + 'suzuran-department-store', + 'tachiya', + 'taiyakan', + 'takarajima', + 'takashimaya', + 'tamaya', + 'tenmaya', + 'times-car-rental', + 'tobu-department-store', + 'tokiwa', + 'tokyu-department-store', + 'tokyu-store', + 'tomato-onion', + 'tonden', + 'toyopet', + 'toyota', + 'toyota-corolla', + 'toyota-parts', + 'toyota-rent-a-car', + 'tsuruya-department-store', + 'tullys-coffee', + 'ud-trucks', + 'victoria', + 'victoria-station', + 'vivre', + 'volks', + 'volkswagen', + 'volvo', + 'yamakataya', + 'yamazaki-shop', + 'yanase', + 'yao-department-store', + 'yayoiken', + 'yellow-hat', + 'york-benimaru', + 'yoshinoya', + 'you-me-mart', + 'yumean', + 'zenrin' + ] as const + }, + category_en: { + values: [ + 'Locality', + 'School Grounds', + 'Swimming Pool', + 'Restaurant', + 'Park', + 'Church', + 'Shop', + 'Playground', + 'Sport Pitch', + 'Convenience Store', + 'Gas Station', + 'Supermarket', + 'Cafe', + 'Cemetery', + 'Bank', + 'Fast Food', + 'Hotel', + 'Isolated Dwelling', + 'Retail Building', + 'Guidepost', + 'Pharmacy', + 'Residential Area', + 'Community Garden', + 'Clothing Store', + 'Kindergarten', + 'Information Board', + 'Graveyard', + 'Memorial', + 'Apartments', + 'Hospital Grounds', + 'Pub', + 'Post Office', + 'Bar', + 'House', + 'Bakery', + 'Industrial Area', + 'Car Repair Shop', + 'Mosque', + 'Place of Worship', + 'Viewpoint', + 'Sports Complex', + 'Police', + 'Beach', + 'Picnic Site', + 'Tourist Attraction', + 'Guest House', + 'Town Hall', + 'Car Parking', + 'Fire Station', + 'Campground', + 'Car Dealership', + "Doctor's Office", + 'Residential Building', + 'Community Center', + 'Library', + 'Museum', + 'Clinic', + 'Information', + 'Dentist', + 'Social Facility', + 'Monument', + 'Hardware Store', + 'Butcher', + 'Wood', + 'Furniture Store', + 'Florist', + 'Marketplace', + 'University Grounds', + 'Electronics Store', + 'DIY Store', + 'Mall', + 'College Grounds', + 'Shoe Store', + 'Mobile Phone Store', + 'University Building', + 'Archaeological Site', + 'Liquor Store', + 'Quarry', + 'Stadium', + 'Commercial Area', + 'Tower', + 'Buddhist Temple', + 'Hostel', + 'Castle', + 'Factory', + 'Bridge', + 'Ruins', + 'Department Store', + 'Motel', + 'Book Store', + 'Jeweler', + 'Optician', + 'Golf Course', + 'Holiday Cottage', + 'Gift Shop', + 'Farmland', + 'Bicycle Shop', + 'Greengrocer', + 'Theater', + 'Retail Area' + ] as const + }, + 'category_zh-Hans': { + values: [ + '地方', + '学校', + '游泳池', + '餐馆', + '公园', + '基督教堂', + '商店', + '墓地', + '儿童游乐场', + '运动场地', + '便利店', + '加油站', + '超市', + '咖啡馆', + '银行', + '快餐店', + '宾馆', + '孤立居所', + '零售业建筑', + '路标', + '药房', + '居民区', + '公共花园', + '服装店', + '幼儿园', + '信息板', + '纪念碑', + '住宅楼', + '诊所', + '医院', + '酒馆', + '邮局', + '酒吧', + '房屋', + '面包店', + '工业区', + '汽车修理店', + '清真寺', + '礼拜场所', + '观景点', + '体育中心/综合体育场', + '警察局', + '海滩', + '野餐地', + '旅游名胜', + '小旅馆', + '政府办公大楼', + '停车场', + '消防站', + '宿营场地', + '汽车店', + '住宅建筑物', + '社区中心', + '图书馆', + '博物馆', + '游客中心', + '牙科医院', + '社会服务设施', + '纪念堂', + '五金店', + '肉店', + '树林', + '家具店', + '花店', + '鞋店', + '市场', + '大学', + '电子产品店', + 'DIY店', + '购物中心', + '学院', + '手机店', + '大学建筑', + '考古遗址', + '外卖酒店', + '露天矿场', + '体育场', + '商业区', + '塔', + '佛教寺庙', + '旅舍', + '城堡', + '工厂', + '桥梁', + '遗迹', + '百货商场', + '汽车旅馆', + '书店', + '珠宝店', + '眼镜店', + '高尔夫球场', + '度假屋', + '礼品店', + '自行车店', + '蔬果店', + '剧院', + '零售商店', + '兽医院', + '旅行社', + '绿地' + ] as const + }, + iso_3166_1: ISO_3166_1_FIELD, + sizerank: { + values: [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 + ] as const + } + }, + + // ============ motorway_junction ============ + motorway_junction: { + iso_3166_1: ISO_3166_1_FIELD, + class: { + values: [ + 'motorway', + 'motorway_link', + 'trunk', + 'trunk_link', + 'primary', + 'secondary', + 'tertiary', + 'primary_link', + 'secondary_link', + 'tertiary_link', + 'street', + 'street_limited', + 'construction', + 'track', + 'service', + 'path', + 'major_rail', + 'minor_rail', + 'service_rail' + ] as const + }, + type: { + values: [ + 'motorway', + 'trunk', + 'motorway_link', + 'primary', + 'trunk_link', + 'secondary', + 'tertiary', + 'primary_link', + 'secondary_link', + 'tertiary_link' + ] as const + }, + maki_beta: { + values: ['interchange', 'junction'] as const + }, + filterrank: { + values: [0, 1, 2, 3, 4, 5] as const + } + }, + + // ============ housenum_label ============ + housenum_label: { + iso_3166_1: ISO_3166_1_FIELD + } +} as const; + +export type SourceLayer = keyof typeof STREETS_V8_FIELDS; diff --git a/src/resources/mapbox-streets-v8-fields-resource/MapboxStreetsV8FieldsResource.ts b/src/resources/mapbox-streets-v8-fields-resource/MapboxStreetsV8FieldsResource.ts index b2b28fe..3c9f667 100644 --- a/src/resources/mapbox-streets-v8-fields-resource/MapboxStreetsV8FieldsResource.ts +++ b/src/resources/mapbox-streets-v8-fields-resource/MapboxStreetsV8FieldsResource.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. import { BaseResource } from '../BaseResource.js'; -import { STREETS_V8_FIELDS } from '../../constants/mapboxStreetsV8Fields.js'; +import { STREETS_V8_FIELDS } from '../../constants/mapboxStreetsV8Fields.trimmed.js'; /** * Resource providing Mapbox Streets v8 source layer field definitions diff --git a/test/resources/MapboxStreetsV8FieldsResource.test.ts b/test/resources/MapboxStreetsV8FieldsResource.test.ts index 96f5b5b..1263b5c 100644 --- a/test/resources/MapboxStreetsV8FieldsResource.test.ts +++ b/test/resources/MapboxStreetsV8FieldsResource.test.ts @@ -3,7 +3,7 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { MapboxStreetsV8FieldsResource } from '../../src/resources/mapbox-streets-v8-fields-resource/MapboxStreetsV8FieldsResource.js'; -import { STREETS_V8_FIELDS } from '../../src/constants/mapboxStreetsV8Fields.js'; +import { STREETS_V8_FIELDS } from '../../src/constants/mapboxStreetsV8Fields.trimmed.js'; describe('MapboxStreetsV8FieldsResource', () => { let resource: MapboxStreetsV8FieldsResource; From 56b9f9048b435e12c060751dd9fe7bfb827f6ab7 Mon Sep 17 00:00:00 2001 From: Matthew Podwysocki Date: Mon, 27 Oct 2025 10:13:58 -0400 Subject: [PATCH 4/4] [resources] Add new resources to server with fallback tool --- .../mapbox-token-scopes-resource/MapboxTokenScopesResource.ts | 1 + src/tools/list-tokens-tool/ListTokensTool.ts | 2 +- test/tools/__snapshots__/tool-naming-convention.test.ts.snap | 2 +- test/tools/list-tokens-tool/ListTokensTool.test.ts | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/resources/mapbox-token-scopes-resource/MapboxTokenScopesResource.ts b/src/resources/mapbox-token-scopes-resource/MapboxTokenScopesResource.ts index 5e094f4..a6890e1 100644 --- a/src/resources/mapbox-token-scopes-resource/MapboxTokenScopesResource.ts +++ b/src/resources/mapbox-token-scopes-resource/MapboxTokenScopesResource.ts @@ -93,6 +93,7 @@ Secret tokens (like \`MAPBOX_ACCESS_TOKEN\` environment variable) typically have - **Purpose**: List and read token information - **Required for**: \`list_tokens_tool\` - **Security**: Keep secret, reveals account tokens +- **Note**: When listing tokens, the actual token value is only returned for public tokens; secret token values are omitted for security (only metadata like id, scopes, and creation date is shown) ### \`tokens:write\` - **Purpose**: Create, update, and delete tokens diff --git a/src/tools/list-tokens-tool/ListTokensTool.ts b/src/tools/list-tokens-tool/ListTokensTool.ts index 7a84d5d..cf4e556 100644 --- a/src/tools/list-tokens-tool/ListTokensTool.ts +++ b/src/tools/list-tokens-tool/ListTokensTool.ts @@ -20,7 +20,7 @@ export class ListTokensTool extends MapboxApiBasedTool< > { readonly name = 'list_tokens_tool'; readonly description = - 'List Mapbox access tokens for the authenticated user with optional filtering and pagination. When using pagination, the "start" parameter must be obtained from the "next_start" field of the previous response (it is not a token ID)'; + 'List Mapbox access tokens for the authenticated user with optional filtering and pagination. Returns metadata for all tokens (public and secret), but the actual token value is only included for public tokens (secret token values are omitted for security). When using pagination, the "start" parameter must be obtained from the "next_start" field of the previous response (it is not a token ID)'; readonly annotations = { readOnlyHint: true, destructiveHint: false, diff --git a/test/tools/__snapshots__/tool-naming-convention.test.ts.snap b/test/tools/__snapshots__/tool-naming-convention.test.ts.snap index 379ca93..d594271 100644 --- a/test/tools/__snapshots__/tool-naming-convention.test.ts.snap +++ b/test/tools/__snapshots__/tool-naming-convention.test.ts.snap @@ -54,7 +54,7 @@ exports[`Tool Naming Convention > should maintain consistent tool list (snapshot }, { "className": "ListTokensTool", - "description": "List Mapbox access tokens for the authenticated user with optional filtering and pagination. When using pagination, the "start" parameter must be obtained from the "next_start" field of the previous response (it is not a token ID)", + "description": "List Mapbox access tokens for the authenticated user with optional filtering and pagination. Returns metadata for all tokens (public and secret), but the actual token value is only included for public tokens (secret token values are omitted for security). When using pagination, the "start" parameter must be obtained from the "next_start" field of the previous response (it is not a token ID)", "toolName": "list_tokens_tool", }, { diff --git a/test/tools/list-tokens-tool/ListTokensTool.test.ts b/test/tools/list-tokens-tool/ListTokensTool.test.ts index d4fc7a8..7f529a6 100644 --- a/test/tools/list-tokens-tool/ListTokensTool.test.ts +++ b/test/tools/list-tokens-tool/ListTokensTool.test.ts @@ -38,7 +38,7 @@ describe('ListTokensTool', () => { expect(tool.name).toBe('list_tokens_tool'); expect(tool.description).toBe( - 'List Mapbox access tokens for the authenticated user with optional filtering and pagination. When using pagination, the "start" parameter must be obtained from the "next_start" field of the previous response (it is not a token ID)' + 'List Mapbox access tokens for the authenticated user with optional filtering and pagination. Returns metadata for all tokens (public and secret), but the actual token value is only included for public tokens (secret token values are omitted for security). When using pagination, the "start" parameter must be obtained from the "next_start" field of the previous response (it is not a token ID)' ); });