All skills
mapbox avatar

/mapbox-mcp-runtime-patterns

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

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

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

This session only. Nothing lands on disk.

referenceslangchain.md

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

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.

Source: SKILL.md on GitHub

1 warning17d5 checks · Risk SAFE
  • Gen Agent Trust Hub17d

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

  • Socket17d

    No alerts

  • Snyk17d

    Risk: LOW · No issues

  • Runlayer6mo

    2/12 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

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

Last checked against GitHub 9 hours ago.

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

README badge

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

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

Generated from the current SKILL.md.

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

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