
Mapbox Mcp Runtime Patterns
- 950 installs
- 71 repo stars
- Updated August 4, 2026
- mapbox/mapbox-agent-skills
mapbox-mcp-runtime-patterns is a Mapbox agent skill that documents production Model Context Protocol integration patterns so developers give coding agents reliable geospatial tools.
About
mapbox-mcp-runtime-patterns is a Mapbox agent skill from mapbox/mapbox-agent-skills that serves as a quick reference for integrating the Mapbox MCP Server into AI applications for production use. The skill explains how the runtime MCP server exposes geospatial tools to agents through the Model Context Protocol and points to the mapbox/mcp-server repository for server setup. Developers reach for mapbox-mcp-runtime-patterns when agents need dependable geocoding, routing, map tile, or location-aware operations inside LLM-driven workflows rather than one-off REST calls. The readme organizes available tools by category with cost considerations, helping teams design agent tool access, error handling, and runtime deployment patterns. The skill targets integration engineers building location-aware agents, copilots, and automation that must call Mapbox capabilities through MCP instead of embedding maps-only frontend snippets.
- 9 offline Turf.js geometry tools including distance, bearing, area, buffer, and point-in-polygon
- 10 Mapbox API tools covering directions, search, isochrone, matrix, and optimization
- 3 free utility tools for version checking and category listing
- Clear separation of free instant operations versus billable Mapbox API calls
- Production runtime patterns for integrating the official Mapbox MCP server
Mapbox Mcp Runtime Patterns by the numbers
- 950 all-time installs (skills.sh)
- +32 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #1,148 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/mapbox/mapbox-agent-skills --skill mapbox-mcp-runtime-patternsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 950 |
|---|---|
| repo stars | ★ 71 |
| Security audit | 2 / 3 scanners passed |
| Last updated | August 4, 2026 |
| Repository | mapbox/mapbox-agent-skills ↗ |
How do you integrate Mapbox MCP into AI agents?
Give their AI coding agent reliable geospatial capabilities through the Model Context Protocol.
Who is it for?
Developers building location-aware AI agents that need Mapbox geospatial tools exposed via Model Context Protocol in production.
Skip if: Teams only embedding static Mapbox GL JS maps without agent MCP tooling or server-side geospatial automation.
When should I use this skill?
User asks about Mapbox MCP Server, geospatial agent tools, MCP runtime patterns, or location APIs for AI applications
What you get
MCP integration patterns, geospatial tool wiring, and production runtime configuration for Mapbox agent servers
- MCP integration patterns
- Geospatial agent tool configuration
Files
Mapbox MCP Runtime Patterns
This skill provides patterns for integrating the Mapbox MCP Server into AI applications for production use with geospatial capabilities.
What is Mapbox MCP Server?
The Mapbox MCP Server is a Model Context Protocol (MCP) server that provides AI agents with geospatial tools:
Offline Tools (Turf.js):
- Distance, bearing, midpoint calculations
- Point-in-polygon tests
- Area, buffer, centroid operations
- Bounding box, geometry simplification
- No API calls, instant results
Mapbox API Tools:
- Directions and routing
- Reverse geocoding
- POI category search
- Isochrones (reachability)
- Travel time matrices
- Static map images
- GPS trace map matching
- Multi-stop route optimization
Utility Tools:
- Server version info
- POI category list
Key benefit: Give your AI application geospatial superpowers without manually integrating multiple APIs.
Understanding Tool Categories
Before integrating, understand the key distinctions between tools to help your LLM choose correctly:
Distance: "As the Crow Flies" vs "Along Roads"
Straight-line distance (offline, instant):
- Tools:
distance_tool,bearing_tool,midpoint_tool - Use for: Proximity checks, "how far away is X?", comparing distances
- Example: "Is this restaurant within 2 miles?" →
distance_tool
Route distance (API, traffic-aware):
- Tools:
directions_tool,matrix_tool - Use for: Navigation, drive time, "how long to drive?"
- Example: "How long to drive there?" →
directions_tool
Search: Type vs Specific Place
Category/type search:
- Tool:
category_search_tool - Use for: "Find coffee shops", "restaurants nearby", browsing by type
- Example: "What hotels are near me?" →
category_search_tool
Specific place/address:
- Tool:
search_and_geocode_tool,reverse_geocode_tool - Use for: Named places, street addresses, landmarks
- Example: "Find 123 Main Street" →
search_and_geocode_tool
Travel Time: Area vs Route
Reachable area (what's within reach):
- Tool:
isochrone_tool - Returns: GeoJSON polygon of everywhere reachable
- Example: "What can I reach in 15 minutes?" →
isochrone_tool
Specific route (how to get there):
- Tool:
directions_tool - Returns: Turn-by-turn directions to one destination
- Example: "How do I get to the airport?" →
directions_tool
Cost & Performance
Offline tools (free, instant):
- No API calls, no token usage
- Use whenever real-time data not needed
- Examples:
distance_tool,point_in_polygon_tool,area_tool
API tools (requires token, counts against usage):
- Real-time traffic, live POI data, current conditions
- Use when accuracy and freshness matter
- Examples:
directions_tool,category_search_tool,isochrone_tool
Best practice: Prefer offline tools when possible, use API tools when you need real-time data or routing.
Installation & Setup
Option 1: Hosted Server (Recommended)
Easiest integration - Use Mapbox's hosted MCP server at:
https://mcp.mapbox.com/mcpNo installation required. Simply pass your Mapbox access token in the Authorization header.
Benefits:
- No server management
- Always up-to-date
- Production-ready
- Lower latency (Mapbox infrastructure)
Authentication:
Use token-based authentication (standard for programmatic access):
Authorization: Bearer your_mapbox_tokenNote: The hosted server also supports OAuth, but that's primarily for interactive flows (coding assistants, not production apps).
Option 2: Self-Hosted
For custom deployments or development:
npm install @mapbox/mcp-serverOr use directly via npx:
npx @mapbox/mcp-serverEnvironment setup:
export MAPBOX_ACCESS_TOKEN="your_token_here"Reference Files
Detailed integration patterns and production guidance are organized into reference files. Load the ones relevant to your task.
- Pydantic AI -- Type-safe Python agents
Load: references/pydantic-ai.md
- CrewAI -- Multi-agent orchestration
Load: references/crewai.md
- Smolagents -- Lightweight HuggingFace agents
Load: references/smolagents.md
- Mastra -- Multi-agent TypeScript systems
Load: references/mastra.md
- LangChain -- Conversational AI with tool chaining
Load: references/langchain.md
- Custom Agent -- Zillow/TripAdvisor/DoorDash-style patterns, architecture diagrams, hybrid approach
Load: references/custom-agent.md
- Use Cases -- Real Estate, Food Delivery, Travel Planning examples
Load: references/use-cases.md
- Production Patterns -- Caching, batch operations, tool descriptions, error handling, security, rate limiting, testing
Load: references/production.md
Resources
When to Use This Skill
Invoke this skill when:
- Integrating Mapbox MCP Server into AI applications
- Building AI agents with geospatial capabilities
- Architecting Zillow/TripAdvisor/DoorDash-style apps with AI
- Choosing between MCP, direct APIs, or SDKs
- Optimizing geospatial operations in production
- Implementing error handling for geospatial AI features
- Testing AI applications with geospatial tools
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-coordinatesarray of objectsisochrone_tool-coordinatesparameterreverse_geocode_tool-coordinatesparametercategory_search_tool-proximityparameterdistance_tool-from/toparametersbearing_tool-from/toparametersmidpoint_tool-from/toparameterspoint_in_polygon_tool-pointparameter
Exception — GeoJSON geometry (arrays only):
buffer_tool-geometryparameter uses[longitude, latitude]arrays (GeoJSON format)point_in_polygon_tool-polygonrings 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/mcpConnect 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
{
"skill_name": "mapbox-mcp-runtime-patterns",
"evals": [
{
"id": 1,
"prompt": "I'm building a Python app using pydantic-ai with OpenAI's GPT-4o as the LLM and want to integrate the Mapbox MCP server. Show me how to set up an agent that can get driving directions from the Eiffel Tower (2.2945° E, 48.8584° N) to the Louvre (2.3376° E, 48.8606° N).",
"expected_output": "Should produce a working pydantic-ai example with correct imports (OpenAIChatModel not OpenAIModel from pydantic_ai.models.openai), correct directions_tool call using coordinates array of {longitude, latitude} objects with routing_profile 'mapbox/driving-traffic', and MCP server connection setup.",
"files": [],
"expectations": [
"Uses OpenAIChatModel (not OpenAIModel) from pydantic_ai.models.openai",
"Calls directions_tool with a 'coordinates' array of {longitude, latitude} objects",
"Includes 'routing_profile' parameter with 'mapbox/' prefix (e.g., 'mapbox/driving-traffic')",
"Does NOT pass coordinates as arrays like [lng, lat]",
"Does NOT use 'origin'/'destination' parameter names for directions_tool",
"Shows MCP server connection setup (MCPServerHTTP or similar)"
]
},
{
"id": 2,
"prompt": "I'm using LangChain in TypeScript and want to find coffee shops near Times Square (40.7580° N, 73.9855° W) using the Mapbox MCP server. Show me the setup code.",
"expected_output": "Should use the modern LangChain createToolCallingAgent + AgentExecutor pattern (NOT the deprecated initializeAgentExecutorWithOptions). The category_search_tool proximity parameter should be an object {longitude, latitude}, not an array.",
"files": [],
"expectations": [
"Uses createToolCallingAgent and AgentExecutor (not initializeAgentExecutorWithOptions)",
"Uses DynamicStructuredTool with Zod schemas (not DynamicTool)",
"category_search_tool proximity parameter is an object {longitude: ..., latitude: ...}",
"Does NOT pass proximity as an array [longitude, latitude]",
"Shows MCP client/transport setup"
]
},
{
"id": 3,
"prompt": "I'm building an AI agent with Mastra and want to check if a user's location (34.0522° N, 118.2437° W in Los Angeles) falls within a service polygon. Show me how to use point_in_polygon_tool with the Mapbox MCP server in Mastra.",
"expected_output": "Should show a Mastra agent that calls point_in_polygon_tool with the point as a {longitude, latitude} object. Should demonstrate MCP integration via Mastra's tool system.",
"files": [],
"expectations": [
"point_in_polygon_tool point parameter is a {longitude, latitude} object",
"Does NOT pass point as an array [longitude, latitude]",
"Demonstrates Mastra agent setup with MCP tools",
"Shows how to pass a polygon (GeoJSON format) to the tool"
]
},
{
"id": 4,
"prompt": "I want to build a custom AI agent in Python that can answer questions like 'What restaurants are within 15 minutes walking from Union Square in San Francisco?' using the Mapbox MCP server. Walk me through the approach and show skeleton code.",
"expected_output": "Should outline a two-step approach: (1) isochrone_tool to get the 15-minute walking zone, (2) category_search_tool to find restaurants. isochrone_tool coordinates should be a {longitude, latitude} object with profile 'mapbox/walking'. category_search_tool proximity as {longitude, latitude} object.",
"files": [],
"expectations": [
"Recommends isochrone_tool to create the 15-minute walking zone with profile 'mapbox/walking'",
"isochrone_tool coordinates parameter is a {longitude, latitude} object",
"Recommends category_search_tool to find restaurants",
"category_search_tool proximity is a {longitude, latitude} object",
"Routing profile includes 'mapbox/' prefix"
]
}
]
}
"""
CrewAI + Mapbox MCP Integration Example
This example shows how to integrate Mapbox MCP Server with CrewAI multi-agent systems.
Prerequisites:
- pip install crewai requests openai python-dotenv
- Set MAPBOX_ACCESS_TOKEN and OPENAI_API_KEY environment variables
Usage:
- python crewai_example.py
"""
import os
import json
import requests
from typing import Type
from pydantic import BaseModel, Field
from crewai import Agent, Task, Crew
from crewai.tools import BaseTool
from dotenv import load_dotenv
load_dotenv()
class MapboxMCP:
"""Mapbox MCP client for hosted server."""
def __init__(self, token: str = None):
self.url = 'https://mcp.mapbox.com/mcp'
token = token or os.getenv('MAPBOX_ACCESS_TOKEN')
if not token:
raise ValueError('MAPBOX_ACCESS_TOKEN is required')
self.headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {token}'
}
def call_tool(self, tool_name: str, params: dict) -> str:
"""Call MCP tool via HTTPS."""
request = {
'jsonrpc': '2.0',
'id': 1,
'method': 'tools/call',
'params': {
'name': tool_name,
'arguments': params
}
}
response = requests.post(
self.url,
headers=self.headers,
json=request
)
response.raise_for_status()
data = response.json()
if 'error' in data:
raise RuntimeError(f"MCP error: {data['error']['message']}")
return data['result']['content'][0]['text']
# Initialize MCP client
mcp = MapboxMCP()
# Create Mapbox tools for CrewAI
class DirectionsInput(BaseModel):
"""Input schema for directions tool."""
origin: list = Field(..., description="Origin coordinates [longitude, latitude]")
destination: list = Field(..., description="Destination coordinates [longitude, latitude]")
class DirectionsTool(BaseTool):
name: str = "directions_tool"
description: str = "Get turn-by-turn driving directions with traffic-aware route distance and travel time along roads. Use when you need the actual driving route or traffic-aware duration. Returns duration and distance."
args_schema: Type[BaseModel] = DirectionsInput
def _run(self, origin: list, destination: list) -> str:
result = mcp.call_tool('directions_tool', {
'coordinates': [
{'longitude': origin[0], 'latitude': origin[1]},
{'longitude': destination[0], 'latitude': destination[1]}
],
'routing_profile': 'mapbox/driving-traffic'
})
return f"Directions: {result}"
class SearchPOIInput(BaseModel):
"""Input schema for POI search tool."""
category: str = Field(..., description="POI category (restaurant, hotel, coffee, etc.)")
location: list = Field(..., description="Search center [longitude, latitude]")
class SearchPOITool(BaseTool):
name: str = "search_poi"
description: str = "Find ALL places of a specific category type near a location. Use when user wants to browse places by type (restaurants, hotels, coffee, etc.), not search for a specific named place. Returns names and addresses."
args_schema: Type[BaseModel] = SearchPOIInput
def _run(self, category: str, location: list) -> str:
result = mcp.call_tool('category_search_tool', {
'category': category,
'proximity': {'longitude': location[0], 'latitude': location[1]}
})
return result
class CalculateDistanceInput(BaseModel):
"""Input schema for distance calculation tool."""
from_coords: list = Field(..., description="Start coordinates [longitude, latitude]")
to_coords: list = Field(..., description="End coordinates [longitude, latitude]")
units: str = Field('miles', description="Units: 'miles' or 'kilometers'")
class CalculateDistanceTool(BaseTool):
name: str = "distance_tool"
description: str = "Calculate straight-line (great-circle) distance between two points. Use for quick 'as the crow flies' distance checks. Works offline, instant, no API cost."
args_schema: Type[BaseModel] = CalculateDistanceInput
def _run(self, from_coords: list, to_coords: list, units: str = 'miles') -> str:
result = mcp.call_tool('distance_tool', {
'from': {'longitude': from_coords[0], 'latitude': from_coords[1]},
'to': {'longitude': to_coords[0], 'latitude': to_coords[1]},
'units': units
})
return f"{result} {units}"
class IsochroneInput(BaseModel):
"""Input schema for isochrone tool."""
location: list = Field(..., description="Center point [longitude, latitude]")
minutes: int = Field(..., description="Time limit in minutes")
profile: str = Field('mapbox/driving', description="Travel mode: mapbox/driving, mapbox/walking, or mapbox/cycling")
class IsochroneTool(BaseTool):
name: str = "isochrone_tool"
description: str = "Calculate the AREA reachable within a time limit from a starting point. Use for 'What can I reach in X minutes?' questions or service area analysis. Returns GeoJSON polygon of reachable area."
args_schema: Type[BaseModel] = IsochroneInput
def _run(self, location: list, minutes: int, profile: str = 'mapbox/driving') -> str:
result = mcp.call_tool('isochrone_tool', {
'coordinates': {'longitude': location[0], 'latitude': location[1]},
'contours_minutes': [minutes],
'profile': profile
})
return result
# Create specialized agents with geospatial tools
location_analyst = Agent(
role='Location Intelligence Analyst',
goal='Analyze geographic locations and find the best places for users',
backstory="""Expert in geographic analysis with years of experience finding optimal locations.
TOOL SELECTION: Use search_poi for finding types of places (restaurants, hotels),
calculate_distance for straight-line distance checks, and get_isochrone for
'what can I reach in X minutes' questions. Prefer offline tools when real-time data not needed.""",
tools=[SearchPOITool(), CalculateDistanceTool(), IsochroneTool()],
verbose=True
)
route_planner = Agent(
role='Route Planning Specialist',
goal='Plan optimal routes and provide accurate travel time estimates',
backstory="""Experienced logistics coordinator specializing in route optimization and traffic analysis.
TOOL SELECTION: Use get_directions for route distance along roads with traffic,
calculate_distance for straight-line distance. Always use get_directions when
traffic-aware travel time is needed.""",
tools=[DirectionsTool(), CalculateDistanceTool()],
verbose=True
)
def example_restaurant_finder():
"""Example: Find restaurants near a location."""
print("\n=== Example 1: Restaurant Finder Crew ===\n")
# Define tasks
find_restaurants = Task(
description="""
Find 5 restaurants near Times Square NYC (coordinates: -73.9857, 40.7484).
Get their names, addresses, and coordinates.
""",
agent=location_analyst,
expected_output="List of 5 restaurants with names and locations"
)
calculate_routes = Task(
description="""
For each restaurant found, calculate the driving time from downtown NYC
(coordinates: -74.0060, 40.7128) with current traffic.
Rank the restaurants by travel time.
""",
agent=route_planner,
expected_output="Restaurants ranked by travel time with durations",
context=[find_restaurants] # Depends on previous task
)
# Create and run crew
crew = Crew(
agents=[location_analyst, route_planner],
tasks=[find_restaurants, calculate_routes],
verbose=True
)
result = crew.kickoff()
print("\nCrew Result:")
print(result)
def example_property_search():
"""Example: Find properties with good commute."""
print("\n=== Example 2: Property Search with Commute Analysis ===\n")
# Define property search task
analyze_commute = Task(
description="""
I'm looking for apartments in San Francisco. My work is at -122.4, 37.79.
1. Calculate the area I can reach within 30 minutes driving from work
2. Find coffee shops within 10 minutes walking from work
3. Recommend neighborhoods that are within the commute area and have coffee shops nearby
""",
agent=location_analyst,
expected_output="Recommended neighborhoods with commute times and nearby amenities"
)
# Create and run crew
crew = Crew(
agents=[location_analyst],
tasks=[analyze_commute],
verbose=True
)
result = crew.kickoff()
print("\nCrew Result:")
print(result)
def main():
"""Run all examples."""
try:
# Example 1: Restaurant finder with multi-agent crew
example_restaurant_finder()
print("\n" + "="*60 + "\n")
# Example 2: Property search with commute analysis
example_property_search()
except Exception as e:
print(f"Error: {e}")
import traceback
traceback.print_exc()
if __name__ == '__main__':
main()
"""
Pydantic AI + Mapbox MCP Integration Example
This example shows how to integrate Mapbox MCP Server with Pydantic AI agents.
Prerequisites:
- pip install pydantic-ai requests openai python-dotenv
- Set MAPBOX_ACCESS_TOKEN and OPENAI_API_KEY environment variables
Usage:
- python pydantic_ai_example.py
"""
import os
import json
from typing import List, Tuple
import requests
from pydantic_ai import Agent, RunContext
from pydantic_ai.models.openai import OpenAIChatModel
from dotenv import load_dotenv
load_dotenv()
class MapboxMCP:
"""Mapbox MCP client for hosted server."""
def __init__(self, token: str = None):
self.url = 'https://mcp.mapbox.com/mcp'
token = token or os.getenv('MAPBOX_ACCESS_TOKEN')
if not token:
raise ValueError('MAPBOX_ACCESS_TOKEN is required')
self.headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {token}'
}
def call_tool(self, tool_name: str, params: dict) -> str:
"""Call MCP tool via HTTPS."""
request = {
'jsonrpc': '2.0',
'id': 1,
'method': 'tools/call',
'params': {
'name': tool_name,
'arguments': params
}
}
response = requests.post(
self.url,
headers=self.headers,
json=request
)
response.raise_for_status()
data = response.json()
if 'error' in data:
raise RuntimeError(f"MCP error: {data['error']['message']}")
return data['result']['content'][0]['text']
# Initialize MCP client
mcp = MapboxMCP()
# Create Pydantic AI agent with Mapbox tools
model = OpenAIChatModel('gateway/openai:gpt-5.2')
agent = Agent(
model,
system_prompt="""You are a location intelligence expert. You help users with:
- Finding places (restaurants, hotels, etc.)
- Planning routes with traffic
- Calculating distances and travel times
- Analyzing reachable areas
Always provide clear, actionable information with specific times and distances."""
)
@agent.tool
def get_directions(
ctx: RunContext,
origin: Tuple[float, float],
destination: Tuple[float, float]
) -> str:
"""Get driving directions between two locations with current traffic.
Args:
origin: Origin coordinates (longitude, latitude)
destination: Destination coordinates (longitude, latitude)
Returns:
JSON string with route details (duration, distance)
"""
result = mcp.call_tool('directions_tool', {
'coordinates': [
{'longitude': origin[0], 'latitude': origin[1]},
{'longitude': destination[0], 'latitude': destination[1]}
],
'routing_profile': 'mapbox/driving-traffic'
})
return result
@agent.tool
def search_poi(
ctx: RunContext,
category: str,
location: Tuple[float, float]
) -> str:
"""Find points of interest near a location.
Args:
category: POI category (restaurant, hotel, coffee, gas_station, etc.)
location: Search center (longitude, latitude)
Returns:
JSON string with nearby POIs
"""
result = mcp.call_tool('category_search_tool', {
'category': category,
'proximity': {'longitude': location[0], 'latitude': location[1]}
})
return result
@agent.tool
def calculate_distance(
ctx: RunContext,
from_coords: Tuple[float, float],
to_coords: Tuple[float, float],
units: str = 'miles'
) -> str:
"""Calculate distance between two points (offline, instant, free).
Args:
from_coords: Start coordinates (longitude, latitude)
to_coords: End coordinates (longitude, latitude)
units: 'miles' or 'kilometers'
Returns:
Distance as a string
"""
result = mcp.call_tool('distance_tool', {
'from': {'longitude': from_coords[0], 'latitude': from_coords[1]},
'to': {'longitude': to_coords[0], 'latitude': to_coords[1]},
'units': units
})
return result
@agent.tool
def get_isochrone(
ctx: RunContext,
location: Tuple[float, float],
minutes: int,
profile: str = 'mapbox/walking'
) -> str:
"""Calculate reachable area within a time limit.
Args:
location: Center point (longitude, latitude)
minutes: Time limit in minutes
profile: 'mapbox/driving', 'mapbox/walking', or 'mapbox/cycling'
Returns:
GeoJSON polygon of reachable area
"""
result = mcp.call_tool('isochrone_tool', {
'coordinates': {'longitude': location[0], 'latitude': location[1]},
'contours_minutes': [minutes],
'profile': profile
})
return result
def main():
"""Run example queries."""
print("Example 1: Finding restaurants near Times Square\n")
result1 = agent.run_sync(
"Find 3 restaurants near Times Square NYC (coordinates: -73.9857, 40.7484) "
"and tell me how far each is from the center."
)
print("Agent:", result1.output)
print("\n---\n")
print("Example 2: Planning route with traffic\n")
result2 = agent.run_sync(
"What is the driving time from Boston (-71.0589, 42.3601) to "
"NYC (-74.0060, 40.7128) with current traffic?"
)
print("Agent:", result2.output)
print("\n---\n")
print("Example 3: Multi-step analysis\n")
result3 = agent.run_sync(
"I work at -122.4, 37.79 in San Francisco. Find coffee shops within "
"10 minutes walking, calculate distance to each, and recommend the closest 3."
)
print("Agent:", result3.output)
if __name__ == '__main__':
main()
# Mapbox MCP Runtime Integration Examples
# Python 3.10+
# Core
requests>=2.32.0
# Agent Frameworks
pydantic-ai>=0.1.0
crewai>=0.83.0
smolagents>=1.0.0
# LLM Providers
openai>=1.58.1
# Utilities
python-dotenv>=1.0.1
"""
Smolagents + Mapbox MCP Integration Example
This example shows TWO ways to integrate Mapbox MCP Server with Smolagents:
1. Using MCPClient (direct MCP connection - recommended)
2. Creating custom tools with @tool decorator
Prerequisites:
- pip install smolagents requests huggingface-hub python-dotenv
- Set MAPBOX_ACCESS_TOKEN and HF_TOKEN environment variables
Usage:
- python smolagents_example.py
"""
import os
from smolagents import CodeAgent, HfApiModel, MCPClient, tool
import requests
from dotenv import load_dotenv
load_dotenv()
# ============================================================================
# Method 1: Direct MCP Connection (RECOMMENDED)
# ============================================================================
def example_with_mcp_client():
"""
Use Smolagents MCPClient to directly connect to Mapbox MCP Server.
This is the recommended approach as it requires minimal code.
"""
print("\n=== Example 1: Using MCPClient (Direct MCP Connection) ===\n")
# Configure Mapbox MCP server connection
server_params = {
"url": "https://mcp.mapbox.com/mcp",
"transport": "streamable-http",
"headers": {
"Authorization": f"Bearer {os.getenv('MAPBOX_ACCESS_TOKEN')}"
}
}
model = HfApiModel()
# Use MCP Client to load all Mapbox tools automatically
with MCPClient(server_params, structured_output=True) as tools:
agent = CodeAgent(
tools=tools,
model=model,
add_base_tools=True
)
# Example 1: Find restaurants
result1 = agent.run(
"Find 3 restaurants near Times Square NYC (coordinates: -73.9857, 40.7484). "
"For each restaurant, calculate how far it is from Times Square."
)
print("\nResult:", result1)
print("\n" + "="*60)
# Example 2: Route planning
result2 = agent.run(
"What is the driving time with traffic from Boston (-71.0589, 42.3601) to "
"NYC (-74.0060, 40.7128)?"
)
print("\nResult:", result2)
# ============================================================================
# Method 2: Custom Tools with @tool Decorator
# ============================================================================
# Mapbox MCP Client for custom tools
class MapboxMCP:
"""Mapbox MCP client for hosted server."""
def __init__(self, token: str = None):
self.url = 'https://mcp.mapbox.com/mcp'
token = token or os.getenv('MAPBOX_ACCESS_TOKEN')
if not token:
raise ValueError('MAPBOX_ACCESS_TOKEN is required')
self.headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {token}'
}
def call_tool(self, tool_name: str, params: dict) -> str:
"""Call MCP tool via HTTPS."""
request = {
'jsonrpc': '2.0',
'id': 1,
'method': 'tools/call',
'params': {
'name': tool_name,
'arguments': params
}
}
response = requests.post(
self.url,
headers=self.headers,
json=request
)
response.raise_for_status()
data = response.json()
if 'error' in data:
raise RuntimeError(f"MCP error: {data['error']['message']}")
return data['result']['content'][0]['text']
mcp = MapboxMCP()
# Create custom tools with @tool decorator
@tool
def get_directions(origin: list, destination: list) -> str:
"""
Get driving directions between two locations with current traffic.
Args:
origin: Origin coordinates [longitude, latitude]
destination: Destination coordinates [longitude, latitude]
Returns:
Route details with duration and distance
"""
return mcp.call_tool('directions_tool', {
'coordinates': [
{'longitude': origin[0], 'latitude': origin[1]},
{'longitude': destination[0], 'latitude': destination[1]}
],
'routing_profile': 'mapbox/driving-traffic'
})
@tool
def search_poi(category: str, location: list) -> str:
"""
Find points of interest (restaurants, hotels, etc.) near a location.
Args:
category: POI category (restaurant, hotel, coffee, gas_station, etc.)
location: Search center [longitude, latitude]
Returns:
List of nearby POIs with names and addresses
"""
return mcp.call_tool('category_search_tool', {
'category': category,
'proximity': {'longitude': location[0], 'latitude': location[1]}
})
@tool
def calculate_distance(from_coords: list, to_coords: list, units: str = 'miles') -> str:
"""
Calculate distance between two points (offline, instant, free).
Args:
from_coords: Start coordinates [longitude, latitude]
to_coords: End coordinates [longitude, latitude]
units: 'miles' or 'kilometers'
Returns:
Distance value
"""
return mcp.call_tool('distance_tool', {
'from': {'longitude': from_coords[0], 'latitude': from_coords[1]},
'to': {'longitude': to_coords[0], 'latitude': to_coords[1]},
'units': units
})
@tool
def get_isochrone(location: list, minutes: int, profile: str = 'mapbox/walking') -> str:
"""
Calculate reachable area within a time limit (isochrone).
Args:
location: Center point [longitude, latitude]
minutes: Time limit in minutes
profile: 'mapbox/driving', 'mapbox/walking', or 'mapbox/cycling'
Returns:
GeoJSON polygon of reachable area
"""
return mcp.call_tool('isochrone_tool', {
'coordinates': {'longitude': location[0], 'latitude': location[1]},
'contours_minutes': [minutes],
'profile': profile
})
def example_with_custom_tools():
"""
Use custom tools created with @tool decorator.
Gives you more control over individual tool behavior.
"""
print("\n=== Example 2: Using Custom Tools (@tool decorator) ===\n")
model = HfApiModel()
# Create agent with custom tools
agent = CodeAgent(
tools=[
get_directions,
search_poi,
calculate_distance,
get_isochrone
],
model=model,
add_base_tools=True
)
# Example: Property search with commute analysis
result = agent.run(
"""I'm looking for an apartment in San Francisco. My work is at -122.4, 37.79.
1. Calculate the area I can reach within 30 minutes driving from work
2. Find coffee shops within 10 minutes walking from work
3. Calculate distance from work to downtown SF (-122.4194, 37.7749)
"""
)
print("\nResult:", result)
# ============================================================================
# Real-World Use Case: Property Search Agent
# ============================================================================
class PropertySearchAgent:
"""Agent for finding properties with good commutes."""
def __init__(self):
self.model = HfApiModel()
self.mcp_params = {
"url": "https://mcp.mapbox.com/mcp",
"transport": "streamable-http",
"headers": {
"Authorization": f"Bearer {os.getenv('MAPBOX_ACCESS_TOKEN')}"
}
}
def find_properties_near_work(
self,
work_location: list,
max_commute_minutes: int
):
"""Find properties within commute time of work."""
with MCPClient(self.mcp_params, structured_output=True) as tools:
agent = CodeAgent(
tools=tools,
model=self.model,
add_base_tools=True
)
prompt = f"""
I work at coordinates {work_location} and want to find good neighborhoods
to live in with a maximum commute of {max_commute_minutes} minutes.
Please:
1. Calculate the reachable area within {max_commute_minutes} minutes driving
2. Find restaurants within 10 minutes walking from work (for lunch)
3. Find coffee shops within 5 minutes walking from work
4. Recommend neighborhoods based on this analysis
"""
return agent.run(prompt)
def example_real_world():
"""Real-world example: Property search with commute."""
print("\n=== Example 3: Real-World Use Case (Property Search) ===\n")
property_agent = PropertySearchAgent()
result = property_agent.find_properties_near_work(
work_location=[-122.4, 37.79], # Downtown SF
max_commute_minutes=30
)
print("\nResult:", result)
# ============================================================================
# Main
# ============================================================================
def main():
"""Run all examples."""
try:
# Recommended approach: Direct MCP connection
example_with_mcp_client()
print("\n\n")
# Alternative: Custom tools for fine control
example_with_custom_tools()
print("\n\n")
# Real-world use case
example_real_world()
except Exception as e:
print(f"Error: {e}")
import traceback
traceback.print_exc()
if __name__ == '__main__':
main()
Mapbox MCP Runtime Integration Examples
Working, compilable examples showing how to integrate Mapbox MCP Server with popular agent frameworks.
Prerequisites
1. Mapbox Access Token: Get one at mapbox.com/account/access-tokens 2. OpenAI API Key (or other LLM provider) 3. HuggingFace Token (for smolagents)
Python Examples
Setup
cd python
pip install -r requirements.txt
# Set environment variables
export MAPBOX_ACCESS_TOKEN="your_token_here"
export OPENAI_API_KEY="your_openai_key"
export HF_TOKEN="your_huggingface_token" # For smolagents1. Pydantic AI Example
Framework: Pydantic AI - Type-safe agents with validation
python pydantic_ai_example.pyFeatures:
- Type-safe tool definitions
- Environment variable support
- Hosted MCP server integration
- Real-world examples (restaurant finder, route planning)
Best for: Production applications requiring type safety and validation
---
2. CrewAI Example
Framework: CrewAI - Multi-agent orchestration
python crewai_example.pyFeatures:
- Multi-agent crews with specialized roles
- Task dependencies and context passing
- Location Analyst + Route Planner agents
- Real-world examples (restaurant crew, property search)
Best for: Complex workflows requiring multiple specialized agents
Agents included:
- Location Analyst: Finds places, analyzes areas
- Route Planner: Calculates routes and travel times
---
3. Smolagents Example
Framework: Smolagents - Hugging Face's lightweight agents
python smolagents_example.pyFeatures:
- Method 1: Direct MCP connection (recommended, minimal code)
- Method 2: Custom tools with
@tooldecorator - Lightweight and fast
- Real-world example (property search agent)
Best for: Production deployment with minimal overhead
Note: Smolagents has native MCP support via MCPClient!
---
TypeScript Examples
Setup
cd typescript
npm install
# Set environment variables
export MAPBOX_ACCESS_TOKEN="your_token_here"
export OPENAI_API_KEY="your_openai_key"1. Mastra Example
Framework: Mastra 1.x - Modern TypeScript agent framework
npm run mastraFeatures:
- Type-safe tool creation with Zod schemas
- Hosted MCP server integration
- Multiple Mapbox tools (directions, POI search, distance, isochrone)
- Real-world examples
Best for: TypeScript applications with strong typing
Tools included:
get-directions: Driving directions with trafficsearch-poi: Find restaurants, hotels, etc.calculate-distance: Offline distance calculationget-isochrone: Reachable area analysis
Verify it compiles:
npm run build # TypeScript type-check---
2. LangChain Example
Framework: LangChain - Conversational AI framework
npm run langchainFeatures:
- Conversational interface
- Tool chaining
- Memory and context management
- Multi-step workflows
Best for: Conversational applications with complex tool chains
Tools included:
- Directions, POI search, distance calculation, isochrones
- All tools use hosted Mapbox MCP server
---
Framework Comparison
| Framework | Language | Best For | Complexity | Type Safety |
|---|---|---|---|---|
| Pydantic AI | Python | Production apps | Medium | ⭐⭐⭐ |
| CrewAI | Python | Multi-agent systems | High | ⭐⭐ |
| Smolagents | Python | Lightweight agents | Low | ⭐⭐ |
| Mastra | TypeScript | Typed agents | Medium | ⭐⭐⭐ |
| LangChain | TypeScript | Conversational AI | High | ⭐⭐ |
Common Use Cases
1. Restaurant Finder
Find restaurants near a location with distances:
- Pydantic AI:
pydantic_ai_example.py(Example 1) - CrewAI:
crewai_example.py(Example 1) - LangChain:
langchain-example.ts(Example 3)
2. Route Planning
Calculate driving time with traffic:
- Pydantic AI:
pydantic_ai_example.py(Example 2) - Mastra:
mastra-example.ts(Example 2)
3. Property Search
Find properties with good commute:
- CrewAI:
crewai_example.py(Example 2) - Smolagents:
smolagents_example.py(Example 3)
Mapbox MCP Tools Available
All examples connect to the hosted Mapbox MCP Server at https://mcp.mapbox.com/mcp.
API Tools (require Mapbox token):
directions_tool: Driving directions with trafficcategory_search_tool: Find POIs by categorysearch_and_geocode_tool: Search for specific places or addressesreverse_geocode_tool: Coordinates to addressisochrone_tool: Reachable area within timematrix_tool: Travel time matrixstatic_map_image_tool: Static map imagesmap_matching_tool: Match GPS traces to roadsoptimization_tool: Optimize multi-stop routes
Offline Tools (free, instant):
distance_tool: Distance between pointsbearing_tool: Compass directionmidpoint_tool: Midpoint between pointspoint_in_polygon_tool: Point containment testarea_tool: Polygon areacentroid_tool: Polygon centerbuffer_tool: Create buffer zonesbbox_tool: Calculate bounding boxessimplify_tool: Simplify geometries
Utility Tools:
version_tool: Get MCP server versioncategory_list_tool: List available POI categories
Testing Examples
Python
# Run all Python examples
cd python
python pydantic_ai_example.py
python crewai_example.py
python smolagents_example.pyTypeScript
# Type-check all TypeScript examples
cd typescript
npm run build
# Run individual examples
npm run mastra
npm run langchainTroubleshooting
Missing MAPBOX_ACCESS_TOKEN
Error: MAPBOX_ACCESS_TOKEN is requiredSolution: Export the environment variable
export MAPBOX_ACCESS_TOKEN="pk.ey..."MCP Connection Failed
Error: MCP request failed: UnauthorizedSolution: Check your token has proper scopes at mapbox.com/account/access-tokens
Import Errors (Python)
ModuleNotFoundError: No module named 'crewai'Solution: Install requirements
pip install -r requirements.txtTypeScript Compilation Errors
Cannot find module '@mastra/core'Solution: Install dependencies
npm installResources
- Mapbox MCP Server
- Mapbox MCP DevKit
- Model Context Protocol
- Pydantic AI Docs
- CrewAI Docs
- Smolagents Docs
- Mastra Docs
- LangChain Docs
Contributing
Found an issue or want to add more examples? Please open a PR!
License
These examples are provided as-is for educational purposes.
/**
* LangChain + Mapbox MCP Integration Example
*
* This example shows how to integrate Mapbox MCP Server with LangChain agents.
*
* Prerequisites:
* - npm install langchain @langchain/core @langchain/openai
* - Set MAPBOX_ACCESS_TOKEN and OPENAI_API_KEY environment variables
*
* Usage:
* - ts-node langchain-example.ts
*/
import { ChatOpenAI } from '@langchain/openai';
import { AgentExecutor, createToolCallingAgent } from 'langchain/agents';
import { DynamicStructuredTool } from '@langchain/core/tools';
import { ChatPromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts';
import { z } from 'zod';
// Mapbox MCP Client (hosted server)
class MapboxMCP {
private url = 'https://mcp.mapbox.com/mcp';
private headers: Record<string, string>;
constructor(token?: string) {
const mapboxToken = token || process.env.MAPBOX_ACCESS_TOKEN;
if (!mapboxToken) {
throw new Error('MAPBOX_ACCESS_TOKEN is required');
}
this.headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${mapboxToken}`
};
}
async callTool(name: string, args: any): Promise<string> {
const request = {
jsonrpc: '2.0',
id: Date.now(),
method: 'tools/call',
params: { name, arguments: args }
};
const response = await fetch(this.url, {
method: 'POST',
headers: this.headers,
body: JSON.stringify(request)
});
if (!response.ok) {
throw new Error(`MCP request failed: ${response.statusText}`);
}
const data = await response.json() as any;
if (data.error) {
throw new Error(`MCP error: ${data.error.message}`);
}
return data.result.content[0].text;
}
}
// Initialize MCP client
const mcp = new MapboxMCP();
// Create LangChain tools from Mapbox MCP
const getDirectionsTool = new DynamicStructuredTool({
name: 'directions_tool',
description: 'Get turn-by-turn driving directions with traffic-aware route distance and travel time along roads. Use when you need the actual driving route, navigation, or traffic-aware duration. Returns route distance and time.',
schema: z.object({
origin: z.tuple([z.number(), z.number()]).describe('Origin coordinates [longitude, latitude]'),
destination: z.tuple([z.number(), z.number()]).describe('Destination coordinates [longitude, latitude]'),
}) as any,
func: async ({ origin, destination }: any) => {
const result = await mcp.callTool('directions_tool', {
coordinates: [
{ longitude: origin[0], latitude: origin[1] },
{ longitude: destination[0], latitude: destination[1] }
],
routing_profile: 'mapbox/driving-traffic'
});
return result;
}
});
const searchPOITool = new DynamicStructuredTool({
name: 'search_poi',
description: 'Find ALL places of a specific category type (restaurants, hotels, coffee shops, gas stations, etc.) near a location. Use when user wants to browse or discover places by type, not search for a specific named place.',
schema: z.object({
category: z.string().describe('POI category: restaurant, hotel, coffee, gas_station, etc.'),
location: z.tuple([z.number(), z.number()]).describe('Search center [longitude, latitude]'),
}) as any,
func: async ({ category, location }: any) => {
const result = await mcp.callTool('category_search_tool', {
category,
proximity: { longitude: location[0], latitude: location[1] }
});
return result;
}
});
const calculateDistanceTool = new DynamicStructuredTool({
name: 'distance_tool',
description: 'Calculate straight-line (great-circle) distance between two points. Use for quick "as the crow flies" distance, proximity checks, or when routing not needed. Works offline, instant, no API cost.',
schema: z.object({
from: z.tuple([z.number(), z.number()]).describe('Start coordinates [longitude, latitude]'),
to: z.tuple([z.number(), z.number()]).describe('End coordinates [longitude, latitude]'),
units: z.enum(['miles', 'kilometers']).optional()
}) as any,
func: async ({ from, to, units }: any) => {
const result = await mcp.callTool('distance_tool', {
from: { longitude: from[0], latitude: from[1] },
to: { longitude: to[0], latitude: to[1] },
units: units || 'miles'
});
return result;
}
});
const getIsochroneTool = new DynamicStructuredTool({
name: 'isochrone_tool',
description: 'Calculate the AREA reachable within a time limit from a starting point. Use for "What can I reach in X minutes?" questions, service areas, or delivery zones. Returns GeoJSON polygon of reachable area.',
schema: z.object({
location: z.tuple([z.number(), z.number()]).describe('Center point [longitude, latitude]'),
minutes: z.number().describe('Time limit in minutes'),
profile: z.enum(['mapbox/driving', 'mapbox/walking', 'mapbox/cycling']).optional()
}) as any,
func: async ({ location, minutes, profile }: any) => {
const result = await mcp.callTool('isochrone_tool', {
coordinates: { longitude: location[0], latitude: location[1] },
contours_minutes: [minutes],
profile: profile || 'mapbox/walking'
});
return result;
}
});
// Create the agent
async function createLocationAgent() {
const tools = [
getDirectionsTool,
searchPOITool,
calculateDistanceTool,
getIsochroneTool
];
const llm = new ChatOpenAI({
model: 'gpt-5.2',
temperature: 0
});
const prompt = ChatPromptTemplate.fromMessages([
['system', `You are a location intelligence assistant. You help users with:
- Finding places (restaurants, hotels, coffee shops, etc.)
- Planning routes with traffic
- Calculating distances and travel times
- Analyzing reachable areas
TOOL SELECTION RULES:
- Use calculate_distance for straight-line distance ("as the crow flies")
- Use get_directions for route distance along roads with traffic
- Use search_poi for finding types of places ("coffee shops", "restaurants")
- Use get_isochrone for "what can I reach in X minutes" questions
- Prefer offline tools (calculate_distance) when real-time data is not needed
Always provide clear, specific information with times and distances.`],
['human', '{input}'],
new MessagesPlaceholder('agent_scratchpad')
]);
// @ts-ignore - Zod tuple schemas cause deep type recursion
const agent = await createToolCallingAgent({
llm,
tools,
prompt
});
return new AgentExecutor({
agent,
tools,
verbose: true
});
}
// Example usage
async function main() {
try {
const executor = await createLocationAgent();
// Example 1: Find coffee shops
console.log('Example 1: Finding coffee shops near Union Square\n');
const result1 = await executor.invoke({
input: 'Find coffee shops within 10 minutes walking from Union Square NYC (coordinates: -73.9908, 40.7360). Tell me their names and how far each is.'
});
console.log('\nResult:', result1.output);
console.log('\n---\n');
// Example 2: Route planning
console.log('Example 2: Planning route with traffic\n');
const result2 = await executor.invoke({
input: 'How long does it take to drive from San Francisco downtown (-122.4194, 37.7749) to Oakland (-122.2712, 37.8044) with current traffic?'
});
console.log('\nResult:', result2.output);
console.log('\n---\n');
// Example 3: Multi-step workflow
console.log('Example 3: Multi-step location analysis\n');
const result3 = await executor.invoke({
input: 'I work at -122.4, 37.79 in San Francisco. Find restaurants within 15 minutes walking, calculate the distance to each, and recommend the closest 3.'
});
console.log('\nResult:', result3.output);
} catch (error) {
console.error('Error:', error);
}
}
// Run if executed directly
if (require.main === module) {
main();
}
export { createLocationAgent, mcp };
/**
* Mastra + Mapbox MCP Integration Example
*
* This example shows how to integrate Mapbox MCP Server with Mastra agents.
*
* Prerequisites:
* - npm install @mastra/core zod
* - Set MAPBOX_ACCESS_TOKEN environment variable
*
* Usage:
* - ts-node mastra-example.ts
*/
import { Agent } from '@mastra/core/agent';
import { createTool } from '@mastra/core/tools';
import { z } from 'zod';
// Mapbox MCP Client (hosted server)
class MapboxMCP {
private url = 'https://mcp.mapbox.com/mcp';
private headers: Record<string, string>;
constructor(token?: string) {
const mapboxToken = token || process.env.MAPBOX_ACCESS_TOKEN;
if (!mapboxToken) {
throw new Error('MAPBOX_ACCESS_TOKEN is required');
}
this.headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${mapboxToken}`
};
}
async callTool(name: string, args: any): Promise<any> {
const request = {
jsonrpc: '2.0',
id: Date.now(),
method: 'tools/call',
params: { name, arguments: args }
};
const response = await fetch(this.url, {
method: 'POST',
headers: this.headers,
body: JSON.stringify(request)
});
if (!response.ok) {
throw new Error(`MCP request failed: ${response.statusText}`);
}
const data = await response.json() as any;
if (data.error) {
throw new Error(`MCP error: ${data.error.message}`);
}
return JSON.parse(data.result.content[0].text);
}
}
// Initialize Mapbox MCP client
const mcp = new MapboxMCP();
// Create Mapbox tools for Mastra
const getDirectionsTool = createTool({
id: 'get-directions',
description: 'Get turn-by-turn driving directions with traffic-aware route distance and travel time along roads. Use when you need the actual driving route or traffic-aware duration.',
inputSchema: z.object({
origin: z.array(z.number()).length(2).describe('Origin coordinates [longitude, latitude]'),
destination: z.array(z.number()).length(2).describe('Destination coordinates [longitude, latitude]'),
}),
outputSchema: z.object({
duration: z.number().describe('Travel time in seconds'),
distance: z.number().describe('Distance in meters'),
summary: z.string().describe('Route summary')
}),
execute: async ({ origin, destination }) => {
const result = await mcp.callTool('directions_tool', {
coordinates: [
{ longitude: origin[0], latitude: origin[1] },
{ longitude: destination[0], latitude: destination[1] }
],
routing_profile: 'mapbox/driving-traffic'
});
return {
duration: result.routes[0].duration,
distance: result.routes[0].distance,
summary: `${Math.round(result.routes[0].duration / 60)} min, ${(result.routes[0].distance / 1000).toFixed(1)} km`
};
}
});
const searchPOITool = createTool({
id: 'search-poi',
description: 'Find ALL places of a specific category type near a location. Use when user wants to browse places by type (restaurants, hotels, coffee, etc.), not search for a specific named place.',
inputSchema: z.object({
category: z.string().describe('POI category: restaurant, hotel, coffee, gas_station, etc.'),
location: z.array(z.number()).length(2).describe('Search center [longitude, latitude]'),
}),
outputSchema: z.object({
results: z.array(z.object({
name: z.string(),
coordinates: z.array(z.number()),
address: z.string().optional()
}))
}),
execute: async ({ category, location }) => {
const result = await mcp.callTool('category_search_tool', {
category,
proximity: { longitude: location[0], latitude: location[1] }
});
return {
results: result.features.map((f: any) => ({
name: f.properties.name,
coordinates: f.geometry.coordinates,
address: f.properties.address
}))
};
}
});
const calculateDistanceTool = createTool({
id: 'calculate-distance',
description: 'Calculate straight-line (great-circle) distance between two points. Use for quick "as the crow flies" distance checks. Works offline, instant, no API cost.',
inputSchema: z.object({
from: z.array(z.number()).length(2).describe('Start coordinates [longitude, latitude]'),
to: z.array(z.number()).length(2).describe('End coordinates [longitude, latitude]'),
units: z.enum(['miles', 'kilometers']).optional().default('miles')
}),
outputSchema: z.object({
distance: z.number().describe('Distance in specified units')
}),
execute: async ({ from, to, units }) => {
const result = await mcp.callTool('distance_tool', {
from: { longitude: from[0], latitude: from[1] },
to: { longitude: to[0], latitude: to[1] },
units: units || 'miles'
});
return {
distance: parseFloat(result)
};
}
});
const getIsochroneTool = createTool({
id: 'get-isochrone',
description: 'Calculate the AREA reachable within a time limit from a starting point. Use for "What can I reach in X minutes?" questions or service area analysis.',
inputSchema: z.object({
location: z.array(z.number()).length(2).describe('Center point [longitude, latitude]'),
minutes: z.number().describe('Time limit in minutes'),
profile: z.enum(['mapbox/driving', 'mapbox/walking', 'mapbox/cycling']).optional().default('mapbox/driving')
}),
outputSchema: z.object({
area: z.string().describe('GeoJSON polygon of reachable area')
}),
execute: async ({ location, minutes, profile }) => {
const result = await mcp.callTool('isochrone_tool', {
coordinates: { longitude: location[0], latitude: location[1] },
contours_minutes: [minutes],
profile: profile || 'mapbox/driving'
});
return {
area: JSON.stringify(result)
};
}
});
// Create Mastra agent with Mapbox tools
const locationAgent = new Agent({
id: 'location-agent',
name: 'Location Intelligence Agent',
instructions: `You are a location intelligence expert. You help users with:
- Finding places (restaurants, hotels, etc.)
- Planning routes with traffic
- Calculating distances and travel times
- Analyzing reachable areas
TOOL SELECTION RULES:
- Use calculate-distance for straight-line distance ("as the crow flies")
- Use get-directions for route distance along roads with traffic
- Use search-poi for finding types of places ("coffee shops", "restaurants")
- Use get-isochrone for "what can I reach in X minutes" questions
- Prefer offline tools (calculate-distance) when real-time data is not needed
Always provide clear, actionable information with specific times and distances.`,
model: 'openai/gpt-5.2',
tools: {
getDirectionsTool,
searchPOITool,
calculateDistanceTool,
getIsochroneTool
}
});
// Example usage
async function main() {
try {
// Example 1: Find restaurants and calculate route
console.log('Example 1: Finding restaurants near Times Square\n');
const response1 = await locationAgent.generate([
{
role: 'user',
content: 'Find 3 restaurants near Times Square NYC (coordinates: -73.9857, 40.7484) and tell me how far each is.'
}
]);
console.log('Agent:', response1.text);
console.log('\n---\n');
// Example 2: Plan a route
console.log('Example 2: Planning route with traffic\n');
const response2 = await locationAgent.generate([
{
role: 'user',
content: 'What is the driving time from Boston (-71.0589, 42.3601) to NYC (-74.0060, 40.7128) with current traffic?'
}
]);
console.log('Agent:', response2.text);
console.log('\n---\n');
// Example 3: Isochrone analysis
console.log('Example 3: Reachable area analysis\n');
const response3 = await locationAgent.generate([
{
role: 'user',
content: 'Show me the area I can reach within 15 minutes driving from downtown SF (-122.4194, 37.7749)'
}
]);
console.log('Agent:', response3.text);
} catch (error) {
console.error('Error:', error);
}
}
// Run if executed directly
if (require.main === module) {
main();
}
export { locationAgent, mcp };
{
"name": "mapbox-mcp-runtime-examples",
"version": "1.0.0",
"description": "Working examples of Mapbox MCP Server integration with popular agent frameworks",
"private": true,
"scripts": {
"mastra": "ts-node mastra-example.ts",
"langchain": "ts-node langchain-example.ts",
"build": "tsc --noEmit"
},
"dependencies": {
"@langchain/core": "^0.3.29",
"@langchain/openai": "^0.3.16",
"@mastra/core": "^1.2.0",
"langchain": "^0.3.13",
"zod": "^3.24.1"
},
"devDependencies": {
"@types/node": "^22.10.5",
"ts-node": "^10.9.2",
"typescript": "^5.7.2"
},
"engines": {
"node": ">=18.0.0"
}
}
{
"compilerOptions": {
"target": "ES2022",
"module": "commonjs",
"lib": ["ES2022"],
"moduleResolution": "node",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"outDir": "./dist",
"rootDir": "."
},
"include": ["*.ts"],
"exclude": ["node_modules", "dist"]
}
CrewAI Integration
Use case: Multi-agent orchestration with geospatial capabilities
CrewAI enables building autonomous agent crews with specialized roles. Integration with Mapbox MCP adds geospatial intelligence to your crew.
from crewai import Agent, Task, Crew
from crewai.tools import BaseTool
import requests
import os
from typing import Type
from pydantic import BaseModel, Field
class MapboxMCP:
"""Mapbox MCP connector."""
def __init__(self, token: str = None):
self.url = 'https://mcp.mapbox.com/mcp'
token = token or os.getenv('MAPBOX_ACCESS_TOKEN')
self.headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {token}'
}
def call_tool(self, tool_name: str, params: dict) -> str:
request = {
'jsonrpc': '2.0',
'id': 1,
'method': 'tools/call',
'params': {'name': tool_name, 'arguments': params}
}
response = requests.post(self.url, headers=self.headers, json=request)
response.raise_for_status()
data = response.json()
if 'error' in data:
raise RuntimeError(f"MCP error: {data['error']['message']}")
return data['result']['content'][0]['text']
# Create Mapbox tools for CrewAI
class DirectionsTool(BaseTool):
name: str = "directions_tool"
description: str = "Get driving directions between two locations"
class InputSchema(BaseModel):
origin: list = Field(description="Origin [lng, lat]")
destination: list = Field(description="Destination [lng, lat]")
args_schema: Type[BaseModel] = InputSchema
def __init__(self):
super().__init__()
self.mcp = MapboxMCP()
def _run(self, origin: list, destination: list) -> str:
result = self.mcp.call_tool('directions_tool', {
'coordinates': [
{'longitude': origin[0], 'latitude': origin[1]},
{'longitude': destination[0], 'latitude': destination[1]}
],
'routing_profile': 'mapbox/driving-traffic'
})
return f"Directions: {result}"
class GeocodeTool(BaseTool):
name: str = "reverse_geocode_tool"
description: str = "Convert coordinates to human-readable address"
class InputSchema(BaseModel):
coordinates: list = Field(description="Coordinates [lng, lat]")
args_schema: Type[BaseModel] = InputSchema
def __init__(self):
super().__init__()
self.mcp = MapboxMCP()
def _run(self, coordinates: list) -> str:
result = self.mcp.call_tool('reverse_geocode_tool', {
'coordinates': {'longitude': coordinates[0], 'latitude': coordinates[1]}
})
return result
class SearchPOITool(BaseTool):
name: str = "search_poi"
description: str = "Find points of interest by category near a location"
class InputSchema(BaseModel):
category: str = Field(description="POI category (restaurant, hotel, etc.)")
location: list = Field(description="Search center [lng, lat]")
args_schema: Type[BaseModel] = InputSchema
def __init__(self):
super().__init__()
self.mcp = MapboxMCP()
def _run(self, category: str, location: list) -> str:
result = self.mcp.call_tool('category_search_tool', {
'category': category,
'proximity': {'longitude': location[0], 'latitude': location[1]}
})
return result
# Create specialized agents with geospatial tools
location_analyst = Agent(
role='Location Analyst',
goal='Analyze geographic locations and provide insights',
backstory="""Expert in geographic analysis and location intelligence.
Use search_poi for finding types of places (restaurants, hotels).
Use reverse_geocode_tool for converting coordinates to addresses.""",
tools=[GeocodeTool(), SearchPOITool()],
verbose=True
)
route_planner = Agent(
role='Route Planner',
goal='Plan optimal routes and provide travel time estimates',
backstory="""Experienced logistics coordinator specializing in route optimization.
Use directions_tool for route distance along roads with traffic.
Always use when traffic-aware travel time is needed.""",
tools=[DirectionsTool()],
verbose=True
)
# Create tasks
find_restaurants_task = Task(
description="""
Find the top 5 restaurants near coordinates [-73.9857, 40.7484] (Times Square).
Provide their names and approximate distances.
""",
agent=location_analyst,
expected_output="List of 5 restaurants with distances"
)
plan_route_task = Task(
description="""
Plan a route from [-74.0060, 40.7128] (downtown NYC) to [-73.9857, 40.7484] (Times Square).
Provide driving time considering current traffic.
""",
agent=route_planner,
expected_output="Route with estimated driving time"
)
# Create and run crew
crew = Crew(
agents=[location_analyst, route_planner],
tasks=[find_restaurants_task, plan_route_task],
verbose=True
)
result = crew.kickoff()
print(result)Real-world example - Restaurant finder crew:
# Define crew for restaurant recommendation system
class RestaurantCrew:
def __init__(self):
self.mcp = MapboxMCP()
# Location specialist agent
self.location_agent = Agent(
role='Location Specialist',
goal='Find and analyze restaurant locations',
tools=[SearchPOITool(), GeocodeTool()],
backstory='Expert in finding the best dining locations'
)
# Logistics agent
self.logistics_agent = Agent(
role='Logistics Coordinator',
goal='Calculate travel times and optimal routes',
tools=[DirectionsTool()],
backstory='Specialist in urban navigation and time optimization'
)
def find_restaurants_with_commute(self, user_location: list, max_minutes: int):
# Task 1: Find nearby restaurants
search_task = Task(
description=f"Find restaurants near {user_location}",
agent=self.location_agent,
expected_output="List of restaurants with coordinates"
)
# Task 2: Calculate travel times
route_task = Task(
description=f"Calculate travel time to each restaurant from {user_location}",
agent=self.logistics_agent,
expected_output="Travel times to each restaurant",
context=[search_task] # Depends on search results
)
crew = Crew(
agents=[self.location_agent, self.logistics_agent],
tasks=[search_task, route_task],
verbose=True
)
return crew.kickoff()
# Usage
restaurant_crew = RestaurantCrew()
results = restaurant_crew.find_restaurants_with_commute(
user_location=[-73.9857, 40.7484],
max_minutes=15
)Benefits:
- Multi-agent orchestration with geospatial tools
- Task dependencies and context passing
- Role-based agent specialization
- Autonomous crew execution
Custom Agent Integration
Use case: Building domain-specific AI applications (Zillow-style, TripAdvisor-style)
interface MCPTool {
name: string;
description: string;
inputSchema: any;
}
class CustomMapboxAgent {
private url = 'https://mcp.mapbox.com/mcp';
private headers: Record<string, string>;
private tools: Map<string, MCPTool> = new Map();
constructor(token?: string) {
const mapboxToken = token || process.env.MAPBOX_ACCESS_TOKEN;
this.headers = {
'Content-Type': 'application/json',
Authorization: `Bearer ${mapboxToken}`
};
}
async initialize() {
// Discover available tools from MCP server
await this.discoverTools();
}
private async discoverTools() {
const request = {
jsonrpc: '2.0',
id: 1,
method: 'tools/list'
};
const response = await this.sendMCPRequest(request);
response.result.tools.forEach((tool: MCPTool) => {
this.tools.set(tool.name, tool);
});
}
async callTool(toolName: string, params: any): Promise<any> {
const request = {
jsonrpc: '2.0',
id: Date.now(),
method: 'tools/call',
params: { name: toolName, arguments: params }
};
const response = await this.sendMCPRequest(request);
return response.result.content[0].text;
}
private async sendMCPRequest(request: any): Promise<any> {
const response = await fetch(this.url, {
method: 'POST',
headers: this.headers,
body: JSON.stringify(request)
});
const data = await response.json();
if (data.error) {
throw new Error(data.error.message);
}
return data;
}
// Domain-specific methods
async findPropertiesWithCommute(
homeLocation: [number, number],
workLocation: [number, number],
maxCommuteMinutes: number
) {
// Get isochrone from work location
const isochrone = await this.callTool('isochrone_tool', {
coordinates: { longitude: workLocation[0], latitude: workLocation[1] },
contours_minutes: [maxCommuteMinutes],
profile: 'mapbox/driving-traffic'
});
// Check if home is within isochrone
const isInRange = await this.callTool('point_in_polygon_tool', {
point: { longitude: homeLocation[0], latitude: homeLocation[1] },
polygon: JSON.parse(isochrone).features[0].geometry
});
return JSON.parse(isInRange);
}
async findRestaurantsNearby(location: [number, number], radiusMiles: number) {
// Search restaurants
const results = await this.callTool('category_search_tool', {
category: 'restaurant',
proximity: { longitude: location[0], latitude: location[1] }
});
// Filter by distance
const restaurants = JSON.parse(results);
const filtered = [];
for (const restaurant of restaurants) {
const distance = await this.callTool('distance_tool', {
from: { longitude: location[0], latitude: location[1] },
to: { longitude: restaurant.coordinates[0], latitude: restaurant.coordinates[1] },
units: 'miles'
});
if (parseFloat(distance) <= radiusMiles) {
filtered.push({
...restaurant,
distance: parseFloat(distance)
});
}
}
return filtered.sort((a, b) => a.distance - b.distance);
}
}
// Usage in Zillow-style app
const agent = new CustomMapboxAgent();
await agent.initialize();
const properties = await agent.findPropertiesWithCommute(
[-122.4194, 37.7749], // Home in SF
[-122.4, 37.79], // Work downtown
30 // Max 30min commute
);
// Usage in TripAdvisor-style app
const restaurants = await agent.findRestaurantsNearby(
[-73.9857, 40.7484], // Times Square
0.5 // Within 0.5 miles
);Benefits:
- Full control over agent behavior
- Domain-specific abstractions
- Custom error handling
Architecture Patterns
Pattern: MCP as Service Layer
┌─────────────────────────────────────┐
│ Your Application │
│ (Next.js, Express, FastAPI, etc.) │
└────────────────┬────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ AI Agent Layer │
│ (pydantic-ai, mastra, custom) │
└────────────────┬────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Mapbox MCP Server │
│ (Geospatial tools abstraction) │
└────────────────┬────────────────────┘
│
┌──────┴──────┐
▼ ▼
┌─────────┐ ┌──────────┐
│ Turf.js │ │ Mapbox │
│ (Local) │ │ APIs │
└─────────┘ └──────────┘Benefits:
- Clean separation of concerns
- Easy to swap MCP server versions
- Centralized geospatial logic
Pattern: Hybrid Approach
You can use MCP for AI agent features while using direct Mapbox APIs for other parts of your app.
class GeospatialService {
constructor(
private mcpServer: MapboxMCPServer, // For AI features
private mapboxSdk: MapboxSDK // For direct app features
) {}
// AI Agent Feature: Natural language search
async aiSearchNearby(userQuery: string): Promise<string> {
// Let AI agent use MCP tools to interpret query and find places
// Returns natural language response
return await this.agent.execute(userQuery, [
this.mcpServer.tools.category_search_tool,
this.mcpServer.tools.directions_tool
]);
}
// Direct App Feature: Display route on map
async getRouteGeometry(origin: Point, dest: Point): Promise<LineString> {
// Direct API call for map rendering - returns GeoJSON
const result = await this.mapboxSdk.directions.getDirections({
waypoints: [origin, dest],
geometries: 'geojson'
});
return result.routes[0].geometry;
}
// Offline Feature: Distance calculations (always use MCP/Turf.js)
async calculateDistance(from: Point, to: Point): Promise<number> {
// No API cost, instant
return await this.mcpServer.callTool('distance_tool', {
from,
to,
units: 'miles'
});
}
}Architecture Decision Guide:
| Use Case | Use This | Why |
|---|---|---|
| AI agent natural language features | MCP Server | Simplified tool interface, AI-friendly responses |
| Map rendering, direct UI controls | Mapbox SDK | More control, better performance |
| Distance/area calculations | MCP Server (offline tools) | Free, instant, no API calls |
| Custom map styling | Mapbox SDK | Fine-grained style control |
| Conversational geospatial queries | MCP Server | AI agent can chain tools |
LangChain Integration
Use case: Building conversational AI with geospatial tools
import { ChatOpenAI } from '@langchain/openai';
import { AgentExecutor, createToolCallingAgent } from 'langchain/agents';
import { DynamicStructuredTool } from '@langchain/core/tools';
import { ChatPromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts';
import { z } from 'zod';
// MCP client/transport setup using @modelcontextprotocol/sdk
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
// Connect to the Mapbox MCP server via SSE transport
const transport = new SSEClientTransport(new URL('https://mcp.mapbox.com/sse'), {
requestInit: {
headers: {
Authorization: `Bearer ${process.env.MAPBOX_ACCESS_TOKEN}`
}
}
});
const mcpClient = new Client({ name: 'langchain-mapbox', version: '1.0.0' });
await mcpClient.connect(transport);
// Helper to call MCP tools through the client
async function callMcpTool(name: string, args: any): Promise<string> {
const result = await mcpClient.callTool({ name, arguments: args });
return (result.content as any)[0].text;
}
const tools = [
new DynamicStructuredTool({
name: 'directions_tool',
description:
'Get turn-by-turn driving directions with traffic-aware route distance along roads. Use when you need the actual driving route or traffic-aware duration.',
schema: z.object({
origin: z.tuple([z.number(), z.number()]).describe('Origin [longitude, latitude]'),
destination: z.tuple([z.number(), z.number()]).describe('Destination [longitude, latitude]')
}) as any,
func: async ({ origin, destination }: any) => {
return await callMcpTool('directions_tool', {
coordinates: [
{ longitude: origin[0], latitude: origin[1] },
{ longitude: destination[0], latitude: destination[1] }
],
routing_profile: 'mapbox/driving-traffic'
});
}
}),
new DynamicStructuredTool({
name: 'category_search_tool',
description:
'Find ALL places of a specific category type near a location. Use when user wants to browse places by type (restaurants, hotels, coffee, etc.).',
schema: z.object({
category: z.string().describe('POI category: restaurant, hotel, coffee, etc.'),
location: z.tuple([z.number(), z.number()]).describe('Search center [longitude, latitude]')
}) as any,
func: async ({ category, location }: any) => {
return await callMcpTool('category_search_tool', {
category,
proximity: { longitude: location[0], latitude: location[1] }
});
}
}),
new DynamicStructuredTool({
name: 'isochrone_tool',
description:
'Calculate the AREA reachable within a time limit from a starting point. Use for "What can I reach in X minutes?" questions.',
schema: z.object({
location: z.tuple([z.number(), z.number()]).describe('Center point [longitude, latitude]'),
minutes: z.number().describe('Time limit in minutes'),
profile: z.enum(['mapbox/driving', 'mapbox/walking', 'mapbox/cycling']).optional()
}) as any,
func: async ({ location, minutes, profile }: any) => {
return await callMcpTool('isochrone_tool', {
coordinates: { longitude: location[0], latitude: location[1] },
contours_minutes: [minutes],
profile: profile || 'mapbox/walking'
});
}
}),
new DynamicStructuredTool({
name: 'distance_tool',
description: 'Calculate straight-line distance between two points (offline, free)',
schema: z.object({
from: z.tuple([z.number(), z.number()]).describe('Start [longitude, latitude]'),
to: z.tuple([z.number(), z.number()]).describe('End [longitude, latitude]'),
units: z.enum(['miles', 'kilometers']).optional()
}) as any,
func: async ({ from, to, units }: any) => {
return await callMcpTool('distance_tool', {
from: { longitude: from[0], latitude: from[1] },
to: { longitude: to[0], latitude: to[1] },
units: units || 'miles'
});
}
})
];
// Create agent
const llm = new ChatOpenAI({ model: 'gpt-5.2', temperature: 0 });
const prompt = ChatPromptTemplate.fromMessages([
['system', 'You are a location intelligence assistant.'],
['human', '{input}'],
new MessagesPlaceholder('agent_scratchpad')
]);
// @ts-ignore - Zod tuple schemas cause deep type recursion
const agent = await createToolCallingAgent({ llm, tools, prompt });
const executor = new AgentExecutor({ agent, tools, verbose: true });
// Use agent
const result = await executor.invoke({
input: 'Find coffee shops within 10 minutes walking from Union Square, NYC'
});Benefits:
- Conversational interface
- Tool chaining
- Memory and context management
TypeScript Type Considerations:
When using DynamicStructuredTool with Zod schemas (especially z.tuple()), TypeScript may encounter deep type recursion errors. This is a known limitation with complex Zod generic types. The minimal fix is to add as any type assertions:
const tool = new DynamicStructuredTool({
name: 'my_tool',
schema: z.object({
coords: z.tuple([z.number(), z.number()])
}) as any, // ← Add 'as any' to prevent type recursion
func: async ({ coords }: any) => {
// ← Type parameters as 'any'
// Implementation
}
});
// For JSON responses from external APIs
const data = (await response.json()) as any;
// For createOpenAIFunctionsAgent with complex tool types
// @ts-ignore - Zod tuple schemas cause deep type recursion
const agent = await createOpenAIFunctionsAgent({ llm, tools, prompt });This doesn't affect runtime validation (Zod still validates at runtime) - it only helps TypeScript's type checker avoid infinite recursion during compilation.
Mastra Integration
Use case: Building multi-agent systems with geospatial workflows
import { Mastra } from '@mastra/core';
class MapboxMCP {
private url = 'https://mcp.mapbox.com/mcp';
private headers: Record<string, string>;
constructor(token?: string) {
const mapboxToken = token || process.env.MAPBOX_ACCESS_TOKEN;
this.headers = {
'Content-Type': 'application/json',
Authorization: `Bearer ${mapboxToken}`
};
}
async callTool(toolName: string, params: any): Promise<any> {
const request = {
jsonrpc: '2.0',
id: Date.now(),
method: 'tools/call',
params: { name: toolName, arguments: params }
};
const response = await fetch(this.url, {
method: 'POST',
headers: this.headers,
body: JSON.stringify(request)
});
const data = await response.json();
return JSON.parse(data.result.content[0].text);
}
}
// Create Mastra agent with Mapbox tools
import { Agent } from '@mastra/core/agent';
import { createTool } from '@mastra/core/tools';
import { z } from 'zod';
const mcp = new MapboxMCP();
// Create Mapbox tools
const searchPOITool = createTool({
id: 'search-poi',
description: 'Find places of a specific category near a location',
inputSchema: z.object({
category: z.string(),
location: z.array(z.number()).length(2)
}),
execute: async ({ category, location }) => {
return await mcp.callTool('category_search_tool', {
category,
proximity: { longitude: location[0], latitude: location[1] }
});
}
});
const getDirectionsTool = createTool({
id: 'get-directions',
description: 'Get driving directions with traffic',
inputSchema: z.object({
origin: z.array(z.number()).length(2),
destination: z.array(z.number()).length(2)
}),
execute: async ({ origin, destination }) => {
return await mcp.callTool('directions_tool', {
coordinates: [
{ longitude: origin[0], latitude: origin[1] },
{ longitude: destination[0], latitude: destination[1] }
],
routing_profile: 'mapbox/driving-traffic'
});
}
});
// Create location agent
const locationAgent = new Agent({
id: 'location-agent',
name: 'Location Intelligence Agent',
instructions: 'You help users find places and plan routes with geospatial tools.',
model: 'openai/gpt-5.2',
tools: {
searchPOITool,
getDirectionsTool
}
});
// Use agent
const result = await locationAgent.generate([
{ role: 'user', content: 'Find restaurants near Times Square NYC (-73.9857, 40.7484)' }
]);Benefits:
- Multi-step geospatial workflows
- Agent orchestration
- State management
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 permissionsRate 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);
});
});Pydantic AI Integration
Use case: Building AI agents with type-safe tools in Python
Using Hosted Server (Recommended)
Common mistake: When using pydantic-ai with OpenAI, the correct import isfrom pydantic_ai.models.openai import OpenAIChatModel. Do NOT useOpenAIModel— that class does not exist in pydantic-ai and will throw an ImportError at runtime.
Use `MCPServerHTTP` from pydantic-ai to connect to the hosted Mapbox MCP server. This is the idiomatic way — avoid writing custom HTTP wrappers.
from pydantic_ai import Agent
from pydantic_ai.models.openai import OpenAIChatModel
from pydantic_ai.mcp import MCPServerHTTP
import os
# Connect to Mapbox MCP server using MCPServerHTTP
mapbox_server = MCPServerHTTP(
url='https://mcp.mapbox.com/sse',
headers={
'Authorization': f'Bearer {os.getenv("MAPBOX_ACCESS_TOKEN")}'
}
)
# Create agent with MCP server — tools are discovered automatically
agent = Agent(
model=OpenAIChatModel('gpt-4o'),
mcp_servers=[mapbox_server]
)
# Use agent — MCP tools (directions_tool, etc.) are available automatically
async def main():
async with agent.run_mcp_servers():
result = await agent.run(
"What's the driving time from the Eiffel Tower to the Louvre?"
)
print(result.output)Key point: WithMCPServerHTTP, you do NOT define tools manually — the agent discovers them from the MCP server. The server exposes tools likedirections_tool,category_search_tool,isochrone_tool, etc.
How the Agent Calls directions_tool
When the agent processes a directions query, it will call directions_tool with these parameters:
# The agent automatically calls directions_tool like this:
{
"coordinates": [
{"longitude": 2.2945, "latitude": 48.8584}, # Eiffel Tower
{"longitude": 2.3376, "latitude": 48.8606} # Louvre
],
"routing_profile": "mapbox/driving-traffic"
}Critical parameter rules:
coordinatesis an array of `{longitude, latitude}` objects — NOT[lng, lat]arraysrouting_profilemust include the `mapbox/` prefix (e.g.,mapbox/driving-traffic,mapbox/walking)- Do NOT use
origin/destinationparameter names — use thecoordinatesarray instead
Using Self-Hosted Server
import subprocess
class MapboxMCPLocal:
def __init__(self, token: str):
self.token = token
self.mcp_process = subprocess.Popen(
['npx', '@mapbox/mcp-server'],
env={'MAPBOX_ACCESS_TOKEN': token},
stdin=subprocess.PIPE,
stdout=subprocess.PIPE
)
def call_tool(self, tool_name: str, params: dict) -> dict:
# ... similar to hosted but via subprocess
passBenefits:
- Type-safe tool definitions
- Seamless MCP integration
- Python-native development
Smolagents Integration
Use case: Lightweight agents with geospatial capabilities (Hugging Face)
Smolagents is Hugging Face's simple, efficient agent framework. Perfect for deploying geospatial agents with minimal overhead.
from smolagents import CodeAgent, Tool, HfApiModel
import requests
import os
class MapboxMCP:
"""Mapbox MCP connector."""
def __init__(self, token: str = None):
self.url = 'https://mcp.mapbox.com/mcp'
token = token or os.getenv('MAPBOX_ACCESS_TOKEN')
self.headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {token}'
}
def call_tool(self, tool_name: str, params: dict) -> str:
request = {
'jsonrpc': '2.0',
'id': 1,
'method': 'tools/call',
'params': {'name': tool_name, 'arguments': params}
}
response = requests.post(self.url, headers=self.headers, json=request)
result = response.json()['result']
return result['content'][0]['text']
# Create Mapbox tools for Smolagents
class DirectionsTool(Tool):
name = "directions_tool"
description = """
Get driving directions between two locations.
Args:
origin: Origin coordinates as [longitude, latitude]
destination: Destination coordinates as [longitude, latitude]
Returns:
Directions with distance and travel time
"""
def __init__(self):
super().__init__()
self.mcp = MapboxMCP()
def forward(self, origin: list, destination: list) -> str:
return self.mcp.call_tool('directions_tool', {
'coordinates': [
{'longitude': origin[0], 'latitude': origin[1]},
{'longitude': destination[0], 'latitude': destination[1]}
],
'routing_profile': 'mapbox/driving-traffic'
})
class CalculateDistanceTool(Tool):
name = "distance_tool"
description = """
Calculate distance between two points (offline, instant).
Args:
from_coords: Start coordinates [longitude, latitude]
to_coords: End coordinates [longitude, latitude]
units: 'miles' or 'kilometers'
Returns:
Distance as a number
"""
def __init__(self):
super().__init__()
self.mcp = MapboxMCP()
def forward(self, from_coords: list, to_coords: list, units: str = 'miles') -> str:
return self.mcp.call_tool('distance_tool', {
'from': {'longitude': from_coords[0], 'latitude': from_coords[1]},
'to': {'longitude': to_coords[0], 'latitude': to_coords[1]},
'units': units
})
class SearchPOITool(Tool):
name = "search_poi"
description = """
Search for points of interest by category.
Args:
category: POI category (restaurant, hotel, gas_station, etc.)
location: Search center [longitude, latitude]
Returns:
List of nearby POIs with names and coordinates
"""
def __init__(self):
super().__init__()
self.mcp = MapboxMCP()
def forward(self, category: str, location: list) -> str:
return self.mcp.call_tool('category_search_tool', {
'category': category,
'proximity': {'longitude': location[0], 'latitude': location[1]}
})
class IsochroneTool(Tool):
name = "isochrone_tool"
description = """
Calculate reachable area within time limit (isochrone).
Args:
location: Center point [longitude, latitude]
minutes: Time limit in minutes
profile: 'mapbox/driving', 'mapbox/walking', or 'mapbox/cycling'
Returns:
GeoJSON polygon of reachable area
"""
def __init__(self):
super().__init__()
self.mcp = MapboxMCP()
def forward(self, location: list, minutes: int, profile: str = 'mapbox/driving') -> str:
return self.mcp.call_tool('isochrone_tool', {
'coordinates': {'longitude': location[0], 'latitude': location[1]},
'contours_minutes': [minutes],
'profile': profile
})
# Create agent with Mapbox tools
model = HfApiModel()
agent = CodeAgent(
tools=[
DirectionsTool(),
CalculateDistanceTool(),
SearchPOITool(),
IsochroneTool()
],
model=model
)
# Use agent
result = agent.run(
"Find restaurants within 10 minutes walking from Times Square NYC "
"(coordinates: -73.9857, 40.7484). Calculate distances to each."
)
print(result)Real-world example - Property search agent:
class PropertySearchAgent:
def __init__(self):
self.mcp = MapboxMCP()
# Create specialized tools
tools = [
IsochroneTool(),
SearchPOITool(),
CalculateDistanceTool()
]
self.agent = CodeAgent(
tools=tools,
model=HfApiModel()
)
def find_properties_near_work(
self,
work_location: list,
max_commute_minutes: int,
property_locations: list[dict]
):
"""Find properties within commute time of work."""
prompt = f"""
I need to find properties within {max_commute_minutes} minutes
driving of my work at {work_location}.
Property locations to check:
{property_locations}
For each property:
1. Calculate if it's within the commute time
2. Find nearby amenities (grocery stores, restaurants)
3. Calculate distances to key locations
Return a ranked list of properties with commute time and nearby amenities.
"""
return self.agent.run(prompt)
# Usage
property_agent = PropertySearchAgent()
properties = [
{'id': 1, 'address': '123 Main St', 'coords': [-122.4194, 37.7749]},
{'id': 2, 'address': '456 Oak Ave', 'coords': [-122.4094, 37.7849]},
]
results = property_agent.find_properties_near_work(
work_location=[-122.4, 37.79], # Downtown SF
max_commute_minutes=30,
property_locations=properties
)Benefits:
- Lightweight and efficient
- Simple tool definition
- Code-based agent execution
- Great for production deployment
Use Cases by Application Type
Real Estate App (Zillow-style)
// Find properties with good commute
async findPropertiesByCommute(
searchArea: Polygon,
workLocation: Point,
maxCommuteMinutes: number
) {
// 1. Get isochrone from work
const reachableArea = await mcp.callTool('isochrone_tool', {
coordinates: { longitude: workLocation[0], latitude: workLocation[1] },
contours_minutes: [maxCommuteMinutes],
profile: 'mapbox/driving'
});
// 2. Check each property
const propertiesInRange = [];
for (const property of properties) {
const inRange = await mcp.callTool('point_in_polygon_tool', {
point: { longitude: property.location[0], latitude: property.location[1] },
polygon: reachableArea
});
if (inRange) {
// 3. Get exact commute time
const directions = await mcp.callTool('directions_tool', {
coordinates: [property.location, workLocation],
routing_profile: 'mapbox/driving-traffic'
});
propertiesInRange.push({
...property,
commuteTime: directions.duration / 60
});
}
}
return propertiesInRange;
}Food Delivery App (DoorDash-style)
// Check if restaurant can deliver to address
async canDeliver(
restaurantLocation: Point,
deliveryAddress: Point,
maxDeliveryTime: number
) {
// 1. Calculate delivery zone
const deliveryZone = await mcp.callTool('isochrone_tool', {
coordinates: restaurantLocation,
contours_minutes: [maxDeliveryTime],
profile: 'mapbox/driving'
});
// 2. Check if address is in zone
const canDeliver = await mcp.callTool('point_in_polygon_tool', {
point: deliveryAddress,
polygon: deliveryZone
});
if (!canDeliver) return false;
// 3. Get accurate delivery time
const route = await mcp.callTool('directions_tool', {
coordinates: [restaurantLocation, deliveryAddress],
routing_profile: 'mapbox/driving-traffic'
});
return {
canDeliver: true,
estimatedTime: route.duration / 60,
distance: route.distance
};
}Travel Planning App (TripAdvisor-style)
// Build day itinerary with travel times
async buildItinerary(
hotel: Point,
attractions: Array<{name: string, location: Point}>
) {
// 1. Calculate distances from hotel
const attractionsWithDistance = await Promise.all(
attractions.map(async (attr) => ({
...attr,
distance: await mcp.callTool('distance_tool', {
from: hotel,
to: attr.location,
units: 'miles'
})
}))
);
// 2. Get travel time matrix
const matrix = await mcp.callTool('matrix_tool', {
origins: [hotel],
destinations: attractions.map(a => a.location),
profile: 'mapbox/walking'
});
// 3. Sort by walking time
return attractionsWithDistance
.map((attr, idx) => ({
...attr,
walkingTime: matrix.durations[0][idx] / 60
}))
.sort((a, b) => a.walkingTime - b.walkingTime);
}Related skills
How it compares
Choose this skill when agents consume Mapbox via MCP tools; use frontend map SDK docs when building static embedded maps only.
FAQ
What is Mapbox MCP Server?
mapbox-mcp-runtime-patterns describes Mapbox MCP Server as a runtime server that exposes geospatial tools to AI agents through the Model Context Protocol, with setup documented in the mapbox/mcp-server GitHub repository.
Who should use mapbox-mcp-runtime-patterns?
mapbox-mcp-runtime-patterns suits developers integrating Mapbox geospatial capabilities into AI applications via MCP who need production runtime patterns, tool categories, and reliable agent access to location services.
Is Mapbox Mcp Runtime Patterns safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.