VJMAP logoVJMAP
CAD · WebGIS
中

Map MCP Protocol

Updated 2026-03-07~ 43 min read
.md

Traditional CAD map applications are operated by humans. Users need to learn complex CAD operation commands and map management workflows, and the operation cost is relatively high. Wouldn't it be very convenient if AI could help us operate maps?

If all CAD map applications could be intelligently transformed so that AI can understand and operate map data, everyone would have a virtual "map intelligent assistant." We would only need to "chat" with the map intelligent assistant like chatting, express our map requirements in natural language, and the intelligent assistant would automatically operate the map application to fulfill our needs. This would greatly improve people's work efficiency and experience in map processing, CAD drawing analysis, spatial data management, and more!

VJMap has implemented the technology of AI replacing humans to operate CAD map applications based on the MCP protocol, and applied it to our VJMap engine, achieving intelligent transformation of the VJMap system. Using MCP protocol tools, they are naturally supported for AI recognition and control, including map browsing, CAD drawing queries, spatial data extraction, graphic drawing, and other operations. It also provides a frontend SDK that supports rapid AI integration for existing map businesses and intelligentization. It currently supports frontend frameworks such as Vue, React, and Angular.

Since it is implemented based on the standard MCP protocol, it has universality and broad applicability. It can control map applications through various types of MCP Hosts—for example, through AI dialog boxes on web pages to control map applications, or through IDE tools such as Trae and Cursor, or through intelligent agent platforms such as Dify and Coze, or even through mobile apps, WeChat mini-programs, and other methods to remotely control your map application. We can chat with AI and let AI help us operate various map applications to fulfill our map-related needs.

Demo

chatmcp.gif
chatmcp.gif

VJMap Cloud Management Platform https://vjmap.com/app/cloud/#/

image-20250803183709135
image-20250803183709135
image-20250803180303071
image-20250803180303071

Click the MCP address in the upper-left corner of the interface to view the MCP address for this session.

image-20250803175643751
image-20250803175643751

You can copy the current session's MCP address to other MCP clients such as cursor, trae, or cherry studio to ask questions about the current map.

Example of invoking map-related operations via MCP address in cursor: mcpcursor.gif

MCP Architecture Principles and Flow

The VJMap MCP system adopts a server-frontend separation architecture and implements bidirectional communication through the MCP (Model Context Protocol). The system supports mixed invocation of server-side tools and frontend tools to implement complex map operations and interactions.

Overall Architecture

image-20250803182907607
image-20250803182907607

Backend MCP Tools

Tool Name Category Type Description Main Parameters
listmaps map Backend Get map list information, supports getting all maps or version information for specified maps mapid, version, mapIds, workspace, pagination, curPage, pageCount
createMap drawing Backend Create new map tool, supports two creation methods: 1. Create map via fileid and mapid; 2. Create via combine into new map API createType, mapid, fileid, uploadname, geom, sourceMapid, sourceVersion, sourceClipbounds, fourParameter, layers, workspace
deletemap delete Backend Delete map function, supports deleting specified version or all versions. Important: Delete operations are irreversible, use with caution! mapid, version, retainVersionMaxCount, workspace
getMapImage map Backend Get map image based on bounds and layer list, returns binary PNG format. Supports layer visibility, auto-calculates image aspect ratio mapid, version, bounds, pictureWidth, layerOn, layerOff, backgroundColor, transparent, styleName, workspace, returnBase64
fullSearch query Backend CAD drawing full-text search, supports searching text content, layers, block names, etc. query, map_ver, workspace, type, bounding_box, time_range, limit, offset, fields, facet
queryFeatures query Backend Query map entities, supports conditional query, rectangle bounds query, point query, and expression query mapid, version, queryType, sql, bounds, point, radius, expression, workspace
extractTable query Backend Automatically extract table data from CAD drawings. When querying drawing info, call this tool first to extract table data for analysis mapid, version, bounds, workspace
sqlDocHelper query Backend Get SQL writing documentation and table structure for querying map entities, provides reference for AI to generate SQL No parameters
createDwgDocHelper drawing Backend Get parameter description and entity type definition for creating drawings, provides reference for AI to generate graphic entity JSON arrays No parameters

Frontend Tools

Tool Name Category Type Description Main Parameters
map_get_info map Frontend Get current map details including zoom, center, bearing, pitch, bounds, layer info, workspace name, thumbnail URL. Coordinates use CAD system. No parameters
map_open_or_switch map Frontend Open or switch map based on mapid and version. Updates map context to backend after opening. mapid, version, isKeepOldLayers, isVectorStyle, isSetCenter, isFitBounds, layeron, layeroff, backcolor
map_view_control map Frontend Map view control - zoom, pan, rotate, pitch, zoom to bounds. All coordinates use CAD (x,y) operation, zoom, center, bearing, pitch, bounds, duration
map_execute_code runcode Frontend Execute custom JavaScript for complex map operations (requires VJMap help documentation MCP) code, description
map_draw_geojson draw Frontend Draw features from GeoJSON, supports all standard GeoJSON geometry types. Style in properties geojsonData
map_delete_drawn_features draw Frontend Delete drawn features, supports deleting specified instance or clearing all drawn layers instanceId, clearAll
map_create_markers draw Frontend Create one or more markers from GeoJSON, style from properties, supports custom style, drag, popup geojsonData
map_delete_markers draw Frontend Delete markers on map, supports deleting specified marker or clearing all markerId, clearAll

Users can customize business-related MCP tools through the SDK.

MCP URL Composition

The VJMap MCP system URL supports tool filtering. You can control available tool categories and specific tools through URL parameters.

URL Format

/ai/mcp?sessionId={sessionId}&include_categories={categories}&exclude_categories={categories}&include_tools={tools}&exclude_tools={tools}&token={token}

Official default MCP URL:

https://vjmap.com/server/ai/mcp?sessionId={sessionId}&include_categories=query,map,draw,custom&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MiwiVXNlcm5hbWUiOiJhZG1pbjEiLCJOaWNrTmFtZSI6ImFkbWluMSIsIkF1dGhvcml0eUlkIjoiYWRtaW4iLCJCdWZmZXJUaW1lIjo4NjQwMCwiZXhwIjo0ODEzMjY3NjM3LCJpc3MiOiJ2am1hcCIsIm5iZiI6MTY1OTY2NjYzN30.cDXCH2ElTzU2sQU36SNHWoTYTAc4wEkVIXmBAIzWh6M

If only backend MCP tools are needed without frontend interaction, sessionId can be left empty:

https://vjmap.com/server/ai/mcp?include_categories=query,map&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MiwiVXNlcm5hbWUiOiJhZG1pbjEiLCJOaWNrTmFtZSI6ImFkbWluMSIsIkF1dGhvcml0eUlkIjoiYWRtaW4iLCJCdWZmZXJUaW1lIjo4NjQwMCwiZXhwIjo0ODEzMjY3NjM3LCJpc3MiOiJ2am1hcCIsIm5iZiI6MTY1OTY2NjYzN30.cDXCH2ElTzU2sQU36SNHWoTYTAc4wEkVIXmBAIzWh6M

Parameter Description

Parameter Name Type Required Description Example
sessionId string Yes Session ID for current session session_123456
token string Yes Access token for authentication eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
include_categories string No Included tool categories, comma-separated query,map,drawing
exclude_categories string No Excluded tool categories, comma-separated delete,utility
include_tools string No Included tool names, comma-separated listmaps,fullSearch
exclude_tools string No Excluded tool names, comma-separated deletemap

Tool Categories

Category Name Category Value Description Included Tools
Map Management map Basic map CRUD listmaps, getMapImage, map_get_info, map_open_or_switch, map_view_control
Data Query query Data query and search fullSearch, queryFeatures, extractTable, sqlDocHelper
Graphic Processing drawing Graphic creation and processing createMap, createDwgDocHelper, map_draw_geojson, map_create_markers
Delete Tools delete Map deletion deletemap
Drawing Tools draw Frontend drawing map_draw_geojson, map_delete_drawn_features, map_create_markers, map_delete_markers
Code Execution runcode Code execution map_execute_code
Frontend Custom custom Frontend custom category

Usage Examples

Example 1: Include only query and map tools

/ai/mcp?sessionId=session_123456&include_categories=query,map&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Note: Only enables query tools and map tools.

Example 2: Exclude delete tools

/ai/mcp?sessionId=session_123456&exclude_categories=delete&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Note: Enables all tools except delete, ensuring users cannot perform delete operations.

Example 3: Include only specific tools

/ai/mcp?sessionId=session_123456&include_tools=listmaps,fullSearch,map_get_info&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Note: Only enables listmaps, fullSearch, map_get_info.

Example 4: Exclude specific tools

/ai/mcp?sessionId=session_123456&exclude_tools=deletemap,map_execute_code&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Note: Excludes deletemap and map_execute_code which may have security risks.

Example 5: Combined include and exclude

/ai/mcp?sessionId=session_123456&include_categories=query,map&exclude_tools=deletemap&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Notes

  1. Priority: include_tools and exclude_tools have higher priority than include_categories and exclude_categories
  2. Conflicts: If a tool is in both include_tools and exclude_tools, exclude_tools takes precedence
  3. Empty values: If a parameter is empty or not provided, system uses default (enable all tools)
  4. Security: Use exclude_categories=delete or exclude_tools=deletemap in production to prevent accidental deletes
  5. URL encoding: Encode special characters in parameter values

Effect when adding MCP address in cursor for Q&A: image-20250803191926787

{
  "mcpServers": {
    "vjmap": {
      "url": "https://vjmap.com/server/ai/mcp?include_categories=query,map&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MiwiVXNlcm5hbWUiOiJhZG1pbjEiLCJOaWNrTmFtZSI6ImFkbWluMSIsIkF1dGhvcml0eUlkIjoiYWRtaW4iLCJCdWZmZXJUaW1lIjo4NjQwMCwiZXhwIjo0ODEzMjY3NjM3LCJpc3MiOiJ2am1hcCIsIm5iZiI6MTY1OTY2NjYzN30.cDXCH2ElTzU2sQU36SNHWoTYTAc4wEkVIXmBAIzWh6M"
    }
  }
}

When frontend interaction is needed, copy the MCP address from the MCP tools in the AI dialog.

image-20250803192608314
image-20250803192608314
{
  "mcpServers": {
    "vjmap": {
      "url": "https://vjmap.com/server/ai/mcp?sessionId=session-6323558e&include_categories=query,map,draw,custom&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MSwiVXNlcm5hbWUiOiJyb290MSIsIk5pY2tOYW1lIjoicm9vdDEiLCJBdXRob3JpdHlJZCI6InJvb3QiLCJCdWZmZXJUaW1lIjo4NjQwMCwiZXhwIjoxOTQyMzg5NTc0LCJpc3MiOiJ2am1hcCIsIm5iZiI6MTYyNzAyODU3NH0.l1pP9FXu6ARDaaa-6ma0lp7ftbIk2t6rgmSmTqXry10"
    }
  }
}

Note: sessionId is different for each session; generate the address based on the current session.

MCP Frontend SDK and Custom Tool Example

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>vjmap demo</title>
    <link rel="stylesheet" type="text/css" href="https://vjmap.com/server/_demo/js/vjmap/vjmap.min.css">
    <script type="text/javascript" src="https://vjmap.com/server/_demo/js/vjmap/vjmap.min.js"></script>
</head>

<body style=" margin: 0;overflow: hidden;background-color:white;font-size: 16px">
    <div id="map" style="left:0;right:0;top:0;bottom:0;position: absolute;z-index: 0;"></div>
</body>
<script>
    (async () => {
        document.body.style.background = "#022B4F"; // dark background
        const env = {
            serviceUrl: "https://vjmap.com/server/api/v1",
            accessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MiwiVXNlcm5hbWUiOiJhZG1pbjEiLCJOaWNrTmFtZSI6ImFkbWluMSIsIkF1dGhvcml0eUlkIjoiYWRtaW4iLCJCdWZmZXJUaW1lIjo4NjQwMCwiZXhwIjo0ODEzMjY3NjM3LCJpc3MiOiJ2am1hcCIsIm5iZiI6MTY1OTY2NjYzN30.cDXCH2ElTzU2sQU36SNHWoTYTAc4wEkVIXmBAIzWh6M",
            exampleMapId: "sys_zp"
        };
        let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
        // Open map
        let res = await svc.openMap({
            mapid: env.exampleMapId, // map ID (ensure it exists or upload to create)
            mapopenway: vjmap.MapOpenWay.GeomRender, // geometry render
            style: vjmap.openMapDarkStyle() // dark style for dark container
        })
        if (res.error) {
            message.error(res.error)
        }
        // Map extent
        let mapExtent = vjmap.GeoBounds.fromString(res.bounds);
        // Create projection
        let prj = new vjmap.GeoProjection(mapExtent);

        // Create map
        let map = new vjmap.Map({
            container: 'map', // container ID
            style: svc.rasterStyle(), // raster tile style
            center: prj.toLngLat(mapExtent.center()), // center
            zoom: 2,
            renderWorldCopies: false
        });
        // Attach service and projection
        map.attach(svc, prj);
        // Fit map bounds
        map.fitMapBounds();
        await map.onLoad(); // wait for map load



        let enableAiMcpChat = true;
        if (enableAiMcpChat) {
            if (typeof VjmapMcpSdk !== "object") {
                let svc = map.getService();
                const _url = svc.baseUrl() + "version";
                // @ts-ignore
                let res = await svc._get(_url, {});
                let version = '';
                if (res && "data" in res) {
                    let data = res["data"];
                    version = data.version
                }
                // Bypass cache when version changes
                // vjchat.umd.js does not bundle vjmap; expose globally
                window.vjmap = vjmap;
                // Load scripts if not present
                await vjmap.addScript([{
                    src: "https://vjmap.com/server/_cloud/lib/vjmap-mcp-sdk.umd.js" + `?ver=${version}`
                }])
                await vjmap.addScript([{
                    src: "https://vjmap.com/server/_cloud/lib/ai-chat-lib.umd.js" + `?ver=${version}`
                }])

                let apiUrl = map.getService().baseUrl();
                let token = map.getService().accessToken;

                // Initialize MCP connection
                console.log('Initializing MCP...')
                let mcpInstance = await VjmapMcpSdk.initializeMCP({
                    apiBase: apiUrl,
                    autoConnect: true,
                    mapInstance: map
                })

                const sessionId = mcpInstance.state.sessionId
                // Register map tools
                await mcpInstance.registerMapTools()

                // Register custom tool example
                await registerCustomTool(mcpInstance, VjmapMcpSdk.z)

                chatContainer = document.createElement('div')
                chatContainer.id = 'ai-chat-container'
                chatContainer.style.cssText = `
                            position: absolute;
                            top: 0;
                            left: 0;
                            width: 100%;
                            height: 100%;
                            pointer-events: none;
                            z-index: 1000;
                        `

                // Allow chat container children to receive events
                const style = document.createElement('style')
                style.textContent = `
                            #ai-chat-container > * {
                            pointer-events: auto !important;
                            }
                        `
                if (!document.head.querySelector('style[data-ai-chat]')) {
                    style.setAttribute('data-ai-chat', 'true')
                    document.head.appendChild(style)
                }
                document.body.appendChild(chatContainer)
                let chatLib = AiChatLib.createAiChatLib({
                    container: chatContainer,
                    apiUrl: apiUrl,
                    token: token,
                    sessionId: sessionId,
                    mcpAddresses: [
                        '{apiUrl}/ai/mcp?sessionId={sessionId}&include_categories=query,map,draw,custom&token={token}'
                    ],
                    systemPrompt: '',
                    quickQuestions: [
                        'Describe what is drawn in the current map',
                        'Locate a table in the current map',
                        "Find text containing 'map' and add point markers at those positions"
                    ],
                    maxHistoryCount: 10,
                    window: {
                        width: 500,
                        height: 780,
                        right: 3,
                        theme: 'dark',
                        draggable: true,
                        title: 'VJMap AI Q&A'
                    },
                    debug: true,
                    autoFocus: true
                })
            }
        }
    })();

    // flashPos implementation
    const flashPos = (bounds) => {
        map.fitMapBounds(vjmap.GeoBounds.fromArray(bounds), { padding: 300 })
        return new Promise((resolve) => {
            const routePath = vjmap.GeoBounds.fromArray(bounds).toPointArray();
            routePath.push(routePath[0])
            let geoLineDatas = [];
            geoLineDatas.push({
                points: map.toLngLat(routePath),
                properties: {
                    opacity: 1.0
                }
            })

            let polylines = new vjmap.Polyline({
                data: geoLineDatas,
                lineColor: 'yellow',
                lineWidth: 3,
                lineOpacity: ['get', 'opacity'],
                isHoverPointer: false,
                isHoverFeatureState: false
            });
            polylines.addTo(map);

            vjmap.createAnimation({
                from: 1,
                to: 10,
                duration: 1000,
                onUpdate: (e) => {
                    const data = polylines.getData();
                    if (data && data.features && data.features[0]) {
                        data.features[0].properties.opacity = parseInt(e.toString()) % 2 ? 1.0 : 0;
                        polylines.setData(data);
                    }
                },
                onStop: () => {
                    polylines.remove()
                    resolve({})
                },
                onComplete: () => {
                    polylines.remove()
                    resolve({})
                }
            })
        })
    }

    // Register custom tool example
    async function registerCustomTool(mcpInstance, z) {
        if (!mcpInstance) return

        // Define tool input schema with zod
        const inputSchema = z.object({
            bounds: z.array(z.number()).length(4).describe('Bounds array [minX, minY, maxX, maxY]')
        })

        // Create tool definition
        const tool = VjmapMcpSdk.createTool(
            'map_flash_position',
            'Flash the given bounds on the map',
            inputSchema,
            'custom'
        )

        // Tool handler
        const handler = async (args) => {
            try {
                const validatedArgs = inputSchema.parse(args)
                const { bounds } = validatedArgs

                await flashPos(bounds)

                return {
                    content: [{
                        type: 'text',
                        text: `Flashed bounds on map: [${bounds.join(', ')}]`
                    }]
                }
            } catch (error) {
                return {
                    content: [{
                        type: 'text',
                        text: `Tool failed: ${error instanceof Error ? error.message : String(error)}`
                    }],
                    isError: true
                }
            }
        }

        mcpInstance.registerTool(tool, handler, "custom")

        // Register tool on server so backend can see it
        try {
            const serviceManager = mcpInstance.getServiceManager()
            if (serviceManager) {
                await serviceManager.registerToolsToServer()
                console.log('Custom tool registered on server')
            }
        } catch (error) {
            console.error('Failed to register custom tool on server:', error)
        }
    }


</script>

</html>

In the above code, ai-chat-lib.umd.js is the AI chat UI library.

In the above code, vjmap-mcp-sdk.umd.js is the vjmap MCP SDK library. It is mainly used for server communication and provides interfaces for users to customize tools. This library can also be installed via npm. Usage is as follows:

VJMap MCP SDK Installation

npm install vjmcpsdk

VJMap MCP SDK Usage

import vjmap from vjmap
import { initializeMCP, createTool, z } from vjmcpsdk

let apiUrl = map.getService().baseUrl();
let token = map.getService().accessToken;

let mcpInstance = await initializeMCP({
    apiBase: apiUrl,
    autoConnect: true,
    mapInstance: map
})

const sessionId = mcpInstance.state.sessionId
await mcpInstance.registerMapTools()
await registerCustomTool(mcpInstance, z)

// flashPos function implementation
const flashPos = (bounds) => {
    map.fitMapBounds(vjmap.GeoBounds.fromArray(bounds), { padding: 300 })
    return new Promise((resolve) => {
        const routePath = vjmap.GeoBounds.fromArray(bounds).toPointArray();
        routePath.push(routePath[0])
        let geoLineDatas = [];
        geoLineDatas.push({
            points: map.toLngLat(routePath),
            properties: { opacity: 1.0 }
        })
        let polylines = new vjmap.Polyline({
            data: geoLineDatas,
            lineColor: 'yellow',
            lineWidth: 3,
            lineOpacity: ['get', 'opacity'],
            isHoverPointer: false,
            isHoverFeatureState: false
        });
        polylines.addTo(map);
        vjmap.createAnimation({
            from: 1,
            to: 10,
            duration: 1000,
            onUpdate: (e) => {
                const data = polylines.getData();
                if (data && data.features && data.features[0]) {
                    data.features[0].properties.opacity = parseInt(e.toString()) % 2 ? 1.0 : 0;
                    polylines.setData(data);
                }
            },
            onStop: () => { polylines.remove(); resolve({}) },
            onComplete: () => { polylines.remove(); resolve({}) }
        })
    })
}

async function registerCustomTool(mcpInstance, z) {
    if (!mcpInstance) return
    const inputSchema = z.object({
        bounds: z.array(z.number()).length(4).describe('Bounds array [minX, minY, maxX, maxY]')
    })
    const tool = createTool(
        'map_flash_position',
        'Flash the given bounds on the map',
        inputSchema,
        'custom'
    )
    const handler = async (args) => {
        try {
            const validatedArgs = inputSchema.parse(args)
            const { bounds } = validatedArgs
            await flashPos(bounds)
            return {
                content: [{ type: 'text', text: `Flashed bounds on map: [${bounds.join(', ')}]` }]
            }
        } catch (error) {
            return {
                content: [{ type: 'text', text: `Tool failed: ${error instanceof Error ? error.message : String(error)}` }],
                isError: true
            }
        }
    }
    mcpInstance.registerTool(tool, handler, "custom")
    try {
        const serviceManager = mcpInstance.getServiceManager()
        if (serviceManager) {
            await serviceManager.registerToolsToServer()
            console.log('Custom tool registered on server')
        }
    } catch (error) {
        console.error('Failed to register custom tool on server:', error)
    }
}

Backend Configuration

Configure in data/ai/aisvr_config.yaml in the backend data directory.

image-20250803190902021
image-20250803190902021
Was this page helpful?