All skills
mapbox avatar

/mapbox-mcp-runtime-patterns

@7cac917 official
by mapboxmapbox/mapbox-agent-skills80 stars
17

Integration patterns for Mapbox MCP Server in AI applications and agent frameworks. Covers runtime integration with pydantic-ai, mastra, LangChain, and custom agents. Use when building AI-powered applications that need geospatial capabilities.

Use this Skill: https://skilld.dev/gh/mapbox/mapbox-agent-skills/mapbox-mcp-runtime-patterns

This session only. Nothing lands on disk.

AGENTS.md

≈3.2k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Mapbox MCP Runtime Patterns

Quick reference for integrating Mapbox MCP Server into AI applications for production use.

What is MCP Server?

Runtime server providing geospatial tools to AI agents via Model Context Protocol.

Repo: https://github.com/mapbox/mcp-server

Tools Available

Category Tools Cost
Offline (Turf.js) distance_tool, bearing_tool, midpoint_tool, point_in_polygon_tool, area_tool, buffer_tool, centroid_tool, bbox_tool, simplify_tool Free, instant
Mapbox APIs directions_tool, search_and_geocode_tool, reverse_geocode_tool, category_search_tool, isochrone_tool, matrix_tool, static_map_image_tool, map_matching_tool, optimization_tool API costs apply
Utility version_tool, category_list_tool Free

Coordinate Formats

All tools use {longitude, latitude} object format — not arrays.

Object format {longitude: lng, latitude: lat}:

  • directions_tool - coordinates array of objects
  • isochrone_tool - coordinates parameter
  • reverse_geocode_tool - coordinates parameter
  • category_search_tool - proximity parameter
  • distance_tool - from/to parameters
  • bearing_tool - from/to parameters
  • midpoint_tool - from/to parameters
  • point_in_polygon_tool - point parameter

Exception — GeoJSON geometry (arrays only):

  • buffer_tool - geometry parameter uses [longitude, latitude] arrays (GeoJSON format)
  • point_in_polygon_tool - polygon rings use [longitude, latitude] arrays

Note: All coordinates use longitude before latitude order.

Installation

Hosted (Recommended)

Use Mapbox's hosted server - no installation needed:

https://mcp.mapbox.com/mcp

Connect with your token in the Authorization: Bearer <token> header.

Note: Hosted server supports OAuth for interactive flows (coding assistants), but use token auth for programmatic runtime access.

Self-Hosted

npm install @mapbox/mcp-server
# Or: npx @mapbox/mcp-server

export MAPBOX_ACCESS_TOKEN="your_token"

Framework Integration

Pydantic AI

from pydantic_ai import Agent
import subprocess

# Start MCP server
mcp = subprocess.Popen(['npx', '@mapbox/mcp-server'],
                       env={'MAPBOX_ACCESS_TOKEN': token})

agent = Agent(
    model='gateway/openai:gpt-5.2',
    tools=[
        lambda from_loc, to_loc: call_mcp('directions_tool', {
            'origin': from_loc,
            'destination': to_loc
        })
    ]
)

Mastra

import { spawn } from 'child_process';

const mcp = spawn('npx', ['@mapbox/mcp-server'], {
  env: { MAPBOX_ACCESS_TOKEN: process.env.MAPBOX_ACCESS_TOKEN }
});

const mastra = new Mastra({
  workflows: {
    findRestaurants: {
      steps: [
        { tool: 'mapbox.category_search_tool', input: {...} },
        { tool: 'mapbox.matrix_tool', input: {...} }
      ]
    }
  }
});

LangChain

import { DynamicTool } from '@langchain/core/tools';

const tools = [
  new DynamicTool({
    name: 'directions_tool',
    description: 'Get driving directions',
    func: async (input) => {
      const { origin, destination } = JSON.parse(input);
      return await callMCP('directions_tool', { origin, destination });
    }
  })
];

Custom Agent

class MapboxAgent {
  private mcpProcess: ChildProcess;

  async initialize() {
    this.mcpProcess = spawn('npx', ['@mapbox/mcp-server'], {
      env: { MAPBOX_ACCESS_TOKEN: process.env.MAPBOX_ACCESS_TOKEN }
    });
  }

  async callTool(name: string, params: any): Promise<any> {
    const request = {
      jsonrpc: '2.0',
      id: Date.now(),
      method: 'tools/call',
      params: { name, arguments: params }
    };

    this.mcpProcess.stdin.write(JSON.stringify(request) + '\n');

    return new Promise((resolve) => {
      this.mcpProcess.stdout.once('data', (data) => {
        const response = JSON.parse(data.toString());
        resolve(response.result);
      });
    });
  }
}

Common Use Cases

Real Estate (Zillow-style)

// Find properties with good commute
async findByCommute(home: Point, work: Point, maxMinutes: number) {
  // 1. Get reachable area from work
  const isochrone = await mcp.call('isochrone_tool', {
    coordinates: work,
    contours_minutes: [maxMinutes]
  });

  // 2. Check if home is within range
  const inRange = await mcp.call('point_in_polygon_tool', {
    point: home,
    polygon: isochrone
  });

  // 3. Get exact commute time
  if (inRange) {
    const route = await mcp.call('directions_tool', {
      coordinates: [
        { longitude: home[0], latitude: home[1] },
        { longitude: work[0], latitude: work[1] }
      ],
      routing_profile: 'mapbox/driving-traffic'
    });
    return { commuteMinutes: route.duration / 60 };
  }
}

Food Delivery (DoorDash-style)

// Check delivery availability
async canDeliver(restaurant: Point, address: Point, maxTime: number) {
  // 1. Calculate delivery zone
  const zone = await mcp.call('isochrone_tool', {
    coordinates: restaurant,
    contours_minutes: [maxTime],
    profile: 'mapbox/driving'
  });

  // 2. Check if address is in zone
  const canDeliver = await mcp.call('point_in_polygon_tool', {
    point: address,
    polygon: zone
  });

  // 3. Get delivery time with traffic
  if (canDeliver) {
    const route = await mcp.call('directions_tool', {
      coordinates: [
        { longitude: restaurant[0], latitude: restaurant[1] },
        { longitude: address[0], latitude: address[1] }
      ],
      routing_profile: 'mapbox/driving-traffic'
    });
    return { eta: route.duration / 60, distance: route.distance };
  }
}

Travel Planning (TripAdvisor-style)

// Find nearby attractions with travel times
async findAttractions(hotel: Point, category: string) {
  // 1. Search nearby
  const places = await mcp.call('category_search_tool', {
    category,
    proximity: hotel
  });

  // 2. Calculate distances (offline, free)
  const withDistances = await Promise.all(
    places.map(async (place) => ({
      ...place,
      distance: await mcp.call('distance_tool', {
        from: hotel,
        to: place.coordinates,
        units: 'miles'
      })
    }))
  );

  // 3. Get travel times (batch API call)
  const matrix = await mcp.call('matrix_tool', {
    origins: [hotel],
    destinations: places.map(p => p.coordinates),
    profile: 'mapbox/walking'
  });

  return withDistances.map((place, i) => ({
    ...place,
    walkingMinutes: matrix.durations[0][i] / 60
  }));
}

Architecture Pattern

Application Layer
      ↓
AI Agent Layer (pydantic-ai, mastra, custom)
      ↓
MCP Server (geospatial tools)
      ↓
   ↙     ↘
Turf.js   Mapbox APIs
(free)    (API costs)

Tool Selection Strategy

Need Use Reason
Distance calculation distance_tool (offline) Free, instant
Point in polygon point_in_polygon_tool (offline) Free, instant
Bounding box bbox_tool (offline) Free, instant
Simplify geometry simplify_tool (offline) Free, instant
Directions with traffic directions_tool (API) Real-time data
Geocoding reverse_geocode_tool (API) Requires database
Isochrones isochrone_tool (API) Complex calculation
Multi-stop optimization optimization_tool (API) Complex calculation
GPS trace matching map_matching_tool (API) Requires routing data
Bearing/midpoint bearing_tool/midpoint_tool (offline) Free, instant
POI categories category_list_tool (utility) Metadata lookup

Performance Optimization

Caching

class CachedMCP {
  private cache = new Map();
  private offlineTools = ['distance_tool', 'point_in_polygon_tool'];

  async callTool(name: string, params: any) {
    // Cache offline tools forever (deterministic)
    const ttl = this.offlineTools.includes(name) ? Infinity : 3600000;

    const key = JSON.stringify({ name, params });
    const cached = this.cache.get(key);

    if (cached && Date.now() - cached.timestamp < ttl) {
      return cached.result;
    }

    const result = await this.mcp.callTool(name, params);
    this.cache.set(key, { result, timestamp: Date.now() });
    return result;
  }
}

Batching

// ❌ Bad: Sequential calls
for (const location of locations) {
  await mcp.call('distance_tool', { from: user, to: location });
}

// ✅ Good: Parallel
await Promise.all(locations.map((loc) => mcp.call('distance_tool', { from: user, to: loc })));

// ✅ Better: Use matrix tool
await mcp.call('matrix_tool', {
  origins: [user],
  destinations: locations
});

Error Handling

class RobustMCP {
  async callWithRetry(name: string, params: any, retries = 3) {
    for (let i = 0; i < retries; i++) {
      try {
        return await this.mcp.callTool(name, params);
      } catch (error) {
        if (error.code === 'RATE_LIMIT') {
          await this.sleep(Math.pow(2, i) * 1000); // Exponential backoff
          continue;
        }
        throw error; // Non-retryable
      }
    }
  }
}

Security

// ✅ Good: Environment variables
const token = process.env.MAPBOX_ACCESS_TOKEN;

// ✅ Good: Scoped tokens (minimal permissions)
// directions:read, geocoding:read only

// ✅ Good: Rate limiting
class RateLimitedMCP {
  private requestsPerMinute = 300;
  private requestCount = 0;

  async callTool(name: string, params: any) {
    if (this.requestCount >= this.requestsPerMinute) {
      await this.waitForNextMinute();
    }
    this.requestCount++;
    return await this.mcp.callTool(name, params);
  }
}

Testing

// Mock MCP for tests
class MockMCP {
  async callTool(name: string, params: any) {
    const mocks = {
      distance_tool: () => '2.5',
      directions_tool: () => ({ duration: 1200, distance: 5000 }),
      point_in_polygon_tool: () => true
    };
    return mocks[name]?.();
  }
}

// Use in tests
const agent = new MapboxAgent(new MockMCP());

When to Use

Use MCP ✅ Use Direct API ❌
AI agent interactions Simple operations
Complex workflows Performance-critical
Offline calculations Client-side rendering
Multi-step geospatial logic Map display
Prototyping Production maps

Cost Optimization

// Prefer offline tools (free)
const freeOps = [
  'distance_tool',
  'point_in_polygon_tool',
  'bearing_tool',
  'area_tool',
  'centroid_tool',
  'bbox_tool',
  'simplify_tool',
  'midpoint_tool',
  'buffer_tool'
];

// Use API tools only when necessary
const apiOps = [
  'directions_tool', // Need traffic data
  'reverse_geocode_tool', // Need address database
  'isochrone_tool', // Complex calculation
  'category_search_tool', // Need POI database
  'matrix_tool', // Travel time matrix
  'static_map_image_tool', // Static map generation
  'map_matching_tool', // GPS trace matching
  'optimization_tool' // Route optimization
];

// Utility tools
const utilityOps = [
  'version_tool', // Server version info
  'category_list_tool' // Available POI categories
];

function chooseTool(operation: string, needsRealtime: boolean) {
  if (needsRealtime) return apiOps[operation];
  return freeOps.includes(operation) ? operation : apiOps[operation];
}

Resources

Source: SKILL.md on GitHub

1 warning17d5 checks · Risk SAFE
  • Gen Agent Trust Hub17d

    The skill provides legitimate integration patterns and code examples for using Mapbox geospatial tools with various AI agent frameworks. It uses standard developer practices for package management, environment configuration, and API interaction with official vendor services.

  • Socket17d

    No alerts

  • Snyk17d

    Risk: LOW · No issues

  • Runlayer6mo

    2/12 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 7cac917. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 9 hours ago.

Activeupdated 6 months ago
  • MCP
  • TypeScript
  • mapbox
  • geospatial
  • routing
  • geocoding
  • pydantic-ai
  • langchain
  • mastra
  • agents

README badge

README badge for mapbox/mapbox-agent-skills/mapbox-mcp-runtime-patterns

Demonstrates runtime integration patterns for the Mapbox MCP Server across pydantic-ai, mastra, LangChain, and custom agents, covering offline tools (Turf.js), API-backed geospatial features (routing, geocoding, isochrones), and production considerations. Use this when building AI applications that need to query maps, calculate distances, search POIs, or optimize routes without manual API integration.

Generated from the current SKILL.md.

What's the difference between offline tools like distance_tool and API tools like directions_tool?
Offline tools (distance, bearing, point-in-polygon) use Turf.js, return instant results, and have no API cost. API tools (directions, geocoding, isochrones) call Mapbox APIs, return real-time data like traffic-aware routing, and count against your token usage.
Can I use the hosted Mapbox MCP server or do I need to self-host?
The hosted server at https://mcp.mapbox.com/mcp is recommended for production — no server management, always up-to-date, and lower latency. Self-hosting via npm is available for custom deployments or development.
Which frameworks does this skill cover?
Integration patterns for Pydantic AI, Mastra, LangChain, CrewAI, Smolagents, and custom agent architectures. Real-estate, food-delivery, and travel-planning use cases are included.
When should I use category_search_tool vs search_and_geocode_tool?
Use category_search_tool to browse by type (e.g. 'find coffee shops nearby'). Use search_and_geocode_tool for specific places or street addresses (e.g. '123 Main Street').
Does this skill include production guidance like caching, rate limiting, and error handling?
Yes. The skill references a production-patterns file covering caching, batch operations, tool descriptions, error handling, security, rate limiting, and testing.

Generated from the current SKILL.md. These answers refresh after source changes.