Skip to content

Conversation

@mattpodwysocki
Copy link
Contributor

Description

This branch introduces structured output schemas for MCP tools to improve type safety and enable better validation of tool responses. The changes affect 82 files with 2,506 additions and 1,326 deletions.

Key Changes

1. Schema Architecture

New Files Created

  • src/schemas/style.ts (242 new lines)
    • Comprehensive Mapbox Style Spec schemas
    • Defines source types (Vector, Raster, GeoJSON, etc.)
    • Layer schema with all layer types
    • Style import schema
    • BaseStylePropertiesSchema with .passthrough() to preserve API metadata

Tool Schema Separation

All tools now have separate input and output schemas:

  • Renamed *.schema.ts*.input.schema.ts
  • Created new *.output.schema.ts files for each tool

2. Core Tool Infrastructure

BaseTool (src/tools/BaseTool.ts)

  • Added outputSchema parameter to constructor
  • Updated installTo() to register output schemas with MCP
  • Tools now return structured content with validated responses

MapboxApiBasedTool (src/tools/MapboxApiBasedTool.ts)

  • Added JWT token validation (isValidJwtFormat())
  • Improved error handling with proper logging
  • Changed from fetchRequest to httpRequest parameter
  • Constructor now requires httpRequest: HttpRequest

3. HTTP Pipeline Refactor

Renamed & Restructured

  • src/utils/fetchRequest.tssrc/utils/httpPipeline.ts
  • test/utils/fetchRequest.test.tstest/utils/httpPipeline.test.ts
  • test/utils/fetchRequestUtils.tstest/utils/httpPipelineUtils.ts

New Utilities

  • src/utils/jwtUtils.ts (53 new lines)
    • getUserNameFromToken() - Extract username from JWT
    • mapboxAccessToken() - Get access token from env
    • mapboxApiEndpoint() - Get API endpoint with fallback
  • test/utils/jwtUtils.test.ts (79 new lines)
    • Comprehensive JWT utility tests

4. Tool Updates

All tools updated with output schemas and structured content:

BoundingBoxTool

  • Input: BoundingBoxTool.input.schema.ts
  • Output: BoundingBoxTool.output.schema.ts (18 lines)
  • Returns [minX, minY, maxX, maxY] array

CountryBoundingBoxTool

  • Input: CountryBoundingBoxTool.input.schema.ts
  • Output: CountryBoundingBoxTool.output.schema.ts (20 lines)
  • Returns bounding box with country code

CoordinateConversionTool

  • Input: CoordinateConversionTool.input.schema.ts
  • Output: CoordinateConversionTool.output.schema.ts (16 lines)
  • Returns converted coordinates array

CreateStyleTool

  • Input: CreateStyleTool.input.schema.ts (17 lines)
  • Output: CreateStyleTool.output.schema.ts (30 lines)
  • Uses BaseStylePropertiesSchema
  • Error Handling Fix: Returns CallToolResult instead of throwing

CreateTokenTool

  • Input: CreateTokenTool.input.schema.ts
  • Output: CreateTokenTool.output.schema.ts (20 lines)
  • Validates token response structure

DeleteStyleTool

  • Input: DeleteStyleTool.input.schema.ts (unchanged)
  • Updated error handling

GeojsonPreviewTool

  • Input: GeojsonPreviewTool.input.schema.ts
  • Returns geojson.io URL

GetMapboxDocSourceTool

  • Input: GetMapboxDocSourceTool.input.schema.ts
  • Enhanced documentation retrieval

ListStylesTool

  • Input: ListStylesTool.input.schema.ts
  • Output: ListStylesTool.output.schema.ts (10 lines)
  • Error Handling Fix: Check response.ok before schema validation
  • Returns array of styles using BaseStylePropertiesSchema

ListTokensTool

  • Input: ListTokensTool.input.schema.ts (unchanged)
  • Output: ListTokensTool.output.schema.ts (26 lines)
  • Validates token list response

PreviewStyleTool

  • Input: PreviewStyleTool.input.schema.ts (unchanged)
  • Returns preview URL

RetrieveStyleTool

  • Input: RetrieveStyleTool.input.schema.ts (unchanged)
  • Output: RetrieveStyleTool.output.schema.ts (30 lines)
  • Uses BaseStylePropertiesSchema

StyleBuilderTool

  • Input: StyleBuilderTool.input.schema.ts
  • No output schema (returns text)

StyleComparisonTool

  • Input: StyleComparisonTool.schema.ts (updated)
  • Returns comparison URL

TilequeryTool

  • Input: TilequeryTool.input.schema.ts
  • Output: TilequeryTool.output.schema.ts (77 lines)
  • Comprehensive feature collection schema

UpdateStyleTool

  • Input: UpdateStyleTool.input.schema.ts (25 lines)
  • Output: UpdateStyleTool.output.schema.ts (29 lines)
  • Uses BaseStylePropertiesSchema

5. Test Fixes & Improvements

Test Infrastructure

  • All tests updated to use setupHttpRequest() from httpPipelineUtils
  • Tests now provide httpRequest to tool constructors

StyleBuilderTool Tests (test/tools/style-builder-tool/StyleBuilderTool.test.ts)

Fixed Issues:

  • Changed invalid base_style: 'streets' as any'streets-v12' (5 occurrences)
  • Removed inappropriate render_type: 'symbol' overrides (8 occurrences)
  • Tests now use correct style values

ListStylesTool Tests (test/tools/list-styles-tool/ListStylesTool.test.ts)

Fixed Issues:

  • Added required sources: {} and layers: [] to mock data
  • Tests now pass schema validation

CreateStyleTool Tests (test/tools/create-style-tool/CreateStyleTool.test.ts)

Fixed Issues:

  • Fixed input format: flattened style properties instead of nested style object (2 occurrences)
  • Updated mock response to include required schema fields

MapboxApiBasedTool Tests (test/tools/MapboxApiBasedTool.test.ts)

Fixed Issues:

  • Fixed execute mock to return proper CallToolResult structure
  • Reduced from 116 lines to better focused tests

Tool Naming Convention Tests (test/tools/tool-naming-convention.test.ts)

Fixed Issues:

  • Added httpRequest from setupHttpRequest() for tool instantiation
  • Eliminated import warnings for tools requiring constructor parameters
  • Updated snapshot to include all tools (now successfully instantiating 7 additional tools)

JWT Utils Tests (test/utils/jwtUtils.test.ts)

New File (79 lines)

  • Tests for getUserNameFromToken()
  • Tests for token validation
  • Tests for error handling

6. Configuration Updates

Package.json

  • Updated dependencies for output schema support

CSpell Configuration (cspell.config.json)

  • Added 16 new terms for schema-related vocabulary

7. Test Results

All 228 tests passing

Test Coverage:

  • 24 test files
  • 228 tests total
  • No warnings or errors
  • All tools successfully instantiating in discovery tests

Migration Guide

For Tool Developers

If you're creating a new tool or updating an existing one:

  1. Split schemas:

    // Before: MyTool.schema.ts
    // After:
    //   - MyTool.input.schema.ts
    //   - MyTool.output.schema.ts (new)
  2. Add output schema to constructor:

    constructor(params: { httpRequest: HttpRequest }) {
      super({
        inputSchema: MyToolSchema,
        outputSchema: MyToolOutputSchema, // Add this
        httpRequest: params.httpRequest
      });
    }
  3. Return structured content:

    return {
      content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
      structuredContent: data, // Add this
      isError: false
    };
  4. Validate responses:

    const parseResult = MyToolOutputSchema.safeParse(data);
    if (!parseResult.success) {
      return {
        content: [{
          type: 'text',
          text: `Response validation failed: ${parseResult.error}`
        }],
        isError: true
      };
    }
  5. Check HTTP response before validation:

    if (!response.ok) {
      return {
        content: [{
          type: 'text',
          text: `Failed: ${response.status} ${response.statusText}`
        }],
        isError: true
      };
    }

For Test Writers

  1. Use httpPipelineUtils:

    import { setupHttpRequest } from '../utils/httpPipelineUtils.js';
    
    const { httpRequest } = setupHttpRequest({
      ok: true,
      json: async () => ({ /* mock data */ })
    });
  2. Provide httpRequest to tools:

    const tool = new MyTool({ httpRequest });
  3. Include required schema fields in mocks:

    const mockStyles = [{
      version: 8,
      sources: {},
      layers: [],
      // ... other fields
    }];

Benefits

  1. Type Safety: Output schemas provide compile-time and runtime validation
  2. Better Error Messages: Schema validation errors are clear and actionable
  3. Structured Content: Tools return both text and structured data for clients
  4. Consistency: All tools follow the same pattern for responses
  5. Documentation: Schemas serve as documentation for tool outputs
  6. Testing: Easier to test with validated schemas

Breaking Changes

Constructor Changes

All MapboxApiBasedTool subclasses now require httpRequest parameter:

// Before
const tool = new ListStylesTool();

// After
const { httpRequest } = setupHttpRequest();
const tool = new ListStylesTool({ httpRequest });

Error Handling

Tools now return CallToolResult with isError: true instead of throwing:

// Before
throw new Error('Failed to create style');

// After
return {
  content: [{ type: 'text', text: 'Failed to create style' }],
  isError: true
};

Testing

Testing list styles:
Screenshot 2025-10-20 at 14 45 02

Testing retrieve style:
Screenshot 2025-10-20 at 14 47 13

Testing country bounding box:
Screenshot 2025-10-20 at 14 49 05

Testing country bounding box on Claude Desktop:
Screenshot 2025-10-20 at 14 50 33


Checklist

  • Code has been tested locally
  • Unit tests have been added or updated
  • Documentation has been updated if needed

Additional Notes

@mattpodwysocki mattpodwysocki requested a review from a team as a code owner October 22, 2025 21:42
Copy link
Member

@zmofei zmofei left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, thanks for the update.

@mattpodwysocki mattpodwysocki merged commit eea8f82 into main Oct 24, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants