Skip to content
Merged
16 changes: 16 additions & 0 deletions cspell.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"version": "0.2",
"language": "en",
"words": [
"bbox",
"denoise",
"isochrone",
"mapbox",
"mmss",
"tilequery"
],
"ignorePaths": [
"node_modules",
"dist"
]
}
1,037 changes: 1,034 additions & 3 deletions package-lock.json

Large diffs are not rendered by default.

15 changes: 9 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@
"mcp-server": "dist/esm/index.js"
},
"scripts": {
"lint": "eslint \"./src/**/*.{ts,tsx}\" \"./test/**/*.{ts,tsx}\"",
"lint:fix": "eslint \"./src/**/*.{ts,tsx}\" \"./test/**/*.{ts,tsx}\" --fix",
"build": "npm run prepare && tshy && npm run generate-version && node scripts/add-shebang.cjs",
"format": "prettier --check \"./src/**/*.{ts,tsx,js,json,md}\" \"./test/**/*.{ts,tsx,js,json,md}\"",
"format:fix": "prettier --write \"./src/**/*.{ts,tsx,js,json,md}\" \"./test/**/*.{ts,tsx,js,json,md}\"",
"prepare": "husky && node .husky/setup-hooks.js",
"test": "vitest",
"build": "npm run prepare && tshy && npm run generate-version && node scripts/add-shebang.cjs",
"generate-version": "node scripts/build-helpers.cjs generate-version",
"inspect:build": "npm run build && npx @modelcontextprotocol/inspector -e MAPBOX_ACCESS_TOKEN=\"$MAPBOX_ACCESS_TOKEN\" node dist/esm/index.js",
"inspect:dev": "npx @modelcontextprotocol/inspector -e MAPBOX_ACCESS_TOKEN=\"$MAPBOX_ACCESS_TOKEN\" npx -y tsx src/index.ts",
"lint": "eslint \"./src/**/*.{ts,tsx}\" \"./test/**/*.{ts,tsx}\"",
"lint:fix": "eslint \"./src/**/*.{ts,tsx}\" \"./test/**/*.{ts,tsx}\" --fix",
"prepare": "husky && node .husky/setup-hooks.js",
"spellcheck": "cspell \"*.md\" \"src/**/*.ts\" \"test/**/*.ts\"",
"sync-manifest": "node scripts/sync-manifest-version.cjs",
"dev:inspect": "npx @modelcontextprotocol/inspector -e MAPBOX_ACCESS_TOKEN=\"$MAPBOX_ACCESS_TOKEN\" npx -y tsx src/index.ts"
"test": "vitest"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": "eslint --fix",
Expand All @@ -33,6 +35,7 @@
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"@vitest/coverage-istanbul": "^3.2.4",
"cspell": "^9.2.1",
"eslint": "^9.0.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-n": "^17.21.3",
Expand Down
163 changes: 163 additions & 0 deletions src/schemas/geojson.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
// Copyright (c) Mapbox, Inc.
// Licensed under the MIT License.

/**
* GeoJSON interfaces based on RFC 7946
* https://tools.ietf.org/html/rfc7946
*/

export type GeoJSONGeometryType =
| 'Point'
| 'LineString'
| 'Polygon'
| 'MultiPoint'
| 'MultiLineString'
| 'MultiPolygon'
| 'GeometryCollection';

export type GeoJSONFeatureType = 'Feature';

export type GeoJSONFeatureCollectionType = 'FeatureCollection';

export type GeoJSONType =
| GeoJSONGeometryType
| GeoJSONFeatureType
| GeoJSONFeatureCollectionType;

/**
* Position array [longitude, latitude] or [longitude, latitude, elevation]
*/
export type Position = [number, number] | [number, number, number];

/**
* Base interface for all GeoJSON objects
*/
export interface GeoJSONBase {
type: GeoJSONType;
bbox?:
| [number, number, number, number]
| [number, number, number, number, number, number];
}

/**
* Point geometry
*/
export interface Point extends GeoJSONBase {
type: 'Point';
coordinates: Position;
}

/**
* LineString geometry
*/
export interface LineString extends GeoJSONBase {
type: 'LineString';
coordinates: Position[];
}

/**
* Polygon geometry
*/
export interface Polygon extends GeoJSONBase {
type: 'Polygon';
coordinates: Position[][];
}

/**
* MultiPoint geometry
*/
export interface MultiPoint extends GeoJSONBase {
type: 'MultiPoint';
coordinates: Position[];
}

/**
* MultiLineString geometry
*/
export interface MultiLineString extends GeoJSONBase {
type: 'MultiLineString';
coordinates: Position[][];
}

/**
* MultiPolygon geometry
*/
export interface MultiPolygon extends GeoJSONBase {
type: 'MultiPolygon';
coordinates: Position[][][];
}

/**
* GeometryCollection
*/
export interface GeometryCollection extends GeoJSONBase {
type: 'GeometryCollection';
geometries: Geometry[];
}

/**
* Union of all geometry types
*/
export type Geometry =
| Point
| LineString
| Polygon
| MultiPoint
| MultiLineString
| MultiPolygon
| GeometryCollection;

/**
* GeoJSON Feature with properties
*/
export interface Feature<
P = Record<string, unknown>,
G extends Geometry = Geometry
> extends GeoJSONBase {
type: 'Feature';
geometry: G | null;
properties: P | null;
id?: string | number;
}

/**
* GeoJSON FeatureCollection
*/
export interface FeatureCollection<
P = Record<string, unknown>,
G extends Geometry = Geometry
> extends GeoJSONBase {
type: 'FeatureCollection';
features: Feature<P, G>[];
}

/**
* Union of all GeoJSON objects
*/
export type GeoJSON = Geometry | Feature | FeatureCollection;

/**
* Mapbox-specific properties commonly found in Mapbox API responses
*/
export interface MapboxFeatureProperties extends Record<string, unknown> {
name?: string;
name_preferred?: string;
full_address?: string;
place_formatted?: string;
feature_type?: string;
poi_category?: string | string[];
category?: string;
mapbox_id?: string;
address?: string;
}

/**
* Mapbox Feature with common properties
*/
export type MapboxFeature = Feature<MapboxFeatureProperties>;

/**
* Mapbox FeatureCollection with common properties
*/
export type MapboxFeatureCollection =
FeatureCollection<MapboxFeatureProperties>;
54 changes: 42 additions & 12 deletions src/tools/BaseTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,42 +9,72 @@ import type {
ToolAnnotations,
CallToolResult
} from '@modelcontextprotocol/sdk/types.js';
import type { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol.js';
import type { ZodTypeAny } from 'zod';
import type { z } from 'zod';

export abstract class BaseTool<InputSchema extends ZodTypeAny> {
export abstract class BaseTool<
InputSchema extends ZodTypeAny,
OutputSchema extends ZodTypeAny = ZodTypeAny
> {
abstract readonly name: string;
abstract readonly description: string;
abstract readonly annotations: ToolAnnotations;

readonly inputSchema: InputSchema;
readonly outputSchema?: OutputSchema;
protected server: McpServer | null = null;

constructor(params: { inputSchema: InputSchema }) {
constructor(params: {
inputSchema: InputSchema;
outputSchema?: OutputSchema;
}) {
this.inputSchema = params.inputSchema;
this.outputSchema = params.outputSchema;
}

/**
* Installs the tool to the given MCP server.
*/
installTo(server: McpServer): RegisteredTool {
this.server = server;
return server.registerTool(
this.name,
{
title: this.annotations.title,
description: this.description,
inputSchema: (this.inputSchema as unknown as z.ZodObject<any>).shape,
annotations: this.annotations
},
(args, extra) => this.run(args, extra)

const config: {
title?: string;
description?: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
inputSchema?: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
outputSchema?: any;
annotations?: ToolAnnotations;
} = {
title: this.annotations.title,
description: this.description,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
inputSchema: (this.inputSchema as unknown as z.ZodObject<any>).shape,
annotations: this.annotations
};

// Add outputSchema if provided
if (this.outputSchema) {
config.outputSchema =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.outputSchema as unknown as z.ZodObject<any>).shape;
}

return server.registerTool(this.name, config, (args, extra) =>
this.run(args, extra)
);
}

/**
* Tool logic to be implemented by subclasses.
*/
abstract run(rawInput: unknown, extra?: any): Promise<CallToolResult>;
abstract run(
rawInput: unknown,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
extra?: RequestHandlerExtra<any, any>
): Promise<CallToolResult>;

/**
* Helper method to send logging messages
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,12 @@ export const OutputSchema = z.object({
})
])
),
/**
* An object containing structured tool output.
*
* If the Tool defines an outputSchema, this field MUST be present in the result,
* and contain a JSON object that matches the schema.
*/
structuredContent: z.object({}).passthrough().optional(),
isError: z.boolean().default(false)
});
Loading