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.

referencesproduction.md

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

Production Patterns

Performance Optimization

Caching Strategy

class CachedMapboxMCP {
  private cache = new Map<string, { result: any; timestamp: number }>();
  private cacheTTL = 3600000; // 1 hour

  async callTool(name: string, params: any): Promise<any> {
    // Cache offline tools indefinitely (deterministic)
    const offlineTools = ['distance_tool', 'point_in_polygon_tool', 'bearing_tool'];
    const ttl = offlineTools.includes(name) ? Infinity : this.cacheTTL;

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

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

    // Call MCP
    const result = await this.mcpServer.callTool(name, params);

    // Store in cache
    this.cache.set(cacheKey, {
      result,
      timestamp: Date.now()
    });

    return result;
  }
}

Batch Operations

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

// ✅ Good: Parallel batch
const distances = await Promise.all(
  locations.map((location) =>
    mcp.callTool('distance_tool', {
      from: userLocation,
      to: location
    })
  )
);

// ✅ Better: Use matrix tool
const matrix = await mcp.callTool('matrix_tool', {
  origins: [userLocation],
  destinations: locations
});

Writing Effective Tool Descriptions

Clear, specific tool descriptions are critical for helping LLMs select the right tools. Poor descriptions lead to incorrect tool calls, wasted API requests, and user frustration.

Common Confusion Points

Problem: "How far is it from A to B?" - Could trigger either directions_tool OR distance_tool

// ❌ Ambiguous descriptions
{
  name: 'directions_tool',
  description: 'Get directions between two locations'  // Could mean distance
}
{
  name: 'distance_tool',
  description: 'Calculate distance between two points'  // Unclear what kind
}

// ✅ Clear, specific descriptions
{
  name: 'directions_tool',
  description: 'Get turn-by-turn driving directions with traffic-aware route distance and travel time. Use when you need the actual route, navigation instructions, or driving duration. Returns route geometry, distance along roads, and time estimate.'
}
{
  name: 'distance_tool',
  description: 'Calculate straight-line (great-circle) distance between two points. Use for quick "as the crow flies" distance checks, proximity comparisons, or when routing is not needed. Works offline, instant, no API cost.'
}

Problem: "Find coffee shops nearby" - Could trigger category_search_tool OR search_and_geocode_tool

// ❌ Ambiguous
{
  name: 'search_poi',
  description: 'Search for places'
}

// ✅ Clear when to use each
{
  name: 'category_search_tool',
  description: 'Find ALL places of a specific type/category (e.g., "all coffee shops", "restaurants", "gas stations") near a location. Use for browsing or discovering places by category. Returns multiple results.'
}
{
  name: 'search_and_geocode_tool',
  description: 'Search for a SPECIFIC named place or address (e.g., "Starbucks on Main St", "123 Market St"). Use when the user provides a business name, street address, or landmark. Returns best match.'
}

Problem: "Where can I go in 15 minutes?" - Could trigger isochrone_tool OR directions_tool

// ❌ Confusing
{
  name: 'isochrone_tool',
  description: 'Calculate travel time area'
}

// ✅ Clear distinction
{
  name: 'isochrone_tool',
  description: 'Calculate the AREA reachable within a time limit from a starting point. Returns a GeoJSON polygon showing everywhere you can reach. Use for: "What can I reach in X minutes?", service area analysis, catchment zones, delivery zones.'
}
{
  name: 'directions_tool',
  description: 'Get route from point A to specific point B. Returns turn-by-turn directions to ONE destination. Use for: "How do I get to X?", "Route from A to B", navigation to a known destination.'
}

Best Practices for Tool Descriptions

  1. Start with the primary use case in simple terms
  2. Explain WHEN to use this tool vs alternatives
  3. Include key distinguishing details: Does it use traffic? Is it offline? Does it cost API calls?
  4. Give concrete examples of questions that should trigger this tool
  5. Mention what it returns so LLMs know if it fits the user's need
// ✅ Complete example
const searchPOITool = new DynamicStructuredTool({
  name: 'category_search_tool',
  description: `Find places by category type (restaurants, hotels, coffee shops, gas stations, etc.) near a location.

  Use when the user wants to:
  - Browse places of a certain type: "coffee shops nearby", "find restaurants"
  - Discover options: "what hotels are in this area?"
  - Search by industry/amenity, not by specific name

  Returns: List of matching places with names, addresses, and coordinates.

  DO NOT use for:
  - Specific named places (use search_and_geocode_tool instead)
  - Addresses (use search_and_geocode_tool or reverse_geocode_tool)`
  // ... schema and implementation
});

System Prompt Guidance

Add tool selection guidance to your agent's system prompt:

const systemPrompt = `You are a location intelligence assistant.

TOOL SELECTION RULES:
- Use distance_tool for straight-line distance ("as the crow flies")
- Use directions_tool for route distance along roads with traffic
- Use category_search_tool for finding types of places ("coffee shops")
- Use search_and_geocode_tool for specific addresses or named places ("123 Main St", "Starbucks downtown")
- Use isochrone_tool for "what can I reach in X minutes" questions
- Use offline tools (distance_tool, point_in_polygon_tool) when real-time data is not needed

When in doubt, prefer:
1. Offline tools over API calls (faster, free)
2. Specific tools over general ones
3. Asking for clarification over guessing`;

Tool Selection

// Use offline tools when possible (faster, free)
const localOps = {
  distance: 'distance_tool', // Turf.js
  pointInPolygon: 'point_in_polygon_tool', // Turf.js
  bearing: 'bearing_tool', // Turf.js
  area: 'area_tool' // Turf.js
};

// Use API tools when necessary (requires token, slower)
const apiOps = {
  directions: 'directions_tool', // Mapbox API
  geocoding: 'reverse_geocode_tool', // Mapbox API
  isochrone: 'isochrone_tool', // Mapbox API
  search: 'category_search_tool' // Mapbox API
};

// Choose based on requirements
function chooseTool(operation: string, needsRealtime: boolean) {
  if (needsRealtime) {
    return apiOps[operation]; // Traffic, live data
  }
  return localOps[operation] || apiOps[operation];
}

Error Handling

class RobustMapboxMCP {
  async callToolWithRetry(name: string, params: any, maxRetries: number = 3): Promise<any> {
    for (let i = 0; i < maxRetries; i++) {
      try {
        return await this.mcpServer.callTool(name, params);
      } catch (error) {
        if (error.code === 'RATE_LIMIT') {
          // Exponential backoff
          await this.sleep(Math.pow(2, i) * 1000);
          continue;
        }

        if (error.code === 'INVALID_TOKEN') {
          // Non-retryable error
          throw error;
        }

        if (i === maxRetries - 1) {
          throw error;
        }
      }
    }
  }

  async callToolWithFallback(primaryTool: string, fallbackTool: string, params: any): Promise<any> {
    try {
      return await this.callTool(primaryTool, params);
    } catch (error) {
      console.warn(`Primary tool ${primaryTool} failed, using fallback`);
      return await this.callTool(fallbackTool, params);
    }
  }
}

Security Best Practices

Token Management

// ✅ Good: Use environment variables
const mcp = new MapboxMCP({
  token: process.env.MAPBOX_ACCESS_TOKEN
});

// ❌ Bad: Hardcode tokens
const mcp = new MapboxMCP({
  token: 'pk.ey...' // Never do this!
});

// ✅ Good: Use scoped tokens
// Create token with minimal scopes:
// - directions:read
// - geocoding:read
// - No write permissions

Rate Limiting

class RateLimitedMCP {
  private requestQueue: Array<() => Promise<any>> = [];
  private requestsPerMinute = 300;
  private currentMinute = Math.floor(Date.now() / 60000);
  private requestCount = 0;

  async callTool(name: string, params: any): Promise<any> {
    // Check rate limit
    const minute = Math.floor(Date.now() / 60000);
    if (minute !== this.currentMinute) {
      this.currentMinute = minute;
      this.requestCount = 0;
    }

    if (this.requestCount >= this.requestsPerMinute) {
      // Wait until next minute
      const waitMs = (this.currentMinute + 1) * 60000 - Date.now();
      await this.sleep(waitMs);
    }

    this.requestCount++;
    return await this.mcpServer.callTool(name, params);
  }
}

Testing

// Mock MCP server for testing
class MockMapboxMCP {
  async callTool(name: string, params: any): Promise<any> {
    const mocks = {
      distance_tool: () => '2.5',
      directions_tool: () => JSON.stringify({
        duration: 1200,
        distance: 5000,
        geometry: {...}
      }),
      point_in_polygon_tool: () => 'true'
    };

    return mocks[name]?.() || '{}';
  }
}

// Use in tests
describe('Property search', () => {
  it('finds properties within commute time', async () => {
    const agent = new CustomMapboxAgent(new MockMapboxMCP());
    const results = await agent.findPropertiesWithCommute(
      [-122.4, 37.7],
      [-122.41, 37.78],
      30
    );

    expect(results).toHaveLength(5);
  });
});

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.